{"version":3,"file":"knock.mjs","sources":["../../src/knock.ts"],"sourcesContent":["import { jwtDecode } from \"jwt-decode\";\n\nimport ApiClient from \"./api\";\nimport FeedClient from \"./clients/feed\";\nimport MessageClient from \"./clients/messages\";\nimport MsTeamsClient from \"./clients/ms-teams\";\nimport ObjectClient from \"./clients/objects\";\nimport Preferences from \"./clients/preferences\";\nimport SlackClient from \"./clients/slack\";\nimport UserClient from \"./clients/users\";\nimport {\n  AuthenticateOptions,\n  KnockOptions,\n  LogLevel,\n  UserId,\n  UserIdOrUserWithProperties,\n  UserTokenExpiringCallback,\n} from \"./interfaces\";\n\nconst DEFAULT_HOST = \"https://api.knock.app\";\n\nclass Knock {\n  public host: string;\n  private apiClient: ApiClient | null = null;\n  public userId: string | undefined | null;\n  public userToken?: string;\n  public logLevel?: LogLevel;\n  public readonly branch?: string;\n  private readonly disconnectOnPageHidden?: boolean;\n  private tokenExpirationTimer: ReturnType<typeof setTimeout> | null = null;\n  readonly feeds = new FeedClient(this);\n  readonly objects = new ObjectClient(this);\n  readonly preferences = new Preferences(this);\n  readonly slack = new SlackClient(this);\n  readonly msTeams = new MsTeamsClient(this);\n  readonly user = new UserClient(this);\n  readonly messages = new MessageClient(this);\n\n  constructor(\n    readonly apiKey: string,\n    options: KnockOptions = {},\n  ) {\n    this.host = options.host || DEFAULT_HOST;\n    this.logLevel = options.logLevel;\n    this.branch = options.branch || undefined;\n    this.disconnectOnPageHidden = options.disconnectOnPageHidden;\n\n    this.log(\"Initialized Knock instance\");\n\n    // Fail loudly if we're using the wrong API key\n    if (this.apiKey && this.apiKey.startsWith(\"sk_\")) {\n      throw new Error(\n        \"[Knock] You are using your secret API key on the client. Please use the public key.\",\n      );\n    }\n  }\n\n  client() {\n    // Initiate a new API client if we don't have one yet\n    if (!this.apiClient) {\n      this.apiClient = this.createApiClient();\n    }\n\n    return this.apiClient;\n  }\n\n  /**\n   * @deprecated Passing `userId` as a `string` is deprecated and will be removed in a future version.\n   * Please pass a `user` object instead containing an `id` value.\n   * example:\n   * ```ts\n   * knock.authenticate({ id: \"user_123\" });\n   * ```\n   */\n  authenticate(\n    userIdOrUserWithProperties: UserId,\n    userToken?: Knock[\"userToken\"],\n    options?: AuthenticateOptions,\n  ): never;\n  authenticate(\n    userIdOrUserWithProperties: UserIdOrUserWithProperties,\n    userToken?: Knock[\"userToken\"],\n    options?: AuthenticateOptions,\n  ): void;\n  authenticate(\n    userIdOrUserWithProperties: UserIdOrUserWithProperties,\n    userToken?: Knock[\"userToken\"],\n    options?: AuthenticateOptions,\n  ) {\n    let reinitializeApi = false;\n    const currentApiClient = this.apiClient;\n    const userId = this.getUserId(userIdOrUserWithProperties);\n    const identificationStrategy = options?.identificationStrategy || \"inline\";\n\n    // If we've previously been initialized and the values have now changed, then we\n    // need to reinitialize any stateful connections we have\n    if (\n      currentApiClient &&\n      (this.userId !== userId || this.userToken !== userToken)\n    ) {\n      this.log(\"userId or userToken changed; reinitializing connections\");\n      this.feeds.teardownInstances();\n      this.teardown();\n      reinitializeApi = true;\n    }\n\n    this.userId = userId;\n    this.userToken = userToken;\n\n    this.log(`Authenticated with userId ${userId}`);\n\n    if (this.userToken && options?.onUserTokenExpiring instanceof Function) {\n      this.maybeScheduleUserTokenExpiration(\n        options.onUserTokenExpiring,\n        options.timeBeforeExpirationInMs,\n      );\n    }\n\n    // If we get the signal to reinitialize the api client, then we want to create a new client\n    // and the reinitialize any existing feed real-time connections we have so everything continues\n    // to work with the new credentials we've been given\n    if (reinitializeApi) {\n      this.apiClient = this.createApiClient();\n      this.feeds.reinitializeInstances();\n      this.log(\"Reinitialized real-time connections\");\n    }\n\n    // We explicitly skip the inline identification if the strategy is set to \"skip\"\n    if (identificationStrategy === \"skip\") {\n      this.log(\"Skipping inline user identification\");\n      return;\n    }\n\n    // Inline identify the user if we've been given an object with an id\n    // and the strategy is set to \"inline\".\n    if (\n      identificationStrategy === \"inline\" &&\n      typeof userIdOrUserWithProperties === \"object\" &&\n      userIdOrUserWithProperties?.id\n    ) {\n      this.log(`Identifying user ${userIdOrUserWithProperties.id} inline`);\n      const { id, ...properties } = userIdOrUserWithProperties;\n      this.user.identify(properties).catch((err) => {\n        const errorMessage =\n          err instanceof Error ? err.message : \"Unknown error\";\n\n        this.log(\n          `Error identifying user ${userIdOrUserWithProperties.id} inline:\\n${errorMessage}`,\n        );\n      });\n    }\n\n    return;\n  }\n\n  failIfNotAuthenticated() {\n    if (!this.isAuthenticated()) {\n      throw new Error(\"Not authenticated. Please call `authenticate` first.\");\n    }\n  }\n\n  /*\n    Returns whether or this Knock instance is authenticated. Passing `true` will check the presence\n    of the userToken as well.\n  */\n  isAuthenticated(checkUserToken = false) {\n    return checkUserToken ? !!(this.userId && this.userToken) : !!this.userId;\n  }\n\n  // Used to teardown any connected instances\n  teardown() {\n    if (this.tokenExpirationTimer) {\n      clearTimeout(this.tokenExpirationTimer);\n    }\n    this.apiClient?.teardown();\n  }\n\n  log(message: string, force = false) {\n    if (this.logLevel === \"debug\" || force) {\n      console.log(`[Knock] ${message}`);\n    }\n  }\n\n  /**\n   * Initiates an API client\n   */\n  private createApiClient() {\n    return new ApiClient({\n      apiKey: this.apiKey,\n      host: this.host,\n      userToken: this.userToken,\n      branch: this.branch,\n      disconnectOnPageHidden: this.disconnectOnPageHidden,\n    });\n  }\n\n  private async maybeScheduleUserTokenExpiration(\n    callbackFn: UserTokenExpiringCallback,\n    timeBeforeExpirationInMs: number = 30_000,\n  ) {\n    if (!this.userToken) return;\n\n    const decoded = jwtDecode(this.userToken);\n    const expiresAtMs = (decoded.exp ?? 0) * 1000;\n    const nowMs = Date.now();\n\n    // Expiration is in the future\n    if (expiresAtMs && expiresAtMs > nowMs) {\n      // Check how long until the token should be regenerated\n      // | ----------------- | ----------------------- |\n      // ^ now               ^ expiration offset       ^ expires at\n      const msInFuture = expiresAtMs - timeBeforeExpirationInMs - nowMs;\n\n      this.tokenExpirationTimer = setTimeout(async () => {\n        const newToken = await callbackFn(this.userToken as string, decoded);\n\n        // Reauthenticate which will handle reinitializing sockets\n        if (typeof newToken === \"string\") {\n          this.authenticate(this.userId!, newToken, {\n            onUserTokenExpiring: callbackFn,\n            timeBeforeExpirationInMs: timeBeforeExpirationInMs,\n          });\n        }\n      }, msInFuture);\n    }\n  }\n\n  /**\n   * Returns the user id from the given userIdOrUserWithProperties\n   * @param userIdOrUserWithProperties - The user id or user object\n   * @returns The user id\n   * @throws {Error} If the user object does not contain an `id` property\n   */\n  private getUserId(userIdOrUserWithProperties: UserIdOrUserWithProperties) {\n    if (\n      typeof userIdOrUserWithProperties === \"string\" ||\n      !userIdOrUserWithProperties\n    ) {\n      return userIdOrUserWithProperties;\n    }\n\n    if (userIdOrUserWithProperties?.id) {\n      return userIdOrUserWithProperties.id;\n    }\n\n    return undefined;\n  }\n}\n\nexport default Knock;\n"],"names":["DEFAULT_HOST","Knock","apiKey","options","__publicField","FeedClient","ObjectClient","Preferences","SlackClient","MsTeamsClient","UserClient","MessageClient","userIdOrUserWithProperties","userToken","reinitializeApi","currentApiClient","userId","identificationStrategy","id","properties","err","errorMessage","checkUserToken","_a","message","force","ApiClient","callbackFn","timeBeforeExpirationInMs","decoded","jwtDecode","expiresAtMs","nowMs","msInFuture","newToken"],"mappings":"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"}