@totemsdk/edge-adapters 0.2.0 → 0.3.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/artifact-cache.d.ts +36 -0
- package/dist/artifact-cache.js +79 -0
- package/dist/cjs/artifact-cache.d.ts +36 -0
- package/dist/cjs/artifact-cache.js +82 -0
- package/dist/cjs/identity.d.ts +19 -0
- package/dist/cjs/identity.js +61 -0
- package/dist/cjs/index.d.ts +25 -0
- package/dist/cjs/index.js +41 -0
- package/dist/cjs/liquidity.d.ts +15 -0
- package/dist/cjs/liquidity.js +45 -0
- package/dist/cjs/location.d.ts +48 -0
- package/dist/cjs/location.js +208 -0
- package/dist/cjs/lookup.d.ts +13 -0
- package/dist/cjs/lookup.js +96 -0
- package/dist/cjs/manifest.d.ts +11 -0
- package/dist/cjs/manifest.js +36 -0
- package/dist/cjs/negotiation-transport.d.ts +50 -0
- package/dist/cjs/negotiation-transport.js +122 -0
- package/dist/cjs/omnia.d.ts +7 -0
- package/dist/cjs/omnia.js +46 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/payment-l1.d.ts +38 -0
- package/dist/cjs/payment-l1.js +46 -0
- package/dist/cjs/payment-l2.d.ts +39 -0
- package/dist/cjs/payment-l2.js +54 -0
- package/dist/cjs/policy.d.ts +13 -0
- package/dist/cjs/policy.js +95 -0
- package/dist/cjs/proof.d.ts +48 -0
- package/dist/cjs/proof.js +88 -0
- package/dist/cjs/pubsub.d.ts +11 -0
- package/dist/cjs/pubsub.js +30 -0
- package/dist/cjs/purchase-authority.d.ts +42 -0
- package/dist/cjs/purchase-authority.js +79 -0
- package/dist/cjs/purchase-lookup.d.ts +44 -0
- package/dist/cjs/purchase-lookup.js +60 -0
- package/dist/cjs/purchase-payment.d.ts +78 -0
- package/dist/cjs/purchase-payment.js +108 -0
- package/dist/cjs/sqlite-commerce-store.d.ts +69 -0
- package/dist/cjs/sqlite-commerce-store.js +416 -0
- package/dist/cjs/stream.d.ts +7 -0
- package/dist/cjs/stream.js +26 -0
- package/dist/identity.js +4 -7
- package/dist/index.d.ts +6 -0
- package/dist/index.js +18 -27
- package/dist/liquidity.js +1 -4
- package/dist/location.js +10 -13
- package/dist/lookup.js +3 -6
- package/dist/manifest.js +5 -7
- package/dist/negotiation-transport.d.ts +50 -0
- package/dist/negotiation-transport.js +118 -0
- package/dist/omnia.js +1 -4
- package/dist/payment-l1.js +1 -4
- package/dist/payment-l2.js +5 -8
- package/dist/policy.js +1 -4
- package/dist/proof.js +7 -10
- package/dist/pubsub.js +1 -4
- package/dist/purchase-authority.d.ts +42 -0
- package/dist/purchase-authority.js +76 -0
- package/dist/purchase-lookup.d.ts +44 -0
- package/dist/purchase-lookup.js +57 -0
- package/dist/purchase-payment.d.ts +78 -0
- package/dist/purchase-payment.js +105 -0
- package/dist/sqlite-commerce-store.d.ts +69 -0
- package/dist/sqlite-commerce-store.js +411 -0
- package/dist/stream.js +1 -4
- package/package.json +37 -26
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createLocationPortAdapter = createLocationPortAdapter;
|
|
4
|
+
const location_proof_1 = require("@totemsdk/location-proof");
|
|
5
|
+
const DEVICE_CLASSES = [
|
|
6
|
+
'drone',
|
|
7
|
+
'vehicle',
|
|
8
|
+
'robot',
|
|
9
|
+
'ship',
|
|
10
|
+
'tractor',
|
|
11
|
+
'phone',
|
|
12
|
+
'sensor',
|
|
13
|
+
'gateway',
|
|
14
|
+
'camera',
|
|
15
|
+
'weather-station',
|
|
16
|
+
'security-device',
|
|
17
|
+
'other',
|
|
18
|
+
];
|
|
19
|
+
const SOURCE_TYPES = [
|
|
20
|
+
'gnss',
|
|
21
|
+
'gps',
|
|
22
|
+
'rtk',
|
|
23
|
+
'cell',
|
|
24
|
+
'wifi',
|
|
25
|
+
'ble',
|
|
26
|
+
'lorawan',
|
|
27
|
+
'gateway',
|
|
28
|
+
'network',
|
|
29
|
+
'manual',
|
|
30
|
+
'derived',
|
|
31
|
+
'other',
|
|
32
|
+
];
|
|
33
|
+
function assertLocationClaim(claim) {
|
|
34
|
+
if (!claim || typeof claim !== 'object') {
|
|
35
|
+
throw new Error('claim is not a LocationClaim');
|
|
36
|
+
}
|
|
37
|
+
const c = claim;
|
|
38
|
+
if (typeof c.subjectId !== 'string' || typeof c.deviceId !== 'string' || !c.location) {
|
|
39
|
+
throw new Error('claim is not a valid LocationClaim');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
function toSourceType(type) {
|
|
43
|
+
if (type !== undefined && SOURCE_TYPES.includes(type)) {
|
|
44
|
+
return type;
|
|
45
|
+
}
|
|
46
|
+
return 'other';
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Wraps @totemsdk/location-proof as an EdgeLocationPort.
|
|
50
|
+
*
|
|
51
|
+
* createClaim/createTrail build content-derived Totem-location claims/trails.
|
|
52
|
+
* createProof returns a SignedProof when config.seed is set; otherwise it
|
|
53
|
+
* returns an UnsignedProof that MUST NOT be presented as a completed proof.
|
|
54
|
+
*/
|
|
55
|
+
function createLocationPortAdapter(config = {}) {
|
|
56
|
+
return {
|
|
57
|
+
async createClaim(params) {
|
|
58
|
+
try {
|
|
59
|
+
const deviceClass = DEVICE_CLASSES.includes(params.deviceClass)
|
|
60
|
+
? params.deviceClass
|
|
61
|
+
: undefined;
|
|
62
|
+
const sourceType = toSourceType(params.source.type);
|
|
63
|
+
const claim = (0, location_proof_1.createLocationClaim)({
|
|
64
|
+
subjectId: params.subjectId,
|
|
65
|
+
deviceId: params.deviceId,
|
|
66
|
+
...(deviceClass !== undefined ? { deviceClass } : {}),
|
|
67
|
+
...(params.operatorId !== undefined ? { operatorId: params.operatorId } : {}),
|
|
68
|
+
observedAt: params.observedAt ?? Date.now(),
|
|
69
|
+
location: {
|
|
70
|
+
lat: params.location.lat,
|
|
71
|
+
lon: params.location.lon,
|
|
72
|
+
...(params.location.altitudeM !== undefined ? { altitudeM: params.location.altitudeM } : {}),
|
|
73
|
+
...(params.location.accuracyM !== undefined ? { accuracyM: params.location.accuracyM } : {}),
|
|
74
|
+
},
|
|
75
|
+
source: {
|
|
76
|
+
type: sourceType,
|
|
77
|
+
...(params.source.fixType !== undefined ? { fixType: params.source.fixType } : {}),
|
|
78
|
+
...(params.source.satellitesUsed !== undefined ? { satellitesUsed: params.source.satellitesUsed } : {}),
|
|
79
|
+
...(params.source.hdop !== undefined ? { hdop: params.source.hdop } : {}),
|
|
80
|
+
...(params.source.vdop !== undefined ? { vdop: params.source.vdop } : {}),
|
|
81
|
+
...(params.source.pdop !== undefined ? { pdop: params.source.pdop } : {}),
|
|
82
|
+
...(params.source.rawPayloadHash !== undefined ? { rawPayloadHash: params.source.rawPayloadHash } : {}),
|
|
83
|
+
...(params.source.nmeaPayloadHash !== undefined ? { nmeaPayloadHash: params.source.nmeaPayloadHash } : {}),
|
|
84
|
+
...(params.source.spoofingFlag !== undefined ? { spoofingFlag: params.source.spoofingFlag } : {}),
|
|
85
|
+
...(params.source.jammingFlag !== undefined ? { jammingFlag: params.source.jammingFlag } : {}),
|
|
86
|
+
...(params.source.metadata !== undefined ? { metadata: params.source.metadata } : {}),
|
|
87
|
+
},
|
|
88
|
+
...(params.challenge !== undefined ? { challenge: params.challenge } : {}),
|
|
89
|
+
...(params.corroboration !== undefined ? { corroboration: params.corroboration } : {}),
|
|
90
|
+
...(params.metadata !== undefined ? { metadata: params.metadata } : {}),
|
|
91
|
+
});
|
|
92
|
+
const validation = (0, location_proof_1.validateLocationClaim)(claim);
|
|
93
|
+
if (!validation.valid) {
|
|
94
|
+
return {
|
|
95
|
+
ok: false,
|
|
96
|
+
error: 'invalid location claim: ' + validation.errors.join('; '),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return { ok: true, data: { claimId: claim.claimId, claim } };
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
async scoreClaim(params) {
|
|
106
|
+
try {
|
|
107
|
+
assertLocationClaim(params.claim);
|
|
108
|
+
const result = (0, location_proof_1.scoreLocationClaim)(params.claim, params.options);
|
|
109
|
+
return {
|
|
110
|
+
ok: true,
|
|
111
|
+
data: {
|
|
112
|
+
score: result.score,
|
|
113
|
+
level: result.level,
|
|
114
|
+
positiveSignals: result.positiveSignals,
|
|
115
|
+
negativeSignals: result.negativeSignals,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
async createTrail(params) {
|
|
124
|
+
try {
|
|
125
|
+
const trail = (0, location_proof_1.createMovementTrail)({
|
|
126
|
+
subjectId: params.subjectId,
|
|
127
|
+
deviceId: params.deviceId,
|
|
128
|
+
samples: params.samples.map((sample) => ({
|
|
129
|
+
observedAt: sample.observedAt,
|
|
130
|
+
location: {
|
|
131
|
+
lat: sample.location.lat,
|
|
132
|
+
lon: sample.location.lon,
|
|
133
|
+
...(sample.location.altitudeM !== undefined ? { altitudeM: sample.location.altitudeM } : {}),
|
|
134
|
+
...(sample.location.accuracyM !== undefined ? { accuracyM: sample.location.accuracyM } : {}),
|
|
135
|
+
},
|
|
136
|
+
...(sample.headingDeg !== undefined ? { headingDeg: sample.headingDeg } : {}),
|
|
137
|
+
...(sample.speedMps !== undefined ? { speedMps: sample.speedMps } : {}),
|
|
138
|
+
...(sample.accuracyM !== undefined ? { accuracyM: sample.accuracyM } : {}),
|
|
139
|
+
...(sample.source !== undefined
|
|
140
|
+
? {
|
|
141
|
+
source: {
|
|
142
|
+
...sample.source,
|
|
143
|
+
type: toSourceType(sample.source.type),
|
|
144
|
+
},
|
|
145
|
+
}
|
|
146
|
+
: {}),
|
|
147
|
+
})),
|
|
148
|
+
...(params.metadata !== undefined ? { metadata: params.metadata } : {}),
|
|
149
|
+
...(params.maxSpeedMps !== undefined ? { maxSpeedMps: params.maxSpeedMps } : {}),
|
|
150
|
+
});
|
|
151
|
+
return { ok: true, data: { trailId: trail.trailId, trail } };
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
async createProof(params) {
|
|
158
|
+
try {
|
|
159
|
+
assertLocationClaim(params.claim);
|
|
160
|
+
const claim = params.claim;
|
|
161
|
+
const issuer = params.context?.['issuer'] ?? config.issuer;
|
|
162
|
+
const unsigned = (0, location_proof_1.createUnsignedLocationProof)({
|
|
163
|
+
claim,
|
|
164
|
+
...(issuer !== undefined ? { issuer } : {}),
|
|
165
|
+
});
|
|
166
|
+
let proof;
|
|
167
|
+
let proofId;
|
|
168
|
+
if (config.seed) {
|
|
169
|
+
if (config.leaseProvider) {
|
|
170
|
+
proof = await (0, location_proof_1.signLocationProofWithLease)(unsigned, config.seed, config.leaseProvider, {
|
|
171
|
+
treeId: config.leaseTreeId,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
proof = (0, location_proof_1.signLocationProof)(unsigned, config.seed, config.keyIndex ?? 0);
|
|
176
|
+
}
|
|
177
|
+
proofId = proof.proofId;
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
proof = unsigned;
|
|
181
|
+
proofId = unsigned.proofId;
|
|
182
|
+
}
|
|
183
|
+
return { ok: true, data: { proofId, proof } };
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
async verifyProof(params) {
|
|
190
|
+
try {
|
|
191
|
+
const result = (0, location_proof_1.verifyLocationProof)(params.proof, { now: params.now });
|
|
192
|
+
return {
|
|
193
|
+
ok: true,
|
|
194
|
+
data: {
|
|
195
|
+
valid: result.valid,
|
|
196
|
+
...(result.reason !== undefined ? { reason: result.reason } : {}),
|
|
197
|
+
...(result.expired !== undefined ? { expired: result.expired } : {}),
|
|
198
|
+
...(result.signerAddress !== undefined ? { signerAddress: result.signerAddress } : {}),
|
|
199
|
+
...(result.claimId !== undefined ? { claimId: result.claimId } : {}),
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
catch (err) {
|
|
204
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { LookupClient } from '@totemsdk/lookup-client';
|
|
2
|
+
import type { EdgeLookupPort } from '@totemsdk/edge';
|
|
3
|
+
/**
|
|
4
|
+
* Wraps a LookupClient as an EdgeLookupPort.
|
|
5
|
+
*
|
|
6
|
+
* lookup: queries coins by address, or a single coin by ID when kind === 'coin'.
|
|
7
|
+
* watch: registers for real-time coin-update push events.
|
|
8
|
+
* announce: encodes the caller's WOTS-signed manifest to bytes, then hands off
|
|
9
|
+
* to announceApp() or announceAgent() on the client. The client signs
|
|
10
|
+
* those bytes with its session Ed25519 keypair — no WOTS key index is
|
|
11
|
+
* consumed. Fire-and-forget: the lookup node sends no ACK.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createLookupPortAdapter(client: LookupClient): EdgeLookupPort;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createLookupPortAdapter = createLookupPortAdapter;
|
|
4
|
+
const manifest_1 = require("@totemsdk/manifest");
|
|
5
|
+
/**
|
|
6
|
+
* Wraps a LookupClient as an EdgeLookupPort.
|
|
7
|
+
*
|
|
8
|
+
* lookup: queries coins by address, or a single coin by ID when kind === 'coin'.
|
|
9
|
+
* watch: registers for real-time coin-update push events.
|
|
10
|
+
* announce: encodes the caller's WOTS-signed manifest to bytes, then hands off
|
|
11
|
+
* to announceApp() or announceAgent() on the client. The client signs
|
|
12
|
+
* those bytes with its session Ed25519 keypair — no WOTS key index is
|
|
13
|
+
* consumed. Fire-and-forget: the lookup node sends no ACK.
|
|
14
|
+
*/
|
|
15
|
+
function createLookupPortAdapter(client) {
|
|
16
|
+
return {
|
|
17
|
+
async lookup(params) {
|
|
18
|
+
try {
|
|
19
|
+
if (params.kind === 'coin') {
|
|
20
|
+
const coin = await client.getCoin(params.query);
|
|
21
|
+
return { ok: true, data: { results: coin ? [coin] : [] } };
|
|
22
|
+
}
|
|
23
|
+
const coins = await client.getCoins({ address: params.query });
|
|
24
|
+
return { ok: true, data: { results: coins } };
|
|
25
|
+
}
|
|
26
|
+
catch (err) {
|
|
27
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
async watch(params) {
|
|
31
|
+
try {
|
|
32
|
+
const unsubscribe = client.subscribeCoinUpdates(params.onUpdate);
|
|
33
|
+
await client.watchAddress(params.address);
|
|
34
|
+
return { ok: true, data: { unsubscribe } };
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
async announce(params) {
|
|
41
|
+
try {
|
|
42
|
+
const manifestBytes = (0, manifest_1.encodeManifest)(params.signed);
|
|
43
|
+
if (params.kind === 'app') {
|
|
44
|
+
await client.announceApp({
|
|
45
|
+
manifest: manifestBytes,
|
|
46
|
+
appId: params.appId,
|
|
47
|
+
expiresAt: params.expiresAt,
|
|
48
|
+
authorAddress: params.authorAddress,
|
|
49
|
+
isFree: params.isFree,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
await client.announceAgent({
|
|
54
|
+
manifest: manifestBytes,
|
|
55
|
+
capabilityId: params.capabilityId,
|
|
56
|
+
expiresAt: params.expiresAt,
|
|
57
|
+
tags: params.tags,
|
|
58
|
+
pricePerCall: params.pricePerCall,
|
|
59
|
+
latencyMs: params.latencyMs,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return { ok: true };
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
async query(params) {
|
|
69
|
+
try {
|
|
70
|
+
if (params.kind === 'app') {
|
|
71
|
+
const apps = await client.queryApps({
|
|
72
|
+
category: params.category,
|
|
73
|
+
authorAddress: params.authorAddress,
|
|
74
|
+
minVersion: params.minVersion,
|
|
75
|
+
freeOnly: params.freeOnly,
|
|
76
|
+
limit: params.limit,
|
|
77
|
+
});
|
|
78
|
+
return { ok: true, data: { results: apps.map((a) => ({ id: a.appId, manifest: a.manifest, nodeId: a.nodeId })) } };
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const agents = await client.queryAgents({
|
|
82
|
+
capabilityName: params.capabilityName,
|
|
83
|
+
tags: params.tags,
|
|
84
|
+
maxPricePerCall: params.maxPricePerCall,
|
|
85
|
+
maxLatencyMs: params.maxLatencyMs,
|
|
86
|
+
limit: params.limit,
|
|
87
|
+
});
|
|
88
|
+
return { ok: true, data: { results: agents.map((a) => ({ id: a.capabilityId, manifest: a.manifest, nodeId: a.nodeId })) } };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { EdgeManifestPort } from '@totemsdk/edge';
|
|
2
|
+
/**
|
|
3
|
+
* Wraps @totemsdk/manifest's signManifest / verifyManifest as an EdgeManifestPort.
|
|
4
|
+
*
|
|
5
|
+
* The port interface already carries seed and keyIndex at call time, so this
|
|
6
|
+
* adapter has no constructor config — it is purely a thin type bridge.
|
|
7
|
+
*
|
|
8
|
+
* Callers are responsible for key-lease reservation before calling sign().
|
|
9
|
+
* This adapter does not interact with @totemsdk/wots-lease.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createManifestPortAdapter(): EdgeManifestPort;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createManifestPortAdapter = createManifestPortAdapter;
|
|
4
|
+
const manifest_1 = require("@totemsdk/manifest");
|
|
5
|
+
const core_1 = require("@totemsdk/core");
|
|
6
|
+
/**
|
|
7
|
+
* Wraps @totemsdk/manifest's signManifest / verifyManifest as an EdgeManifestPort.
|
|
8
|
+
*
|
|
9
|
+
* The port interface already carries seed and keyIndex at call time, so this
|
|
10
|
+
* adapter has no constructor config — it is purely a thin type bridge.
|
|
11
|
+
*
|
|
12
|
+
* Callers are responsible for key-lease reservation before calling sign().
|
|
13
|
+
* This adapter does not interact with @totemsdk/wots-lease.
|
|
14
|
+
*/
|
|
15
|
+
function createManifestPortAdapter() {
|
|
16
|
+
return {
|
|
17
|
+
async sign(manifest, seed, keyIndex) {
|
|
18
|
+
try {
|
|
19
|
+
const signed = await (0, manifest_1.signManifest)(manifest, (0, core_1.createUnifiedChildTreeKey)(seed, 0), keyIndex);
|
|
20
|
+
return { ok: true, data: { signed } };
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
async verify(signed) {
|
|
27
|
+
try {
|
|
28
|
+
const result = (0, manifest_1.verifyManifest)(signed);
|
|
29
|
+
return { ok: true, data: { valid: result.valid, reason: result.reason } };
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* negotiation-transport.ts — NegotiationTransport adapters over Edge ports.
|
|
3
|
+
*
|
|
4
|
+
* Two adapters:
|
|
5
|
+
* - createStreamNegotiationTransport: raw byte stream (EdgeStreamPort).
|
|
6
|
+
* Messages are framed as length-prefixed JSON. The sender address is
|
|
7
|
+
* resolved by the caller (the stream is a single authenticated peer).
|
|
8
|
+
* - createPubSubNegotiationTransport: topic-based (EdgePubSubPort).
|
|
9
|
+
* Each negotiation uses a topic derived from the negotiationId; the
|
|
10
|
+
* sender address is carried in the envelope.
|
|
11
|
+
*
|
|
12
|
+
* Transport metadata stays separate from economic protocol objects. The
|
|
13
|
+
* NegotiationEngine never sees sockets/topics/connection IDs.
|
|
14
|
+
*/
|
|
15
|
+
import type { NegotiationTransport } from '@totemsdk/edge';
|
|
16
|
+
import type { EdgePubSubPort, EdgeStreamPort } from '@totemsdk/edge';
|
|
17
|
+
export interface StreamNegotiationTransportConfig {
|
|
18
|
+
stream: EdgeStreamPort;
|
|
19
|
+
/** The authenticated sender address for this stream (resolved by caller). */
|
|
20
|
+
sender: string;
|
|
21
|
+
/** The local recipient address. */
|
|
22
|
+
recipient: string;
|
|
23
|
+
/** Max message size in bytes (default 64 KiB). */
|
|
24
|
+
maxBytes?: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create a NegotiationTransport over a raw byte stream.
|
|
28
|
+
*
|
|
29
|
+
* Messages are framed as `[4-byte BE length][JSON bytes]`. The stream is
|
|
30
|
+
* assumed to be a single authenticated peer connection, so the sender address
|
|
31
|
+
* is fixed by the caller.
|
|
32
|
+
*/
|
|
33
|
+
export declare function createStreamNegotiationTransport(config: StreamNegotiationTransportConfig): NegotiationTransport;
|
|
34
|
+
export interface PubSubNegotiationTransportConfig {
|
|
35
|
+
pubsub: EdgePubSubPort;
|
|
36
|
+
/** The local recipient address. */
|
|
37
|
+
recipient: string;
|
|
38
|
+
/** Topic prefix (default 'totem.negotiation'). */
|
|
39
|
+
topicPrefix?: string;
|
|
40
|
+
/** Max message size in bytes (default 64 KiB). */
|
|
41
|
+
maxBytes?: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Create a NegotiationTransport over a pub/sub transport.
|
|
45
|
+
*
|
|
46
|
+
* Each negotiation uses a topic derived from its negotiationId. The sender
|
|
47
|
+
* address is carried in the envelope (a `sender` field on the message), so
|
|
48
|
+
* the ingress pipeline can authenticate it.
|
|
49
|
+
*/
|
|
50
|
+
export declare function createPubSubNegotiationTransport(config: PubSubNegotiationTransportConfig): NegotiationTransport;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* negotiation-transport.ts — NegotiationTransport adapters over Edge ports.
|
|
4
|
+
*
|
|
5
|
+
* Two adapters:
|
|
6
|
+
* - createStreamNegotiationTransport: raw byte stream (EdgeStreamPort).
|
|
7
|
+
* Messages are framed as length-prefixed JSON. The sender address is
|
|
8
|
+
* resolved by the caller (the stream is a single authenticated peer).
|
|
9
|
+
* - createPubSubNegotiationTransport: topic-based (EdgePubSubPort).
|
|
10
|
+
* Each negotiation uses a topic derived from the negotiationId; the
|
|
11
|
+
* sender address is carried in the envelope.
|
|
12
|
+
*
|
|
13
|
+
* Transport metadata stays separate from economic protocol objects. The
|
|
14
|
+
* NegotiationEngine never sees sockets/topics/connection IDs.
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.createStreamNegotiationTransport = createStreamNegotiationTransport;
|
|
18
|
+
exports.createPubSubNegotiationTransport = createPubSubNegotiationTransport;
|
|
19
|
+
/**
|
|
20
|
+
* Create a NegotiationTransport over a raw byte stream.
|
|
21
|
+
*
|
|
22
|
+
* Messages are framed as `[4-byte BE length][JSON bytes]`. The stream is
|
|
23
|
+
* assumed to be a single authenticated peer connection, so the sender address
|
|
24
|
+
* is fixed by the caller.
|
|
25
|
+
*/
|
|
26
|
+
function createStreamNegotiationTransport(config) {
|
|
27
|
+
const { stream, sender, recipient } = config;
|
|
28
|
+
const maxBytes = config.maxBytes ?? 64 * 1024;
|
|
29
|
+
return {
|
|
30
|
+
async send(_recipient, message) {
|
|
31
|
+
const bytes = new TextEncoder().encode(JSON.stringify(message));
|
|
32
|
+
if (bytes.length > maxBytes) {
|
|
33
|
+
throw new Error(`negotiation message exceeds ${maxBytes} bytes`);
|
|
34
|
+
}
|
|
35
|
+
const frame = new Uint8Array(4 + bytes.length);
|
|
36
|
+
new DataView(frame.buffer).setUint32(0, bytes.length);
|
|
37
|
+
frame.set(bytes, 4);
|
|
38
|
+
// Raw byte-stream send does NOT prove durable remote processing —
|
|
39
|
+
// return no receipt so the outbox drainer does not mark delivered.
|
|
40
|
+
stream.send(frame);
|
|
41
|
+
return undefined;
|
|
42
|
+
},
|
|
43
|
+
subscribe(handler) {
|
|
44
|
+
let buffer = new Uint8Array(0);
|
|
45
|
+
const off = stream.onData((chunk) => {
|
|
46
|
+
buffer = concatBytes(buffer, chunk);
|
|
47
|
+
while (buffer.length >= 4) {
|
|
48
|
+
const view = new DataView(buffer.buffer, buffer.byteOffset, 4);
|
|
49
|
+
const len = view.getUint32(0);
|
|
50
|
+
if (buffer.length < 4 + len)
|
|
51
|
+
break;
|
|
52
|
+
const payload = buffer.slice(4, 4 + len);
|
|
53
|
+
buffer = buffer.slice(4 + len);
|
|
54
|
+
try {
|
|
55
|
+
const message = JSON.parse(new TextDecoder().decode(payload));
|
|
56
|
+
void handler(message, { sender, recipient });
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Malformed frame — drop.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
return off;
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function topicFor(topicPrefix, negotiationId) {
|
|
68
|
+
return `${topicPrefix}.${negotiationId}`;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create a NegotiationTransport over a pub/sub transport.
|
|
72
|
+
*
|
|
73
|
+
* Each negotiation uses a topic derived from its negotiationId. The sender
|
|
74
|
+
* address is carried in the envelope (a `sender` field on the message), so
|
|
75
|
+
* the ingress pipeline can authenticate it.
|
|
76
|
+
*/
|
|
77
|
+
function createPubSubNegotiationTransport(config) {
|
|
78
|
+
const { pubsub, recipient } = config;
|
|
79
|
+
const topicPrefix = config.topicPrefix ?? 'totem.negotiation';
|
|
80
|
+
const maxBytes = config.maxBytes ?? 64 * 1024;
|
|
81
|
+
return {
|
|
82
|
+
async send(recipientAddress, message) {
|
|
83
|
+
const negotiationId = message.negotiationId;
|
|
84
|
+
if (!negotiationId)
|
|
85
|
+
throw new Error('negotiation message missing negotiationId');
|
|
86
|
+
const topic = topicFor(topicPrefix, negotiationId);
|
|
87
|
+
const envelope = { ...message, sender: recipientAddress, recipient };
|
|
88
|
+
const bytes = new TextEncoder().encode(JSON.stringify(envelope));
|
|
89
|
+
if (bytes.length > maxBytes) {
|
|
90
|
+
throw new Error(`negotiation message exceeds ${maxBytes} bytes`);
|
|
91
|
+
}
|
|
92
|
+
// Pub/sub publish() proves local broker transmission, not durable remote
|
|
93
|
+
// processing — return no receipt so the outbox drainer does not mark
|
|
94
|
+
// delivered on local send alone.
|
|
95
|
+
await pubsub.publish(topic, bytes);
|
|
96
|
+
return undefined;
|
|
97
|
+
},
|
|
98
|
+
subscribe(handler) {
|
|
99
|
+
return pubsub.onMessage(({ topic, payload }) => {
|
|
100
|
+
if (!topic.startsWith(topicPrefix))
|
|
101
|
+
return;
|
|
102
|
+
try {
|
|
103
|
+
const envelope = JSON.parse(new TextDecoder().decode(payload));
|
|
104
|
+
const sender = envelope.sender ?? 'unknown';
|
|
105
|
+
void handler(envelope, { sender, recipient });
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Malformed message — drop.
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
115
|
+
// Helpers
|
|
116
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
117
|
+
function concatBytes(a, b) {
|
|
118
|
+
const out = new Uint8Array(a.length + b.length);
|
|
119
|
+
out.set(a, 0);
|
|
120
|
+
out.set(b, a.length);
|
|
121
|
+
return out;
|
|
122
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { EdgeOmniaPort } from '@totemsdk/edge';
|
|
2
|
+
export interface OmniaHostPortConfig {
|
|
3
|
+
endpoint: string;
|
|
4
|
+
fetch?: typeof globalThis.fetch;
|
|
5
|
+
headers?: Record<string, string>;
|
|
6
|
+
}
|
|
7
|
+
export declare function createOmniaHostPort(config: OmniaHostPortConfig): EdgeOmniaPort;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createOmniaHostPort = createOmniaHostPort;
|
|
4
|
+
function methodName(operation) {
|
|
5
|
+
return `totem_omnia${operation[0].toUpperCase()}${operation.slice(1)}`;
|
|
6
|
+
}
|
|
7
|
+
function createOmniaHostPort(config) {
|
|
8
|
+
const request = config.fetch ?? globalThis.fetch;
|
|
9
|
+
const endpoint = config.endpoint.replace(/\/$/, '');
|
|
10
|
+
async function call(operation, params = {}) {
|
|
11
|
+
try {
|
|
12
|
+
const response = await request(`${endpoint}/rpc`, {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: { 'content-type': 'application/json', ...config.headers },
|
|
15
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: Date.now(), method: methodName(operation), params }),
|
|
16
|
+
});
|
|
17
|
+
const body = await response.json();
|
|
18
|
+
if (!response.ok || body.error) {
|
|
19
|
+
return {
|
|
20
|
+
ok: false,
|
|
21
|
+
error: body.error?.message ?? `Omnia host returned HTTP ${response.status}`,
|
|
22
|
+
errorCode: body.error?.code ? String(body.error.code) : 'OMNIA_HOST_ERROR',
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return { ok: true, data: body.result };
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
return { ok: false, error: error instanceof Error ? error.message : String(error), errorCode: 'OMNIA_HOST_UNAVAILABLE' };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
getChannels: (params) => call('getChannels', params),
|
|
33
|
+
openChannel: (params) => call('openChannel', params),
|
|
34
|
+
pay: (params) => call('pay', params),
|
|
35
|
+
settle: (params) => call('settle', params),
|
|
36
|
+
closeChannel: (params) => call('closeChannel', params),
|
|
37
|
+
getRoute: (params) => call('getRoute', params),
|
|
38
|
+
payMultiHop: (params) => call('payMultiHop', params),
|
|
39
|
+
getSwapRate: (params) => call('getSwapRate', params),
|
|
40
|
+
createFactory: (params) => call('createFactory', params),
|
|
41
|
+
openVirtualChannel: (params) => call('openVirtualChannel', params),
|
|
42
|
+
closeFactory: (params) => call('closeFactory', params),
|
|
43
|
+
spliceIn: (params) => call('spliceIn', params),
|
|
44
|
+
spliceOut: (params) => call('spliceOut', params),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"type":"commonjs"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { ChainStateProvider } from '@totemsdk/chain-provider';
|
|
2
|
+
import type { EdgePaymentPort } from '@totemsdk/edge';
|
|
3
|
+
export interface MinimaL1PaymentPortConfig {
|
|
4
|
+
/** ChainStateProvider used to broadcast the signed TxPoW. */
|
|
5
|
+
provider: ChainStateProvider;
|
|
6
|
+
/**
|
|
7
|
+
* Injected signing function. Receives the payment intent and returns a
|
|
8
|
+
* fully mined TxPoW hex string ready for broadcast.
|
|
9
|
+
*
|
|
10
|
+
* This keeps the adapter agnostic to key management — callers wire in their
|
|
11
|
+
* own signer (e.g. @totemsdk/server's sendTransaction, a hardware wallet
|
|
12
|
+
* bridge, or a minima-rpc command sequence).
|
|
13
|
+
*/
|
|
14
|
+
sign(params: {
|
|
15
|
+
toAddress: string;
|
|
16
|
+
amount: string;
|
|
17
|
+
tokenId: string;
|
|
18
|
+
memo?: string;
|
|
19
|
+
}): Promise<string>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Minima L1 payment adapter.
|
|
23
|
+
*
|
|
24
|
+
* Delegates transaction construction and signing to the injected `sign` function,
|
|
25
|
+
* then broadcasts the result via the ChainStateProvider. This keeps key material
|
|
26
|
+
* out of the adapter entirely.
|
|
27
|
+
*
|
|
28
|
+
* For a batteries-included L1 adapter backed by @totemsdk/server's sendTransaction,
|
|
29
|
+
* wrap sendTransaction in the sign callback:
|
|
30
|
+
*
|
|
31
|
+
* createMinimaL1PaymentPort({
|
|
32
|
+
* provider: new PureMinimaRpcProvider(rpcConfig),
|
|
33
|
+
* sign: ({ toAddress, amount, tokenId }) =>
|
|
34
|
+
* sendTransaction({ seed, addressIndex, toAddress, amount, tokenId, ... })
|
|
35
|
+
* .then(r => r.minedHex),
|
|
36
|
+
* })
|
|
37
|
+
*/
|
|
38
|
+
export declare function createMinimaL1PaymentPort(config: MinimaL1PaymentPortConfig): EdgePaymentPort;
|