@calimero-network/mero-js 12.1.0 → 13.1.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/index.cjs CHANGED
@@ -25,6 +25,7 @@ __export(index_exports, {
25
25
  CAPABILITIES: () => CAPABILITIES,
26
26
  CloudClient: () => CloudClient,
27
27
  DEFAULT_LOCAL_NODE_PORTS: () => DEFAULT_LOCAL_NODE_PORTS,
28
+ EphemeralClient: () => EphemeralClient,
28
29
  HTTPError: () => HTTPError,
29
30
  LocalStorageTokenStore: () => LocalStorageTokenStore,
30
31
  MemoryTokenStore: () => MemoryTokenStore,
@@ -54,6 +55,7 @@ __export(index_exports, {
54
55
  createUniversalHttpClient: () => createUniversalHttpClient,
55
56
  discoverLocalNodes: () => discoverLocalNodes,
56
57
  hasCap: () => hasCap,
58
+ jsonCodec: () => jsonCodec,
57
59
  localNodeUrl: () => localNodeUrl,
58
60
  nodeEndpoint: () => nodeEndpoint,
59
61
  parseAuthCallback: () => parseAuthCallback,
@@ -1538,35 +1540,33 @@ var RpcError = class extends Error {
1538
1540
  this.type = type;
1539
1541
  }
1540
1542
  };
1543
+ async function jsonRpcCall(httpClient, method, params) {
1544
+ const response = await httpClient.post("/jsonrpc", {
1545
+ jsonrpc: "2.0",
1546
+ id: 1,
1547
+ method,
1548
+ params
1549
+ });
1550
+ if (response.error) {
1551
+ const err = response.error;
1552
+ throw new RpcError(err.code ?? -1, err.message ?? err.type ?? "RPC error", err.data, err.type);
1553
+ }
1554
+ return response.result;
1555
+ }
1541
1556
  var RpcClient = class {
1542
1557
  constructor(opts) {
1543
1558
  this.httpClient = opts.httpClient;
1544
1559
  }
1545
1560
  async execute(params) {
1546
- const body = {
1547
- jsonrpc: "2.0",
1548
- id: 1,
1549
- method: "execute",
1550
- params: {
1551
- contextId: params.contextId,
1552
- method: params.method,
1553
- argsJson: params.argsJson ?? {}
1554
- }
1555
- };
1556
- const response = await this.httpClient.post(
1557
- "/jsonrpc",
1558
- body
1559
- );
1560
- if (response.error) {
1561
- const err = response.error;
1562
- const code = err.code ?? -1;
1563
- const message = err.message ?? err.type ?? "RPC error";
1564
- throw new RpcError(code, message, err.data, err.type);
1565
- }
1566
- if (response.result && "output" in response.result) {
1567
- return response.result.output;
1561
+ const result = await jsonRpcCall(this.httpClient, "execute", {
1562
+ contextId: params.contextId,
1563
+ method: params.method,
1564
+ argsJson: params.argsJson ?? {}
1565
+ });
1566
+ if (result && "output" in result) {
1567
+ return result.output;
1568
1568
  }
1569
- return response.result;
1569
+ return result;
1570
1570
  }
1571
1571
  /**
1572
1572
  * One-tap owner-driven convert: re-signs the caller's identity-gated entries
@@ -2057,6 +2057,72 @@ var _WsClient = class _WsClient {
2057
2057
  _WsClient.MAX_BACKOFF_MS = 3e4;
2058
2058
  var WsClient = _WsClient;
2059
2059
 
2060
+ // src/ephemeral/codec.ts
2061
+ function jsonCodec() {
2062
+ return {
2063
+ encode(value) {
2064
+ return Array.from(new TextEncoder().encode(JSON.stringify(value)));
2065
+ },
2066
+ decode(bytes) {
2067
+ return JSON.parse(new TextDecoder().decode(new Uint8Array(bytes)));
2068
+ }
2069
+ };
2070
+ }
2071
+
2072
+ // src/ephemeral/index.ts
2073
+ var EphemeralClient = class {
2074
+ constructor(opts) {
2075
+ this.httpClient = opts.httpClient;
2076
+ this.sse = opts.sse;
2077
+ }
2078
+ /**
2079
+ * Publish your own presence slice, replacing it wholesale.
2080
+ *
2081
+ * Rejects with an `RpcError` (not a bare `Error`), so a typed failure such as
2082
+ * an oversized slice keeps its `type` and `data` — e.g. `SliceTooLarge` with
2083
+ * the offending size — and is `instanceof RpcError` like every other RPC in
2084
+ * this SDK.
2085
+ */
2086
+ async set(contextId, state, codec = jsonCodec()) {
2087
+ await jsonRpcCall(this.httpClient, "set_ephemeral", {
2088
+ contextId,
2089
+ state: codec.encode(state)
2090
+ });
2091
+ }
2092
+ /**
2093
+ * Observe presence changes for a context.
2094
+ *
2095
+ * This adds NO transport — it is a typed filter over the existing SSE event
2096
+ * stream, which already carries `{ contextId, type, data }`. On subscribing,
2097
+ * the node replays that context's current presence to this connection as
2098
+ * ordinary `Ephemeral` events before any live deltas; a replayed entry
2099
+ * carries `ageMs`, a live delta does not (never synthesized as `0` — absent
2100
+ * and zero mean different things). Returns an unsubscribe function.
2101
+ */
2102
+ subscribe(contextId, handler, codec = jsonCodec()) {
2103
+ const listener = (event) => {
2104
+ const e = event;
2105
+ if (e.type !== "Ephemeral" || e.contextId !== contextId) return;
2106
+ const data = e.data;
2107
+ if (!data?.author) return;
2108
+ const removed = Boolean(data.removed);
2109
+ const entry = {
2110
+ author: data.author,
2111
+ state: removed || data.state === void 0 ? void 0 : codec.decode(data.state),
2112
+ removed
2113
+ };
2114
+ if (data.ageMs !== void 0) entry.ageMs = data.ageMs;
2115
+ handler(entry);
2116
+ };
2117
+ this.sse.on("event", listener);
2118
+ void this.sse.connect().catch(() => void 0);
2119
+ void this.sse.subscribe([contextId]).catch(() => void 0);
2120
+ return () => {
2121
+ this.sse.off("event", listener);
2122
+ };
2123
+ }
2124
+ };
2125
+
2060
2126
  // src/mero-js.ts
2061
2127
  var REFRESH_LOCK_NAME = "mero-js:token-refresh";
2062
2128
  function getLockManager() {
@@ -2175,6 +2241,21 @@ var MeroJs = class {
2175
2241
  }
2176
2242
  return this.sseClient;
2177
2243
  }
2244
+ /**
2245
+ * Get the ephemeral-presence client (lazy initialized).
2246
+ *
2247
+ * Shares this instance's SSE client, so `subscribe` reuses the one event
2248
+ * stream rather than opening a second connection.
2249
+ */
2250
+ get ephemeral() {
2251
+ if (!this.ephemeralClient) {
2252
+ this.ephemeralClient = new EphemeralClient({
2253
+ httpClient: this.httpClient,
2254
+ sse: this.events
2255
+ });
2256
+ }
2257
+ return this.ephemeralClient;
2258
+ }
2178
2259
  /**
2179
2260
  * Get the WebSocket event client (lazy initialized).
2180
2261
  * @experimental Use `events` (SSE) for production. WsClient is experimental.