mindcache 2.2.0 → 2.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.
@@ -1,5 +1,5 @@
1
- import { M as MindCache, C as CloudConfig, a as CloudAdapter } from '../index-XM7bmK7C.mjs';
2
- export { d as ClearOperation, c as CloudAdapterEvents, b as ConnectionState, D as DeleteOperation, O as Operation, S as SetOperation } from '../index-XM7bmK7C.mjs';
1
+ import { M as MindCache, C as CloudConfig, a as CloudAdapter } from '../index-DXb0fL3e.mjs';
2
+ export { d as ClearOperation, c as CloudAdapterEvents, b as ConnectionState, D as DeleteOperation, O as Operation, S as SetOperation } from '../index-DXb0fL3e.mjs';
3
3
 
4
4
  /**
5
5
  * Connect a MindCache instance to the cloud for real-time sync.
@@ -1,5 +1,5 @@
1
- import { M as MindCache, C as CloudConfig, a as CloudAdapter } from '../index-XM7bmK7C.js';
2
- export { d as ClearOperation, c as CloudAdapterEvents, b as ConnectionState, D as DeleteOperation, O as Operation, S as SetOperation } from '../index-XM7bmK7C.js';
1
+ import { M as MindCache, C as CloudConfig, a as CloudAdapter } from '../index-DXb0fL3e.js';
2
+ export { d as ClearOperation, c as CloudAdapterEvents, b as ConnectionState, D as DeleteOperation, O as Operation, S as SetOperation } from '../index-DXb0fL3e.js';
3
3
 
4
4
  /**
5
5
  * Connect a MindCache instance to the cloud for real-time sync.
@@ -17,16 +17,17 @@ var CloudAdapter_exports = {};
17
17
  __export(CloudAdapter_exports, {
18
18
  CloudAdapter: () => exports.CloudAdapter
19
19
  });
20
- var DEFAULT_BASE_URL, RECONNECT_DELAY, MAX_RECONNECT_DELAY; exports.CloudAdapter = void 0;
20
+ var RECONNECT_DELAY, MAX_RECONNECT_DELAY; exports.CloudAdapter = void 0;
21
21
  var init_CloudAdapter = __esm({
22
22
  "src/cloud/CloudAdapter.ts"() {
23
- DEFAULT_BASE_URL = "wss://api.mindcache.io";
24
23
  RECONNECT_DELAY = 1e3;
25
24
  MAX_RECONNECT_DELAY = 3e4;
26
25
  exports.CloudAdapter = class {
27
26
  constructor(config) {
28
27
  this.config = config;
29
- this.config.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
28
+ if (!config.baseUrl) {
29
+ throw new Error("MindCache Cloud: baseUrl is required. Please provide the cloud API URL in your configuration.");
30
+ }
30
31
  }
31
32
  ws = null;
32
33
  queue = [];
@@ -87,6 +88,40 @@ var init_CloudAdapter = __esm({
87
88
  }
88
89
  this.mindcache = null;
89
90
  }
91
+ /**
92
+ * Fetch a short-lived WebSocket token from the API using the API key.
93
+ * This keeps the API key secure by only using it for a single HTTPS request,
94
+ * then using the short-lived token for the WebSocket connection.
95
+ *
96
+ * Supports two key formats:
97
+ * - API keys: mc_live_xxx or mc_test_xxx → Bearer token
98
+ * - Delegate keys: del_xxx:sec_xxx → ApiKey format
99
+ */
100
+ async fetchTokenWithApiKey() {
101
+ if (!this.config.apiKey) {
102
+ throw new Error("API key is required to fetch token");
103
+ }
104
+ const httpBaseUrl = this.config.baseUrl.replace("wss://", "https://").replace("ws://", "http://");
105
+ const isDelegate = this.config.apiKey.startsWith("del_") && this.config.apiKey.includes(":");
106
+ const authHeader = isDelegate ? `ApiKey ${this.config.apiKey}` : `Bearer ${this.config.apiKey}`;
107
+ const response = await fetch(`${httpBaseUrl}/api/ws-token`, {
108
+ method: "POST",
109
+ headers: {
110
+ "Content-Type": "application/json",
111
+ "Authorization": authHeader
112
+ },
113
+ body: JSON.stringify({
114
+ instanceId: this.config.instanceId,
115
+ permission: "write"
116
+ })
117
+ });
118
+ if (!response.ok) {
119
+ const error = await response.json().catch(() => ({ error: "Failed to get token" }));
120
+ throw new Error(error.error || `Failed to get WebSocket token: ${response.status}`);
121
+ }
122
+ const data = await response.json();
123
+ return data.token;
124
+ }
90
125
  /**
91
126
  * Connect to the cloud service
92
127
  */
