@unicitylabs/sphere-sdk 0.2.5 → 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.
@@ -3,6 +3,19 @@ import { AggregatorClient } from '@unicitylabs/state-transition-sdk/lib/api/Aggr
3
3
  import { RootTrustBase } from '@unicitylabs/state-transition-sdk/lib/bft/RootTrustBase';
4
4
  import { TransferCommitment as TransferCommitment$1 } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment';
5
5
 
6
+ interface GroupChatModuleConfig {
7
+ /** Override relay URLs (default: from network config) */
8
+ relays?: string[];
9
+ /** Default message fetch limit (default: 50) */
10
+ defaultMessageLimit?: number;
11
+ /** Max previous message IDs in ordering tags (default: 3) */
12
+ maxPreviousTags?: number;
13
+ /** Reconnect delay in ms (default: 3000) */
14
+ reconnectDelayMs?: number;
15
+ /** Max reconnect attempts (default: 5) */
16
+ maxReconnectAttempts?: number;
17
+ }
18
+
6
19
  /**
7
20
  * SDK2 Core Types
8
21
  * Platform-independent type definitions
@@ -160,22 +173,6 @@ interface TokenStorageProvider<TData = unknown> extends BaseProvider {
160
173
  * Subscribe to storage events
161
174
  */
162
175
  onEvent?(callback: StorageEventCallback): () => void;
163
- /**
164
- * Save individual token (for file-based storage like lottery pattern)
165
- */
166
- saveToken?(tokenId: string, tokenData: unknown): Promise<void>;
167
- /**
168
- * Get individual token
169
- */
170
- getToken?(tokenId: string): Promise<unknown | null>;
171
- /**
172
- * List all token IDs
173
- */
174
- listTokenIds?(): Promise<string[]>;
175
- /**
176
- * Delete individual token
177
- */
178
- deleteToken?(tokenId: string): Promise<void>;
179
176
  }
180
177
  type StorageEventType = 'storage:saving' | 'storage:saved' | 'storage:loading' | 'storage:loaded' | 'storage:error' | 'storage:remote-updated' | 'sync:started' | 'sync:completed' | 'sync:conflict' | 'sync:error';
181
178
  interface StorageEvent {
@@ -301,10 +298,6 @@ declare class FileTokenStorageProvider implements TokenStorageProvider<TxfStorag
301
298
  save(data: TxfStorageDataBase): Promise<SaveResult>;
302
299
  sync(localData: TxfStorageDataBase): Promise<SyncResult<TxfStorageDataBase>>;
303
300
  clear(): Promise<boolean>;
304
- deleteToken(tokenId: string): Promise<void>;
305
- saveToken(tokenId: string, tokenData: unknown): Promise<void>;
306
- getToken(tokenId: string): Promise<unknown | null>;
307
- listTokenIds(): Promise<string[]>;
308
301
  }
309
302
  declare function createFileTokenStorageProvider(config: FileTokenStorageConfig | string): FileTokenStorageProvider;
310
303
 
@@ -1117,6 +1110,7 @@ declare const NETWORKS: {
1117
1110
  readonly nostrRelays: readonly ["wss://relay.unicity.network", "wss://relay.damus.io", "wss://nos.lol", "wss://relay.nostr.band"];
1118
1111
  readonly ipfsGateways: readonly ["https://ipfs.unicity.network", "https://dweb.link", "https://ipfs.io"];
1119
1112
  readonly electrumUrl: "wss://fulcrum.alpha.unicity.network:50004";
1113
+ readonly groupRelays: readonly ["wss://sphere-relay.unicity.network"];
1120
1114
  };
