{"version":3,"file":"TokenSubscription.cjs","names":["StreamFanout","#apiBaseUrl","#signingKey","#signingKeyFallback","#validate","#getSubscriptionToken","#channelId","#topics","url: URL","#debug","createDeferredPromise","#running","#ws","parsedJson: unknown","Realtime","#fanout","streamId: unknown","#chunkStreams","endStreamId: unknown","#encoder"],"sources":["../../../../src/components/realtime/subscribe/TokenSubscription.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport debug from \"debug\";\nimport { createDeferredPromise } from \"../../../helpers/promises.ts\";\nimport { Realtime } from \"../types.ts\";\nimport { StreamFanout } from \"./StreamFanout.ts\";\n\nconst extractSchema = (topicEntry: unknown): StandardSchemaV1 | undefined => {\n  if (!topicEntry || typeof topicEntry !== \"object\") {\n    return undefined;\n  }\n\n  if (\"schema\" in topicEntry && topicEntry.schema) {\n    return topicEntry.schema as StandardSchemaV1;\n  }\n\n  return undefined;\n};\n\nexport interface TokenSubscriptionOptions {\n  token: Realtime.Subscribe.Token;\n  apiBaseUrl?: string;\n  signingKey?: string;\n  signingKeyFallback?: string;\n  validate?: boolean;\n\n  //\n  // When provided, used for lazy token retrieval instead of env-based lookup\n  getSubscriptionToken?: (channel: string, topics: string[]) => Promise<string>;\n}\n\nexport class TokenSubscription {\n  #apiBaseUrl?: string;\n  #channelId: string;\n  #debug = debug(\"inngest:realtime\");\n  #encoder = new TextEncoder();\n  #fanout = new StreamFanout<Realtime.Message>();\n  #running = false;\n  #topics: Map<string, unknown>;\n  #ws: WebSocket | null = null;\n  #signingKey: string | undefined;\n  #signingKeyFallback: string | undefined;\n  #validate: boolean;\n  #getSubscriptionToken?: (\n    channel: string,\n    topics: string[],\n  ) => Promise<string>;\n\n  #chunkStreams = new Map<\n    string,\n    { stream: ReadableStream; controller: ReadableStreamDefaultController }\n  >();\n\n  public token: Realtime.Subscribe.Token;\n\n  constructor(options: TokenSubscriptionOptions) {\n    this.token = options.token;\n    this.#apiBaseUrl = options.apiBaseUrl;\n    this.#signingKey = options.signingKey;\n    this.#signingKeyFallback = options.signingKeyFallback;\n    this.#validate = options.validate ?? true;\n    this.#getSubscriptionToken = options.getSubscriptionToken;\n\n    const channel = this.token.channel;\n\n    if (typeof channel === \"string\") {\n      this.#channelId = channel;\n\n      //\n      // String channel — no topic definitions available, store empty entries.\n      // Schema validation will be skipped for these topics.\n      this.#topics = new Map(\n        this.token.topics.map((name) => [name, undefined]),\n      );\n    } else {\n      this.#channelId = channel.name;\n\n      //\n      // Channel object — store the topic config for optional schema validation\n      // on received messages.\n      this.#topics = new Map(\n        this.token.topics.map((name) => [\n          name,\n          (\n            channel.topics as Record<string, Realtime.TopicConfig | undefined>\n          )?.[name],\n        ]),\n      );\n    }\n  }\n\n  private getWsUrl(token: string): URL {\n    const path = \"/v1/realtime/connect\";\n\n    let url: URL;\n\n    if (this.#apiBaseUrl) {\n      url = new URL(path, this.#apiBaseUrl);\n    } else {\n      url = new URL(path, \"https://api.inngest.com/\");\n    }\n\n    url.protocol = url.protocol === \"http:\" ? \"ws:\" : \"wss:\";\n    url.searchParams.set(\"token\", token);\n\n    return url;\n  }\n\n  private isExpectedChannel(channel: string): boolean {\n    if (channel === this.#channelId) {\n      return true;\n    }\n\n    this.#debug(\n      `Received message for unexpected channel \"${channel}\" (expected \"${this.#channelId}\")`,\n    );\n    return false;\n  }\n\n  public async connect() {\n    this.#debug(\n      `Establishing connection to channel \"${\n        this.#channelId\n      }\" with topics ${JSON.stringify([...this.#topics.keys()])}...`,\n    );\n\n    if (typeof WebSocket === \"undefined\") {\n      throw new Error(\"WebSockets not supported in current environment\");\n    }\n\n    let key = this.token.key;\n    if (!key) {\n      this.#debug(\n        \"No subscription token key passed; attempting to retrieve one automatically...\",\n      );\n\n      key = await this.lazilyGetSubscriptionToken();\n\n      if (!key) {\n        throw new Error(\n          \"No subscription token key passed and failed to retrieve one automatically\",\n        );\n      }\n    }\n\n    const ret = createDeferredPromise<void>();\n    let isConnectSettled = false;\n    let hasOpened = false;\n\n    const resolveConnect = () => {\n      if (isConnectSettled) {\n        return;\n      }\n      isConnectSettled = true;\n      ret.resolve();\n    };\n\n    const rejectConnect = (err: unknown) => {\n      if (isConnectSettled) {\n        return;\n      }\n      isConnectSettled = true;\n      ret.reject(err);\n    };\n\n    try {\n      this.#running = true;\n      this.#ws = new WebSocket(this.getWsUrl(key));\n\n      this.#ws.onopen = () => {\n        this.#debug(\"WebSocket connection established\");\n        hasOpened = true;\n        resolveConnect();\n      };\n\n      this.#ws.onmessage = async (event) => {\n        let parsedJson: unknown;\n        try {\n          parsedJson = JSON.parse(event.data as string);\n        } catch (err) {\n          this.#debug(\"Received non-JSON message:\", err);\n          return;\n        }\n\n        const parseRes =\n          await Realtime.messageSchema.safeParseAsync(parsedJson);\n\n        if (!parseRes.success) {\n          this.#debug(\"Received invalid message:\", parseRes.error);\n          return;\n        }\n\n        const msg = parseRes.data;\n\n        if (!this.#running) {\n          this.#debug(\n            `Received message on channel \"${msg.channel}\" for topic \"${msg.topic}\" but stream is closed`,\n          );\n          return;\n        }\n\n        switch (msg.kind) {\n          case \"data\": {\n            if (!msg.channel) {\n              this.#debug(\n                `Received message on channel \"${msg.channel}\" with no channel`,\n              );\n              return;\n            }\n\n            if (!this.isExpectedChannel(msg.channel)) {\n              return;\n            }\n\n            if (!msg.topic) {\n              this.#debug(\n                `Received message on channel \"${msg.channel}\" with no topic`,\n              );\n              return;\n            }\n\n            if (!this.#topics.has(msg.topic)) {\n              this.#debug(\n                `Received message on channel \"${msg.channel}\" for unknown topic \"${msg.topic}\"`,\n              );\n              return;\n            }\n\n            const dataTopic = this.#topics.get(msg.topic);\n            const schema = extractSchema(dataTopic);\n            if (this.#validate && schema) {\n              const validateRes = await schema[\"~standard\"].validate(msg.data);\n              if (validateRes.issues) {\n                console.error(\n                  `Received message on channel \"${msg.channel}\" for topic \"${msg.topic}\" that failed schema validation:`,\n                  validateRes.issues,\n                );\n                return;\n              }\n\n              msg.data = validateRes.value;\n            }\n\n            this.#debug(\n              `Received message on channel \"${msg.channel}\" for topic \"${msg.topic}\":`,\n              msg.data,\n            );\n            return this.#fanout.write({\n              channel: msg.channel,\n              topic: msg.topic,\n              data: msg.data,\n              fnId: msg.fn_id,\n              createdAt: msg.created_at || new Date(),\n              runId: msg.run_id,\n              kind: \"data\",\n              envId: msg.env_id,\n            });\n          }\n\n          case \"run\": {\n            if (msg.channel && !this.isExpectedChannel(msg.channel)) {\n              return;\n            }\n\n            this.#debug(`Received run lifecycle message on \"${msg.channel}\"`);\n            return this.#fanout.write({\n              channel: msg.channel,\n              topic: msg.topic,\n              data: msg.data,\n              fnId: msg.fn_id,\n              createdAt: msg.created_at || new Date(),\n              runId: msg.run_id,\n              kind: \"run\",\n              envId: msg.env_id,\n            });\n          }\n\n          case \"datastream-start\": {\n            if (!msg.channel || !msg.topic) {\n              this.#debug(\n                `Received message on channel \"${msg.channel}\" with no channel or topic`,\n              );\n              return;\n            }\n\n            if (!this.isExpectedChannel(msg.channel)) {\n              return;\n            }\n\n            const streamId: unknown = msg.data;\n            if (typeof streamId !== \"string\" || !streamId) {\n              this.#debug(\n                `Received message on channel \"${msg.channel}\" with no stream ID`,\n              );\n              return;\n            }\n\n            if (this.#chunkStreams.has(streamId)) {\n              this.#debug(\n                `Received message on channel \"${msg.channel}\" to create stream ID \"${streamId}\" that already exists`,\n              );\n              return;\n            }\n\n            const stream = new ReadableStream({\n              start: (controller) => {\n                this.#chunkStreams.set(streamId, { stream, controller });\n              },\n              cancel: () => {\n                this.#chunkStreams.delete(streamId);\n              },\n            });\n\n            this.#debug(\n              `Created stream ID \"${streamId}\" on channel \"${msg.channel}\"`,\n            );\n            return this.#fanout.write({\n              channel: msg.channel,\n              topic: msg.topic,\n              kind: \"datastream-start\",\n              data: streamId,\n              streamId,\n              fnId: msg.fn_id,\n              runId: msg.run_id,\n              stream,\n            });\n          }\n\n          case \"datastream-end\": {\n            if (!msg.channel || !msg.topic) {\n              this.#debug(\n                `Received message on channel \"${msg.channel}\" with no channel or topic`,\n              );\n              return;\n            }\n\n            if (!this.isExpectedChannel(msg.channel)) {\n              return;\n            }\n\n            const endStreamId: unknown = msg.data;\n            if (typeof endStreamId !== \"string\" || !endStreamId) {\n              this.#debug(\n                `Received message on channel \"${msg.channel}\" with no stream ID`,\n              );\n              return;\n            }\n\n            const endStream = this.#chunkStreams.get(endStreamId);\n            if (!endStream) {\n              this.#debug(\n                `Received message on channel \"${msg.channel}\" to close stream ID \"${endStreamId}\" that doesn't exist`,\n              );\n              return;\n            }\n\n            endStream.controller.close();\n            this.#chunkStreams.delete(endStreamId);\n\n            this.#debug(\n              `Closed stream ID \"${endStreamId}\" on channel \"${msg.channel}\"`,\n            );\n            return this.#fanout.write({\n              channel: msg.channel,\n              topic: msg.topic,\n              kind: \"datastream-end\",\n              data: endStreamId,\n              streamId: endStreamId,\n              fnId: msg.fn_id,\n              runId: msg.run_id,\n              stream: endStream.stream,\n            });\n          }\n\n          case \"chunk\": {\n            if (!msg.channel || !msg.topic) {\n              this.#debug(\n                `Received message on channel \"${msg.channel}\" with no channel or topic`,\n              );\n              return;\n            }\n\n            if (!this.isExpectedChannel(msg.channel)) {\n              return;\n            }\n\n            if (!msg.stream_id) {\n              this.#debug(\n                `Received message on channel \"${msg.channel}\" with no stream ID`,\n              );\n              return;\n            }\n\n            const chunkStream = this.#chunkStreams.get(msg.stream_id);\n            if (!chunkStream) {\n              this.#debug(\n                `Received message on channel \"${msg.channel}\" for unknown stream ID \"${msg.stream_id}\"`,\n              );\n              return;\n            }\n\n            this.#debug(\n              `Received chunk on channel \"${msg.channel}\" for stream ID \"${msg.stream_id}\":`,\n              msg.data,\n            );\n\n            chunkStream.controller.enqueue(msg.data);\n\n            return this.#fanout.write({\n              channel: msg.channel,\n              topic: msg.topic,\n              kind: \"chunk\",\n              data: msg.data,\n              streamId: msg.stream_id,\n              fnId: msg.fn_id,\n              runId: msg.run_id,\n              stream: chunkStream.stream,\n            });\n          }\n\n          default: {\n            this.#debug(\n              `Received message on channel \"${msg.channel}\" with unhandled kind \"${msg.kind}\"`,\n            );\n            return;\n          }\n        }\n      };\n\n      this.#ws.onerror = (event) => {\n        console.error(\"WebSocket error observed:\", event);\n        rejectConnect(event);\n      };\n\n      this.#ws.onclose = (event) => {\n        this.#debug(\"WebSocket closed:\", event.reason);\n        if (!hasOpened) {\n          rejectConnect(\n            new Error(\n              `WebSocket closed before opening${\n                event.reason ? `: ${event.reason}` : \"\"\n              }`,\n            ),\n          );\n        }\n        this.close();\n      };\n    } catch (err) {\n      this.#running = false;\n      ret.reject(err);\n    }\n\n    return ret.promise;\n  }\n\n  private async lazilyGetSubscriptionToken(): Promise<string> {\n    const channelId = this.#channelId;\n\n    if (!channelId) {\n      throw new Error(\"Channel ID is required to create a subscription token\");\n    }\n\n    if (this.#getSubscriptionToken) {\n      return this.#getSubscriptionToken(\n        channelId,\n        this.token.topics as string[],\n      );\n    }\n\n    //\n    // Fallback: try fetching directly using env-based signing keys.\n    // This path is used when no Inngest client is available.\n    throw new Error(\n      \"No getSubscriptionToken handler provided. Pass an Inngest client or provide a token key.\",\n    );\n  }\n\n  public close(reason = \"Userland closed connection\") {\n    if (!this.#running) {\n      return;\n    }\n\n    this.#debug(\"close() called; closing connection...\");\n    this.#running = false;\n    this.#ws?.close(1000, reason);\n    this.#ws = null;\n\n    for (const { controller } of this.#chunkStreams.values()) {\n      try {\n        controller.close();\n      } catch {\n        // no-op\n      }\n    }\n    this.#chunkStreams.clear();\n\n    this.#debug(`Closing ${this.#fanout.size()} streams...`);\n    this.#fanout.close();\n  }\n\n  public getJsonStream() {\n    return this.#fanout.createStream();\n  }\n\n  public getEncodedStream() {\n    return this.#fanout.createStream((chunk) => {\n      return this.#encoder.encode(`data: ${JSON.stringify(chunk)}\\n\\n`);\n    });\n  }\n\n  public useCallback(\n    callback: Realtime.Subscribe.Callback,\n    stream: ReadableStream<Realtime.Message> = this.getJsonStream(),\n    onError?: (err: unknown) => void,\n  ) {\n    void (async () => {\n      const reader = stream.getReader();\n      try {\n        while (this.#running) {\n          const { done, value } = await reader.read();\n          if (done || !this.#running) break;\n          try {\n            await callback(value);\n          } catch (err) {\n            if (onError) {\n              onError(err);\n            } else {\n              console.error(\"Realtime subscription callback failed:\", err);\n            }\n          }\n        }\n      } finally {\n        reader.releaseLock();\n      }\n    })();\n  }\n}\n"],"mappings":"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"}