convex 1.41.0 → 1.42.1

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.
Files changed (191) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/dist/browser.bundle.js +24 -6
  3. package/dist/browser.bundle.js.map +2 -2
  4. package/dist/cjs/browser/simple_client-node.js +85 -27
  5. package/dist/cjs/browser/simple_client-node.js.map +3 -3
  6. package/dist/cjs/browser/sync/authentication_manager.js +21 -4
  7. package/dist/cjs/browser/sync/authentication_manager.js.map +2 -2
  8. package/dist/cjs/browser/sync/client.js +2 -1
  9. package/dist/cjs/browser/sync/client.js.map +2 -2
  10. package/dist/cjs/cli/env.js +12 -5
  11. package/dist/cjs/cli/env.js.map +2 -2
  12. package/dist/cjs/cli/envDefault.js +12 -7
  13. package/dist/cjs/cli/envDefault.js.map +2 -2
  14. package/dist/cjs/cli/insights.js +11 -1
  15. package/dist/cjs/cli/insights.js.map +2 -2
  16. package/dist/cjs/cli/lib/env.js +5 -1
  17. package/dist/cjs/cli/lib/env.js.map +2 -2
  18. package/dist/cjs/cli/lib/login.js +16 -12
  19. package/dist/cjs/cli/lib/login.js.map +2 -2
  20. package/dist/cjs/cli/lib/logs.js +4 -1
  21. package/dist/cjs/cli/lib/logs.js.map +2 -2
  22. package/dist/cjs/cli/lib/run.js +27 -2
  23. package/dist/cjs/cli/lib/run.js.map +2 -2
  24. package/dist/cjs/cli/lib/typecheck.js +28 -10
  25. package/dist/cjs/cli/lib/typecheck.js.map +2 -2
  26. package/dist/cjs/cli/lib/utils/utils.js +30 -0
  27. package/dist/cjs/cli/lib/utils/utils.js.map +2 -2
  28. package/dist/cjs/cli/program.js +2 -1
  29. package/dist/cjs/cli/program.js.map +2 -2
  30. package/dist/cjs/cli/project.js +27 -0
  31. package/dist/cjs/cli/project.js.map +7 -0
  32. package/dist/cjs/cli/projectCreate.js +90 -0
  33. package/dist/cjs/cli/projectCreate.js.map +7 -0
  34. package/dist/cjs/index.js +1 -1
  35. package/dist/cjs/index.js.map +1 -1
  36. package/dist/cjs/server/database.js.map +1 -1
  37. package/dist/cjs/server/impl/meta_impl.js +6 -6
  38. package/dist/cjs/server/impl/meta_impl.js.map +2 -2
  39. package/dist/cjs/server/impl/registration_impl.js +9 -4
  40. package/dist/cjs/server/impl/registration_impl.js.map +2 -2
  41. package/dist/cjs/server/index.js.map +2 -2
  42. package/dist/cjs/server/meta.js.map +1 -1
  43. package/dist/cjs/server/query.js.map +1 -1
  44. package/dist/cjs/server/registration.js.map +1 -1
  45. package/dist/cjs/server/storage.js.map +1 -1
  46. package/dist/cjs/server/system_fields.js.map +1 -1
  47. package/dist/cjs-types/browser/sync/authentication_manager.d.ts +2 -0
  48. package/dist/cjs-types/browser/sync/authentication_manager.d.ts.map +1 -1
  49. package/dist/cjs-types/browser/sync/client.d.ts +16 -0
  50. package/dist/cjs-types/browser/sync/client.d.ts.map +1 -1
  51. package/dist/cjs-types/cli/env.d.ts.map +1 -1
  52. package/dist/cjs-types/cli/envDefault.d.ts.map +1 -1
  53. package/dist/cjs-types/cli/insights.d.ts +1 -0
  54. package/dist/cjs-types/cli/insights.d.ts.map +1 -1
  55. package/dist/cjs-types/cli/lib/env.d.ts +3 -1
  56. package/dist/cjs-types/cli/lib/env.d.ts.map +1 -1
  57. package/dist/cjs-types/cli/lib/env.test.d.ts +2 -0
  58. package/dist/cjs-types/cli/lib/env.test.d.ts.map +1 -0
  59. package/dist/cjs-types/cli/lib/login.d.ts.map +1 -1
  60. package/dist/cjs-types/cli/lib/logs.d.ts +1 -1
  61. package/dist/cjs-types/cli/lib/logs.d.ts.map +1 -1
  62. package/dist/cjs-types/cli/lib/run.d.ts +1 -1
  63. package/dist/cjs-types/cli/lib/run.d.ts.map +1 -1
  64. package/dist/cjs-types/cli/lib/utils/utils.d.ts +10 -0
  65. package/dist/cjs-types/cli/lib/utils/utils.d.ts.map +1 -1
  66. package/dist/cjs-types/cli/program.d.ts.map +1 -1
  67. package/dist/cjs-types/cli/project.d.ts +3 -0
  68. package/dist/cjs-types/cli/project.d.ts.map +1 -0
  69. package/dist/cjs-types/cli/projectCreate.d.ts +5 -0
  70. package/dist/cjs-types/cli/projectCreate.d.ts.map +1 -0
  71. package/dist/cjs-types/index.d.ts +1 -1
  72. package/dist/cjs-types/server/database.d.ts +21 -3
  73. package/dist/cjs-types/server/database.d.ts.map +1 -1
  74. package/dist/cjs-types/server/impl/meta_impl.d.ts.map +1 -1
  75. package/dist/cjs-types/server/impl/registration_impl.d.ts.map +1 -1
  76. package/dist/cjs-types/server/index.d.ts +1 -1
  77. package/dist/cjs-types/server/index.d.ts.map +1 -1
  78. package/dist/cjs-types/server/meta.d.ts +10 -0
  79. package/dist/cjs-types/server/meta.d.ts.map +1 -1
  80. package/dist/cjs-types/server/query.d.ts +1 -1
  81. package/dist/cjs-types/server/registration.d.ts +15 -3
  82. package/dist/cjs-types/server/registration.d.ts.map +1 -1
  83. package/dist/cjs-types/server/storage.d.ts +20 -3
  84. package/dist/cjs-types/server/storage.d.ts.map +1 -1
  85. package/dist/cli.bundle.cjs +1099 -634
  86. package/dist/cli.bundle.cjs.map +4 -4
  87. package/dist/esm/browser/simple_client-node.js +85 -27
  88. package/dist/esm/browser/simple_client-node.js.map +3 -3
  89. package/dist/esm/browser/sync/authentication_manager.js +21 -4
  90. package/dist/esm/browser/sync/authentication_manager.js.map +2 -2
  91. package/dist/esm/browser/sync/client.js +2 -1
  92. package/dist/esm/browser/sync/client.js.map +2 -2
  93. package/dist/esm/cli/env.js +12 -5
  94. package/dist/esm/cli/env.js.map +2 -2
  95. package/dist/esm/cli/envDefault.js +12 -7
  96. package/dist/esm/cli/envDefault.js.map +2 -2
  97. package/dist/esm/cli/insights.js +11 -1
  98. package/dist/esm/cli/insights.js.map +2 -2
  99. package/dist/esm/cli/lib/env.js +5 -1
  100. package/dist/esm/cli/lib/env.js.map +2 -2
  101. package/dist/esm/cli/lib/login.js +18 -13
  102. package/dist/esm/cli/lib/login.js.map +2 -2
  103. package/dist/esm/cli/lib/logs.js +5 -2
  104. package/dist/esm/cli/lib/logs.js.map +2 -2
  105. package/dist/esm/cli/lib/run.js +32 -3
  106. package/dist/esm/cli/lib/run.js.map +2 -2
  107. package/dist/esm/cli/lib/typecheck.js +28 -10
  108. package/dist/esm/cli/lib/typecheck.js.map +3 -3
  109. package/dist/esm/cli/lib/utils/utils.js +29 -0
  110. package/dist/esm/cli/lib/utils/utils.js.map +2 -2
  111. package/dist/esm/cli/program.js +2 -1
  112. package/dist/esm/cli/program.js.map +2 -2
  113. package/dist/esm/cli/project.js +5 -0
  114. package/dist/esm/cli/project.js.map +7 -0
  115. package/dist/esm/cli/projectCreate.js +68 -0
  116. package/dist/esm/cli/projectCreate.js.map +7 -0
  117. package/dist/esm/index.js +1 -1
  118. package/dist/esm/index.js.map +1 -1
  119. package/dist/esm/server/impl/meta_impl.js +6 -6
  120. package/dist/esm/server/impl/meta_impl.js.map +2 -2
  121. package/dist/esm/server/impl/registration_impl.js +9 -4
  122. package/dist/esm/server/impl/registration_impl.js.map +2 -2
  123. package/dist/esm/server/index.js.map +2 -2
  124. package/dist/esm-types/browser/sync/authentication_manager.d.ts +2 -0
  125. package/dist/esm-types/browser/sync/authentication_manager.d.ts.map +1 -1
  126. package/dist/esm-types/browser/sync/client.d.ts +16 -0
  127. package/dist/esm-types/browser/sync/client.d.ts.map +1 -1
  128. package/dist/esm-types/cli/env.d.ts.map +1 -1
  129. package/dist/esm-types/cli/envDefault.d.ts.map +1 -1
  130. package/dist/esm-types/cli/insights.d.ts +1 -0
  131. package/dist/esm-types/cli/insights.d.ts.map +1 -1
  132. package/dist/esm-types/cli/lib/env.d.ts +3 -1
  133. package/dist/esm-types/cli/lib/env.d.ts.map +1 -1
  134. package/dist/esm-types/cli/lib/env.test.d.ts +2 -0
  135. package/dist/esm-types/cli/lib/env.test.d.ts.map +1 -0
  136. package/dist/esm-types/cli/lib/login.d.ts.map +1 -1
  137. package/dist/esm-types/cli/lib/logs.d.ts +1 -1
  138. package/dist/esm-types/cli/lib/logs.d.ts.map +1 -1
  139. package/dist/esm-types/cli/lib/run.d.ts +1 -1
  140. package/dist/esm-types/cli/lib/run.d.ts.map +1 -1
  141. package/dist/esm-types/cli/lib/utils/utils.d.ts +10 -0
  142. package/dist/esm-types/cli/lib/utils/utils.d.ts.map +1 -1
  143. package/dist/esm-types/cli/program.d.ts.map +1 -1
  144. package/dist/esm-types/cli/project.d.ts +3 -0
  145. package/dist/esm-types/cli/project.d.ts.map +1 -0
  146. package/dist/esm-types/cli/projectCreate.d.ts +5 -0
  147. package/dist/esm-types/cli/projectCreate.d.ts.map +1 -0
  148. package/dist/esm-types/index.d.ts +1 -1
  149. package/dist/esm-types/server/database.d.ts +21 -3
  150. package/dist/esm-types/server/database.d.ts.map +1 -1
  151. package/dist/esm-types/server/impl/meta_impl.d.ts.map +1 -1
  152. package/dist/esm-types/server/impl/registration_impl.d.ts.map +1 -1
  153. package/dist/esm-types/server/index.d.ts +1 -1
  154. package/dist/esm-types/server/index.d.ts.map +1 -1
  155. package/dist/esm-types/server/meta.d.ts +10 -0
  156. package/dist/esm-types/server/meta.d.ts.map +1 -1
  157. package/dist/esm-types/server/query.d.ts +1 -1
  158. package/dist/esm-types/server/registration.d.ts +15 -3
  159. package/dist/esm-types/server/registration.d.ts.map +1 -1
  160. package/dist/esm-types/server/storage.d.ts +20 -3
  161. package/dist/esm-types/server/storage.d.ts.map +1 -1
  162. package/dist/react.bundle.js +24 -6
  163. package/dist/react.bundle.js.map +2 -2
  164. package/package.json +4 -4
  165. package/src/browser/sync/authentication_manager.ts +42 -14
  166. package/src/browser/sync/client.ts +17 -0
  167. package/src/cli/env.ts +16 -6
  168. package/src/cli/envDefault.ts +13 -7
  169. package/src/cli/insights.ts +12 -0
  170. package/src/cli/lib/env.test.ts +60 -0
  171. package/src/cli/lib/env.ts +9 -1
  172. package/src/cli/lib/login.ts +18 -18
  173. package/src/cli/lib/logs.ts +7 -2
  174. package/src/cli/lib/run.ts +37 -3
  175. package/src/cli/lib/typecheck.ts +26 -10
  176. package/src/cli/lib/utils/utils.ts +56 -0
  177. package/src/cli/program.ts +2 -0
  178. package/src/cli/project.ts +7 -0
  179. package/src/cli/projectCreate.ts +99 -0
  180. package/src/index.ts +1 -1
  181. package/src/react/ConvexAuthState.test.tsx +145 -0
  182. package/src/server/database.ts +21 -3
  183. package/src/server/impl/meta_impl.ts +7 -6
  184. package/src/server/impl/registration_impl.ts +11 -5
  185. package/src/server/index.ts +1 -0
  186. package/src/server/meta.ts +10 -0
  187. package/src/server/query.ts +1 -1
  188. package/src/server/registration.test.ts +25 -1
  189. package/src/server/registration.ts +16 -1
  190. package/src/server/storage.ts +22 -3
  191. package/src/server/system_fields.ts +1 -1
