{"version":3,"sources":["../src/base-model.ts","../src/config.ts"],"sourcesContent":["import type { Container, FeedOptions, ItemDefinition, Resource } from '@azure/cosmos'\nimport type { CosmosClient, SqlQuerySpec } from '@azure/cosmos'\nimport { input } from '@azure/functions'\nimport { ulid } from 'ulidx'\n\nexport type Base = object\n\n// TODO: Make sure these always returns the correct type\ntype CosmosResource<T extends Base> = Resource & T\ntype CosmosItemDefinition<T extends Base> = ItemDefinition & T\n\ninterface AutoFields {\n  /** Automatically generate an ID on document creation - defaults to true */\n  id?: boolean\n  /** Automatically generate createdAt and updatedAt fields on document create/updates - defaults to true */\n  timestamp?: boolean\n}\n\nexport interface ModelOptions {\n  /** The name of the Cosmos database */\n  database: string\n  /** The name of the Cosmos container within the database */\n  container: string\n  /** The instantiated Cosmos client */\n  client: CosmosClient\n  /** The name of the env of the Cosmos connection string - defaults to `COSMOS_CONNECTION_STRING` */\n  connectionStringSetting?: string\n  /** Automatic fields creation - defaults to true */\n  fields?: AutoFields | boolean\n}\n\nconst initial = {}\n\nexport class BaseModel<T extends Base = typeof initial> {\n  client: Container\n\n  connectionStringSetting = 'COSMOS_CONNECTION_STRING'\n\n  fields: AutoFields = {\n    id: true,\n    timestamp: true,\n  }\n\n  constructor(private options: ModelOptions) {\n    if (options.connectionStringSetting) {\n      this.connectionStringSetting = options.connectionStringSetting\n    }\n\n    if (typeof this.options === 'boolean' && !this.options) {\n      this.fields = { id: false, timestamp: false }\n    }\n    if (typeof this.options === 'object') {\n      this.fields = {\n        ...this.fields,\n        // Don't know why this won't remove the bool type\n        ...(this.options.fields as AutoFields),\n      }\n    }\n\n    this.client = options.client.database(options.database).container(options.container)\n  }\n\n  /**\n   * Create an Azure Function app input binding to find a specific document by an input variable.\n   * You can also provide a type (matching `Record<string, any>`) as the first type generic,\n   * and it will validate the variable to be one of the types keys.\n   *\n   * @example\n   * ```ts\n   * const shopInput = orm.shops.createFindBinding(`shop`)\n   * ```\n   * @example\n   * An example with a type to check the variable:\n   * ```ts\n   * interface ActivityInput {\n   *  shop: string\n   *  // ...\n   * }\n   *\n   * const shopDocument = orm.shops.createFindBinding<ActivityInput>('shop') // ✅\n   * const shopDocument2 = orm.shops.createFindBinding<ActivityInput>('id') // ❌\n   * ```\n   */\n  public createFindBinding<Input extends object = object, Key = keyof Input & string>(variable: Key) {\n    const binding = `{${variable}}`\n\n    return input.cosmosDB({\n      databaseName: this.options.database,\n      containerName: this.options.container,\n      connection: this.connectionStringSetting,\n      id: binding,\n      partitionKey: binding,\n    })\n  }\n\n  /** Create an Azure Function app input binding to fetch all documents from this container. */\n  public createAllBinding() {\n    return input.cosmosDB({\n      databaseName: this.options.database,\n      containerName: this.options.container,\n      connection: this.connectionStringSetting,\n    })\n  }\n\n  /**\n   * Create an Azure Function app input binding with a custom SQL query.\n   * @example\n   * ```ts\n   * const inputDoc = orm.posts.createSQLBinding(`SELECT * FROM c WHERE c.deleted_at IS NULL`)\n   * ```\n   */\n  public createSQLBinding(sqlQuery: string) {\n    return input.cosmosDB({\n      databaseName: this.options.database,\n      containerName: this.options.container,\n      connection: this.connectionStringSetting,\n      sqlQuery,\n    })\n  }\n\n  /** Fetch all resources in a container */\n  public async all(): Promise<CosmosItemDefinition<T>[]> {\n    const { resources } = await this.client.items.readAll().fetchAll()\n    return resources as CosmosItemDefinition<T>[]\n  }\n\n  /** Fetch a specific resource by its ID */\n  public async find(id: string): Promise<CosmosResource<T> | undefined> {\n    const { resource } = await this.client.item(id, id).read()\n    return resource as CosmosResource<T> | undefined\n  }\n\n  /** Fetch multiple resources using their ID's */\n  public async findMany(ids: string[]): Promise<CosmosResource<T>[]> {\n    const { resources } = await this.client.items\n      .query({\n        query: 'SELECT * FROM C WHERE ARRAY_CONTAINS(@ids, C.id)',\n        parameters: [{ name: '@ids', value: ids }],\n      })\n      .fetchAll()\n\n    return resources as CosmosResource<T>[]\n  }\n\n  /** Find a resource by a specific key */\n  public async findBy(key: keyof T, value: string): Promise<CosmosResource<T> | undefined> {\n    const { resources } = await this.client.items\n      .query({\n        query: 'SELECT * FROM C WHERE C[@key] = @value OFFSET 0 LIMIT 1',\n        parameters: [\n          { name: '@key', value: String(key) },\n          { name: '@value', value: value },\n        ],\n      })\n      .fetchAll()\n\n    const [resource] = resources\n\n    return resource as CosmosResource<T> | undefined\n  }\n\n  /** Find multiple resources by a specific key */\n  public async findManyBy(key: keyof T, value: string): Promise<CosmosResource<T>[]> {\n    const { resources } = await this.client.items\n      .query({\n        query: 'SELECT * FROM C WHERE C[@key] = @value',\n        parameters: [\n          { name: '@key', value: String(key) },\n          { name: '@value', value: value },\n        ],\n      })\n      .fetchAll()\n\n    const [resource] = resources\n\n    return resource as CosmosResource<T>[]\n  }\n\n  /** Create a resource */\n  public async create(\n    input: Omit<T, 'id' | 'createdAt' | 'updatedAt'> & Partial<{ id: string; createdAt: string; updatedAt: string }>,\n  ): Promise<CosmosResource<T> | undefined> {\n    const merged = {\n      ...input,\n      id: this.fields.id ? ulid() : input.id,\n      createdAt: this.fields.timestamp ? new Date().toISOString() : input.createdAt,\n      updatedAt: this.fields.timestamp ? new Date().toISOString() : input.updatedAt,\n    }\n\n    const { resource } = await this.client.items.create(merged)\n    return resource as CosmosResource<T> | undefined\n  }\n\n  /** Either update or create a resource */\n  public async upsert(input: T & { id: string }): Promise<CosmosResource<T> | undefined> {\n    const { resource } = await this.client.items.upsert(input)\n    return resource as CosmosResource<T> | undefined\n  }\n\n  /** Update a resource - replaces the whole resource, so make sure to provide a full input */\n  public async replace(\n    id: string,\n    input: Omit<T, 'updatedAt'> & Partial<{ updatedAt: string }>,\n  ): Promise<CosmosResource<T> | undefined> {\n    const merged = {\n      ...input,\n      updatedAt: this.fields.timestamp ? new Date().toISOString() : input.updatedAt,\n    }\n\n    const { resource } = await this.client.item(id, id).replace(merged)\n    return resource as CosmosResource<T> | undefined\n  }\n\n  /** Delete a resource */\n  public async delete(id: string): Promise<CosmosResource<T> | undefined> {\n    const { resource } = await this.client.item(id, id).delete<T>()\n    return resource as CosmosResource<T>\n  }\n\n  /**\n   * Run a query, and fetch all results\n   *\n   * This function accepts a generic, so you can pass in the type of the response if\n   * you are running a custom select query - for example:\n   *\n   * `.query<{ id: string }>('SELECT c.id FROM c') // returns { id: string }[]`\n   *\n   * `.query<number>('SELECT VALUE count(c.id) FROM c') // returns [number]`\n   *\n   * This is just a wrapper of the `.client.items.query()` function, so you can use that\n   * instead if you need access to the request metrics for example.\n   */\n\n  // biome-ignore lint/suspicious/noExplicitAny: <explanation>\n  public async query<R = any>(query: string | SqlQuerySpec, options?: Pick<FeedOptions, 'maxItemCount'>): Promise<R[]> {\n    const { resources } = await this.client.items.query(query, options).fetchAll()\n    return resources as R[]\n  }\n}\n","import { CosmosClient } from '@azure/cosmos'\nimport { type Base, BaseModel } from './base-model'\nimport type { ModelOptions } from './base-model'\n\ninterface Builder {\n  createModel: <T extends Base>(container: string, options?: Pick<ModelOptions, 'fields'>) => BaseModel<T>\n}\n\n/** Default client configuration - for example the connection string setting, and the database name. */\nexport interface Options<M extends { [K: string]: BaseModel }> {\n  /** The name of the Cosmos database */\n  database: string\n  /**\n   * The name of the env of the Cosmos connection string - defaults to `COSMOS_CONNECTION_STRING`.\n   * This can be replaced by directly passing in the connection string with the `connectionString` option,\n   * but if you are using the binding shortcuts then this setting is required as it is used in the Azure Function bindings.\n   */\n  connectionStringSetting?: string\n  /**\n   * The Cosmos connection string - overrides using the `connectionStringSetting` env.\n   *\n   * Preferably use the `connectionStringSetting` with the connection string as an environment variable if you are using\n   * this within an Azure Functions app.\n   */\n  connectionString?: string\n  /** A list of the models to create, and their container names. */\n  models: (builder: Builder) => M\n}\n\nexport type DB<M extends Record<string, BaseModel>> = ReturnType<Options<M>['models']> & {\n  client: CosmosClient\n}\n\nexport function createClient<M extends Record<string, BaseModel>>(options: Options<M>): DB<M> {\n  const connectionStringSetting = options.connectionStringSetting || 'COSMOS_CONNECTION_STRING'\n  const connectionString = options.connectionString ?? process.env[connectionStringSetting]\n\n  if (typeof connectionString !== 'string') {\n    if (options.connectionString) throw new Error('Missing connection string value (from `options.connectionString`)')\n    throw new Error(`Missing connection string for ${connectionStringSetting}`)\n  }\n\n  const client = new CosmosClient(connectionString)\n\n  const builder: Builder = {\n    createModel: (container, config = {}) => new BaseModel({ client, container, ...options, ...config }),\n  }\n\n  const models = options.models(builder)\n\n  return {\n    client,\n    ...models,\n  }\n}\n"],"mappings":"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","names":["input"]}