{"version":3,"sources":["../../src/stream-manager/rtdb.ts"],"sourcesContent":["/**\n * Copyright 2025 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { App } from 'firebase-admin/app';\nimport { Database, getDatabase } from 'firebase-admin/database';\nimport {\n  GenkitError,\n  StreamNotFoundError,\n  type ActionStreamInput,\n  type ActionStreamSubscriber,\n  type StreamManager,\n} from 'genkit/beta';\n\nexport interface RtdbStreamManagerOptions {\n  firebaseApp?: App;\n  db?: Database;\n  refPrefix?: string;\n  timeout?: number;\n}\n\nclass RtdbActionStream<S, O> implements ActionStreamInput<S, O> {\n  private db: Database;\n  private streamRef: string;\n  private metadataRef: string;\n\n  constructor(db: Database, streamRef: string) {\n    this.db = db;\n    this.streamRef = `${streamRef}/stream`;\n    this.metadataRef = `${streamRef}/metadata`;\n  }\n\n  private async update(): Promise<void> {\n    await this.db.ref(this.metadataRef).update({ updatedAt: Date.now() });\n  }\n\n  async write(chunk: S): Promise<void> {\n    await this.db.ref(this.streamRef).push({ type: 'chunk', chunk });\n    await this.update();\n  }\n\n  async done(output: O): Promise<void> {\n    await this.db.ref(this.streamRef).push({ type: 'done', output });\n    await this.update();\n  }\n\n  async error(err: any): Promise<void> {\n    const serializableError = {\n      message: err.message,\n      stack: err.stack,\n      ...err,\n    };\n    await this.db\n      .ref(this.streamRef)\n      .push({ type: 'error', err: serializableError });\n    await this.update();\n  }\n}\n\nexport class RtdbStreamManager implements StreamManager {\n  private db: Database;\n  private refRoot: string;\n  private timeout: number;\n\n  constructor(opts: RtdbStreamManagerOptions) {\n    this.refRoot = opts.refPrefix ?? 'genkit-streams';\n    // refRoot should have a trailing slash for simplicity.\n    if (this.refRoot && !this.refRoot.endsWith('/')) {\n      this.refRoot += '/';\n    }\n    this.db =\n      opts.db ??\n      (opts.firebaseApp ? getDatabase(opts.firebaseApp) : getDatabase());\n    this.timeout = opts.timeout ?? 60000;\n  }\n\n  async open<S, O>(streamId: string): Promise<ActionStreamInput<S, O>> {\n    const streamRef = this.refRoot + streamId;\n    await this.db.ref(streamRef).remove();\n    await this.db.ref(`${streamRef}/metadata`).set({ createdAt: Date.now() });\n    return new RtdbActionStream(this.db, streamRef);\n  }\n\n  async subscribe<S, O>(\n    streamId: string,\n    callbacks: ActionStreamSubscriber<S, O>\n  ): Promise<{\n    unsubscribe: () => void;\n  }> {\n    const streamRef = this.db.ref(`${this.refRoot}${streamId}`);\n    const snapshot = await streamRef.get();\n    if (!snapshot.exists()) {\n      throw new StreamNotFoundError(`Stream ${streamId} not found.`);\n    }\n    const streamDataRef = streamRef.child('stream');\n    let timeoutId: NodeJS.Timeout;\n    const resetTimeout = () => {\n      clearTimeout(timeoutId);\n      timeoutId = setTimeout(() => {\n        callbacks.onError?.(\n          new GenkitError({\n            status: 'DEADLINE_EXCEEDED',\n            message: 'Stream timed out.',\n          })\n        );\n        unsubscribe();\n      }, this.timeout);\n    };\n    const handleEvent = (snapshot) => {\n      resetTimeout();\n      if (!snapshot.exists()) {\n        return;\n      }\n      const event = snapshot.val();\n      if (event.type === 'chunk') {\n        callbacks.onChunk?.(event.chunk);\n      } else if (event.type === 'done') {\n        clearTimeout(timeoutId);\n        callbacks.onDone?.(event.output);\n        unsubscribe();\n      } else if (event.type === 'error') {\n        clearTimeout(timeoutId);\n        callbacks.onError?.(event.err);\n        unsubscribe();\n      }\n    };\n    const unsubscribe = () => {\n      streamDataRef.off('child_added', handleEvent);\n    };\n    streamDataRef.on('child_added', handleEvent);\n    resetTimeout();\n    return { unsubscribe };\n  }\n}\n"],"mappings":"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","names":["snapshot"]}