{"version":3,"sources":["../src/resource.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 {\n  action,\n  Action,\n  ActionContext,\n  GenkitError,\n  isAction,\n  z,\n} from '@genkit-ai/core';\nimport { Registry } from '@genkit-ai/core/registry';\nimport uriTemplate from 'uri-templates';\nimport { PartSchema } from './model-types.js';\n\n/**\n * Options for defining a resource.\n */\nexport interface ResourceOptions {\n  /**\n   * Resource name. If not specified, uri or template will be used as name.\n   */\n  name?: string;\n\n  /**\n   * The URI of the resource. Can contain template variables.\n   */\n  uri?: string;\n\n  /**\n   * The URI template (ex. `my://resource/{id}`). See RFC6570 for specification.\n   */\n  template?: string;\n\n  /**\n   * A description of the resource.\n   */\n  description?: string;\n\n  /**\n   * Resource metadata.\n   */\n  metadata?: Record<string, any>;\n}\n\nexport const ResourceInputSchema = z.object({\n  uri: z.string(),\n});\n\nexport type ResourceInput = z.infer<typeof ResourceInputSchema>;\n\nexport const ResourceOutputSchema = z.object({\n  content: z.array(PartSchema),\n});\n\nexport type ResourceOutput = z.infer<typeof ResourceOutputSchema>;\n\n/**\n * A function that returns parts for a given resource.\n */\nexport type ResourceFn = (\n  input: ResourceInput,\n  ctx: ActionContext\n) => ResourceOutput | Promise<ResourceOutput>;\n\n/**\n * A resource action.\n */\nexport interface ResourceAction\n  extends Action<typeof ResourceInputSchema, typeof ResourceOutputSchema> {\n  matches(input: ResourceInput): boolean;\n}\n\n/**\n * A reference to a resource in the form of a name or a ResourceAction.\n */\nexport type ResourceArgument = ResourceAction | string;\n\nexport async function resolveResources(\n  registry: Registry,\n  resources?: ResourceArgument[]\n): Promise<ResourceAction[]> {\n  if (!resources || resources.length === 0) {\n    return [];\n  }\n\n  return await Promise.all(\n    resources.map(async (ref): Promise<ResourceAction> => {\n      if (typeof ref === 'string') {\n        return await lookupResourceByName(registry, ref);\n      } else if (isAction(ref)) {\n        return ref;\n      }\n      throw new Error('Resources must be strings, or actions');\n    })\n  );\n}\n\nexport async function lookupResourceByName(\n  registry: Registry,\n  name: string\n): Promise<ResourceAction> {\n  const resource =\n    (await registry.lookupAction(name)) ||\n    (await registry.lookupAction(`/resource/${name}`)) ||\n    (await registry.lookupAction(`/dynamic-action-provider/${name}`));\n  if (!resource) {\n    throw new Error(`Resource ${name} not found`);\n  }\n  return resource as ResourceAction;\n}\n\n/**\n * Defines a resource.\n *\n * @param registry The registry to register the resource with.\n * @param opts The resource options.\n * @param fn The resource function.\n * @returns The resource action.\n */\nexport function defineResource(\n  registry: Registry,\n  opts: ResourceOptions,\n  fn: ResourceFn\n): ResourceAction {\n  const action = dynamicResource(opts, fn);\n  action.matches = createMatcher(opts.uri, opts.template);\n  action.__action.metadata.dynamic = false;\n  registry.registerAction('resource', action);\n  return action;\n}\n\n/**\n * A dynamic action with a `resource` type. Dynamic resources are detached actions -- not associated with any registry.\n */\nexport type DynamicResourceAction = ResourceAction & {\n  __action: {\n    metadata: {\n      type: 'resource';\n    };\n  };\n  /** @deprecated no-op, for backwards compatibility only. */\n  attach(registry: Registry): ResourceAction;\n  matches(input: ResourceInput): boolean;\n};\n\n/**\n * Finds a matching resource in the registry. If not found returns undefined.\n */\nexport async function findMatchingResource(\n  registry: Registry,\n  resources: ResourceAction[],\n  input: ResourceInput\n): Promise<ResourceAction | undefined> {\n  // First look in any resources explicitly listed in the generate request\n  for (const res of resources) {\n    if (res.matches(input)) {\n      return res;\n    }\n  }\n\n  // Then search the registry\n  for (const registryKey of Object.keys(\n    await registry.listResolvableActions()\n  )) {\n    // We decided not to look in DAP actions because they might be slow.\n    // DAP actions with resources will only be found if they are listed in the\n    // resources section, and then they will be found above.\n    if (registryKey.startsWith('/resource/')) {\n      const resource = (await registry.lookupAction(\n        registryKey\n      )) as ResourceAction;\n\n      if (resource.matches(input)) {\n        return resource;\n      }\n    }\n  }\n  return undefined;\n}\n\n/** Checks whether provided object is a dynamic resource. */\nexport function isDynamicResourceAction(t: unknown): t is ResourceAction {\n  return isAction(t) && t.__action?.metadata?.dynamic === true;\n}\n\n/**\n * Defines a dynamic resource. Dynamic resources are just like regular resources but will not be\n * registered in the Genkit registry and can be defined dynamically at runtime.\n */\nexport function resource(\n  opts: ResourceOptions,\n  fn: ResourceFn\n): ResourceAction {\n  return dynamicResource(opts, fn);\n}\n\n/**\n * Defines a dynamic resource. Dynamic resources are just like regular resources but will not be\n * registered in the Genkit registry and can be defined dynamically at runtime.\n *\n * @deprecated renamed to {@link resource}.\n */\nexport function dynamicResource(\n  opts: ResourceOptions,\n  fn: ResourceFn\n): DynamicResourceAction {\n  const uri = opts.uri ?? opts.template;\n  if (!uri) {\n    throw new GenkitError({\n      status: 'INVALID_ARGUMENT',\n      message: `must specify either url or template options`,\n    });\n  }\n  const matcher = createMatcher(opts.uri, opts.template);\n\n  const act = action(\n    {\n      actionType: 'resource',\n      name: opts.name ?? uri,\n      description: opts.description,\n      inputSchema: ResourceInputSchema,\n      outputSchema: ResourceOutputSchema,\n      metadata: {\n        resource: {\n          uri: opts.uri,\n          template: opts.template,\n        },\n        ...opts.metadata,\n        type: 'resource',\n        dynamic: true,\n      },\n    },\n    async (input, ctx) => {\n      const templateMatch = matcher(input);\n      if (!templateMatch) {\n        throw new GenkitError({\n          status: 'INVALID_ARGUMENT',\n          message: `input ${input} did not match template ${uri}`,\n        });\n      }\n      const parts = await fn(input, ctx);\n      parts.content.map((p) => {\n        if (!p.metadata) {\n          p.metadata = {};\n        }\n        if (p.metadata?.resource) {\n          if (!(p.metadata as any).resource.parent) {\n            (p.metadata as any).resource.parent = {\n              uri: input.uri,\n            };\n            if (opts.template) {\n              (p.metadata as any).resource.parent.template = opts.template;\n            }\n          }\n        } else {\n          (p.metadata as any).resource = {\n            uri: input.uri,\n          };\n          if (opts.template) {\n            (p.metadata as any).resource.template = opts.template;\n          }\n        }\n        return p;\n      });\n      return parts;\n    }\n  ) as DynamicResourceAction;\n\n  act.matches = matcher;\n  act.attach = (_: Registry) => act;\n  return act;\n}\n\nfunction createMatcher(\n  uriOpt: string | undefined,\n  templateOpt: string | undefined\n): (input: ResourceInput) => boolean {\n  // TODO: normalize resource URI during comparisons\n  // foo://bar?baz=1&qux=2 and foo://bar?qux=2&baz=1 are equivalent URIs but would not match.\n  if (uriOpt) {\n    return (input: ResourceInput) => input.uri === uriOpt;\n  }\n\n  if (templateOpt) {\n    const template = uriTemplate(templateOpt);\n    return (input: ResourceInput) => template!.fromUri(input.uri) !== undefined;\n  }\n\n  throw new GenkitError({\n    status: 'INVALID_ARGUMENT',\n    message: 'must specify either url or template options',\n  });\n}\n"],"mappings":"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","names":["resource","action"]}