@calimero-network/mero-js 13.0.0 → 13.2.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/account/index.d.ts +43 -0
- package/dist/account/index.js +139 -0
- package/dist/ephemeral/codec.d.ts +4 -0
- package/dist/ephemeral/codec.js +12 -0
- package/dist/ephemeral/index.d.ts +51 -0
- package/dist/ephemeral/index.js +81 -0
- package/dist/ephemeral/types.d.ts +22 -0
- package/dist/ephemeral/types.js +1 -0
- package/dist/events/sse.js +23 -2
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +4 -4
- package/dist/index.cjs +216 -25
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +3 -0
- package/dist/index.js +4 -0
- package/dist/index.mjs +216 -25
- package/dist/index.mjs.map +4 -4
- package/dist/mero-js.d.ts +9 -0
- package/dist/mero-js.js +16 -0
- package/dist/rpc/index.d.ts +8 -0
- package/dist/rpc/index.js +28 -20
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export * from './auth-api/index.js';
|
|
|
5
5
|
export * from './admin-api/index.js';
|
|
6
6
|
export { parseAuthCallback, buildAuthLoginUrl } from './auth/index.js';
|
|
7
7
|
export type { AuthCallbackResult, AuthLoginOptions } from './auth/index.js';
|
|
8
|
+
export { toAccountHex, toAccountBase58, sameAccount, } from './account/index.js';
|
|
8
9
|
export { MemoryTokenStore, LocalStorageTokenStore } from './token-store/index.js';
|
|
9
10
|
export type { TokenStore } from './token-store/index.js';
|
|
10
11
|
export { RpcClient, RpcError } from './rpc/index.js';
|
|
@@ -12,6 +13,8 @@ export type { MigrateMyEntriesSummary } from './rpc/index.js';
|
|
|
12
13
|
export type { ExecuteParams } from './rpc/index.js';
|
|
13
14
|
export { SseClient, WsClient } from './events/index.js';
|
|
14
15
|
export type { SseEventData, WsEventData, AppVersionChangedEvent, GroupMembershipEventData, GroupMigrationEventData, MigrationStartedData, MigrationProgressData, CascadeProgressData, MigrationCompletedData, } from './events/index.js';
|
|
16
|
+
export { EphemeralClient, jsonCodec } from './ephemeral/index.js';
|
|
17
|
+
export type { Codec, EphemeralEntry } from './ephemeral/index.js';
|
|
15
18
|
export * from './cloud/index.js';
|
|
16
19
|
export * from './capabilities.js';
|
|
17
20
|
export { DEFAULT_LOCAL_NODE_PORTS, localNodeUrl, nodeEndpoint, probeNodeHealth, discoverLocalNodes, } from './nodeDiscovery.js';
|
package/dist/index.js
CHANGED
|
@@ -10,12 +10,16 @@ export * from './auth-api/index.js';
|
|
|
10
10
|
export * from './admin-api/index.js';
|
|
11
11
|
// Auth utilities
|
|
12
12
|
export { parseAuthCallback, buildAuthLoginUrl } from './auth/index.js';
|
|
13
|
+
// Account id encoding (hex on the admin API, base58 in contract data)
|
|
14
|
+
export { toAccountHex, toAccountBase58, sameAccount, } from './account/index.js';
|
|
13
15
|
// Token store
|
|
14
16
|
export { MemoryTokenStore, LocalStorageTokenStore } from './token-store/index.js';
|
|
15
17
|
// RPC client
|
|
16
18
|
export { RpcClient, RpcError } from './rpc/index.js';
|
|
17
19
|
// Events (SSE / WebSocket)
|
|
18
20
|
export { SseClient, WsClient } from './events/index.js';
|
|
21
|
+
// Ephemeral presence (cursors / typing / online)
|
|
22
|
+
export { EphemeralClient, jsonCodec } from './ephemeral/index.js';
|
|
19
23
|
// Cloud client (enable-HA, disable-HA)
|
|
20
24
|
export * from './cloud/index.js';
|
|
21
25
|
// Member capability bitmask constants & helpers
|
package/dist/index.mjs
CHANGED
|
@@ -1472,35 +1472,33 @@ var RpcError = class extends Error {
|
|
|
1472
1472
|
this.type = type;
|
|
1473
1473
|
}
|
|
1474
1474
|
};
|
|
1475
|
+
async function jsonRpcCall(httpClient, method, params) {
|
|
1476
|
+
const response = await httpClient.post("/jsonrpc", {
|
|
1477
|
+
jsonrpc: "2.0",
|
|
1478
|
+
id: 1,
|
|
1479
|
+
method,
|
|
1480
|
+
params
|
|
1481
|
+
});
|
|
1482
|
+
if (response.error) {
|
|
1483
|
+
const err = response.error;
|
|
1484
|
+
throw new RpcError(err.code ?? -1, err.message ?? err.type ?? "RPC error", err.data, err.type);
|
|
1485
|
+
}
|
|
1486
|
+
return response.result;
|
|
1487
|
+
}
|
|
1475
1488
|
var RpcClient = class {
|
|
1476
1489
|
constructor(opts) {
|
|
1477
1490
|
this.httpClient = opts.httpClient;
|
|
1478
1491
|
}
|
|
1479
1492
|
async execute(params) {
|
|
1480
|
-
const
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
argsJson: params.argsJson ?? {}
|
|
1488
|
-
}
|
|
1489
|
-
};
|
|
1490
|
-
const response = await this.httpClient.post(
|
|
1491
|
-
"/jsonrpc",
|
|
1492
|
-
body
|
|
1493
|
-
);
|
|
1494
|
-
if (response.error) {
|
|
1495
|
-
const err = response.error;
|
|
1496
|
-
const code = err.code ?? -1;
|
|
1497
|
-
const message = err.message ?? err.type ?? "RPC error";
|
|
1498
|
-
throw new RpcError(code, message, err.data, err.type);
|
|
1499
|
-
}
|
|
1500
|
-
if (response.result && "output" in response.result) {
|
|
1501
|
-
return response.result.output;
|
|
1493
|
+
const result = await jsonRpcCall(this.httpClient, "execute", {
|
|
1494
|
+
contextId: params.contextId,
|
|
1495
|
+
method: params.method,
|
|
1496
|
+
argsJson: params.argsJson ?? {}
|
|
1497
|
+
});
|
|
1498
|
+
if (result && "output" in result) {
|
|
1499
|
+
return result.output;
|
|
1502
1500
|
}
|
|
1503
|
-
return
|
|
1501
|
+
return result;
|
|
1504
1502
|
}
|
|
1505
1503
|
/**
|
|
1506
1504
|
* One-tap owner-driven convert: re-signs the caller's identity-gated entries
|
|
@@ -1529,6 +1527,7 @@ function isGroupMigrationEvent(event) {
|
|
|
1529
1527
|
}
|
|
1530
1528
|
|
|
1531
1529
|
// src/events/sse.ts
|
|
1530
|
+
var TERMINAL_AUTH_ERRORS2 = /* @__PURE__ */ new Set(["token_reuse", "token_revoked"]);
|
|
1532
1531
|
var SseClient = class {
|
|
1533
1532
|
constructor(opts) {
|
|
1534
1533
|
this.sessionId = null;
|
|
@@ -1606,7 +1605,8 @@ var SseClient = class {
|
|
|
1606
1605
|
this.abortController = new AbortController();
|
|
1607
1606
|
try {
|
|
1608
1607
|
const token = await this.getAuthToken();
|
|
1609
|
-
const
|
|
1608
|
+
const url = `${this.baseUrl}/sse`;
|
|
1609
|
+
const response = await fetch(url, {
|
|
1610
1610
|
headers: {
|
|
1611
1611
|
"Authorization": `Bearer ${token}`,
|
|
1612
1612
|
"Accept": "text/event-stream"
|
|
@@ -1614,7 +1614,25 @@ var SseClient = class {
|
|
|
1614
1614
|
signal: this.abortController.signal
|
|
1615
1615
|
});
|
|
1616
1616
|
if (!response.ok) {
|
|
1617
|
-
|
|
1617
|
+
const authError = response.headers.get("x-auth-error");
|
|
1618
|
+
const bodyText = await response.text().catch(() => void 0);
|
|
1619
|
+
if (response.status === 401 && authError && TERMINAL_AUTH_ERRORS2.has(authError)) {
|
|
1620
|
+
throw new AuthRevokedError(
|
|
1621
|
+
authError,
|
|
1622
|
+
response.status,
|
|
1623
|
+
response.statusText,
|
|
1624
|
+
url,
|
|
1625
|
+
response.headers,
|
|
1626
|
+
bodyText
|
|
1627
|
+
);
|
|
1628
|
+
}
|
|
1629
|
+
throw new HTTPError(
|
|
1630
|
+
response.status,
|
|
1631
|
+
response.statusText,
|
|
1632
|
+
url,
|
|
1633
|
+
response.headers,
|
|
1634
|
+
bodyText
|
|
1635
|
+
);
|
|
1618
1636
|
}
|
|
1619
1637
|
if (!response.body) {
|
|
1620
1638
|
throw new Error("SSE response has no body");
|
|
@@ -1991,6 +2009,72 @@ var _WsClient = class _WsClient {
|
|
|
1991
2009
|
_WsClient.MAX_BACKOFF_MS = 3e4;
|
|
1992
2010
|
var WsClient = _WsClient;
|
|
1993
2011
|
|
|
2012
|
+
// src/ephemeral/codec.ts
|
|
2013
|
+
function jsonCodec() {
|
|
2014
|
+
return {
|
|
2015
|
+
encode(value) {
|
|
2016
|
+
return Array.from(new TextEncoder().encode(JSON.stringify(value)));
|
|
2017
|
+
},
|
|
2018
|
+
decode(bytes) {
|
|
2019
|
+
return JSON.parse(new TextDecoder().decode(new Uint8Array(bytes)));
|
|
2020
|
+
}
|
|
2021
|
+
};
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
// src/ephemeral/index.ts
|
|
2025
|
+
var EphemeralClient = class {
|
|
2026
|
+
constructor(opts) {
|
|
2027
|
+
this.httpClient = opts.httpClient;
|
|
2028
|
+
this.sse = opts.sse;
|
|
2029
|
+
}
|
|
2030
|
+
/**
|
|
2031
|
+
* Publish your own presence slice, replacing it wholesale.
|
|
2032
|
+
*
|
|
2033
|
+
* Rejects with an `RpcError` (not a bare `Error`), so a typed failure such as
|
|
2034
|
+
* an oversized slice keeps its `type` and `data` — e.g. `SliceTooLarge` with
|
|
2035
|
+
* the offending size — and is `instanceof RpcError` like every other RPC in
|
|
2036
|
+
* this SDK.
|
|
2037
|
+
*/
|
|
2038
|
+
async set(contextId, state, codec = jsonCodec()) {
|
|
2039
|
+
await jsonRpcCall(this.httpClient, "set_ephemeral", {
|
|
2040
|
+
contextId,
|
|
2041
|
+
state: codec.encode(state)
|
|
2042
|
+
});
|
|
2043
|
+
}
|
|
2044
|
+
/**
|
|
2045
|
+
* Observe presence changes for a context.
|
|
2046
|
+
*
|
|
2047
|
+
* This adds NO transport — it is a typed filter over the existing SSE event
|
|
2048
|
+
* stream, which already carries `{ contextId, type, data }`. On subscribing,
|
|
2049
|
+
* the node replays that context's current presence to this connection as
|
|
2050
|
+
* ordinary `Ephemeral` events before any live deltas; a replayed entry
|
|
2051
|
+
* carries `ageMs`, a live delta does not (never synthesized as `0` — absent
|
|
2052
|
+
* and zero mean different things). Returns an unsubscribe function.
|
|
2053
|
+
*/
|
|
2054
|
+
subscribe(contextId, handler, codec = jsonCodec()) {
|
|
2055
|
+
const listener = (event) => {
|
|
2056
|
+
const e = event;
|
|
2057
|
+
if (e.type !== "Ephemeral" || e.contextId !== contextId) return;
|
|
2058
|
+
const data = e.data;
|
|
2059
|
+
if (!data?.author) return;
|
|
2060
|
+
const removed = Boolean(data.removed);
|
|
2061
|
+
const entry = {
|
|
2062
|
+
author: data.author,
|
|
2063
|
+
state: removed || data.state === void 0 ? void 0 : codec.decode(data.state),
|
|
2064
|
+
removed
|
|
2065
|
+
};
|
|
2066
|
+
if (data.ageMs !== void 0) entry.ageMs = data.ageMs;
|
|
2067
|
+
handler(entry);
|
|
2068
|
+
};
|
|
2069
|
+
this.sse.on("event", listener);
|
|
2070
|
+
void this.sse.connect().catch(() => void 0);
|
|
2071
|
+
void this.sse.subscribe([contextId]).catch(() => void 0);
|
|
2072
|
+
return () => {
|
|
2073
|
+
this.sse.off("event", listener);
|
|
2074
|
+
};
|
|
2075
|
+
}
|
|
2076
|
+
};
|
|
2077
|
+
|
|
1994
2078
|
// src/mero-js.ts
|
|
1995
2079
|
var REFRESH_LOCK_NAME = "mero-js:token-refresh";
|
|
1996
2080
|
function getLockManager() {
|
|
@@ -2109,6 +2193,21 @@ var MeroJs = class {
|
|
|
2109
2193
|
}
|
|
2110
2194
|
return this.sseClient;
|
|
2111
2195
|
}
|
|
2196
|
+
/**
|
|
2197
|
+
* Get the ephemeral-presence client (lazy initialized).
|
|
2198
|
+
*
|
|
2199
|
+
* Shares this instance's SSE client, so `subscribe` reuses the one event
|
|
2200
|
+
* stream rather than opening a second connection.
|
|
2201
|
+
*/
|
|
2202
|
+
get ephemeral() {
|
|
2203
|
+
if (!this.ephemeralClient) {
|
|
2204
|
+
this.ephemeralClient = new EphemeralClient({
|
|
2205
|
+
httpClient: this.httpClient,
|
|
2206
|
+
sse: this.events
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2209
|
+
return this.ephemeralClient;
|
|
2210
|
+
}
|
|
2112
2211
|
/**
|
|
2113
2212
|
* Get the WebSocket event client (lazy initialized).
|
|
2114
2213
|
* @experimental Use `events` (SSE) for production. WsClient is experimental.
|
|
@@ -2299,6 +2398,93 @@ function createMeroJs(config) {
|
|
|
2299
2398
|
return new MeroJs(config);
|
|
2300
2399
|
}
|
|
2301
2400
|
|
|
2401
|
+
// src/account/index.ts
|
|
2402
|
+
var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
2403
|
+
var HEX_ACCOUNT = /^[0-9a-f]{64}$/i;
|
|
2404
|
+
function encodeBase58(bytes) {
|
|
2405
|
+
if (bytes.length === 0) return "";
|
|
2406
|
+
const digits = [0];
|
|
2407
|
+
for (const byte of bytes) {
|
|
2408
|
+
let carry = byte;
|
|
2409
|
+
for (let i = 0; i < digits.length; i++) {
|
|
2410
|
+
carry += digits[i] << 8;
|
|
2411
|
+
digits[i] = carry % 58;
|
|
2412
|
+
carry = carry / 58 | 0;
|
|
2413
|
+
}
|
|
2414
|
+
while (carry > 0) {
|
|
2415
|
+
digits.push(carry % 58);
|
|
2416
|
+
carry = carry / 58 | 0;
|
|
2417
|
+
}
|
|
2418
|
+
}
|
|
2419
|
+
let out = "";
|
|
2420
|
+
for (const byte of bytes) {
|
|
2421
|
+
if (byte !== 0) break;
|
|
2422
|
+
out += BASE58_ALPHABET[0];
|
|
2423
|
+
}
|
|
2424
|
+
for (let i = digits.length - 1; i >= 0; i--) {
|
|
2425
|
+
out += BASE58_ALPHABET[digits[i]];
|
|
2426
|
+
}
|
|
2427
|
+
return out;
|
|
2428
|
+
}
|
|
2429
|
+
function decodeBase58(value) {
|
|
2430
|
+
if (value.length === 0) return null;
|
|
2431
|
+
const bytes = [0];
|
|
2432
|
+
for (const char of value) {
|
|
2433
|
+
const index = BASE58_ALPHABET.indexOf(char);
|
|
2434
|
+
if (index < 0) return null;
|
|
2435
|
+
let carry = index;
|
|
2436
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2437
|
+
carry += bytes[i] * 58;
|
|
2438
|
+
bytes[i] = carry & 255;
|
|
2439
|
+
carry >>= 8;
|
|
2440
|
+
}
|
|
2441
|
+
while (carry > 0) {
|
|
2442
|
+
bytes.push(carry & 255);
|
|
2443
|
+
carry >>= 8;
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
let leadingZeros = 0;
|
|
2447
|
+
for (const char of value) {
|
|
2448
|
+
if (char !== BASE58_ALPHABET[0]) break;
|
|
2449
|
+
leadingZeros++;
|
|
2450
|
+
}
|
|
2451
|
+
return Uint8Array.from([
|
|
2452
|
+
...new Array(leadingZeros).fill(0),
|
|
2453
|
+
...bytes.reverse()
|
|
2454
|
+
]);
|
|
2455
|
+
}
|
|
2456
|
+
function toHex(bytes) {
|
|
2457
|
+
let out = "";
|
|
2458
|
+
for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
|
|
2459
|
+
return out;
|
|
2460
|
+
}
|
|
2461
|
+
function fromHex(hex) {
|
|
2462
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
2463
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
2464
|
+
bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
2465
|
+
}
|
|
2466
|
+
return bytes;
|
|
2467
|
+
}
|
|
2468
|
+
function toAccountHex(id) {
|
|
2469
|
+
const trimmed = (id ?? "").trim();
|
|
2470
|
+
if (!trimmed) return "";
|
|
2471
|
+
if (HEX_ACCOUNT.test(trimmed)) return trimmed.toLowerCase();
|
|
2472
|
+
const decoded = decodeBase58(trimmed);
|
|
2473
|
+
if (!decoded || decoded.length !== 32) return trimmed;
|
|
2474
|
+
return toHex(decoded);
|
|
2475
|
+
}
|
|
2476
|
+
function toAccountBase58(id) {
|
|
2477
|
+
const trimmed = (id ?? "").trim();
|
|
2478
|
+
if (!trimmed) return "";
|
|
2479
|
+
if (!HEX_ACCOUNT.test(trimmed)) return trimmed;
|
|
2480
|
+
return encodeBase58(fromHex(trimmed));
|
|
2481
|
+
}
|
|
2482
|
+
function sameAccount(a, b) {
|
|
2483
|
+
const left = toAccountHex(a);
|
|
2484
|
+
const right = toAccountHex(b);
|
|
2485
|
+
return HEX_ACCOUNT.test(left) && left === right;
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2302
2488
|
// src/token-store/index.ts
|
|
2303
2489
|
var MemoryTokenStore = class {
|
|
2304
2490
|
constructor() {
|
|
@@ -2462,6 +2648,7 @@ export {
|
|
|
2462
2648
|
CAPABILITIES,
|
|
2463
2649
|
CloudClient,
|
|
2464
2650
|
DEFAULT_LOCAL_NODE_PORTS,
|
|
2651
|
+
EphemeralClient,
|
|
2465
2652
|
HTTPError,
|
|
2466
2653
|
LocalStorageTokenStore,
|
|
2467
2654
|
MemoryTokenStore,
|
|
@@ -2491,10 +2678,14 @@ export {
|
|
|
2491
2678
|
createUniversalHttpClient,
|
|
2492
2679
|
discoverLocalNodes,
|
|
2493
2680
|
hasCap,
|
|
2681
|
+
jsonCodec,
|
|
2494
2682
|
localNodeUrl,
|
|
2495
2683
|
nodeEndpoint,
|
|
2496
2684
|
parseAuthCallback,
|
|
2497
2685
|
probeNodeHealth,
|
|
2686
|
+
sameAccount,
|
|
2687
|
+
toAccountBase58,
|
|
2688
|
+
toAccountHex,
|
|
2498
2689
|
withCap,
|
|
2499
2690
|
withRetry,
|
|
2500
2691
|
withoutCap
|