| … | … | @@ -1,57 +1,70 @@ |
| 1 | # M0 — UDS bridge + codec round-trip | |
| 1 | # hyper-atomspace bridge — M0 + M1 | |
| 2 | 2 | |
| 3 | First milestone of the HyperStorageNode (spec: `../../spec/hyperstoragenode.md`; | |
| 4 | `atomspace` org issue #4). Proves the data path with **no Hypercore** and **no full | |
| 5 | StorageNode/guile-module boilerplate** yet: | |
| 3 | Milestones of the HyperStorageNode (spec: `../../spec/hyperstoragenode.md`; | |
| 4 | `atomspace` org issue #4). A Bare daemon holds the op-log; a C++ client speaks the | |
| 5 | real opencog s-expr codec over a UNIX-domain socket. An AtomSpace is a | |
| 6 | **deterministic projection of an append-only log** — and here the log is a | |
| 7 | **Hypercore**. | |
| 6 | 8 | |
| 7 | 9 | ``` |
| 8 | C++ (real Sexpr codec) Bare daemon (in-memory s-expr log) | |
| 9 | ──────────────────────── ───────────────────────────────── | |
| 10 | build small AtomSpace | |
| 11 | Sexpr::encode_atom ──store──▶ log.push(sexpr) | |
| 12 | (barrier) ──barrier─▶ ok | |
| 13 | (replay) ──load────▶ (sexpr1 sexpr2 …) | |
| 14 | Sexpr::decode_atom ◀─────────── | |
| 15 | gold check: src AtomSpace ≡ dst AtomSpace → M0 GOLD PASS | |
| 10 | C++ (real Sexpr codec) Bare daemon (Hypercore op-log) | |
| 11 | ──────────────────────── ────────────────────────────── | |
| 12 | build AtomSpace | |
| 13 | Sexpr::encode_atom ──store───▶ core.append(sexpr) | |
| 14 | (barrier) ──barrier──▶ ok | |
| 15 | (replay) ──load─────▶ ( get(0..len) ) | |
| 16 | (checkout @K) ──loadat K─▶ ( get(0..K) ) ← cheap fork | |
| 17 | Sexpr::decode_atom ◀──────────── | |
| 18 | gold check: src AtomSpace ≡ dst AtomSpace | |
| 16 | 19 | ``` |
| 17 | 20 | |
| 18 | ## What this establishes | |
| 19 | - A C++ `AF_UNIX` client (`HyperChannel`, adapted from `CogSimpleStorage`'s | |
| 20 | `do_send`/`do_recv`, TCP→UDS) round-trips atoms through a **Bare** daemon. | |
| 21 | - The wire payload is the **real opencog s-expr codec** (`Sexpr::encode_atom` / | |
| 22 | `Sexpr::decode_atom` from `libsexpr`), not a bespoke serialization — so encode is | |
| 23 | a genuine inverse of decode. | |
| 24 | - bare-net UDS framing + half-open handling (`allowHalfOpen:false` + destroy on | |
| 25 | `end`, the hyper-svc leak fix) work against a foreign (C++) client. | |
| 21 | ## Milestones proven here (`./run.sh` runs all three) | |
| 22 | - **M0 — bridge + codec round-trip.** C++ `AF_UNIX` client round-trips atoms | |
| 23 | through the Bare daemon; payload is the real `libsexpr` codec (encode is a | |
| 24 | genuine inverse of decode). `[full]` phase. | |
| 25 | - **M1 #1 — durable deterministic replay.** The daemon is **restarted on the same | |
| 26 | Hypercore dir**; replaying the log rebuilds a byte-identical AtomSpace. `[verify]` | |
| 27 | phase → `GOLD #1`. | |
| 28 | - **M1 #2 — cheap fork.** `loadat K` is a read-only **checkout at seq K**: read | |
| 29 | fewer blocks, get an earlier, strictly-smaller graph, no reprocessing. `[fork]` | |
| 30 | phase → `GOLD #2` (full=8, checkout@3=3, subset ✓, strict ✓). | |
| 26 | 31 | |
| 27 | 32 | ## Files |
| 28 | 33 | | File | Role | |
| 29 | 34 | |---|---| |
| 30 | | `daemon.js` | Bare UDS server; in-memory `log[]` of s-expr strings; verbs `store`/`load`/`clear`/`barrier`/`count`. At M1 the array becomes a Hypercore. | | |
| 31 | | `m0_roundtrip.cc` | C++ harness. `HyperChannel` = the reusable transport the future `HyperStorageNode` embeds; `main()` builds an 8-atom AtomSpace, stores, replays, gold-checks. | | |
| 32 | | `run.sh` | build + start daemon + run harness + gold check + cleanup. | | |
| 35 | | `daemon.js` | Bare daemon. Op-log = a **Hypercore** (`append` on store, `get(0..len)` on load, `get(0..K)` on `loadat`, `truncate(0)` on clear). Ordered promise-chain so load/barrier observe all prior stores. `allowHalfOpen:false` + destroy-on-`end` (hyper-svc leak fix). `usage: bare daemon.js <sock> <storage-dir>`. | | |
| 36 | | `m0_roundtrip.cc` | C++ harness. `HyperChannel` = reusable `AF_UNIX` transport (from `CogSimpleStorage` do_send/do_recv) that lifts into `HyperStorageNode : public StorageNode`. Modes `full` / `verify` / `fork`. | | |
| 37 | | `run.sh` | build + 3-phase drive (full → restart-verify → fork) + cleanup. | | |
| 33 | 38 | |
| 34 | 39 | ## Run |
| 35 | 40 | ```bash |
| 36 | ./run.sh # prints "M0 GOLD PASS", exits 0 | |
| 41 | ./run.sh | |
| 42 | # [full] live round-trip src=8 dst=8 PASS | |
| 43 | # [verify] durable replay src=8 dst=8 GOLD #1 PASS | |
| 44 | # [fork] full=8 checkout@3=3 subset=1 strict=1 GOLD #2 PASS | |
| 45 | # M1 ALL PASS | |
| 37 | 46 | ``` |
| 38 | Requires: `bare` (v1.26+), `g++` (C++20), opencog installed under `/usr/local` | |
| 39 | (`libatomspace`, `libsexpr`, `libatombase`, `libatom_types`). `npm install` pulls | |
| 40 | `bare-net`/`bare-fs`/`bare-process` into a local `node_modules` (gitignored). | |
| 47 | Requires: `bare` (v1.26+), `g++` (**C++20** — opencog headers use `std::atomic::wait`), | |
| 48 | opencog under `/usr/local` (`libatomspace`/`libsexpr`/`libatombase`/`libatom_types`). | |
| 49 | `npm install` pulls `bare-net`/`bare-fs`/`bare-process`/`hypercore` into a local | |
| 50 | `node_modules` (gitignored). | |
| 41 | 51 | |
| 42 | 52 | ## Wire protocol (newline-framed) |
| 43 | 53 | | Request | Reply | Meaning | |
| 44 | 54 | |---|---|---| |
| 45 | | `store <sexpr>` | — | append one atom's s-expr to the log (fire-and-forget) | | |
| 46 | | `load` | `(<sexpr>…)\n` | whole log wrapped in one outer paren (the shape the C++ decode loop expects) | | |
| 47 | | `clear` | `ok\n` | empty the log | | |
| 48 | | `barrier` | `ok\n` | sync point (ordered stream ⇒ all prior stores are applied) | | |
| 49 | | `count` | `<n>\n` | log length (debug) | | |
| 55 | | `store <sexpr>` | — | `core.append(sexpr)` (fire-and-forget) | | |
| 56 | | `load` | `(<sexpr>…)\n` | replay whole log | | |
| 57 | | `loadat <K>` | `(<sexpr>…)\n` | replay prefix 0..K (checkout / cheap fork) | | |
| 58 | | `clear` | `ok\n` | `core.truncate(0)` | | |
| 59 | | `barrier` | `ok\n` | sync point (all prior stores applied) | | |
| 60 | | `count` | `<len>\n` | `core.length` | | |
| 50 | 61 | |
| 51 | ## Deliberately NOT here (deferred) | |
| 52 | - **Hypercore** — the log is a plain array (that is M1: append + replay + fork). | |
| 53 | - **Values** — structure only; per the synthesis rule *event-source the graph, | |
| 54 | checkpoint the Values* (`../../spec/activegraph-synthesis.md`). | |
| 55 | - **Full `HyperStorageNode : public StorageNode` + guile module** — `HyperChannel` | |
| 56 | is written to lift straight into it at M1; no atom_types/PersistSCM/CMake yet. | |
| 57 | - **Autobase multi-writer** — single client, single daemon (M4). | |
| 62 | ## Deferred (next) | |
| 63 | - **Values** — structure only; per *event-source the graph, checkpoint the Values* | |
| 64 | (`../../spec/activegraph-synthesis.md`). | |
| 65 | - **`HyperStorageNode : public StorageNode` + guile module** — packaging, not new | |
| 66 | capability: the data path is proven and `HyperChannel` is the transplant unit. | |
| 67 | Needs atom_types codegen + PersistSCM + CMake (a loadable `.so`). | |
| 68 | - **Autobase multi-writer** (M4) — two daemons, one linearized log = two agents. | |
| 69 | - **Branch-fork with divergent writes** — M1 `loadat` is a read-only checkout; | |
| 70 | true forking-with-writes rides Autobase. |
| … | … | @@ -1,41 +1,51 @@ |
| 1 | // M0 hyper-atomspace daemon (Bare). | |
| 1 | // hyper-atomspace daemon (Bare) — M1: Hypercore-backed op-log. | |
| 2 | 2 | // |
| 3 | // Purpose: prove the HyperStorageNode bridge + s-expr codec round-trip with an | |
| 4 | // in-memory op-log. NO Hypercore yet (that is M1). The daemon holds an array of | |
| 5 | // s-expr strings (one per stored atom) and can replay them on demand. | |
| 3 | // The op-log is a real Hypercore (append-only, durable on disk). store appends | |
| 4 | // one s-expr per atom; load replays the whole log; loadat replays a prefix (a | |
| 5 | // read-only checkout at seq K = "cheap fork": read fewer blocks, get an earlier | |
| 6 | // graph, no reprocessing). This is the M1 backend swap from M0's in-memory array. | |
| 7 | // | |
| 8 | // usage: bare daemon.js <socket-path> <storage-dir> | |
| 6 | 9 | // |
| 7 | 10 | // Wire protocol — newline-framed request lines over a UNIX-domain socket: |
| 8 | // store <sexpr>\n append <sexpr> to the log. NO reply (fire-and-forget, | |
| 9 | // mirrors CogSimpleStorage::storeAtom). | |
| 10 | // load\n reply one line: (<sexpr1><sexpr2>...)\n — the whole log | |
| 11 | // wrapped in a single outer paren, the exact shape the C++ | |
| 12 | // side's decode loop (cf. CogSimpleStorage::decode_atom_list) | |
| 13 | // expects. | |
| 14 | // clear\n empty the log. reply: ok\n | |
| 11 | // store <sexpr>\n append <sexpr> to the log. NO reply (fire-and-forget). | |
| 12 | // load\n reply: (<sexpr0>…<sexpr_{len-1}>)\n — whole log. | |
| 13 | // loadat <K>\n reply: (<sexpr0>…<sexpr_{K-1}>)\n — prefix/checkout @K. | |
| 14 | // clear\n truncate the log to 0. reply: ok\n | |
| 15 | 15 | // barrier\n sync point. reply: ok\n |
| 16 | // count\n reply: <n>\n (debug) | |
| 16 | // count\n reply: <len>\n | |
| 17 | 17 | // |
| 18 | // Framing note: bare-net defaults allowHalfOpen:true and only emits 'close' on an | |
| 19 | // explicit destroy (see hyper-svc UDS leak post-mortem). We create the server | |
| 20 | // allowHalfOpen:false AND destroy on 'end' so client fds are released. | |
| 18 | // All commands run on a single ordered promise-chain, so by the time a load or | |
| 19 | // barrier executes, every prior store's append has resolved. | |
| 21 | 20 | |
| 22 | 21 | import net from 'bare-net' |
| 23 | 22 | import fs from 'bare-fs' |
| 24 | 23 | import process from 'bare-process' |
| 24 | import Hypercore from 'hypercore' | |
| 25 | 25 | |
| 26 | 26 | const sockPath = process.argv[2] |
| 27 | if (!sockPath) { | |
| 28 | console.error('usage: bare daemon.js <socket-path>') | |
| 27 | const storageDir = process.argv[3] | |
| 28 | if (!sockPath || !storageDir) { | |
| 29 | console.error('usage: bare daemon.js <socket-path> <storage-dir>') | |
| 29 | 30 | process.exit(1) |
| 30 | 31 | } |
| 31 | 32 | |
| 32 | // Fresh bind: a stale socket file makes listen() fail with EADDRINUSE. | |
| 33 | // Fresh socket bind: a stale socket file makes listen() fail. | |
| 33 | 34 | try { fs.unlinkSync(sockPath) } catch (_) { /* not there — fine */ } |
| 34 | 35 | |
| 35 | /** @type {string[]} the in-memory op-log: one s-expr per stored atom. */ | |
| 36 | const log = [] | |
| 36 | const core = new Hypercore(storageDir, { valueEncoding: 'utf-8' }) | |
| 37 | await core.ready() | |
| 38 | ||
| 39 | // Read blocks [0, upto) and wrap them in one outer paren — the exact shape the | |
| 40 | // C++ decode loop expects. | |
| 41 | async function replay (upto) { | |
| 42 | const n = Math.max(0, Math.min(upto, core.length)) | |
| 43 | let body = '' | |
| 44 | for (let i = 0; i < n; i++) body += await core.get(i) | |
| 45 | return '(' + body + ')\n' | |
| 46 | } | |
| 37 | 47 | |
| 38 | function handleLine (line, sock) { | |
| 48 | async function handleLine (line, sock) { | |
| 39 | 49 | if (line.length === 0) return |
| 40 | 50 | const sp = line.indexOf(' ') |
| 41 | 51 | const verb = sp === -1 ? line : line.slice(0, sp) |
| … | … | @@ -43,20 +53,23 @@ function handleLine (line, sock) { |
| 43 | 53 | |
| 44 | 54 | switch (verb) { |
| 45 | 55 | case 'store': |
| 46 | log.push(rest) | |
| 56 | await core.append(rest) | |
| 47 | 57 | return // no reply |
| 48 | 58 | case 'load': |
| 49 | sock.write('(' + log.join('') + ')\n') | |
| 59 | sock.write(await replay(core.length)) | |
| 60 | return | |
| 61 | case 'loadat': | |
| 62 | sock.write(await replay(parseInt(rest, 10) || 0)) | |
| 50 | 63 | return |
| 51 | 64 | case 'clear': |
| 52 | log.length = 0 | |
| 65 | await core.truncate(0) | |
| 53 | 66 | sock.write('ok\n') |
| 54 | 67 | return |
| 55 | 68 | case 'barrier': |
| 56 | 69 | sock.write('ok\n') |
| 57 | 70 | return |
| 58 | 71 | case 'count': |
| 59 | sock.write(String(log.length) + '\n') | |
| 72 | sock.write(String(core.length) + '\n') | |
| 60 | 73 | return |
| 61 | 74 | default: |
| 62 | 75 | sock.write('err unknown-verb ' + verb + '\n') |
| … | … | @@ -65,29 +78,30 @@ function handleLine (line, sock) { |
| 65 | 78 | |
| 66 | 79 | const server = net.createServer({ allowHalfOpen: false }, (sock) => { |
| 67 | 80 | let buf = '' |
| 81 | // Single ordered chain: guarantees appends apply in arrival order and that a | |
| 82 | // later load/barrier observes all prior stores. | |
| 83 | let tail = Promise.resolve() | |
| 68 | 84 | sock.on('data', (chunk) => { |
| 69 | 85 | buf += chunk.toString() |
| 70 | 86 | let nl |
| 71 | 87 | while ((nl = buf.indexOf('\n')) !== -1) { |
| 72 | 88 | const line = buf.slice(0, nl) |
| 73 | 89 | buf = buf.slice(nl + 1) |
| 74 | handleLine(line, sock) | |
| 90 | tail = tail.then(() => handleLine(line, sock)).catch((e) => console.error(e)) | |
| 75 | 91 | } |
| 76 | 92 | }) |
| 77 | // Peer finished + disconnected. bare-net will not emit 'close' on its own; | |
| 78 | // destroy to release the fd (the hyper-svc leak fix). | |
| 79 | sock.on('end', () => sock.destroy()) | |
| 93 | sock.on('end', () => sock.destroy()) // release fd (hyper-svc leak fix) | |
| 80 | 94 | sock.on('error', () => sock.destroy()) |
| 81 | 95 | }) |
| 82 | 96 | |
| 83 | 97 | server.listen(sockPath, () => { |
| 84 | // Single line on stdout = readiness signal for run.sh / test harness. | |
| 85 | console.log('hyper-atomspace-m0 listening ' + sockPath) | |
| 98 | console.log('hyper-atomspace listening ' + sockPath + ' store=' + storageDir + | |
| 99 | ' len=' + core.length) | |
| 86 | 100 | }) |
| 87 | 101 | |
| 88 | // Clean shutdown so the socket file does not linger. | |
| 89 | function shutdown () { | |
| 102 | async function shutdown () { | |
| 90 | 103 | try { server.close() } catch (_) {} |
| 104 | try { await core.close() } catch (_) {} | |
| 91 | 105 | try { fs.unlinkSync(sockPath) } catch (_) {} |
| 92 | 106 | process.exit(0) |
| 93 | 107 | } |
| … | … | @@ -1,17 +1,20 @@ |
| 1 | 1 | /* |
| 2 | * m0_roundtrip.cc — M0 bridge + codec round-trip harness. | |
| 2 | * m0_roundtrip.cc — HyperStorageNode bridge harness (M0 + M1). | |
| 3 | 3 | * |
| 4 | * Proves the HyperStorageNode data path with NO Hypercore and NO full | |
| 5 | * StorageNode/guile-module boilerplate yet: build a small AtomSpace, encode each | |
| 6 | * atom with the REAL opencog s-expr codec (Sexpr::encode_atom), ship it over a | |
| 7 | * UNIX-domain socket to the Bare `hyper-atomspace` daemon (daemon.js), replay the | |
| 8 | * log back, decode with the REAL codec (Sexpr::decode_atom) into a fresh | |
| 9 | * AtomSpace, and gold-check that the two AtomSpaces are identical. | |
| 4 | * Ships a small AtomSpace over a UNIX-domain socket to the Bare `hyper-atomspace` | |
| 5 | * daemon (daemon.js), using the REAL opencog s-expr codec (Sexpr::encode_atom / | |
| 6 | * Sexpr::decode_atom), then replays the log back and gold-checks identity. | |
| 10 | 7 | * |
| 11 | * `HyperChannel` is the reusable AF_UNIX transport (adapted from | |
| 12 | * CogSimpleStorage's do_send/do_recv, TCP -> UDS). At M1 it lifts straight into | |
| 13 | * `HyperStorageNode : public StorageNode` (cf. CogChannel in cog-storage); here | |
| 14 | * main() is just the test harness around it. | |
| 8 | * `HyperChannel` is the reusable AF_UNIX transport (adapted from CogSimpleStorage | |
| 9 | * do_send/do_recv, TCP->UDS); at the guile-module step it lifts into | |
| 10 | * `HyperStorageNode : public StorageNode`. main() is the test harness. | |
| 11 | * | |
| 12 | * Modes: | |
| 13 | * full (default) clear + store + barrier + load + gold-check (live append/replay) | |
| 14 | * verify load + gold-check ONLY (run against a RESTARTED daemon on the | |
| 15 | * same Hypercore dir -> proves durable deterministic replay, M1 #1) | |
| 16 | * fork clear + store + load(full) + loadat(K) + prefix check | |
| 17 | * (checkout at seq K = cheap fork, M1 #2) | |
| 15 | 18 | * |
| 16 | 19 | * SPDX-License-Identifier: AGPL-3.0-or-later |
| 17 | 20 | */ |
| … | … | @@ -35,11 +38,10 @@ using namespace opencog; |
| 35 | 38 | |
| 36 | 39 | // --------------------------------------------------------------------------- |
| 37 | 40 | // HyperChannel — newline-framed s-expr protocol over a UNIX-domain socket. |
| 38 | // (The transport the future HyperStorageNode will embed.) | |
| 39 | 41 | class HyperChannel |
| 40 | 42 | { |
| 41 | 43 | int _fd = -1; |
| 42 | std::string _rbuf; // leftover bytes past a newline | |
| 44 | std::string _rbuf; | |
| 43 | 45 | |
| 44 | 46 | public: |
| 45 | 47 | void open(const std::string& path) |
| … | … | @@ -59,13 +61,9 @@ public: |
| 59 | 61 | + strerror(errno)); |
| 60 | 62 | } |
| 61 | 63 | |
| 62 | void close_fd() | |
| 63 | { | |
| 64 | if (_fd >= 0) { ::close(_fd); _fd = -1; } | |
| 65 | } | |
| 64 | void close_fd() { if (_fd >= 0) { ::close(_fd); _fd = -1; } } | |
| 66 | 65 | ~HyperChannel() { close_fd(); } |
| 67 | 66 | |
| 68 | // Fire-and-forget send of one line (must include its own '\n'). | |
| 69 | 67 | void send_line(const std::string& line) |
| 70 | 68 | { |
| 71 | 69 | size_t off = 0; |
| … | … | @@ -77,7 +75,6 @@ public: |
| 77 | 75 | } |
| 78 | 76 | } |
| 79 | 77 | |
| 80 | // Read exactly one '\n'-terminated line; returns it without the newline. | |
| 81 | 78 | std::string recv_line() |
| 82 | 79 | { |
| 83 | 80 | for (;;) |
| … | … | @@ -97,95 +94,137 @@ public: |
| 97 | 94 | } |
| 98 | 95 | } |
| 99 | 96 | |
| 100 | // Protocol verbs | |
| 101 | 97 | void store(const std::string& sexpr) { send_line("store " + sexpr + "\n"); } |
| 102 | 98 | void clear() { send_line("clear\n"); (void) recv_line(); } |
| 103 | 99 | void barrier() { send_line("barrier\n"); (void) recv_line(); } |
| 104 | 100 | std::string load() { send_line("load\n"); return recv_line(); } |
| 101 | std::string loadat(int k) | |
| 102 | { | |
| 103 | send_line("loadat " + std::to_string(k) + "\n"); | |
| 104 | return recv_line(); | |
| 105 | } | |
| 105 | 106 | }; |
| 106 | 107 | |
| 107 | 108 | // --------------------------------------------------------------------------- |
| 108 | // Enumerate every atom in `as`, encode each to its s-expr, return sorted vector. | |
| 109 | // Deterministic source AtomSpace. `order` returns the atoms in creation order | |
| 110 | // (nodes before the links that use them), so any prefix is a self-consistent | |
| 111 | // sub-graph — which is what makes the fork/checkout check crisp. | |
| 112 | static AtomSpacePtr build_src(HandleSeq& order) | |
| 113 | { | |
| 114 | AtomSpacePtr as = createAtomSpace(); | |
| 115 | Handle cat = as->add_node(CONCEPT_NODE, "cat"); order.push_back(cat); | |
| 116 | Handle animal = as->add_node(CONCEPT_NODE, "animal"); order.push_back(animal); | |
| 117 | order.push_back(as->add_link(INHERITANCE_LINK, cat, animal)); | |
| 118 | Handle whale = as->add_node(CONCEPT_NODE, "whale"); order.push_back(whale); | |
| 119 | Handle water = as->add_node(CONCEPT_NODE, "water"); order.push_back(water); | |
| 120 | Handle livesIn = as->add_node(PREDICATE_NODE, "lives-in"); order.push_back(livesIn); | |
| 121 | Handle lst = as->add_link(LIST_LINK, whale, water); order.push_back(lst); | |
| 122 | order.push_back(as->add_link(EVALUATION_LINK, livesIn, lst)); | |
| 123 | return as; | |
| 124 | } | |
| 125 | ||
| 126 | // Every atom in `as`, encoded and sorted (for set comparison). | |
| 109 | 127 | static std::vector<std::string> encoded_sorted(const AtomSpacePtr& as) |
| 110 | 128 | { |
| 111 | 129 | HandleSeq all; |
| 112 | as->get_handles_by_type(all, ATOM, true /* subclasses */); | |
| 130 | as->get_handles_by_type(all, ATOM, true); | |
| 113 | 131 | std::vector<std::string> out; |
| 114 | for (const Handle& h : all) | |
| 115 | out.push_back(Sexpr::encode_atom(h)); | |
| 132 | for (const Handle& h : all) out.push_back(Sexpr::encode_atom(h)); | |
| 116 | 133 | std::sort(out.begin(), out.end()); |
| 117 | 134 | return out; |
| 118 | 135 | } |
| 119 | 136 | |
| 137 | // Decode a "(<atom><atom>…)" daemon reply into a fresh AtomSpace. | |
| 138 | static AtomSpacePtr decode_reply(const std::string& reply) | |
| 139 | { | |
| 140 | AtomSpacePtr dst = createAtomSpace(); | |
| 141 | std::unordered_map<std::string, Handle> cache; | |
| 142 | size_t l = reply.find('('); | |
| 143 | size_t end = reply.rfind(')'); | |
| 144 | if (l == std::string::npos || end == std::string::npos || l >= end) | |
| 145 | return dst; | |
| 146 | l += 1; | |
| 147 | size_t r = end; | |
| 148 | while (true) | |
| 149 | { | |
| 150 | int pcnt = Sexpr::get_next_expr(reply, l, r, 0); | |
| 151 | if (l == r) break; | |
| 152 | if (0 < pcnt) break; | |
| 153 | Handle h = Sexpr::decode_atom(reply, l, r, 0, cache); | |
| 154 | dst->add_atom(h); | |
| 155 | l = r + 1; | |
| 156 | r = end; | |
| 157 | } | |
| 158 | return dst; | |
| 159 | } | |
| 160 | ||
| 120 | 161 | int main(int argc, char** argv) |
| 121 | 162 | { |
| 122 | 163 | if (argc < 2) |
| 123 | 164 | { |
| 124 | std::cerr << "usage: m0_roundtrip <socket-path>\n"; | |
| 165 | std::cerr << "usage: m0_roundtrip <socket-path> [full|verify|fork]\n"; | |
| 125 | 166 | return 2; |
| 126 | 167 | } |
| 127 | 168 | const std::string sockpath = argv[1]; |
| 169 | const std::string mode = (argc >= 3) ? argv[2] : "full"; | |
| 128 | 170 | |
| 129 | // --- 1. Build a source AtomSpace: nodes, a link, a nested Eval/List. --- | |
| 130 | AtomSpacePtr src = createAtomSpace(); | |
| 131 | Handle cat = src->add_node(CONCEPT_NODE, "cat"); | |
| 132 | Handle animal = src->add_node(CONCEPT_NODE, "animal"); | |
| 133 | src->add_link(INHERITANCE_LINK, cat, animal); | |
| 134 | ||
| 135 | Handle whale = src->add_node(CONCEPT_NODE, "whale"); | |
| 136 | Handle water = src->add_node(CONCEPT_NODE, "water"); | |
| 137 | Handle livesIn = src->add_node(PREDICATE_NODE, "lives-in"); | |
| 138 | Handle lst = src->add_link(LIST_LINK, whale, water); | |
| 139 | src->add_link(EVALUATION_LINK, livesIn, lst); | |
| 171 | HandleSeq order; | |
| 172 | AtomSpacePtr src = build_src(order); | |
| 173 | std::vector<std::string> es = encoded_sorted(src); | |
| 140 | 174 | |
| 141 | // --- 2. Store every atom over the UDS bridge. --- | |
| 142 | 175 | HyperChannel ch; |
| 143 | 176 | ch.open(sockpath); |
| 144 | ch.clear(); | |
| 145 | for (const std::string& s : encoded_sorted(src)) | |
| 146 | ch.store(s); | |
| 147 | ch.barrier(); | |
| 148 | 177 | |
| 149 | // --- 3. Replay the log; decode into a fresh AtomSpace. --- | |
| 150 | std::string reply = ch.load(); | |
| 151 | AtomSpacePtr dst = createAtomSpace(); | |
| 178 | if (mode == "verify") | |
| 152 | 179 | { |
| 153 | std::unordered_map<std::string, Handle> cache; | |
| 154 | size_t l = reply.find('('); | |
| 155 | size_t end = reply.rfind(')'); | |
| 156 | if (l != std::string::npos && end != std::string::npos && l < end) | |
| 157 | { | |
| 158 | l += 1; // step past the outer '(' | |
| 159 | size_t r = end; | |
| 160 | while (true) | |
| 161 | { | |
| 162 | int pcnt = Sexpr::get_next_expr(reply, l, r, 0); | |
| 163 | if (l == r) break; | |
| 164 | if (0 < pcnt) break; // unbalanced — malformed reply | |
| 165 | Handle h = Sexpr::decode_atom(reply, l, r, 0, cache); | |
| 166 | dst->add_atom(h); | |
| 167 | l = r + 1; | |
| 168 | r = end; | |
| 169 | } | |
| 170 | } | |
| 180 | // Load ONLY — the daemon was restarted on the same Hypercore dir. | |
| 181 | AtomSpacePtr dst = decode_reply(ch.load()); | |
| 182 | std::vector<std::string> ed = encoded_sorted(dst); | |
| 183 | bool ok = (es == ed); | |
| 184 | std::cout << "[verify] durable replay src=" << src->get_size() | |
| 185 | << " dst=" << dst->get_size() | |
| 186 | << (ok ? " GOLD #1 PASS\n" : " GOLD #1 FAIL\n"); | |
| 187 | return ok ? 0 : 1; | |
| 171 | 188 | } |
| 172 | ch.close_fd(); | |
| 173 | 189 | |
| 174 | // --- 4. Gold check: src and dst must be byte-identical atom sets. --- | |
| 175 | std::vector<std::string> es = encoded_sorted(src); | |
| 176 | std::vector<std::string> ed = encoded_sorted(dst); | |
| 177 | ||
| 178 | std::cout << "src atoms: " << src->get_size() | |
| 179 | << " dst atoms: " << dst->get_size() << "\n"; | |
| 190 | if (mode == "fork") | |
| 191 | { | |
| 192 | ch.clear(); | |
| 193 | for (const Handle& h : order) ch.store(Sexpr::encode_atom(h)); // creation order | |
| 194 | ch.barrier(); | |
| 195 | ||
| 196 | AtomSpacePtr full = decode_reply(ch.load()); | |
| 197 | AtomSpacePtr forkK = decode_reply(ch.loadat(3)); // checkout @ seq 3 | |
| 198 | ||
| 199 | std::vector<std::string> ef = encoded_sorted(forkK); | |
| 200 | std::vector<std::string> eF = encoded_sorted(full); | |
| 201 | ||
| 202 | bool szF = (full->get_size() == 8); | |
| 203 | bool szf = (forkK->get_size() == 3); // cat, animal, Inheritance(cat,animal) | |
| 204 | bool subset = std::includes(eF.begin(), eF.end(), ef.begin(), ef.end()); | |
| 205 | bool strict = (forkK->get_size() < full->get_size()); | |
| 206 | bool ok = szF && szf && subset && strict; | |
| 207 | std::cout << "[fork] full=" << full->get_size() | |
| 208 | << " checkout@3=" << forkK->get_size() | |
| 209 | << " subset=" << subset << " strict=" << strict | |
| 210 | << (ok ? " GOLD #2 PASS\n" : " GOLD #2 FAIL\n"); | |
| 211 | return ok ? 0 : 1; | |
| 212 | } | |
| 180 | 213 | |
| 214 | // mode == full : live append + replay round-trip. | |
| 215 | ch.clear(); | |
| 216 | for (const Handle& h : order) ch.store(Sexpr::encode_atom(h)); | |
| 217 | ch.barrier(); | |
| 218 | AtomSpacePtr dst = decode_reply(ch.load()); | |
| 219 | std::vector<std::string> ed = encoded_sorted(dst); | |
| 181 | 220 | bool ok = (es == ed) && (src->get_size() == dst->get_size()); |
| 221 | std::cout << "[full] live round-trip src=" << src->get_size() | |
| 222 | << " dst=" << dst->get_size() | |
| 223 | << (ok ? " PASS\n" : " FAIL\n"); | |
| 182 | 224 | if (not ok) |
| 183 | 225 | { |
| 184 | std::cout << "MISMATCH\n src:\n"; | |
| 185 | for (const auto& s : es) std::cout << " " << s << "\n"; | |
| 186 | std::cout << " dst:\n"; | |
| 187 | for (const auto& s : ed) std::cout << " " << s << "\n"; | |
| 226 | std::cout << " src:\n"; for (auto& s : es) std::cout << " " << s << "\n"; | |
| 227 | std::cout << " dst:\n"; for (auto& s : ed) std::cout << " " << s << "\n"; | |
| 188 | 228 | } |
| 189 | std::cout << (ok ? "M0 GOLD PASS" : "M0 GOLD FAIL") << "\n"; | |
| 190 | 229 | return ok ? 0 : 1; |
| 191 | 230 | } |
| … | … | @@ -10,7 +10,25 @@ |
| 10 | 10 | "dependencies": { |
| 11 | 11 | "bare-fs": "^4.7.3", |
| 12 | 12 | "bare-net": "^2.3.2", |
| 13 | "bare-process": "^4.5.0" | |
| 13 | "bare-process": "^4.5.0", | |
| 14 | "hypercore": "^11.33.5" | |
| 15 | } | |
| 16 | }, | |
| 17 | "node_modules/@hyperswarm/secret-stream": { | |
| 18 | "version": "6.9.1", | |
| 19 | "resolved": "https://registry.npmjs.org/@hyperswarm/secret-stream/-/secret-stream-6.9.1.tgz", | |
| 20 | "integrity": "sha512-xb0S5y3YJwBakD77JOGBHlBxdp63mHClZoXBYoLv+9wH8e054ESKlmQptWqjJK5dv5VMUIVYOJB4MaOpB0JdGw==", | |
| 21 | "license": "Apache-2.0", | |
| 22 | "dependencies": { | |
| 23 | "b4a": "^1.1.0", | |
| 24 | "hypercore-crypto": "^3.3.1", | |
| 25 | "noise-curve-ed": "^2.0.1", | |
| 26 | "noise-handshake": "^4.0.0", | |
| 27 | "sodium-secretstream": "^1.1.0", | |
| 28 | "sodium-universal": "^5.0.0", | |
| 29 | "streamx": "^2.14.0", | |
| 30 | "timeout-refresh": "^2.0.0", | |
| 31 | "unslab": "^1.3.0" | |
| 14 | 32 | } |
| 15 | 33 | }, |
| 16 | 34 | "node_modules/b4a": { |
| … | … | @@ -33,6 +51,50 @@ |
| 33 | 51 | "integrity": "sha512-zdc8l88eB11Jsz5rDd6sCAgv2kUFXgdrZWoMlgU6JMkfAi1/uuGFC3IEHswKbIRQTk5H3T5CMuechsXYxiaHlQ==", |
| 34 | 52 | "license": "Apache-2.0" |
| 35 | 53 | }, |
| 54 | "node_modules/bare-addon-resolve": { | |
| 55 | "version": "1.10.0", | |
| 56 | "resolved": "https://registry.npmjs.org/bare-addon-resolve/-/bare-addon-resolve-1.10.0.tgz", | |
| 57 | "integrity": "sha512-sSd0jieRJlDaODOzj0oe0RjFVC1QI0ZIjGIdPkbrTXsdVVtENg14c+lHHAhHwmWCZ2nQlMhy8jA3Y5LYPc/isA==", | |
| 58 | "license": "Apache-2.0", | |
| 59 | "dependencies": { | |
| 60 | "bare-module-resolve": "^1.10.0", | |
| 61 | "bare-semver": "^1.0.0" | |
| 62 | }, | |
| 63 | "peerDependencies": { | |
| 64 | "bare-url": "*" | |
| 65 | }, | |
| 66 | "peerDependenciesMeta": { | |
| 67 | "bare-url": { | |
| 68 | "optional": true | |
| 69 | } | |
| 70 | } | |
| 71 | }, | |
| 72 | "node_modules/bare-ansi-escapes": { | |
| 73 | "version": "2.2.3", | |
| 74 | "resolved": "https://registry.npmjs.org/bare-ansi-escapes/-/bare-ansi-escapes-2.2.3.tgz", | |
| 75 | "integrity": "sha512-02ES4/E2RbrtZSnHJ9LntBhYkLA6lPpSEeP8iqS3MccBIVhVBlEmruF1I7HZqx5Q8aiTeYfQVeqmrU9YO2yYoQ==", | |
| 76 | "license": "Apache-2.0", | |
| 77 | "dependencies": { | |
| 78 | "bare-stream": "^2.6.5" | |
| 79 | }, | |
| 80 | "peerDependencies": { | |
| 81 | "bare-buffer": "*" | |
| 82 | }, | |
| 83 | "peerDependenciesMeta": { | |
| 84 | "bare-buffer": { | |
| 85 | "optional": true | |
| 86 | } | |
| 87 | } | |
| 88 | }, | |
| 89 | "node_modules/bare-assert": { | |
| 90 | "version": "1.2.0", | |
| 91 | "resolved": "https://registry.npmjs.org/bare-assert/-/bare-assert-1.2.0.tgz", | |
| 92 | "integrity": "sha512-c6uvgvTJBspTDxtVnPgrBKmLgcpW3Fp72NVKDLg6oT4QjQbhGtvrkHMhGYMK1sh4vjBHOBmuUalyt9hSzV37fQ==", | |
| 93 | "license": "Apache-2.0", | |
| 94 | "dependencies": { | |
| 95 | "bare-inspect": "^3.1.2" | |
| 96 | } | |
| 97 | }, | |
| 36 | 98 | "node_modules/bare-dns": { |
| 37 | 99 | "version": "2.1.4", |
| 38 | 100 | "resolved": "https://registry.npmjs.org/bare-dns/-/bare-dns-2.1.4.tgz", |
| … | … | @@ -95,6 +157,36 @@ |
| 95 | 157 | "integrity": "sha512-VMb3tHo05gsnbu3OXTmkDiwTjMlOsbQmKoysKqKEyR09m77TuDrYFbj3Q5GGk10dAKsUHrnXmwCaeJqzVpB5ZA==", |
| 96 | 158 | "license": "Apache-2.0" |
| 97 | 159 | }, |
| 160 | "node_modules/bare-inspect": { | |
| 161 | "version": "3.1.4", | |
| 162 | "resolved": "https://registry.npmjs.org/bare-inspect/-/bare-inspect-3.1.4.tgz", | |
| 163 | "integrity": "sha512-jfW5KRA84o3REpI6Vr4nbvMn+hqVAw8GU1mMdRwUsY5yJovQamxYeKGVKGqdzs+8ZbG4jRzGUXP/3Ji/DnqfPg==", | |
| 164 | "license": "Apache-2.0", | |
| 165 | "dependencies": { | |
| 166 | "bare-ansi-escapes": "^2.1.0", | |
| 167 | "bare-type": "^1.0.0" | |
| 168 | }, | |
| 169 | "engines": { | |
| 170 | "bare": ">=1.18.0" | |
| 171 | } | |
| 172 | }, | |
| 173 | "node_modules/bare-module-resolve": { | |
| 174 | "version": "1.12.2", | |
| 175 | "resolved": "https://registry.npmjs.org/bare-module-resolve/-/bare-module-resolve-1.12.2.tgz", | |
| 176 | "integrity": "sha512-j+hiD5k99qec4KjJvYsI67q5AOBifmy9JG3oeMVxTmvrhn2sIdp8StrUvZu4YNgwTpO+NhniQG16N1ETDe1k5w==", | |
| 177 | "license": "Apache-2.0", | |
| 178 | "dependencies": { | |
| 179 | "bare-semver": "^1.0.0" | |
| 180 | }, | |
| 181 | "peerDependencies": { | |
| 182 | "bare-url": "*" | |
| 183 | }, | |
| 184 | "peerDependenciesMeta": { | |
| 185 | "bare-url": { | |
| 186 | "optional": true | |
| 187 | } | |
| 188 | } | |
| 189 | }, | |
| 98 | 190 | "node_modules/bare-net": { |
| 99 | 191 | "version": "2.3.2", |
| 100 | 192 | "resolved": "https://registry.npmjs.org/bare-net/-/bare-net-2.3.2.tgz", |
| … | … | @@ -161,6 +253,12 @@ |
| 161 | 253 | "bare-stdio": "^1.0.1" |
| 162 | 254 | } |
| 163 | 255 | }, |
| 256 | "node_modules/bare-semver": { | |
| 257 | "version": "1.1.0", | |
| 258 | "resolved": "https://registry.npmjs.org/bare-semver/-/bare-semver-1.1.0.tgz", | |
| 259 | "integrity": "sha512-1Hw5qJ7hXdVt3uPUqjeFTuxyvBUJauvz5A1I2jk8gzjZMHp04n//6nV9MDbG9CMw78JHY2lGV0w6s//LrASm2w==", | |
| 260 | "license": "Apache-2.0" | |
| 261 | }, | |
| 164 | 262 | "node_modules/bare-signals": { |
| 165 | 263 | "version": "4.2.0", |
| 166 | 264 | "resolved": "https://registry.npmjs.org/bare-signals/-/bare-signals-4.2.0.tgz", |
| … | … | @@ -248,6 +346,15 @@ |
| 248 | 346 | "bare": ">=1.16.0" |
| 249 | 347 | } |
| 250 | 348 | }, |
| 349 | "node_modules/bare-type": { | |
| 350 | "version": "1.1.0", | |
| 351 | "resolved": "https://registry.npmjs.org/bare-type/-/bare-type-1.1.0.tgz", | |
| 352 | "integrity": "sha512-LdtnnEEYldOc87Dr4GpsKnStStZk3zfgoEMXy8yvEZkXrcCv9RtYDrUYWFsBQHtaB0s1EUWmcvS6XmEZYIj3Bw==", | |
| 353 | "license": "Apache-2.0", | |
| 354 | "engines": { | |
| 355 | "bare": ">=1.2.0" | |
| 356 | } | |
| 357 | }, | |
| 251 | 358 | "node_modules/bare-url": { |
| 252 | 359 | "version": "2.4.5", |
| 253 | 360 | "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.5.tgz", |
| … | … | @@ -257,6 +364,35 @@ |
| 257 | 364 | "bare-path": "^3.0.0" |
| 258 | 365 | } |
| 259 | 366 | }, |
| 367 | "node_modules/big-sparse-array": { | |
| 368 | "version": "1.0.3", | |
| 369 | "resolved": "https://registry.npmjs.org/big-sparse-array/-/big-sparse-array-1.0.3.tgz", | |
| 370 | "integrity": "sha512-6RjV/3mSZORlMdpUaQ6rUSpG637cZm0//E54YYGtQg1c1O+AbZP8UTdJ/TchsDZcTVLmyWZcseBfp2HBeXUXOQ==", | |
| 371 | "license": "MIT" | |
| 372 | }, | |
| 373 | "node_modules/compact-encoding": { | |
| 374 | "version": "3.3.0", | |
| 375 | "resolved": "https://registry.npmjs.org/compact-encoding/-/compact-encoding-3.3.0.tgz", | |
| 376 | "integrity": "sha512-e64XyzlBvTRJ3iScuU/U2w25Dglkm7fhrRbrGaHIwuTlNnzjRKMaQBCVl7mJRvZg7wI5a9wX1IVTXCuaAANNkw==", | |
| 377 | "license": "Apache-2.0", | |
| 378 | "dependencies": { | |
| 379 | "b4a": "^1.3.0" | |
| 380 | } | |
| 381 | }, | |
| 382 | "node_modules/device-file": { | |
| 383 | "version": "2.3.1", | |
| 384 | "resolved": "https://registry.npmjs.org/device-file/-/device-file-2.3.1.tgz", | |
| 385 | "integrity": "sha512-bmON44lwxJPle9N2OcH4tqM44pMGZKT8G6OkzXkz0urvqQ9LKkoQdTS6w0ztYfmLdUE27R4UUmx0+y2gEz4Jug==", | |
| 386 | "license": "Apache-2.0", | |
| 387 | "dependencies": { | |
| 388 | "b4a": "^1.6.7", | |
| 389 | "bare-fs": "^4.0.1", | |
| 390 | "bare-path": "^3.0.0", | |
| 391 | "fd-lock": "^2.1.0", | |
| 392 | "fs-native-extensions": "^1.4.0", | |
| 393 | "ready-resource": "^1.2.0" | |
| 394 | } | |
| 395 | }, | |
| 260 | 396 | "node_modules/events-universal": { |
| 261 | 397 | "version": "1.0.1", |
| 262 | 398 | "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", |
| … | … | @@ -272,6 +408,379 @@ |
| 272 | 408 | "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", |
| 273 | 409 | "license": "MIT" |
| 274 | 410 | }, |
| 411 | "node_modules/fd-lock": { | |
| 412 | "version": "2.2.0", | |
| 413 | "resolved": "https://registry.npmjs.org/fd-lock/-/fd-lock-2.2.0.tgz", | |
| 414 | "integrity": "sha512-Il4jWBhjjgvUg+z8d0bC7ncXqB42caqKTUpnZ9jpcqiPmw8bkIixt7Kic1czbZPMxAQD/9kEqfZ5Dq77nSll0w==", | |
| 415 | "license": "Apache-2.0", | |
| 416 | "dependencies": { | |
| 417 | "bare-fs": "^4.5.0", | |
| 418 | "fs-native-extensions": "^1.4.4", | |
| 419 | "ready-resource": "^1.2.0", | |
| 420 | "resource-on-exit": "^1.0.0" | |
| 421 | } | |
| 422 | }, | |
| 423 | "node_modules/flat-tree": { | |
| 424 | "version": "1.13.0", | |
| 425 | "resolved": "https://registry.npmjs.org/flat-tree/-/flat-tree-1.13.0.tgz", | |
| 426 | "integrity": "sha512-fT3HIuCPwHhFgJ20QYzDHgUG0zMmFg5cHvFiFo5h+QMSJ28TihsEVY0f8HGliuO+pOzmvjMx1odToeaEWkTnyQ==", | |
| 427 | "license": "MIT" | |
| 428 | }, | |
| 429 | "node_modules/fs-native-extensions": { | |
| 430 | "version": "1.5.0", | |
| 431 | "resolved": "https://registry.npmjs.org/fs-native-extensions/-/fs-native-extensions-1.5.0.tgz", | |
| 432 | "integrity": "sha512-nuZLFm9mGCxvyi7Llww/J4OyifKCS21nEUTAmnlTZp3FObPOvA32aCedCmt4Z+8yk+caqfClNaSCfn/P7T7FLQ==", | |
| 433 | "license": "Apache-2.0", | |
| 434 | "dependencies": { | |
| 435 | "require-addon": "^1.1.0", | |
| 436 | "which-runtime": "^1.2.0" | |
| 437 | } | |
| 438 | }, | |
| 439 | "node_modules/generate-object-property": { | |
| 440 | "version": "2.0.0", | |
| 441 | "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-2.0.0.tgz", | |
| 442 | "integrity": "sha512-KwuURPyqn2Mz8DdV29pJwQu0Y7tcsbkULr82eeOcY/ZllFK6I9Wm8dsRByIu7CKWlFi9BdW1b3mcXMp/kQBQsw==", | |
| 443 | "license": "MIT", | |
| 444 | "dependencies": { | |
| 445 | "is-property": "^1.0.0" | |
| 446 | } | |
| 447 | }, | |
| 448 | "node_modules/generate-string": { | |
| 449 | "version": "1.0.1", | |
| 450 | "resolved": "https://registry.npmjs.org/generate-string/-/generate-string-1.0.1.tgz", | |
| 451 | "integrity": "sha512-IfTY0dKZM43ACyGvXkbG7De7WY7MxTS5VO6Juhe8oJKpCmrYYXoqp/cJMskkpi0k9H8wuXq0H+eI898/BCqvXg==", | |
| 452 | "license": "MIT" | |
| 453 | }, | |
| 454 | "node_modules/hypercore": { | |
| 455 | "version": "11.33.5", | |
| 456 | "resolved": "https://registry.npmjs.org/hypercore/-/hypercore-11.33.5.tgz", | |
| 457 | "integrity": "sha512-K88HURg5MvLjPxW8y6AzHDaf5rcaC81Ujaz4z8ugsl5zwucpmWRDf4YKXrIKGLSWyVSysSKywYDUYoAfMKJkWg==", | |
| 458 | "license": "MIT", | |
| 459 | "dependencies": { | |
| 460 | "@hyperswarm/secret-stream": "^6.0.0", | |
| 461 | "b4a": "^1.1.0", | |
| 462 | "bare-events": "^2.2.0", | |
| 463 | "big-sparse-array": "^1.0.3", | |
| 464 | "compact-encoding": "^3.0.0", | |
| 465 | "fast-fifo": "^1.3.0", | |
| 466 | "flat-tree": "^1.9.0", | |
| 467 | "hypercore-crypto": "^3.2.1", | |
| 468 | "hypercore-errors": "^1.5.0", | |
| 469 | "hypercore-id-encoding": "^1.2.0", | |
| 470 | "hypercore-storage": "^3.0.0", | |
| 471 | "is-options": "^1.0.1", | |
| 472 | "nanoassert": "^2.0.0", | |
| 473 | "protomux": "^3.5.0", | |
| 474 | "quickbit-universal": "^2.2.0", | |
| 475 | "random-array-iterator": "^1.0.0", | |
| 476 | "safety-catch": "^1.0.1", | |
| 477 | "sodium-universal": "^5.0.1", | |
| 478 | "streamx": "^2.12.4", | |
| 479 | "unslab": "^1.3.0", | |
| 480 | "z32": "^1.0.0" | |
| 481 | } | |
| 482 | }, | |
| 483 | "node_modules/hypercore-crypto": { | |
| 484 | "version": "3.7.0", | |
| 485 | "resolved": "https://registry.npmjs.org/hypercore-crypto/-/hypercore-crypto-3.7.0.tgz", | |
| 486 | "integrity": "sha512-SWxobptQf2V/+ZLCCDRTXqFzGfTPIkNIuDyLKXpEMfcpJ1/ec94N9aWgylHcWOHmRt14ng/KR7z0BugebWHK9Q==", | |
| 487 | "license": "MIT", | |
| 488 | "dependencies": { | |
| 489 | "b4a": "^1.6.6", | |
| 490 | "compact-encoding": "^3.0.0", | |
| 491 | "sodium-universal": "^5.0.0" | |
| 492 | } | |
| 493 | }, | |
| 494 | "node_modules/hypercore-errors": { | |
| 495 | "version": "1.5.0", | |
| 496 | "resolved": "https://registry.npmjs.org/hypercore-errors/-/hypercore-errors-1.5.0.tgz", | |
| 497 | "integrity": "sha512-5KQ/SuDxsvet+7qWA35Ay6zdD9WyAHQoyWHGcPUTbmJBd300gvNIJoi3oma7kp4TTCSzii6qYumNZe/s0j/saQ==", | |
| 498 | "license": "Apache-2.0", | |
| 499 | "dependencies": { | |
| 500 | "hypercore-id-encoding": "^1.3.0" | |
| 501 | } | |
| 502 | }, | |
| 503 | "node_modules/hypercore-id-encoding": { | |
| 504 | "version": "1.3.0", | |
| 505 | "resolved": "https://registry.npmjs.org/hypercore-id-encoding/-/hypercore-id-encoding-1.3.0.tgz", | |
| 506 | "integrity": "sha512-W6sHdGo5h7LXEsoWfKf/KfuROZmZRQDlGqJF2EPHW+noCK66Vvr0+zE6cL0vqQi18s0kQPeN7Sq3QyR0Ytc2VQ==", | |
| 507 | "license": "Apache-2.0", | |
| 508 | "dependencies": { | |
| 509 | "b4a": "^1.5.3", | |
| 510 | "z32": "^1.0.0" | |
| 511 | } | |
| 512 | }, | |
| 513 | "node_modules/hypercore-storage": { | |
| 514 | "version": "3.1.2", | |
| 515 | "resolved": "https://registry.npmjs.org/hypercore-storage/-/hypercore-storage-3.1.2.tgz", | |
| 516 | "integrity": "sha512-Iw6C0Nx9QIIyFLfALiDNBUpkWTB19gqrxiK3igfTpp0STqqbL4AO0ywrY9+Mv9ynt0PNs/hTnlTW6WRe76iOUw==", | |
| 517 | "license": "Apache-2.0", | |
| 518 | "dependencies": { | |
| 519 | "b4a": "^1.6.7", | |
| 520 | "bare-path": "^3.0.0", | |
| 521 | "compact-encoding": "^3.1.0", | |
| 522 | "device-file": "^2.1.2", | |
| 523 | "flat-tree": "^1.12.1", | |
| 524 | "hypercore-crypto": "^3.4.2", | |
| 525 | "hyperschema": "^1.21.0", | |
| 526 | "index-encoder": "^3.3.2", | |
| 527 | "resolve-reject-promise": "^1.0.0", | |
| 528 | "rocksdb-native": "^3.11.0", | |
| 529 | "scope-lock": "^1.2.4", | |
| 530 | "streamx": "^2.21.1" | |
| 531 | } | |
| 532 | }, | |
| 533 | "node_modules/hyperschema": { | |
| 534 | "version": "1.21.0", | |
| 535 | "resolved": "https://registry.npmjs.org/hyperschema/-/hyperschema-1.21.0.tgz", | |
| 536 | "integrity": "sha512-lEnIbLTUQf7w6FU6X+R3yUJhBwCzF4ewRnAaYOrPdcVWe1rbOf94PzMiXb5pBKxroCXzlrPLxVujxAsTrrHc8g==", | |
| 537 | "license": "Apache-2.0", | |
| 538 | "dependencies": { | |
| 539 | "bare-fs": "^4.0.1", | |
| 540 | "compact-encoding": "^3.0.0", | |
| 541 | "generate-object-property": "^2.0.0", | |
| 542 | "generate-string": "^1.0.1" | |
| 543 | } | |
| 544 | }, | |
| 545 | "node_modules/index-encoder": { | |
| 546 | "version": "3.5.0", | |
| 547 | "resolved": "https://registry.npmjs.org/index-encoder/-/index-encoder-3.5.0.tgz", | |
| 548 | "integrity": "sha512-idZ1cxtZz2dRV6rUiaP9Xo99UjXbSzjcMacoQmxUMu/A7fEQcNPngvwDJYeWelQUS5XFlY71/or70lKn6XnwbQ==", | |
| 549 | "license": "Apache-2.0", | |
| 550 | "dependencies": { | |
| 551 | "b4a": "^1.6.4" | |
| 552 | } | |
| 553 | }, | |
| 554 | "node_modules/is-options": { | |
| 555 | "version": "1.0.2", | |
| 556 | "resolved": "https://registry.npmjs.org/is-options/-/is-options-1.0.2.tgz", | |
| 557 | "integrity": "sha512-u+Ai74c8Q74aS8BuHwPdI1jptGOT1FQXgCq8/zv0xRuE+wRgSMEJLj8lVO8Zp9BeGb29BXY6AsNPinfqjkr7Fg==", | |
| 558 | "license": "MIT", | |
| 559 | "dependencies": { | |
| 560 | "b4a": "^1.1.1" | |
| 561 | } | |
| 562 | }, | |
| 563 | "node_modules/is-property": { | |
| 564 | "version": "1.0.2", | |
| 565 | "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", | |
| 566 | "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", | |
| 567 | "license": "MIT" | |
| 568 | }, | |
| 569 | "node_modules/nanoassert": { | |
| 570 | "version": "2.0.0", | |
| 571 | "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-2.0.0.tgz", | |
| 572 | "integrity": "sha512-7vO7n28+aYO4J+8w96AzhmU8G+Y/xpPDJz/se19ICsqj/momRbb9mh9ZUtkoJ5X3nTnPdhEJyc0qnM6yAsHBaA==", | |
| 573 | "license": "ISC" | |
| 574 | }, | |
| 575 | "node_modules/noise-curve-ed": { | |
| 576 | "version": "2.1.0", | |
| 577 | "resolved": "https://registry.npmjs.org/noise-curve-ed/-/noise-curve-ed-2.1.0.tgz", | |
| 578 | "integrity": "sha512-zAzJx+VwZM3w6EA1hTmDhJfvAnCeBQn/1FAeZ0LtGxCcCtlAK/uJXQVF/eDVUOaAZ286lHlx77WJ+qj9SmsRRg==", | |
| 579 | "license": "ISC", | |
| 580 | "dependencies": { | |
| 581 | "b4a": "^1.1.0", | |
| 582 | "nanoassert": "^2.0.0", | |
| 583 | "sodium-universal": "^5.0.0" | |
| 584 | } | |
| 585 | }, | |
| 586 | "node_modules/noise-handshake": { | |
| 587 | "version": "4.2.0", | |
| 588 | "resolved": "https://registry.npmjs.org/noise-handshake/-/noise-handshake-4.2.0.tgz", | |
| 589 | "integrity": "sha512-9O/VTNX/E2/AToyMTTDU0J/4WhaXMTdqc2DHs9vf+snoZ0cenSBq0dNYTVV1snYYEkmo6QeRrYMxtqtoYnY+LA==", | |
| 590 | "license": "Apache-2.0", | |
| 591 | "dependencies": { | |
| 592 | "b4a": "^1.1.0", | |
| 593 | "nanoassert": "^2.0.0", | |
| 594 | "sodium-universal": "^5.0.0" | |
| 595 | } | |
| 596 | }, | |
| 597 | "node_modules/protomux": { | |
| 598 | "version": "3.11.0", | |
| 599 | "resolved": "https://registry.npmjs.org/protomux/-/protomux-3.11.0.tgz", | |
| 600 | "integrity": "sha512-Ze43XaNHbL6zQ/zJwVYvue7Aj8pDB1HNnISsrFhAur3291Y+Iu2fMhOpySD73J/32BoBIUYrhl07lJsBR/fUVA==", | |
| 601 | "license": "MIT", | |
| 602 | "dependencies": { | |
| 603 | "b4a": "^1.3.1", | |
| 604 | "compact-encoding": "^3.0.0", | |
| 605 | "queue-tick": "^1.0.0", | |
| 606 | "safety-catch": "^1.0.1", | |
| 607 | "unslab": "^1.3.0" | |
| 608 | } | |
| 609 | }, | |
| 610 | "node_modules/queue-tick": { | |
| 611 | "version": "1.0.1", | |
| 612 | "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", | |
| 613 | "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", | |
| 614 | "license": "MIT" | |
| 615 | }, | |
| 616 | "node_modules/quickbit-native": { | |
| 617 | "version": "2.4.8", | |
| 618 | "resolved": "https://registry.npmjs.org/quickbit-native/-/quickbit-native-2.4.8.tgz", | |
| 619 | "integrity": "sha512-FcCcqI+nIAWGknqhtrYT5TSD7t/N+Xd8ctM+2PrIIBuwOi5hx0SxAvuPtzLIEMfT/2h9+fhBakUe2uALOHX6yw==", | |
| 620 | "license": "Apache-2.0", | |
| 621 | "optional": true, | |
| 622 | "dependencies": { | |
| 623 | "require-addon": "^1.1.0" | |
| 624 | } | |
| 625 | }, | |
| 626 | "node_modules/quickbit-universal": { | |
| 627 | "version": "2.2.0", | |
| 628 | "resolved": "https://registry.npmjs.org/quickbit-universal/-/quickbit-universal-2.2.0.tgz", | |
| 629 | "integrity": "sha512-w02i1R8n7+6pEKTud8DfF8zbFY9o7RtPlUc3jWbtCkDKvhbx/AvV7oNnz4/TcmsPGpSJS+fq5Ud6RH6+YPvSGg==", | |
| 630 | "license": "ISC", | |
| 631 | "dependencies": { | |
| 632 | "b4a": "^1.6.0", | |
| 633 | "simdle-universal": "^1.1.0" | |
| 634 | }, | |
| 635 | "optionalDependencies": { | |
| 636 | "quickbit-native": "^2.2.0" | |
| 637 | } | |
| 638 | }, | |
| 639 | "node_modules/random-array-iterator": { | |
| 640 | "version": "1.0.0", | |
| 641 | "resolved": "https://registry.npmjs.org/random-array-iterator/-/random-array-iterator-1.0.0.tgz", | |
| 642 | "integrity": "sha512-u7xCM93XqKEvPTP6xZp2ehttcAemKnh73oKNf1FvzuVCfpt6dILDt1Kxl1LeBjm2iNIeR49VGFhy4Iz3yOun+Q==", | |
| 643 | "license": "MIT" | |
| 644 | }, | |
| 645 | "node_modules/ready-resource": { | |
| 646 | "version": "1.2.0", | |
| 647 | "resolved": "https://registry.npmjs.org/ready-resource/-/ready-resource-1.2.0.tgz", | |
| 648 | "integrity": "sha512-nfcco/8iAFV0M+2PYnmIc+/xY0iRb35d42HFHQ7AfjulbGEAFa+XWpByfwSyeVeiBoMLLFVMv1HixxNCqzSQ1g==", | |
| 649 | "license": "MIT", | |
| 650 | "dependencies": { | |
| 651 | "bare-events": "^2.2.0" | |
| 652 | } | |
| 653 | }, | |
| 654 | "node_modules/refcounter": { | |
| 655 | "version": "1.0.0", | |
| 656 | "resolved": "https://registry.npmjs.org/refcounter/-/refcounter-1.0.0.tgz", | |
| 657 | "integrity": "sha512-1WosVzUy0kPUaPMEtlNDwm99UsteALIhXXR8rerELoa63WkYIXAl0hxgwPFrIYBRWZPGUyekQ04FRtPJ7dHk9w==", | |
| 658 | "license": "Apache-2.0" | |
| 659 | }, | |
| 660 | "node_modules/require-addon": { | |
| 661 | "version": "1.2.0", | |
| 662 | "resolved": "https://registry.npmjs.org/require-addon/-/require-addon-1.2.0.tgz", | |
| 663 | "integrity": "sha512-VNPDZlYgIYQwWp9jMTzljx+k0ZtatKlcvOhktZ/anNPI3dQ9NXk7cq2U4iJ1wd9IrytRnYhyEocFWbkdPb+MYA==", | |
| 664 | "license": "Apache-2.0", | |
| 665 | "dependencies": { | |
| 666 | "bare-addon-resolve": "^1.3.0" | |
| 667 | }, | |
| 668 | "engines": { | |
| 669 | "bare": ">=1.10.0" | |
| 670 | } | |
| 671 | }, | |
| 672 | "node_modules/resolve-reject-promise": { | |
| 673 | "version": "1.1.0", | |
| 674 | "resolved": "https://registry.npmjs.org/resolve-reject-promise/-/resolve-reject-promise-1.1.0.tgz", | |
| 675 | "integrity": "sha512-LWsTOA91AqzBTjSGgX79Tc130pwcBK6xjpJEO+qRT5IKZ6bGnHKcc8QL3upUBcWuU8OTIDzKK2VNSwmmlqvAVg==", | |
| 676 | "license": "MIT" | |
| 677 | }, | |
| 678 | "node_modules/resource-on-exit": { | |
| 679 | "version": "1.0.0", | |
| 680 | "resolved": "https://registry.npmjs.org/resource-on-exit/-/resource-on-exit-1.0.0.tgz", | |
| 681 | "integrity": "sha512-ViJwJAknCkLRJRPR+9SISQQ7R5eRgtdIHLJsM2hHx1MweAJbJxJ5XnMjjq0Lc7ZGv44ufzAqds1nKxiVkdy4ag==", | |
| 682 | "license": "Apache-2.0" | |
| 683 | }, | |
| 684 | "node_modules/rocksdb-native": { | |
| 685 | "version": "3.17.1", | |
| 686 | "resolved": "https://registry.npmjs.org/rocksdb-native/-/rocksdb-native-3.17.1.tgz", | |
| 687 | "integrity": "sha512-33hbNSx57vbdM/kyP6dozy1eRLeiN69kTNcbPZ6AMkOjFtSY3G6O94TxKNgUIq53IQzFP6kClriKHeu4mu1q6w==", | |
| 688 | "license": "Apache-2.0", | |
| 689 | "dependencies": { | |
| 690 | "compact-encoding": "^3.0.0", | |
| 691 | "ready-resource": "^1.0.0", | |
| 692 | "refcounter": "^1.0.0", | |
| 693 | "require-addon": "^1.0.2", | |
| 694 | "resolve-reject-promise": "^1.1.0", | |
| 695 | "signal-promise": "^1.0.3", | |
| 696 | "streamx": "^2.16.1" | |
| 697 | }, | |
| 698 | "engines": { | |
| 699 | "bare": ">=1.16.0" | |
| 700 | } | |
| 701 | }, | |
| 702 | "node_modules/safety-catch": { | |
| 703 | "version": "1.0.3", | |
| 704 | "resolved": "https://registry.npmjs.org/safety-catch/-/safety-catch-1.0.3.tgz", | |
| 705 | "integrity": "sha512-Zq+J1TefpoEq/HTUabo0YXX5MNvttjWYODGohgPBO2jfko8Wqx3JYMgE823szDFVamdH5PlpByvfiWScTdSYDA==", | |
| 706 | "license": "MIT" | |
| 707 | }, | |
| 708 | "node_modules/scope-lock": { | |
| 709 | "version": "1.2.4", | |
| 710 | "resolved": "https://registry.npmjs.org/scope-lock/-/scope-lock-1.2.4.tgz", | |
| 711 | "integrity": "sha512-BpSd8VCuCxW9ZitcdIC/vjs3gMaP9bRBL5nkHcyfX2VrS52n13/rHuBA2xJ/S/4DPuRdAO/Bk8pWd8eD/gHCIA==", | |
| 712 | "license": "Apache-2.0" | |
| 713 | }, | |
| 714 | "node_modules/signal-promise": { | |
| 715 | "version": "1.0.3", | |
| 716 | "resolved": "https://registry.npmjs.org/signal-promise/-/signal-promise-1.0.3.tgz", | |
| 717 | "integrity": "sha512-WBgv0UnIq2C+Aeh0/n+IRpP6967eIx9WpynTUoiW3isPpfe1zu2LJzyfXdo9Tgef8yR/sGjcMvoUXD7EYdiz+g==", | |
| 718 | "license": "MIT" | |
| 719 | }, | |
| 720 | "node_modules/simdle-native": { | |
| 721 | "version": "1.3.9", | |
| 722 | "resolved": "https://registry.npmjs.org/simdle-native/-/simdle-native-1.3.9.tgz", | |
| 723 | "integrity": "sha512-Isc8sP4OiiIU0mpslD4GHEnR0VQWvR/54WN7YtwEDkNdTJVWtpmvsSvsgRlw5BNGxdYXlVRegdnrSu10H/PhvA==", | |
| 724 | "license": "Apache-2.0", | |
| 725 | "optional": true, | |
| 726 | "dependencies": { | |
| 727 | "b4a": "^1.6.0", | |
| 728 | "require-addon": "^1.1.0" | |
| 729 | } | |
| 730 | }, | |
| 731 | "node_modules/simdle-universal": { | |
| 732 | "version": "1.1.2", | |
| 733 | "resolved": "https://registry.npmjs.org/simdle-universal/-/simdle-universal-1.1.2.tgz", | |
| 734 | "integrity": "sha512-3n3w1bs+uwgHKQjt6arez83EywNlhZzYvNOhvAASTl/8KqNIcqr6aHyGt3JRlfuUC7iB0tomJRPlJ2cRGIpBzA==", | |
| 735 | "license": "ISC", | |
| 736 | "dependencies": { | |
| 737 | "b4a": "^1.6.0" | |
| 738 | }, | |
| 739 | "optionalDependencies": { | |
| 740 | "simdle-native": "^1.1.1" | |
| 741 | } | |
| 742 | }, | |
| 743 | "node_modules/sodium-native": { | |
| 744 | "version": "5.1.0", | |
| 745 | "resolved": "https://registry.npmjs.org/sodium-native/-/sodium-native-5.1.0.tgz", | |
| 746 | "integrity": "sha512-3RxgyWyJlhTsABPnJVpCI5CoTDANZTqqFrEPqr+kjfnRaBihpVtMUE3yTF40ukdoB1APXeoBNKF3MzZAIHg39g==", | |
| 747 | "license": "MIT", | |
| 748 | "dependencies": { | |
| 749 | "bare-assert": "^1.2.0", | |
| 750 | "require-addon": "^1.1.0", | |
| 751 | "which-runtime": "^1.2.1" | |
| 752 | }, | |
| 753 | "engines": { | |
| 754 | "bare": ">=1.16.0" | |
| 755 | } | |
| 756 | }, | |
| 757 | "node_modules/sodium-secretstream": { | |
| 758 | "version": "1.2.0", | |
| 759 | "resolved": "https://registry.npmjs.org/sodium-secretstream/-/sodium-secretstream-1.2.0.tgz", | |
| 760 | "integrity": "sha512-q/DbraNFXm1KfCiiZvapmz5UC3OlpirYFIvBK2MhGaOFSb3gRyk8OXTi17UI9SGfshQNCpsVvlopogbzZNyW6Q==", | |
| 761 | "license": "MIT", | |
| 762 | "dependencies": { | |
| 763 | "b4a": "^1.1.1", | |
| 764 | "sodium-universal": "^5.0.0" | |
| 765 | } | |
| 766 | }, | |
| 767 | "node_modules/sodium-universal": { | |
| 768 | "version": "5.0.1", | |
| 769 | "resolved": "https://registry.npmjs.org/sodium-universal/-/sodium-universal-5.0.1.tgz", | |
| 770 | "integrity": "sha512-rv+aH+tnKB5H0MAc2UadHShLMslpJsc4wjdnHRtiSIEYpOetCgu8MS4ExQRia+GL/MK3uuCyZPeEsi+J3h+Q+Q==", | |
| 771 | "license": "MIT", | |
| 772 | "dependencies": { | |
| 773 | "sodium-native": "^5.0.1" | |
| 774 | }, | |
| 775 | "peerDependencies": { | |
| 776 | "sodium-javascript": "~0.8.0" | |
| 777 | }, | |
| 778 | "peerDependenciesMeta": { | |
| 779 | "sodium-javascript": { | |
| 780 | "optional": true | |
| 781 | } | |
| 782 | } | |
| 783 | }, | |
| 275 | 784 | "node_modules/streamx": { |
| 276 | 785 | "version": "2.28.0", |
| 277 | 786 | "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", |
| … | … | @@ -300,6 +809,36 @@ |
| 300 | 809 | "dependencies": { |
| 301 | 810 | "b4a": "^1.6.4" |
| 302 | 811 | } |
| 812 | }, | |
| 813 | "node_modules/timeout-refresh": { | |
| 814 | "version": "2.0.1", | |
| 815 | "resolved": "https://registry.npmjs.org/timeout-refresh/-/timeout-refresh-2.0.1.tgz", | |
| 816 | "integrity": "sha512-SVqEcMZBsZF9mA78rjzCrYrUs37LMJk3ShZ851ygZYW1cMeIjs9mL57KO6Iv5mmjSQnOe/29/VAfGXo+oRCiVw==", | |
| 817 | "license": "MIT" | |
| 818 | }, | |
| 819 | "node_modules/unslab": { | |
| 820 | "version": "1.3.0", | |
| 821 | "resolved": "https://registry.npmjs.org/unslab/-/unslab-1.3.0.tgz", | |
| 822 | "integrity": "sha512-YATkfKAFj47kTzmiQrWXMyRvaVrHsW6MEALa4bm+FhiA2YG4oira+Z3DXN6LrYOYn2Y8eO94Lwl9DOHjs1FpoQ==", | |
| 823 | "license": "Apache-2.0", | |
| 824 | "dependencies": { | |
| 825 | "b4a": "^1.6.6" | |
| 826 | } | |
| 827 | }, | |
| 828 | "node_modules/which-runtime": { | |
| 829 | "version": "1.4.0", | |
| 830 | "resolved": "https://registry.npmjs.org/which-runtime/-/which-runtime-1.4.0.tgz", | |
| 831 | "integrity": "sha512-0ugbP4CJW4e2D20jvEcC4973dCgIaHI4Rw1PT+26U9zEve7FyYdWAIwUnoeOYvoCfn+wXHoHTKb1KhkYlb60Pw==", | |
| 832 | "license": "Apache-2.0" | |
| 833 | }, | |
| 834 | "node_modules/z32": { | |
| 835 | "version": "1.1.0", | |
| 836 | "resolved": "https://registry.npmjs.org/z32/-/z32-1.1.0.tgz", | |
| 837 | "integrity": "sha512-1WUHy+VS6d0HPNspDxvLssBbeQjXMjSnpv0vH82vRAUfg847NmX3OXozp/hRP5jPhxBbrVzrgvAt+UsGNzRFQQ==", | |
| 838 | "license": "MIT", | |
| 839 | "dependencies": { | |
| 840 | "b4a": "^1.5.3" | |
| 841 | } | |
| 303 | 842 | } |
| 304 | 843 | } |
| 305 | 844 | } |
| … | … | @@ -7,6 +7,7 @@ |
| 7 | 7 | "dependencies": { |
| 8 | 8 | "bare-fs": "^4.7.3", |
| 9 | 9 | "bare-net": "^2.3.2", |
| 10 | "bare-process": "^4.5.0" | |
| 10 | "bare-process": "^4.5.0", | |
| 11 | "hypercore": "^11.33.5" | |
| 11 | 12 | } |
| 12 | 13 | } |
| … | … | @@ -1,40 +1,65 @@ |
| 1 | 1 | #!/usr/bin/env bash |
| 2 | # M0 end-to-end: build the C++ harness, start the Bare daemon, round-trip a small | |
| 3 | # AtomSpace through the UDS bridge, gold-check identity. Exits 0 on PASS. | |
| 2 | # HyperStorageNode bridge — M0 + M1 end-to-end. | |
| 3 | # full : live append + replay round-trip over a real Hypercore | |
| 4 | # verify : restart the daemon on the SAME Hypercore dir, replay -> GOLD #1 | |
| 5 | # (durable deterministic replay) | |
| 6 | # fork : checkout at seq K (read fewer blocks -> earlier graph) -> GOLD #2 | |
| 7 | # Exits 0 iff all three pass. | |
| 4 | 8 | set -euo pipefail |
| 5 | 9 | cd "$(dirname "$0")" |
| 6 | 10 | |
| 7 | # Short socket path: AF_UNIX sun_path caps at ~108 bytes, so NOT the scratchpad. | |
| 8 | SOCK="$(mktemp -u /tmp/m0-XXXXXX.sock)" | |
| 9 | DLOG="$(mktemp /tmp/m0-daemon-XXXXXX.log)" | |
| 11 | # Short paths: AF_UNIX sun_path caps at ~108 bytes. | |
| 12 | WORK="$(mktemp -d /tmp/m1-XXXXXX)" | |
| 13 | SOCK="$WORK/svc.sock" | |
| 14 | STORE1="$WORK/store1" | |
| 15 | STORE2="$WORK/store2" | |
| 10 | 16 | DPID="" |
| 11 | 17 | |
| 12 | 18 | cleanup() { |
| 13 | # `bare` is an nvm shim that execs a child runtime, so killing $DPID alone can | |
| 14 | # leave the child. Also match the daemon by its unique (mktemp) socket path. | |
| 19 | # `bare` is an nvm shim that execs a child runtime; kill $DPID AND match the | |
| 20 | # daemon by its unique socket path. | |
| 15 | 21 | [ -n "$DPID" ] && kill -9 "$DPID" 2>/dev/null || true |
| 16 | pkill -9 -f "daemon.js ${SOCK}\$" 2>/dev/null || true | |
| 17 | rm -f "$SOCK" "$DLOG" | |
| 22 | pkill -9 -f "daemon.js $SOCK" 2>/dev/null || true | |
| 23 | rm -rf "$WORK" | |
| 18 | 24 | } |
| 19 | 25 | trap cleanup EXIT |
| 20 | 26 | |
| 21 | # 1. deps + build | |
| 22 | [ -d node_modules/bare-net ] || npm install --silent | |
| 27 | start_daemon() { # $1=storage-dir | |
| 28 | bare daemon.js "$SOCK" "$1" > "$WORK/d.log" 2>&1 & | |
| 29 | DPID=$! | |
| 30 | for _ in $(seq 1 100); do | |
| 31 | grep -q listening "$WORK/d.log" && return 0 | |
| 32 | sleep 0.05 | |
| 33 | done | |
| 34 | echo "daemon failed to start:"; cat "$WORK/d.log"; return 1 | |
| 35 | } | |
| 36 | stop_daemon() { | |
| 37 | [ -n "$DPID" ] && kill -9 "$DPID" 2>/dev/null || true | |
| 38 | pkill -9 -f "daemon.js $SOCK" 2>/dev/null || true | |
| 39 | DPID="" | |
| 40 | sleep 0.2 # let the core close and the fd release before rebind | |
| 41 | } | |
| 42 | ||
| 43 | # deps + build | |
| 44 | [ -d node_modules/hypercore ] || npm install --silent | |
| 23 | 45 | g++ -std=gnu++20 -O2 -I/usr/local/include m0_roundtrip.cc \ |
| 24 | 46 | -L/usr/local/lib/opencog -Wl,-rpath,/usr/local/lib/opencog \ |
| 25 | 47 | -latomspace -lsexpr -latombase -latom_types \ |
| 26 | 48 | -o m0_roundtrip |
| 27 | 49 | |
| 28 | # 2. start the Bare daemon, wait for its readiness line | |
| 29 | bare daemon.js "$SOCK" > "$DLOG" 2>&1 & | |
| 30 | DPID=$! | |
| 31 | for _ in $(seq 1 100); do | |
| 32 | grep -q listening "$DLOG" && break | |
| 33 | sleep 0.05 | |
| 34 | done | |
| 35 | if ! grep -q listening "$DLOG"; then | |
| 36 | echo "daemon failed to start:"; cat "$DLOG"; exit 1 | |
| 37 | fi | |
| 38 | ||
| 39 | # 3. run the harness (build src AtomSpace -> UDS -> replay -> gold check) | |
| 40 | ./m0_roundtrip "$SOCK" | |
| 50 | # Phase 1 — live append + replay onto Hypercore STORE1 | |
| 51 | start_daemon "$STORE1" | |
| 52 | ./m0_roundtrip "$SOCK" full | |
| 53 | stop_daemon | |
| 54 | ||
| 55 | # Phase 2 — restart on the SAME store, replay only (durable determinism, GOLD #1) | |
| 56 | start_daemon "$STORE1" | |
| 57 | ./m0_roundtrip "$SOCK" verify | |
| 58 | stop_daemon | |
| 59 | ||
| 60 | # Phase 3 — fresh store, fork/checkout at seq K (GOLD #2) | |
| 61 | start_daemon "$STORE2" | |
| 62 | ./m0_roundtrip "$SOCK" fork | |
| 63 | stop_daemon | |
| 64 | ||
| 65 | echo "M1 ALL PASS" |