@@ -44,6 +44,7 @@ class AuthenticationManager {
44
44
  __publicField(this, "clearAuth");
45
45
  __publicField(this, "logger");
46
46
  __publicField(this, "refreshTokenLeewaySeconds");
47
+ __publicField(this, "initialAuthTokenReuse");
47
48
  // Track last value to avoid redundant calls
48
49
  __publicField(this, "lastRefreshChange");
49
50
  // Number of times we have attempted to confirm the latest token. We retry up
@@ -58,6 +59,7 @@ class AuthenticationManager {
58
59
  this.clearAuth = callbacks.clearAuth;
59
60
  this.logger = config.logger;
60
61
  this.refreshTokenLeewaySeconds = config.refreshTokenLeewaySeconds;
62
+ this.initialAuthTokenReuse = config.initialAuthTokenReuse;
61
63
  this.lastRefreshChange = false;
62
64
  }
63
65
  notifyRefreshChange(isRefreshing) {
@@ -113,7 +115,12 @@ class AuthenticationManager {
113
115
  this.syncState.markAuthCompletion();
114
116
  if (this.authState.state === "waitingForServerConfirmationOfCachedToken") {
115
117
  this._logVerbose("server confirmed auth token is valid");
116
- void this.refetchToken();
118
+ const cachedToken = this.syncState.getAuth()?.value;
119
+ if (this.initialAuthTokenReuse && cachedToken) {
120
+ this.scheduleTokenRefetch(cachedToken, serverMessage.clientClockSkew);
121
+ } else {
122
+ void this.refetchToken();
123
+ }
117
124
  this.authState.config.onAuthChange(true);
118
125
  return;
119
126
  }
@@ -244,7 +251,7 @@ class AuthenticationManager {
244
251
  );
245
252
  this.tryRestartSocket();
246
253
  }
247
- scheduleTokenRefetch(token) {
254
+ scheduleTokenRefetch(token, clientClockSkewMs) {
248
255
  if (this.authState.state === "noAuth") {
249
256
  return;
250
257
  }
@@ -262,13 +269,23 @@ class AuthenticationManager {
262
269
  );
263
270
  return;
264
271
  }
265
- const tokenValiditySeconds = exp - iat;
266
- if (tokenValiditySeconds <= 2) {
272
+ const fullLifetimeSeconds = exp - iat;
273
+ if (fullLifetimeSeconds <= 2) {
267
274
  this.logger.error(
268
275
  "Auth token does not live long enough, cannot refetch the token"
269
276
  );
270
277
  return;
271
278
  }
279
+ let tokenValiditySeconds;
280
+ if (clientClockSkewMs !== void 0) {
281
+ const estimatedServerNowSeconds = (Date.now() - clientClockSkewMs) / 1e3;
282
+ tokenValiditySeconds = exp - estimatedServerNowSeconds;
283
+ if (tokenValiditySeconds <= 0) {
284
+ tokenValiditySeconds = 0;
285
+ }
286
+ } else {
287
+ tokenValiditySeconds = fullLifetimeSeconds;
288
+ }
272
289
  let delay = Math.min(
273
290
  MAXIMUM_REFRESH_DELAY,
274
291
  (tokenValiditySeconds - this.refreshTokenLeewaySeconds) * 1e3
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/browser/sync/authentication_manager.ts"],
4
- "sourcesContent": ["import { Logger } from \"../logging.js\";\nimport { LocalSyncState } from \"./local_state.js\";\nimport { AuthError, IdentityVersion, Transition } from \"./protocol.js\";\nimport { jwtDecode } from \"../../vendor/jwt-decode/index.js\";\n\n// setTimout uses 32 bit integer, so it can only\n// schedule about 24 days in the future.\nconst MAXIMUM_REFRESH_DELAY = 20 * 24 * 60 * 60 * 1000; // 20 days\n\nconst MAX_TOKEN_CONFIRMATION_ATTEMPTS = 2;\n\n/**\n * An async function returning a JWT. Depending on the auth providers\n * configured in convex/auth.config.ts, this may be a JWT-encoded OpenID\n * Connect Identity Token or a traditional JWT.\n *\n * `forceRefreshToken` is `true` if the server rejected a previously\n * returned token or the token is anticipated to expiring soon\n * based on its `exp` time.\n *\n * See {@link ConvexReactClient.setAuth}.\n *\n * @public\n */\nexport type AuthTokenFetcher = (args: {\n forceRefreshToken: boolean;\n}) => Promise<string | null | undefined>;\n\n/**\n * What is provided to the client.\n */\ntype AuthConfig = {\n fetchToken: AuthTokenFetcher;\n onAuthChange: (isAuthenticated: boolean) => void;\n onRefreshChange?: ((isRefreshing: boolean) => void) | undefined;\n};\n\n/**\n * In general we take 3 steps:\n * 1. Fetch a possibly cached token\n * 2. Immediately fetch a fresh token without using a cache\n * 3. Repeat step 2 before the end of the fresh token's lifetime\n *\n * When we fetch without using a cache we know when the token\n * will expire, and can schedule refetching it.\n *\n * If we get an error before a scheduled refetch, we go back\n * to step 2.\n */\ntype AuthState =\n | { state: \"noAuth\" }\n | {\n state: \"waitingForServerConfirmationOfCachedToken\";\n config: AuthConfig;\n hasRetried: boolean;\n }\n | {\n state: \"initialRefetch\";\n config: AuthConfig;\n }\n | {\n state: \"waitingForServerConfirmationOfFreshToken\";\n config: AuthConfig;\n hadAuth: boolean;\n token: string;\n }\n | {\n state: \"waitingForScheduledRefetch\";\n config: AuthConfig;\n refetchTokenTimeoutId: ReturnType<typeof setTimeout>;\n }\n // Special/weird state when we got a valid token\n // but could not fetch a new one.\n | {\n state: \"notRefetching\";\n config: AuthConfig;\n };\n\n/**\n * Handles the state transitions for auth. The server is the source\n * of truth.\n */\nexport class AuthenticationManager {\n private authState: AuthState = { state: \"noAuth\" };\n // Used to detect races involving `setConfig` calls\n // while a token is being fetched.\n private configVersion = 0;\n // Shared by the BaseClient so that the auth manager can easily inspect it\n private readonly syncState: LocalSyncState;\n // Passed down by BaseClient, sends a message to the server\n private readonly authenticate: (token: string) => IdentityVersion;\n private readonly stopSocket: () => Promise<void>;\n private readonly tryRestartSocket: () => void;\n private readonly pauseSocket: () => void;\n private readonly resumeSocket: () => void;\n // Passed down by BaseClient, sends a message to the server\n private readonly clearAuth: () => void;\n private readonly logger: Logger;\n private readonly refreshTokenLeewaySeconds: number;\n // Track last value to avoid redundant calls\n private lastRefreshChange: boolean;\n // Number of times we have attempted to confirm the latest token. We retry up\n // to `MAX_TOKEN_CONFIRMATION_ATTEMPTS` times.\n private tokenConfirmationAttempts = 0;\n constructor(\n syncState: LocalSyncState,\n callbacks: {\n authenticate: (token: string) => IdentityVersion;\n stopSocket: () => Promise<void>;\n tryRestartSocket: () => void;\n pauseSocket: () => void;\n resumeSocket: () => void;\n clearAuth: () => void;\n },\n config: {\n refreshTokenLeewaySeconds: number;\n logger: Logger;\n },\n ) {\n this.syncState = syncState;\n this.authenticate = callbacks.authenticate;\n this.stopSocket = callbacks.stopSocket;\n this.tryRestartSocket = callbacks.tryRestartSocket;\n this.pauseSocket = callbacks.pauseSocket;\n this.resumeSocket = callbacks.resumeSocket;\n this.clearAuth = callbacks.clearAuth;\n this.logger = config.logger;\n this.refreshTokenLeewaySeconds = config.refreshTokenLeewaySeconds;\n this.lastRefreshChange = false;\n }\n\n private notifyRefreshChange(isRefreshing: boolean) {\n if (\n this.authState.state !== \"noAuth\" &&\n this.authState.state !== \"initialRefetch\" &&\n this.authState.config.onRefreshChange &&\n this.lastRefreshChange !== isRefreshing\n ) {\n this.lastRefreshChange = isRefreshing;\n this.authState.config.onRefreshChange(isRefreshing);\n }\n }\n\n async setConfig(\n fetchToken: AuthTokenFetcher,\n onChange: (isAuthenticated: boolean) => void,\n onRefreshChange?: (isRefreshing: boolean) => void,\n ) {\n this.resetAuthState();\n this._logVerbose(\"pausing WS for auth token fetch\");\n this.pauseSocket();\n const token = await this.fetchTokenAndGuardAgainstRace(fetchToken, {\n forceRefreshToken: false,\n });\n if (token.isFromOutdatedConfig) {\n return;\n }\n const config: AuthConfig = {\n fetchToken,\n onAuthChange: onChange,\n onRefreshChange,\n };\n if (token.value) {\n this.setAuthState({\n state: \"waitingForServerConfirmationOfCachedToken\",\n config,\n hasRetried: false,\n });\n this.authenticate(token.value);\n } else {\n this.setAuthState({\n state: \"initialRefetch\",\n config,\n });\n // Try again with `forceRefreshToken: true`\n await this.refetchToken();\n }\n this._logVerbose(\"resuming WS after auth token fetch\");\n this.resumeSocket();\n }\n\n onTransition(serverMessage: Transition) {\n if (\n !this.syncState.isCurrentOrNewerAuthVersion(\n serverMessage.endVersion.identity,\n )\n ) {\n // This is a stale transition - client has moved on to\n // a newer auth version.\n return;\n }\n if (\n serverMessage.endVersion.identity <= serverMessage.startVersion.identity\n ) {\n // This transition did not change auth - it is not a response to Authenticate.\n return;\n }\n\n this._logVerbose(\n `auth state is ${this.authState.state} when handling transition`,\n );\n\n // This transition advanced the auth version, which means the token used was valid\n // and the client and server auth states are in sync.\n this.syncState.markAuthCompletion();\n\n if (this.authState.state === \"waitingForServerConfirmationOfCachedToken\") {\n this._logVerbose(\"server confirmed auth token is valid\");\n void this.refetchToken();\n this.authState.config.onAuthChange(true);\n return;\n }\n if (this.authState.state === \"waitingForServerConfirmationOfFreshToken\") {\n this._logVerbose(\"server confirmed new auth token is valid\");\n this.notifyRefreshChange(false);\n this.scheduleTokenRefetch(this.authState.token);\n this.tokenConfirmationAttempts = 0;\n if (!this.authState.hadAuth) {\n this.authState.config.onAuthChange(true);\n }\n }\n }\n\n onAuthError(serverMessage: AuthError) {\n // If the AuthError is not due to updating the token, and we're currently\n // waiting on the result of a token update, ignore.\n if (\n serverMessage.authUpdateAttempted === false &&\n (this.authState.state === \"waitingForServerConfirmationOfFreshToken\" ||\n this.authState.state === \"waitingForServerConfirmationOfCachedToken\")\n ) {\n this._logVerbose(\"ignoring non-auth token expired error\");\n return;\n }\n const { baseVersion } = serverMessage;\n // Versioned AuthErrors are ignored if the client advanced to\n // a newer auth identity\n // Error are reporting the previous version, since the server\n // didn't advance, hence `+ 1`.\n if (!this.syncState.isCurrentOrNewerAuthVersion(baseVersion + 1)) {\n this._logVerbose(\"ignoring auth error for previous auth attempt\");\n return;\n }\n void this.tryToReauthenticate(serverMessage);\n return;\n }\n\n // This is similar to `refetchToken` defined below, in fact we\n // don't represent them as different states, but it is different\n // in that we pause the WebSocket so that mutations\n // don't retry with bad auth.\n private async tryToReauthenticate(serverMessage: AuthError) {\n this._logVerbose(`attempting to reauthenticate: ${serverMessage.error}`);\n if (\n // No way to fetch another token, kaboom\n this.authState.state === \"noAuth\" ||\n // We failed on a fresh token. After a small number of retries, we give up\n // and clear the auth state to avoid infinite retries.\n (this.authState.state === \"waitingForServerConfirmationOfFreshToken\" &&\n this.tokenConfirmationAttempts >= MAX_TOKEN_CONFIRMATION_ATTEMPTS)\n ) {\n this.logger.error(\n `Failed to authenticate: \"${serverMessage.error}\", check your server auth config`,\n );\n if (this.syncState.hasAuth()) {\n this.syncState.clearAuth();\n }\n if (this.authState.state !== \"noAuth\") {\n this.setAndReportAuthFailed(this.authState.config.onAuthChange);\n }\n return;\n }\n if (this.authState.state === \"waitingForServerConfirmationOfFreshToken\") {\n this.tokenConfirmationAttempts++;\n this._logVerbose(\n `retrying reauthentication, ${MAX_TOKEN_CONFIRMATION_ATTEMPTS - this.tokenConfirmationAttempts} attempts remaining`,\n );\n }\n\n this.notifyRefreshChange(true);\n await this.stopSocket();\n if ((this.authState.state as AuthState[\"state\"]) === \"noAuth\") {\n // setConfig() or stop() ran during the await.\n return;\n }\n const token = await this.fetchTokenAndGuardAgainstRace(\n this.authState.config.fetchToken,\n {\n forceRefreshToken: true,\n },\n );\n if (token.isFromOutdatedConfig) {\n return;\n }\n\n if (token.value && this.syncState.isNewAuth(token.value)) {\n this.authenticate(token.value);\n this.setAuthState({\n state: \"waitingForServerConfirmationOfFreshToken\",\n config: this.authState.config,\n token: token.value,\n hadAuth:\n this.authState.state === \"notRefetching\" ||\n this.authState.state === \"waitingForScheduledRefetch\",\n });\n } else {\n this._logVerbose(\"reauthentication failed, could not fetch a new token\");\n if (this.syncState.hasAuth()) {\n this.syncState.clearAuth();\n }\n this.setAndReportAuthFailed(this.authState.config.onAuthChange);\n }\n this.tryRestartSocket();\n }\n\n // Force refetch the token and schedule another refetch\n // before the token expires - an active client should never\n // need to reauthenticate.\n private async refetchToken() {\n if (this.authState.state === \"noAuth\") {\n return;\n }\n this._logVerbose(\"refetching auth token\");\n const token = await this.fetchTokenAndGuardAgainstRace(\n this.authState.config.fetchToken,\n {\n forceRefreshToken: true,\n },\n );\n if (token.isFromOutdatedConfig) {\n return;\n }\n\n if (token.value) {\n if (this.syncState.isNewAuth(token.value)) {\n this.setAuthState({\n state: \"waitingForServerConfirmationOfFreshToken\",\n hadAuth: this.syncState.hasAuth(),\n token: token.value,\n config: this.authState.config,\n });\n this.authenticate(token.value);\n } else {\n this.setAuthState({\n state: \"notRefetching\",\n config: this.authState.config,\n });\n }\n } else {\n this._logVerbose(\"refetching token failed\");\n if (this.syncState.hasAuth()) {\n this.clearAuth();\n }\n this.setAndReportAuthFailed(this.authState.config.onAuthChange);\n }\n // Restart in case this refetch was triggered via schedule during\n // a reauthentication attempt.\n this._logVerbose(\n \"restarting WS after auth token fetch (if currently stopped)\",\n );\n this.tryRestartSocket();\n }\n\n private scheduleTokenRefetch(token: string) {\n if (this.authState.state === \"noAuth\") {\n return;\n }\n const decodedToken = this.decodeToken(token);\n if (!decodedToken) {\n // This is no longer really possible, because\n // we wait on server response before scheduling token refetch,\n // and the server currently requires JWT tokens.\n this.logger.error(\n \"Auth token is not a valid JWT, cannot refetch the token\",\n );\n return;\n }\n // iat: issued at time, UTC seconds timestamp at which the JWT was issued\n // exp: expiration time, UTC seconds timestamp at which the JWT will expire\n const { iat, exp } = decodedToken as { iat?: number; exp?: number };\n if (!iat || !exp) {\n this.logger.error(\n \"Auth token does not have required fields, cannot refetch the token\",\n );\n return;\n }\n // Because the client and server clocks may be out of sync,\n // we only know that the token will expire after `exp - iat`,\n // and since we just fetched a fresh one we know when that\n // will happen.\n const tokenValiditySeconds = exp - iat;\n if (tokenValiditySeconds <= 2) {\n this.logger.error(\n \"Auth token does not live long enough, cannot refetch the token\",\n );\n return;\n }\n // Attempt to refresh the token `refreshTokenLeewaySeconds` before it expires,\n // or immediately if the token is already expiring soon.\n let delay = Math.min(\n MAXIMUM_REFRESH_DELAY,\n (tokenValiditySeconds - this.refreshTokenLeewaySeconds) * 1000,\n );\n if (delay <= 0) {\n // Refetch immediately, but this might be due to configuring a `refreshTokenLeewaySeconds`\n // that is too large compared to the token's actual lifetime.\n this.logger.warn(\n `Refetching auth token immediately, configured leeway ${this.refreshTokenLeewaySeconds}s is larger than the token's lifetime ${tokenValiditySeconds}s`,\n );\n delay = 0;\n }\n const refetchTokenTimeoutId = setTimeout(() => {\n this._logVerbose(\"running scheduled token refetch\");\n void this.refetchToken();\n }, delay);\n this.setAuthState({\n state: \"waitingForScheduledRefetch\",\n refetchTokenTimeoutId,\n config: this.authState.config,\n });\n this._logVerbose(\n `scheduled preemptive auth token refetching in ${delay}ms`,\n );\n }\n\n // Protects against simultaneous calls to `setConfig`\n // while we're fetching a token\n private async fetchTokenAndGuardAgainstRace(\n fetchToken: AuthTokenFetcher,\n fetchArgs: {\n forceRefreshToken: boolean;\n },\n ) {\n const originalConfigVersion = ++this.configVersion;\n this._logVerbose(\n `fetching token with config version ${originalConfigVersion}`,\n );\n const token = await fetchToken(fetchArgs);\n if (this.configVersion !== originalConfigVersion) {\n // This is a stale config\n this._logVerbose(\n `stale config version, expected ${originalConfigVersion}, got ${this.configVersion}`,\n );\n return { isFromOutdatedConfig: true };\n }\n return { isFromOutdatedConfig: false, value: token };\n }\n\n stop() {\n this.resetAuthState();\n // Bump this in case we are mid-token-fetch when we get stopped\n this.configVersion++;\n this._logVerbose(`config version bumped to ${this.configVersion}`);\n }\n\n private setAndReportAuthFailed(\n onAuthChange: (authenticated: boolean) => void,\n ) {\n onAuthChange(false);\n this.resetAuthState();\n }\n\n // The sole path to `state === \"noAuth\"`; consumers rely on this firing\n // `notifyRefreshChange(false)` to pair any in-flight `(true)`. May run\n // when refresh state is already false.\n private resetAuthState() {\n this.notifyRefreshChange(false);\n this.setAuthState({ state: \"noAuth\" });\n }\n\n private setAuthState(newAuth: AuthState) {\n const authStateForLog =\n newAuth.state === \"waitingForServerConfirmationOfFreshToken\"\n ? {\n hadAuth: newAuth.hadAuth,\n state: newAuth.state,\n token: `...${newAuth.token.slice(-7)}`,\n }\n : { state: newAuth.state };\n this._logVerbose(\n `setting auth state to ${JSON.stringify(authStateForLog)}`,\n );\n switch (newAuth.state) {\n case \"waitingForScheduledRefetch\":\n case \"notRefetching\":\n case \"noAuth\":\n this.tokenConfirmationAttempts = 0;\n break;\n case \"waitingForServerConfirmationOfFreshToken\":\n case \"waitingForServerConfirmationOfCachedToken\":\n case \"initialRefetch\":\n break;\n default: {\n newAuth satisfies never;\n }\n }\n if (this.authState.state === \"waitingForScheduledRefetch\") {\n // TODO: this side-effect would be better situated with scheduling refetch\n clearTimeout(this.authState.refetchTokenTimeoutId);\n }\n this.authState = newAuth;\n }\n\n private decodeToken(token: string) {\n try {\n return jwtDecode(token);\n } catch (e) {\n this._logVerbose(\n `Error decoding token: ${e instanceof Error ? e.message : \"Unknown error\"}`,\n );\n return null;\n }\n }\n\n private _logVerbose(message: string) {\n this.logger.logVerbose(`${message} [v${this.configVersion}]`);\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { Logger } from \"../logging.js\";\nimport { LocalSyncState } from \"./local_state.js\";\nimport { AuthError, IdentityVersion, Transition } from \"./protocol.js\";\nimport { jwtDecode } from \"../../vendor/jwt-decode/index.js\";\n\n// setTimout uses 32 bit integer, so it can only\n// schedule about 24 days in the future.\nconst MAXIMUM_REFRESH_DELAY = 20 * 24 * 60 * 60 * 1000; // 20 days\n\nconst MAX_TOKEN_CONFIRMATION_ATTEMPTS = 2;\n\n/**\n * An async function returning a JWT. Depending on the auth providers\n * configured in convex/auth.config.ts, this may be a JWT-encoded OpenID\n * Connect Identity Token or a traditional JWT.\n *\n * `forceRefreshToken` is `true` if the server rejected a previously\n * returned token or the token is anticipated to expiring soon\n * based on its `exp` time.\n *\n * See {@link ConvexReactClient.setAuth}.\n *\n * @public\n */\nexport type AuthTokenFetcher = (args: {\n forceRefreshToken: boolean;\n}) => Promise<string | null | undefined>;\n\n/**\n * What is provided to the client.\n */\ntype AuthConfig = {\n fetchToken: AuthTokenFetcher;\n onAuthChange: (isAuthenticated: boolean) => void;\n onRefreshChange?: ((isRefreshing: boolean) => void) | undefined;\n};\n\n/**\n * By default we take 3 steps:\n * 1. Fetch a possibly cached token and send it to the server\n * 2. Immediately fetch a fresh token without using a cache\n * 3. Repeat step 2 before the end of the fresh token's lifetime\n *\n * When `initialAuthTokenReuse` is enabled, step 2 is skipped: the\n * cached token is reused and a refetch is scheduled based on its\n * remaining lifetime (estimated via the server's clock skew). This\n * avoids a second Authenticate message that would cause the server\n * to re-execute all authenticated queries.\n *\n * If the server rejects a token before a scheduled refetch, we\n * immediately fetch a fresh token and retry.\n */\ntype AuthState =\n | { state: \"noAuth\" }\n | {\n state: \"waitingForServerConfirmationOfCachedToken\";\n config: AuthConfig;\n hasRetried: boolean;\n }\n | {\n state: \"initialRefetch\";\n config: AuthConfig;\n }\n | {\n state: \"waitingForServerConfirmationOfFreshToken\";\n config: AuthConfig;\n hadAuth: boolean;\n token: string;\n }\n | {\n state: \"waitingForScheduledRefetch\";\n config: AuthConfig;\n refetchTokenTimeoutId: ReturnType<typeof setTimeout>;\n }\n // Special/weird state when we got a valid token\n // but could not fetch a new one.\n | {\n state: \"notRefetching\";\n config: AuthConfig;\n };\n\n/**\n * Handles the state transitions for auth. The server is the source\n * of truth.\n */\nexport class AuthenticationManager {\n private authState: AuthState = { state: \"noAuth\" };\n // Used to detect races involving `setConfig` calls\n // while a token is being fetched.\n private configVersion = 0;\n // Shared by the BaseClient so that the auth manager can easily inspect it\n private readonly syncState: LocalSyncState;\n // Passed down by BaseClient, sends a message to the server\n private readonly authenticate: (token: string) => IdentityVersion;\n private readonly stopSocket: () => Promise<void>;\n private readonly tryRestartSocket: () => void;\n private readonly pauseSocket: () => void;\n private readonly resumeSocket: () => void;\n // Passed down by BaseClient, sends a message to the server\n private readonly clearAuth: () => void;\n private readonly logger: Logger;\n private readonly refreshTokenLeewaySeconds: number;\n private readonly initialAuthTokenReuse: boolean;\n // Track last value to avoid redundant calls\n private lastRefreshChange: boolean;\n // Number of times we have attempted to confirm the latest token. We retry up\n // to `MAX_TOKEN_CONFIRMATION_ATTEMPTS` times.\n private tokenConfirmationAttempts = 0;\n constructor(\n syncState: LocalSyncState,\n callbacks: {\n authenticate: (token: string) => IdentityVersion;\n stopSocket: () => Promise<void>;\n tryRestartSocket: () => void;\n pauseSocket: () => void;\n resumeSocket: () => void;\n clearAuth: () => void;\n },\n config: {\n refreshTokenLeewaySeconds: number;\n logger: Logger;\n initialAuthTokenReuse: boolean;\n },\n ) {\n this.syncState = syncState;\n this.authenticate = callbacks.authenticate;\n this.stopSocket = callbacks.stopSocket;\n this.tryRestartSocket = callbacks.tryRestartSocket;\n this.pauseSocket = callbacks.pauseSocket;\n this.resumeSocket = callbacks.resumeSocket;\n this.clearAuth = callbacks.clearAuth;\n this.logger = config.logger;\n this.refreshTokenLeewaySeconds = config.refreshTokenLeewaySeconds;\n this.initialAuthTokenReuse = config.initialAuthTokenReuse;\n this.lastRefreshChange = false;\n }\n\n private notifyRefreshChange(isRefreshing: boolean) {\n if (\n this.authState.state !== \"noAuth\" &&\n this.authState.state !== \"initialRefetch\" &&\n this.authState.config.onRefreshChange &&\n this.lastRefreshChange !== isRefreshing\n ) {\n this.lastRefreshChange = isRefreshing;\n this.authState.config.onRefreshChange(isRefreshing);\n }\n }\n\n async setConfig(\n fetchToken: AuthTokenFetcher,\n onChange: (isAuthenticated: boolean) => void,\n onRefreshChange?: (isRefreshing: boolean) => void,\n ) {\n this.resetAuthState();\n this._logVerbose(\"pausing WS for auth token fetch\");\n this.pauseSocket();\n const token = await this.fetchTokenAndGuardAgainstRace(fetchToken, {\n forceRefreshToken: false,\n });\n if (token.isFromOutdatedConfig) {\n return;\n }\n const config: AuthConfig = {\n fetchToken,\n onAuthChange: onChange,\n onRefreshChange,\n };\n if (token.value) {\n this.setAuthState({\n state: \"waitingForServerConfirmationOfCachedToken\",\n config,\n hasRetried: false,\n });\n this.authenticate(token.value);\n } else {\n this.setAuthState({\n state: \"initialRefetch\",\n config,\n });\n // Try again with `forceRefreshToken: true`\n await this.refetchToken();\n }\n this._logVerbose(\"resuming WS after auth token fetch\");\n this.resumeSocket();\n }\n\n onTransition(serverMessage: Transition) {\n if (\n !this.syncState.isCurrentOrNewerAuthVersion(\n serverMessage.endVersion.identity,\n )\n ) {\n // This is a stale transition - client has moved on to\n // a newer auth version.\n return;\n }\n if (\n serverMessage.endVersion.identity <= serverMessage.startVersion.identity\n ) {\n // This transition did not change auth - it is not a response to Authenticate.\n return;\n }\n\n this._logVerbose(\n `auth state is ${this.authState.state} when handling transition`,\n );\n\n // This transition advanced the auth version, which means the token used was valid\n // and the client and server auth states are in sync.\n this.syncState.markAuthCompletion();\n\n if (this.authState.state === \"waitingForServerConfirmationOfCachedToken\") {\n this._logVerbose(\"server confirmed auth token is valid\");\n const cachedToken = this.syncState.getAuth()?.value;\n if (this.initialAuthTokenReuse && cachedToken) {\n this.scheduleTokenRefetch(cachedToken, serverMessage.clientClockSkew);\n } else {\n void this.refetchToken();\n }\n this.authState.config.onAuthChange(true);\n return;\n }\n if (this.authState.state === \"waitingForServerConfirmationOfFreshToken\") {\n this._logVerbose(\"server confirmed new auth token is valid\");\n this.notifyRefreshChange(false);\n this.scheduleTokenRefetch(this.authState.token);\n this.tokenConfirmationAttempts = 0;\n if (!this.authState.hadAuth) {\n this.authState.config.onAuthChange(true);\n }\n }\n }\n\n onAuthError(serverMessage: AuthError) {\n // If the AuthError is not due to updating the token, and we're currently\n // waiting on the result of a token update, ignore.\n if (\n serverMessage.authUpdateAttempted === false &&\n (this.authState.state === \"waitingForServerConfirmationOfFreshToken\" ||\n this.authState.state === \"waitingForServerConfirmationOfCachedToken\")\n ) {\n this._logVerbose(\"ignoring non-auth token expired error\");\n return;\n }\n const { baseVersion } = serverMessage;\n // Versioned AuthErrors are ignored if the client advanced to\n // a newer auth identity\n // Error are reporting the previous version, since the server\n // didn't advance, hence `+ 1`.\n if (!this.syncState.isCurrentOrNewerAuthVersion(baseVersion + 1)) {\n this._logVerbose(\"ignoring auth error for previous auth attempt\");\n return;\n }\n void this.tryToReauthenticate(serverMessage);\n return;\n }\n\n // This is similar to `refetchToken` defined below, in fact we\n // don't represent them as different states, but it is different\n // in that we pause the WebSocket so that mutations\n // don't retry with bad auth.\n private async tryToReauthenticate(serverMessage: AuthError) {\n this._logVerbose(`attempting to reauthenticate: ${serverMessage.error}`);\n if (\n // No way to fetch another token, kaboom\n this.authState.state === \"noAuth\" ||\n // We failed on a fresh token. After a small number of retries, we give up\n // and clear the auth state to avoid infinite retries.\n (this.authState.state === \"waitingForServerConfirmationOfFreshToken\" &&\n this.tokenConfirmationAttempts >= MAX_TOKEN_CONFIRMATION_ATTEMPTS)\n ) {\n this.logger.error(\n `Failed to authenticate: \"${serverMessage.error}\", check your server auth config`,\n );\n if (this.syncState.hasAuth()) {\n this.syncState.clearAuth();\n }\n if (this.authState.state !== \"noAuth\") {\n this.setAndReportAuthFailed(this.authState.config.onAuthChange);\n }\n return;\n }\n if (this.authState.state === \"waitingForServerConfirmationOfFreshToken\") {\n this.tokenConfirmationAttempts++;\n this._logVerbose(\n `retrying reauthentication, ${MAX_TOKEN_CONFIRMATION_ATTEMPTS - this.tokenConfirmationAttempts} attempts remaining`,\n );\n }\n\n this.notifyRefreshChange(true);\n await this.stopSocket();\n if ((this.authState.state as AuthState[\"state\"]) === \"noAuth\") {\n // setConfig() or stop() ran during the await.\n return;\n }\n const token = await this.fetchTokenAndGuardAgainstRace(\n this.authState.config.fetchToken,\n {\n forceRefreshToken: true,\n },\n );\n if (token.isFromOutdatedConfig) {\n return;\n }\n\n if (token.value && this.syncState.isNewAuth(token.value)) {\n this.authenticate(token.value);\n this.setAuthState({\n state: \"waitingForServerConfirmationOfFreshToken\",\n config: this.authState.config,\n token: token.value,\n hadAuth:\n this.authState.state === \"notRefetching\" ||\n this.authState.state === \"waitingForScheduledRefetch\",\n });\n } else {\n this._logVerbose(\"reauthentication failed, could not fetch a new token\");\n if (this.syncState.hasAuth()) {\n this.syncState.clearAuth();\n }\n this.setAndReportAuthFailed(this.authState.config.onAuthChange);\n }\n this.tryRestartSocket();\n }\n\n // Force refetch the token and schedule another refetch\n // before the token expires - an active client should never\n // need to reauthenticate.\n private async refetchToken() {\n if (this.authState.state === \"noAuth\") {\n return;\n }\n this._logVerbose(\"refetching auth token\");\n const token = await this.fetchTokenAndGuardAgainstRace(\n this.authState.config.fetchToken,\n {\n forceRefreshToken: true,\n },\n );\n if (token.isFromOutdatedConfig) {\n return;\n }\n\n if (token.value) {\n if (this.syncState.isNewAuth(token.value)) {\n this.setAuthState({\n state: \"waitingForServerConfirmationOfFreshToken\",\n hadAuth: this.syncState.hasAuth(),\n token: token.value,\n config: this.authState.config,\n });\n this.authenticate(token.value);\n } else {\n this.setAuthState({\n state: \"notRefetching\",\n config: this.authState.config,\n });\n }\n } else {\n this._logVerbose(\"refetching token failed\");\n if (this.syncState.hasAuth()) {\n this.clearAuth();\n }\n this.setAndReportAuthFailed(this.authState.config.onAuthChange);\n }\n // Restart in case this refetch was triggered via schedule during\n // a reauthentication attempt.\n this._logVerbose(\n \"restarting WS after auth token fetch (if currently stopped)\",\n );\n this.tryRestartSocket();\n }\n\n private scheduleTokenRefetch(token: string, clientClockSkewMs?: number) {\n if (this.authState.state === \"noAuth\") {\n return;\n }\n const decodedToken = this.decodeToken(token);\n if (!decodedToken) {\n // This is no longer really possible, because\n // we wait on server response before scheduling token refetch,\n // and the server currently requires JWT tokens.\n this.logger.error(\n \"Auth token is not a valid JWT, cannot refetch the token\",\n );\n return;\n }\n // iat: issued at time, UTC seconds timestamp at which the JWT was issued\n // exp: expiration time, UTC seconds timestamp at which the JWT will expire\n const { iat, exp } = decodedToken as { iat?: number; exp?: number };\n if (!iat || !exp) {\n this.logger.error(\n \"Auth token does not have required fields, cannot refetch the token\",\n );\n return;\n }\n const fullLifetimeSeconds = exp - iat;\n if (fullLifetimeSeconds <= 2) {\n this.logger.error(\n \"Auth token does not live long enough, cannot refetch the token\",\n );\n return;\n }\n let tokenValiditySeconds: number;\n if (clientClockSkewMs !== undefined) {\n // For cached tokens we use the server's clock skew estimate to\n // compute remaining lifetime. The server computes\n // clientClockSkew = clientTs - serverReceiveTs, so it's positive\n // when the client is ahead and negative when behind. To recover\n // server time: serverNow = Date.now() - clientClockSkew.\n // Both Date.now() and setTimeout use the same client clock, so\n // the scheduled delay is accurate regardless of absolute offset.\n // Connect latency inflates the skew toward \"client behind,\" which\n // is conservative \u2014 we schedule the refetch slightly early.\n const estimatedServerNowSeconds = (Date.now() - clientClockSkewMs) / 1000;\n tokenValiditySeconds = exp - estimatedServerNowSeconds;\n if (tokenValiditySeconds <= 0) {\n tokenValiditySeconds = 0;\n }\n } else {\n // For freshly-fetched tokens, `exp - iat` is the full lifetime\n // starting from now.\n tokenValiditySeconds = fullLifetimeSeconds;\n }\n // Attempt to refresh the token `refreshTokenLeewaySeconds` before it expires,\n // or immediately if the token is already expiring soon.\n let delay = Math.min(\n MAXIMUM_REFRESH_DELAY,\n (tokenValiditySeconds - this.refreshTokenLeewaySeconds) * 1000,\n );\n if (delay <= 0) {\n // Refetch immediately, but this might be due to configuring a `refreshTokenLeewaySeconds`\n // that is too large compared to the token's actual lifetime.\n this.logger.warn(\n `Refetching auth token immediately, configured leeway ${this.refreshTokenLeewaySeconds}s is larger than the token's lifetime ${tokenValiditySeconds}s`,\n );\n delay = 0;\n }\n const refetchTokenTimeoutId = setTimeout(() => {\n this._logVerbose(\"running scheduled token refetch\");\n void this.refetchToken();\n }, delay);\n this.setAuthState({\n state: \"waitingForScheduledRefetch\",\n refetchTokenTimeoutId,\n config: this.authState.config,\n });\n this._logVerbose(\n `scheduled preemptive auth token refetching in ${delay}ms`,\n );\n }\n\n // Protects against simultaneous calls to `setConfig`\n // while we're fetching a token\n private async fetchTokenAndGuardAgainstRace(\n fetchToken: AuthTokenFetcher,\n fetchArgs: {\n forceRefreshToken: boolean;\n },\n ) {\n const originalConfigVersion = ++this.configVersion;\n this._logVerbose(\n `fetching token with config version ${originalConfigVersion}`,\n );\n const token = await fetchToken(fetchArgs);\n if (this.configVersion !== originalConfigVersion) {\n // This is a stale config\n this._logVerbose(\n `stale config version, expected ${originalConfigVersion}, got ${this.configVersion}`,\n );\n return { isFromOutdatedConfig: true };\n }\n return { isFromOutdatedConfig: false, value: token };\n }\n\n stop() {\n this.resetAuthState();\n // Bump this in case we are mid-token-fetch when we get stopped\n this.configVersion++;\n this._logVerbose(`config version bumped to ${this.configVersion}`);\n }\n\n private setAndReportAuthFailed(\n onAuthChange: (authenticated: boolean) => void,\n ) {\n onAuthChange(false);\n this.resetAuthState();\n }\n\n // The sole path to `state === \"noAuth\"`; consumers rely on this firing\n // `notifyRefreshChange(false)` to pair any in-flight `(true)`. May run\n // when refresh state is already false.\n private resetAuthState() {\n this.notifyRefreshChange(false);\n this.setAuthState({ state: \"noAuth\" });\n }\n\n private setAuthState(newAuth: AuthState) {\n const authStateForLog =\n newAuth.state === \"waitingForServerConfirmationOfFreshToken\"\n ? {\n hadAuth: newAuth.hadAuth,\n state: newAuth.state,\n token: `...${newAuth.token.slice(-7)}`,\n }\n : { state: newAuth.state };\n this._logVerbose(\n `setting auth state to ${JSON.stringify(authStateForLog)}`,\n );\n switch (newAuth.state) {\n case \"waitingForScheduledRefetch\":\n case \"notRefetching\":\n case \"noAuth\":\n this.tokenConfirmationAttempts = 0;\n break;\n case \"waitingForServerConfirmationOfFreshToken\":\n case \"waitingForServerConfirmationOfCachedToken\":\n case \"initialRefetch\":\n break;\n default: {\n newAuth satisfies never;\n }\n }\n if (this.authState.state === \"waitingForScheduledRefetch\") {\n // TODO: this side-effect would be better situated with scheduling refetch\n clearTimeout(this.authState.refetchTokenTimeoutId);\n }\n this.authState = newAuth;\n }\n\n private decodeToken(token: string) {\n try {\n return jwtDecode(token);\n } catch (e) {\n this._logVerbose(\n `Error decoding token: ${e instanceof Error ? e.message : \"Unknown error\"}`,\n );\n return null;\n }\n }\n\n private _logVerbose(message: string) {\n this.logger.logVerbose(`${message} [v${this.configVersion}]`);\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -153,7 +153,8 @@ class BaseConvexClient {
153
153
  },
154
154
  {
155
155
  logger: this.logger,
156
- refreshTokenLeewaySeconds: authRefreshTokenLeewaySeconds
156
+ refreshTokenLeewaySeconds: authRefreshTokenLeewaySeconds,
157
+ initialAuthTokenReuse: options.initialAuthTokenReuse ?? false
157
158
  }
158
159
  );
159
160
  this.optimisticQueryResults = new import_optimistic_updates_impl.OptimisticQueryResults();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/browser/sync/client.ts"],
4
- "sourcesContent": ["/**\n * BaseConvexClient should not be used directly and does not provide a stable\n * interface. It is a \"Base\" client not because it expects to be inherited from\n * but because other clients are built around it.\n *\n * BaseConvexClient is not Convex Function type-aware: it deals\n * with queries as functions that return Value, not the specific value.\n * Use a higher-level library to get types.\n */\nimport { version } from \"../../index.js\";\nimport { convexToJson, Value } from \"../../values/index.js\";\nimport {\n createHybridErrorStacktrace,\n forwardData,\n instantiateDefaultLogger,\n instantiateNoopLogger,\n logFatalError,\n Logger,\n} from \"../logging.js\";\nimport { LocalSyncState } from \"./local_state.js\";\nimport { RequestManager } from \"./request_manager.js\";\nimport {\n OptimisticLocalStore,\n OptimisticUpdate,\n} from \"./optimistic_updates.js\";\nimport {\n OptimisticQueryResults,\n QueryResultsMap,\n} from \"./optimistic_updates_impl.js\";\nimport {\n ActionRequest,\n MutationRequest,\n QueryId,\n QueryJournal,\n ServerMessage,\n RequestId,\n TS,\n UserIdentityAttributes,\n} from \"./protocol.js\";\nimport { RemoteQuerySet } from \"./remote_query_set.js\";\nimport { QueryToken, serializePathAndArgs } from \"./udf_path_utils.js\";\nimport { ReconnectMetadata, WebSocketManager } from \"./web_socket_manager.js\";\nimport { newSessionId } from \"./session.js\";\nimport { FunctionResult } from \"./function_result.js\";\nimport {\n AuthenticationManager,\n AuthTokenFetcher,\n} from \"./authentication_manager.js\";\nexport { type AuthTokenFetcher } from \"./authentication_manager.js\";\nimport { getMarksReport, mark, MarkName } from \"./metrics.js\";\nimport { parseArgs, validateDeploymentUrl } from \"../../common/index.js\";\nimport { ConvexError } from \"../../values/errors.js\";\nimport { jwtDecode } from \"../../vendor/jwt-decode/index.js\";\n\n/**\n * Options for {@link BaseConvexClient}.\n *\n * @public\n */\nexport interface BaseConvexClientOptions {\n /**\n * Whether to prompt the user if they have unsaved changes pending\n * when navigating away or closing a web page.\n *\n * This is only possible when the `window` object exists, i.e. in a browser.\n *\n * The default value is `true` in browsers.\n */\n unsavedChangesWarning?: boolean;\n /**\n * Specifies an alternate\n * [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)\n * constructor to use for client communication with the Convex cloud.\n * The default behavior is to use `WebSocket` from the global environment.\n */\n webSocketConstructor?: typeof WebSocket;\n /**\n * Adds additional logging for debugging purposes.\n *\n * The default value is `false`.\n */\n verbose?: boolean;\n /**\n * A logger, `true`, or `false`. If not provided or `true`, logs to the console.\n * If `false`, logs are not printed anywhere.\n *\n * You can construct your own logger to customize logging to log elsewhere.\n * A logger is an object with 4 methods: log(), warn(), error(), and logVerbose().\n * These methods can receive multiple arguments of any types, like console.log().\n */\n logger?: Logger | boolean;\n /**\n * Sends additional metrics to Convex for debugging purposes.\n *\n * The default value is `false`.\n */\n reportDebugInfoToConvex?: boolean;\n /**\n * This API is experimental: it may change or disappear.\n *\n * A function to call on receiving abnormal WebSocket close messages from the\n * connected Convex deployment. The content of these messages is not stable,\n * it is an implementation detail that may change.\n *\n * Consider this API an observability stopgap until higher level codes with\n * recommendations on what to do are available, which could be a more stable\n * interface instead of `string`.\n *\n * Check `connectionState` for more quantitative metrics about connection status.\n */\n onServerDisconnectError?: (message: string) => void;\n /**\n * Skip validating that the Convex deployment URL looks like\n * `https://happy-animal-123.convex.cloud` or localhost.\n *\n * This can be useful if running a self-hosted Convex backend that uses a different\n * URL.\n *\n * The default value is `false`\n */\n skipConvexDeploymentUrlCheck?: boolean;\n /**\n * If using auth, the number of seconds before a token expires that we should refresh it.\n *\n * The default value is `10`.\n */\n authRefreshTokenLeewaySeconds?: number;\n /**\n * This API is experimental: it may change or disappear.\n *\n * Whether query, mutation, and action requests should be held back\n * until the first auth token can be sent.\n *\n * Opting into this behavior works well for pages that should\n * only be viewed by authenticated clients.\n *\n * Defaults to false, not waiting for an auth token.\n */\n expectAuth?: boolean;\n}\n\n/**\n * State describing the client's connection with the Convex backend.\n *\n * @public\n */\nexport type ConnectionState = {\n hasInflightRequests: boolean;\n isWebSocketConnected: boolean;\n timeOfOldestInflightRequest: Date | null;\n /**\n * True if the client has ever opened a WebSocket to the \"ready\" state.\n */\n hasEverConnected: boolean;\n /**\n * The number of times this client has connected to the Convex backend.\n *\n * A number of things can cause the client to reconnect -- server errors,\n * bad internet, auth expiring. But this number being high is an indication\n * that the client is having trouble keeping a stable connection.\n */\n connectionCount: number;\n /**\n * The number of times this client has tried (and failed) to connect to the Convex backend.\n */\n connectionRetries: number;\n /**\n * The number of mutations currently in flight.\n */\n inflightMutations: number;\n /**\n * The number of actions currently in flight.\n */\n inflightActions: number;\n};\n\n/**\n * Options for {@link BaseConvexClient.subscribe}.\n *\n * @public\n */\nexport interface SubscribeOptions {\n /**\n * An (optional) journal produced from a previous execution of this query\n * function.\n *\n * If there is an existing subscription to a query function with the same\n * name and arguments, this journal will have no effect.\n */\n journal?: QueryJournal;\n\n /**\n * @internal\n */\n componentPath?: string | undefined;\n}\n\n/**\n * Options for {@link BaseConvexClient.mutation}.\n *\n * @public\n */\nexport interface MutationOptions {\n /**\n * An optimistic update to apply along with this mutation.\n *\n * An optimistic update locally updates queries while a mutation is pending.\n * Once the mutation completes, the update will be rolled back.\n */\n optimisticUpdate?: OptimisticUpdate<any> | undefined;\n}\n\n/**\n * Type describing updates to a query within a `Transition`.\n *\n * @public\n */\nexport type QueryModification =\n // `undefined` generally comes from an optimistic update setting the query to be loading\n { kind: \"Updated\"; result: FunctionResult | undefined } | { kind: \"Removed\" };\n\n/**\n * Object describing a transition passed into the `onTransition` handler.\n *\n * These can be from receiving a transition from the server, or from applying an\n * optimistic update locally.\n *\n * @public\n */\nexport type Transition = {\n queries: Array<{ token: QueryToken; modification: QueryModification }>;\n reflectedMutations: Array<{ requestId: RequestId; result: FunctionResult }>;\n timestamp: TS;\n};\n\n/**\n * Low-level client for directly integrating state management libraries\n * with Convex.\n *\n * Most developers should use higher level clients, like\n * the {@link ConvexHttpClient} or the React hook based {@link react.ConvexReactClient}.\n *\n * @public\n */\nexport class BaseConvexClient {\n private readonly address: string;\n private readonly state: LocalSyncState;\n private readonly requestManager: RequestManager;\n private readonly webSocketManager: WebSocketManager;\n private readonly authenticationManager: AuthenticationManager;\n private remoteQuerySet: RemoteQuerySet;\n private readonly optimisticQueryResults: OptimisticQueryResults;\n private _transitionHandlerCounter = 0;\n private _nextRequestId: RequestId;\n private _onTransitionFns: Map<number, (transition: Transition) => void> =\n new Map();\n private readonly _sessionId: string;\n private firstMessageReceived = false;\n private readonly debug: boolean;\n private readonly logger: Logger;\n private maxObservedTimestamp: TS | undefined;\n private connectionStateSubscribers = new Map<\n number,\n (connectionState: ConnectionState) => void\n >();\n private nextConnectionStateSubscriberId: number = 0;\n private _lastPublishedConnectionState: ConnectionState | undefined;\n\n /**\n * @param address - The url of your Convex deployment, often provided\n * by an environment variable. E.g. `https://small-mouse-123.convex.cloud`.\n * @param onTransition - A callback receiving an array of query tokens\n * corresponding to query results that have changed -- additional handlers\n * can be added via `addOnTransitionHandler`.\n * @param options - See {@link BaseConvexClientOptions} for a full description.\n */\n constructor(\n address: string,\n onTransition: (updatedQueries: QueryToken[]) => void,\n options?: BaseConvexClientOptions,\n ) {\n if (typeof address === \"object\") {\n throw new Error(\n \"Passing a ClientConfig object is no longer supported. Pass the URL of the Convex deployment as a string directly.\",\n );\n }\n if (options?.skipConvexDeploymentUrlCheck !== true) {\n validateDeploymentUrl(address);\n }\n options = { ...options };\n const authRefreshTokenLeewaySeconds =\n options.authRefreshTokenLeewaySeconds ?? 10;\n let webSocketConstructor = options.webSocketConstructor;\n if (!webSocketConstructor && typeof WebSocket === \"undefined\") {\n throw new Error(\n \"No WebSocket global variable defined! To use Convex in an environment without WebSocket try the HTTP client: https://docs.convex.dev/api/classes/browser.ConvexHttpClient\",\n );\n }\n webSocketConstructor = webSocketConstructor || WebSocket;\n this.debug = options.reportDebugInfoToConvex ?? false;\n this.address = address;\n this.logger =\n options.logger === false\n ? instantiateNoopLogger({ verbose: options.verbose ?? false })\n : options.logger !== true && options.logger\n ? options.logger\n : instantiateDefaultLogger({ verbose: options.verbose ?? false });\n // Substitute http(s) with ws(s)\n const i = address.search(\"://\");\n if (i === -1) {\n throw new Error(\"Provided address was not an absolute URL.\");\n }\n const origin = address.substring(i + 3); // move past the double slash\n const protocol = address.substring(0, i);\n let wsProtocol;\n if (protocol === \"http\") {\n wsProtocol = \"ws\";\n } else if (protocol === \"https\") {\n wsProtocol = \"wss\";\n } else {\n throw new Error(`Unknown parent protocol ${protocol}`);\n }\n const wsUri = `${wsProtocol}://${origin}/api/${version}/sync`;\n\n this.state = new LocalSyncState();\n this.remoteQuerySet = new RemoteQuerySet(\n (queryId) => this.state.queryPath(queryId),\n this.logger,\n );\n this.requestManager = new RequestManager(\n this.logger,\n this.markConnectionStateDirty,\n );\n\n // This is a callback for AuthenticationManager (which can't call\n // this synchronously, the callback wouldn't work) so the initial\n // pause for expectAuth we call it at the end of this constructor.\n const pauseSocket = () => {\n this.webSocketManager.pause();\n this.state.pause();\n };\n this.authenticationManager = new AuthenticationManager(\n this.state,\n {\n authenticate: (token) => {\n const message = this.state.setAuth(token);\n this.webSocketManager.sendMessage(message);\n return message.baseVersion;\n },\n stopSocket: () => this.webSocketManager.stop(),\n tryRestartSocket: () => this.webSocketManager.tryRestart(),\n pauseSocket,\n resumeSocket: () => this.webSocketManager.resume(),\n clearAuth: () => {\n this.clearAuth();\n },\n },\n {\n logger: this.logger,\n refreshTokenLeewaySeconds: authRefreshTokenLeewaySeconds,\n },\n );\n this.optimisticQueryResults = new OptimisticQueryResults();\n this.addOnTransitionHandler((transition) => {\n onTransition(transition.queries.map((q) => q.token));\n });\n this._nextRequestId = 0;\n this._sessionId = newSessionId();\n\n const { unsavedChangesWarning } = options;\n if (\n typeof window === \"undefined\" ||\n typeof window.addEventListener === \"undefined\"\n ) {\n if (unsavedChangesWarning === true) {\n throw new Error(\n \"unsavedChangesWarning requested, but window.addEventListener not found! Remove {unsavedChangesWarning: true} from Convex client options.\",\n );\n }\n } else if (unsavedChangesWarning !== false) {\n // Listen for tab close events and notify the user on unsaved changes.\n window.addEventListener(\"beforeunload\", (e) => {\n if (this.requestManager.hasIncompleteRequests()) {\n // There are 3 different ways to trigger this pop up so just try all of\n // them.\n\n e.preventDefault();\n // This confirmation message doesn't actually appear in most modern\n // browsers but we tried.\n const confirmationMessage =\n \"Are you sure you want to leave? Your changes may not be saved.\";\n // Recommended method for legacy (IE) browsers.\n // casts to avoid deprecation notices\n ((e || (window as any).event) as any).returnValue =\n confirmationMessage;\n return confirmationMessage;\n }\n });\n }\n\n this.webSocketManager = new WebSocketManager(\n wsUri,\n {\n onOpen: (reconnectMetadata: ReconnectMetadata) => {\n // We have a new WebSocket!\n this.mark(\"convexWebSocketOpen\");\n this.webSocketManager.sendMessage({\n ...reconnectMetadata,\n type: \"Connect\",\n sessionId: this._sessionId,\n maxObservedTimestamp: this.maxObservedTimestamp,\n });\n\n // Throw out our remote query, reissue queries\n // and outstanding mutations, and reauthenticate.\n this.remoteQuerySet = new RemoteQuerySet(\n (queryId) => this.state.queryPath(queryId),\n this.logger,\n );\n const [querySetModification, authModification] = this.state.restart();\n if (authModification) {\n this.webSocketManager.sendMessage(authModification);\n }\n this.webSocketManager.sendMessage(querySetModification);\n for (const message of this.requestManager.restart()) {\n this.webSocketManager.sendMessage(message);\n }\n },\n onResume: () => {\n const [querySetModification, authModification] = this.state.resume();\n if (authModification) {\n this.webSocketManager.sendMessage(authModification);\n }\n if (querySetModification) {\n this.webSocketManager.sendMessage(querySetModification);\n }\n for (const message of this.requestManager.resume()) {\n this.webSocketManager.sendMessage(message);\n }\n },\n onMessage: (serverMessage: ServerMessage) => {\n // Metrics events grow linearly with reconnection attempts so this\n // conditional prevents n^2 metrics reporting.\n if (!this.firstMessageReceived) {\n this.firstMessageReceived = true;\n this.mark(\"convexFirstMessageReceived\");\n this.reportMarks();\n }\n switch (serverMessage.type) {\n case \"Transition\": {\n this.observedTimestamp(serverMessage.endVersion.ts);\n this.authenticationManager.onTransition(serverMessage);\n this.remoteQuerySet.transition(serverMessage);\n this.state.transition(serverMessage);\n const completedRequests = this.requestManager.removeCompleted(\n this.remoteQuerySet.timestamp(),\n );\n this.notifyOnQueryResultChanges(completedRequests);\n break;\n }\n case \"MutationResponse\": {\n if (serverMessage.success) {\n this.observedTimestamp(serverMessage.ts);\n }\n const completedMutationInfo =\n this.requestManager.onResponse(serverMessage);\n if (completedMutationInfo !== null) {\n this.notifyOnQueryResultChanges(\n new Map([\n [\n completedMutationInfo.requestId,\n completedMutationInfo.result,\n ],\n ]),\n );\n }\n break;\n }\n case \"ActionResponse\": {\n this.requestManager.onResponse(serverMessage);\n break;\n }\n case \"AuthError\": {\n this.authenticationManager.onAuthError(serverMessage);\n break;\n }\n case \"FatalError\": {\n const error = logFatalError(this.logger, serverMessage.error);\n void this.webSocketManager.terminate();\n throw error;\n }\n default: {\n serverMessage satisfies never;\n }\n }\n\n return {\n hasSyncedPastLastReconnect: this.hasSyncedPastLastReconnect(),\n };\n },\n onServerDisconnectError: options.onServerDisconnectError,\n },\n webSocketConstructor,\n this.logger,\n this.markConnectionStateDirty,\n this.debug,\n );\n this.mark(\"convexClientConstructed\");\n\n // Begin client in a paused state waiting for an auth token.\n if (options.expectAuth) {\n pauseSocket();\n }\n }\n\n /**\n * Return true if there is outstanding work from prior to the time of the most recent restart.\n * This indicates that the client has not proven itself to have gotten past the issue that\n * potentially led to the restart. Use this to influence when to reset backoff after a failure.\n */\n private hasSyncedPastLastReconnect() {\n const hasSyncedPastLastReconnect =\n this.requestManager.hasSyncedPastLastReconnect() &&\n this.state.hasSyncedPastLastReconnect();\n return hasSyncedPastLastReconnect;\n }\n\n private observedTimestamp(observedTs: TS) {\n if (\n this.maxObservedTimestamp === undefined ||\n this.maxObservedTimestamp.lessThanOrEqual(observedTs)\n ) {\n this.maxObservedTimestamp = observedTs;\n }\n }\n\n getMaxObservedTimestamp() {\n return this.maxObservedTimestamp;\n }\n\n /**\n * Compute the current query results based on the remoteQuerySet and the\n * current optimistic updates and call `onTransition` for all the changed\n * queries.\n *\n * @param completedMutations - A set of mutation IDs whose optimistic updates\n * are no longer needed.\n */\n private notifyOnQueryResultChanges(\n completedRequests: Map<RequestId, FunctionResult>,\n ) {\n const remoteQueryResults: Map<QueryId, FunctionResult> =\n this.remoteQuerySet.remoteQueryResults();\n const queryTokenToValue: QueryResultsMap = new Map();\n for (const [queryId, result] of remoteQueryResults) {\n const queryToken = this.state.queryToken(queryId);\n // It's possible that we've already unsubscribed to this query but\n // the server hasn't learned about that yet. If so, ignore this one.\n\n if (queryToken !== null) {\n const query = {\n result,\n udfPath: this.state.queryPath(queryId)!,\n args: this.state.queryArgs(queryId)!,\n };\n queryTokenToValue.set(queryToken, query);\n }\n }\n\n // Query tokens that are new (because of new server results or new local optimistic updates)\n // or differ from old values (because of changes from local optimistic updates or new results\n // from the server).\n const changedQueryTokens =\n this.optimisticQueryResults.ingestQueryResultsFromServer(\n queryTokenToValue,\n new Set(completedRequests.keys()),\n );\n\n this.handleTransition({\n queries: changedQueryTokens.map((token) => {\n const optimisticResult =\n this.optimisticQueryResults.rawQueryResult(token);\n return {\n token,\n modification: {\n kind: \"Updated\" as const,\n result: optimisticResult,\n },\n };\n }),\n reflectedMutations: Array.from(completedRequests).map(\n ([requestId, result]) => ({\n requestId,\n result,\n }),\n ),\n timestamp: this.remoteQuerySet.timestamp(),\n });\n }\n\n private handleTransition(transition: Transition) {\n for (const fn of this._onTransitionFns.values()) {\n fn(transition);\n }\n }\n\n /**\n * Add a handler that will be called on a transition.\n *\n * Any external side effects (e.g. setting React state) should be handled here.\n *\n * @param fn\n *\n * @returns\n */\n addOnTransitionHandler(fn: (transition: Transition) => void) {\n const id = this._transitionHandlerCounter++;\n this._onTransitionFns.set(id, fn);\n return () => this._onTransitionFns.delete(id);\n }\n\n /**\n * Get the current JWT auth token and decoded claims.\n */\n getCurrentAuthClaims():\n | { token: string; decoded: Record<string, any> }\n | undefined {\n const authToken = this.state.getAuth();\n let decoded: Record<string, any> = {};\n if (authToken && authToken.tokenType === \"User\") {\n try {\n decoded = authToken ? jwtDecode(authToken.value) : {};\n } catch {\n decoded = {};\n }\n } else {\n return undefined;\n }\n return { token: authToken.value, decoded };\n }\n\n /**\n * Set the authentication token to be used for subsequent queries and mutations.\n * `fetchToken` will be called automatically again if a token expires.\n * `fetchToken` should return `null` if the token cannot be retrieved, for example\n * when the user's rights were permanently revoked.\n * @param fetchToken - an async function returning the JWT-encoded OpenID Connect Identity Token\n * @param onChange - a callback that will be called when the authentication status changes\n * @param onRefreshChange - a callback called with `true` when the socket is paused to fetch a replacement token after a server rejection, and `false` when refresh completes\n */\n setAuth(\n fetchToken: AuthTokenFetcher,\n onChange: (isAuthenticated: boolean) => void,\n onRefreshChange?: (isRefreshing: boolean) => void,\n ) {\n void this.authenticationManager.setConfig(\n fetchToken,\n onChange,\n onRefreshChange,\n );\n }\n\n hasAuth() {\n return this.state.hasAuth();\n }\n\n /** @internal */\n setAdminAuth(value: string, fakeUserIdentity?: UserIdentityAttributes) {\n const message = this.state.setAdminAuth(value, fakeUserIdentity);\n this.webSocketManager.sendMessage(message);\n }\n\n clearAuth() {\n const message = this.state.clearAuth();\n this.webSocketManager.sendMessage(message);\n }\n\n /**\n * Subscribe to a query function.\n *\n * Whenever this query's result changes, the `onTransition` callback\n * passed into the constructor will be called.\n *\n * @param name - The name of the query.\n * @param args - An arguments object for the query. If this is omitted, the\n * arguments will be `{}`.\n * @param options - A {@link SubscribeOptions} options object for this query.\n\n * @returns An object containing a {@link QueryToken} corresponding to this\n * query and an `unsubscribe` callback.\n */\n subscribe(\n name: string,\n args?: Record<string, Value>,\n options?: SubscribeOptions,\n ): { queryToken: QueryToken; unsubscribe: () => void } {\n const argsObject = parseArgs(args);\n\n const { modification, queryToken, unsubscribe } = this.state.subscribe(\n name,\n argsObject,\n options?.journal,\n options?.componentPath,\n );\n if (modification !== null) {\n this.webSocketManager.sendMessage(modification);\n }\n return {\n queryToken,\n unsubscribe: () => {\n const modification = unsubscribe();\n if (modification) {\n this.webSocketManager.sendMessage(modification);\n }\n },\n };\n }\n\n /**\n * A query result based only on the current, local state.\n *\n * The only way this will return a value is if we're already subscribed to the\n * query or its value has been set optimistically.\n */\n localQueryResult(\n udfPath: string,\n args?: Record<string, Value>,\n ): Value | undefined {\n const argsObject = parseArgs(args);\n const queryToken = serializePathAndArgs(udfPath, argsObject);\n return this.optimisticQueryResults.queryResult(queryToken);\n }\n\n /**\n * Get query result by query token based on current, local state\n *\n * The only way this will return a value is if we're already subscribed to the\n * query or its value has been set optimistically.\n *\n * @internal\n */\n localQueryResultByToken(queryToken: QueryToken): Value | undefined {\n return this.optimisticQueryResults.queryResult(queryToken);\n }\n\n /**\n * Whether local query result is available for a token.\n *\n * This method does not throw if the result is an error.\n *\n * @internal\n */\n hasLocalQueryResultByToken(queryToken: QueryToken): boolean {\n return this.optimisticQueryResults.hasQueryResult(queryToken);\n }\n\n /**\n * @internal\n */\n localQueryLogs(\n udfPath: string,\n args?: Record<string, Value>,\n ): string[] | undefined {\n const argsObject = parseArgs(args);\n const queryToken = serializePathAndArgs(udfPath, argsObject);\n return this.optimisticQueryResults.queryLogs(queryToken);\n }\n\n /**\n * Retrieve the current {@link QueryJournal} for this query function.\n *\n * If we have not yet received a result for this query, this will be `undefined`.\n *\n * @param name - The name of the query.\n * @param args - The arguments object for this query.\n * @returns The query's {@link QueryJournal} or `undefined`.\n */\n queryJournal(\n name: string,\n args?: Record<string, Value>,\n ): QueryJournal | undefined {\n const argsObject = parseArgs(args);\n const queryToken = serializePathAndArgs(name, argsObject);\n return this.state.queryJournal(queryToken);\n }\n\n /**\n * Get the current {@link ConnectionState} between the client and the Convex\n * backend.\n *\n * @returns The {@link ConnectionState} with the Convex backend.\n */\n connectionState(): ConnectionState {\n const wsConnectionState = this.webSocketManager.connectionState();\n return {\n hasInflightRequests: this.requestManager.hasInflightRequests(),\n isWebSocketConnected: wsConnectionState.isConnected,\n hasEverConnected: wsConnectionState.hasEverConnected,\n connectionCount: wsConnectionState.connectionCount,\n connectionRetries: wsConnectionState.connectionRetries,\n timeOfOldestInflightRequest:\n this.requestManager.timeOfOldestInflightRequest(),\n inflightMutations: this.requestManager.inflightMutations(),\n inflightActions: this.requestManager.inflightActions(),\n };\n }\n\n /**\n * Call this whenever the connection state may have changed in a way that could\n * require publishing it. Schedules a possibly update.\n */\n private markConnectionStateDirty = () => {\n void Promise.resolve().then(() => {\n const curConnectionState = this.connectionState();\n if (\n JSON.stringify(curConnectionState) !==\n JSON.stringify(this._lastPublishedConnectionState)\n ) {\n this._lastPublishedConnectionState = curConnectionState;\n for (const cb of this.connectionStateSubscribers.values()) {\n // One of these callback throwing will prevent other callbacks\n // from running but will not leave the client in a undefined state.\n cb(curConnectionState);\n }\n }\n });\n };\n\n /**\n * Subscribe to the {@link ConnectionState} between the client and the Convex\n * backend, calling a callback each time it changes.\n *\n * Subscribed callbacks will be called when any part of ConnectionState changes.\n * ConnectionState may grow in future versions (e.g. to provide a array of\n * inflight requests) in which case callbacks would be called more frequently.\n *\n * @returns An unsubscribe function to stop listening.\n */\n subscribeToConnectionState(\n cb: (connectionState: ConnectionState) => void,\n ): () => void {\n const id = this.nextConnectionStateSubscriberId++;\n this.connectionStateSubscribers.set(id, cb);\n return () => {\n this.connectionStateSubscribers.delete(id);\n };\n }\n\n /**\n * Execute a mutation function.\n *\n * @param name - The name of the mutation.\n * @param args - An arguments object for the mutation. If this is omitted,\n * the arguments will be `{}`.\n * @param options - A {@link MutationOptions} options object for this mutation.\n\n * @returns - A promise of the mutation's result.\n */\n async mutation(\n name: string,\n args?: Record<string, Value>,\n options?: MutationOptions,\n ): Promise<any> {\n const result = await this.mutationInternal(name, args, options);\n if (!result.success) {\n if (result.errorData !== undefined) {\n throw forwardData(\n result,\n new ConvexError(\n createHybridErrorStacktrace(\"mutation\", name, result),\n ),\n );\n }\n throw new Error(createHybridErrorStacktrace(\"mutation\", name, result));\n }\n return result.value;\n }\n\n /**\n * @internal\n */\n async mutationInternal(\n udfPath: string,\n args?: Record<string, Value>,\n options?: MutationOptions,\n componentPath?: string,\n ): Promise<FunctionResult> {\n const { mutationPromise } = this.enqueueMutation(\n udfPath,\n args,\n options,\n componentPath,\n );\n return mutationPromise;\n }\n\n /**\n * @internal\n */\n enqueueMutation(\n udfPath: string,\n args?: Record<string, Value>,\n options?: MutationOptions,\n componentPath?: string,\n ): { requestId: RequestId; mutationPromise: Promise<FunctionResult> } {\n const mutationArgs = parseArgs(args);\n this.tryReportLongDisconnect();\n const requestId = this.nextRequestId;\n this._nextRequestId++;\n\n if (options !== undefined) {\n const optimisticUpdate = options.optimisticUpdate;\n if (optimisticUpdate !== undefined) {\n const wrappedUpdate = (localQueryStore: OptimisticLocalStore) => {\n const result: unknown = optimisticUpdate(\n localQueryStore,\n mutationArgs,\n );\n if (result instanceof Promise) {\n this.logger.warn(\n \"Optimistic update handler returned a Promise. Optimistic updates should be synchronous.\",\n );\n }\n };\n\n const changedQueryTokens =\n this.optimisticQueryResults.applyOptimisticUpdate(\n wrappedUpdate,\n requestId,\n );\n\n const changedQueries = changedQueryTokens.map((token) => {\n const localResult = this.localQueryResultByToken(token);\n return {\n token,\n modification: {\n kind: \"Updated\" as const,\n result:\n localResult === undefined\n ? undefined\n : {\n success: true as const,\n value: localResult,\n logLines: [],\n },\n },\n };\n });\n this.handleTransition({\n queries: changedQueries,\n reflectedMutations: [],\n timestamp: this.remoteQuerySet.timestamp(),\n });\n }\n }\n\n const message: MutationRequest = {\n type: \"Mutation\",\n requestId,\n udfPath,\n componentPath,\n args: [convexToJson(mutationArgs)],\n };\n const mightBeSent = this.webSocketManager.sendMessage(message);\n const mutationPromise = this.requestManager.request(message, mightBeSent);\n return {\n requestId,\n mutationPromise,\n };\n }\n\n /**\n * Execute an action function.\n *\n * @param name - The name of the action.\n * @param args - An arguments object for the action. If this is omitted,\n * the arguments will be `{}`.\n * @returns A promise of the action's result.\n */\n async action(name: string, args?: Record<string, Value>): Promise<any> {\n const result = await this.actionInternal(name, args);\n if (!result.success) {\n if (result.errorData !== undefined) {\n throw forwardData(\n result,\n new ConvexError(createHybridErrorStacktrace(\"action\", name, result)),\n );\n }\n throw new Error(createHybridErrorStacktrace(\"action\", name, result));\n }\n return result.value;\n }\n\n /**\n * @internal\n */\n async actionInternal(\n udfPath: string,\n args?: Record<string, Value>,\n componentPath?: string,\n ): Promise<FunctionResult> {\n const actionArgs = parseArgs(args);\n const requestId = this.nextRequestId;\n this._nextRequestId++;\n this.tryReportLongDisconnect();\n\n const message: ActionRequest = {\n type: \"Action\",\n requestId,\n udfPath,\n componentPath,\n args: [convexToJson(actionArgs)],\n };\n\n const mightBeSent = this.webSocketManager.sendMessage(message);\n return this.requestManager.request(message, mightBeSent);\n }\n\n /**\n * Close any network handles associated with this client and stop all subscriptions.\n *\n * Call this method when you're done with an {@link BaseConvexClient} to\n * dispose of its sockets and resources.\n *\n * @returns A `Promise` fulfilled when the connection has been completely closed.\n */\n async close(): Promise<void> {\n this.authenticationManager.stop();\n return this.webSocketManager.terminate();\n }\n\n /**\n * Return the address for this client, useful for creating a new client.\n *\n * Not guaranteed to match the address with which this client was constructed:\n * it may be canonicalized.\n */\n get url() {\n return this.address;\n }\n\n /**\n * @internal\n */\n get nextRequestId() {\n return this._nextRequestId;\n }\n\n /**\n * @internal\n */\n get sessionId() {\n return this._sessionId;\n }\n\n // Instance property so that `mark()` doesn't need to be called as a method.\n private mark = (name: MarkName) => {\n if (this.debug) {\n mark(name, this.sessionId);\n }\n };\n\n /**\n * Reports performance marks to the server. This should only be called when\n * we have a functional websocket.\n */\n private reportMarks() {\n if (this.debug) {\n const report = getMarksReport(this.sessionId);\n this.webSocketManager.sendMessage({\n type: \"Event\",\n eventType: \"ClientConnect\",\n event: report,\n });\n }\n }\n\n private tryReportLongDisconnect() {\n if (!this.debug) {\n return;\n }\n const timeOfOldestRequest =\n this.connectionState().timeOfOldestInflightRequest;\n if (\n timeOfOldestRequest === null ||\n Date.now() - timeOfOldestRequest.getTime() <= 60 * 1000\n ) {\n return;\n }\n const endpoint = `${this.address}/api/debug_event`;\n fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Convex-Client\": `npm-${version}`,\n },\n body: JSON.stringify({ event: \"LongWebsocketDisconnect\" }),\n })\n .then((response) => {\n if (!response.ok) {\n this.logger.warn(\n \"Analytics request failed with response:\",\n response.body,\n );\n }\n })\n .catch((error) => {\n this.logger.warn(\"Analytics response failed with error:\", error);\n });\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * BaseConvexClient should not be used directly and does not provide a stable\n * interface. It is a \"Base\" client not because it expects to be inherited from\n * but because other clients are built around it.\n *\n * BaseConvexClient is not Convex Function type-aware: it deals\n * with queries as functions that return Value, not the specific value.\n * Use a higher-level library to get types.\n */\nimport { version } from \"../../index.js\";\nimport { convexToJson, Value } from \"../../values/index.js\";\nimport {\n createHybridErrorStacktrace,\n forwardData,\n instantiateDefaultLogger,\n instantiateNoopLogger,\n logFatalError,\n Logger,\n} from \"../logging.js\";\nimport { LocalSyncState } from \"./local_state.js\";\nimport { RequestManager } from \"./request_manager.js\";\nimport {\n OptimisticLocalStore,\n OptimisticUpdate,\n} from \"./optimistic_updates.js\";\nimport {\n OptimisticQueryResults,\n QueryResultsMap,\n} from \"./optimistic_updates_impl.js\";\nimport {\n ActionRequest,\n MutationRequest,\n QueryId,\n QueryJournal,\n ServerMessage,\n RequestId,\n TS,\n UserIdentityAttributes,\n} from \"./protocol.js\";\nimport { RemoteQuerySet } from \"./remote_query_set.js\";\nimport { QueryToken, serializePathAndArgs } from \"./udf_path_utils.js\";\nimport { ReconnectMetadata, WebSocketManager } from \"./web_socket_manager.js\";\nimport { newSessionId } from \"./session.js\";\nimport { FunctionResult } from \"./function_result.js\";\nimport {\n AuthenticationManager,\n AuthTokenFetcher,\n} from \"./authentication_manager.js\";\nexport { type AuthTokenFetcher } from \"./authentication_manager.js\";\nimport { getMarksReport, mark, MarkName } from \"./metrics.js\";\nimport { parseArgs, validateDeploymentUrl } from \"../../common/index.js\";\nimport { ConvexError } from \"../../values/errors.js\";\nimport { jwtDecode } from \"../../vendor/jwt-decode/index.js\";\n\n/**\n * Options for {@link BaseConvexClient}.\n *\n * @public\n */\nexport interface BaseConvexClientOptions {\n /**\n * Whether to prompt the user if they have unsaved changes pending\n * when navigating away or closing a web page.\n *\n * This is only possible when the `window` object exists, i.e. in a browser.\n *\n * The default value is `true` in browsers.\n */\n unsavedChangesWarning?: boolean;\n /**\n * Specifies an alternate\n * [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)\n * constructor to use for client communication with the Convex cloud.\n * The default behavior is to use `WebSocket` from the global environment.\n */\n webSocketConstructor?: typeof WebSocket;\n /**\n * Adds additional logging for debugging purposes.\n *\n * The default value is `false`.\n */\n verbose?: boolean;\n /**\n * A logger, `true`, or `false`. If not provided or `true`, logs to the console.\n * If `false`, logs are not printed anywhere.\n *\n * You can construct your own logger to customize logging to log elsewhere.\n * A logger is an object with 4 methods: log(), warn(), error(), and logVerbose().\n * These methods can receive multiple arguments of any types, like console.log().\n */\n logger?: Logger | boolean;\n /**\n * Sends additional metrics to Convex for debugging purposes.\n *\n * The default value is `false`.\n */\n reportDebugInfoToConvex?: boolean;\n /**\n * This API is experimental: it may change or disappear.\n *\n * A function to call on receiving abnormal WebSocket close messages from the\n * connected Convex deployment. The content of these messages is not stable,\n * it is an implementation detail that may change.\n *\n * Consider this API an observability stopgap until higher level codes with\n * recommendations on what to do are available, which could be a more stable\n * interface instead of `string`.\n *\n * Check `connectionState` for more quantitative metrics about connection status.\n */\n onServerDisconnectError?: (message: string) => void;\n /**\n * Skip validating that the Convex deployment URL looks like\n * `https://happy-animal-123.convex.cloud` or localhost.\n *\n * This can be useful if running a self-hosted Convex backend that uses a different\n * URL.\n *\n * The default value is `false`\n */\n skipConvexDeploymentUrlCheck?: boolean;\n /**\n * If using auth, the number of seconds before a token expires that we should refresh it.\n *\n * The default value is `10`.\n */\n authRefreshTokenLeewaySeconds?: number;\n /**\n * This API is experimental: it may change or disappear.\n *\n * Whether query, mutation, and action requests should be held back\n * until the first auth token can be sent.\n *\n * Opting into this behavior works well for pages that should\n * only be viewed by authenticated clients.\n *\n * Defaults to false, not waiting for an auth token.\n */\n expectAuth?: boolean;\n /**\n * This API is experimental: it may change or disappear.\n *\n * When true, the client reuses the initial cached auth token instead\n * of immediately fetching a fresh one. This avoids a second\n * Authenticate message that causes the server to re-execute all\n * authenticated queries.\n *\n * The cached token's remaining lifetime is estimated using the\n * server's clock skew measurement, and a refresh is scheduled\n * before it expires.\n *\n * Defaults to false, preserving the original behavior of immediately\n * fetching a fresh token after the cached token is confirmed.\n */\n initialAuthTokenReuse?: boolean;\n}\n\n/**\n * State describing the client's connection with the Convex backend.\n *\n * @public\n */\nexport type ConnectionState = {\n hasInflightRequests: boolean;\n isWebSocketConnected: boolean;\n timeOfOldestInflightRequest: Date | null;\n /**\n * True if the client has ever opened a WebSocket to the \"ready\" state.\n */\n hasEverConnected: boolean;\n /**\n * The number of times this client has connected to the Convex backend.\n *\n * A number of things can cause the client to reconnect -- server errors,\n * bad internet, auth expiring. But this number being high is an indication\n * that the client is having trouble keeping a stable connection.\n */\n connectionCount: number;\n /**\n * The number of times this client has tried (and failed) to connect to the Convex backend.\n */\n connectionRetries: number;\n /**\n * The number of mutations currently in flight.\n */\n inflightMutations: number;\n /**\n * The number of actions currently in flight.\n */\n inflightActions: number;\n};\n\n/**\n * Options for {@link BaseConvexClient.subscribe}.\n *\n * @public\n */\nexport interface SubscribeOptions {\n /**\n * An (optional) journal produced from a previous execution of this query\n * function.\n *\n * If there is an existing subscription to a query function with the same\n * name and arguments, this journal will have no effect.\n */\n journal?: QueryJournal;\n\n /**\n * @internal\n */\n componentPath?: string | undefined;\n}\n\n/**\n * Options for {@link BaseConvexClient.mutation}.\n *\n * @public\n */\nexport interface MutationOptions {\n /**\n * An optimistic update to apply along with this mutation.\n *\n * An optimistic update locally updates queries while a mutation is pending.\n * Once the mutation completes, the update will be rolled back.\n */\n optimisticUpdate?: OptimisticUpdate<any> | undefined;\n}\n\n/**\n * Type describing updates to a query within a `Transition`.\n *\n * @public\n */\nexport type QueryModification =\n // `undefined` generally comes from an optimistic update setting the query to be loading\n { kind: \"Updated\"; result: FunctionResult | undefined } | { kind: \"Removed\" };\n\n/**\n * Object describing a transition passed into the `onTransition` handler.\n *\n * These can be from receiving a transition from the server, or from applying an\n * optimistic update locally.\n *\n * @public\n */\nexport type Transition = {\n queries: Array<{ token: QueryToken; modification: QueryModification }>;\n reflectedMutations: Array<{ requestId: RequestId; result: FunctionResult }>;\n timestamp: TS;\n};\n\n/**\n * Low-level client for directly integrating state management libraries\n * with Convex.\n *\n * Most developers should use higher level clients, like\n * the {@link ConvexHttpClient} or the React hook based {@link react.ConvexReactClient}.\n *\n * @public\n */\nexport class BaseConvexClient {\n private readonly address: string;\n private readonly state: LocalSyncState;\n private readonly requestManager: RequestManager;\n private readonly webSocketManager: WebSocketManager;\n private readonly authenticationManager: AuthenticationManager;\n private remoteQuerySet: RemoteQuerySet;\n private readonly optimisticQueryResults: OptimisticQueryResults;\n private _transitionHandlerCounter = 0;\n private _nextRequestId: RequestId;\n private _onTransitionFns: Map<number, (transition: Transition) => void> =\n new Map();\n private readonly _sessionId: string;\n private firstMessageReceived = false;\n private readonly debug: boolean;\n private readonly logger: Logger;\n private maxObservedTimestamp: TS | undefined;\n private connectionStateSubscribers = new Map<\n number,\n (connectionState: ConnectionState) => void\n >();\n private nextConnectionStateSubscriberId: number = 0;\n private _lastPublishedConnectionState: ConnectionState | undefined;\n\n /**\n * @param address - The url of your Convex deployment, often provided\n * by an environment variable. E.g. `https://small-mouse-123.convex.cloud`.\n * @param onTransition - A callback receiving an array of query tokens\n * corresponding to query results that have changed -- additional handlers\n * can be added via `addOnTransitionHandler`.\n * @param options - See {@link BaseConvexClientOptions} for a full description.\n */\n constructor(\n address: string,\n onTransition: (updatedQueries: QueryToken[]) => void,\n options?: BaseConvexClientOptions,\n ) {\n if (typeof address === \"object\") {\n throw new Error(\n \"Passing a ClientConfig object is no longer supported. Pass the URL of the Convex deployment as a string directly.\",\n );\n }\n if (options?.skipConvexDeploymentUrlCheck !== true) {\n validateDeploymentUrl(address);\n }\n options = { ...options };\n const authRefreshTokenLeewaySeconds =\n options.authRefreshTokenLeewaySeconds ?? 10;\n let webSocketConstructor = options.webSocketConstructor;\n if (!webSocketConstructor && typeof WebSocket === \"undefined\") {\n throw new Error(\n \"No WebSocket global variable defined! To use Convex in an environment without WebSocket try the HTTP client: https://docs.convex.dev/api/classes/browser.ConvexHttpClient\",\n );\n }\n webSocketConstructor = webSocketConstructor || WebSocket;\n this.debug = options.reportDebugInfoToConvex ?? false;\n this.address = address;\n this.logger =\n options.logger === false\n ? instantiateNoopLogger({ verbose: options.verbose ?? false })\n : options.logger !== true && options.logger\n ? options.logger\n : instantiateDefaultLogger({ verbose: options.verbose ?? false });\n // Substitute http(s) with ws(s)\n const i = address.search(\"://\");\n if (i === -1) {\n throw new Error(\"Provided address was not an absolute URL.\");\n }\n const origin = address.substring(i + 3); // move past the double slash\n const protocol = address.substring(0, i);\n let wsProtocol;\n if (protocol === \"http\") {\n wsProtocol = \"ws\";\n } else if (protocol === \"https\") {\n wsProtocol = \"wss\";\n } else {\n throw new Error(`Unknown parent protocol ${protocol}`);\n }\n const wsUri = `${wsProtocol}://${origin}/api/${version}/sync`;\n\n this.state = new LocalSyncState();\n this.remoteQuerySet = new RemoteQuerySet(\n (queryId) => this.state.queryPath(queryId),\n this.logger,\n );\n this.requestManager = new RequestManager(\n this.logger,\n this.markConnectionStateDirty,\n );\n\n // This is a callback for AuthenticationManager (which can't call\n // this synchronously, the callback wouldn't work) so the initial\n // pause for expectAuth we call it at the end of this constructor.\n const pauseSocket = () => {\n this.webSocketManager.pause();\n this.state.pause();\n };\n this.authenticationManager = new AuthenticationManager(\n this.state,\n {\n authenticate: (token) => {\n const message = this.state.setAuth(token);\n this.webSocketManager.sendMessage(message);\n return message.baseVersion;\n },\n stopSocket: () => this.webSocketManager.stop(),\n tryRestartSocket: () => this.webSocketManager.tryRestart(),\n pauseSocket,\n resumeSocket: () => this.webSocketManager.resume(),\n clearAuth: () => {\n this.clearAuth();\n },\n },\n {\n logger: this.logger,\n refreshTokenLeewaySeconds: authRefreshTokenLeewaySeconds,\n initialAuthTokenReuse: options.initialAuthTokenReuse ?? false,\n },\n );\n this.optimisticQueryResults = new OptimisticQueryResults();\n this.addOnTransitionHandler((transition) => {\n onTransition(transition.queries.map((q) => q.token));\n });\n this._nextRequestId = 0;\n this._sessionId = newSessionId();\n\n const { unsavedChangesWarning } = options;\n if (\n typeof window === \"undefined\" ||\n typeof window.addEventListener === \"undefined\"\n ) {\n if (unsavedChangesWarning === true) {\n throw new Error(\n \"unsavedChangesWarning requested, but window.addEventListener not found! Remove {unsavedChangesWarning: true} from Convex client options.\",\n );\n }\n } else if (unsavedChangesWarning !== false) {\n // Listen for tab close events and notify the user on unsaved changes.\n window.addEventListener(\"beforeunload\", (e) => {\n if (this.requestManager.hasIncompleteRequests()) {\n // There are 3 different ways to trigger this pop up so just try all of\n // them.\n\n e.preventDefault();\n // This confirmation message doesn't actually appear in most modern\n // browsers but we tried.\n const confirmationMessage =\n \"Are you sure you want to leave? Your changes may not be saved.\";\n // Recommended method for legacy (IE) browsers.\n // casts to avoid deprecation notices\n ((e || (window as any).event) as any).returnValue =\n confirmationMessage;\n return confirmationMessage;\n }\n });\n }\n\n this.webSocketManager = new WebSocketManager(\n wsUri,\n {\n onOpen: (reconnectMetadata: ReconnectMetadata) => {\n // We have a new WebSocket!\n this.mark(\"convexWebSocketOpen\");\n this.webSocketManager.sendMessage({\n ...reconnectMetadata,\n type: \"Connect\",\n sessionId: this._sessionId,\n maxObservedTimestamp: this.maxObservedTimestamp,\n });\n\n // Throw out our remote query, reissue queries\n // and outstanding mutations, and reauthenticate.\n this.remoteQuerySet = new RemoteQuerySet(\n (queryId) => this.state.queryPath(queryId),\n this.logger,\n );\n const [querySetModification, authModification] = this.state.restart();\n if (authModification) {\n this.webSocketManager.sendMessage(authModification);\n }\n this.webSocketManager.sendMessage(querySetModification);\n for (const message of this.requestManager.restart()) {\n this.webSocketManager.sendMessage(message);\n }\n },\n onResume: () => {\n const [querySetModification, authModification] = this.state.resume();\n if (authModification) {\n this.webSocketManager.sendMessage(authModification);\n }\n if (querySetModification) {\n this.webSocketManager.sendMessage(querySetModification);\n }\n for (const message of this.requestManager.resume()) {\n this.webSocketManager.sendMessage(message);\n }\n },\n onMessage: (serverMessage: ServerMessage) => {\n // Metrics events grow linearly with reconnection attempts so this\n // conditional prevents n^2 metrics reporting.\n if (!this.firstMessageReceived) {\n this.firstMessageReceived = true;\n this.mark(\"convexFirstMessageReceived\");\n this.reportMarks();\n }\n switch (serverMessage.type) {\n case \"Transition\": {\n this.observedTimestamp(serverMessage.endVersion.ts);\n this.authenticationManager.onTransition(serverMessage);\n this.remoteQuerySet.transition(serverMessage);\n this.state.transition(serverMessage);\n const completedRequests = this.requestManager.removeCompleted(\n this.remoteQuerySet.timestamp(),\n );\n this.notifyOnQueryResultChanges(completedRequests);\n break;\n }\n case \"MutationResponse\": {\n if (serverMessage.success) {\n this.observedTimestamp(serverMessage.ts);\n }\n const completedMutationInfo =\n this.requestManager.onResponse(serverMessage);\n if (completedMutationInfo !== null) {\n this.notifyOnQueryResultChanges(\n new Map([\n [\n completedMutationInfo.requestId,\n completedMutationInfo.result,\n ],\n ]),\n );\n }\n break;\n }\n case \"ActionResponse\": {\n this.requestManager.onResponse(serverMessage);\n break;\n }\n case \"AuthError\": {\n this.authenticationManager.onAuthError(serverMessage);\n break;\n }\n case \"FatalError\": {\n const error = logFatalError(this.logger, serverMessage.error);\n void this.webSocketManager.terminate();\n throw error;\n }\n default: {\n serverMessage satisfies never;\n }\n }\n\n return {\n hasSyncedPastLastReconnect: this.hasSyncedPastLastReconnect(),\n };\n },\n onServerDisconnectError: options.onServerDisconnectError,\n },\n webSocketConstructor,\n this.logger,\n this.markConnectionStateDirty,\n this.debug,\n );\n this.mark(\"convexClientConstructed\");\n\n // Begin client in a paused state waiting for an auth token.\n if (options.expectAuth) {\n pauseSocket();\n }\n }\n\n /**\n * Return true if there is outstanding work from prior to the time of the most recent restart.\n * This indicates that the client has not proven itself to have gotten past the issue that\n * potentially led to the restart. Use this to influence when to reset backoff after a failure.\n */\n private hasSyncedPastLastReconnect() {\n const hasSyncedPastLastReconnect =\n this.requestManager.hasSyncedPastLastReconnect() &&\n this.state.hasSyncedPastLastReconnect();\n return hasSyncedPastLastReconnect;\n }\n\n private observedTimestamp(observedTs: TS) {\n if (\n this.maxObservedTimestamp === undefined ||\n this.maxObservedTimestamp.lessThanOrEqual(observedTs)\n ) {\n this.maxObservedTimestamp = observedTs;\n }\n }\n\n getMaxObservedTimestamp() {\n return this.maxObservedTimestamp;\n }\n\n /**\n * Compute the current query results based on the remoteQuerySet and the\n * current optimistic updates and call `onTransition` for all the changed\n * queries.\n *\n * @param completedMutations - A set of mutation IDs whose optimistic updates\n * are no longer needed.\n */\n private notifyOnQueryResultChanges(\n completedRequests: Map<RequestId, FunctionResult>,\n ) {\n const remoteQueryResults: Map<QueryId, FunctionResult> =\n this.remoteQuerySet.remoteQueryResults();\n const queryTokenToValue: QueryResultsMap = new Map();\n for (const [queryId, result] of remoteQueryResults) {\n const queryToken = this.state.queryToken(queryId);\n // It's possible that we've already unsubscribed to this query but\n // the server hasn't learned about that yet. If so, ignore this one.\n\n if (queryToken !== null) {\n const query = {\n result,\n udfPath: this.state.queryPath(queryId)!,\n args: this.state.queryArgs(queryId)!,\n };\n queryTokenToValue.set(queryToken, query);\n }\n }\n\n // Query tokens that are new (because of new server results or new local optimistic updates)\n // or differ from old values (because of changes from local optimistic updates or new results\n // from the server).\n const changedQueryTokens =\n this.optimisticQueryResults.ingestQueryResultsFromServer(\n queryTokenToValue,\n new Set(completedRequests.keys()),\n );\n\n this.handleTransition({\n queries: changedQueryTokens.map((token) => {\n const optimisticResult =\n this.optimisticQueryResults.rawQueryResult(token);\n return {\n token,\n modification: {\n kind: \"Updated\" as const,\n result: optimisticResult,\n },\n };\n }),\n reflectedMutations: Array.from(completedRequests).map(\n ([requestId, result]) => ({\n requestId,\n result,\n }),\n ),\n timestamp: this.remoteQuerySet.timestamp(),\n });\n }\n\n private handleTransition(transition: Transition) {\n for (const fn of this._onTransitionFns.values()) {\n fn(transition);\n }\n }\n\n /**\n * Add a handler that will be called on a transition.\n *\n * Any external side effects (e.g. setting React state) should be handled here.\n *\n * @param fn\n *\n * @returns\n */\n addOnTransitionHandler(fn: (transition: Transition) => void) {\n const id = this._transitionHandlerCounter++;\n this._onTransitionFns.set(id, fn);\n return () => this._onTransitionFns.delete(id);\n }\n\n /**\n * Get the current JWT auth token and decoded claims.\n */\n getCurrentAuthClaims():\n | { token: string; decoded: Record<string, any> }\n | undefined {\n const authToken = this.state.getAuth();\n let decoded: Record<string, any> = {};\n if (authToken && authToken.tokenType === \"User\") {\n try {\n decoded = authToken ? jwtDecode(authToken.value) : {};\n } catch {\n decoded = {};\n }\n } else {\n return undefined;\n }\n return { token: authToken.value, decoded };\n }\n\n /**\n * Set the authentication token to be used for subsequent queries and mutations.\n * `fetchToken` will be called automatically again if a token expires.\n * `fetchToken` should return `null` if the token cannot be retrieved, for example\n * when the user's rights were permanently revoked.\n * @param fetchToken - an async function returning the JWT-encoded OpenID Connect Identity Token\n * @param onChange - a callback that will be called when the authentication status changes\n * @param onRefreshChange - a callback called with `true` when the socket is paused to fetch a replacement token after a server rejection, and `false` when refresh completes\n */\n setAuth(\n fetchToken: AuthTokenFetcher,\n onChange: (isAuthenticated: boolean) => void,\n onRefreshChange?: (isRefreshing: boolean) => void,\n ) {\n void this.authenticationManager.setConfig(\n fetchToken,\n onChange,\n onRefreshChange,\n );\n }\n\n hasAuth() {\n return this.state.hasAuth();\n }\n\n /** @internal */\n setAdminAuth(value: string, fakeUserIdentity?: UserIdentityAttributes) {\n const message = this.state.setAdminAuth(value, fakeUserIdentity);\n this.webSocketManager.sendMessage(message);\n }\n\n clearAuth() {\n const message = this.state.clearAuth();\n this.webSocketManager.sendMessage(message);\n }\n\n /**\n * Subscribe to a query function.\n *\n * Whenever this query's result changes, the `onTransition` callback\n * passed into the constructor will be called.\n *\n * @param name - The name of the query.\n * @param args - An arguments object for the query. If this is omitted, the\n * arguments will be `{}`.\n * @param options - A {@link SubscribeOptions} options object for this query.\n\n * @returns An object containing a {@link QueryToken} corresponding to this\n * query and an `unsubscribe` callback.\n */\n subscribe(\n name: string,\n args?: Record<string, Value>,\n options?: SubscribeOptions,\n ): { queryToken: QueryToken; unsubscribe: () => void } {\n const argsObject = parseArgs(args);\n\n const { modification, queryToken, unsubscribe } = this.state.subscribe(\n name,\n argsObject,\n options?.journal,\n options?.componentPath,\n );\n if (modification !== null) {\n this.webSocketManager.sendMessage(modification);\n }\n return {\n queryToken,\n unsubscribe: () => {\n const modification = unsubscribe();\n if (modification) {\n this.webSocketManager.sendMessage(modification);\n }\n },\n };\n }\n\n /**\n * A query result based only on the current, local state.\n *\n * The only way this will return a value is if we're already subscribed to the\n * query or its value has been set optimistically.\n */\n localQueryResult(\n udfPath: string,\n args?: Record<string, Value>,\n ): Value | undefined {\n const argsObject = parseArgs(args);\n const queryToken = serializePathAndArgs(udfPath, argsObject);\n return this.optimisticQueryResults.queryResult(queryToken);\n }\n\n /**\n * Get query result by query token based on current, local state\n *\n * The only way this will return a value is if we're already subscribed to the\n * query or its value has been set optimistically.\n *\n * @internal\n */\n localQueryResultByToken(queryToken: QueryToken): Value | undefined {\n return this.optimisticQueryResults.queryResult(queryToken);\n }\n\n /**\n * Whether local query result is available for a token.\n *\n * This method does not throw if the result is an error.\n *\n * @internal\n */\n hasLocalQueryResultByToken(queryToken: QueryToken): boolean {\n return this.optimisticQueryResults.hasQueryResult(queryToken);\n }\n\n /**\n * @internal\n */\n localQueryLogs(\n udfPath: string,\n args?: Record<string, Value>,\n ): string[] | undefined {\n const argsObject = parseArgs(args);\n const queryToken = serializePathAndArgs(udfPath, argsObject);\n return this.optimisticQueryResults.queryLogs(queryToken);\n }\n\n /**\n * Retrieve the current {@link QueryJournal} for this query function.\n *\n * If we have not yet received a result for this query, this will be `undefined`.\n *\n * @param name - The name of the query.\n * @param args - The arguments object for this query.\n * @returns The query's {@link QueryJournal} or `undefined`.\n */\n queryJournal(\n name: string,\n args?: Record<string, Value>,\n ): QueryJournal | undefined {\n const argsObject = parseArgs(args);\n const queryToken = serializePathAndArgs(name, argsObject);\n return this.state.queryJournal(queryToken);\n }\n\n /**\n * Get the current {@link ConnectionState} between the client and the Convex\n * backend.\n *\n * @returns The {@link ConnectionState} with the Convex backend.\n */\n connectionState(): ConnectionState {\n const wsConnectionState = this.webSocketManager.connectionState();\n return {\n hasInflightRequests: this.requestManager.hasInflightRequests(),\n isWebSocketConnected: wsConnectionState.isConnected,\n hasEverConnected: wsConnectionState.hasEverConnected,\n connectionCount: wsConnectionState.connectionCount,\n connectionRetries: wsConnectionState.connectionRetries,\n timeOfOldestInflightRequest:\n this.requestManager.timeOfOldestInflightRequest(),\n inflightMutations: this.requestManager.inflightMutations(),\n inflightActions: this.requestManager.inflightActions(),\n };\n }\n\n /**\n * Call this whenever the connection state may have changed in a way that could\n * require publishing it. Schedules a possibly update.\n */\n private markConnectionStateDirty = () => {\n void Promise.resolve().then(() => {\n const curConnectionState = this.connectionState();\n if (\n JSON.stringify(curConnectionState) !==\n JSON.stringify(this._lastPublishedConnectionState)\n ) {\n this._lastPublishedConnectionState = curConnectionState;\n for (const cb of this.connectionStateSubscribers.values()) {\n // One of these callback throwing will prevent other callbacks\n // from running but will not leave the client in a undefined state.\n cb(curConnectionState);\n }\n }\n });\n };\n\n /**\n * Subscribe to the {@link ConnectionState} between the client and the Convex\n * backend, calling a callback each time it changes.\n *\n * Subscribed callbacks will be called when any part of ConnectionState changes.\n * ConnectionState may grow in future versions (e.g. to provide a array of\n * inflight requests) in which case callbacks would be called more frequently.\n *\n * @returns An unsubscribe function to stop listening.\n */\n subscribeToConnectionState(\n cb: (connectionState: ConnectionState) => void,\n ): () => void {\n const id = this.nextConnectionStateSubscriberId++;\n this.connectionStateSubscribers.set(id, cb);\n return () => {\n this.connectionStateSubscribers.delete(id);\n };\n }\n\n /**\n * Execute a mutation function.\n *\n * @param name - The name of the mutation.\n * @param args - An arguments object for the mutation. If this is omitted,\n * the arguments will be `{}`.\n * @param options - A {@link MutationOptions} options object for this mutation.\n\n * @returns - A promise of the mutation's result.\n */\n async mutation(\n name: string,\n args?: Record<string, Value>,\n options?: MutationOptions,\n ): Promise<any> {\n const result = await this.mutationInternal(name, args, options);\n if (!result.success) {\n if (result.errorData !== undefined) {\n throw forwardData(\n result,\n new ConvexError(\n createHybridErrorStacktrace(\"mutation\", name, result),\n ),\n );\n }\n throw new Error(createHybridErrorStacktrace(\"mutation\", name, result));\n }\n return result.value;\n }\n\n /**\n * @internal\n */\n async mutationInternal(\n udfPath: string,\n args?: Record<string, Value>,\n options?: MutationOptions,\n componentPath?: string,\n ): Promise<FunctionResult> {\n const { mutationPromise } = this.enqueueMutation(\n udfPath,\n args,\n options,\n componentPath,\n );\n return mutationPromise;\n }\n\n /**\n * @internal\n */\n enqueueMutation(\n udfPath: string,\n args?: Record<string, Value>,\n options?: MutationOptions,\n componentPath?: string,\n ): { requestId: RequestId; mutationPromise: Promise<FunctionResult> } {\n const mutationArgs = parseArgs(args);\n this.tryReportLongDisconnect();\n const requestId = this.nextRequestId;\n this._nextRequestId++;\n\n if (options !== undefined) {\n const optimisticUpdate = options.optimisticUpdate;\n if (optimisticUpdate !== undefined) {\n const wrappedUpdate = (localQueryStore: OptimisticLocalStore) => {\n const result: unknown = optimisticUpdate(\n localQueryStore,\n mutationArgs,\n );\n if (result instanceof Promise) {\n this.logger.warn(\n \"Optimistic update handler returned a Promise. Optimistic updates should be synchronous.\",\n );\n }\n };\n\n const changedQueryTokens =\n this.optimisticQueryResults.applyOptimisticUpdate(\n wrappedUpdate,\n requestId,\n );\n\n const changedQueries = changedQueryTokens.map((token) => {\n const localResult = this.localQueryResultByToken(token);\n return {\n token,\n modification: {\n kind: \"Updated\" as const,\n result:\n localResult === undefined\n ? undefined\n : {\n success: true as const,\n value: localResult,\n logLines: [],\n },\n },\n };\n });\n this.handleTransition({\n queries: changedQueries,\n reflectedMutations: [],\n timestamp: this.remoteQuerySet.timestamp(),\n });\n }\n }\n\n const message: MutationRequest = {\n type: \"Mutation\",\n requestId,\n udfPath,\n componentPath,\n args: [convexToJson(mutationArgs)],\n };\n const mightBeSent = this.webSocketManager.sendMessage(message);\n const mutationPromise = this.requestManager.request(message, mightBeSent);\n return {\n requestId,\n mutationPromise,\n };\n }\n\n /**\n * Execute an action function.\n *\n * @param name - The name of the action.\n * @param args - An arguments object for the action. If this is omitted,\n * the arguments will be `{}`.\n * @returns A promise of the action's result.\n */\n async action(name: string, args?: Record<string, Value>): Promise<any> {\n const result = await this.actionInternal(name, args);\n if (!result.success) {\n if (result.errorData !== undefined) {\n throw forwardData(\n result,\n new ConvexError(createHybridErrorStacktrace(\"action\", name, result)),\n );\n }\n throw new Error(createHybridErrorStacktrace(\"action\", name, result));\n }\n return result.value;\n }\n\n /**\n * @internal\n */\n async actionInternal(\n udfPath: string,\n args?: Record<string, Value>,\n componentPath?: string,\n ): Promise<FunctionResult> {\n const actionArgs = parseArgs(args);\n const requestId = this.nextRequestId;\n this._nextRequestId++;\n this.tryReportLongDisconnect();\n\n const message: ActionRequest = {\n type: \"Action\",\n requestId,\n udfPath,\n componentPath,\n args: [convexToJson(actionArgs)],\n };\n\n const mightBeSent = this.webSocketManager.sendMessage(message);\n return this.requestManager.request(message, mightBeSent);\n }\n\n /**\n * Close any network handles associated with this client and stop all subscriptions.\n *\n * Call this method when you're done with an {@link BaseConvexClient} to\n * dispose of its sockets and resources.\n *\n * @returns A `Promise` fulfilled when the connection has been completely closed.\n */\n async close(): Promise<void> {\n this.authenticationManager.stop();\n return this.webSocketManager.terminate();\n }\n\n /**\n * Return the address for this client, useful for creating a new client.\n *\n * Not guaranteed to match the address with which this client was constructed:\n * it may be canonicalized.\n */\n get url() {\n return this.address;\n }\n\n /**\n * @internal\n */\n get nextRequestId() {\n return this._nextRequestId;\n }\n\n /**\n * @internal\n */\n get sessionId() {\n return this._sessionId;\n }\n\n // Instance property so that `mark()` doesn't need to be called as a method.\n private mark = (name: MarkName) => {\n if (this.debug) {\n mark(name, this.sessionId);\n }\n };\n\n /**\n * Reports performance marks to the server. This should only be called when\n * we have a functional websocket.\n */\n private reportMarks() {\n if (this.debug) {\n const report = getMarksReport(this.sessionId);\n this.webSocketManager.sendMessage({\n type: \"Event\",\n eventType: \"ClientConnect\",\n event: report,\n });\n }\n }\n\n private tryReportLongDisconnect() {\n if (!this.debug) {\n return;\n }\n const timeOfOldestRequest =\n this.connectionState().timeOfOldestInflightRequest;\n if (\n timeOfOldestRequest === null ||\n Date.now() - timeOfOldestRequest.getTime() <= 60 * 1000\n ) {\n return;\n }\n const endpoint = `${this.address}/api/debug_event`;\n fetch(endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"Convex-Client\": `npm-${version}`,\n },\n body: JSON.stringify({ event: \"LongWebsocketDisconnect\" }),\n })\n .then((response) => {\n if (!response.ok) {\n this.logger.warn(\n \"Analytics request failed with response:\",\n response.body,\n );\n }\n })\n .catch((error) => {\n this.logger.warn(\"Analytics response failed with error:\", error);\n });\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["modification"]
7
7
  }
@@ -129,13 +129,17 @@ const envRemoveCmd = new import_extra_typings.Command("remove").alias("rm").alia
129
129
  }
130
130
  });
131
131
  });
