{"version":3,"file":"ir.cjs","sources":["../../../src/query/ir.ts"],"sourcesContent":["/*\nThis is the intermediate representation of the query.\n*/\n\nimport type { CompareOptions } from './builder/types'\nimport type { Collection, CollectionImpl } from '../collection/index.js'\nimport type { NamespacedRow } from '../types'\n\nexport interface QueryIR {\n  from: From\n  select?: Select\n  join?: Join\n  where?: Array<Where>\n  groupBy?: GroupBy\n  having?: Array<Having>\n  orderBy?: OrderBy\n  limit?: Limit\n  offset?: Offset\n  distinct?: true\n  singleResult?: true\n\n  // Functional variants\n  fnSelect?: (row: NamespacedRow) => any\n  fnWhere?: Array<(row: NamespacedRow) => any>\n  fnHaving?: Array<(row: NamespacedRow) => any>\n}\n\nexport type IncludesMaterialization = `collection` | `array` | `concat`\n\nexport const INCLUDES_SCALAR_FIELD = `__includes_scalar__`\n\nexport type From = CollectionRef | QueryRef\n\nexport type Select = {\n  [alias: string]: BasicExpression | Aggregate | Select | IncludesSubquery\n}\n\nexport type Join = Array<JoinClause>\n\nexport interface JoinClause {\n  from: CollectionRef | QueryRef\n  type: `left` | `right` | `inner` | `outer` | `full` | `cross`\n  left: BasicExpression\n  right: BasicExpression\n}\n\nexport type Where =\n  | BasicExpression<boolean>\n  | { expression: BasicExpression<boolean>; residual?: boolean }\n\nexport type GroupBy = Array<BasicExpression>\n\nexport type Having = Where\n\nexport type OrderBy = Array<OrderByClause>\n\nexport type OrderByClause = {\n  expression: BasicExpression\n  compareOptions: CompareOptions\n}\n\nexport type OrderByDirection = `asc` | `desc`\n\nexport type Limit = number\n\nexport type Offset = number\n\n/* Expressions */\n\nabstract class BaseExpression<T = any> {\n  public abstract type: string\n  /** @internal - Type brand for TypeScript inference */\n  declare readonly __returnType: T\n}\n\nexport class CollectionRef extends BaseExpression {\n  public type = `collectionRef` as const\n  constructor(\n    public collection: CollectionImpl,\n    public alias: string,\n  ) {\n    super()\n  }\n}\n\nexport class QueryRef extends BaseExpression {\n  public type = `queryRef` as const\n  constructor(\n    public query: QueryIR,\n    public alias: string,\n  ) {\n    super()\n  }\n}\n\nexport class PropRef<T = any> extends BaseExpression<T> {\n  public type = `ref` as const\n  constructor(\n    public path: Array<string>, // path to the property in the collection, with the alias as the first element\n  ) {\n    super()\n  }\n}\n\nexport class Value<T = any> extends BaseExpression<T> {\n  public type = `val` as const\n  constructor(\n    public value: T, // any js value\n  ) {\n    super()\n  }\n}\n\nexport class Func<T = any> extends BaseExpression<T> {\n  public type = `func` as const\n  constructor(\n    public name: string, // such as eq, gt, lt, upper, lower, etc.\n    public args: Array<BasicExpression>,\n  ) {\n    super()\n  }\n}\n\n// This is the basic expression type that is used in the majority of expression\n// builder callbacks (select, where, groupBy, having, orderBy, etc.)\n// it doesn't include aggregate functions as those are only used in the select clause\nexport type BasicExpression<T = any> = PropRef<T> | Value<T> | Func<T>\n\nexport class Aggregate<T = any> extends BaseExpression<T> {\n  public type = `agg` as const\n  constructor(\n    public name: string, // such as count, avg, sum, min, max, etc.\n    public args: Array<BasicExpression>,\n  ) {\n    super()\n  }\n}\n\nexport class IncludesSubquery extends BaseExpression {\n  public type = `includesSubquery` as const\n  constructor(\n    public query: QueryIR, // Child query (correlation WHERE removed)\n    public correlationField: PropRef, // Parent-side ref (e.g., project.id)\n    public childCorrelationField: PropRef, // Child-side ref (e.g., issue.projectId)\n    public fieldName: string, // Result field name (e.g., \"issues\")\n    public parentFilters?: Array<Where>, // WHERE clauses referencing parent aliases (applied post-join)\n    public parentProjection?: Array<PropRef>, // Parent field refs used by parentFilters\n    public materialization: IncludesMaterialization = `collection`,\n    public scalarField?: string,\n  ) {\n    super()\n  }\n}\n\n/**\n * Runtime helper to detect IR expression-like objects.\n * Prefer this over ad-hoc local implementations to keep behavior consistent.\n */\nexport function isExpressionLike(value: any): boolean {\n  return (\n    value instanceof Aggregate ||\n    value instanceof Func ||\n    value instanceof PropRef ||\n    value instanceof Value ||\n    value instanceof IncludesSubquery\n  )\n}\n\n/**\n * Helper functions for working with Where clauses\n */\n\n/**\n * Extract the expression from a Where clause\n */\nexport function getWhereExpression(where: Where): BasicExpression<boolean> {\n  return typeof where === `object` && `expression` in where\n    ? where.expression\n    : where\n}\n\n/**\n * Extract the expression from a HAVING clause\n * HAVING clauses can contain aggregates, unlike regular WHERE clauses\n */\nexport function getHavingExpression(\n  having: Having,\n): BasicExpression | Aggregate {\n  return typeof having === `object` && `expression` in having\n    ? having.expression\n    : having\n}\n\n/**\n * Check if a Where clause is marked as residual\n */\nexport function isResidualWhere(where: Where): boolean {\n  return (\n    typeof where === `object` &&\n    `expression` in where &&\n    where.residual === true\n  )\n}\n\n/**\n * Create a residual Where clause from an expression\n */\nexport function createResidualWhere(\n  expression: BasicExpression<boolean>,\n): Where {\n  return { expression, residual: true }\n}\n\nfunction getRefFromAlias(\n  query: QueryIR,\n  alias: string,\n): CollectionRef | QueryRef | void {\n  if (query.from.alias === alias) {\n    return query.from\n  }\n\n  for (const join of query.join || []) {\n    if (join.from.alias === alias) {\n      return join.from\n    }\n  }\n}\n\n/**\n * Follows the given reference in a query\n * until its finds the root field the reference points to.\n * @returns The collection, its alias, and the path to the root field in this collection\n */\nexport function followRef(\n  query: QueryIR,\n  ref: PropRef<any>,\n  collection: Collection,\n): { collection: Collection; path: Array<string> } | void {\n  if (ref.path.length === 0) {\n    return\n  }\n\n  if (ref.path.length === 1) {\n    // This field should be part of this collection\n    const field = ref.path[0]!\n    // is it part of the select clause?\n    if (query.select) {\n      const selectedField = query.select[field]\n      if (selectedField && selectedField.type === `ref`) {\n        return followRef(query, selectedField, collection)\n      }\n    }\n\n    // Either this field is not part of the select clause\n    // and thus it must be part of the collection itself\n    // or it is part of the select but is not a reference\n    // so we can stop here and don't have to follow it\n    return { collection, path: [field] }\n  }\n\n  if (ref.path.length > 1) {\n    // This is a nested field\n    const [alias, ...rest] = ref.path\n    const aliasRef = getRefFromAlias(query, alias!)\n    if (!aliasRef) {\n      return\n    }\n\n    if (aliasRef.type === `queryRef`) {\n      return followRef(aliasRef.query, new PropRef(rest), collection)\n    } else {\n      // This is a reference to a collection\n      // we can't follow it further\n      // so the field must be on the collection itself\n      return { collection: aliasRef.collection, path: rest }\n    }\n  }\n}\n"],"names":[],"mappings":"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"}