@augmentd-labs/canvas-edge 0.0.0-stage → 0.4.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/LICENSE +661 -0
- package/NOTICE +112 -0
- package/README.md +76 -2
- package/bin/canvas-edge +2 -0
- package/bin/healthcheck.mjs +9 -0
- package/node_modules/canvas-stored/LICENSE +661 -0
- package/node_modules/canvas-stored/NOTICE +85 -0
- package/node_modules/canvas-stored/README.md +472 -0
- package/node_modules/canvas-stored/package.json +26 -0
- package/node_modules/canvas-stored/src/backends/BackendManager.js +60 -0
- package/node_modules/canvas-stored/src/backends/StorageBackend.js +72 -0
- package/node_modules/canvas-stored/src/backends/cacache/index.js +130 -0
- package/node_modules/canvas-stored/src/backends/canvas/index.js +722 -0
- package/node_modules/canvas-stored/src/backends/file/index.js +658 -0
- package/node_modules/canvas-stored/src/backends/gdrive/index.js +924 -0
- package/node_modules/canvas-stored/src/backends/http/index.js +47 -0
- package/node_modules/canvas-stored/src/backends/s3/index.js +25 -0
- package/node_modules/canvas-stored/src/cache/index.js +65 -0
- package/node_modules/canvas-stored/src/extractors/audio.js +53 -0
- package/node_modules/canvas-stored/src/extractors/image.js +90 -0
- package/node_modules/canvas-stored/src/extractors/index.js +41 -0
- package/node_modules/canvas-stored/src/extractors/text.js +65 -0
- package/node_modules/canvas-stored/src/index/index.js +360 -0
- package/node_modules/canvas-stored/src/index.js +2058 -0
- package/node_modules/canvas-stored/src/sync/JobQueue.js +0 -0
- package/node_modules/canvas-stored/src/sync/Ledger.js +138 -0
- package/node_modules/canvas-stored/src/sync/Mirror.js +995 -0
- package/node_modules/canvas-stored/src/sync/SyncQueue.js +111 -0
- package/node_modules/canvas-stored/src/sync/keys.js +140 -0
- package/node_modules/canvas-stored/src/sync/worker.js +52 -0
- package/node_modules/canvas-stored/src/utils/checksum.js +32 -0
- package/node_modules/canvas-stored/src/utils/common.js +37 -0
- package/node_modules/canvas-stored/src/utils/mime.js +107 -0
- package/node_modules/canvas-stored/src/utils/mount.js +99 -0
- package/package.json +49 -4
- package/src/EdgeClient.js +129 -0
- package/src/control.js +48 -0
- package/src/daemon.js +88 -0
- package/src/env.js +182 -0
- package/src/fuse-runtime.js +173 -0
- package/src/index.js +26 -0
- package/src/mirror-runtime.js +232 -0
- package/src/remote-config.js +69 -0
- package/src/runtime.js +61 -0
|
Binary file
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { open } from 'lmdb';
|
|
2
|
+
import Debug from 'debug';
|
|
3
|
+
|
|
4
|
+
const debug = Debug('stored:sync:ledger');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Per-mirror base ledger — what the device last agreed with the hub.
|
|
8
|
+
*
|
|
9
|
+
* One LMDB sub-database (`mirror`, in the stored index env) keyed by
|
|
10
|
+
* `<kind>/<mirror>/<key>`:
|
|
11
|
+
* - `base/<mirror>/<key>` → `{ sha256, size, mtime, remoteSeq, docId, version, at }`
|
|
12
|
+
* - `cursor/<mirror>` → hub change-feed seq reconciled up to
|
|
13
|
+
* - `skip/<mirror>/<key>` → `{ reason, ts }` (keys the mirror refuses)
|
|
14
|
+
* - `state/<mirror>` → free-form engine state (instanceId, head, …)
|
|
15
|
+
*
|
|
16
|
+
* Keys and digests are the identity for reconciliation. `docId`/`version`
|
|
17
|
+
* (the hub document behind the key and its row version, when the hub told
|
|
18
|
+
* us) are protection evidence only — what this replica reports as applied —
|
|
19
|
+
* never an input to the three-way decision.
|
|
20
|
+
*/
|
|
21
|
+
export default class Ledger {
|
|
22
|
+
#db;
|
|
23
|
+
#own = null;
|
|
24
|
+
#mirror;
|
|
25
|
+
|
|
26
|
+
constructor({ index = null, path = null, mirror, name = 'mirror' } = {}) {
|
|
27
|
+
if (!mirror) throw new Error('Ledger requires a mirror id');
|
|
28
|
+
this.#mirror = String(mirror);
|
|
29
|
+
if (index && typeof index.openDB === 'function') {
|
|
30
|
+
this.#db = index.openDB(name);
|
|
31
|
+
} else if (path) {
|
|
32
|
+
this.#own = open({ path, name, compression: true });
|
|
33
|
+
this.#db = this.#own;
|
|
34
|
+
} else {
|
|
35
|
+
throw new Error('Ledger requires { index } or { path }');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get mirror() { return this.#mirror; }
|
|
40
|
+
|
|
41
|
+
#k(kind, key = null) { return key == null ? `${kind}/${this.#mirror}` : `${kind}/${this.#mirror}/${key}`; }
|
|
42
|
+
|
|
43
|
+
// ── base ─────────────────────────────────────────────────────────────
|
|
44
|
+
|
|
45
|
+
getBase(key) { return this.#db.get(this.#k('base', key)) || null; }
|
|
46
|
+
|
|
47
|
+
setBase(key, { sha256, size = null, mtime = null, remoteSeq = 0, docId = null, version = null } = {}) {
|
|
48
|
+
if (!sha256) throw new Error(`Ledger.setBase(${key}): sha256 required`);
|
|
49
|
+
const int = (v) => (Number.isInteger(Number(v)) && Number(v) > 0 ? Number(v) : null);
|
|
50
|
+
const base = { sha256: String(sha256).toLowerCase(), size, mtime, remoteSeq: Number(remoteSeq) || 0, docId: int(docId), version: int(version), at: Date.now() };
|
|
51
|
+
this.#db.putSync(this.#k('base', key), base);
|
|
52
|
+
debug(`base ${key} = ${base.sha256.slice(0, 12)} (seq ${base.remoteSeq})`);
|
|
53
|
+
return base;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
removeBase(key) {
|
|
57
|
+
const had = this.#db.get(this.#k('base', key));
|
|
58
|
+
if (had) this.#db.removeSync(this.#k('base', key));
|
|
59
|
+
return !!had;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Re-key a base (rename): returns the moved base or null. */
|
|
63
|
+
moveBase(from, to, patch = {}) {
|
|
64
|
+
return this.#db.transactionSync(() => {
|
|
65
|
+
const base = this.#db.get(this.#k('base', from));
|
|
66
|
+
if (!base) return null;
|
|
67
|
+
this.#db.removeSync(this.#k('base', from));
|
|
68
|
+
const next = { ...base, ...patch, at: Date.now() };
|
|
69
|
+
this.#db.putSync(this.#k('base', to), next);
|
|
70
|
+
return next;
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** All bases in key order: yields `[key, base]`. */
|
|
75
|
+
*bases(prefix = '') {
|
|
76
|
+
const start = this.#k('base', prefix);
|
|
77
|
+
const end = `${this.#k('base', prefix)}`;
|
|
78
|
+
const head = `${this.#k('base')}/`;
|
|
79
|
+
for (const { key, value } of this.#db.getRange({ start, end })) {
|
|
80
|
+
if (typeof key !== 'string' || !key.startsWith(head)) continue;
|
|
81
|
+
yield [key.slice(head.length), value];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ── cursor / state ───────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
get cursor() { const v = this.#db.get(this.#k('cursor')); return v == null ? null : Number(v); }
|
|
88
|
+
set cursor(seq) { this.#db.putSync(this.#k('cursor'), Number(seq) || 0); }
|
|
89
|
+
|
|
90
|
+
getState() { return this.#db.get(this.#k('state')) || {}; }
|
|
91
|
+
setState(patch) {
|
|
92
|
+
const next = { ...this.getState(), ...patch };
|
|
93
|
+
this.#db.putSync(this.#k('state'), next);
|
|
94
|
+
return next;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ── skips ────────────────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
skip(key, reason) {
|
|
100
|
+
const entry = { reason: String(reason), ts: Date.now() };
|
|
101
|
+
this.#db.putSync(this.#k('skip', key), entry);
|
|
102
|
+
return entry;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
unskip(key) {
|
|
106
|
+
const had = this.#db.get(this.#k('skip', key));
|
|
107
|
+
if (had) this.#db.removeSync(this.#k('skip', key));
|
|
108
|
+
return !!had;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
isSkipped(key) { return !!this.#db.get(this.#k('skip', key)); }
|
|
112
|
+
|
|
113
|
+
*skips() {
|
|
114
|
+
const head = `${this.#k('skip')}/`;
|
|
115
|
+
for (const { key, value } of this.#db.getRange({ start: head, end: `${head}` })) {
|
|
116
|
+
if (typeof key !== 'string' || !key.startsWith(head)) continue;
|
|
117
|
+
yield [key.slice(head.length), value];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** Wipe this mirror's ledger (bases, cursor, skips, state). */
|
|
122
|
+
clear() {
|
|
123
|
+
this.#db.transactionSync(() => {
|
|
124
|
+
for (const kind of ['base', 'skip']) {
|
|
125
|
+
const head = `${this.#k(kind)}/`;
|
|
126
|
+
for (const { key } of this.#db.getRange({ start: head, end: `${head}` })) {
|
|
127
|
+
if (typeof key === 'string' && key.startsWith(head)) this.#db.removeSync(key);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
this.#db.removeSync(this.#k('cursor'));
|
|
131
|
+
this.#db.removeSync(this.#k('state'));
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
close() {
|
|
136
|
+
if (this.#own) { this.#own.close(); this.#own = null; }
|
|
137
|
+
}
|
|
138
|
+
}
|