132
- const envListCmd = new import_extra_typings.Command("list").summary("List all variables and their values").description(
132
+ const envListCmd = new import_extra_typings.Command("list").summary("List all environment variables and their values").description(
133
133
  [
134
- "\u2022 List all variables: `npx convex env list`",
134
+ "\u2022 List all variables and their values: `npx convex env list`",
135
+ "\u2022 List only variable names (no values): `npx convex env list --names-only`",
135
136
  "\u2022 Save all variables to a file: `npx convex env list > .env.convex`",
136
137
  "\u2022 Append to a file: `npx convex env list >> .env.convex`"
137
138
  ].join("\n")
138
- ).configureHelp({ showGlobalOptions: true }).allowExcessArguments(false).action(async (_options, cmd) => {
139
+ ).option(
140
+ "--names-only",
141
+ "List only the names of environment variables, without their values"
142
+ ).configureHelp({ showGlobalOptions: true }).allowExcessArguments(false).action(async (cmdOptions, cmd) => {
139
143
  const options = cmd.optsWithGlobals();
140
144
  const { ctx, deployment } = await selectEnvDeployment(options);
141
145
  await (0, import_utils.ensureHasConvexDependency)(ctx, "env list");
@@ -144,7 +148,9 @@ const envListCmd = new import_extra_typings.Command("list").summary("List all va
144
148
  deployment,
145
149
  action: async () => {
146
150
  const backend = (0, import_env.deploymentEnvBackend)(ctx, deployment);
147
- await (0, import_env.envList)(ctx, backend);
151
+ await (0, import_env.envList)(ctx, backend, {
152
+ namesOnly: cmdOptions.namesOnly ?? false
153
+ });
148
154
  }
149
155
  });
150
156
  });
@@ -156,7 +162,8 @@ const env = new import_extra_typings.Command("env").summary("Set and view enviro
156
162
  "\u2022 Set interactively: `npx convex env set NAME`",
157
163
  "\u2022 Set multiple from file: `npx convex env set --from-file .env`",
158
164
  "\u2022 Unset a variable: `npx convex env remove NAME`",
159
- "\u2022 List all variables: `npx convex env list`",
165
+ "\u2022 List all variables and their values: `npx convex env list`",
166
+ "\u2022 List only variable names (no values): `npx convex env list --names-only`",
160
167
  "\u2022 Print a variable's value: `npx convex env get NAME`",
161
168
  "",
162
169
  "By default, this sets and views variables on your dev deployment.",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/cli/env.ts"],
4
- "sourcesContent": ["import { Command } from \"@commander-js/extra-typings\";\nimport { chalkStderr } from \"chalk\";\nimport { Context, oneoffContext } from \"../bundler/context.js\";\nimport {\n DeploymentSelectionOptions,\n DetailedDeploymentCredentials,\n loadSelectedDeploymentCredentials,\n} from \"./lib/api.js\";\nimport { actionDescription } from \"./lib/command.js\";\nimport { ensureHasConvexDependency } from \"./lib/utils/utils.js\";\nimport {\n deploymentEnvBackend,\n envGet,\n envList,\n envRemove,\n envSet,\n} from \"./lib/env.js\";\nimport { getDeploymentSelection } from \"./lib/deploymentSelection.js\";\nimport { withRunningBackend } from \"./lib/localDeployment/run.js\";\nimport { envDefault } from \"./envDefault.js\";\n\nconst envSetCmd = new Command(\"set\")\n // Pretend value is required\n .usage(\"[options] <name> <value>\")\n .argument(\"[name]\", \"The name of the environment variable to set.\")\n .argument(\n \"[value]\",\n \"The value to set the variable to. Omit to set it interactively.\",\n )\n .summary(\"Set a variable\")\n .description(\n [\n \"Set environment variables on your deployment.\",\n \"\",\n \"\u2022 `npx convex env set NAME 'value'`\",\n \"\u2022 `npx convex env set NAME # omit a value to set one interactively`\",\n \"\u2022 `npx convex env set NAME --from-file value.txt`\",\n \"\u2022 `npx convex env set --from-file .env.defaults`\",\n \"\",\n \"When setting multiple values, it will refuse all changes if any variables are already set to different values by default. Pass --force to overwrite the provided values.\",\n \"\",\n \"To keep secrets out of your shell history, omit the value to pipe it in via stdin, for instance:\",\n \"\u2022 `pbpaste | npx convex env set API_KEY` (macOS)\",\n \"\u2022 `Get-Clipboard | npx convex env set API_KEY` (Windows PowerShell)\",\n \"\",\n \"To update many variables at once, save them with `npx convex env list > .env.convex`, edit the file, then reapply the changes with `npx convex env set --force < .env.convex`.\",\n ].join(\"\\n\"),\n )\n .option(\n \"--from-file <file>\",\n \"Read environment variables from a .env file. Without --force, fails if any existing variable has a different value.\",\n )\n .option(\n \"--force\",\n \"When setting multiple variables, overwrite existing environment variable values instead of failing on mismatch.\",\n )\n .configureHelp({ showGlobalOptions: true })\n .allowExcessArguments(false)\n .action(async (name, value, cmdOptions, cmd) => {\n // Note: We use `as` here because optsWithGlobals() type inference doesn't\n // include global options from the parent command (added via addDeploymentSelectionOptions)\n const options = cmd.optsWithGlobals() as DeploymentSelectionOptions;\n const { ctx, deployment } = await selectEnvDeployment(options);\n await ensureHasConvexDependency(ctx, \"env set\");\n await withRunningBackend({\n ctx,\n deployment,\n action: async () => {\n const backend = deploymentEnvBackend(ctx, deployment);\n const didAnything = await envSet(ctx, backend, name, value, cmdOptions);\n if (didAnything === false) {\n cmd.outputHelp({ error: true });\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"error: No environment variables specified to be set.\",\n });\n }\n },\n });\n });\n\nexport async function selectEnvDeployment(\n options: DeploymentSelectionOptions,\n): Promise<{\n ctx: Context;\n deployment: {\n deploymentUrl: string;\n adminKey: string;\n deploymentNotice: string;\n deploymentFields: DetailedDeploymentCredentials[\"deploymentFields\"];\n };\n}> {\n const ctx = await oneoffContext(options);\n const deploymentSelection = await getDeploymentSelection(ctx, options);\n const {\n adminKey,\n url: deploymentUrl,\n deploymentFields,\n } = await loadSelectedDeploymentCredentials(ctx, deploymentSelection, {\n ensureLocalRunning: false,\n });\n\n const deploymentNotice =\n deploymentFields !== null\n ? ` (on ${chalkStderr.bold(deploymentFields.deploymentType)} deployment ${chalkStderr.bold(deploymentFields.deploymentName)})`\n : \"\";\n const result = {\n ctx,\n deployment: {\n deploymentUrl,\n adminKey,\n deploymentNotice,\n deploymentFields,\n },\n };\n return result;\n}\n\nconst envGetCmd = new Command(\"get\")\n .argument(\"<name>\", \"The name of the environment variable to print.\")\n .summary(\"Print a variable's value\")\n .description(\"Print a variable's value: `npx convex env get NAME`\")\n .configureHelp({ showGlobalOptions: true })\n .allowExcessArguments(false)\n .action(async (envVarName, _options, cmd) => {\n const options = cmd.optsWithGlobals();\n const { ctx, deployment } = await selectEnvDeployment(options);\n await ensureHasConvexDependency(ctx, \"env get\");\n await withRunningBackend({\n ctx,\n deployment,\n action: async () => {\n const backend = deploymentEnvBackend(ctx, deployment);\n await envGet(ctx, backend, envVarName);\n },\n });\n });\n\nconst envRemoveCmd = new Command(\"remove\")\n .alias(\"rm\")\n .alias(\"unset\")\n .argument(\"<name>\", \"The name of the environment variable to unset.\")\n .summary(\"Unset a variable\")\n .description(\n \"Unset a variable: `npx convex env remove NAME`\\n\" +\n \"If the variable doesn't exist, the command doesn't do anything and succeeds.\",\n )\n .configureHelp({ showGlobalOptions: true })\n .allowExcessArguments(false)\n .action(async (name, _options, cmd) => {\n const options = cmd.optsWithGlobals();\n const { ctx, deployment } = await selectEnvDeployment(options);\n await ensureHasConvexDependency(ctx, \"env remove\");\n await withRunningBackend({\n ctx,\n deployment,\n action: async () => {\n const backend = deploymentEnvBackend(ctx, deployment);\n await envRemove(ctx, backend, name);\n },\n });\n });\n\nconst envListCmd = new Command(\"list\")\n .summary(\"List all variables and their values\")\n .description(\n [\n \"\u2022 List all variables: `npx convex env list`\",\n \"\u2022 Save all variables to a file: `npx convex env list > .env.convex`\",\n \"\u2022 Append to a file: `npx convex env list >> .env.convex`\",\n ].join(\"\\n\"),\n )\n .configureHelp({ showGlobalOptions: true })\n .allowExcessArguments(false)\n .action(async (_options, cmd) => {\n const options = cmd.optsWithGlobals();\n const { ctx, deployment } = await selectEnvDeployment(options);\n await ensureHasConvexDependency(ctx, \"env list\");\n await withRunningBackend({\n ctx,\n deployment,\n action: async () => {\n const backend = deploymentEnvBackend(ctx, deployment);\n await envList(ctx, backend);\n },\n });\n });\n\nexport const env = new Command(\"env\")\n .summary(\"Set and view environment variables\")\n .description(\n [\n \"Set and view environment variables on your deployment\",\n \"\",\n \"\u2022 Set a variable: `npx convex env set NAME 'value'`\",\n \"\u2022 Set interactively: `npx convex env set NAME`\",\n \"\u2022 Set multiple from file: `npx convex env set --from-file .env`\",\n \"\u2022 Unset a variable: `npx convex env remove NAME`\",\n \"\u2022 List all variables: `npx convex env list`\",\n \"\u2022 Print a variable's value: `npx convex env get NAME`\",\n \"\",\n \"By default, this sets and views variables on your dev deployment.\",\n \"\",\n \"See the environment variables guide (https://docs.convex.dev/production/environment-variables) to learn more.\",\n ].join(\"\\n\"),\n )\n .addCommand(envSetCmd)\n .addCommand(envGetCmd)\n .addCommand(envRemoveCmd)\n .addCommand(envListCmd)\n .addCommand(envDefault)\n .helpCommand(false)\n .addDeploymentSelectionOptions(\n actionDescription(\"Set and view environment variables on\"),\n );\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAAwB;AACxB,mBAA4B;AAC5B,qBAAuC;AACvC,iBAIO;AACP,qBAAkC;AAClC,mBAA0C;AAC1C,iBAMO;AACP,iCAAuC;AACvC,iBAAmC;AACnC,wBAA2B;AAE3B,MAAM,YAAY,IAAI,6BAAQ,KAAK,EAEhC,MAAM,0BAA0B,EAChC,SAAS,UAAU,8CAA8C,EACjE;AAAA,EACC;AAAA,EACA;AACF,EACC,QAAQ,gBAAgB,EACxB;AAAA,EACC;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb,EACC;AAAA,EACC;AAAA,EACA;AACF,EACC;AAAA,EACC;AAAA,EACA;AACF,EACC,cAAc,EAAE,mBAAmB,KAAK,CAAC,EACzC,qBAAqB,KAAK,EAC1B,OAAO,OAAO,MAAM,OAAO,YAAY,QAAQ;AAG9C,QAAM,UAAU,IAAI,gBAAgB;AACpC,QAAM,EAAE,KAAK,WAAW,IAAI,MAAM,oBAAoB,OAAO;AAC7D,YAAM,wCAA0B,KAAK,SAAS;AAC9C,YAAM,+BAAmB;AAAA,IACvB;AAAA,IACA;AAAA,IACA,QAAQ,YAAY;AAClB,YAAM,cAAU,iCAAqB,KAAK,UAAU;AACpD,YAAM,cAAc,UAAM,mBAAO,KAAK,SAAS,MAAM,OAAO,UAAU;AACtE,UAAI,gBAAgB,OAAO;AACzB,YAAI,WAAW,EAAE,OAAO,KAAK,CAAC;AAC9B,eAAO,MAAM,IAAI,MAAM;AAAA,UACrB,UAAU;AAAA,UACV,WAAW;AAAA,UACX,gBACE;AAAA,QACJ,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,CAAC;AACH,CAAC;AAEH,eAAsB,oBACpB,SASC;AACD,QAAM,MAAM,UAAM,8BAAc,OAAO;AACvC,QAAM,sBAAsB,UAAM,mDAAuB,KAAK,OAAO;AACrE,QAAM;AAAA,IACJ;AAAA,IACA,KAAK;AAAA,IACL;AAAA,EACF,IAAI,UAAM,8CAAkC,KAAK,qBAAqB;AAAA,IACpE,oBAAoB;AAAA,EACtB,CAAC;AAED,QAAM,mBACJ,qBAAqB,OACjB,QAAQ,yBAAY,KAAK,iBAAiB,cAAc,CAAC,eAAe,yBAAY,KAAK,iBAAiB,cAAc,CAAC,MACzH;AACN,QAAM,SAAS;AAAA,IACb;AAAA,IACA,YAAY;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,MAAM,YAAY,IAAI,6BAAQ,KAAK,EAChC,SAAS,UAAU,gDAAgD,EACnE,QAAQ,0BAA0B,EAClC,YAAY,qDAAqD,EACjE,cAAc,EAAE,mBAAmB,KAAK,CAAC,EACzC,qBAAqB,KAAK,EAC1B,OAAO,OAAO,YAAY,UAAU,QAAQ;AAC3C,QAAM,UAAU,IAAI,gBAAgB;AACpC,QAAM,EAAE,KAAK,WAAW,IAAI,MAAM,oBAAoB,OAAO;AAC7D,YAAM,wCAA0B,KAAK,SAAS;AAC9C,YAAM,+BAAmB;AAAA,IACvB;AAAA,IACA;AAAA,IACA,QAAQ,YAAY;AAClB,YAAM,cAAU,iCAAqB,KAAK,UAAU;AACpD,gBAAM,mBAAO,KAAK,SAAS,UAAU;AAAA,IACvC;AAAA,EACF,CAAC;AACH,CAAC;AAEH,MAAM,eAAe,IAAI,6BAAQ,QAAQ,EACtC,MAAM,IAAI,EACV,MAAM,OAAO,EACb,SAAS,UAAU,gDAAgD,EACnE,QAAQ,kBAAkB,EAC1B;AAAA,EACC;AAEF,EACC,cAAc,EAAE,mBAAmB,KAAK,CAAC,EACzC,qBAAqB,KAAK,EAC1B,OAAO,OAAO,MAAM,UAAU,QAAQ;AACrC,QAAM,UAAU,IAAI,gBAAgB;AACpC,QAAM,EAAE,KAAK,WAAW,IAAI,MAAM,oBAAoB,OAAO;AAC7D,YAAM,wCAA0B,KAAK,YAAY;AACjD,YAAM,+BAAmB;AAAA,IACvB;AAAA,IACA;AAAA,IACA,QAAQ,YAAY;AAClB,YAAM,cAAU,iCAAqB,KAAK,UAAU;AACpD,gBAAM,sBAAU,KAAK,SAAS,IAAI;AAAA,IACpC;AAAA,EACF,CAAC;AACH,CAAC;AAEH,MAAM,aAAa,IAAI,6BAAQ,MAAM,EAClC,QAAQ,qCAAqC,EAC7C;AAAA,EACC;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb,EACC,cAAc,EAAE,mBAAmB,KAAK,CAAC,EACzC,qBAAqB,KAAK,EAC1B,OAAO,OAAO,UAAU,QAAQ;AAC/B,QAAM,UAAU,IAAI,gBAAgB;AACpC,QAAM,EAAE,KAAK,WAAW,IAAI,MAAM,oBAAoB,OAAO;AAC7D,YAAM,wCAA0B,KAAK,UAAU;AAC/C,YAAM,+BAAmB;AAAA,IACvB;AAAA,IACA;AAAA,IACA,QAAQ,YAAY;AAClB,YAAM,cAAU,iCAAqB,KAAK,UAAU;AACpD,gBAAM,oBAAQ,KAAK,OAAO;AAAA,IAC5B;AAAA,EACF,CAAC;AACH,CAAC;AAEI,MAAM,MAAM,IAAI,6BAAQ,KAAK,EACjC,QAAQ,oCAAoC,EAC5C;AAAA,EACC;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb,EACC,WAAW,SAAS,EACpB,WAAW,SAAS,EACpB,WAAW,YAAY,EACvB,WAAW,UAAU,EACrB,WAAW,4BAAU,EACrB,YAAY,KAAK,EACjB;AAAA,MACC,kCAAkB,uCAAuC;AAC3D;",
4
+ "sourcesContent": ["import { Command } from \"@commander-js/extra-typings\";\nimport { chalkStderr } from \"chalk\";\nimport { Context, oneoffContext } from \"../bundler/context.js\";\nimport {\n DeploymentSelectionOptions,\n DetailedDeploymentCredentials,\n loadSelectedDeploymentCredentials,\n} from \"./lib/api.js\";\nimport { actionDescription } from \"./lib/command.js\";\nimport { ensureHasConvexDependency } from \"./lib/utils/utils.js\";\nimport {\n deploymentEnvBackend,\n envGet,\n envList,\n envRemove,\n envSet,\n} from \"./lib/env.js\";\nimport { getDeploymentSelection } from \"./lib/deploymentSelection.js\";\nimport { withRunningBackend } from \"./lib/localDeployment/run.js\";\nimport { envDefault } from \"./envDefault.js\";\n\nconst envSetCmd = new Command(\"set\")\n // Pretend value is required\n .usage(\"[options] <name> <value>\")\n .argument(\"[name]\", \"The name of the environment variable to set.\")\n .argument(\n \"[value]\",\n \"The value to set the variable to. Omit to set it interactively.\",\n )\n .summary(\"Set a variable\")\n .description(\n [\n \"Set environment variables on your deployment.\",\n \"\",\n \"\u2022 `npx convex env set NAME 'value'`\",\n \"\u2022 `npx convex env set NAME # omit a value to set one interactively`\",\n \"\u2022 `npx convex env set NAME --from-file value.txt`\",\n \"\u2022 `npx convex env set --from-file .env.defaults`\",\n \"\",\n \"When setting multiple values, it will refuse all changes if any variables are already set to different values by default. Pass --force to overwrite the provided values.\",\n \"\",\n \"To keep secrets out of your shell history, omit the value to pipe it in via stdin, for instance:\",\n \"\u2022 `pbpaste | npx convex env set API_KEY` (macOS)\",\n \"\u2022 `Get-Clipboard | npx convex env set API_KEY` (Windows PowerShell)\",\n \"\",\n \"To update many variables at once, save them with `npx convex env list > .env.convex`, edit the file, then reapply the changes with `npx convex env set --force < .env.convex`.\",\n ].join(\"\\n\"),\n )\n .option(\n \"--from-file <file>\",\n \"Read environment variables from a .env file. Without --force, fails if any existing variable has a different value.\",\n )\n .option(\n \"--force\",\n \"When setting multiple variables, overwrite existing environment variable values instead of failing on mismatch.\",\n )\n .configureHelp({ showGlobalOptions: true })\n .allowExcessArguments(false)\n .action(async (name, value, cmdOptions, cmd) => {\n // Note: We use `as` here because optsWithGlobals() type inference doesn't\n // include global options from the parent command (added via addDeploymentSelectionOptions)\n const options = cmd.optsWithGlobals() as DeploymentSelectionOptions;\n const { ctx, deployment } = await selectEnvDeployment(options);\n await ensureHasConvexDependency(ctx, \"env set\");\n await withRunningBackend({\n ctx,\n deployment,\n action: async () => {\n const backend = deploymentEnvBackend(ctx, deployment);\n const didAnything = await envSet(ctx, backend, name, value, cmdOptions);\n if (didAnything === false) {\n cmd.outputHelp({ error: true });\n return await ctx.crash({\n exitCode: 1,\n errorType: \"fatal\",\n printedMessage:\n \"error: No environment variables specified to be set.\",\n });\n }\n },\n });\n });\n\nexport async function selectEnvDeployment(\n options: DeploymentSelectionOptions,\n): Promise<{\n ctx: Context;\n deployment: {\n deploymentUrl: string;\n adminKey: string;\n deploymentNotice: string;\n deploymentFields: DetailedDeploymentCredentials[\"deploymentFields\"];\n };\n}> {\n const ctx = await oneoffContext(options);\n const deploymentSelection = await getDeploymentSelection(ctx, options);\n const {\n adminKey,\n url: deploymentUrl,\n deploymentFields,\n } = await loadSelectedDeploymentCredentials(ctx, deploymentSelection, {\n ensureLocalRunning: false,\n });\n\n const deploymentNotice =\n deploymentFields !== null\n ? ` (on ${chalkStderr.bold(deploymentFields.deploymentType)} deployment ${chalkStderr.bold(deploymentFields.deploymentName)})`\n : \"\";\n const result = {\n ctx,\n deployment: {\n deploymentUrl,\n adminKey,\n deploymentNotice,\n deploymentFields,\n },\n };\n return result;\n}\n\nconst envGetCmd = new Command(\"get\")\n .argument(\"<name>\", \"The name of the environment variable to print.\")\n .summary(\"Print a variable's value\")\n .description(\"Print a variable's value: `npx convex env get NAME`\")\n .configureHelp({ showGlobalOptions: true })\n .allowExcessArguments(false)\n .action(async (envVarName, _options, cmd) => {\n const options = cmd.optsWithGlobals();\n const { ctx, deployment } = await selectEnvDeployment(options);\n await ensureHasConvexDependency(ctx, \"env get\");\n await withRunningBackend({\n ctx,\n deployment,\n action: async () => {\n const backend = deploymentEnvBackend(ctx, deployment);\n await envGet(ctx, backend, envVarName);\n },\n });\n });\n\nconst envRemoveCmd = new Command(\"remove\")\n .alias(\"rm\")\n .alias(\"unset\")\n .argument(\"<name>\", \"The name of the environment variable to unset.\")\n .summary(\"Unset a variable\")\n .description(\n \"Unset a variable: `npx convex env remove NAME`\\n\" +\n \"If the variable doesn't exist, the command doesn't do anything and succeeds.\",\n )\n .configureHelp({ showGlobalOptions: true })\n .allowExcessArguments(false)\n .action(async (name, _options, cmd) => {\n const options = cmd.optsWithGlobals();\n const { ctx, deployment } = await selectEnvDeployment(options);\n await ensureHasConvexDependency(ctx, \"env remove\");\n await withRunningBackend({\n ctx,\n deployment,\n action: async () => {\n const backend = deploymentEnvBackend(ctx, deployment);\n await envRemove(ctx, backend, name);\n },\n });\n });\n\nconst envListCmd = new Command(\"list\")\n .summary(\"List all environment variables and their values\")\n .description(\n [\n \"\u2022 List all variables and their values: `npx convex env list`\",\n \"\u2022 List only variable names (no values): `npx convex env list --names-only`\",\n \"\u2022 Save all variables to a file: `npx convex env list > .env.convex`\",\n \"\u2022 Append to a file: `npx convex env list >> .env.convex`\",\n ].join(\"\\n\"),\n )\n .option(\n \"--names-only\",\n \"List only the names of environment variables, without their values\",\n )\n .configureHelp({ showGlobalOptions: true })\n .allowExcessArguments(false)\n .action(async (cmdOptions, cmd) => {\n // Note: We use `as` here because optsWithGlobals() type inference doesn't\n // include global options from the parent command (added via addDeploymentSelectionOptions)\n const options = cmd.optsWithGlobals() as DeploymentSelectionOptions;\n const { ctx, deployment } = await selectEnvDeployment(options);\n await ensureHasConvexDependency(ctx, \"env list\");\n await withRunningBackend({\n ctx,\n deployment,\n action: async () => {\n const backend = deploymentEnvBackend(ctx, deployment);\n await envList(ctx, backend, {\n namesOnly: cmdOptions.namesOnly ?? false,\n });\n },\n });\n });\n\nexport const env = new Command(\"env\")\n .summary(\"Set and view environment variables\")\n .description(\n [\n \"Set and view environment variables on your deployment\",\n \"\",\n \"\u2022 Set a variable: `npx convex env set NAME 'value'`\",\n \"\u2022 Set interactively: `npx convex env set NAME`\",\n \"\u2022 Set multiple from file: `npx convex env set --from-file .env`\",\n \"\u2022 Unset a variable: `npx convex env remove NAME`\",\n \"\u2022 List all variables and their values: `npx convex env list`\",\n \"\u2022 List only variable names (no values): `npx convex env list --names-only`\",\n \"\u2022 Print a variable's value: `npx convex env get NAME`\",\n \"\",\n \"By default, this sets and views variables on your dev deployment.\",\n \"\",\n \"See the environment variables guide (https://docs.convex.dev/production/environment-variables) to learn more.\",\n ].join(\"\\n\"),\n )\n .addCommand(envSetCmd)\n .addCommand(envGetCmd)\n .addCommand(envRemoveCmd)\n .addCommand(envListCmd)\n .addCommand(envDefault)\n .helpCommand(false)\n .addDeploymentSelectionOptions(\n actionDescription(\"Set and view environment variables on\"),\n );\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -116,20 +116,24 @@ const envDefaultRemove = addEnvDefaultOptions(
116
116
  await (0, import_env.envRemove)(ctx, backend, name);
117
117
  });
118
118
  const envDefaultList = addEnvDefaultOptions(
119
- new import_extra_typings.Command("list").summary("List all default variables").description(
119
+ new import_extra_typings.Command("list").summary("List all default environment variables and their values").description(
120
120
  [
121
- "List all default variables.",
122
- "",
123
- "\u2022 `npx convex env default list`",
121
+ "\u2022 List all default variables and their values: `npx convex env default list`",
122
+ "\u2022 List only default variable names (no values): `npx convex env default list --names-only`",
124
123
  "",
125
124
  "The deployment type is determined by the current deployment (local maps to dev), or by --type if provided."
126
125
  ].join("\n")
126
+ ).option(
127
+ "--names-only",
128
+ "List only the names of environment variables, without their values"
127
129
  ).configureHelp({ showGlobalOptions: true }).allowExcessArguments(false)
128
- ).action(async (_options, cmd) => {
130
+ ).action(async (cmdOptions, cmd) => {
129
131
  const options = cmd.optsWithGlobals();
130
132
  const { ctx, backend } = await resolveEnvDefaultBackend(options);
131
133
  await (0, import_utils.ensureHasConvexDependency)(ctx, "env default list");
132
- await (0, import_env.envList)(ctx, backend);
134
+ await (0, import_env.envList)(ctx, backend, {
135
+ namesOnly: cmdOptions.namesOnly ?? false
136
+ });
133
137
  });
134
138
  const envDefault = new import_extra_typings.Command("default").summary("Manage project-level default environment variables").description(
135
139
  [
@@ -139,7 +143,8 @@ const envDefault = new import_extra_typings.Command("default").summary("Manage p
139
143
  "",
140
144
  "\u2022 Set a default variable: `npx convex env default set NAME 'value'`",
141
145
  "\u2022 Unset a default variable: `npx convex env default remove NAME`",
142
- "\u2022 List all default variables: `npx convex env default list`",
146
+ "\u2022 List all default variables and their values: `npx convex env default list`",
147
+ "\u2022 List only default variable names (no values): `npx convex env default list --names-only`",
143
148
  "\u2022 Print a default variable's value: `npx convex env default get NAME`"
144
149
  ].join("\n")
145
150
  ).addCommand(envDefaultSet).addCommand(envDefaultGet).addCommand(envDefaultRemove).addCommand(envDefaultList).helpCommand(false);