@totemsdk/omnia-host 0.11.0 → 0.12.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/dist/lifecycle.d.ts +3 -0
- package/dist/lifecycle.js +24 -8
- package/dist/stores/persistent-map.d.ts +31 -0
- package/dist/stores/persistent-map.js +172 -0
- package/package.json +5 -5
package/dist/lifecycle.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { OmniaHostConfig } from './config.js';
|
|
2
2
|
import { ObservationBus } from './observe/bus.js';
|
|
3
|
+
import { SyncSqliteStore } from '@totemsdk/storage/sqlite-sync';
|
|
3
4
|
import type { ObservationStore } from './observe/types.js';
|
|
4
5
|
import type { OmniaSwarm, ChannelSigner, WotsLeaseProviderLike, MinimalChainProvider } from '@totemsdk/omnia';
|
|
5
6
|
import { createOmniaSwarm } from '@totemsdk/omnia';
|
|
@@ -55,6 +56,8 @@ export interface OmniaHostDependencies {
|
|
|
55
56
|
/** Observation feed: inject a store/bus (tests/embedding) or disable it. */
|
|
56
57
|
observationStore?: ObservationStore;
|
|
57
58
|
observationBus?: ObservationBus;
|
|
59
|
+
/** Synchronous storage adapter for durable factories/splices/VTXOs/pools. */
|
|
60
|
+
hostStateStore?: SyncSqliteStore;
|
|
58
61
|
observe?: boolean;
|
|
59
62
|
networkId?: string;
|
|
60
63
|
sourceNodeId?: string;
|
package/dist/lifecycle.js
CHANGED
|
@@ -9,6 +9,8 @@ const bus_js_1 = require("./observe/bus.js");
|
|
|
9
9
|
const emitter_js_1 = require("./observe/emitter.js");
|
|
10
10
|
const store_js_1 = require("./observe/store.js");
|
|
11
11
|
const snapshot_js_1 = require("./observe/snapshot.js");
|
|
12
|
+
const sqlite_sync_1 = require("@totemsdk/storage/sqlite-sync");
|
|
13
|
+
const persistent_map_js_1 = require("./stores/persistent-map.js");
|
|
12
14
|
const omnia_1 = require("@totemsdk/omnia");
|
|
13
15
|
const sqlite_store_js_1 = require("./stores/sqlite-store.js");
|
|
14
16
|
const operations_js_1 = require("./stores/operations.js");
|
|
@@ -37,6 +39,8 @@ function createOmniaHost(config, dependencies = {}) {
|
|
|
37
39
|
let ownedOperationStore;
|
|
38
40
|
let observationStore = dependencies.observationStore;
|
|
39
41
|
let ownedObservationStore;
|
|
42
|
+
let hostStateStore = dependencies.hostStateStore;
|
|
43
|
+
let ownedHostStateStore;
|
|
40
44
|
const observationBus = dependencies.observationBus ?? new bus_js_1.ObservationBus();
|
|
41
45
|
let observation;
|
|
42
46
|
let controlServer = dependencies.controlServer;
|
|
@@ -90,6 +94,16 @@ function createOmniaHost(config, dependencies = {}) {
|
|
|
90
94
|
sourceNodeId: config.sourceNodeId ?? dependencies.sourceNodeId ?? config.localPartyId ?? 'omnia-host',
|
|
91
95
|
});
|
|
92
96
|
}
|
|
97
|
+
if (!hostStateStore) {
|
|
98
|
+
try {
|
|
99
|
+
ownedHostStateStore = new sqlite_sync_1.SyncSqliteStore(config.dbPath);
|
|
100
|
+
hostStateStore = ownedHostStateStore;
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
console.error('[omnia-host] host-state store disabled:', err instanceof Error ? err.message : String(err));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const hostMap = (namespace, injected) => injected ?? (hostStateStore ? new persistent_map_js_1.PersistentMap(hostStateStore, namespace) : new Map());
|
|
93
107
|
if (!swarm) {
|
|
94
108
|
const relay = config.relay
|
|
95
109
|
? { mode: 'self-hosted', relayUrl: config.relay }
|
|
@@ -156,7 +170,7 @@ function createOmniaHost(config, dependencies = {}) {
|
|
|
156
170
|
},
|
|
157
171
|
}
|
|
158
172
|
: undefined);
|
|
159
|
-
const factories = dependencies.factories ?? (factoryBundles ?
|
|
173
|
+
const factories = dependencies.factories ?? (factoryBundles ? hostMap('omnia:factory:') : undefined);
|
|
160
174
|
const localBundle = localParticipant
|
|
161
175
|
? factoryBundles?.[localParticipant.partyId]
|
|
162
176
|
: undefined;
|
|
@@ -169,8 +183,8 @@ function createOmniaHost(config, dependencies = {}) {
|
|
|
169
183
|
: {}),
|
|
170
184
|
}
|
|
171
185
|
: undefined);
|
|
172
|
-
const spliceProposals = dependencies.spliceProposals
|
|
173
|
-
const spliceAcceptances = dependencies.spliceAcceptances
|
|
186
|
+
const spliceProposals = hostMap('omnia:splice:proposal:', dependencies.spliceProposals);
|
|
187
|
+
const spliceAcceptances = hostMap('omnia:splice:acceptance:', dependencies.spliceAcceptances);
|
|
174
188
|
const onSpliceMessage = async (peer, msg) => {
|
|
175
189
|
if (!spliceLeaseProvider || !channelStore)
|
|
176
190
|
return;
|
|
@@ -449,12 +463,12 @@ function createOmniaHost(config, dependencies = {}) {
|
|
|
449
463
|
const preparedOpen = new Map();
|
|
450
464
|
const preparedSplice = new Map();
|
|
451
465
|
const preparedFactory = new Map();
|
|
452
|
-
const vtxoPools =
|
|
453
|
-
const vtxos =
|
|
454
|
-
const vtxoReceipts =
|
|
466
|
+
const vtxoPools = hostMap('omnia:vtxo:pool:');
|
|
467
|
+
const vtxos = hostMap('omnia:vtxo:');
|
|
468
|
+
const vtxoReceipts = hostMap('omnia:vtxo:receipt:');
|
|
455
469
|
const preparedVtxoReceipts = new Map();
|
|
456
|
-
const pools =
|
|
457
|
-
const poolPositions =
|
|
470
|
+
const pools = hostMap('omnia:pool:');
|
|
471
|
+
const poolPositions = hostMap('omnia:pool:position:');
|
|
458
472
|
if (!controlServer) {
|
|
459
473
|
const readOnly = !dependencies.signer || !dependencies.leaseProvider;
|
|
460
474
|
if (readOnly) {
|
|
@@ -567,6 +581,8 @@ function createOmniaHost(config, dependencies = {}) {
|
|
|
567
581
|
if (ownedObservationStore)
|
|
568
582
|
await ownedObservationStore.close();
|
|
569
583
|
ownedObservationStore = undefined;
|
|
584
|
+
ownedHostStateStore?.close();
|
|
585
|
+
ownedHostStateStore = undefined;
|
|
570
586
|
ownedStore?.close();
|
|
571
587
|
ownedStore = undefined;
|
|
572
588
|
started = false;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { SyncSqliteStore } from '@totemsdk/storage/sqlite-sync';
|
|
2
|
+
/**
|
|
3
|
+
* Durable Map facade for host state (factories, virtual channels, splices,
|
|
4
|
+
* VTXOs, pools). Backed by `@totemsdk/storage/sqlite-sync` (same `totemsdk_kv`
|
|
5
|
+
* table + versioned codec as the channel/operation stores).
|
|
6
|
+
*
|
|
7
|
+
* Values are plain objects/arrays/bigints/Uint8Arrays. Runtime-only handles
|
|
8
|
+
* (`localSigner`, signer/lease/swarm objects) are stripped; Map/Set/Date are
|
|
9
|
+
* explicitly tagged so they round-trip. A function anywhere else is dropped
|
|
10
|
+
* rather than persisted (never serialise authority material by accident).
|
|
11
|
+
*/
|
|
12
|
+
export declare const HOST_STATE_FORMAT_KEY = "omnia:store:host-state:format";
|
|
13
|
+
export declare const HOST_STATE_FORMAT = 1;
|
|
14
|
+
export declare class PersistentMap<V> extends Map<string, V> {
|
|
15
|
+
private readonly kv;
|
|
16
|
+
private readonly namespace;
|
|
17
|
+
constructor(kv: SyncSqliteStore, namespace: string);
|
|
18
|
+
private key;
|
|
19
|
+
private keysInNamespace;
|
|
20
|
+
get(id: string): V | undefined;
|
|
21
|
+
set(id: string, value: V): this;
|
|
22
|
+
has(id: string): boolean;
|
|
23
|
+
delete(id: string): boolean;
|
|
24
|
+
clear(): void;
|
|
25
|
+
get size(): number;
|
|
26
|
+
keys(): MapIterator<string>;
|
|
27
|
+
values(): MapIterator<V>;
|
|
28
|
+
entries(): MapIterator<[string, V]>;
|
|
29
|
+
[Symbol.iterator](): MapIterator<[string, V]>;
|
|
30
|
+
forEach(callbackfn: (value: V, key: string, map: Map<string, V>) => void): void;
|
|
31
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PersistentMap = exports.HOST_STATE_FORMAT = exports.HOST_STATE_FORMAT_KEY = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Durable Map facade for host state (factories, virtual channels, splices,
|
|
6
|
+
* VTXOs, pools). Backed by `@totemsdk/storage/sqlite-sync` (same `totemsdk_kv`
|
|
7
|
+
* table + versioned codec as the channel/operation stores).
|
|
8
|
+
*
|
|
9
|
+
* Values are plain objects/arrays/bigints/Uint8Arrays. Runtime-only handles
|
|
10
|
+
* (`localSigner`, signer/lease/swarm objects) are stripped; Map/Set/Date are
|
|
11
|
+
* explicitly tagged so they round-trip. A function anywhere else is dropped
|
|
12
|
+
* rather than persisted (never serialise authority material by accident).
|
|
13
|
+
*/
|
|
14
|
+
exports.HOST_STATE_FORMAT_KEY = 'omnia:store:host-state:format';
|
|
15
|
+
exports.HOST_STATE_FORMAT = 1;
|
|
16
|
+
const EPHEMERAL_KEYS = new Set([
|
|
17
|
+
'localSigner',
|
|
18
|
+
'signer',
|
|
19
|
+
'leaseProvider',
|
|
20
|
+
'signerPort',
|
|
21
|
+
'swarm',
|
|
22
|
+
'routing',
|
|
23
|
+
]);
|
|
24
|
+
function encode(value, seen = new WeakSet()) {
|
|
25
|
+
if (value === null || value === undefined)
|
|
26
|
+
return null;
|
|
27
|
+
if (typeof value === 'bigint')
|
|
28
|
+
return value;
|
|
29
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
if (typeof value === 'function')
|
|
33
|
+
return null;
|
|
34
|
+
if (value instanceof Uint8Array)
|
|
35
|
+
return value;
|
|
36
|
+
if (value instanceof Date)
|
|
37
|
+
return { $date: value.toISOString() };
|
|
38
|
+
if (Array.isArray(value)) {
|
|
39
|
+
return value.map((item) => encode(item, seen));
|
|
40
|
+
}
|
|
41
|
+
if (value instanceof Map) {
|
|
42
|
+
const entries = [];
|
|
43
|
+
for (const [key, item] of value.entries())
|
|
44
|
+
entries.push([String(key), encode(item, seen)]);
|
|
45
|
+
return { $map: entries };
|
|
46
|
+
}
|
|
47
|
+
if (value instanceof Set) {
|
|
48
|
+
return { $set: Array.from(value.values(), (item) => encode(item, seen)) };
|
|
49
|
+
}
|
|
50
|
+
if (typeof value === 'object') {
|
|
51
|
+
if (seen.has(value))
|
|
52
|
+
throw new Error('cannot persist cyclic host state');
|
|
53
|
+
seen.add(value);
|
|
54
|
+
const out = {};
|
|
55
|
+
for (const [key, item] of Object.entries(value)) {
|
|
56
|
+
if (EPHEMERAL_KEYS.has(key) || item === undefined || typeof item === 'function')
|
|
57
|
+
continue;
|
|
58
|
+
out[key] = encode(item, seen);
|
|
59
|
+
}
|
|
60
|
+
seen.delete(value);
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
function decode(value) {
|
|
66
|
+
if (value === null)
|
|
67
|
+
return null;
|
|
68
|
+
if (typeof value === 'bigint' ||
|
|
69
|
+
typeof value === 'string' ||
|
|
70
|
+
typeof value === 'number' ||
|
|
71
|
+
typeof value === 'boolean') {
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
if (value instanceof Uint8Array)
|
|
75
|
+
return value;
|
|
76
|
+
if (Array.isArray(value))
|
|
77
|
+
return value.map(decode);
|
|
78
|
+
if (typeof value === 'object') {
|
|
79
|
+
const record = value;
|
|
80
|
+
if (Array.isArray(record['$map'])) {
|
|
81
|
+
return new Map(record['$map'].map(([key, item]) => [key, decode(item)]));
|
|
82
|
+
}
|
|
83
|
+
if (Array.isArray(record['$set'])) {
|
|
84
|
+
return new Set(record['$set'].map(decode));
|
|
85
|
+
}
|
|
86
|
+
if (typeof record['$date'] === 'string')
|
|
87
|
+
return new Date(record['$date']);
|
|
88
|
+
const out = {};
|
|
89
|
+
for (const [key, item] of Object.entries(record))
|
|
90
|
+
out[key] = decode(item);
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
return value;
|
|
94
|
+
}
|
|
95
|
+
function iterator(values) {
|
|
96
|
+
let index = 0;
|
|
97
|
+
return {
|
|
98
|
+
next() {
|
|
99
|
+
if (index >= values.length)
|
|
100
|
+
return { done: true, value: undefined };
|
|
101
|
+
return { done: false, value: values[index++] };
|
|
102
|
+
},
|
|
103
|
+
[Symbol.iterator]() {
|
|
104
|
+
return this;
|
|
105
|
+
},
|
|
106
|
+
[Symbol.dispose]() {
|
|
107
|
+
index = values.length;
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
class PersistentMap extends Map {
|
|
112
|
+
kv;
|
|
113
|
+
namespace;
|
|
114
|
+
constructor(kv, namespace) {
|
|
115
|
+
super();
|
|
116
|
+
this.kv = kv;
|
|
117
|
+
this.namespace = namespace;
|
|
118
|
+
const format = kv.get(exports.HOST_STATE_FORMAT_KEY);
|
|
119
|
+
if (format !== null && format !== exports.HOST_STATE_FORMAT) {
|
|
120
|
+
throw new Error(`Unsupported omnia-host state format ${format} (this build supports ${exports.HOST_STATE_FORMAT}) — refusing to open`);
|
|
121
|
+
}
|
|
122
|
+
if (format === null)
|
|
123
|
+
kv.set(exports.HOST_STATE_FORMAT_KEY, exports.HOST_STATE_FORMAT);
|
|
124
|
+
}
|
|
125
|
+
key(id) {
|
|
126
|
+
return this.namespace + id;
|
|
127
|
+
}
|
|
128
|
+
keysInNamespace() {
|
|
129
|
+
return this.kv.keys().filter((key) => key.startsWith(this.namespace));
|
|
130
|
+
}
|
|
131
|
+
get(id) {
|
|
132
|
+
const raw = this.kv.get(this.key(id));
|
|
133
|
+
return raw === null ? undefined : decode(raw);
|
|
134
|
+
}
|
|
135
|
+
set(id, value) {
|
|
136
|
+
this.kv.set(this.key(id), encode(value));
|
|
137
|
+
return this;
|
|
138
|
+
}
|
|
139
|
+
has(id) {
|
|
140
|
+
return this.kv.has(this.key(id));
|
|
141
|
+
}
|
|
142
|
+
delete(id) {
|
|
143
|
+
return this.kv.remove(this.key(id));
|
|
144
|
+
}
|
|
145
|
+
clear() {
|
|
146
|
+
for (const key of this.keysInNamespace())
|
|
147
|
+
this.kv.remove(key);
|
|
148
|
+
}
|
|
149
|
+
get size() {
|
|
150
|
+
return this.keysInNamespace().length;
|
|
151
|
+
}
|
|
152
|
+
keys() {
|
|
153
|
+
return iterator(this.keysInNamespace().map((key) => key.slice(this.namespace.length)));
|
|
154
|
+
}
|
|
155
|
+
values() {
|
|
156
|
+
return iterator(Array.from(this.entries(), ([, value]) => value));
|
|
157
|
+
}
|
|
158
|
+
entries() {
|
|
159
|
+
return iterator(this.keysInNamespace().map((key) => [
|
|
160
|
+
key.slice(this.namespace.length),
|
|
161
|
+
decode(this.kv.get(key)),
|
|
162
|
+
]));
|
|
163
|
+
}
|
|
164
|
+
[Symbol.iterator]() {
|
|
165
|
+
return this.entries();
|
|
166
|
+
}
|
|
167
|
+
forEach(callbackfn) {
|
|
168
|
+
for (const [key, value] of this.entries())
|
|
169
|
+
callbackfn(value, key, this);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
exports.PersistentMap = PersistentMap;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@totemsdk/omnia-host",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Durable Omnia node daemon for channel lifecycle, routing, and control APIs",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -22,20 +22,20 @@
|
|
|
22
22
|
],
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"ws": "^8.21.1",
|
|
25
|
+
"@totemsdk/chain-provider": "^0.3.0",
|
|
25
26
|
"@totemsdk/core": "^1.4.0",
|
|
26
27
|
"@totemsdk/identity": "^1.0.2",
|
|
27
|
-
"@totemsdk/chain-provider": "^0.3.0",
|
|
28
28
|
"@totemsdk/liquidity-bond": "^0.3.1",
|
|
29
|
-
"@totemsdk/manifest": "^2.0.
|
|
29
|
+
"@totemsdk/manifest": "^2.0.2",
|
|
30
30
|
"@totemsdk/minima-rpc": "^0.4.0",
|
|
31
31
|
"@totemsdk/omnia": "^2.3.1",
|
|
32
32
|
"@totemsdk/omnia-factory": "^0.7.0",
|
|
33
33
|
"@totemsdk/omnia-router": "^0.4.0",
|
|
34
|
-
"@totemsdk/omnia-pool": "^0.5.3",
|
|
35
34
|
"@totemsdk/omnia-splice": "^0.3.0",
|
|
35
|
+
"@totemsdk/omnia-pool": "^0.5.3",
|
|
36
36
|
"@totemsdk/p2p-hub": "^0.1.0",
|
|
37
|
-
"@totemsdk/omnia-vtxo": "^0.6.1",
|
|
38
37
|
"@totemsdk/storage": "^0.2.0",
|
|
38
|
+
"@totemsdk/omnia-vtxo": "^0.6.1",
|
|
39
39
|
"@totemsdk/wots-lease": "^1.0.1"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|