@@ -96,13 +131,19 @@ var init_CloudAdapter = __esm({
96
131
  }
97
132
  this._state = "connecting";
98
133
  try {
99
- if (this.config.tokenProvider && !this.token) {
100
- this.token = await this.config.tokenProvider();
134
+ if (!this.token) {
135
+ if (this.config.tokenProvider) {
136
+ this.token = await this.config.tokenProvider();
137
+ } else if (this.config.apiKey) {
138
+ this.token = await this.fetchTokenWithApiKey();
139
+ }
101
140
  }
102
141
  let url = `${this.config.baseUrl}/sync/${this.config.instanceId}`;
103
142
  if (this.token) {
104
143
  url += `?token=${encodeURIComponent(this.token)}`;
105
144
  this.token = null;
145
+ } else {
146
+ throw new Error("MindCache Cloud: No authentication method available. Provide apiKey or tokenProvider.");
106
147
  }
107
148
  this.ws = new WebSocket(url);
108
149
  this.setupWebSocket();
@@ -164,12 +205,6 @@ var init_CloudAdapter = __esm({
164
205
  return;
165
206
  }
166
207
  this.ws.onopen = () => {
167
- if (this.config.apiKey) {
168
- this.ws.send(JSON.stringify({
169
- type: "auth",
170
- apiKey: this.config.apiKey
171
- }));
172
- }
173
208
  };
174
209
  this.ws.onmessage = (event) => {
175
210
  try {
@@ -177,6 +212,7 @@ var init_CloudAdapter = __esm({
177
212
  this.handleMessage(msg);
178
213
  } catch (error) {
179
214
  console.error("MindCache Cloud: Failed to parse message:", error);
215
+ console.error("Raw message:", typeof event.data === "string" ? event.data.slice(0, 200) : event.data);
180
216
  }
181
217
  };
182
218
  this.ws.onclose = () => {
@@ -184,10 +220,12 @@ var init_CloudAdapter = __esm({
184
220
  this.emit("disconnected");
185
221
  this.scheduleReconnect();
186
222
  };
187
- this.ws.onerror = (error) => {
223
+ this.ws.onerror = () => {
188
224
  this._state = "error";
189
- this.emit("error", new Error("WebSocket error"));
190
- console.error("MindCache Cloud: WebSocket error:", error);
225
+ const url = `${this.config.baseUrl}/sync/${this.config.instanceId}`;
226
+ console.error(`MindCache Cloud: WebSocket error connecting to ${url}`);
227
+ console.error("Check that the instance ID and API key are correct, and that the server is reachable.");
228
+ this.emit("error", new Error(`WebSocket connection failed to ${url}`));
191
229
  };
192
230
  }
193
231
  handleMessage(msg) {
@@ -264,15 +302,6 @@ var init_CloudAdapter = __esm({
264
302
  this.reconnectTimeout = setTimeout(async () => {
265
303
  this.reconnectTimeout = null;
266
304
  this.reconnectAttempts++;
267
- if (this.config.tokenProvider) {
268
- try {
269
- this.token = await this.config.tokenProvider();
270
- } catch (error) {
271
- console.error("MindCache Cloud: Failed to get token for reconnect:", error);
272
- this.emit("error", error);
273
- return;
274
- }
275
- }
276
305
  this.connect();
277
306
  }, delay);
278
307
  }
@@ -327,6 +356,7 @@ var MindCache = class {
327
356
  _cloudConfig = null;
328
357
  // Access level for system operations
329
358
  _accessLevel = "user";
359
+ _initPromise = null;
330
360
  constructor(options) {
331
361
  if (options?.accessLevel) {
332
362
  this._accessLevel = options.accessLevel;
@@ -335,7 +365,7 @@ var MindCache = class {
335
365
  this._cloudConfig = options.cloud;
336
366
  this._isLoaded = false;
337
367
  this._connectionState = "disconnected";
338
- this._initCloud();
368
+ this._initPromise = this._initCloud();
339
369
  }
340
370
  }
341
371
  /**
@@ -355,9 +385,11 @@ var MindCache = class {
355
385
  return;
356
386
  }
357
387
  try {
358
- const { CloudAdapter: CloudAdapter2 } = await Promise.resolve().then(() => (init_CloudAdapter(), CloudAdapter_exports));
359
- const configUrl = this._cloudConfig.baseUrl || "https://api.mindcache.io";
360
- const baseUrl = configUrl.replace("https://", "wss://").replace("http://", "ws://");
388
+ const CloudAdapter2 = await this._getCloudAdapterClass();
389
+ if (!this._cloudConfig.baseUrl) {
390
+ throw new Error("MindCache Cloud: baseUrl is required. Please provide the cloud API URL in your configuration.");
391
+ }
392
+ const baseUrl = this._cloudConfig.baseUrl.replace("https://", "wss://").replace("http://", "ws://");
361
393
  const adapter = new CloudAdapter2({
362
394
  instanceId: this._cloudConfig.instanceId,
363
395
  projectId: this._cloudConfig.projectId || "default",
@@ -425,12 +457,46 @@ var MindCache = class {
425
457
  get isLoaded() {
426
458
  return this._isLoaded;
427
459
  }
460
+ /**
461
+ * Protected method to load CloudAdapter class.
462
+ * Can be overridden/mocked for testing.
463
+ */
464
+ async _getCloudAdapterClass() {
465
+ const { CloudAdapter: CloudAdapter2 } = await Promise.resolve().then(() => (init_CloudAdapter(), CloudAdapter_exports));
466
+ return CloudAdapter2;
467
+ }
428
468
  /**
429
469
  * Check if this instance is connected to cloud
430
470
  */
431
471
  get isCloud() {
432
472
  return this._cloudConfig !== null;
433
473
  }
474
+ /**
475
+ * Wait for initial sync to complete (or resolve immediately if already synced/local).
476
+ * Useful for scripts or linear execution flows.
477
+ */
478
+ async waitForSync() {
479
+ if (this._isLoaded) {
480
+ return;
481
+ }
482
+ if (this._initPromise) {
483
+ await this._initPromise;
484
+ }
485
+ if (this._isLoaded) {
486
+ return;
487
+ }
488
+ return new Promise((resolve) => {
489
+ if (!this._cloudAdapter) {
490
+ resolve();
491
+ return;
492
+ }
493
+ const handler = () => {
494
+ this._cloudAdapter?.off("synced", handler);
495
+ resolve();
496
+ };
497
+ this._cloudAdapter.on("synced", handler);
498
+ });
499
+ }
434
500
  /**
435
501
  * Disconnect from cloud (if connected)
436
502
  */