{"version":3,"sources":["../src/dynamic-action-provider.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 * as z from 'zod';\nimport {\n  Action,\n  ActionMetadata,\n  ActionMetadataSchema,\n  defineAction,\n} from './action.mjs';\nimport { GenkitError } from './error.mjs';\nimport { ActionMetadataRecord, ActionType, Registry } from './registry.mjs';\n\ntype DapValue = {\n  [K in ActionType]?: Action<z.ZodTypeAny, z.ZodTypeAny, z.ZodTypeAny>[];\n};\n\nclass SimpleCache {\n  private value: DapValue | undefined;\n  private expiresAt: number | undefined;\n  private ttlMillis: number;\n  private dap: DynamicActionProviderAction | undefined;\n  private dapFn: DapFn;\n  private fetchPromise: Promise<DapValue> | null = null;\n\n  constructor(config: DapConfig, dapFn: DapFn) {\n    this.dapFn = dapFn;\n    this.ttlMillis = !config.cacheConfig?.ttlMillis\n      ? 3 * 1000\n      : config.cacheConfig?.ttlMillis;\n  }\n\n  setDap(dap: DynamicActionProviderAction) {\n    this.dap = dap;\n  }\n\n  setValue(value: DapValue) {\n    const dapId = this.dap?.__action?.name;\n    if (dapId) {\n      Object.entries(value).forEach(([actionType, actionList]) => {\n        actionList?.forEach((action) => {\n          action.__action.key = `/dynamic-action-provider/${dapId}:${actionType}/${action.__action.name}`;\n        });\n      });\n    }\n    this.value = value;\n    this.expiresAt = Date.now() + this.ttlMillis;\n  }\n\n  /**\n   * Gets or fetches the DAP data.\n   * @param skipTrace Don't run the action. i.e. don't create a trace log.\n   * @returns The DAP data\n   */\n  async getOrFetch(params?: { skipTrace?: boolean }): Promise<DapValue> {\n    const isStale =\n      !this.value ||\n      !this.expiresAt ||\n      this.ttlMillis < 0 ||\n      Date.now() > this.expiresAt;\n    if (!isStale) {\n      return this.value!;\n    }\n\n    if (!this.fetchPromise) {\n      this.fetchPromise = (async () => {\n        try {\n          if (this.dap && !params?.skipTrace) {\n            await this.dap.run(); // calls setValue\n          } else {\n            this.setValue(await this.dapFn());\n          }\n          if (!this.value) {\n            throw new Error('value is undefined');\n          }\n          return this.value;\n        } catch (error) {\n          console.error('Error fetching Dynamic Action Provider value:', error);\n          this.invalidate();\n          throw error; // Rethrow to reject the fetchPromise\n        } finally {\n          // Allow new fetches after this one completes or fails.\n          this.fetchPromise = null;\n        }\n      })();\n    }\n    return await this.fetchPromise;\n  }\n\n  invalidate() {\n    this.value = undefined;\n  }\n}\n\nexport interface DynamicRegistry {\n  __cache: SimpleCache;\n  invalidateCache(): void;\n  getAction(\n    actionType: string,\n    actionName: string\n  ): Promise<Action<z.ZodTypeAny, z.ZodTypeAny, z.ZodTypeAny> | undefined>;\n  listActionMetadata(\n    actionType: string,\n    actionName: string\n  ): Promise<ActionMetadata[]>;\n  getActionMetadataRecord(dapPrefix: string): Promise<ActionMetadataRecord>;\n}\n\nexport type DynamicActionProviderAction = Action<\n  z.ZodVoid,\n  z.ZodArray<typeof ActionMetadataSchema>,\n  z.ZodTypeAny\n> &\n  DynamicRegistry & {\n    __action: {\n      metadata: {\n        type: 'dynamic-action-provider';\n      };\n    };\n  };\n\nexport function isDynamicActionProvider(\n  obj: Action<z.ZodTypeAny, z.ZodTypeAny>\n): obj is DynamicActionProviderAction {\n  return obj.__action?.metadata?.type == 'dynamic-action-provider';\n}\n\nexport interface DapConfig {\n  name: string;\n  description?: string;\n  cacheConfig?: {\n    // Negative = no caching\n    // Zero or undefined = default (3000 milliseconds)\n    // Positive number = how many milliseconds the cache is valid for\n    ttlMillis: number | undefined;\n  };\n  metadata?: Record<string, any>;\n}\n\nexport type DapFn = () => Promise<DapValue>;\nexport type DapMetadata = {\n  [K in ActionType]?: ActionMetadata[];\n};\n\nfunction transformDapValue(value: DapValue): ActionMetadata[] {\n  return Object.values(value).flatMap(\n    (actions) => actions?.map((a) => a.__action) || []\n  );\n}\n\nexport function defineDynamicActionProvider(\n  registry: Registry,\n  config: DapConfig | string,\n  fn: DapFn\n): DynamicActionProviderAction {\n  let cfg: DapConfig;\n  if (typeof config == 'string') {\n    cfg = { name: config };\n  } else {\n    cfg = { ...config };\n  }\n  const cache = new SimpleCache(cfg, fn);\n  const a = defineAction(\n    registry,\n    {\n      ...cfg,\n      inputSchema: z.void(),\n      outputSchema: z.array(ActionMetadataSchema),\n      actionType: 'dynamic-action-provider',\n      metadata: { ...(cfg.metadata || {}), type: 'dynamic-action-provider' },\n    },\n    async (_options) => {\n      const dapValue = await fn();\n      cache.setValue(dapValue);\n      return transformDapValue(dapValue);\n    }\n  );\n  implementDap(a as DynamicActionProviderAction, cache);\n  return a as DynamicActionProviderAction;\n}\n\nfunction implementDap(dap: DynamicActionProviderAction, cache: SimpleCache) {\n  cache.setDap(dap);\n  dap.__cache = cache;\n  dap.invalidateCache = () => {\n    dap.__cache.invalidate();\n  };\n\n  dap.getAction = async (actionType: string, actionName: string) => {\n    const result = await dap.__cache.getOrFetch();\n    if (result[actionType]) {\n      return result[actionType].find((t) => t.__action.name == actionName);\n    }\n    return undefined;\n  };\n\n  dap.listActionMetadata = async (actionType: string, actionName: string) => {\n    const result = await dap.__cache.getOrFetch();\n    if (!result[actionType]) {\n      return [];\n    }\n\n    // Match everything in the actionType\n    const metadata = result[actionType].map((a) => a.__action);\n    if (actionName == '*') {\n      return metadata;\n    }\n\n    // Prefix matching\n    if (actionName.endsWith('*')) {\n      const prefix = actionName.slice(0, -1);\n      return metadata.filter((m) => m.name.startsWith(prefix));\n    }\n\n    // Single match or empty array\n    return metadata.filter((m) => m.name == actionName);\n  };\n\n  // This is called by listResolvableActions which is used by the\n  // reflection API.\n  dap.getActionMetadataRecord = async (dapPrefix: string) => {\n    const dapActions = {} as ActionMetadataRecord;\n    // We want to skip traces so we don't get a new action trace\n    // every time the DevUI requests the list of actions.\n    // This is ok, because the DevUI will show the actions, so\n    // not having them in the trace is fine.\n    const result = await dap.__cache.getOrFetch({ skipTrace: true });\n    for (const actions of Object.values(result)) {\n      const metadataList = actions.map((a) => a.__action);\n      for (const metadata of metadataList) {\n        if (!metadata.name || !metadata.key) {\n          throw new GenkitError({\n            status: 'INVALID_ARGUMENT',\n            message: `Invalid metadata when listing dynamic actions from ${dapPrefix} - name required`,\n          });\n        }\n        dapActions[metadata.key] = metadata;\n      }\n    }\n    return dapActions;\n  };\n}\n"],"mappings":"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","names":[]}