1121
1115
  readonly testnet: {
1122
1116
  readonly name: "Testnet";
@@ -1124,6 +1118,7 @@ declare const NETWORKS: {
1124
1118
  readonly nostrRelays: readonly ["wss://nostr-relay.testnet.unicity.network"];
1125
1119
  readonly ipfsGateways: readonly ["https://ipfs.unicity.network", "https://dweb.link", "https://ipfs.io"];
1126
1120
  readonly electrumUrl: "wss://fulcrum.alpha.testnet.unicity.network:50004";
1121
+ readonly groupRelays: readonly ["wss://sphere-relay.unicity.network"];
1127
1122
  };
1128
1123
  readonly dev: {
1129
1124
  readonly name: "Development";
@@ -1131,6 +1126,7 @@ declare const NETWORKS: {
1131
1126
  readonly nostrRelays: readonly ["wss://nostr-relay.testnet.unicity.network"];
1132
1127
  readonly ipfsGateways: readonly ["https://ipfs.unicity.network", "https://dweb.link", "https://ipfs.io"];
1133
1128
  readonly electrumUrl: "wss://fulcrum.alpha.testnet.unicity.network:50004";
1129
+ readonly groupRelays: readonly ["wss://sphere-relay.unicity.network"];
1134
1130
  };
1135
1131
  };
1136
1132
  type NetworkType = keyof typeof NETWORKS;
@@ -1415,6 +1411,11 @@ interface NodeProvidersConfig {
1415
1411
  price?: BasePriceConfig;
1416
1412
  /** Token sync backends configuration */
1417
1413
  tokenSync?: NodeTokenSyncConfig;
1414
+ /** Group chat (NIP-29) configuration. true = enable with defaults, object = custom config */
1415
+ groupChat?: {
1416
+ enabled?: boolean;
1417
+ relays?: string[];
1418
+ } | boolean;
1418
1419
  }
1419
1420
  interface NodeProviders {
1420
1421
  storage: StorageProvider;
@@ -1427,6 +1428,8 @@ interface NodeProviders {
1427
1428
  price?: PriceProvider;
1428
1429
  /** IPFS token storage provider (when tokenSync.ipfs.enabled is true) */
1429
1430
  ipfsTokenStorage?: TokenStorageProvider<TxfStorageDataBase>;
1431
+ /** Group chat config (resolved, for passing to Sphere.init) */
1432
+ groupChat?: GroupChatModuleConfig | boolean;
1430
1433
  }
1431
1434
  /**
1432
1435
  * Create all Node.js providers with default configuration
@@ -3,6 +3,19 @@ import { AggregatorClient } from '@unicitylabs/state-transition-sdk/lib/api/Aggr
3
3
  import { RootTrustBase } from '@unicitylabs/state-transition-sdk/lib/bft/RootTrustBase';
4
4
  import { TransferCommitment as TransferCommitment$1 } from '@unicitylabs/state-transition-sdk/lib/transaction/TransferCommitment';
5
5
 
6
+ interface GroupChatModuleConfig {
7
+ /** Override relay URLs (default: from network config) */
8
+ relays?: string[];
9
+ /** Default message fetch limit (default: 50) */
10
+ defaultMessageLimit?: number;
11
+ /** Max previous message IDs in ordering tags (default: 3) */
12
+ maxPreviousTags?: number;
13
+ /** Reconnect delay in ms (default: 3000) */
14
+ reconnectDelayMs?: number;
15
+ /** Max reconnect attempts (default: 5) */
16
+ maxReconnectAttempts?: number;
17
+ }
18
+
6
19
  /**
7
20
  * SDK2 Core Types
8
21
  * Platform-independent type definitions
@@ -160,22 +173,6 @@ interface TokenStorageProvider<TData = unknown> extends BaseProvider {
160
173
  * Subscribe to storage events
161
174
  */
162
175
  onEvent?(callback: StorageEventCallback): () => void;
163
- /**
164
- * Save individual token (for file-based storage like lottery pattern)
165
- */
166
- saveToken?(tokenId: string, tokenData: unknown): Promise<void>;
167
- /**
168
- * Get individual token
169
- */
170
- getToken?(tokenId: string): Promise<unknown | null>;
171
- /**
172
- * List all token IDs
173
- */
174
- listTokenIds?(): Promise<string[]>;
175
- /**
176
- * Delete individual token
177
- */
178
- deleteToken?(tokenId: string): Promise<void>;
179
176
  }
180
177
  type StorageEventType = 'storage:saving' | 'storage:saved' | 'storage:loading' | 'storage:loaded' | 'storage:error' | 'storage:remote-updated' | 'sync:started' | 'sync:completed' | 'sync:conflict' | 'sync:error';
181
178
  interface StorageEvent {
@@ -301,10 +298,6 @@ declare class FileTokenStorageProvider implements TokenStorageProvider<TxfStorag
301
298
  save(data: TxfStorageDataBase): Promise<SaveResult>;
302
299
  sync(localData: TxfStorageDataBase): Promise<SyncResult<TxfStorageDataBase>>;
303
300
  clear(): Promise<boolean>;
304
- deleteToken(tokenId: string): Promise<void>;
305
- saveToken(tokenId: string, tokenData: unknown): Promise<void>;
306
- getToken(tokenId: string): Promise<unknown | null>;
307
- listTokenIds(): Promise<string[]>;
308
301
  }
