{"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  ActionContext,\n  detachedAction,\n  DetachedAction,\n  GenkitError,\n  isAction,\n  isDetachedAction,\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 * 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).attach(registry) as ResourceAction;\n  // when defined via defineResource it's not dynamic.\n  delete action.__action.metadata?.dynamic;\n  action.matches = createMatcher(opts.uri, opts.template);\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 = DetachedAction<\n  typeof ResourceInputSchema,\n  typeof ResourceOutputSchema\n> & {\n  __action: {\n    metadata: {\n      type: 'resource';\n    };\n  };\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  input: ResourceInput\n): Promise<ResourceAction | undefined> {\n  for (const actKeys of Object.keys(await registry.listResolvableActions())) {\n    if (actKeys.startsWith('/resource/')) {\n      const resource = (await registry.lookupAction(actKeys)) as ResourceAction;\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(\n  t: unknown\n): t is DynamicResourceAction {\n  return (\n    (isDetachedAction(t) || isAction(t)) &&\n    t.__action.metadata?.type === 'resource' &&\n    !!t.__action.metadata?.dynamic\n  );\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 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 action = detachedAction(\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  action.matches = matcher;\n\n  return {\n    __action: {\n      ...action.__action,\n      metadata: {\n        ...action.__action.metadata,\n        type: 'resource',\n      },\n    },\n    attach(registry) {\n      const bound = action.attach(registry) as ResourceAction;\n      bound.matches = matcher;\n      return bound;\n    },\n    matches: matcher,\n  } as DynamicResourceAction;\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":[]}