@rocicorp/zero 0.25.10-canary.21 → 0.25.10-canary.22

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,4 +1,4 @@
1
- const version = "0.25.10-canary.21";
1
+ const version = "0.25.10-canary.22";
2
2
  const packageJson = {
3
3
  version
4
4
  };
@@ -73,6 +73,9 @@ export declare class ConnectionManager extends Subscribable<ConnectionManagerSta
73
73
  * This starts the timeout timer, but if we've entered disconnected state,
74
74
  * we stay there and continue retrying.
75
75
  *
76
+ * If we're disconnected due to a hidden tab, allow a transition back to
77
+ * connecting when visibility returns.
78
+ *
76
79
  * @returns An object containing a promise that resolves on the next state change.
77
80
  */
78
81
  connecting(reason?: ZeroError): {
@@ -1 +1 @@
1
- {"version":3,"file":"connection-manager.d.ts","sourceRoot":"","sources":["../../../../../zero-client/src/client/connection-manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,YAAY,EAAC,MAAM,qCAAqC,CAAC;AAEjE,OAAO,EAAC,gBAAgB,EAAC,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACf,MAAM,YAAY,CAAC;AAIpB,MAAM,MAAM,sBAAsB,GAC9B;IACE,IAAI,EAAE,gBAAgB,CAAC,YAAY,CAAC;IACpC,MAAM,EAAE,kBAAkB,CAAC;CAC5B,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,UAAU,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;CAChC,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,SAAS,CAAC;CAClC,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,SAAS,CAAC;IACjC,MAAM,EAAE,SAAS,CAAC;CACnB,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,KAAK,CAAC;IAC7B,MAAM,EAAE,SAAS,CAAC;CACnB,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC;IAC9B,MAAM,EAAE,WAAW,CAAC;CACrB,CAAC;AAEN,MAAM,MAAM,wBAAwB,GAAG;IACrC;;;OAGG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7C,CAAC;AAEF,QAAA,MAAM,eAAe,yBAGkB,CAAC;AAExC,KAAK,wBAAwB,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AACjE,KAAK,8BAA8B,GAAG,OAAO,CAC3C,sBAAsB,EACtB;IAAC,IAAI,EAAE,wBAAwB,CAAA;CAAC,CACjC,CAAC;AAEF,qBAAa,iBAAkB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;;gBAgC7D,OAAO,EAAE,wBAAwB;IAiB7C,IAAI,KAAK,IAAI,sBAAsB,CAElC;IAED;;OAEG;IACH,EAAE,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO;IAIrC;;;OAGG;IACH,iBAAiB,IAAI,OAAO;IAI5B;;;OAGG;IACH,MAAM,CAAC,eAAe,CACpB,KAAK,EAAE,sBAAsB,GAC5B,KAAK,IAAI,8BAA8B;IAM1C;;;;OAIG;IACH,qBAAqB,IAAI,OAAO;IAIhC;;;OAGG;IACH,kBAAkB,IAAI,OAAO,CAAC,sBAAsB,CAAC;IAIrD;;;;;;;OAOG;IACH,UAAU,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG;QAC9B,gBAAgB,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;KACnD;IA+CD;;;;;OAKG;IACH,SAAS,IAAI;QAAC,gBAAgB,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAA;KAAC;IAsBhE;;;;;;OAMG;IACH,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG;QACxC,gBAAgB,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;KACnD;IA6BD;;;;;;OAMG;IACH,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG;QAC5B,gBAAgB,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;KACnD;IAuBD;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,EAAE,SAAS,GAAG;QACxB,gBAAgB,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;KACnD;IAuBD;;;OAGG;IACH,MAAM;IAqBG,OAAO,QAAO,IAAI,CAGzB;CA4DH;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAI,OAAO,sBAAsB,SAkBnE,CAAC"}
1
+ {"version":3,"file":"connection-manager.d.ts","sourceRoot":"","sources":["../../../../../zero-client/src/client/connection-manager.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,YAAY,EAAC,MAAM,qCAAqC,CAAC;AAEjE,OAAO,EAAC,gBAAgB,EAAC,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,SAAS,EACf,MAAM,YAAY,CAAC;AAIpB,MAAM,MAAM,sBAAsB,GAC9B;IACE,IAAI,EAAE,gBAAgB,CAAC,YAAY,CAAC;IACpC,MAAM,EAAE,kBAAkB,CAAC;CAC5B,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,UAAU,CAAC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;CAChC,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,SAAS,CAAC;CAClC,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,SAAS,CAAC;IACjC,MAAM,EAAE,SAAS,CAAC;CACnB,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,KAAK,CAAC;IAC7B,MAAM,EAAE,SAAS,CAAC;CACnB,GACD;IACE,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC;IAC9B,MAAM,EAAE,WAAW,CAAC;CACrB,CAAC;AAEN,MAAM,MAAM,wBAAwB,GAAG;IACrC;;;OAGG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,sBAAsB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC7C,CAAC;AAEF,QAAA,MAAM,eAAe,yBAGkB,CAAC;AAExC,KAAK,wBAAwB,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AACjE,KAAK,8BAA8B,GAAG,OAAO,CAC3C,sBAAsB,EACtB;IAAC,IAAI,EAAE,wBAAwB,CAAA;CAAC,CACjC,CAAC;AAEF,qBAAa,iBAAkB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;;gBAgC7D,OAAO,EAAE,wBAAwB;IAiB7C,IAAI,KAAK,IAAI,sBAAsB,CAElC;IAED;;OAEG;IACH,EAAE,CAAC,MAAM,EAAE,gBAAgB,GAAG,OAAO;IAIrC;;;OAGG;IACH,iBAAiB,IAAI,OAAO;IAI5B;;;OAGG;IACH,MAAM,CAAC,eAAe,CACpB,KAAK,EAAE,sBAAsB,GAC5B,KAAK,IAAI,8BAA8B;IAM1C;;;;OAIG;IACH,qBAAqB,IAAI,OAAO;IAIhC;;;OAGG;IACH,kBAAkB,IAAI,OAAO,CAAC,sBAAsB,CAAC;IAIrD;;;;;;;;;;OAUG;IACH,UAAU,CAAC,MAAM,CAAC,EAAE,SAAS,GAAG;QAC9B,gBAAgB,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;KACnD;IA0DD;;;;;OAKG;IACH,SAAS,IAAI;QAAC,gBAAgB,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAA;KAAC;IAsBhE;;;;;;OAMG;IACH,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG;QACxC,gBAAgB,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;KACnD;IA6BD;;;;;;OAMG;IACH,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG;QAC5B,gBAAgB,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;KACnD;IAuBD;;;;;;OAMG;IACH,KAAK,CAAC,MAAM,EAAE,SAAS,GAAG;QACxB,gBAAgB,EAAE,OAAO,CAAC,sBAAsB,CAAC,CAAC;KACnD;IAuBD;;;OAGG;IACH,MAAM;IAqBG,OAAO,QAAO,IAAI,CAGzB;CA4DH;AAED;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAI,OAAO,sBAAsB,SAmBnE,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { resolver } from "@rocicorp/resolver";
2
2
  import { Subscribable } from "../../../shared/src/subscribable.js";
3
- import { ClientClosed, Offline, ConnectTimeout, AbruptClose, CleanClose } from "./client-error-kind-enum.js";
3
+ import { Hidden, ClientClosed, Offline, ConnectTimeout, AbruptClose, CleanClose } from "./client-error-kind-enum.js";
4
4
  import { Connecting, NeedsAuth, Error, Closed, Disconnected, Connected } from "./connection-status-enum.js";
5
5
  import { ClientError, isClientError } from "./error.js";
6
6
  const DEFAULT_TIMEOUT_CHECK_INTERVAL_MS = 1e3;
@@ -93,16 +93,23 @@ class ConnectionManager extends Subscribable {
93
93
  * This starts the timeout timer, but if we've entered disconnected state,
94
94
  * we stay there and continue retrying.
95
95
  *
96
+ * If we're disconnected due to a hidden tab, allow a transition back to
97
+ * connecting when visibility returns.
98
+ *
96
99
  * @returns An object containing a promise that resolves on the next state change.
97
100
  */
98
101
  connecting(reason) {
99
102
  if (this.#state.name === Closed) {
100
103
  return { nextStatePromise: this.#nextStatePromise() };
101
104
  }
102
- if (this.#state.name === Disconnected) {
105
+ const isHiddenDisconnect = this.#state.name === Disconnected && this.#state.reason.kind === Hidden;
106
+ if (this.#state.name === Disconnected && !isHiddenDisconnect) {
103
107
  return { nextStatePromise: this.#nextStatePromise() };
104
108
  }
105
109
  const now = Date.now();
110
+ if (isHiddenDisconnect) {
111
+ this.#connectingStartedAt = void 0;
112
+ }
106
113
  if (this.#state.name === Connecting) {
107
114
  this.#state = {
108
115
  ...this.#state,
@@ -297,7 +304,7 @@ class ConnectionManager extends Subscribable {
297
304
  }
298
305
  const throwIfConnectionError = (state) => {
299
306
  if (ConnectionManager.isTerminalState(state) || state.name === Closed || (state.name === Connecting || state.name === Disconnected) && state.reason) {
300
- if (isClientError(state.reason) && (state.reason.kind === ConnectTimeout || state.reason.kind === AbruptClose || state.reason.kind === CleanClose)) {
307
+ if (isClientError(state.reason) && (state.reason.kind === ConnectTimeout || state.reason.kind === AbruptClose || state.reason.kind === CleanClose || state.reason.kind === Hidden)) {
301
308
  return;
302
309
  }
303
310
  throw state.reason;
@@ -1 +1 @@
1
- {"version":3,"file":"connection-manager.js","sources":["../../../../../zero-client/src/client/connection-manager.ts"],"sourcesContent":["import {resolver, type Resolver} from '@rocicorp/resolver';\nimport {Subscribable} from '../../../shared/src/subscribable.ts';\nimport {ClientErrorKind} from './client-error-kind.ts';\nimport {ConnectionStatus} from './connection-status.ts';\nimport {\n ClientError,\n isClientError,\n type AuthError,\n type ClosedError,\n type DisconnectedReason,\n type ZeroError,\n} from './error.ts';\n\nconst DEFAULT_TIMEOUT_CHECK_INTERVAL_MS = 1_000;\n\nexport type ConnectionManagerState =\n | {\n name: ConnectionStatus.Disconnected;\n reason: DisconnectedReason;\n }\n | {\n name: ConnectionStatus.Connecting;\n attempt: number;\n disconnectAt: number;\n reason?: ZeroError | undefined;\n }\n | {\n name: ConnectionStatus.Connected;\n }\n | {\n name: ConnectionStatus.NeedsAuth;\n reason: AuthError;\n }\n | {\n name: ConnectionStatus.Error;\n reason: ZeroError;\n }\n | {\n name: ConnectionStatus.Closed;\n reason: ClosedError;\n };\n\nexport type ConnectionManagerOptions = {\n /**\n * The amount of milliseconds we allow for continuous connecting attempts before\n * transitioning to disconnected state.\n */\n disconnectTimeout: number;\n /**\n * How frequently we check whether the connecting timeout has elapsed.\n * Defaults to 1 second.\n */\n timeoutCheckIntervalMs?: number | undefined;\n};\n\nconst TERMINAL_STATES = [\n ConnectionStatus.NeedsAuth,\n ConnectionStatus.Error,\n] as const satisfies ConnectionStatus[];\n\ntype TerminalConnectionStatus = (typeof TERMINAL_STATES)[number];\ntype TerminalConnectionManagerState = Extract<\n ConnectionManagerState,\n {name: TerminalConnectionStatus}\n>;\n\nexport class ConnectionManager extends Subscribable<ConnectionManagerState> {\n #state: ConnectionManagerState;\n\n /**\n * The timestamp when we first started trying to connect.\n * This is used to track the retry window.\n * Reset to undefined when we successfully connect or when we transition to disconnected.\n */\n #connectingStartedAt: number | undefined;\n\n /**\n * The amount of milliseconds we allow for continuous connecting attempts before\n * transitioning to disconnected state.\n */\n #disconnectTimeout: number;\n\n /**\n * Handle for the timeout interval that periodically checks whether we've\n * exceeded the allowed connecting window.\n */\n #timeoutInterval: ReturnType<typeof setInterval> | undefined;\n\n /**\n * Interval duration for checking whether the connecting timeout has elapsed.\n */\n #timeoutCheckIntervalMs: number;\n\n /**\n * Resolver used to signal waiting callers when the state changes.\n */\n #stateChangeResolver: Resolver<ConnectionManagerState> = resolver();\n\n constructor(options: ConnectionManagerOptions) {\n super();\n\n const now = Date.now();\n\n this.#disconnectTimeout = options.disconnectTimeout;\n this.#timeoutCheckIntervalMs =\n options.timeoutCheckIntervalMs ?? DEFAULT_TIMEOUT_CHECK_INTERVAL_MS;\n this.#state = {\n name: ConnectionStatus.Connecting,\n attempt: 0,\n disconnectAt: now + this.#disconnectTimeout,\n };\n this.#connectingStartedAt = now;\n this.#maybeStartTimeoutInterval();\n }\n\n get state(): ConnectionManagerState {\n return this.#state;\n }\n\n /**\n * Returns true if the current state is equal to the given status.\n */\n is(status: ConnectionStatus): boolean {\n return this.#state.name === status;\n }\n\n /**\n * Returns true if the current state is a terminal state\n * that can be recovered from by calling connect().\n */\n isInTerminalState(): boolean {\n return ConnectionManager.isTerminalState(this.#state);\n }\n\n /**\n * Returns true if the given status is a terminal state\n * that can be recovered from by calling connect().\n */\n static isTerminalState(\n state: ConnectionManagerState,\n ): state is TerminalConnectionManagerState {\n return (TERMINAL_STATES as readonly ConnectionStatus[]).includes(\n state.name,\n );\n }\n\n /**\n * Returns true if the run loop should continue.\n * The run loop continues in all states except closed.\n * In needs-auth and error states, the run loop pauses and waits for connect() to be called.\n */\n shouldContinueRunLoop(): boolean {\n return this.#state.name !== ConnectionStatus.Closed;\n }\n\n /**\n * Waits for the next state change.\n * @returns A promise that resolves when the next state change occurs.\n */\n waitForStateChange(): Promise<ConnectionManagerState> {\n return this.#nextStatePromise();\n }\n\n /**\n * Transition to connecting state.\n *\n * This starts the timeout timer, but if we've entered disconnected state,\n * we stay there and continue retrying.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n connecting(reason?: ZeroError): {\n nextStatePromise: Promise<ConnectionManagerState>;\n } {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // we cannot intentionally transition from disconnected to connecting\n // disconnected can transition to connected on successful connection\n // or a terminal state\n if (this.#state.name === ConnectionStatus.Disconnected) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n const now = Date.now();\n\n // If we're already connecting, increment the attempt counter\n if (this.#state.name === ConnectionStatus.Connecting) {\n this.#state = {\n ...this.#state,\n attempt: this.#state.attempt + 1,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n this.#maybeStartTimeoutInterval();\n return {nextStatePromise};\n }\n\n // Starting a new connecting session\n // If #connectingStartedAt is undefined, this is a fresh start - set it to now\n // If it's already set, we're retrying within the same retry window, so keep it\n if (this.#connectingStartedAt === undefined) {\n this.#connectingStartedAt = now;\n }\n\n const disconnectAt = this.#connectingStartedAt + this.#disconnectTimeout;\n\n this.#state = {\n name: ConnectionStatus.Connecting,\n attempt: 1,\n disconnectAt,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n this.#maybeStartTimeoutInterval();\n return {nextStatePromise};\n }\n\n /**\n * Transition to connected state.\n * This resets the connecting timeout timer.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n connected(): {nextStatePromise: Promise<ConnectionManagerState>} {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Already connected, no-op\n if (this.#state.name === ConnectionStatus.Connected) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Reset the timeout timer on successful connection\n this.#connectingStartedAt = undefined;\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.Connected,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n return {nextStatePromise};\n }\n\n /**\n * Transition to disconnected state.\n * This is called when the timeout expires.\n * The run loop will continue trying to reconnect.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n disconnected(reason: DisconnectedReason): {\n nextStatePromise: Promise<ConnectionManagerState>;\n } {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Already disconnected, no-op\n if (this.#state.name === ConnectionStatus.Disconnected) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // When transitioning from connected to disconnected, we've lost a connection\n // we previously had. Clear the timeout timer so we can start a fresh timeout window.\n if (this.#state.name === ConnectionStatus.Connected) {\n this.#connectingStartedAt = undefined;\n }\n // When transitioning from connecting to disconnected (e.g., due to timeout),\n // we keep the start time to maintain the context that we've been trying for a while.\n\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.Disconnected,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n return {nextStatePromise};\n }\n\n /**\n * Transition to needs-auth state.\n * This pauses the run loop until connect() is called with new credentials.\n * Resets the retry window and attempt counter.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n needsAuth(reason: AuthError): {\n nextStatePromise: Promise<ConnectionManagerState>;\n } {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Already in needs-auth state, no-op\n if (this.#state.name === ConnectionStatus.NeedsAuth) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Reset the timeout timer and connecting start time\n this.#connectingStartedAt = undefined;\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.NeedsAuth,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n return {nextStatePromise};\n }\n\n /**\n * Transition to error state.\n * This pauses the run loop until connect() is called.\n * Resets the retry window and attempt counter.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n error(reason: ZeroError): {\n nextStatePromise: Promise<ConnectionManagerState>;\n } {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Already in error state, no-op\n if (this.#state.name === ConnectionStatus.Error) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Reset the timeout timer and connecting start time\n this.#connectingStartedAt = undefined;\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.Error,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n return {nextStatePromise};\n }\n\n /**\n * Transition to closed state.\n * This is terminal - no further transitions are allowed.\n */\n closed() {\n // Already closed, no-op\n if (this.#state.name === ConnectionStatus.Closed) {\n return;\n }\n\n this.#connectingStartedAt = undefined;\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.Closed,\n reason: new ClientError({\n kind: ClientErrorKind.ClientClosed,\n message: 'Zero was explicitly closed by calling zero.close()',\n }),\n };\n this.#publishState();\n this.cleanup();\n return;\n }\n\n override cleanup = (): void => {\n this._listeners.clear();\n this.#resolveNextStateWaiters();\n };\n\n #resolveNextStateWaiters(): void {\n this.#stateChangeResolver.resolve(this.#state);\n this.#stateChangeResolver = resolver();\n }\n\n #publishState(): void {\n this.notify(this.#state);\n this.#resolveNextStateWaiters();\n }\n\n #nextStatePromise(): Promise<ConnectionManagerState> {\n return this.#stateChangeResolver.promise;\n }\n\n #publishStateAndGetPromise(): Promise<ConnectionManagerState> {\n this.#publishState();\n return this.#nextStatePromise();\n }\n\n /**\n * Check if we should transition from connecting to disconnected due to timeout.\n * Returns true if the transition happened.\n */\n #checkTimeout(): boolean {\n if (this.#state.name !== ConnectionStatus.Connecting) {\n return false;\n }\n\n const now = Date.now();\n if (now >= this.#state.disconnectAt) {\n this.disconnected(\n new ClientError({\n kind: ClientErrorKind.Offline,\n message: `Zero was unable to connect for ${Math.floor(this.#disconnectTimeout / 1_000)} seconds and was disconnected`,\n }),\n );\n return true;\n }\n\n return false;\n }\n\n #maybeStartTimeoutInterval(): void {\n if (this.#timeoutInterval !== undefined) {\n return;\n }\n this.#timeoutInterval = setInterval(() => {\n this.#checkTimeout();\n }, this.#timeoutCheckIntervalMs);\n }\n\n #maybeStopTimeoutInterval(): void {\n if (this.#timeoutInterval === undefined) {\n return;\n }\n clearInterval(this.#timeoutInterval);\n this.#timeoutInterval = undefined;\n }\n}\n\n/**\n * Used to trigger the catch block when a terminal state is reached.\n *\n * @param state - The current connection state.\n */\nexport const throwIfConnectionError = (state: ConnectionManagerState) => {\n if (\n ConnectionManager.isTerminalState(state) ||\n state.name === ConnectionStatus.Closed ||\n ((state.name === ConnectionStatus.Connecting ||\n state.name === ConnectionStatus.Disconnected) &&\n state.reason)\n ) {\n if (\n isClientError(state.reason) &&\n (state.reason.kind === ClientErrorKind.ConnectTimeout ||\n state.reason.kind === ClientErrorKind.AbruptClose ||\n state.reason.kind === ClientErrorKind.CleanClose)\n ) {\n return;\n }\n throw state.reason;\n }\n};\n"],"names":["ConnectionStatus.NeedsAuth","ConnectionStatus.Error","ConnectionStatus.Connecting","ConnectionStatus.Closed","ConnectionStatus.Disconnected","nextStatePromise","ConnectionStatus.Connected","ClientErrorKind.ClientClosed","ClientErrorKind.Offline","ClientErrorKind.ConnectTimeout","ClientErrorKind.AbruptClose","ClientErrorKind.CleanClose"],"mappings":"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"}
1
+ {"version":3,"file":"connection-manager.js","sources":["../../../../../zero-client/src/client/connection-manager.ts"],"sourcesContent":["import {resolver, type Resolver} from '@rocicorp/resolver';\nimport {Subscribable} from '../../../shared/src/subscribable.ts';\nimport {ClientErrorKind} from './client-error-kind.ts';\nimport {ConnectionStatus} from './connection-status.ts';\nimport {\n ClientError,\n isClientError,\n type AuthError,\n type ClosedError,\n type DisconnectedReason,\n type ZeroError,\n} from './error.ts';\n\nconst DEFAULT_TIMEOUT_CHECK_INTERVAL_MS = 1_000;\n\nexport type ConnectionManagerState =\n | {\n name: ConnectionStatus.Disconnected;\n reason: DisconnectedReason;\n }\n | {\n name: ConnectionStatus.Connecting;\n attempt: number;\n disconnectAt: number;\n reason?: ZeroError | undefined;\n }\n | {\n name: ConnectionStatus.Connected;\n }\n | {\n name: ConnectionStatus.NeedsAuth;\n reason: AuthError;\n }\n | {\n name: ConnectionStatus.Error;\n reason: ZeroError;\n }\n | {\n name: ConnectionStatus.Closed;\n reason: ClosedError;\n };\n\nexport type ConnectionManagerOptions = {\n /**\n * The amount of milliseconds we allow for continuous connecting attempts before\n * transitioning to disconnected state.\n */\n disconnectTimeout: number;\n /**\n * How frequently we check whether the connecting timeout has elapsed.\n * Defaults to 1 second.\n */\n timeoutCheckIntervalMs?: number | undefined;\n};\n\nconst TERMINAL_STATES = [\n ConnectionStatus.NeedsAuth,\n ConnectionStatus.Error,\n] as const satisfies ConnectionStatus[];\n\ntype TerminalConnectionStatus = (typeof TERMINAL_STATES)[number];\ntype TerminalConnectionManagerState = Extract<\n ConnectionManagerState,\n {name: TerminalConnectionStatus}\n>;\n\nexport class ConnectionManager extends Subscribable<ConnectionManagerState> {\n #state: ConnectionManagerState;\n\n /**\n * The timestamp when we first started trying to connect.\n * This is used to track the retry window.\n * Reset to undefined when we successfully connect or when we transition to disconnected.\n */\n #connectingStartedAt: number | undefined;\n\n /**\n * The amount of milliseconds we allow for continuous connecting attempts before\n * transitioning to disconnected state.\n */\n #disconnectTimeout: number;\n\n /**\n * Handle for the timeout interval that periodically checks whether we've\n * exceeded the allowed connecting window.\n */\n #timeoutInterval: ReturnType<typeof setInterval> | undefined;\n\n /**\n * Interval duration for checking whether the connecting timeout has elapsed.\n */\n #timeoutCheckIntervalMs: number;\n\n /**\n * Resolver used to signal waiting callers when the state changes.\n */\n #stateChangeResolver: Resolver<ConnectionManagerState> = resolver();\n\n constructor(options: ConnectionManagerOptions) {\n super();\n\n const now = Date.now();\n\n this.#disconnectTimeout = options.disconnectTimeout;\n this.#timeoutCheckIntervalMs =\n options.timeoutCheckIntervalMs ?? DEFAULT_TIMEOUT_CHECK_INTERVAL_MS;\n this.#state = {\n name: ConnectionStatus.Connecting,\n attempt: 0,\n disconnectAt: now + this.#disconnectTimeout,\n };\n this.#connectingStartedAt = now;\n this.#maybeStartTimeoutInterval();\n }\n\n get state(): ConnectionManagerState {\n return this.#state;\n }\n\n /**\n * Returns true if the current state is equal to the given status.\n */\n is(status: ConnectionStatus): boolean {\n return this.#state.name === status;\n }\n\n /**\n * Returns true if the current state is a terminal state\n * that can be recovered from by calling connect().\n */\n isInTerminalState(): boolean {\n return ConnectionManager.isTerminalState(this.#state);\n }\n\n /**\n * Returns true if the given status is a terminal state\n * that can be recovered from by calling connect().\n */\n static isTerminalState(\n state: ConnectionManagerState,\n ): state is TerminalConnectionManagerState {\n return (TERMINAL_STATES as readonly ConnectionStatus[]).includes(\n state.name,\n );\n }\n\n /**\n * Returns true if the run loop should continue.\n * The run loop continues in all states except closed.\n * In needs-auth and error states, the run loop pauses and waits for connect() to be called.\n */\n shouldContinueRunLoop(): boolean {\n return this.#state.name !== ConnectionStatus.Closed;\n }\n\n /**\n * Waits for the next state change.\n * @returns A promise that resolves when the next state change occurs.\n */\n waitForStateChange(): Promise<ConnectionManagerState> {\n return this.#nextStatePromise();\n }\n\n /**\n * Transition to connecting state.\n *\n * This starts the timeout timer, but if we've entered disconnected state,\n * we stay there and continue retrying.\n *\n * If we're disconnected due to a hidden tab, allow a transition back to\n * connecting when visibility returns.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n connecting(reason?: ZeroError): {\n nextStatePromise: Promise<ConnectionManagerState>;\n } {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n const isHiddenDisconnect =\n this.#state.name === ConnectionStatus.Disconnected &&\n this.#state.reason.kind === ClientErrorKind.Hidden;\n\n // we cannot intentionally transition from disconnected to connecting\n // unless the disconnection was due to a hidden tab\n if (\n this.#state.name === ConnectionStatus.Disconnected &&\n !isHiddenDisconnect\n ) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n const now = Date.now();\n\n if (isHiddenDisconnect) {\n // Reset the retry window after a hidden-tab disconnect.\n this.#connectingStartedAt = undefined;\n }\n\n // If we're already connecting, increment the attempt counter\n if (this.#state.name === ConnectionStatus.Connecting) {\n this.#state = {\n ...this.#state,\n attempt: this.#state.attempt + 1,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n this.#maybeStartTimeoutInterval();\n return {nextStatePromise};\n }\n\n // Starting a new connecting session\n // If #connectingStartedAt is undefined, this is a fresh start - set it to now\n // If it's already set, we're retrying within the same retry window, so keep it\n if (this.#connectingStartedAt === undefined) {\n this.#connectingStartedAt = now;\n }\n\n const disconnectAt = this.#connectingStartedAt + this.#disconnectTimeout;\n\n this.#state = {\n name: ConnectionStatus.Connecting,\n attempt: 1,\n disconnectAt,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n this.#maybeStartTimeoutInterval();\n return {nextStatePromise};\n }\n\n /**\n * Transition to connected state.\n * This resets the connecting timeout timer.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n connected(): {nextStatePromise: Promise<ConnectionManagerState>} {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Already connected, no-op\n if (this.#state.name === ConnectionStatus.Connected) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Reset the timeout timer on successful connection\n this.#connectingStartedAt = undefined;\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.Connected,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n return {nextStatePromise};\n }\n\n /**\n * Transition to disconnected state.\n * This is called when the timeout expires.\n * The run loop will continue trying to reconnect.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n disconnected(reason: DisconnectedReason): {\n nextStatePromise: Promise<ConnectionManagerState>;\n } {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Already disconnected, no-op\n if (this.#state.name === ConnectionStatus.Disconnected) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // When transitioning from connected to disconnected, we've lost a connection\n // we previously had. Clear the timeout timer so we can start a fresh timeout window.\n if (this.#state.name === ConnectionStatus.Connected) {\n this.#connectingStartedAt = undefined;\n }\n // When transitioning from connecting to disconnected (e.g., due to timeout),\n // we keep the start time to maintain the context that we've been trying for a while.\n\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.Disconnected,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n return {nextStatePromise};\n }\n\n /**\n * Transition to needs-auth state.\n * This pauses the run loop until connect() is called with new credentials.\n * Resets the retry window and attempt counter.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n needsAuth(reason: AuthError): {\n nextStatePromise: Promise<ConnectionManagerState>;\n } {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Already in needs-auth state, no-op\n if (this.#state.name === ConnectionStatus.NeedsAuth) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Reset the timeout timer and connecting start time\n this.#connectingStartedAt = undefined;\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.NeedsAuth,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n return {nextStatePromise};\n }\n\n /**\n * Transition to error state.\n * This pauses the run loop until connect() is called.\n * Resets the retry window and attempt counter.\n *\n * @returns An object containing a promise that resolves on the next state change.\n */\n error(reason: ZeroError): {\n nextStatePromise: Promise<ConnectionManagerState>;\n } {\n // cannot transition from closed to any other status\n if (this.#state.name === ConnectionStatus.Closed) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Already in error state, no-op\n if (this.#state.name === ConnectionStatus.Error) {\n return {nextStatePromise: this.#nextStatePromise()};\n }\n\n // Reset the timeout timer and connecting start time\n this.#connectingStartedAt = undefined;\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.Error,\n reason,\n };\n const nextStatePromise = this.#publishStateAndGetPromise();\n return {nextStatePromise};\n }\n\n /**\n * Transition to closed state.\n * This is terminal - no further transitions are allowed.\n */\n closed() {\n // Already closed, no-op\n if (this.#state.name === ConnectionStatus.Closed) {\n return;\n }\n\n this.#connectingStartedAt = undefined;\n this.#maybeStopTimeoutInterval();\n\n this.#state = {\n name: ConnectionStatus.Closed,\n reason: new ClientError({\n kind: ClientErrorKind.ClientClosed,\n message: 'Zero was explicitly closed by calling zero.close()',\n }),\n };\n this.#publishState();\n this.cleanup();\n return;\n }\n\n override cleanup = (): void => {\n this._listeners.clear();\n this.#resolveNextStateWaiters();\n };\n\n #resolveNextStateWaiters(): void {\n this.#stateChangeResolver.resolve(this.#state);\n this.#stateChangeResolver = resolver();\n }\n\n #publishState(): void {\n this.notify(this.#state);\n this.#resolveNextStateWaiters();\n }\n\n #nextStatePromise(): Promise<ConnectionManagerState> {\n return this.#stateChangeResolver.promise;\n }\n\n #publishStateAndGetPromise(): Promise<ConnectionManagerState> {\n this.#publishState();\n return this.#nextStatePromise();\n }\n\n /**\n * Check if we should transition from connecting to disconnected due to timeout.\n * Returns true if the transition happened.\n */\n #checkTimeout(): boolean {\n if (this.#state.name !== ConnectionStatus.Connecting) {\n return false;\n }\n\n const now = Date.now();\n if (now >= this.#state.disconnectAt) {\n this.disconnected(\n new ClientError({\n kind: ClientErrorKind.Offline,\n message: `Zero was unable to connect for ${Math.floor(this.#disconnectTimeout / 1_000)} seconds and was disconnected`,\n }),\n );\n return true;\n }\n\n return false;\n }\n\n #maybeStartTimeoutInterval(): void {\n if (this.#timeoutInterval !== undefined) {\n return;\n }\n this.#timeoutInterval = setInterval(() => {\n this.#checkTimeout();\n }, this.#timeoutCheckIntervalMs);\n }\n\n #maybeStopTimeoutInterval(): void {\n if (this.#timeoutInterval === undefined) {\n return;\n }\n clearInterval(this.#timeoutInterval);\n this.#timeoutInterval = undefined;\n }\n}\n\n/**\n * Used to trigger the catch block when a terminal state is reached.\n *\n * @param state - The current connection state.\n */\nexport const throwIfConnectionError = (state: ConnectionManagerState) => {\n if (\n ConnectionManager.isTerminalState(state) ||\n state.name === ConnectionStatus.Closed ||\n ((state.name === ConnectionStatus.Connecting ||\n state.name === ConnectionStatus.Disconnected) &&\n state.reason)\n ) {\n if (\n isClientError(state.reason) &&\n (state.reason.kind === ClientErrorKind.ConnectTimeout ||\n state.reason.kind === ClientErrorKind.AbruptClose ||\n state.reason.kind === ClientErrorKind.CleanClose ||\n state.reason.kind === ClientErrorKind.Hidden)\n ) {\n return;\n }\n throw state.reason;\n }\n};\n"],"names":["ConnectionStatus.NeedsAuth","ConnectionStatus.Error","ConnectionStatus.Connecting","ConnectionStatus.Closed","ConnectionStatus.Disconnected","ClientErrorKind.Hidden","nextStatePromise","ConnectionStatus.Connected","ClientErrorKind.ClientClosed","ClientErrorKind.Offline","ClientErrorKind.ConnectTimeout","ClientErrorKind.AbruptClose","ClientErrorKind.CleanClose"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- const version = "0.25.10-canary.21";
1
+ const version = "0.25.10-canary.22";
2
2
  export {
3
3
  version
4
4
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rocicorp/zero",
3
- "version": "0.25.10-canary.21",
3
+ "version": "0.25.10-canary.22",
4
4
  "description": "Zero is a web framework for serverless web development.",
5
5
  "author": "Rocicorp, Inc.",
6
6
  "repository": {