309
302
  declare function createFileTokenStorageProvider(config: FileTokenStorageConfig | string): FileTokenStorageProvider;
310
303
 
@@ -1117,6 +1110,7 @@ declare const NETWORKS: {
1117
1110
  readonly nostrRelays: readonly ["wss://relay.unicity.network", "wss://relay.damus.io", "wss://nos.lol", "wss://relay.nostr.band"];
1118
1111
  readonly ipfsGateways: readonly ["https://ipfs.unicity.network", "https://dweb.link", "https://ipfs.io"];
1119
1112
  readonly electrumUrl: "wss://fulcrum.alpha.unicity.network:50004";
1113
+ readonly groupRelays: readonly ["wss://sphere-relay.unicity.network"];
1120
1114
  };
1121
1115
  readonly testnet: {
1122
1116
  readonly name: "Testnet";
@@ -1124,6 +1118,7 @@ declare const NETWORKS: {
1124
1118
  readonly nostrRelays: readonly ["wss://nostr-relay.testnet.unicity.network"];
1125
1119
  readonly ipfsGateways: readonly ["https://ipfs.unicity.network", "https://dweb.link", "https://ipfs.io"];
1126
1120
  readonly electrumUrl: "wss://fulcrum.alpha.testnet.unicity.network:50004";
1121
+ readonly groupRelays: readonly ["wss://sphere-relay.unicity.network"];
1127
1122
  };
1128
1123
  readonly dev: {
1129
1124
  readonly name: "Development";
@@ -1131,6 +1126,7 @@ declare const NETWORKS: {
1131
1126
  readonly nostrRelays: readonly ["wss://nostr-relay.testnet.unicity.network"];
1132
1127
  readonly ipfsGateways: readonly ["https://ipfs.unicity.network", "https://dweb.link", "https://ipfs.io"];
1133
1128
  readonly electrumUrl: "wss://fulcrum.alpha.testnet.unicity.network:50004";
1129
+ readonly groupRelays: readonly ["wss://sphere-relay.unicity.network"];
1134
1130
  };
1135
1131
  };
1136
1132
  type NetworkType = keyof typeof NETWORKS;
@@ -1415,6 +1411,11 @@ interface NodeProvidersConfig {
1415
1411
  price?: BasePriceConfig;
1416
1412
  /** Token sync backends configuration */
1417
1413
  tokenSync?: NodeTokenSyncConfig;
1414
+ /** Group chat (NIP-29) configuration. true = enable with defaults, object = custom config */
1415
+ groupChat?: {
1416
+ enabled?: boolean;
1417
+ relays?: string[];
1418
+ } | boolean;
1418
1419
  }
1419
1420
  interface NodeProviders {
1420
1421
  storage: StorageProvider;
@@ -1427,6 +1428,8 @@ interface NodeProviders {
1427
1428
  price?: PriceProvider;
1428
1429
  /** IPFS token storage provider (when tokenSync.ipfs.enabled is true) */
1429
1430
  ipfsTokenStorage?: TokenStorageProvider<TxfStorageDataBase>;
1431
+ /** Group chat config (resolved, for passing to Sphere.init) */
1432
+ groupChat?: GroupChatModuleConfig | boolean;
1430
1433
  }
1431
1434
  /**
1432
1435
  * Create all Node.js providers with default configuration
@@ -27,7 +27,17 @@ var STORAGE_KEYS_GLOBAL = {
27
27
  /** Active addresses registry (JSON: TrackedAddressesStorage) */
28
28
  TRACKED_ADDRESSES: "tracked_addresses",
29
29
  /** Last processed Nostr wallet event timestamp (unix seconds), keyed per pubkey */
30
- LAST_WALLET_EVENT_TS: "last_wallet_event_ts"
30
+ LAST_WALLET_EVENT_TS: "last_wallet_event_ts",
31
+ /** Group chat: joined groups */
32
+ GROUP_CHAT_GROUPS: "group_chat_groups",
33
+ /** Group chat: messages */
34
+ GROUP_CHAT_MESSAGES: "group_chat_messages",
35
+ /** Group chat: members */
36
+ GROUP_CHAT_MEMBERS: "group_chat_members",
37
+ /** Group chat: processed event IDs for deduplication */
38
+ GROUP_CHAT_PROCESSED_EVENTS: "group_chat_processed_events",
39
+ /** Group chat: last used relay URL (stale data detection) */
40
+ GROUP_CHAT_RELAY_URL: "group_chat_relay_url"
31
41
  };
32
42
  var STORAGE_KEYS_ADDRESS = {
33
43
  /** Pending transfers for this address */
@@ -108,27 +118,33 @@ var TEST_ELECTRUM_URL = "wss://fulcrum.alpha.testnet.unicity.network:50004";
108
118
  var TEST_NOSTR_RELAYS = [
109
119
  "wss://nostr-relay.testnet.unicity.network"
110
120
  ];
121
+ var DEFAULT_GROUP_RELAYS = [
122
+ "wss://sphere-relay.unicity.network"
123
+ ];
111
124
  var NETWORKS = {
112
125
  mainnet: {
113
126
  name: "Mainnet",
114
127
  aggregatorUrl: DEFAULT_AGGREGATOR_URL,
115
128
  nostrRelays: DEFAULT_NOSTR_RELAYS,
116
129
  ipfsGateways: DEFAULT_IPFS_GATEWAYS,
117
- electrumUrl: DEFAULT_ELECTRUM_URL
130
+ electrumUrl: DEFAULT_ELECTRUM_URL,
131
+ groupRelays: DEFAULT_GROUP_RELAYS
118
132
  },
119
133
  testnet: {
120
134
  name: "Testnet",
121
135
  aggregatorUrl: TEST_AGGREGATOR_URL,
122
136
  nostrRelays: TEST_NOSTR_RELAYS,
123
137
  ipfsGateways: DEFAULT_IPFS_GATEWAYS,
124
- electrumUrl: TEST_ELECTRUM_URL
138
+ electrumUrl: TEST_ELECTRUM_URL,
139
+ groupRelays: DEFAULT_GROUP_RELAYS
125
140
  },
126
141
  dev: {
127
142
  name: "Development",
128
143
  aggregatorUrl: DEV_AGGREGATOR_URL,
129
144
  nostrRelays: TEST_NOSTR_RELAYS,
130
145
  ipfsGateways: DEFAULT_IPFS_GATEWAYS,
131
- electrumUrl: TEST_ELECTRUM_URL
146
+ electrumUrl: TEST_ELECTRUM_URL,
147
+ groupRelays: DEFAULT_GROUP_RELAYS
132
148
  }
133
149
  };
134
150
  var TIMEOUTS = {
@@ -445,34 +461,6 @@ var FileTokenStorageProvider = class {
445
461
  return false;
446
462
  }
447
463
  }
448
- async deleteToken(tokenId) {
449
- const filePath = path2.join(this.tokensDir, `${tokenId}.json`);
450
- if (fs2.existsSync(filePath)) {
451
- fs2.unlinkSync(filePath);
452
- }
453
- }
454
- async saveToken(tokenId, tokenData) {
455
- fs2.writeFileSync(
456
- path2.join(this.tokensDir, `${tokenId}.json`),
457
- JSON.stringify(tokenData, null, 2)
458
- );
459
- }
460
- async getToken(tokenId) {
461
- const filePath = path2.join(this.tokensDir, `${tokenId}.json`);
462
- if (!fs2.existsSync(filePath)) {
463
- return null;
464
- }
465
- try {
466
- const content = fs2.readFileSync(filePath, "utf-8");
467
- return JSON.parse(content);
468
- } catch {
469
- return null;
470
- }
471
- }
472
- async listTokenIds() {
473
- const files = fs2.readdirSync(this.tokensDir).filter((f) => f.endsWith(".json") && f !== "_meta.json");
474
- return files.map((f) => path2.basename(f, ".json"));
475
- }
476
464
  };
477
465
  function createFileTokenStorageProvider(config) {
478
466
  return new FileTokenStorageProvider(config);
@@ -3617,9 +3605,13 @@ function mergeTxfData(local, remote) {
3617
3605
  remote._invalid ?? [],
3618
3606
  "tokenId"
3619
3607
  );
3608
+ const localNametags = local._nametags ?? [];
3609
+ const remoteNametags = remote._nametags ?? [];
3610
+ const mergedNametags = mergeNametagsByName(localNametags, remoteNametags);
3620
3611
  const merged = {
3621
3612
  _meta: mergedMeta,
3622
3613
  _tombstones: mergedTombstones.length > 0 ? mergedTombstones : void 0,
3614
+ _nametags: mergedNametags.length > 0 ? mergedNametags : void 0,
3623
3615
  _outbox: mergedOutbox.length > 0 ? mergedOutbox : void 0,
3624
3616
  _sent: mergedSent.length > 0 ? mergedSent : void 0,
3625
3617
  _invalid: mergedInvalid.length > 0 ? mergedInvalid : void 0,
@@ -3646,6 +3638,7 @@ function getTokenKeys(data) {
3646
3638
  "_sent",
3647
3639
  "_invalid",
3648
3640
  "_nametag",
3641
+ "_nametags",
3649
3642
  "_mintOutbox",
3650
3643
  "_invalidatedNametags",
3651
3644
  "_integrity"
@@ -3653,7 +3646,7 @@ function getTokenKeys(data) {
3653
3646
  const keys = /* @__PURE__ */ new Set();
3654
3647
  for (const key of Object.keys(data)) {
3655
3648
  if (reservedKeys.has(key)) continue;
3656
- if (key.startsWith("archived-") || key.startsWith("_forked_") || key.startsWith("nametag-")) continue;
3649
+ if (key.startsWith("archived-") || key.startsWith("_forked_")) continue;
3657
3650
  keys.add(key);
3658
3651
  }
3659
3652
  return keys;
@@ -3668,6 +3661,18 @@ function isTokenTombstoned(tokenId, localToken, remoteToken, tombstoneKeys) {
3668
3661
  void remoteToken;
3669
3662
  return false;
3670
3663
  }
3664
+ function mergeNametagsByName(local, remote) {
3665
+ const seen = /* @__PURE__ */ new Map();
3666
+ for (const item of local) {
3667
+ if (item.name) seen.set(item.name, item);
3668
+ }
3669
+ for (const item of remote) {
3670
+ if (item.name && !seen.has(item.name)) {
3671
+ seen.set(item.name, item);
3672
+ }
3673
+ }
3674
+ return Array.from(seen.values());
3675
+ }
3671
3676
  function mergeArrayById(local, remote, idField) {
3672
3677
  const seen = /* @__PURE__ */ new Map();
3673
3678
  for (const item of local) {
@@ -4022,14 +4027,11 @@ var AsyncSerialQueue = class {
4022
4027
  var WriteBuffer = class {
4023
4028
  /** Full TXF data from save() calls — latest wins */
4024
4029
  txfData = null;
4025
- /** Individual token mutations: key -> { op: 'save'|'delete', data? } */
4026
- tokenMutations = /* @__PURE__ */ new Map();
4027
4030
  get isEmpty() {
4028
- return this.txfData === null && this.tokenMutations.size === 0;
4031
+ return this.txfData === null;
4029
4032
  }
4030
4033
  clear() {
4031
4034
  this.txfData = null;
4032
- this.tokenMutations.clear();
4033
4035
  }
4034
4036
  /**
4035
4037
  * Merge another buffer's contents into this one (for rollback).
@@ -4039,11 +4041,6 @@ var WriteBuffer = class {
4039
4041
  if (other.txfData && !this.txfData) {
4040
4042
  this.txfData = other.txfData;
4041
4043
  }
4042
- for (const [id, mutation] of other.tokenMutations) {
4043
- if (!this.tokenMutations.has(id)) {
4044
- this.tokenMutations.set(id, mutation);
4045
- }
4046
- }
4047
4044
  }
4048
4045
  };
4049
4046
 
@@ -4083,9 +4080,6 @@ var IpfsStorageProvider = class {
4083
4080
  subscriptionClient = null;
4084
4081
  /** Unsubscribe function from subscription client */
4085
4082
  subscriptionUnsubscribe = null;
4086
- /** In-memory buffer for individual token save/delete calls */
4087
- tokenBuffer = /* @__PURE__ */ new Map();
4088
- deletedTokenIds = /* @__PURE__ */ new Set();
4089
4083
  /** Write-behind buffer: serializes flush / sync / shutdown */
4090
4084
  flushQueue = new AsyncSerialQueue();
4091
4085
  /** Pending mutations not yet flushed to IPFS */
@@ -4274,14 +4268,6 @@ var IpfsStorageProvider = class {
4274
4268
  metaUpdate.lastCid = this.remoteCid;
4275
4269
  }
4276
4270
  const updatedData = { ...data, _meta: metaUpdate };
4277
- for (const [tokenId, tokenData] of this.tokenBuffer) {
4278
- if (!this.deletedTokenIds.has(tokenId)) {
4279
- updatedData[tokenId] = tokenData;
4280
- }
4281
- }
4282
- for (const tokenId of this.deletedTokenIds) {
4283
- delete updatedData[tokenId];
4284
- }
4285
4271
  const { cid } = await this.httpClient.upload(updatedData);
4286
4272
  this.log(`Content uploaded: CID=${cid}`);
4287
4273
  const baseSeq = this.ipnsSequenceNumber > this.lastKnownRemoteSequence ? this.ipnsSequenceNumber : this.lastKnownRemoteSequence;
@@ -4320,7 +4306,6 @@ var IpfsStorageProvider = class {
4320
4306
  lastCid: cid,
4321
4307
  version: this.dataVersion
4322
4308
  });
4323
- this.deletedTokenIds.clear();
4324
4309
  this.emitEvent({
4325
4310
  type: "storage:saved",
4326
4311
  timestamp: Date.now(),
@@ -4432,7 +4417,6 @@ var IpfsStorageProvider = class {
4432
4417
  if (typeof remoteVersion === "number" && remoteVersion > this.dataVersion) {
4433
4418
  this.dataVersion = remoteVersion;
4434
4419
  }
4435
- this.populateTokenBuffer(data);
4436
4420
  this.emitEvent({
4437
4421
  type: "storage:loaded",
4438
4422
  timestamp: Date.now(),
@@ -4478,7 +4462,7 @@ var IpfsStorageProvider = class {
4478
4462
  this.emitEvent({ type: "sync:completed", timestamp: Date.now() });
4479
4463
  return {
4480
4464
  success: saveResult2.success,
4481
- merged: this.enrichWithTokenBuffer(localData),
4465
+ merged: localData,
4482
4466
  added: 0,
4483
4467
  removed: 0,
4484
4468
  conflicts: 0,
@@ -4493,7 +4477,7 @@ var IpfsStorageProvider = class {
4493
4477
  this.emitEvent({ type: "sync:completed", timestamp: Date.now() });
4494
4478
  return {
4495
4479
  success: true,
4496
- merged: this.enrichWithTokenBuffer(localData),
4480
+ merged: localData,
4497
4481
  added: 0,
4498
4482
  removed: 0,
4499
4483
  conflicts: 0
@@ -4516,7 +4500,7 @@ var IpfsStorageProvider = class {
4516
4500
  });
4517
4501
  return {
4518
4502
  success: saveResult.success,
4519
- merged: this.enrichWithTokenBuffer(merged),
4503
+ merged,
4520
4504
  added,
4521
4505
  removed,
4522
4506
  conflicts,
@@ -4542,21 +4526,6 @@ var IpfsStorageProvider = class {
4542
4526
  // ---------------------------------------------------------------------------
4543
4527
  // Private Helpers
4544
4528
  // ---------------------------------------------------------------------------
4545
- /**
4546
- * Enrich TXF data with individually-buffered tokens before returning to caller.
4547
- * PaymentsModule.createStorageData() passes empty tokens (they're stored via
4548
- * saveToken()), but loadFromStorageData() needs them in the merged result.
4549
- */
4550
- enrichWithTokenBuffer(data) {
4551
- if (this.tokenBuffer.size === 0) return data;
4552
- const enriched = { ...data };
4553
- for (const [tokenId, tokenData] of this.tokenBuffer) {
4554
- if (!this.deletedTokenIds.has(tokenId)) {
4555
- enriched[tokenId] = tokenData;
4556
- }
4557
- }
4558
- return enriched;
4559
- }
4560
4529
  // ---------------------------------------------------------------------------
4561
4530
  // Optional Methods
4562
4531
  // ---------------------------------------------------------------------------
@@ -4586,8 +4555,6 @@ var IpfsStorageProvider = class {
4586
4555
  const result = await this._doSave(emptyData);
4587
4556
  if (result.success) {
4588
4557
  this.cache.clear();
4589
- this.tokenBuffer.clear();
4590
- this.deletedTokenIds.clear();
4591
4558
  await this.statePersistence.clear(this.ipnsName);
4592
4559
  }
4593
4560
  return result.success;
@@ -4598,27 +4565,6 @@ var IpfsStorageProvider = class {
4598
4565
  this.eventCallbacks.delete(callback);
4599
4566
  };
4600
4567
  }
4601
- async saveToken(tokenId, tokenData) {
4602
- this.pendingBuffer.tokenMutations.set(tokenId, { op: "save", data: tokenData });
4603
- this.tokenBuffer.set(tokenId, tokenData);
4604
- this.deletedTokenIds.delete(tokenId);
4605
- this.scheduleFlush();
4606
- }
4607
- async getToken(tokenId) {
4608
- if (this.deletedTokenIds.has(tokenId)) return null;
4609
- return this.tokenBuffer.get(tokenId) ?? null;
4610
- }
4611
- async listTokenIds() {
4612
- return Array.from(this.tokenBuffer.keys()).filter(
4613
- (id) => !this.deletedTokenIds.has(id)
4614
- );
4615
- }
4616
- async deleteToken(tokenId) {
4617
- this.pendingBuffer.tokenMutations.set(tokenId, { op: "delete" });
4618
- this.tokenBuffer.delete(tokenId);
4619
- this.deletedTokenIds.add(tokenId);
4620
- this.scheduleFlush();
4621
- }
4622
4568
  // ---------------------------------------------------------------------------
4623
4569
  // Public Accessors
4624
4570
  // ---------------------------------------------------------------------------
@@ -4710,26 +4656,6 @@ var IpfsStorageProvider = class {
4710
4656
  console.log(`[IPFS-Storage] ${message}`);
4711
4657
  }
4712
4658
  }
4713
- META_KEYS = /* @__PURE__ */ new Set([
4714
- "_meta",
4715
- "_tombstones",
4716
- "_outbox",
4717
- "_sent",
4718
- "_invalid",
4719
- "_nametag",
4720
- "_mintOutbox",
4721
- "_invalidatedNametags",
4722
- "_integrity"
4723
- ]);
4724
- populateTokenBuffer(data) {
4725
- this.tokenBuffer.clear();
4726
- this.deletedTokenIds.clear();
4727
- for (const key of Object.keys(data)) {
4728
- if (!this.META_KEYS.has(key)) {
4729
- this.tokenBuffer.set(key, data[key]);
4730
- }
4731
- }
4732
- }
4733
4659
  };
4734
4660
 
4735
4661
  // impl/nodejs/ipfs/nodejs-ipfs-state-persistence.ts
@@ -4955,6 +4881,20 @@ function resolvePriceConfig(config) {
4955
4881
  debug: config.debug
4956
4882
  };
4957
4883
  }
4884
+ function resolveGroupChatConfig(network, config) {
4885
+ if (!config) return void 0;
4886
+ if (config === true) {
4887
+ const netConfig2 = getNetworkConfig(network);
4888
+ return { relays: [...netConfig2.groupRelays] };
4889
+ }
4890
+ if (typeof config === "object" && config.enabled === false) {
4891
+ return void 0;
4892
+ }
4893
+ const netConfig = getNetworkConfig(network);
4894
+ return {
4895
+ relays: config.relays ?? [...netConfig.groupRelays]
4896
+ };
4897
+ }
4958
4898
 
4959
4899
  // impl/nodejs/index.ts
4960
4900
  function createNodeProviders(config) {
@@ -4968,8 +4908,10 @@ function createNodeProviders(config) {
4968
4908
  });
4969
4909
  const ipfsSync = config?.tokenSync?.ipfs;
4970
4910
  const ipfsTokenStorage = ipfsSync?.enabled ? createNodeIpfsStorageProvider(ipfsSync.config, storage) : void 0;
4911
+ const groupChat = resolveGroupChatConfig(network, config?.groupChat);
4971
4912
  return {
4972
4913
  storage,
4914
+ groupChat,
4973
4915
  tokenStorage: createFileTokenStorageProvider({
4974
4916
  tokensDir: config?.tokensDir ?? "./sphere-tokens"
4975
4917
  }),