@lark-sh/client 0.1.19 → 0.1.20

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.
@@ -1536,17 +1536,14 @@ var SubscriptionManager = class {
1536
1536
  queryParamsChanged = view.updateQueryParams(queryParams);
1537
1537
  tag = this.viewKeyToTag.get(viewKey);
1538
1538
  }
1539
- const existingEventTypes = view.getEventTypes();
1540
- const isNewEventType = !existingEventTypes.includes(eventType);
1541
1539
  const unsubscribe = view.addCallback(eventType, callback);
1542
1540
  const wrappedUnsubscribe = () => {
1543
1541
  this.unsubscribeCallback(normalizedPath, eventType, callback, queryId);
1544
1542
  };
1545
- if (isNewView || isNewEventType || queryParamsChanged) {
1543
+ if (isNewView || queryParamsChanged) {
1546
1544
  const hasAncestorComplete = this.hasAncestorCompleteView(normalizedPath);
1547
1545
  if (!hasAncestorComplete) {
1548
- const allEventTypes = view.getEventTypes();
1549
- this.sendSubscribe?.(normalizedPath, allEventTypes, view.queryParams ?? void 0, tag).catch((err) => {
1546
+ this.sendSubscribe?.(normalizedPath, view.queryParams ?? void 0, tag).catch((err) => {
1550
1547
  console.error("Failed to subscribe:", err);
1551
1548
  });
1552
1549
  }
@@ -1638,11 +1635,6 @@ var SubscriptionManager = class {
1638
1635
  this.sendUnsubscribe?.(normalizedPath, queryParams).catch((err) => {
1639
1636
  console.error("Failed to unsubscribe:", err);
1640
1637
  });
1641
- } else {
1642
- const remainingEventTypes = view.getEventTypes();
1643
- this.sendSubscribe?.(normalizedPath, remainingEventTypes, queryParams).catch((err) => {
1644
- console.error("Failed to update subscription:", err);
1645
- });
1646
1638
  }
1647
1639
  }
1648
1640
  if (queryIds.size === 0) {
@@ -2070,10 +2062,9 @@ var SubscriptionManager = class {
2070
2062
  */
2071
2063
  async resubscribeAll() {
2072
2064
  for (const view of this.views.values()) {
2073
- const eventTypes = view.getEventTypes();
2074
- if (eventTypes.length > 0) {
2065
+ if (view.hasCallbacks()) {
2075
2066
  try {
2076
- await this.sendSubscribe?.(view.path, eventTypes, view.queryParams ?? void 0);
2067
+ await this.sendSubscribe?.(view.path, view.queryParams ?? void 0);
2077
2068
  } catch (err) {
2078
2069
  console.error(`Failed to resubscribe to ${view.path}:`, err);
2079
2070
  }
@@ -4243,21 +4234,33 @@ var ServerValue = {
4243
4234
  increment: (delta) => ({ ".sv": { increment: delta } })
4244
4235
  };
4245
4236
  var LarkDatabase = class {
4246
- constructor() {
4237
+ /**
4238
+ * Create a new LarkDatabase instance.
4239
+ *
4240
+ * @param databaseId - Database ID in format "project/database"
4241
+ * @param options - Connection options (token, anonymous, domain, transport).
4242
+ * Defaults to anonymous auth if not specified.
4243
+ *
4244
+ * @example
4245
+ * ```typescript
4246
+ * // Lazy connection - connects on first operation
4247
+ * const db = new LarkDatabase('project/database', { anonymous: true });
4248
+ * db.ref('/users').on('value', cb); // Auto-connects here
4249
+ *
4250
+ * // Explicit connection - for UI feedback or error handling
4251
+ * const db = new LarkDatabase('project/database', { anonymous: true });
4252
+ * await db.connect(); // Explicitly await connection
4253
+ * db.ref('/users').on('value', cb);
4254
+ * ```
4255
+ */
4256
+ constructor(databaseId, options = {}) {
4247
4257
  this._state = "disconnected";
4248
4258
  this._auth = null;
4249
- this._databaseId = null;
4250
- this._domain = null;
4251
4259
  this._volatilePaths = [];
4252
4260
  this._transportType = null;
4253
- // Auth state
4254
- this._currentToken = null;
4255
- // Token for auth (empty string = anonymous)
4256
- this._isAnonymous = false;
4257
4261
  // True if connected anonymously
4258
4262
  // Reconnection state
4259
4263
  this._connectionId = null;
4260
- this._connectOptions = null;
4261
4264
  this._intentionalDisconnect = false;
4262
4265
  this._reconnectAttempt = 0;
4263
4266
  this._reconnectTimer = null;
@@ -4270,13 +4273,21 @@ var LarkDatabase = class {
4270
4273
  this.authStateChangedCallbacks = /* @__PURE__ */ new Set();
4271
4274
  // .info path subscriptions (handled locally, not sent to server)
4272
4275
  this.infoSubscriptions = [];
4273
- // Authentication promise - resolves when auth completes, allows operations to queue
4274
- this.authenticationPromise = null;
4275
- this.authenticationResolve = null;
4276
+ // Connection promise - shared by connect() and lazy operations
4277
+ this._connectionPromise = null;
4276
4278
  this._serverTimeOffset = 0;
4277
4279
  this.messageQueue = new MessageQueue();
4278
4280
  this.subscriptionManager = new SubscriptionManager();
4279
4281
  this.pendingWrites = new PendingWriteManager();
4282
+ const projectId = databaseId.split("/")[0];
4283
+ if (!projectId) {
4284
+ throw new Error('Invalid database ID: must be in format "projectId/databaseName"');
4285
+ }
4286
+ this._databaseId = databaseId;
4287
+ this._connectOptions = options;
4288
+ this._domain = options.domain || DEFAULT_LARK_DOMAIN;
4289
+ this._currentToken = options.token || "";
4290
+ this._isAnonymous = !options.token && options.anonymous !== false;
4280
4291
  }
4281
4292
  // ============================================
4282
4293
  // Connection State
@@ -4361,21 +4372,38 @@ var LarkDatabase = class {
4361
4372
  // Connection Management
4362
4373
  // ============================================
4363
4374
  /**
4364
- * Connect to a database.
4375
+ * Explicitly connect to the database.
4365
4376
  *
4366
- * @param databaseId - Database ID in format "project/database"
4367
- * @param options - Connection options (token, anonymous, domain)
4377
+ * This is optional - operations will auto-connect if needed.
4378
+ * Use this when you want to:
4379
+ * - Await connection completion for UI feedback
4380
+ * - Handle connection errors separately from operation errors
4381
+ * - Pre-warm the connection before first operation
4382
+ *
4383
+ * @returns Promise that resolves when fully authenticated
4368
4384
  */
4369
- async connect(databaseId, options = {}) {
4370
- if (this._state !== "disconnected") {
4371
- throw new Error("Already connected or connecting");
4385
+ async connect() {
4386
+ return this.ensureConnected();
4387
+ }
4388
+ /**
4389
+ * Ensure connection is established, triggering it if needed.
4390
+ * Multiple concurrent calls share the same connection promise.
4391
+ */
4392
+ async ensureConnected() {
4393
+ if (this._state === "authenticated") {
4394
+ return;
4395
+ }
4396
+ if (this._connectionPromise) {
4397
+ return this._connectionPromise;
4372
4398
  }
4373
- this._connectOptions = options;
4374
4399
  this._intentionalDisconnect = false;
4375
- this.authenticationPromise = new Promise((resolve) => {
4376
- this.authenticationResolve = resolve;
4377
- });
4378
- await this.performConnect(databaseId, options);
4400
+ this._connectionPromise = this.performConnect(this._databaseId, this._connectOptions);
4401
+ try {
4402
+ await this._connectionPromise;
4403
+ } catch (error) {
4404
+ this._connectionPromise = null;
4405
+ throw error;
4406
+ }
4379
4407
  }
4380
4408
  /**
4381
4409
  * Internal connect implementation used by both initial connect and reconnect.
@@ -4385,19 +4413,9 @@ var LarkDatabase = class {
4385
4413
  * 3. Send auth (authenticates user - required even for anonymous)
4386
4414
  */
4387
4415
  async performConnect(databaseId, options, isReconnect = false) {
4388
- const previousState = this._state;
4389
4416
  this._state = isReconnect ? "reconnecting" : "connecting";
4390
- this._databaseId = databaseId;
4391
- this._domain = options.domain || DEFAULT_LARK_DOMAIN;
4392
- if (!isReconnect) {
4393
- this._currentToken = options.token || "";
4394
- this._isAnonymous = !options.token && options.anonymous !== false;
4395
- }
4396
4417
  try {
4397
4418
  const projectId = databaseId.split("/")[0];
4398
- if (!projectId) {
4399
- throw new Error('Invalid database ID: must be in format "projectId/databaseName"');
4400
- }
4401
4419
  const wsUrl = `wss://${projectId}.${this._domain}/ws`;
4402
4420
  const transportResult = await createTransport(
4403
4421
  wsUrl,
@@ -4450,10 +4468,6 @@ var LarkDatabase = class {
4450
4468
  };
4451
4469
  this._state = "authenticated";
4452
4470
  this._reconnectAttempt = 0;
4453
- if (this.authenticationResolve) {
4454
- this.authenticationResolve();
4455
- this.authenticationResolve = null;
4456
- }
4457
4471
  this.fireConnectionStateChange();
4458
4472
  if (!isReconnect) {
4459
4473
  this.subscriptionManager.initialize({
@@ -4475,12 +4489,8 @@ var LarkDatabase = class {
4475
4489
  }
4476
4490
  this._state = "disconnected";
4477
4491
  this._auth = null;
4478
- this._databaseId = null;
4479
- this._connectOptions = null;
4480
4492
  this._connectionId = null;
4481
4493
  this._transportType = null;
4482
- this._currentToken = null;
4483
- this._isAnonymous = false;
4484
4494
  this.transport?.close();
4485
4495
  this.transport = null;
4486
4496
  throw error;
@@ -4548,8 +4558,9 @@ var LarkDatabase = class {
4548
4558
  }
4549
4559
  }
4550
4560
  /**
4551
- * Full cleanup - clears all state including subscriptions.
4561
+ * Full cleanup - clears connection state including subscriptions.
4552
4562
  * Used for intentional disconnect.
4563
+ * Preserves config (databaseId, domain, token) so connect() can be called again.
4553
4564
  */
4554
4565
  cleanupFull() {
4555
4566
  const wasAuthenticated = this._state === "authenticated";
@@ -4557,17 +4568,11 @@ var LarkDatabase = class {
4557
4568
  this.transport = null;
4558
4569
  this._state = "disconnected";
4559
4570
  this._auth = null;
4560
- this._databaseId = null;
4561
4571
  this._volatilePaths = [];
4562
- this._domain = null;
4563
4572
  this._connectionId = null;
4564
- this._connectOptions = null;
4565
4573
  this._transportType = null;
4566
- this._currentToken = null;
4567
- this._isAnonymous = false;
4568
4574
  this._reconnectAttempt = 0;
4569
- this.authenticationPromise = null;
4570
- this.authenticationResolve = null;
4575
+ this._connectionPromise = null;
4571
4576
  this.subscriptionManager.clear();
4572
4577
  this.messageQueue.rejectAll(new Error("Connection closed"));
4573
4578
  this.pendingWrites.clear();
@@ -4668,15 +4673,14 @@ var LarkDatabase = class {
4668
4673
  * Attempt to reconnect to the database.
4669
4674
  */
4670
4675
  async attemptReconnect() {
4671
- if (this._intentionalDisconnect || !this._databaseId || !this._connectOptions) {
4676
+ if (this._intentionalDisconnect) {
4672
4677
  return;
4673
4678
  }
4674
- this.authenticationPromise = new Promise((resolve) => {
4675
- this.authenticationResolve = resolve;
4676
- });
4679
+ this._connectionPromise = this.performConnect(this._databaseId, this._connectOptions, true);
4677
4680
  try {
4678
- await this.performConnect(this._databaseId, this._connectOptions, true);
4681
+ await this._connectionPromise;
4679
4682
  } catch {
4683
+ this._connectionPromise = null;
4680
4684
  }
4681
4685
  }
4682
4686
  /**
@@ -4840,9 +4844,7 @@ var LarkDatabase = class {
4840
4844
  * @internal Send a transaction to the server.
4841
4845
  */
4842
4846
  async _sendTransaction(ops) {
4843
- if (!this.isAuthenticatedOrThrow()) {
4844
- if (!this.isAuthenticatedOrThrow()) await this.waitForAuthenticated();
4845
- }
4847
+ if (this._state !== "authenticated") await this.ensureConnected();
4846
4848
  const requestId = this.messageQueue.nextRequestId();
4847
4849
  this.pendingWrites.trackWrite(requestId, "transaction", "/", ops);
4848
4850
  const message = {
@@ -5036,41 +5038,6 @@ var LarkDatabase = class {
5036
5038
  // ============================================
5037
5039
  // Internal: Sending Messages
5038
5040
  // ============================================
5039
- /**
5040
- * Check if authenticated synchronously.
5041
- * Returns true if authenticated, false if connecting (should wait), throws if disconnected.
5042
- */
5043
- isAuthenticatedOrThrow() {
5044
- if (this._state === "authenticated") {
5045
- return true;
5046
- }
5047
- if (this._state === "connecting" || this._state === "connected" || this._state === "joined" || this._state === "reconnecting") {
5048
- return false;
5049
- }
5050
- throw new LarkError("not_connected", "Not connected - call connect() first");
5051
- }
5052
- /**
5053
- * Wait for authentication to complete before performing an operation.
5054
- * If already authenticated, returns immediately (synchronously).
5055
- * If connecting/reconnecting, waits for auth to complete.
5056
- * If disconnected and no connect in progress, throws.
5057
- *
5058
- * IMPORTANT: This returns a Promise only if waiting is needed.
5059
- * Callers should use: `if (!this.isAuthenticatedOrThrow()) if (!this.isAuthenticatedOrThrow()) await this.waitForAuthenticated();`
5060
- * to preserve synchronous execution when already authenticated.
5061
- */
5062
- async waitForAuthenticated() {
5063
- if (this._state === "authenticated") {
5064
- return;
5065
- }
5066
- if (this._state === "connecting" || this._state === "connected" || this._state === "joined" || this._state === "reconnecting") {
5067
- if (this.authenticationPromise) {
5068
- await this.authenticationPromise;
5069
- return;
5070
- }
5071
- }
5072
- throw new LarkError("not_connected", "Not connected - call connect() first");
5073
- }
5074
5041
  send(message) {
5075
5042
  if (!this.transport || !this.transport.connected) {
5076
5043
  throw new LarkError("not_connected", "Not connected to database");
@@ -5085,7 +5052,7 @@ var LarkDatabase = class {
5085
5052
  * Note: Priority is now part of the value (as .priority), not a separate field.
5086
5053
  */
5087
5054
  async _sendSet(path, value) {
5088
- if (!this.isAuthenticatedOrThrow()) await this.waitForAuthenticated();
5055
+ if (this._state !== "authenticated") await this.ensureConnected();
5089
5056
  const normalizedPath = normalizePath(path) || "/";
5090
5057
  validateWriteData(value, normalizedPath);
5091
5058
  validateWriteSize(value, normalizedPath);
@@ -5111,7 +5078,7 @@ var LarkDatabase = class {
5111
5078
  * @internal Send an update operation.
5112
5079
  */
5113
5080
  async _sendUpdate(path, values) {
5114
- if (!this.isAuthenticatedOrThrow()) await this.waitForAuthenticated();
5081
+ if (this._state !== "authenticated") await this.ensureConnected();
5115
5082
  const normalizedPath = normalizePath(path) || "/";
5116
5083
  for (const [key, value] of Object.entries(values)) {
5117
5084
  const fullPath = key.startsWith("/") ? key : `${normalizedPath}/${key}`;
@@ -5144,7 +5111,7 @@ var LarkDatabase = class {
5144
5111
  * @internal Send a delete operation.
5145
5112
  */
5146
5113
  async _sendDelete(path) {
5147
- if (!this.isAuthenticatedOrThrow()) await this.waitForAuthenticated();
5114
+ if (this._state !== "authenticated") await this.ensureConnected();
5148
5115
  const normalizedPath = normalizePath(path) || "/";
5149
5116
  const requestId = this.messageQueue.nextRequestId();
5150
5117
  const pendingWriteIds = this.subscriptionManager.getPendingWriteIdsForPath(normalizedPath);
@@ -5253,7 +5220,7 @@ var LarkDatabase = class {
5253
5220
  return new DataSnapshot(cached.value, path, this);
5254
5221
  }
5255
5222
  }
5256
- if (!this.isAuthenticatedOrThrow()) await this.waitForAuthenticated();
5223
+ if (this._state !== "authenticated") await this.ensureConnected();
5257
5224
  const requestId = this.messageQueue.nextRequestId();
5258
5225
  const message = {
5259
5226
  o: "o",
@@ -5270,7 +5237,7 @@ var LarkDatabase = class {
5270
5237
  * @internal Send an onDisconnect operation.
5271
5238
  */
5272
5239
  async _sendOnDisconnect(path, action, value) {
5273
- if (!this.isAuthenticatedOrThrow()) await this.waitForAuthenticated();
5240
+ if (this._state !== "authenticated") await this.ensureConnected();
5274
5241
  const requestId = this.messageQueue.nextRequestId();
5275
5242
  const message = {
5276
5243
  o: "od",
@@ -5296,13 +5263,12 @@ var LarkDatabase = class {
5296
5263
  * @internal Send a subscribe message to server.
5297
5264
  * Includes tag for non-default queries to enable proper event routing.
5298
5265
  */
5299
- async sendSubscribeMessage(path, eventTypes, queryParams, tag) {
5300
- if (!this.isAuthenticatedOrThrow()) await this.waitForAuthenticated();
5266
+ async sendSubscribeMessage(path, queryParams, tag) {
5267
+ if (this._state !== "authenticated") await this.ensureConnected();
5301
5268
  const requestId = this.messageQueue.nextRequestId();
5302
5269
  const message = {
5303
5270
  o: "sb",
5304
5271
  p: normalizePath(path) || "/",
5305
- e: eventTypes,
5306
5272
  r: requestId,
5307
5273
  ...queryParams,
5308
5274
  ...tag !== void 0 ? { tag } : {}
@@ -5315,7 +5281,7 @@ var LarkDatabase = class {
5315
5281
  * Includes query params and tag so server can identify which specific subscription to remove.
5316
5282
  */
5317
5283
  async sendUnsubscribeMessage(path, queryParams, tag) {
5318
- if (!this.isAuthenticatedOrThrow()) await this.waitForAuthenticated();
5284
+ if (this._state !== "authenticated") await this.ensureConnected();
5319
5285
  const requestId = this.messageQueue.nextRequestId();
5320
5286
  const message = {
5321
5287
  o: "us",
@@ -5380,4 +5346,4 @@ export {
5380
5346
  ServerValue,
5381
5347
  LarkDatabase
5382
5348
  };
5383
- //# sourceMappingURL=chunk-UF3SZ62Y.mjs.map
5349
+ //# sourceMappingURL=chunk-ELNTJSEE.mjs.map