@calimero-network/mero-js 13.0.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.d.ts CHANGED
@@ -12,6 +12,8 @@ export type { MigrateMyEntriesSummary } from './rpc/index.js';
12
12
  export type { ExecuteParams } from './rpc/index.js';
13
13
  export { SseClient, WsClient } from './events/index.js';
14
14
  export type { SseEventData, WsEventData, AppVersionChangedEvent, GroupMembershipEventData, GroupMigrationEventData, MigrationStartedData, MigrationProgressData, CascadeProgressData, MigrationCompletedData, } from './events/index.js';
15
+ export { EphemeralClient, jsonCodec } from './ephemeral/index.js';
16
+ export type { Codec, EphemeralEntry } from './ephemeral/index.js';
15
17
  export * from './cloud/index.js';
16
18
  export * from './capabilities.js';
17
19
  export { DEFAULT_LOCAL_NODE_PORTS, localNodeUrl, nodeEndpoint, probeNodeHealth, discoverLocalNodes, } from './nodeDiscovery.js';
package/dist/index.js CHANGED
@@ -16,6 +16,8 @@ export { MemoryTokenStore, LocalStorageTokenStore } from './token-store/index.js
16
16
  export { RpcClient, RpcError } from './rpc/index.js';
17
17
  // Events (SSE / WebSocket)
18
18
  export { SseClient, WsClient } from './events/index.js';
19
+ // Ephemeral presence (cursors / typing / online)
20
+ export { EphemeralClient, jsonCodec } from './ephemeral/index.js';
19
21
  // Cloud client (enable-HA, disable-HA)
20
22
  export * from './cloud/index.js';
21
23
  // 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 body = {
1481
- jsonrpc: "2.0",
1482
- id: 1,
1483
- method: "execute",
1484
- params: {
1485
- contextId: params.contextId,
1486
- method: params.method,
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 response.result;
1501
+ return result;
1504
1502
  }
1505
1503
  /**
1506
1504
  * One-tap owner-driven convert: re-signs the caller's identity-gated entries
@@ -1991,6 +1989,72 @@ var _WsClient = class _WsClient {
1991
1989
  _WsClient.MAX_BACKOFF_MS = 3e4;
1992
1990
  var WsClient = _WsClient;
1993
1991
 
1992
+ // src/ephemeral/codec.ts
1993
+ function jsonCodec() {
1994
+ return {
1995
+ encode(value) {
1996
+ return Array.from(new TextEncoder().encode(JSON.stringify(value)));
1997
+ },
1998
+ decode(bytes) {
1999
+ return JSON.parse(new TextDecoder().decode(new Uint8Array(bytes)));
2000
+ }
2001
+ };
2002
+ }
2003
+
2004
+ // src/ephemeral/index.ts
2005
+ var EphemeralClient = class {
2006
+ constructor(opts) {
2007
+ this.httpClient = opts.httpClient;
2008
+ this.sse = opts.sse;
2009
+ }
2010
+ /**
2011
+ * Publish your own presence slice, replacing it wholesale.
2012
+ *
2013
+ * Rejects with an `RpcError` (not a bare `Error`), so a typed failure such as
2014
+ * an oversized slice keeps its `type` and `data` — e.g. `SliceTooLarge` with
2015
+ * the offending size — and is `instanceof RpcError` like every other RPC in
2016
+ * this SDK.
2017
+ */
2018
+ async set(contextId, state, codec = jsonCodec()) {
2019
+ await jsonRpcCall(this.httpClient, "set_ephemeral", {
2020
+ contextId,
2021
+ state: codec.encode(state)
2022
+ });
2023
+ }
2024
+ /**
2025
+ * Observe presence changes for a context.
2026
+ *
2027
+ * This adds NO transport — it is a typed filter over the existing SSE event
2028
+ * stream, which already carries `{ contextId, type, data }`. On subscribing,
2029
+ * the node replays that context's current presence to this connection as
2030
+ * ordinary `Ephemeral` events before any live deltas; a replayed entry
2031
+ * carries `ageMs`, a live delta does not (never synthesized as `0` — absent
2032
+ * and zero mean different things). Returns an unsubscribe function.
2033
+ */
2034
+ subscribe(contextId, handler, codec = jsonCodec()) {
2035
+ const listener = (event) => {
2036
+ const e = event;
2037
+ if (e.type !== "Ephemeral" || e.contextId !== contextId) return;
2038
+ const data = e.data;
2039
+ if (!data?.author) return;
2040
+ const removed = Boolean(data.removed);
2041
+ const entry = {
2042
+ author: data.author,
2043
+ state: removed || data.state === void 0 ? void 0 : codec.decode(data.state),
2044
+ removed
2045
+ };
2046
+ if (data.ageMs !== void 0) entry.ageMs = data.ageMs;
2047
+ handler(entry);
2048
+ };
2049
+ this.sse.on("event", listener);
2050
+ void this.sse.connect().catch(() => void 0);
2051
+ void this.sse.subscribe([contextId]).catch(() => void 0);
2052
+ return () => {
2053
+ this.sse.off("event", listener);
2054
+ };
2055
+ }
2056
+ };
2057
+
1994
2058
  // src/mero-js.ts
1995
2059
  var REFRESH_LOCK_NAME = "mero-js:token-refresh";
1996
2060
  function getLockManager() {
@@ -2109,6 +2173,21 @@ var MeroJs = class {
2109
2173
  }
2110
2174
  return this.sseClient;
2111
2175
  }
2176
+ /**
2177
+ * Get the ephemeral-presence client (lazy initialized).
2178
+ *
2179
+ * Shares this instance's SSE client, so `subscribe` reuses the one event
2180
+ * stream rather than opening a second connection.
2181
+ */
2182
+ get ephemeral() {
2183
+ if (!this.ephemeralClient) {
2184
+ this.ephemeralClient = new EphemeralClient({
2185
+ httpClient: this.httpClient,
2186
+ sse: this.events
2187
+ });
2188
+ }
2189
+ return this.ephemeralClient;
2190
+ }
2112
2191
  /**
2113
2192
  * Get the WebSocket event client (lazy initialized).
2114
2193
  * @experimental Use `events` (SSE) for production. WsClient is experimental.
@@ -2462,6 +2541,7 @@ export {
2462
2541
  CAPABILITIES,
2463
2542
  CloudClient,
2464
2543
  DEFAULT_LOCAL_NODE_PORTS,
2544
+ EphemeralClient,
2465
2545
  HTTPError,
2466
2546
  LocalStorageTokenStore,
2467
2547
  MemoryTokenStore,
@@ -2491,6 +2571,7 @@ export {
2491
2571
  createUniversalHttpClient,
2492
2572
  discoverLocalNodes,
2493
2573
  hasCap,
2574
+ jsonCodec,
2494
2575
  localNodeUrl,
2495
2576
  nodeEndpoint,
2496
2577
  parseAuthCallback,