@514labs/moose-lib 0.6.437 → 0.6.438

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/dmv2/utils/stackTrace.ts","../src/dmv2/typedBase.ts","../src/dataModels/dataModelTypes.ts","../src/dataModels/types.ts","../src/sqlHelpers.ts","../src/dmv2/sdk/olapTable.ts","../src/dmv2/sdk/stream.ts","../src/dmv2/sdk/workflow.ts","../src/dmv2/sdk/ingestApi.ts","../src/dmv2/sdk/consumptionApi.ts","../src/dmv2/sdk/ingestPipeline.ts","../src/dmv2/sdk/etlPipeline.ts","../src/dmv2/sdk/materializedView.ts","../src/dmv2/sdk/sqlResource.ts","../src/dmv2/sdk/view.ts","../src/dmv2/sdk/lifeCycle.ts","../src/dmv2/sdk/webApp.ts","../src/dmv2/registry.ts","../src/dmv2/index.ts","../src/browserCompatible.ts","../src/commons.ts","../src/secrets.ts","../src/consumption-apis/helpers.ts","../src/consumption-apis/webAppHelpers.ts","../src/scripts/task.ts","../src/clients/redisClient.ts","../src/config/configFile.ts","../src/config/runtime.ts","../src/consumption-apis/standalone.ts","../src/utilities/json.ts","../src/utilities/dataParser.ts","../src/utilities/index.ts","../src/connectors/dataSource.ts","../src/query-layer/sql-utils.ts","../src/query-layer/helpers.ts","../src/query-layer/query-model.ts","../src/query-layer/query-builder.ts","../src/query-layer/model-tools.ts","../src/consumption-apis/validation.ts","../src/query-layer/index.ts","../src/index.ts","../src/compiler-config.ts","../src/dmv2/utils/sourceFiles.ts","../src/dmv2/utils/index.ts","../src/dmv2/dependencyAnalysis.ts","../src/dmv2/internal.ts","../src/moose-runner.ts","../src/consumption-apis/runner.ts","../src/cluster-utils.ts","../src/utils/structured-logging.ts","../src/streaming-functions/runner.ts","../src/moduleExportSerializer.ts","../src/scripts/runner.ts","../src/scripts/activity.ts","../src/scripts/task-context.ts","../src/scripts/logger.ts"],"sourcesContent":["/**\n * Stack trace utilities for extracting source file information.\n *\n * This module provides functions for parsing stack traces to determine\n * where user code is located, filtering out internal library paths.\n */\n\n/**\n * Information extracted from a stack trace about source file location.\n */\nexport interface SourceFileInfo {\n /** The file path */\n file?: string;\n /** The line number (as a string) */\n line?: string;\n}\n\n/**\n * Source location with file, line, and column information.\n * Used for precise error location tracking.\n */\nexport interface SourceLocation {\n /** The file path */\n file: string;\n /** The line number */\n line: number;\n /** The column number (optional - may not always be available from stack trace) */\n column?: number;\n}\n\n/**\n * Check if a stack trace line should be skipped (internal/library code).\n * @internal\n */\nfunction shouldSkipStackLine(line: string): boolean {\n return (\n line.includes(\"node_modules\") || // Skip npm installed packages (prod)\n line.includes(\"node:internal\") || // Skip Node.js internals (modern format)\n line.includes(\"internal/modules\") || // Skip Node.js internals (older format)\n line.includes(\"ts-node\") || // Skip TypeScript execution\n line.includes(\"/ts-moose-lib/src/\") || // Skip dev/linked moose-lib src (Unix)\n line.includes(\"\\\\ts-moose-lib\\\\src\\\\\") || // Skip dev/linked moose-lib src (Windows)\n line.includes(\"/ts-moose-lib/dist/\") || // Skip dev/linked moose-lib dist (Unix)\n line.includes(\"\\\\ts-moose-lib\\\\dist\\\\\") // Skip dev/linked moose-lib dist (Windows)\n );\n}\n\n/**\n * Extract file path and line number from a stack trace line.\n * @internal\n */\nfunction parseStackLine(line: string): SourceFileInfo | undefined {\n const match =\n line.match(/\\((.*):(\\d+):(\\d+)\\)/) || line.match(/at (.*):(\\d+):(\\d+)/);\n if (match && match[1]) {\n return {\n file: match[1],\n line: match[2],\n };\n }\n return undefined;\n}\n\n/**\n * Extract source file information from a stack trace.\n * Works in both development (npm link) and production (npm install) environments.\n *\n * @param stack - The stack trace string from an Error object\n * @returns Object with file path and line number, or empty object if not found\n */\nexport function getSourceFileInfo(stack?: string): SourceFileInfo {\n if (!stack) return {};\n const lines = stack.split(\"\\n\");\n for (const line of lines) {\n if (shouldSkipStackLine(line)) continue;\n const info = parseStackLine(line);\n if (info) return info;\n }\n return {};\n}\n\n/**\n * Extracts source location (file, line, column) from a stack trace.\n *\n * Stack trace formats vary by environment:\n * - V8 (Node/Chrome): \" at Function (file.ts:10:15)\"\n * - SpiderMonkey (Firefox): \"Function@file.ts:10:15\"\n *\n * @param stack - Error stack trace string\n * @returns SourceLocation or undefined if parsing fails\n */\nexport function getSourceLocationFromStack(\n stack: string | undefined,\n): SourceLocation | undefined {\n if (!stack) return undefined;\n\n const lines = stack.split(\"\\n\");\n\n // Skip first line (error message) and internal frames\n for (const line of lines.slice(1)) {\n // Skip node_modules and internal moose-lib frames\n if (shouldSkipStackLine(line)) {\n continue;\n }\n\n // V8 format: \" at Function (file.ts:10:15)\" or \" at file.ts:10:15\"\n const v8Match = line.match(/at\\s+(?:.*?\\s+\\()?(.+):(\\d+):(\\d+)\\)?/);\n if (v8Match) {\n return {\n file: v8Match[1],\n line: parseInt(v8Match[2], 10),\n column: parseInt(v8Match[3], 10),\n };\n }\n\n // SpiderMonkey format: \"Function@file.ts:10:15\"\n const smMatch = line.match(/(?:.*@)?(.+):(\\d+):(\\d+)/);\n if (smMatch) {\n return {\n file: smMatch[1],\n line: parseInt(smMatch[2], 10),\n column: parseInt(smMatch[3], 10),\n };\n }\n }\n\n return undefined;\n}\n\n/**\n * Extract the first file path outside moose-lib internals from a stack trace.\n * Works in both development (npm link) and production (npm install) environments.\n *\n * @deprecated Use getSourceLocationFromStack instead\n * @param stack - The stack trace string from an Error object\n * @returns The first user-code file path, or undefined if not found\n */\nexport function getSourceFileFromStack(stack?: string): string | undefined {\n const location = getSourceLocationFromStack(stack);\n return location?.file;\n}\n","import type { IJsonSchemaCollection } from \"typia\";\nimport { Column } from \"../dataModels/dataModelTypes\";\nimport { getSourceFileInfo } from \"./utils/stackTrace\";\n\n/**\n * Type definition for typia validation functions\n */\nexport interface TypiaValidators<T> {\n /** Typia validator function: returns { success: boolean, data?: T, errors?: any[] } */\n validate?: (data: unknown) => { success: boolean; data?: T; errors?: any[] };\n /** Typia assert function: throws on validation failure, returns T on success */\n assert?: (data: unknown) => T;\n /** Typia is function: returns boolean indicating if data matches type T */\n is?: (data: unknown) => data is T;\n}\n\n/**\n * Base class for all typed Moose dmv2 resources (OlapTable, Stream, etc.).\n * Handles the storage and injection of schema information (JSON schema and Column array)\n * provided by the Moose compiler plugin.\n *\n * @template T The data type (interface or type alias) defining the schema of the resource.\n * @template C The specific configuration type for the resource (e.g., OlapConfig, StreamConfig).\n */\nexport class TypedBase<T, C> {\n /** The JSON schema representation of type T. Injected by the compiler plugin. */\n schema: IJsonSchemaCollection.IV3_1;\n /** The name assigned to this resource instance. */\n name: string;\n\n /** A dictionary mapping column names (keys of T) to their Column definitions. */\n columns: {\n [columnName in keyof Required<T>]: Column;\n };\n /** An array containing the Column definitions for this resource. Injected by the compiler plugin. */\n columnArray: Column[];\n\n /** The configuration object specific to this resource type. */\n config: C;\n\n /** Typia validation functions for type T. Injected by the compiler plugin for OlapTable. */\n validators?: TypiaValidators<T>;\n\n /** Optional metadata for the resource, always present as an object. */\n metadata!: { [key: string]: any };\n\n /**\n * Whether this resource allows extra fields beyond the defined columns.\n * When true, extra fields in payloads are passed through to streaming functions.\n * Injected by the compiler plugin when the type has an index signature.\n */\n allowExtraFields: boolean;\n\n /**\n * @internal Constructor intended for internal use by subclasses and the compiler plugin.\n * It expects the schema and columns to be provided, typically injected by the compiler.\n *\n * @param name The name for the resource instance.\n * @param config The configuration object for the resource.\n * @param schema The JSON schema for the resource's data type T (injected).\n * @param columns The array of Column definitions for T (injected).\n * @param allowExtraFields Whether extra fields are allowed (injected when type has index signature).\n */\n constructor(\n name: string,\n config: C,\n schema?: IJsonSchemaCollection.IV3_1,\n columns?: Column[],\n validators?: TypiaValidators<T>,\n allowExtraFields?: boolean,\n ) {\n if (schema === undefined || columns === undefined) {\n throw new Error(\n \"Supply the type param T so that the schema is inserted by the compiler plugin.\",\n );\n }\n\n this.schema = schema;\n this.columnArray = columns;\n const columnsObj = {} as any;\n columns.forEach((column) => {\n columnsObj[column.name] = column;\n });\n this.columns = columnsObj;\n\n this.name = name;\n this.config = config;\n this.validators = validators;\n this.allowExtraFields = allowExtraFields ?? false;\n\n // Always ensure metadata is an object and attach stackTrace (last 10 lines only)\n this.metadata =\n (config as any)?.metadata ? { ...(config as any).metadata } : {};\n\n if (!this.metadata.source) {\n const stack = new Error().stack;\n if (stack) {\n const info = getSourceFileInfo(stack);\n this.metadata.source = { file: info.file, line: info.line };\n }\n }\n }\n}\n","import ts from \"typescript\";\nimport { IdentifierBrandedString } from \"../sqlHelpers\";\n\nexport type EnumValues =\n | { name: string; value: { Int: number } }[]\n | { name: string; value: { String: string } }[];\nexport type DataEnum = { name: string; values: EnumValues };\nexport type Nested = { name: string; columns: Column[]; jwt: boolean };\nexport type ArrayType = { elementType: DataType; elementNullable: boolean };\nexport type NamedTupleType = { fields: Array<[string, DataType]> };\nexport type MapType = { keyType: DataType; valueType: DataType };\nexport type JsonOptions = {\n max_dynamic_paths?: number;\n max_dynamic_types?: number;\n typed_paths?: Array<[string, DataType]>;\n skip_paths?: string[];\n skip_regexps?: string[];\n};\nexport type DataType =\n | string\n | DataEnum\n | ArrayType\n | Nested\n | NamedTupleType\n | MapType\n | JsonOptions\n | { nullable: DataType };\nexport interface Column {\n name: IdentifierBrandedString;\n data_type: DataType;\n required: boolean;\n unique: false; // what is this for?\n primary_key: boolean;\n default: string | null;\n materialized: string | null;\n ttl: string | null;\n codec: string | null;\n annotations: [string, any][];\n comment: string | null;\n}\n\nexport interface DataModel {\n columns: Column[];\n name: string;\n}\n\nexport class UnknownType extends Error {\n t: ts.Type;\n fieldName: string;\n typeName: string;\n constructor(t: ts.Type, fieldName: string, typeName: string) {\n super();\n this.t = t;\n this.fieldName = fieldName;\n this.typeName = typeName;\n }\n}\n\nexport class NullType extends Error {\n fieldName: string;\n typeName: string;\n constructor(fieldName: string, typeName: string) {\n super();\n this.fieldName = fieldName;\n this.typeName = typeName;\n }\n}\n\nexport class UnsupportedEnum extends Error {\n enumName: string;\n constructor(enumName: string) {\n super();\n this.enumName = enumName;\n }\n}\n\nexport class UnsupportedFeature extends Error {\n featureName: string;\n constructor(featureName: string) {\n super();\n this.featureName = featureName;\n }\n}\n\nexport class IndexType extends Error {\n typeName: string;\n indexSignatures: string[];\n\n constructor(typeName: string, indexSignatures: string[]) {\n const explanation =\n \"Index signatures (e.g. [key: string]: value) are not supported in data models.\";\n\n const suggestion =\n \"Consider splitting this into separate types or using a single Record<K, V> type.\";\n\n const signatures = `Found index signatures: ${indexSignatures.join(\", \")}`;\n\n super(\n `${explanation}\\n\\nType: ${typeName}\\n\\n${signatures}\\n\\nSuggestion: ${suggestion}`,\n );\n\n this.typeName = typeName;\n this.indexSignatures = indexSignatures;\n }\n}\n\n/**\n * Type guard: is this DataType an Array(Nested(...))?\n * Uses the ArrayType and Nested types for type safety.\n */\nexport function isArrayNestedType(\n dt: DataType,\n): dt is ArrayType & { elementType: Nested } {\n return (\n typeof dt === \"object\" &&\n dt !== null &&\n (dt as ArrayType).elementType !== null &&\n typeof (dt as ArrayType).elementType === \"object\" &&\n (dt as ArrayType).elementType.hasOwnProperty(\"columns\") &&\n Array.isArray(((dt as ArrayType).elementType as Nested).columns)\n );\n}\n\n/**\n * Type guard: is this DataType a Nested struct (not array)?\n */\nexport function isNestedType(dt: DataType): dt is Nested {\n return (\n typeof dt === \"object\" &&\n dt !== null &&\n Array.isArray((dt as Nested).columns)\n );\n}\n","import { Pattern, TagBase } from \"typia/lib/tags\";\nimport { tags } from \"typia\";\n\nexport type ClickHousePrecision<P extends number> = {\n _clickhouse_precision?: P;\n};\n\nexport const DecimalRegex: \"^-?\\\\d+(\\\\.\\\\d+)?$\" = \"^-?\\\\d+(\\\\.\\\\d+)?$\";\n\nexport type ClickHouseDecimal<P extends number, S extends number> = {\n _clickhouse_precision?: P;\n _clickhouse_scale?: S;\n} & Pattern<typeof DecimalRegex>;\n\nexport type ClickHouseFixedStringSize<N extends number> = {\n _clickhouse_fixed_string_size?: N;\n};\n\n/**\n * FixedString(N) - Fixed-length string of exactly N bytes.\n *\n * ClickHouse stores exactly N bytes, padding shorter values with null bytes.\n * Values exceeding N bytes will throw an exception.\n *\n * Use for binary data: hashes, IP addresses, UUIDs, MAC addresses.\n *\n * @example\n * interface BinaryData {\n * md5_hash: string & FixedString<16>; // 16-byte MD5\n * sha256_hash: string & FixedString<32>; // 32-byte SHA256\n * }\n */\nexport type FixedString<N extends number> = string &\n ClickHouseFixedStringSize<N>;\n\nexport type ClickHouseByteSize<N extends number> = {\n _clickhouse_byte_size?: N;\n};\n\nexport type LowCardinality = {\n _LowCardinality?: true;\n};\n\n// ClickHouse-friendly helper aliases for clarity in user schemas\n// These are erased at compile time but guide the ClickHouse mapping logic.\nexport type DateTime = Date;\nexport type DateTime64<P extends number> = Date & ClickHousePrecision<P>;\n\nexport type DateTimeString = string & tags.Format<\"date-time\">;\n/**\n * JS Date objects cannot hold microsecond precision.\n * Use string as the runtime type to avoid losing information.\n */\nexport type DateTime64String<P extends number> = string &\n tags.Format<\"date-time\"> &\n ClickHousePrecision<P>;\n\n// Numeric convenience tags mirroring ClickHouse integer and float families\nexport type Float32 = number & ClickHouseFloat<\"float32\">;\nexport type Float64 = number & ClickHouseFloat<\"float64\">;\n\nexport type Int8 = number & ClickHouseInt<\"int8\">;\nexport type Int16 = number & ClickHouseInt<\"int16\">;\nexport type Int32 = number & ClickHouseInt<\"int32\">;\nexport type Int64 = number & ClickHouseInt<\"int64\">;\n\nexport type UInt8 = number & ClickHouseInt<\"uint8\">;\nexport type UInt16 = number & ClickHouseInt<\"uint16\">;\nexport type UInt32 = number & ClickHouseInt<\"uint32\">;\nexport type UInt64 = number & ClickHouseInt<\"uint64\">;\n\n// Decimal(P, S) annotation\nexport type Decimal<P extends number, S extends number> = string &\n ClickHouseDecimal<P, S>;\n\n/**\n * Attach compression codec to a column type.\n *\n * Any valid ClickHouse codec expression is allowed. ClickHouse validates the codec at runtime.\n *\n * @template T The base data type\n * @template CodecExpr The codec expression (single codec or chain)\n *\n * @example\n * interface Metrics {\n * // Single codec\n * log_blob: string & ClickHouseCodec<\"ZSTD(3)\">;\n *\n * // Codec chain (processed left-to-right)\n * timestamp: Date & ClickHouseCodec<\"Delta, LZ4\">;\n * temperature: number & ClickHouseCodec<\"Gorilla, ZSTD\">;\n *\n * // Specialized codecs\n * counter: number & ClickHouseCodec<\"DoubleDelta\">;\n *\n * // Can combine with other annotations\n * count: UInt64 & ClickHouseCodec<\"DoubleDelta, LZ4\">;\n * }\n */\nexport type ClickHouseCodec<CodecExpr extends string> = {\n _clickhouse_codec?: CodecExpr;\n};\n\nexport type ClickHouseFloat<Value extends \"float32\" | \"float64\"> = tags.Type<\n Value extends \"float32\" ? \"float\" : \"double\"\n>;\n\nexport type ClickHouseInt<\n Value extends\n | \"int8\"\n | \"int16\"\n | \"int32\"\n | \"int64\"\n // | \"int128\"\n // | \"int256\"\n | \"uint8\"\n | \"uint16\"\n | \"uint32\"\n | \"uint64\",\n // | \"uint128\"\n // | \"uint256\",\n> =\n Value extends \"int32\" | \"int64\" | \"uint32\" | \"uint64\" ? tags.Type<Value>\n : TagBase<{\n target: \"number\";\n kind: \"type\";\n value: Value;\n validate: Value extends \"int8\" ? \"-128 <= $input && $input <= 127\"\n : Value extends \"int16\" ? \"-32768 <= $input && $input <= 32767\"\n : Value extends \"uint8\" ? \"0 <= $input && $input <= 255\"\n : Value extends \"uint16\" ? \"0 <= $input && $input <= 65535\"\n : never;\n exclusive: true;\n schema: {\n type: \"integer\";\n };\n }>;\n\n/**\n * By default, nested objects map to the `Nested` type in clickhouse.\n * Write `nestedObject: AnotherInterfaceType & ClickHouseNamedTuple`\n * to map AnotherInterfaceType to the named tuple type.\n */\nexport type ClickHouseNamedTuple = {\n _clickhouse_mapped_type?: \"namedTuple\";\n};\n\nexport type ClickHouseJson<\n maxDynamicPaths extends number | undefined = undefined,\n maxDynamicTypes extends number | undefined = undefined,\n skipPaths extends string[] = [],\n skipRegexes extends string[] = [],\n> = {\n _clickhouse_mapped_type?: \"JSON\";\n _clickhouse_json_settings?: {\n maxDynamicPaths?: maxDynamicPaths;\n maxDynamicTypes?: maxDynamicTypes;\n skipPaths?: skipPaths;\n skipRegexes?: skipRegexes;\n };\n};\n\n// Geometry helper types\nexport type ClickHousePoint = [number, number] & {\n _clickhouse_mapped_type?: \"Point\";\n};\nexport type ClickHouseRing = ClickHousePoint[] & {\n _clickhouse_mapped_type?: \"Ring\";\n};\nexport type ClickHouseLineString = ClickHousePoint[] & {\n _clickhouse_mapped_type?: \"LineString\";\n};\nexport type ClickHouseMultiLineString = ClickHouseLineString[] & {\n _clickhouse_mapped_type?: \"MultiLineString\";\n};\nexport type ClickHousePolygon = ClickHouseRing[] & {\n _clickhouse_mapped_type?: \"Polygon\";\n};\nexport type ClickHouseMultiPolygon = ClickHousePolygon[] & {\n _clickhouse_mapped_type?: \"MultiPolygon\";\n};\n\n/**\n * typia may have trouble handling this type.\n * In which case, use {@link WithDefault} as a workaround\n *\n * @example\n * { field: number & ClickHouseDefault<\"0\"> }\n */\nexport type ClickHouseDefault<SqlExpression extends string> = {\n _clickhouse_default?: SqlExpression;\n};\n\n/**\n * @example\n * {\n * ...\n * timestamp: Date;\n * debugMessage: string & ClickHouseTTL<\"timestamp + INTERVAL 1 WEEK\">;\n * }\n */\nexport type ClickHouseTTL<SqlExpression extends string> = {\n _clickhouse_ttl?: SqlExpression;\n};\n\n/**\n * ClickHouse MATERIALIZED column annotation.\n * The column value is computed at INSERT time and physically stored.\n * Cannot be explicitly inserted by users.\n *\n * @example\n * interface Events {\n * eventTime: DateTime;\n * // Extract date component - computed and stored at insert time\n * eventDate: Date & ClickHouseMaterialized<\"toDate(event_time)\">;\n *\n * userId: string;\n * // Precompute hash for fast lookups\n * userHash: UInt64 & ClickHouseMaterialized<\"cityHash64(userId)\">;\n * }\n *\n * @remarks\n * - MATERIALIZED and DEFAULT are mutually exclusive\n * - Can be combined with ClickHouseCodec for compression\n * - Changing the expression modifies the column in-place (existing values preserved)\n */\nexport type ClickHouseMaterialized<SqlExpression extends string> = {\n _clickhouse_materialized?: SqlExpression;\n};\n\n/**\n * ClickHouse ALIAS column annotation.\n * The column value is computed on-the-fly at SELECT time and NOT physically stored.\n * Cannot be explicitly inserted by users.\n *\n * @example\n * interface Events {\n * eventTime: DateTime;\n * // Computed at query time, not stored on disk\n * eventDate: Date & ClickHouseAlias<\"toDate(event_time)\">;\n *\n * firstName: string;\n * lastName: string;\n * // Virtual computed column\n * fullName: string & ClickHouseAlias<\"concat(first_name, ' ', last_name)\">;\n * }\n *\n * @remarks\n * - ALIAS, MATERIALIZED, and DEFAULT are mutually exclusive\n * - ALIAS columns are NOT stored on disk (saves storage, costs CPU at query time)\n * - Cannot be used in ORDER BY, PRIMARY KEY, or PARTITION BY\n * - Can be combined with ClickHouseCodec (though rarely useful since not stored)\n */\nexport type ClickHouseAlias<SqlExpression extends string> = {\n _clickhouse_alias?: SqlExpression;\n};\n\n/**\n * See also {@link ClickHouseDefault}\n *\n * @example{ updated_at: WithDefault<Date, \"now()\"> }\n */\nexport type WithDefault<T, _SqlExpression extends string> = T;\n\n/**\n * ClickHouse table engine types supported by Moose.\n */\nexport enum ClickHouseEngines {\n MergeTree = \"MergeTree\",\n ReplacingMergeTree = \"ReplacingMergeTree\",\n SummingMergeTree = \"SummingMergeTree\",\n AggregatingMergeTree = \"AggregatingMergeTree\",\n CollapsingMergeTree = \"CollapsingMergeTree\",\n VersionedCollapsingMergeTree = \"VersionedCollapsingMergeTree\",\n GraphiteMergeTree = \"GraphiteMergeTree\",\n S3Queue = \"S3Queue\",\n S3 = \"S3\",\n Buffer = \"Buffer\",\n Distributed = \"Distributed\",\n IcebergS3 = \"IcebergS3\",\n Kafka = \"Kafka\",\n Merge = \"Merge\",\n ReplicatedMergeTree = \"ReplicatedMergeTree\",\n ReplicatedReplacingMergeTree = \"ReplicatedReplacingMergeTree\",\n ReplicatedAggregatingMergeTree = \"ReplicatedAggregatingMergeTree\",\n ReplicatedSummingMergeTree = \"ReplicatedSummingMergeTree\",\n ReplicatedCollapsingMergeTree = \"ReplicatedCollapsingMergeTree\",\n ReplicatedVersionedCollapsingMergeTree = \"ReplicatedVersionedCollapsingMergeTree\",\n}\n","// source https://github.com/blakeembrey/sql-template-tag/blob/main/src/index.ts\nimport { Column } from \"./dataModels/dataModelTypes\";\nimport { OlapTable, View } from \"./dmv2\";\n\nimport { AggregationFunction } from \"./dataModels/typeConvert\";\n\n/**\n * Quote a ClickHouse identifier with backticks if not already quoted.\n * Backticks allow special characters (e.g., hyphens) in identifiers.\n */\nexport const quoteIdentifier = (name: string): string => {\n return name.startsWith(\"`\") && name.endsWith(\"`\") ? name : `\\`${name}\\``;\n};\n\nconst isTable = (\n value: RawValue | Column | OlapTable<any> | View,\n): value is OlapTable<any> =>\n typeof value === \"object\" &&\n value !== null &&\n \"kind\" in value &&\n value.kind === \"OlapTable\";\n\nconst isView = (\n value: RawValue | Column | OlapTable<any> | View,\n): value is View =>\n typeof value === \"object\" &&\n value !== null &&\n \"kind\" in value &&\n value.kind === \"View\";\n\nexport type IdentifierBrandedString = string & {\n readonly __identifier_brand?: unique symbol;\n};\nexport type NonIdentifierBrandedString = string & {\n readonly __identifier_brand?: unique symbol;\n};\n\n/**\n * Values supported by SQL engine.\n */\nexport type Value =\n | NonIdentifierBrandedString\n | number\n | boolean\n | Date\n | [string, string];\n\n/**\n * Supported value or SQL instance.\n */\nexport type RawValue = Value | Sql;\n\nconst isColumn = (\n value: RawValue | Column | OlapTable<any> | View,\n): value is Column =>\n typeof value === \"object\" &&\n value !== null &&\n !(\"kind\" in value) &&\n \"name\" in value &&\n \"annotations\" in value;\n\n/**\n * Sql template tag interface with attached helper methods.\n */\nexport interface SqlTemplateTag {\n /**\n * @deprecated Use `sql.statement` for full SQL statements or `sql.fragment` for SQL fragments.\n */\n (\n strings: readonly string[],\n ...values: readonly (RawValue | Column | OlapTable<any> | View)[]\n ): Sql;\n\n /**\n * Template literal tag for complete SQL statements (e.g. SELECT, INSERT, CREATE).\n * Produces a Sql instance with `isFragment = false`.\n */\n statement(\n strings: readonly string[],\n ...values: readonly (RawValue | Column | OlapTable<any> | View)[]\n ): Sql;\n\n /**\n * Template literal tag for SQL fragments (e.g. expressions, conditions, partial clauses).\n * Produces a Sql instance with `isFragment = true`.\n */\n fragment(\n strings: readonly string[],\n ...values: readonly (RawValue | Column | OlapTable<any> | View)[]\n ): Sql;\n\n /**\n * Join an array of Sql fragments with a separator.\n * @param fragments - Array of Sql fragments to join\n * @param separator - Optional separator string (defaults to \", \")\n */\n join(fragments: Sql[], separator?: string): Sql;\n\n /**\n * Create raw SQL from a string without parameterization.\n * WARNING: SQL injection risk if used with untrusted input.\n */\n raw(text: string): Sql;\n}\n\nfunction sqlImpl(\n strings: readonly string[],\n ...values: readonly (RawValue | Column | OlapTable<any> | View)[]\n): Sql {\n return new Sql(strings, values);\n}\n\nexport const sql: SqlTemplateTag = sqlImpl as SqlTemplateTag;\n\nsql.statement = function (\n strings: readonly string[],\n ...values: readonly (RawValue | Column | OlapTable<any> | View)[]\n): Sql {\n return new Sql(strings, values, false);\n};\n\nsql.fragment = function (\n strings: readonly string[],\n ...values: readonly (RawValue | Column | OlapTable<any> | View)[]\n): Sql {\n return new Sql(strings, values, true);\n};\n\nconst instanceofSql = (\n value: RawValue | Column | OlapTable<any> | View,\n): value is Sql =>\n typeof value === \"object\" && \"values\" in value && \"strings\" in value;\n\n/**\n * A SQL instance can be nested within each other to build SQL strings.\n */\nexport class Sql {\n readonly values: Value[];\n readonly strings: string[];\n readonly isFragment: boolean | undefined;\n\n constructor(\n rawStrings: readonly string[],\n rawValues: readonly (RawValue | Column | OlapTable<any> | View | Sql)[],\n isFragment?: boolean,\n ) {\n if (rawStrings.length - 1 !== rawValues.length) {\n if (rawStrings.length === 0) {\n throw new TypeError(\"Expected at least 1 string\");\n }\n\n throw new TypeError(\n `Expected ${rawStrings.length} strings to have ${\n rawStrings.length - 1\n } values`,\n );\n }\n\n const valuesLength = rawValues.reduce<number>(\n (len: number, value: RawValue | Column | OlapTable<any> | View | Sql) =>\n len +\n (instanceofSql(value) ? value.values.length\n : isColumn(value) || isTable(value) || isView(value) ? 0\n : 1),\n 0,\n );\n\n this.values = new Array(valuesLength);\n this.strings = new Array(valuesLength + 1);\n this.isFragment = isFragment;\n\n this.strings[0] = rawStrings[0];\n\n // Iterate over raw values, strings, and children. The value is always\n // positioned between two strings, e.g. `index + 1`.\n let i = 0,\n pos = 0;\n while (i < rawValues.length) {\n const child = rawValues[i++];\n const rawString = rawStrings[i];\n\n // Check for nested `sql` queries.\n if (instanceofSql(child)) {\n // Append child prefix text to current string.\n this.strings[pos] += child.strings[0];\n\n let childIndex = 0;\n while (childIndex < child.values.length) {\n this.values[pos++] = child.values[childIndex++];\n this.strings[pos] = child.strings[childIndex];\n }\n\n // Append raw string to current string.\n this.strings[pos] += rawString;\n } else if (isColumn(child)) {\n const aggregationFunction = child.annotations.find(\n ([k, _]) => k === \"aggregationFunction\",\n );\n if (aggregationFunction !== undefined) {\n const funcName = (aggregationFunction[1] as AggregationFunction)\n .functionName;\n const parenIdx = funcName.indexOf(\"(\");\n const mergedName =\n parenIdx !== -1 ?\n `${funcName.slice(0, parenIdx)}Merge${funcName.slice(parenIdx)}`\n : `${funcName}Merge`;\n this.strings[pos] += `${mergedName}(\\`${child.name}\\`)`;\n } else {\n this.strings[pos] += `\\`${child.name}\\``;\n }\n this.strings[pos] += rawString;\n } else if (isTable(child)) {\n if (child.config.database) {\n this.strings[pos] += `\\`${child.config.database}\\`.\\`${child.name}\\``;\n } else {\n this.strings[pos] += `\\`${child.name}\\``;\n }\n this.strings[pos] += rawString;\n } else if (isView(child)) {\n this.strings[pos] += `\\`${child.name}\\``;\n this.strings[pos] += rawString;\n } else {\n this.values[pos++] = child;\n this.strings[pos] = rawString;\n }\n }\n }\n\n /**\n * Append another Sql fragment, returning a new Sql instance.\n */\n append(other: Sql): Sql {\n return new Sql(\n [...this.strings, \"\"],\n [...this.values, other],\n this.isFragment,\n );\n }\n}\n\nsql.join = function (fragments: Sql[], separator?: string): Sql {\n if (fragments.length === 0) return new Sql([\"\"], [], true);\n if (fragments.length === 1) {\n const frag = fragments[0];\n return new Sql(frag.strings, frag.values, true);\n }\n const sep = separator ?? \", \";\n const normalized = sep.includes(\" \") ? sep : ` ${sep} `;\n const strings = [\"\", ...Array(fragments.length - 1).fill(normalized), \"\"];\n return new Sql(strings, fragments, true);\n};\n\nsql.raw = function (text: string): Sql {\n return new Sql([text], [], true);\n};\n\nexport const toStaticQuery = (sql: Sql): string => {\n const [query, params] = toQuery(sql);\n if (Object.keys(params).length !== 0) {\n throw new Error(\n \"Dynamic SQL is not allowed in the select statement in view creation.\",\n );\n }\n return query;\n};\n\nexport const toQuery = (sql: Sql): [string, { [pN: string]: any }] => {\n const parameterizedStubs = sql.values.map((v, i) =>\n createClickhouseParameter(i, v),\n );\n\n const query = sql.strings\n .map((s, i) =>\n s != \"\" ? `${s}${emptyIfUndefined(parameterizedStubs[i])}` : \"\",\n )\n .join(\"\");\n\n const query_params = sql.values.reduce(\n (acc: Record<string, unknown>, v, i) => ({\n ...acc,\n [`p${i}`]: getValueFromParameter(v),\n }),\n {},\n );\n return [query, query_params];\n};\n\n/**\n * Build a display-only SQL string with values inlined for logging/debugging.\n * Does not alter execution behavior; use toQuery for actual execution.\n */\nexport const toQueryPreview = (sql: Sql): string => {\n try {\n const formatValue = (v: Value): string => {\n // Unwrap identifiers: [\"Identifier\", name]\n if (Array.isArray(v)) {\n const [type, val] = v as unknown as [string, any];\n if (type === \"Identifier\") {\n // Quote identifiers with backticks like other helpers\n return `\\`${String(val)}\\``;\n }\n // Fallback for unexpected arrays\n return `[${(v as unknown as any[]).map((x) => formatValue(x as Value)).join(\", \")}]`;\n }\n if (v === null || v === undefined) return \"NULL\";\n if (typeof v === \"string\") return `'${v.replace(/'/g, \"''\")}'`;\n if (typeof v === \"number\") return String(v);\n if (typeof v === \"boolean\") return v ? \"true\" : \"false\";\n if (v instanceof Date)\n return `'${v.toISOString().replace(\"T\", \" \").slice(0, 19)}'`;\n try {\n return JSON.stringify(v as unknown as any);\n } catch {\n return String(v);\n }\n };\n\n let out = sql.strings[0] ?? \"\";\n for (let i = 0; i < sql.values.length; i++) {\n const val = getValueFromParameter(sql.values[i] as any);\n out += formatValue(val as Value);\n out += sql.strings[i + 1] ?? \"\";\n }\n return out.replace(/\\s+/g, \" \").trim();\n } catch (error) {\n console.log(`toQueryPreview error: ${error}`);\n return \"/* query preview unavailable */\";\n }\n};\n\nexport const getValueFromParameter = (value: any) => {\n if (Array.isArray(value)) {\n const [type, val] = value;\n if (type === \"Identifier\") return val;\n }\n return value;\n};\nexport function createClickhouseParameter(\n parameterIndex: number,\n value: Value,\n) {\n // ClickHouse use {name:type} be a placeholder, so if we only use number string as name e.g: {1:Unit8}\n // it will face issue when converting to the query params => {1: value1}, because the key is value not string type, so here add prefix \"p\" to avoid this issue.\n return `{p${parameterIndex}:${mapToClickHouseType(value)}}`;\n}\n\n/**\n * Convert the JS type (source is JSON format by API query parameter) to the corresponding ClickHouse type for generating named placeholder of parameterized query.\n * Only support to convert number to Int or Float, boolean to Bool, string to String, other types will convert to String.\n * If exist complex type e.g: object, Array, null, undefined, Date, Record.. etc, just convert to string type by ClickHouse function in SQL.\n * ClickHouse support converting string to other types function.\n * Please see Each section of the https://clickhouse.com/docs/en/sql-reference/functions and https://clickhouse.com/docs/en/sql-reference/functions/type-conversion-functions\n * @param value\n * @returns 'Float', 'Int', 'Bool', 'String'\n */\nexport const mapToClickHouseType = (value: Value) => {\n if (typeof value === \"number\") {\n // infer the float or int according to exist remainder or not\n return Number.isInteger(value) ? \"Int\" : \"Float\";\n }\n // When define column type or query result with parameterized query, The Bool or Boolean type both supported.\n // But the column type of query result only return Bool, so we only support Bool type for safety.\n if (typeof value === \"boolean\") return \"Bool\";\n if (value instanceof Date) return \"DateTime\";\n if (Array.isArray(value)) {\n const [type, _] = value;\n return type;\n }\n return \"String\";\n};\nfunction emptyIfUndefined(value: string | undefined): string {\n return value === undefined ? \"\" : value;\n}\n","import { IJsonSchemaCollection } from \"typia\";\nimport { TypedBase, TypiaValidators } from \"../typedBase\";\nimport {\n Column,\n isArrayNestedType,\n isNestedType,\n} from \"../../dataModels/dataModelTypes\";\nimport { ClickHouseEngines } from \"../../dataModels/types\";\nimport { getMooseInternal, isClientOnlyMode } from \"../internal\";\nimport { Readable } from \"node:stream\";\nimport { createHash } from \"node:crypto\";\nimport type {\n ConfigurationRegistry,\n RuntimeClickHouseConfig,\n} from \"../../config/runtime\";\nimport { LifeCycle } from \"./lifeCycle\";\nimport { IdentifierBrandedString, quoteIdentifier } from \"../../sqlHelpers\";\nimport type { NodeClickHouseClient } from \"@clickhouse/client/dist/client\";\n\nexport interface TableIndex {\n name: string;\n expression: string;\n type: string;\n arguments?: string[];\n granularity?: number;\n}\n\nexport interface TableProjection {\n name: string;\n body: string;\n}\n\n/**\n * Represents a failed record during insertion with error details\n */\nexport interface FailedRecord<T> {\n /** The original record that failed to insert */\n record: T;\n /** The error message describing why the insertion failed */\n error: string;\n /** Optional: The index of this record in the original batch */\n index?: number;\n}\n\n/**\n * Result of an insert operation with detailed success/failure information\n */\nexport interface InsertResult<T> {\n /** Number of records successfully inserted */\n successful: number;\n /** Number of records that failed to insert */\n failed: number;\n /** Total number of records processed */\n total: number;\n /** Detailed information about failed records (if record isolation was used) */\n failedRecords?: FailedRecord<T>[];\n}\n\n/**\n * Error handling strategy for insert operations\n */\nexport type ErrorStrategy =\n | \"fail-fast\" // Fail immediately on any error (default)\n | \"discard\" // Discard bad records and continue with good ones\n | \"isolate\"; // Retry individual records to isolate failures\n\n/**\n * Options for insert operations\n */\nexport interface InsertOptions {\n /** Maximum number of bad records to tolerate before failing */\n allowErrors?: number;\n /** Maximum ratio of bad records to tolerate (0.0 to 1.0) before failing */\n allowErrorsRatio?: number;\n /** Error handling strategy */\n strategy?: ErrorStrategy;\n /** Whether to enable dead letter queue for failed records (future feature) */\n deadLetterQueue?: boolean;\n /** Whether to validate data against schema before insertion (default: true) */\n validate?: boolean;\n /** Whether to skip validation for individual records during 'isolate' strategy retries (default: false) */\n skipValidationOnRetry?: boolean;\n}\n\n/**\n * Validation result for a record with detailed error information\n */\nexport interface ValidationError {\n /** The original record that failed validation */\n record: any;\n /** Detailed validation error message */\n error: string;\n /** Optional: The index of this record in the original batch */\n index?: number;\n /** The path to the field that failed validation */\n path?: string;\n}\n\n/**\n * Result of data validation with success/failure breakdown\n */\nexport interface ValidationResult<T> {\n /** Records that passed validation */\n valid: T[];\n /** Records that failed validation with detailed error information */\n invalid: ValidationError[];\n /** Total number of records processed */\n total: number;\n}\n\n/**\n * S3Queue-specific table settings that can be modified with ALTER TABLE MODIFY SETTING\n * Note: Since ClickHouse 24.7, settings no longer require the 's3queue_' prefix\n */\nexport interface S3QueueTableSettings {\n /** Processing mode: \"ordered\" for sequential or \"unordered\" for parallel processing */\n mode?: \"ordered\" | \"unordered\";\n /** What to do with files after processing: 'keep' or 'delete' */\n after_processing?: \"keep\" | \"delete\";\n /** ZooKeeper/Keeper path for coordination between replicas */\n keeper_path?: string;\n /** Number of retry attempts for failed files */\n loading_retries?: string;\n /** Number of threads for parallel processing */\n processing_threads_num?: string;\n /** Enable parallel inserts */\n parallel_inserts?: string;\n /** Enable logging to system.s3queue_log table */\n enable_logging_to_queue_log?: string;\n /** Last processed file path (for ordered mode) */\n last_processed_path?: string;\n /** Maximum number of tracked files in ZooKeeper */\n tracked_files_limit?: string;\n /** TTL for tracked files in seconds */\n tracked_file_ttl_sec?: string;\n /** Minimum polling timeout in milliseconds */\n polling_min_timeout_ms?: string;\n /** Maximum polling timeout in milliseconds */\n polling_max_timeout_ms?: string;\n /** Polling backoff in milliseconds */\n polling_backoff_ms?: string;\n /** Minimum cleanup interval in milliseconds */\n cleanup_interval_min_ms?: string;\n /** Maximum cleanup interval in milliseconds */\n cleanup_interval_max_ms?: string;\n /** Number of buckets for sharding (0 = disabled) */\n buckets?: string;\n /** Batch size for listing objects */\n list_objects_batch_size?: string;\n /** Enable hash ring filtering for distributed processing */\n enable_hash_ring_filtering?: string;\n /** Maximum files to process before committing */\n max_processed_files_before_commit?: string;\n /** Maximum rows to process before committing */\n max_processed_rows_before_commit?: string;\n /** Maximum bytes to process before committing */\n max_processed_bytes_before_commit?: string;\n /** Maximum processing time in seconds before committing */\n max_processing_time_sec_before_commit?: string;\n /** Use persistent processing nodes (available from 25.8) */\n use_persistent_processing_nodes?: string;\n /** TTL for persistent processing nodes in seconds */\n persistent_processing_nodes_ttl_seconds?: string;\n /** Additional settings */\n [key: string]: string | undefined;\n}\n\n/**\n * Base configuration shared by all table engines\n * @template T The data type of the records stored in the table.\n */\n\nexport type BaseOlapConfig<T> = (\n | {\n /**\n * Specifies the fields to use for ordering data within the ClickHouse table.\n * This is crucial for optimizing query performance.\n */\n orderByFields: (keyof T & string)[];\n orderByExpression?: undefined;\n }\n | {\n orderByFields?: undefined;\n /**\n * An arbitrary ClickHouse SQL expression for the order by clause.\n *\n * `orderByExpression: \"(id, name)\"` is equivalent to `orderByFields: [\"id\", \"name\"]`\n * `orderByExpression: \"tuple()\"` means no sorting\n */\n orderByExpression: string;\n }\n // specify either or leave both unspecified\n | { orderByFields?: undefined; orderByExpression?: undefined }\n) & {\n partitionBy?: string;\n /**\n * SAMPLE BY expression for approximate query processing.\n *\n * Examples:\n * ```typescript\n * // Single unsigned integer field\n * sampleByExpression: \"userId\"\n *\n * // Hash function on any field type\n * sampleByExpression: \"cityHash64(id)\"\n *\n * // Multiple fields with hash\n * sampleByExpression: \"cityHash64(userId, timestamp)\"\n * ```\n *\n * Requirements:\n * - Expression must evaluate to an unsigned integer (UInt8/16/32/64)\n * - Expression must be present in the ORDER BY clause\n * - If using hash functions, the same expression must appear in orderByExpression\n */\n sampleByExpression?: string;\n /**\n * Optional PRIMARY KEY expression.\n * When specified, this overrides the primary key inferred from Key<T> column annotations.\n *\n * This allows for:\n * - Complex primary keys using functions (e.g., \"cityHash64(id)\")\n * - Different column ordering in primary key vs schema definition\n * - Primary keys that differ from ORDER BY\n *\n * Example: primaryKeyExpression: \"(userId, cityHash64(eventId))\"\n *\n * Note: When this is set, any Key<T> annotations on columns are ignored for PRIMARY KEY generation.\n */\n primaryKeyExpression?: string;\n version?: string;\n lifeCycle?: LifeCycle;\n settings?: { [key: string]: string };\n /**\n * Optional TTL configuration for the table.\n * e.g., \"TTL timestamp + INTERVAL 90 DAY DELETE\"\n *\n * Use the {@link ClickHouseTTL} type to configure column level TTL\n */\n ttl?: string;\n /** Optional secondary/data-skipping indexes */\n indexes?: TableIndex[];\n /** Optional projections for alternative data ordering within parts */\n projections?: TableProjection[];\n /**\n * Optional database name for multi-database support.\n * When not specified, uses the global ClickHouse config database.\n */\n database?: string;\n /**\n * Optional cluster name for ON CLUSTER support.\n * Use this to enable replicated tables across ClickHouse clusters.\n * The cluster must be defined in config.toml (dev environment only).\n * Example: cluster: \"prod_cluster\"\n */\n cluster?: string;\n /**\n * Optional seed filter applied when `moose seed clickhouse` populates a\n * local/testing database from a remote source.\n *\n * Example:\n * ```typescript\n * seedFilter: { limit: 100, where: \"user_id = 10\" }\n * ```\n */\n seedFilter?: {\n /** Maximum number of rows to seed for this table. */\n limit?: number;\n /** ClickHouse SQL WHERE expression to filter seeded rows. */\n where?: string;\n };\n};\n\n/**\n * Configuration for MergeTree engine\n * @template T The data type of the records stored in the table.\n */\nexport type MergeTreeConfig<T> = BaseOlapConfig<T> & {\n engine: ClickHouseEngines.MergeTree;\n};\n\n/**\n * Configuration for ReplacingMergeTree engine (deduplication)\n * @template T The data type of the records stored in the table.\n */\nexport type ReplacingMergeTreeConfig<T> = BaseOlapConfig<T> & {\n engine: ClickHouseEngines.ReplacingMergeTree;\n ver?: keyof T & string; // Optional version column\n isDeleted?: keyof T & string; // Optional is_deleted column\n};\n\n/**\n * Configuration for AggregatingMergeTree engine\n * @template T The data type of the records stored in the table.\n */\nexport type AggregatingMergeTreeConfig<T> = BaseOlapConfig<T> & {\n engine: ClickHouseEngines.AggregatingMergeTree;\n};\n\n/**\n * Configuration for SummingMergeTree engine\n * @template T The data type of the records stored in the table.\n */\nexport type SummingMergeTreeConfig<T> = BaseOlapConfig<T> & {\n engine: ClickHouseEngines.SummingMergeTree;\n columns?: string[];\n};\n\n/**\n * Configuration for CollapsingMergeTree engine\n * @template T The data type of the records stored in the table.\n */\nexport type CollapsingMergeTreeConfig<T> = BaseOlapConfig<T> & {\n engine: ClickHouseEngines.CollapsingMergeTree;\n sign: keyof T & string; // Sign column (1 = state, -1 = cancel)\n};\n\n/**\n * Configuration for VersionedCollapsingMergeTree engine\n * @template T The data type of the records stored in the table.\n */\nexport type VersionedCollapsingMergeTreeConfig<T> = BaseOlapConfig<T> & {\n engine: ClickHouseEngines.VersionedCollapsingMergeTree;\n sign: keyof T & string; // Sign column (1 = state, -1 = cancel)\n ver: keyof T & string; // Version column for ordering state changes\n};\n\ninterface ReplicatedEngineProperties {\n keeperPath?: string;\n replicaName?: string;\n}\n\n/**\n * Configuration for ReplicatedMergeTree engine\n * @template T The data type of the records stored in the table.\n *\n * Note: keeperPath and replicaName are optional. Omit them for ClickHouse Cloud,\n * which manages replication automatically. For self-hosted with ClickHouse Keeper,\n * provide both parameters or neither (to use server defaults).\n */\nexport type ReplicatedMergeTreeConfig<T> = Omit<MergeTreeConfig<T>, \"engine\"> &\n ReplicatedEngineProperties & {\n engine: ClickHouseEngines.ReplicatedMergeTree;\n };\n\n/**\n * Configuration for ReplicatedReplacingMergeTree engine\n * @template T The data type of the records stored in the table.\n *\n * Note: keeperPath and replicaName are optional. Omit them for ClickHouse Cloud,\n * which manages replication automatically. For self-hosted with ClickHouse Keeper,\n * provide both parameters or neither (to use server defaults).\n */\nexport type ReplicatedReplacingMergeTreeConfig<T> = Omit<\n ReplacingMergeTreeConfig<T>,\n \"engine\"\n> &\n ReplicatedEngineProperties & {\n engine: ClickHouseEngines.ReplicatedReplacingMergeTree;\n };\n\n/**\n * Configuration for ReplicatedAggregatingMergeTree engine\n * @template T The data type of the records stored in the table.\n *\n * Note: keeperPath and replicaName are optional. Omit them for ClickHouse Cloud,\n * which manages replication automatically. For self-hosted with ClickHouse Keeper,\n * provide both parameters or neither (to use server defaults).\n */\nexport type ReplicatedAggregatingMergeTreeConfig<T> = Omit<\n AggregatingMergeTreeConfig<T>,\n \"engine\"\n> &\n ReplicatedEngineProperties & {\n engine: ClickHouseEngines.ReplicatedAggregatingMergeTree;\n };\n\n/**\n * Configuration for ReplicatedSummingMergeTree engine\n * @template T The data type of the records stored in the table.\n *\n * Note: keeperPath and replicaName are optional. Omit them for ClickHouse Cloud,\n * which manages replication automatically. For self-hosted with ClickHouse Keeper,\n * provide both parameters or neither (to use server defaults).\n */\nexport type ReplicatedSummingMergeTreeConfig<T> = Omit<\n SummingMergeTreeConfig<T>,\n \"engine\"\n> &\n ReplicatedEngineProperties & {\n engine: ClickHouseEngines.ReplicatedSummingMergeTree;\n };\n\n/**\n * Configuration for ReplicatedCollapsingMergeTree engine\n * @template T The data type of the records stored in the table.\n *\n * Note: keeperPath and replicaName are optional. Omit them for ClickHouse Cloud,\n * which manages replication automatically. For self-hosted with ClickHouse Keeper,\n * provide both parameters or neither (to use server defaults).\n */\nexport type ReplicatedCollapsingMergeTreeConfig<T> = Omit<\n CollapsingMergeTreeConfig<T>,\n \"engine\"\n> &\n ReplicatedEngineProperties & {\n engine: ClickHouseEngines.ReplicatedCollapsingMergeTree;\n };\n\n/**\n * Configuration for ReplicatedVersionedCollapsingMergeTree engine\n * @template T The data type of the records stored in the table.\n *\n * Note: keeperPath and replicaName are optional. Omit them for ClickHouse Cloud,\n * which manages replication automatically. For self-hosted with ClickHouse Keeper,\n * provide both parameters or neither (to use server defaults).\n */\nexport type ReplicatedVersionedCollapsingMergeTreeConfig<T> = Omit<\n VersionedCollapsingMergeTreeConfig<T>,\n \"engine\"\n> &\n ReplicatedEngineProperties & {\n engine: ClickHouseEngines.ReplicatedVersionedCollapsingMergeTree;\n };\n\n/**\n * Configuration for S3Queue engine - only non-alterable constructor parameters.\n * S3Queue-specific settings like 'mode', 'keeper_path', etc. should be specified\n * in the settings field, not here.\n * @template T The data type of the records stored in the table.\n */\nexport type S3QueueConfig<T> = Omit<\n BaseOlapConfig<T>,\n | \"settings\"\n | \"orderByFields\"\n | \"partitionBy\"\n | \"sampleByExpression\"\n | \"projections\"\n> & {\n engine: ClickHouseEngines.S3Queue;\n /** S3 bucket path with wildcards (e.g., 's3://bucket/data/*.json') */\n s3Path: string;\n /** Data format (e.g., 'JSONEachRow', 'CSV', 'Parquet') */\n format: string;\n /** AWS access key ID (optional, omit for NOSIGN/public buckets) */\n awsAccessKeyId?: string;\n /** AWS secret access key */\n awsSecretAccessKey?: string;\n /** Compression type (e.g., 'gzip', 'zstd') */\n compression?: string;\n /** Custom HTTP headers */\n headers?: { [key: string]: string };\n /**\n * S3Queue-specific table settings that can be modified with ALTER TABLE MODIFY SETTING.\n * These settings control the behavior of the S3Queue engine.\n */\n settings?: S3QueueTableSettings;\n};\n\n/**\n * Configuration for S3 engine\n * Note: S3 engine supports ORDER BY clause, unlike S3Queue, Buffer, and Distributed engines\n * @template T The data type of the records stored in the table.\n */\nexport type S3Config<T> = Omit<\n BaseOlapConfig<T>,\n \"sampleByExpression\" | \"projections\"\n> & {\n engine: ClickHouseEngines.S3;\n /** S3 path (e.g., 's3://bucket/path/file.json') */\n path: string;\n /** Data format (e.g., 'JSONEachRow', 'CSV', 'Parquet') */\n format: string;\n /** AWS access key ID (optional, omit for NOSIGN/public buckets) */\n awsAccessKeyId?: string;\n /** AWS secret access key */\n awsSecretAccessKey?: string;\n /** Compression type (e.g., 'gzip', 'zstd', 'auto') */\n compression?: string;\n /** Partition strategy (optional) */\n partitionStrategy?: string;\n /** Partition columns in data file (optional) */\n partitionColumnsInDataFile?: string;\n};\n\n/**\n * Configuration for Buffer engine\n * @template T The data type of the records stored in the table.\n */\nexport type BufferConfig<T> = Omit<\n BaseOlapConfig<T>,\n | \"orderByFields\"\n | \"orderByExpression\"\n | \"partitionBy\"\n | \"sampleByExpression\"\n | \"projections\"\n> & {\n engine: ClickHouseEngines.Buffer;\n /** Target database name for the destination table */\n targetDatabase: string;\n /** Target table name where data will be flushed */\n targetTable: string;\n /** Number of buffer layers (typically 16) */\n numLayers: number;\n /** Minimum time in seconds before flushing */\n minTime: number;\n /** Maximum time in seconds before flushing */\n maxTime: number;\n /** Minimum number of rows before flushing */\n minRows: number;\n /** Maximum number of rows before flushing */\n maxRows: number;\n /** Minimum bytes before flushing */\n minBytes: number;\n /** Maximum bytes before flushing */\n maxBytes: number;\n /** Optional: Flush time in seconds */\n flushTime?: number;\n /** Optional: Flush number of rows */\n flushRows?: number;\n /** Optional: Flush number of bytes */\n flushBytes?: number;\n};\n\n/**\n * Configuration for Distributed engine\n * @template T The data type of the records stored in the table.\n */\nexport type DistributedConfig<T> = Omit<\n BaseOlapConfig<T>,\n | \"orderByFields\"\n | \"orderByExpression\"\n | \"partitionBy\"\n | \"sampleByExpression\"\n | \"projections\"\n> & {\n engine: ClickHouseEngines.Distributed;\n /** Cluster name from the ClickHouse configuration */\n cluster: string;\n /** Database name on the cluster */\n targetDatabase: string;\n /** Table name on the cluster */\n targetTable: string;\n /** Optional: Sharding key expression for data distribution */\n shardingKey?: string;\n /** Optional: Policy name for data distribution */\n policyName?: string;\n};\n\n/** Kafka table settings. See: https://clickhouse.com/docs/engines/table-engines/integrations/kafka */\nexport interface KafkaTableSettings {\n kafka_security_protocol?: \"PLAINTEXT\" | \"SSL\" | \"SASL_PLAINTEXT\" | \"SASL_SSL\";\n kafka_sasl_mechanism?:\n | \"GSSAPI\"\n | \"PLAIN\"\n | \"SCRAM-SHA-256\"\n | \"SCRAM-SHA-512\"\n | \"OAUTHBEARER\";\n kafka_sasl_username?: string;\n kafka_sasl_password?: string;\n kafka_schema?: string;\n kafka_num_consumers?: string;\n kafka_max_block_size?: string;\n kafka_skip_broken_messages?: string;\n kafka_commit_every_batch?: string;\n kafka_client_id?: string;\n kafka_poll_timeout_ms?: string;\n kafka_poll_max_batch_size?: string;\n kafka_flush_interval_ms?: string;\n kafka_consumer_reschedule_ms?: string;\n kafka_thread_per_consumer?: string;\n kafka_handle_error_mode?: \"default\" | \"stream\";\n kafka_commit_on_select?: string;\n kafka_max_rows_per_message?: string;\n kafka_compression_codec?: string;\n kafka_compression_level?: string;\n}\n\n/** Kafka engine for streaming data from Kafka topics. Additional settings go in `settings`. */\nexport type KafkaConfig<T> = Omit<\n BaseOlapConfig<T>,\n | \"orderByFields\"\n | \"orderByExpression\"\n | \"partitionBy\"\n | \"sampleByExpression\"\n | \"projections\"\n> & {\n engine: ClickHouseEngines.Kafka;\n brokerList: string;\n topicList: string;\n groupName: string;\n format: string;\n settings?: KafkaTableSettings;\n};\n\n/**\n * Configuration for IcebergS3 engine - read-only Iceberg table access\n *\n * Provides direct querying of Apache Iceberg tables stored on S3.\n * Data is not copied; queries stream directly from Parquet/ORC files.\n *\n * @template T The data type of the records stored in the table.\n *\n * @example\n * ```typescript\n * const lakeEvents = new OlapTable<Event>(\"lake_events\", {\n * engine: ClickHouseEngines.IcebergS3,\n * path: \"s3://datalake/events/\",\n * format: \"Parquet\",\n * awsAccessKeyId: mooseRuntimeEnv.get(\"AWS_ACCESS_KEY_ID\"),\n * awsSecretAccessKey: mooseRuntimeEnv.get(\"AWS_SECRET_ACCESS_KEY\")\n * });\n * ```\n *\n * @remarks\n * - IcebergS3 engine is read-only\n * - Does not support ORDER BY, PARTITION BY, or SAMPLE BY clauses\n * - Queries always see the latest Iceberg snapshot (with metadata cache)\n */\nexport type IcebergS3Config<T> = Omit<\n BaseOlapConfig<T>,\n | \"orderByFields\"\n | \"orderByExpression\"\n | \"partitionBy\"\n | \"sampleByExpression\"\n | \"projections\"\n> & {\n engine: ClickHouseEngines.IcebergS3;\n /** S3 path to Iceberg table root (e.g., 's3://bucket/warehouse/events/') */\n path: string;\n /** Data format - 'Parquet' or 'ORC' */\n format: \"Parquet\" | \"ORC\";\n /** AWS access key ID (optional, omit for NOSIGN/public buckets) */\n awsAccessKeyId?: string;\n /** AWS secret access key (optional) */\n awsSecretAccessKey?: string;\n /** Compression type (optional: 'gzip', 'zstd', 'auto') */\n compression?: string;\n};\n\n/**\n * Configuration for Merge engine - read-only view over multiple tables matching a regex pattern.\n *\n * @template T The data type of the records in the source tables.\n *\n * @example\n * ```typescript\n * const allEvents = new OlapTable<Event>(\"all_events\", {\n * engine: ClickHouseEngines.Merge,\n * sourceDatabase: \"currentDatabase()\",\n * tablesRegexp: \"^events_\\\\d+$\",\n * });\n * ```\n *\n * @remarks\n * - Merge engine is read-only; INSERT operations are not supported\n * - Cannot be used as a destination in IngestPipeline\n * - Does not support ORDER BY, PARTITION BY, or SAMPLE BY clauses\n */\nexport type MergeConfig<T> = Omit<\n BaseOlapConfig<T>,\n | \"orderByFields\"\n | \"orderByExpression\"\n | \"partitionBy\"\n | \"sampleByExpression\"\n | \"projections\"\n> & {\n engine: ClickHouseEngines.Merge;\n /** Database to scan for source tables (literal name, currentDatabase(), or REGEXP(...)) */\n sourceDatabase: string;\n /** Regex pattern to match table names in the source database */\n tablesRegexp: string;\n};\n\n/**\n * Legacy configuration (backward compatibility) - defaults to MergeTree engine\n * @template T The data type of the records stored in the table.\n */\nexport type LegacyOlapConfig<T> = BaseOlapConfig<T>;\n\ntype EngineConfig<T> =\n | MergeTreeConfig<T>\n | ReplacingMergeTreeConfig<T>\n | AggregatingMergeTreeConfig<T>\n | SummingMergeTreeConfig<T>\n | CollapsingMergeTreeConfig<T>\n | VersionedCollapsingMergeTreeConfig<T>\n | ReplicatedMergeTreeConfig<T>\n | ReplicatedReplacingMergeTreeConfig<T>\n | ReplicatedAggregatingMergeTreeConfig<T>\n | ReplicatedSummingMergeTreeConfig<T>\n | ReplicatedCollapsingMergeTreeConfig<T>\n | ReplicatedVersionedCollapsingMergeTreeConfig<T>\n | S3QueueConfig<T>\n | S3Config<T>\n | BufferConfig<T>\n | DistributedConfig<T>\n | IcebergS3Config<T>\n | KafkaConfig<T>\n | MergeConfig<T>;\n\n/**\n * Union of all engine-specific configurations (new API)\n * @template T The data type of the records stored in the table.\n */\nexport type OlapConfig<T> = EngineConfig<T> | LegacyOlapConfig<T>;\n\n/**\n * Represents an OLAP (Online Analytical Processing) table, typically corresponding to a ClickHouse table.\n * Provides a typed interface for interacting with the table.\n *\n * @template T The data type of the records stored in the table. The structure of T defines the table schema.\n */\nexport class OlapTable<T> extends TypedBase<T, OlapConfig<T>> {\n name: IdentifierBrandedString;\n\n /** @internal */\n public readonly kind = \"OlapTable\";\n\n /** @internal Memoized ClickHouse client for reusing connections across insert calls */\n private _memoizedClient?: any;\n /** @internal Hash of the configuration used to create the memoized client */\n private _configHash?: string;\n /** @internal Cached table name to avoid repeated generation */\n private _cachedTableName?: string;\n\n /**\n * Creates a new OlapTable instance.\n * @param name The name of the table. This name is used for the underlying ClickHouse table.\n * @param config Optional configuration for the OLAP table.\n */\n constructor(name: string, config?: OlapConfig<T>);\n\n /** @internal **/\n constructor(\n name: string,\n config: OlapConfig<T>,\n schema: IJsonSchemaCollection.IV3_1,\n columns: Column[],\n validators?: TypiaValidators<T>,\n );\n\n constructor(\n name: string,\n config?: OlapConfig<T>,\n schema?: IJsonSchemaCollection.IV3_1,\n columns?: Column[],\n validators?: TypiaValidators<T>,\n ) {\n // Handle legacy configuration by defaulting to MergeTree when no engine is specified\n const resolvedConfig =\n config ?\n \"engine\" in config ?\n config\n : { ...config, engine: ClickHouseEngines.MergeTree }\n : { engine: ClickHouseEngines.MergeTree };\n\n // Enforce mutual exclusivity at runtime as well\n const hasFields =\n Array.isArray((resolvedConfig as any).orderByFields) &&\n (resolvedConfig as any).orderByFields.length > 0;\n const hasExpr =\n typeof (resolvedConfig as any).orderByExpression === \"string\" &&\n (resolvedConfig as any).orderByExpression.length > 0;\n if (hasFields && hasExpr) {\n throw new Error(\n `OlapTable ${name}: Provide either orderByFields or orderByExpression, not both.`,\n );\n }\n\n // Validate cluster and explicit replication params are not both specified\n const hasCluster = typeof (resolvedConfig as any).cluster === \"string\";\n const hasKeeperPath =\n typeof (resolvedConfig as any).keeperPath === \"string\";\n const hasReplicaName =\n typeof (resolvedConfig as any).replicaName === \"string\";\n\n if (hasCluster && (hasKeeperPath || hasReplicaName)) {\n throw new Error(\n `OlapTable ${name}: Cannot specify both 'cluster' and explicit replication params ('keeperPath' or 'replicaName'). ` +\n `Use 'cluster' for auto-injected params, or use explicit 'keeperPath' and 'replicaName' without 'cluster'.`,\n );\n }\n\n super(name, resolvedConfig, schema, columns, validators);\n this.name = name;\n\n const tables = getMooseInternal().tables;\n const registryKey =\n this.config.version ? `${name}_${this.config.version}` : name;\n // In client-only mode (MOOSE_CLIENT_ONLY=true), allow duplicate registrations\n // to support Next.js HMR which re-executes modules without clearing the registry\n if (!isClientOnlyMode() && tables.has(registryKey)) {\n throw new Error(\n `OlapTable with name ${name} and version ${config?.version ?? \"unversioned\"} already exists`,\n );\n }\n tables.set(registryKey, this);\n }\n\n /**\n * Generates the versioned table name following Moose's naming convention\n * Format: {tableName}_{version_with_dots_replaced_by_underscores}\n */\n private generateTableName(): string {\n // Cache the table name since version rarely changes\n if (this._cachedTableName) {\n return this._cachedTableName;\n }\n\n const tableVersion = this.config.version;\n if (!tableVersion) {\n this._cachedTableName = this.name;\n } else {\n const versionSuffix = tableVersion.replace(/\\./g, \"_\");\n this._cachedTableName = `${this.name}_${versionSuffix}`;\n }\n\n return this._cachedTableName;\n }\n\n /**\n * Creates a fast hash of the ClickHouse configuration.\n * Uses crypto.createHash for better performance than JSON.stringify.\n *\n * @private\n */\n private createConfigHash(clickhouseConfig: any): string {\n // Use per-table database if specified, otherwise fall back to global config\n const effectiveDatabase = this.config.database ?? clickhouseConfig.database;\n const configString = `${clickhouseConfig.host}:${clickhouseConfig.port}:${clickhouseConfig.username}:${clickhouseConfig.password}:${effectiveDatabase}:${clickhouseConfig.useSSL}`;\n return createHash(\"sha256\")\n .update(configString)\n .digest(\"hex\")\n .substring(0, 16);\n }\n\n /**\n * Gets or creates a memoized ClickHouse client.\n * The client is cached and reused across multiple insert calls for better performance.\n * If the configuration changes, a new client will be created.\n *\n * @private\n */\n private async getMemoizedClient(): Promise<{\n client: NodeClickHouseClient;\n config: RuntimeClickHouseConfig;\n }> {\n await import(\"../../config/runtime\");\n const configRegistry = (globalThis as any)\n ._mooseConfigRegistry as ConfigurationRegistry;\n const { getClickhouseClient } = await import(\"../../commons\");\n\n const clickhouseConfig = await configRegistry.getClickHouseConfig();\n const currentConfigHash = this.createConfigHash(clickhouseConfig);\n\n // If we have a cached client and the config hasn't changed, reuse it\n if (this._memoizedClient && this._configHash === currentConfigHash) {\n return { client: this._memoizedClient, config: clickhouseConfig };\n }\n\n // Close existing client if config changed\n if (this._memoizedClient && this._configHash !== currentConfigHash) {\n try {\n await this._memoizedClient.close();\n } catch (error) {\n // Ignore errors when closing old client\n }\n }\n\n // Create new client with standard configuration\n // Use per-table database if specified, otherwise fall back to global config\n const effectiveDatabase = this.config.database ?? clickhouseConfig.database;\n const client = getClickhouseClient({\n username: clickhouseConfig.username,\n password: clickhouseConfig.password,\n database: effectiveDatabase,\n useSSL: clickhouseConfig.useSSL ? \"true\" : \"false\",\n host: clickhouseConfig.host,\n port: clickhouseConfig.port,\n });\n\n // Cache the new client and config hash\n this._memoizedClient = client;\n this._configHash = currentConfigHash;\n\n return { client, config: clickhouseConfig };\n }\n\n /**\n * Closes the memoized ClickHouse client if it exists.\n * This is useful for cleaning up connections when the table instance is no longer needed.\n * The client will be automatically recreated on the next insert call if needed.\n */\n async closeClient(): Promise<void> {\n if (this._memoizedClient) {\n try {\n await this._memoizedClient.close();\n } catch (error) {\n // Ignore errors when closing\n } finally {\n this._memoizedClient = undefined;\n this._configHash = undefined;\n }\n }\n }\n\n /**\n * Validates a single record using typia's comprehensive type checking.\n * This provides the most accurate validation as it uses the exact TypeScript type information.\n *\n * @param record The record to validate\n * @returns Validation result with detailed error information\n */\n validateRecord(record: unknown): {\n success: boolean;\n data?: T;\n errors?: string[];\n } {\n // Use injected typia validator if available\n if (this.validators?.validate) {\n try {\n const result = this.validators.validate(record);\n return {\n success: result.success,\n data: result.data,\n errors: result.errors?.map((err) =>\n typeof err === \"string\" ? err : JSON.stringify(err),\n ),\n };\n } catch (error) {\n return {\n success: false,\n errors: [error instanceof Error ? error.message : String(error)],\n };\n }\n }\n\n throw new Error(\"No typia validator found\");\n }\n\n /**\n * Type guard function using typia's is() function.\n * Provides compile-time type narrowing for TypeScript.\n *\n * @param record The record to check\n * @returns True if record matches type T, with type narrowing\n */\n isValidRecord(record: unknown): record is T {\n if (this.validators?.is) {\n return this.validators.is(record);\n }\n\n throw new Error(\"No typia validator found\");\n }\n\n /**\n * Assert that a record matches type T, throwing detailed errors if not.\n * Uses typia's assert() function for the most detailed error reporting.\n *\n * @param record The record to assert\n * @returns The validated and typed record\n * @throws Detailed validation error if record doesn't match type T\n */\n assertValidRecord(record: unknown): T {\n if (this.validators?.assert) {\n return this.validators.assert(record);\n }\n\n throw new Error(\"No typia validator found\");\n }\n\n /**\n * Validates an array of records with comprehensive error reporting.\n * Uses the most appropriate validation method available (typia or basic).\n *\n * @param data Array of records to validate\n * @returns Detailed validation results\n */\n async validateRecords(data: unknown[]): Promise<ValidationResult<T>> {\n const valid: T[] = [];\n const invalid: ValidationError[] = [];\n\n // Pre-allocate arrays with estimated sizes to reduce reallocations\n valid.length = 0;\n invalid.length = 0;\n\n // Use for loop instead of forEach for better performance\n const dataLength = data.length;\n for (let i = 0; i < dataLength; i++) {\n const record = data[i];\n\n try {\n // Fast path: use typia's is() function first for type checking\n if (this.isValidRecord(record)) {\n valid.push(this.mapToClickhouseRecord(record));\n } else {\n // Only use expensive validateRecord for detailed errors when needed\n const result = this.validateRecord(record);\n if (result.success) {\n valid.push(this.mapToClickhouseRecord(record));\n } else {\n invalid.push({\n record,\n error: result.errors?.join(\", \") || \"Validation failed\",\n index: i,\n path: \"root\",\n });\n }\n }\n } catch (error) {\n invalid.push({\n record,\n error: error instanceof Error ? error.message : String(error),\n index: i,\n path: \"root\",\n });\n }\n }\n\n return {\n valid,\n invalid,\n total: dataLength,\n };\n }\n\n /**\n * Optimized batch retry that minimizes individual insert operations.\n * Groups records into smaller batches to reduce round trips while still isolating failures.\n *\n * @private\n */\n private async retryIndividualRecords(\n client: any,\n tableName: string,\n records: T[],\n ): Promise<{ successful: T[]; failed: FailedRecord<T>[] }> {\n const successful: T[] = [];\n const failed: FailedRecord<T>[] = [];\n\n // Instead of individual inserts, try smaller batches first (batches of 10)\n const RETRY_BATCH_SIZE = 10;\n const totalRecords = records.length;\n\n for (let i = 0; i < totalRecords; i += RETRY_BATCH_SIZE) {\n const batchEnd = Math.min(i + RETRY_BATCH_SIZE, totalRecords);\n const batch = records.slice(i, batchEnd);\n\n try {\n await client.insert({\n table: quoteIdentifier(tableName),\n values: batch,\n format: \"JSONEachRow\",\n clickhouse_settings: {\n date_time_input_format: \"best_effort\",\n // Add performance settings for retries\n max_insert_block_size: RETRY_BATCH_SIZE,\n max_block_size: RETRY_BATCH_SIZE,\n },\n });\n successful.push(...batch);\n } catch (batchError) {\n // If small batch fails, fall back to individual records\n for (let j = 0; j < batch.length; j++) {\n const record = batch[j];\n try {\n await client.insert({\n table: quoteIdentifier(tableName),\n values: [record],\n format: \"JSONEachRow\",\n clickhouse_settings: {\n date_time_input_format: \"best_effort\",\n },\n });\n successful.push(record);\n } catch (error) {\n failed.push({\n record,\n error: error instanceof Error ? error.message : String(error),\n index: i + j,\n });\n }\n }\n }\n }\n\n return { successful, failed };\n }\n\n /**\n * Validates input parameters and strategy compatibility\n * @private\n */\n private validateInsertParameters(\n data: T[] | Readable,\n options?: InsertOptions,\n ): { isStream: boolean; strategy: string; shouldValidate: boolean } {\n const isStream = data instanceof Readable;\n const strategy = options?.strategy || \"fail-fast\";\n const shouldValidate = options?.validate !== false;\n\n // Validate strategy compatibility with streams\n if (isStream && strategy === \"isolate\") {\n throw new Error(\n \"The 'isolate' error strategy is not supported with stream input. Use 'fail-fast' or 'discard' instead.\",\n );\n }\n\n // Validate that validation is not attempted on streams\n if (isStream && shouldValidate) {\n console.warn(\n \"Validation is not supported with stream input. Validation will be skipped.\",\n );\n }\n\n return { isStream, strategy, shouldValidate };\n }\n\n /**\n * Handles early return cases for empty data\n * @private\n */\n private handleEmptyData(\n data: T[] | Readable,\n isStream: boolean,\n ): InsertResult<T> | null {\n if (isStream && !data) {\n return {\n successful: 0,\n failed: 0,\n total: 0,\n };\n }\n\n if (!isStream && (!data || (data as T[]).length === 0)) {\n return {\n successful: 0,\n failed: 0,\n total: 0,\n };\n }\n\n return null;\n }\n\n /**\n * Performs pre-insertion validation for array data\n * @private\n */\n private async performPreInsertionValidation(\n data: T[],\n shouldValidate: boolean,\n strategy: string,\n options?: InsertOptions,\n ): Promise<{ validatedData: T[]; validationErrors: ValidationError[] }> {\n if (!shouldValidate) {\n return { validatedData: data, validationErrors: [] };\n }\n\n try {\n const validationResult = await this.validateRecords(data as unknown[]);\n const validatedData = validationResult.valid;\n const validationErrors = validationResult.invalid;\n\n if (validationErrors.length > 0) {\n this.handleValidationErrors(validationErrors, strategy, data, options);\n\n // Return appropriate data based on strategy\n switch (strategy) {\n case \"discard\":\n return { validatedData, validationErrors };\n case \"isolate\":\n return { validatedData: data, validationErrors };\n default:\n return { validatedData, validationErrors };\n }\n }\n\n return { validatedData, validationErrors };\n } catch (validationError) {\n if (strategy === \"fail-fast\") {\n throw validationError;\n }\n console.warn(\"Validation error:\", validationError);\n return { validatedData: data, validationErrors: [] };\n }\n }\n\n /**\n * Handles validation errors based on the specified strategy\n * @private\n */\n private handleValidationErrors(\n validationErrors: ValidationError[],\n strategy: string,\n data: T[],\n options?: InsertOptions,\n ): void {\n switch (strategy) {\n case \"fail-fast\":\n const firstError = validationErrors[0];\n throw new Error(\n `Validation failed for record at index ${firstError.index}: ${firstError.error}`,\n );\n\n case \"discard\":\n this.checkValidationThresholds(validationErrors, data.length, options);\n break;\n\n case \"isolate\":\n // For isolate strategy, validation errors will be handled in the final result\n break;\n }\n }\n\n /**\n * Checks if validation errors exceed configured thresholds\n * @private\n */\n private checkValidationThresholds(\n validationErrors: ValidationError[],\n totalRecords: number,\n options?: InsertOptions,\n ): void {\n const validationFailedCount = validationErrors.length;\n const validationFailedRatio = validationFailedCount / totalRecords;\n\n if (\n options?.allowErrors !== undefined &&\n validationFailedCount > options.allowErrors\n ) {\n throw new Error(\n `Too many validation failures: ${validationFailedCount} > ${options.allowErrors}. Errors: ${validationErrors.map((e) => e.error).join(\", \")}`,\n );\n }\n\n if (\n options?.allowErrorsRatio !== undefined &&\n validationFailedRatio > options.allowErrorsRatio\n ) {\n throw new Error(\n `Validation failure ratio too high: ${validationFailedRatio.toFixed(3)} > ${options.allowErrorsRatio}. Errors: ${validationErrors.map((e) => e.error).join(\", \")}`,\n );\n }\n }\n\n /**\n * Optimized insert options preparation with better memory management\n * @private\n */\n private prepareInsertOptions(\n tableName: string,\n data: T[] | Readable,\n validatedData: T[],\n isStream: boolean,\n strategy: string,\n options?: InsertOptions,\n ): any {\n const insertOptions: any = {\n table: quoteIdentifier(tableName),\n format: \"JSONEachRow\",\n clickhouse_settings: {\n date_time_input_format: \"best_effort\",\n wait_end_of_query: 1, // Ensure at least once delivery for INSERT operations\n // Performance optimizations\n max_insert_block_size:\n isStream ? 100000 : Math.min(validatedData.length, 100000),\n max_block_size: 65536,\n // Use async inserts for better performance with large datasets\n async_insert: validatedData.length > 1000 ? 1 : 0,\n wait_for_async_insert: 1, // For at least once delivery\n },\n };\n\n // Handle stream vs array input\n if (isStream) {\n insertOptions.values = data;\n } else {\n insertOptions.values = validatedData;\n }\n\n // For discard strategy, add optimized ClickHouse error tolerance settings\n if (\n strategy === \"discard\" &&\n (options?.allowErrors !== undefined ||\n options?.allowErrorsRatio !== undefined)\n ) {\n if (options.allowErrors !== undefined) {\n insertOptions.clickhouse_settings.input_format_allow_errors_num =\n options.allowErrors;\n }\n\n if (options.allowErrorsRatio !== undefined) {\n insertOptions.clickhouse_settings.input_format_allow_errors_ratio =\n options.allowErrorsRatio;\n }\n }\n\n return insertOptions;\n }\n\n /**\n * Creates success result for completed insertions\n * @private\n */\n private createSuccessResult(\n data: T[] | Readable,\n validatedData: T[],\n validationErrors: ValidationError[],\n isStream: boolean,\n shouldValidate: boolean,\n strategy: string,\n ): InsertResult<T> {\n if (isStream) {\n return {\n successful: -1, // -1 indicates stream mode where count is unknown\n failed: 0,\n total: -1,\n };\n }\n\n const insertedCount = validatedData.length;\n const totalProcessed =\n shouldValidate ? (data as T[]).length : insertedCount;\n\n const result: InsertResult<T> = {\n successful: insertedCount,\n failed: shouldValidate ? validationErrors.length : 0,\n total: totalProcessed,\n };\n\n // Add failed records if there are validation errors and using discard strategy\n if (\n shouldValidate &&\n validationErrors.length > 0 &&\n strategy === \"discard\"\n ) {\n result.failedRecords = validationErrors.map((ve) => ({\n record: ve.record as T,\n error: `Validation error: ${ve.error}`,\n index: ve.index,\n }));\n }\n\n return result;\n }\n\n /**\n * Handles insertion errors based on the specified strategy\n * @private\n */\n private async handleInsertionError(\n batchError: any,\n strategy: string,\n tableName: string,\n data: T[] | Readable,\n validatedData: T[],\n validationErrors: ValidationError[],\n isStream: boolean,\n shouldValidate: boolean,\n options?: InsertOptions,\n ): Promise<InsertResult<T>> {\n switch (strategy) {\n case \"fail-fast\":\n throw new Error(\n `Failed to insert data into table ${tableName}: ${batchError}`,\n );\n\n case \"discard\":\n throw new Error(\n `Too many errors during insert into table ${tableName}. Error threshold exceeded: ${batchError}`,\n );\n\n case \"isolate\":\n return await this.handleIsolateStrategy(\n batchError,\n tableName,\n data,\n validatedData,\n validationErrors,\n isStream,\n shouldValidate,\n options,\n );\n\n default:\n throw new Error(`Unknown error strategy: ${strategy}`);\n }\n }\n\n /**\n * Handles the isolate strategy for insertion errors\n * @private\n */\n private async handleIsolateStrategy(\n batchError: any,\n tableName: string,\n data: T[] | Readable,\n validatedData: T[],\n validationErrors: ValidationError[],\n isStream: boolean,\n shouldValidate: boolean,\n options?: InsertOptions,\n ): Promise<InsertResult<T>> {\n if (isStream) {\n throw new Error(\n `Isolate strategy is not supported with stream input: ${batchError}`,\n );\n }\n\n try {\n const { client } = await this.getMemoizedClient();\n const skipValidationOnRetry = options?.skipValidationOnRetry || false;\n const retryData = skipValidationOnRetry ? (data as T[]) : validatedData;\n\n const { successful, failed } = await this.retryIndividualRecords(\n client,\n tableName,\n retryData,\n );\n\n // Combine validation errors with insertion errors\n const allFailedRecords: FailedRecord<T>[] = [\n // Validation errors (if any and not skipping validation on retry)\n ...(shouldValidate && !skipValidationOnRetry ?\n validationErrors.map((ve) => ({\n record: ve.record as T,\n error: `Validation error: ${ve.error}`,\n index: ve.index,\n }))\n : []),\n // Insertion errors\n ...failed,\n ];\n\n this.checkInsertionThresholds(\n allFailedRecords,\n (data as T[]).length,\n options,\n );\n\n return {\n successful: successful.length,\n failed: allFailedRecords.length,\n total: (data as T[]).length,\n failedRecords: allFailedRecords,\n };\n } catch (isolationError) {\n throw new Error(\n `Failed to insert data into table ${tableName} during record isolation: ${isolationError}`,\n );\n }\n }\n\n /**\n * Checks if insertion errors exceed configured thresholds\n * @private\n */\n private checkInsertionThresholds(\n failedRecords: FailedRecord<T>[],\n totalRecords: number,\n options?: InsertOptions,\n ): void {\n const totalFailed = failedRecords.length;\n const failedRatio = totalFailed / totalRecords;\n\n if (\n options?.allowErrors !== undefined &&\n totalFailed > options.allowErrors\n ) {\n throw new Error(\n `Too many failed records: ${totalFailed} > ${options.allowErrors}. Failed records: ${failedRecords.map((f) => f.error).join(\", \")}`,\n );\n }\n\n if (\n options?.allowErrorsRatio !== undefined &&\n failedRatio > options.allowErrorsRatio\n ) {\n throw new Error(\n `Failed record ratio too high: ${failedRatio.toFixed(3)} > ${options.allowErrorsRatio}. Failed records: ${failedRecords.map((f) => f.error).join(\", \")}`,\n );\n }\n }\n\n /**\n * Recursively transforms a record to match ClickHouse's JSONEachRow requirements\n *\n * - For every Array(Nested(...)) field at any depth, each item is wrapped in its own array and recursively processed.\n * - For every Nested struct (not array), it recurses into the struct.\n * - This ensures compatibility with kafka_clickhouse_sync\n *\n * @param record The input record to transform (may be deeply nested)\n * @param columns The schema columns for this level (defaults to this.columnArray at the top level)\n * @returns The transformed record, ready for ClickHouse JSONEachRow insertion\n */\n private mapToClickhouseRecord(\n record: any,\n columns: Column[] = this.columnArray,\n ): any {\n const result = { ...record };\n for (const col of columns) {\n const value = record[col.name];\n const dt = col.data_type;\n\n if (isArrayNestedType(dt)) {\n // For Array(Nested(...)), wrap each item in its own array and recurse\n if (\n Array.isArray(value) &&\n (value.length === 0 || typeof value[0] === \"object\")\n ) {\n result[col.name] = value.map((item) => [\n this.mapToClickhouseRecord(item, dt.elementType.columns),\n ]);\n }\n } else if (isNestedType(dt)) {\n // For Nested struct (not array), recurse into it\n if (value && typeof value === \"object\") {\n result[col.name] = this.mapToClickhouseRecord(value, dt.columns);\n }\n }\n // All other types: leave as is for now\n }\n return result;\n }\n\n /**\n * Inserts data directly into the ClickHouse table with enhanced error handling and validation.\n * This method establishes a direct connection to ClickHouse using the project configuration\n * and inserts the provided data into the versioned table.\n *\n * PERFORMANCE OPTIMIZATIONS:\n * - Memoized client connections with fast config hashing\n * - Single-pass validation with pre-allocated arrays\n * - Batch-optimized retry strategy (batches of 10, then individual)\n * - Optimized ClickHouse settings for large datasets\n * - Reduced memory allocations and object creation\n *\n * Uses advanced typia validation when available for comprehensive type checking,\n * with fallback to basic validation for compatibility.\n *\n * The ClickHouse client is memoized and reused across multiple insert calls for better performance.\n * If the configuration changes, a new client will be automatically created.\n *\n * @param data Array of objects conforming to the table schema, or a Node.js Readable stream\n * @param options Optional configuration for error handling, validation, and insertion behavior\n * @returns Promise resolving to detailed insertion results\n * @throws {ConfigError} When configuration cannot be read or parsed\n * @throws {ClickHouseError} When insertion fails based on the error strategy\n * @throws {ValidationError} When validation fails and strategy is 'fail-fast'\n *\n * @example\n * ```typescript\n * // Create an OlapTable instance (typia validators auto-injected)\n * const userTable = new OlapTable<User>('users');\n *\n * // Insert with comprehensive typia validation\n * const result1 = await userTable.insert([\n * { id: 1, name: 'John', email: 'john@example.com' },\n * { id: 2, name: 'Jane', email: 'jane@example.com' }\n * ]);\n *\n * // Insert data with stream input (validation not available for streams)\n * const dataStream = new Readable({\n * objectMode: true,\n * read() { // Stream implementation }\n * });\n * const result2 = await userTable.insert(dataStream, { strategy: 'fail-fast' });\n *\n * // Insert with validation disabled for performance\n * const result3 = await userTable.insert(data, { validate: false });\n *\n * // Insert with error handling strategies\n * const result4 = await userTable.insert(mixedData, {\n * strategy: 'isolate',\n * allowErrorsRatio: 0.1,\n * validate: true // Use typia validation (default)\n * });\n *\n * // Optional: Clean up connection when completely done\n * await userTable.closeClient();\n * ```\n */\n async insert(\n data: T[] | Readable,\n options?: InsertOptions,\n ): Promise<InsertResult<T>> {\n // Validate input parameters and strategy compatibility\n const { isStream, strategy, shouldValidate } =\n this.validateInsertParameters(data, options);\n\n // Handle early return cases for empty data\n const emptyResult = this.handleEmptyData(data, isStream);\n if (emptyResult) {\n return emptyResult;\n }\n\n // Pre-insertion validation for arrays (optimized single-pass)\n let validatedData: T[] = [];\n let validationErrors: ValidationError[] = [];\n\n if (!isStream && shouldValidate) {\n const validationResult = await this.performPreInsertionValidation(\n data as T[],\n shouldValidate,\n strategy,\n options,\n );\n validatedData = validationResult.validatedData;\n validationErrors = validationResult.validationErrors;\n } else {\n // No validation or stream input\n validatedData = isStream ? [] : (data as T[]);\n }\n\n // Get memoized client and generate cached table name\n const { client } = await this.getMemoizedClient();\n const tableName = this.generateTableName();\n\n try {\n // Prepare and execute insertion with optimized settings\n const insertOptions = this.prepareInsertOptions(\n tableName,\n data,\n validatedData,\n isStream,\n strategy,\n options,\n );\n\n await client.insert(insertOptions);\n\n // Return success result\n return this.createSuccessResult(\n data,\n validatedData,\n validationErrors,\n isStream,\n shouldValidate,\n strategy,\n );\n } catch (batchError) {\n // Handle insertion failure based on strategy with optimized retry\n return await this.handleInsertionError(\n batchError,\n strategy,\n tableName,\n data,\n validatedData,\n validationErrors,\n isStream,\n shouldValidate,\n options,\n );\n }\n // Note: We don't close the client here since it's memoized for reuse\n // Use closeClient() method if you need to explicitly close the connection\n }\n\n // Note: Static factory methods (withS3Queue, withReplacingMergeTree, withMergeTree)\n // were removed in ENG-856. Use direct configuration instead, e.g.:\n // new OlapTable(name, { engine: ClickHouseEngines.ReplacingMergeTree, orderByFields: [\"id\"], ver: \"updated_at\" })\n}\n","/**\n * @fileoverview Stream SDK for data streaming operations in Moose.\n *\n * This module provides the core streaming functionality including:\n * - Stream creation and configuration\n * - Message transformations between streams\n * - Consumer registration for message processing\n * - Dead letter queue handling for error recovery\n *\n * @module Stream\n */\n\nimport { IJsonSchemaCollection } from \"typia\";\nimport { TypedBase } from \"../typedBase\";\nimport { Column } from \"../../dataModels/dataModelTypes\";\nimport { dlqColumns, dlqSchema, getMooseInternal } from \"../internal\";\nimport { OlapTable } from \"./olapTable\";\nimport { LifeCycle } from \"./lifeCycle\";\nimport type {\n RuntimeKafkaConfig,\n ConfigurationRegistry,\n} from \"../../config/runtime\";\nimport { createHash } from \"node:crypto\";\nimport { Logger, Producer } from \"../../commons\";\nimport { getSourceFileFromStack } from \"../utils/stackTrace\";\n\n/**\n * Represents zero, one, or many values of type T.\n * Used for flexible return types in transformations where a single input\n * can produce no output, one output, or multiple outputs.\n *\n * @template T The type of the value(s)\n * @example\n * ```typescript\n * // Can return a single value\n * const single: ZeroOrMany<string> = \"hello\";\n *\n * // Can return an array\n * const multiple: ZeroOrMany<string> = [\"hello\", \"world\"];\n *\n * // Can return null/undefined to filter out\n * const filtered: ZeroOrMany<string> = null;\n * ```\n */\nexport type ZeroOrMany<T> = T | T[] | undefined | null;\n\n/**\n * Function type for transforming records from one type to another.\n * Supports both synchronous and asynchronous transformations.\n *\n * @template T The input record type\n * @template U The output record type\n * @param record The input record to transform\n * @returns The transformed record(s), or null/undefined to filter out\n *\n * @example\n * ```typescript\n * const transform: SyncOrAsyncTransform<InputType, OutputType> = (record) => {\n * return { ...record, processed: true };\n * };\n * ```\n */\nexport type SyncOrAsyncTransform<T, U> = (\n record: T,\n) => ZeroOrMany<U> | Promise<ZeroOrMany<U>>;\n\n/**\n * Function type for consuming records without producing output.\n * Used for side effects like logging, external API calls, or database writes.\n *\n * @template T The record type to consume\n * @param record The record to process\n * @returns Promise<void> or void\n *\n * @example\n * ```typescript\n * const consumer: Consumer<UserEvent> = async (event) => {\n * await sendToAnalytics(event);\n * };\n * ```\n */\nexport type Consumer<T> = (record: T) => Promise<void> | void;\n\n/**\n * Configuration options for stream transformations.\n *\n * @template T The type of records being transformed\n */\nexport interface TransformConfig<T> {\n /**\n * Optional version identifier for this transformation.\n * Multiple transformations to the same destination can coexist with different versions.\n */\n version?: string;\n\n /**\n * Optional metadata for documentation and tracking purposes.\n */\n metadata?: { description?: string };\n\n /**\n * Optional dead letter queue for handling transformation failures.\n * Failed records will be sent to this queue for manual inspection or reprocessing.\n * Uses {@link Stream.defaultDeadLetterQueue} by default\n * unless a DeadLetterQueue is provided, or it is explicitly disabled with a null value\n */\n deadLetterQueue?: DeadLetterQueue<T> | null;\n\n /**\n * @internal Source file path where this transform was declared.\n * Automatically captured from stack trace.\n */\n sourceFile?: string;\n}\n\n/**\n * Configuration options for stream consumers.\n *\n * @template T The type of records being consumed\n */\nexport interface ConsumerConfig<T> {\n /**\n * Optional version identifier for this consumer.\n * Multiple consumers can coexist with different versions.\n */\n version?: string;\n\n /**\n * Optional dead letter queue for handling consumer failures.\n * Failed records will be sent to this queue for manual inspection or reprocessing.\n * Uses {@link Stream.defaultDeadLetterQueue} by default\n * unless a DeadLetterQueue is provided, or it is explicitly disabled with a null value\n */\n deadLetterQueue?: DeadLetterQueue<T> | null;\n\n /**\n * @internal Source file path where this consumer was declared.\n * Automatically captured from stack trace.\n */\n sourceFile?: string;\n}\n\nexport type SchemaRegistryEncoding = \"JSON\" | \"AVRO\" | \"PROTOBUF\";\n\nexport type SchemaRegistryReference =\n | { id: number }\n | { subjectLatest: string }\n | { subject: string; version: number };\n\nexport interface KafkaSchemaConfig {\n kind: SchemaRegistryEncoding;\n reference: SchemaRegistryReference;\n}\n\n/**\n * Represents a message routed to a specific destination stream.\n * Used internally by the multi-transform functionality to specify\n * where transformed messages should be sent.\n *\n * @internal\n */\nclass RoutedMessage {\n /** The destination stream for the message */\n destination: Stream<any>;\n\n /** The message value(s) to send */\n values: ZeroOrMany<any>;\n\n /**\n * Creates a new routed message.\n *\n * @param destination The target stream\n * @param values The message(s) to route\n */\n constructor(destination: Stream<any>, values: ZeroOrMany<any>) {\n this.destination = destination;\n this.values = values;\n }\n}\n\n/**\n * Configuration options for a data stream (e.g., a Redpanda topic).\n * @template T The data type of the messages in the stream.\n */\nexport interface StreamConfig<T> {\n /**\n * Specifies the number of partitions for the stream. Affects parallelism and throughput.\n */\n parallelism?: number;\n /**\n * Specifies the data retention period for the stream in seconds. Messages older than this may be deleted.\n */\n retentionPeriod?: number;\n /**\n * An optional destination OLAP table where messages from this stream should be automatically ingested.\n */\n destination?: OlapTable<T>;\n /**\n * An optional version string for this configuration. Can be used for tracking changes or managing deployments.\n */\n version?: string;\n metadata?: { description?: string };\n lifeCycle?: LifeCycle;\n\n defaultDeadLetterQueue?: DeadLetterQueue<T>;\n\n /** Optional Schema Registry configuration for this stream */\n schemaConfig?: KafkaSchemaConfig;\n}\n\n/**\n * Represents a data stream, typically corresponding to a Redpanda topic.\n * Provides a typed interface for producing to and consuming from the stream, and defining transformations.\n *\n * @template T The data type of the messages flowing through the stream. The structure of T defines the message schema.\n */\nexport class Stream<T> extends TypedBase<T, StreamConfig<T>> {\n defaultDeadLetterQueue?: DeadLetterQueue<T>;\n /** @internal Memoized KafkaJS producer for reusing connections across sends */\n private _memoizedProducer?: Producer;\n /** @internal Hash of the configuration used to create the memoized Kafka producer */\n private _kafkaConfigHash?: string;\n\n /**\n * Creates a new Stream instance.\n * @param name The name of the stream. This name is used for the underlying Redpanda topic.\n * @param config Optional configuration for the stream.\n */\n constructor(name: string, config?: StreamConfig<T>);\n\n /**\n * @internal\n * Note: `validators` parameter is a positional placeholder (always undefined for Stream).\n * It exists because TypedBase has validators as the 5th param, and we need to pass\n * allowExtraFields as the 6th param. Stream doesn't use validators.\n */\n constructor(\n name: string,\n config: StreamConfig<T>,\n schema: IJsonSchemaCollection.IV3_1,\n columns: Column[],\n validators: undefined,\n allowExtraFields: boolean,\n );\n\n constructor(\n name: string,\n config?: StreamConfig<T>,\n schema?: IJsonSchemaCollection.IV3_1,\n columns?: Column[],\n validators?: undefined,\n allowExtraFields?: boolean,\n ) {\n super(name, config ?? {}, schema, columns, undefined, allowExtraFields);\n const streams = getMooseInternal().streams;\n if (streams.has(name)) {\n throw new Error(`Stream with name ${name} already exists`);\n }\n streams.set(name, this);\n this.defaultDeadLetterQueue = this.config.defaultDeadLetterQueue;\n }\n\n /**\n * Internal map storing transformation configurations.\n * Maps destination stream names to arrays of transformation functions and their configs.\n *\n * @internal\n */\n _transformations = new Map<\n string,\n [Stream<any>, SyncOrAsyncTransform<T, any>, TransformConfig<T>][]\n >();\n\n /**\n * Internal function for multi-stream transformations.\n * Allows a single transformation to route messages to multiple destinations.\n *\n * @internal\n */\n _multipleTransformations?: (record: T) => [RoutedMessage];\n\n /**\n * Internal array storing consumer configurations.\n *\n * @internal\n */\n _consumers = new Array<{\n consumer: Consumer<T>;\n config: ConsumerConfig<T>;\n }>();\n\n /**\n * Builds the full Kafka topic name including optional namespace and version suffix.\n * Version suffix is appended as _x_y_z where dots in version are replaced with underscores.\n */\n private buildFullTopicName(namespace?: string): string {\n const versionSuffix =\n this.config.version ? `_${this.config.version.replace(/\\./g, \"_\")}` : \"\";\n const base = `${this.name}${versionSuffix}`;\n return namespace !== undefined && namespace.length > 0 ?\n `${namespace}.${base}`\n : base;\n }\n\n /**\n * Creates a fast hash string from relevant Kafka configuration fields.\n */\n private createConfigHash(kafkaConfig: RuntimeKafkaConfig): string {\n const configString = [\n kafkaConfig.broker,\n kafkaConfig.messageTimeoutMs,\n kafkaConfig.saslUsername,\n kafkaConfig.saslPassword,\n kafkaConfig.saslMechanism,\n kafkaConfig.securityProtocol,\n kafkaConfig.namespace,\n ].join(\":\");\n return createHash(\"sha256\")\n .update(configString)\n .digest(\"hex\")\n .substring(0, 16);\n }\n\n /**\n * Gets or creates a memoized KafkaJS producer using runtime configuration.\n */\n private async getMemoizedProducer(): Promise<{\n producer: Producer;\n kafkaConfig: RuntimeKafkaConfig;\n }> {\n // dynamic import to keep Stream objects browser compatible\n await import(\"../../config/runtime\");\n const configRegistry = (globalThis as any)\n ._mooseConfigRegistry as ConfigurationRegistry;\n const { getKafkaProducer } = await import(\"../../commons\");\n\n const kafkaConfig = await (configRegistry as any).getKafkaConfig();\n const currentHash = this.createConfigHash(kafkaConfig);\n\n if (this._memoizedProducer && this._kafkaConfigHash === currentHash) {\n return { producer: this._memoizedProducer, kafkaConfig };\n }\n\n // Close existing producer if config changed\n if (this._memoizedProducer && this._kafkaConfigHash !== currentHash) {\n try {\n await this._memoizedProducer.disconnect();\n } catch {\n // ignore\n }\n this._memoizedProducer = undefined;\n }\n\n const clientId = `moose-sdk-stream-${this.name}`;\n const logger: Logger = {\n logPrefix: clientId,\n log: (message: string): void => {\n console.log(`${clientId}: ${message}`);\n },\n error: (message: string): void => {\n console.error(`${clientId}: ${message}`);\n },\n warn: (message: string): void => {\n console.warn(`${clientId}: ${message}`);\n },\n };\n\n const producer = await getKafkaProducer(\n {\n clientId,\n broker: kafkaConfig.broker,\n securityProtocol: kafkaConfig.securityProtocol,\n saslUsername: kafkaConfig.saslUsername,\n saslPassword: kafkaConfig.saslPassword,\n saslMechanism: kafkaConfig.saslMechanism,\n },\n logger,\n );\n\n this._memoizedProducer = producer;\n this._kafkaConfigHash = currentHash;\n\n return { producer, kafkaConfig };\n }\n\n /**\n * Closes the memoized Kafka producer if it exists.\n */\n async closeProducer(): Promise<void> {\n if (this._memoizedProducer) {\n try {\n await this._memoizedProducer.disconnect();\n } catch {\n // ignore\n } finally {\n this._memoizedProducer = undefined;\n this._kafkaConfigHash = undefined;\n }\n }\n }\n\n /**\n * Sends one or more records to this stream's Kafka topic.\n * Values are JSON-serialized as message values.\n */\n async send(values: ZeroOrMany<T>): Promise<void> {\n // Normalize to flat array of records\n const flat: T[] =\n Array.isArray(values) ? values\n : values !== undefined && values !== null ? [values as T]\n : [];\n\n if (flat.length === 0) return;\n\n const { producer, kafkaConfig } = await this.getMemoizedProducer();\n const topic = this.buildFullTopicName(kafkaConfig.namespace);\n\n // Use Schema Registry JSON envelope if configured\n const sr = this.config.schemaConfig;\n if (sr && sr.kind === \"JSON\") {\n const schemaRegistryUrl = kafkaConfig.schemaRegistryUrl;\n if (!schemaRegistryUrl) {\n throw new Error(\"Schema Registry URL not configured\");\n }\n\n const {\n default: { SchemaRegistry },\n } = await import(\"@kafkajs/confluent-schema-registry\");\n const registry = new SchemaRegistry({ host: schemaRegistryUrl });\n\n let schemaId: undefined | number = undefined;\n\n if (\"id\" in sr.reference) {\n schemaId = sr.reference.id;\n } else if (\"subjectLatest\" in sr.reference) {\n schemaId = await registry.getLatestSchemaId(sr.reference.subjectLatest);\n } else if (\"subject\" in sr.reference) {\n schemaId = await registry.getRegistryId(\n sr.reference.subject,\n sr.reference.version,\n );\n }\n\n if (schemaId === undefined) {\n throw new Error(\"Malformed schema reference.\");\n }\n\n const encoded = await Promise.all(\n flat.map((v) =>\n registry.encode(schemaId, v as unknown as Record<string, unknown>),\n ),\n );\n await producer.send({\n topic,\n messages: encoded.map((value) => ({ value })),\n });\n return;\n } else if (sr !== undefined) {\n throw new Error(\"Currently only JSON Schema is supported.\");\n }\n\n await producer.send({\n topic,\n messages: flat.map((v) => ({ value: JSON.stringify(v) })),\n });\n }\n\n /**\n * Adds a transformation step that processes messages from this stream and sends the results to a destination stream.\n * Multiple transformations to the same destination stream can be added if they have distinct `version` identifiers in their config.\n *\n * @template U The data type of the messages in the destination stream.\n * @param destination The destination stream for the transformed messages.\n * @param transformation A function that takes a message of type T and returns zero or more messages of type U (or a Promise thereof).\n * Return `null` or `undefined` or an empty array `[]` to filter out a message. Return an array to emit multiple messages.\n * @param config Optional configuration for this specific transformation step, like a version.\n */\n addTransform<U>(\n destination: Stream<U>,\n transformation: SyncOrAsyncTransform<T, U>,\n config?: TransformConfig<T>,\n ) {\n // Capture source file from call stack at this exact moment\n const sourceFile = getSourceFileFromStack(new Error().stack);\n\n const transformConfig: TransformConfig<T> = {\n ...(config ?? {}),\n sourceFile,\n };\n if (transformConfig.deadLetterQueue === undefined) {\n transformConfig.deadLetterQueue = this.defaultDeadLetterQueue;\n }\n\n if (this._transformations.has(destination.name)) {\n const existingTransforms = this._transformations.get(destination.name)!;\n const hasVersion = existingTransforms.some(\n ([_, __, cfg]) => cfg.version === transformConfig.version,\n );\n\n if (!hasVersion) {\n existingTransforms.push([destination, transformation, transformConfig]);\n }\n } else {\n this._transformations.set(destination.name, [\n [destination, transformation, transformConfig],\n ]);\n }\n }\n\n /**\n * Adds a consumer function that processes messages from this stream.\n * Multiple consumers can be added if they have distinct `version` identifiers in their config.\n *\n * @param consumer A function that takes a message of type T and performs an action (e.g., side effect, logging). Should return void or Promise<void>.\n * @param config Optional configuration for this specific consumer, like a version.\n */\n addConsumer(consumer: Consumer<T>, config?: ConsumerConfig<T>) {\n // Capture source file from call stack at this exact moment\n const sourceFile = getSourceFileFromStack(new Error().stack);\n\n const consumerConfig: ConsumerConfig<T> = {\n ...(config ?? {}),\n sourceFile,\n };\n if (consumerConfig.deadLetterQueue === undefined) {\n consumerConfig.deadLetterQueue = this.defaultDeadLetterQueue;\n }\n const hasVersion = this._consumers.some(\n (existing) => existing.config.version === consumerConfig.version,\n );\n\n if (!hasVersion) {\n this._consumers.push({ consumer, config: consumerConfig });\n }\n }\n\n /**\n * Helper method for `addMultiTransform` to specify the destination and values for a routed message.\n * @param values The value or values to send to this stream.\n * @returns A `RoutedMessage` object associating the values with this stream.\n *\n * @example\n * ```typescript\n * sourceStream.addMultiTransform((record) => [\n * destinationStream1.routed(transformedRecord1),\n * destinationStream2.routed([record2a, record2b])\n * ]);\n * ```\n */\n routed = (values: ZeroOrMany<T>) => new RoutedMessage(this, values);\n\n /**\n * Adds a single transformation function that can route messages to multiple destination streams.\n * This is an alternative to adding multiple individual `addTransform` calls.\n * Only one multi-transform function can be added per stream.\n *\n * @param transformation A function that takes a message of type T and returns an array of `RoutedMessage` objects,\n * each specifying a destination stream and the message(s) to send to it.\n */\n addMultiTransform(transformation: (record: T) => [RoutedMessage]) {\n this._multipleTransformations = transformation;\n }\n}\n\n/**\n * Base model for dead letter queue entries.\n * Contains the original failed record along with error information.\n */\nexport interface DeadLetterModel {\n /** The original record that failed processing */\n originalRecord: Record<string, any>;\n\n /** Human-readable error message describing the failure */\n errorMessage: string;\n\n /** Classification of the error type (e.g., \"ValidationError\", \"TransformError\") */\n errorType: string;\n\n /** Timestamp when the failure occurred */\n failedAt: Date;\n\n /** The source component where the failure occurred */\n source: \"api\" | \"transform\" | \"table\";\n}\n\n/**\n * Enhanced dead letter model with type recovery functionality.\n * Extends the base model with the ability to recover the original typed record.\n *\n * @template T The original record type before failure\n */\nexport interface DeadLetter<T> extends DeadLetterModel {\n /**\n * Recovers the original record as its typed form.\n * Useful for reprocessing failed records with proper type safety.\n *\n * @returns The original record cast to type T\n */\n asTyped: () => T;\n}\n\n/**\n * Internal function to attach type guard functionality to dead letter records.\n *\n * @internal\n * @template T The original record type\n * @param dl The dead letter model to enhance\n * @param typeGuard Function to validate and cast the original record\n */\nfunction attachTypeGuard<T>(\n dl: DeadLetterModel,\n typeGuard: (input: any) => T,\n): asserts dl is DeadLetter<T> {\n (dl as any).asTyped = () => typeGuard(dl.originalRecord);\n}\n\n/**\n * Specialized stream for handling failed records (dead letters).\n * Provides type-safe access to failed records for reprocessing or analysis.\n *\n * @template T The original record type that failed processing\n *\n * @example\n * ```typescript\n * const dlq = new DeadLetterQueue<UserEvent>(\"user-events-dlq\");\n *\n * dlq.addConsumer(async (deadLetter) => {\n * const originalEvent = deadLetter.asTyped();\n * console.log(`Failed event: ${deadLetter.errorMessage}`);\n * // Potentially reprocess or alert\n * });\n * ```\n */\nexport class DeadLetterQueue<T> extends Stream<DeadLetterModel> {\n /**\n * Creates a new DeadLetterQueue instance.\n * @param name The name of the dead letter queue stream\n * @param config Optional configuration for the stream. The metadata property is always present and includes stackTrace.\n */\n constructor(name: string, config?: StreamConfig<DeadLetterModel>);\n\n /** @internal **/\n constructor(\n name: string,\n config: StreamConfig<DeadLetterModel>,\n validate: (originalRecord: any) => T,\n );\n\n constructor(\n name: string,\n config?: StreamConfig<DeadLetterModel>,\n typeGuard?: (originalRecord: any) => T,\n ) {\n if (typeGuard === undefined) {\n throw new Error(\n \"Supply the type param T so that the schema is inserted by the compiler plugin.\",\n );\n }\n\n super(name, config ?? {}, dlqSchema, dlqColumns, undefined, false);\n this.typeGuard = typeGuard;\n getMooseInternal().streams.set(name, this);\n }\n\n /**\n * Internal type guard function for validating and casting original records.\n *\n * @internal\n */\n private typeGuard: (originalRecord: any) => T;\n\n /**\n * Adds a transformation step for dead letter records.\n * The transformation function receives a DeadLetter<T> with type recovery capabilities.\n *\n * @template U The output type for the transformation\n * @param destination The destination stream for transformed messages\n * @param transformation Function to transform dead letter records\n * @param config Optional transformation configuration\n */\n addTransform<U>(\n destination: Stream<U>,\n transformation: SyncOrAsyncTransform<DeadLetter<T>, U>,\n config?: TransformConfig<DeadLetterModel>,\n ) {\n const withValidate: SyncOrAsyncTransform<DeadLetterModel, U> = (\n deadLetter,\n ) => {\n attachTypeGuard<T>(deadLetter, this.typeGuard);\n return transformation(deadLetter);\n };\n super.addTransform(destination, withValidate, config);\n }\n\n /**\n * Adds a consumer for dead letter records.\n * The consumer function receives a DeadLetter<T> with type recovery capabilities.\n *\n * @param consumer Function to process dead letter records\n * @param config Optional consumer configuration\n */\n addConsumer(\n consumer: Consumer<DeadLetter<T>>,\n config?: ConsumerConfig<DeadLetterModel>,\n ) {\n const withValidate: Consumer<DeadLetterModel> = (deadLetter) => {\n attachTypeGuard<T>(deadLetter, this.typeGuard);\n return consumer(deadLetter);\n };\n super.addConsumer(withValidate, config);\n }\n\n /**\n * Adds a multi-stream transformation for dead letter records.\n * The transformation function receives a DeadLetter<T> with type recovery capabilities.\n *\n * @param transformation Function to route dead letter records to multiple destinations\n */\n addMultiTransform(\n transformation: (record: DeadLetter<T>) => [RoutedMessage],\n ) {\n const withValidate: (record: DeadLetterModel) => [RoutedMessage] = (\n deadLetter,\n ) => {\n attachTypeGuard<T>(deadLetter, this.typeGuard);\n return transformation(deadLetter);\n };\n super.addMultiTransform(withValidate);\n }\n}\n","import { getMooseInternal } from \"../internal\";\nimport { getSourceLocationFromStack } from \"../utils/stackTrace\";\n\n/**\n * Context passed to task handlers. Single param to future-proof API changes.\n *\n * - state: shared mutable state for the task and its lifecycle hooks\n * - input: optional typed input for the task (undefined when task has no input)\n */\n/**\n * Task handler context. If the task declares an input type (T != null),\n * `input` is required and strongly typed. For no-input tasks (T = null),\n * `input` is omitted/optional.\n */\nexport type TaskContext<TInput> =\n TInput extends null ? { state: any; input?: null }\n : { state: any; input: TInput };\n\n/**\n * Configuration options for defining a task within a workflow.\n *\n * @template T - The input type for the task\n * @template R - The return type for the task\n */\nexport interface TaskConfig<T, R> {\n /** The main function that executes the task logic */\n run: (context: TaskContext<T>) => Promise<R>;\n\n /**\n * Optional array of tasks to execute after this task completes successfully.\n * Supports all combinations of input types (real type or null) and output types (real type or void).\n * When this task returns void, onComplete tasks expect null as input.\n * When this task returns a real type, onComplete tasks expect that type as input.\n */\n onComplete?: (\n | Task<R extends void ? null : R, any>\n | Task<R extends void ? null : R, void>\n )[];\n\n /**\n * Optional function that is called when the task is cancelled.\n */\n /** Optional function that is called when the task is cancelled. */\n onCancel?: (context: TaskContext<T>) => Promise<void>;\n\n /** Optional timeout duration for the task execution (e.g., \"30s\", \"5m\") */\n timeout?: string;\n\n /** Optional number of retry attempts if the task fails */\n retries?: number;\n}\n\n/**\n * Represents a single task within a workflow system.\n *\n * A Task encapsulates the execution logic, completion handlers, and configuration\n * for a unit of work that can be chained with other tasks in a workflow.\n *\n * @template T - The input type that this task expects\n * @template R - The return type that this task produces\n */\nexport class Task<T, R> {\n /**\n * Creates a new Task instance.\n *\n * @param name - Unique identifier for the task\n * @param config - Configuration object defining the task behavior\n *\n * @example\n * ```typescript\n * // No input, no output\n * const task1 = new Task<null, void>(\"task1\", {\n * run: async () => {\n * console.log(\"No input/output\");\n * }\n * });\n *\n * // No input, but has output\n * const task2 = new Task<null, OutputType>(\"task2\", {\n * run: async () => {\n * return someOutput;\n * }\n * });\n *\n * // Has input, no output\n * const task3 = new Task<InputType, void>(\"task3\", {\n * run: async (input: InputType) => {\n * // process input but return nothing\n * }\n * });\n *\n * // Has both input and output\n * const task4 = new Task<InputType, OutputType>(\"task4\", {\n * run: async (input: InputType) => {\n * return process(input);\n * }\n * });\n * ```\n */\n constructor(\n readonly name: string,\n readonly config: TaskConfig<T, R>,\n ) {}\n}\n\n/**\n * Configuration options for defining a workflow.\n *\n * A workflow orchestrates the execution of multiple tasks in a defined sequence\n * or pattern, with support for scheduling, retries, and timeouts.\n */\nexport interface WorkflowConfig {\n /**\n * The initial task that begins the workflow execution.\n * Supports all combinations of input types (real type or null) and output types (real type or void):\n * - Task<null, OutputType>: No input, returns a type\n * - Task<null, void>: No input, returns nothing\n * - Task<InputType, OutputType>: Has input, returns a type\n * - Task<InputType, void>: Has input, returns nothing\n */\n startingTask:\n | Task<null, any>\n | Task<null, void>\n | Task<any, any>\n | Task<any, void>;\n\n /** Optional number of retry attempts if the entire workflow fails */\n retries?: number;\n\n /** Optional timeout duration for the entire workflow execution (e.g., \"10m\", \"1h\") */\n timeout?: string;\n\n /** Optional cron-style schedule string for automated workflow execution */\n schedule?: string;\n}\n\n/**\n * Represents a complete workflow composed of interconnected tasks.\n *\n * A Workflow manages the execution flow of multiple tasks, handling scheduling,\n * error recovery, and task orchestration. Once created, workflows are automatically\n * registered with the internal Moose system.\n *\n * @example\n * ```typescript\n * const dataProcessingWorkflow = new Workflow(\"dataProcessing\", {\n * startingTask: extractDataTask,\n * schedule: \"0 2 * * *\", // Run daily at 2 AM\n * timeout: \"1h\",\n * retries: 2\n * });\n * ```\n */\nexport class Workflow {\n /** @internal Source file path where this workflow was declared */\n sourceFile?: string;\n\n /** @internal Source line number where this workflow was declared */\n sourceLine?: number;\n\n /** @internal Source column number where this workflow was declared */\n sourceColumn?: number;\n\n /**\n * Creates a new Workflow instance and registers it with the Moose system.\n *\n * @param name - Unique identifier for the workflow\n * @param config - Configuration object defining the workflow behavior and task orchestration\n * @throws {Error} When the workflow contains null/undefined tasks or infinite loops\n */\n constructor(\n readonly name: string,\n readonly config: WorkflowConfig,\n ) {\n const stack = new Error().stack;\n const location = getSourceLocationFromStack(stack);\n if (location) {\n this.sourceFile = location.file;\n this.sourceLine = location.line;\n this.sourceColumn = location.column;\n }\n\n const workflows = getMooseInternal().workflows;\n if (workflows.has(name)) {\n throw new Error(`Workflow with name ${name} already exists`);\n }\n this.validateTaskGraph(config.startingTask, name);\n workflows.set(name, this);\n }\n\n /**\n * Validates the task graph to ensure there are no null tasks or infinite loops.\n *\n * @private\n * @param startingTask - The starting task to begin validation from\n * @param workflowName - The name of the workflow being validated (for error messages)\n * @throws {Error} When null/undefined tasks are found or infinite loops are detected\n */\n private validateTaskGraph(\n startingTask: Task<any, any> | null | undefined,\n workflowName: string,\n ): void {\n if (startingTask === null || startingTask === undefined) {\n throw new Error(\n `Workflow \"${workflowName}\" has a null or undefined starting task`,\n );\n }\n\n const visited = new Set<string>();\n const recursionStack = new Set<string>();\n\n const validateTask = (\n task: Task<any, any> | null | undefined,\n currentPath: string[],\n ): void => {\n if (task === null || task === undefined) {\n const pathStr =\n currentPath.length > 0 ? currentPath.join(\" -> \") + \" -> \" : \"\";\n throw new Error(\n `Workflow \"${workflowName}\" contains a null or undefined task in the task chain: ${pathStr}null`,\n );\n }\n\n const taskName = task.name;\n\n if (recursionStack.has(taskName)) {\n const cycleStartIndex = currentPath.indexOf(taskName);\n const cyclePath =\n cycleStartIndex >= 0 ?\n currentPath.slice(cycleStartIndex).concat(taskName)\n : currentPath.concat(taskName);\n throw new Error(\n `Workflow \"${workflowName}\" contains an infinite loop in task chain: ${cyclePath.join(\" -> \")}`,\n );\n }\n\n if (visited.has(taskName)) {\n // Already processed this task and its children\n return;\n }\n\n visited.add(taskName);\n recursionStack.add(taskName);\n\n if (task.config.onComplete) {\n for (const nextTask of task.config.onComplete) {\n validateTask(nextTask, [...currentPath, taskName]);\n }\n }\n\n recursionStack.delete(taskName);\n };\n\n validateTask(startingTask, []);\n }\n}\n","import { IJsonSchemaCollection } from \"typia\";\nimport { TypedBase } from \"../typedBase\";\nimport { Column } from \"../../dataModels/dataModelTypes\";\nimport { getMooseInternal } from \"../internal\";\nimport { DeadLetterQueue, Stream } from \"./stream\";\n\n/**\n * @template T The data type of the messages expected by the destination stream.\n */\nexport interface IngestConfig<T> {\n /**\n * The destination stream where the ingested data should be sent.\n */\n destination: Stream<T>;\n\n deadLetterQueue?: DeadLetterQueue<T>;\n /**\n * An optional version string for this configuration.\n */\n version?: string;\n /**\n * An optional custom path for the ingestion endpoint.\n */\n path?: string;\n metadata?: { description?: string };\n}\n\n/**\n * Represents an Ingest API endpoint, used for sending data into a Moose system, typically writing to a Stream.\n * Provides a typed interface for the expected data format.\n *\n * @template T The data type of the records that this API endpoint accepts. The structure of T defines the expected request body schema.\n */\nexport class IngestApi<T> extends TypedBase<T, IngestConfig<T>> {\n /**\n * Creates a new IngestApi instance.\n * @param name The name of the ingest API endpoint.\n * @param config Optional configuration for the ingest API.\n */\n constructor(name: string, config?: IngestConfig<T>);\n\n /**\n * @internal\n * Note: `validators` parameter is a positional placeholder (always undefined for IngestApi).\n * It exists because TypedBase has validators as the 5th param, and we need to pass\n * allowExtraFields as the 6th param. IngestApi doesn't use validators.\n */\n constructor(\n name: string,\n config: IngestConfig<T>,\n schema: IJsonSchemaCollection.IV3_1,\n columns: Column[],\n validators: undefined,\n allowExtraFields: boolean,\n );\n\n constructor(\n name: string,\n config: IngestConfig<T>,\n schema?: IJsonSchemaCollection.IV3_1,\n columns?: Column[],\n validators?: undefined,\n allowExtraFields?: boolean,\n ) {\n super(name, config, schema, columns, undefined, allowExtraFields);\n const ingestApis = getMooseInternal().ingestApis;\n if (ingestApis.has(name)) {\n throw new Error(`Ingest API with name ${name} already exists`);\n }\n ingestApis.set(name, this);\n }\n}\n","import { IJsonSchemaCollection } from \"typia\";\nimport { TypedBase } from \"../typedBase\";\nimport { Column } from \"../../dataModels/dataModelTypes\";\nimport { getMooseInternal } from \"../internal\";\nimport type { ApiUtil } from \"../../consumption-apis/helpers\";\n\n/**\n * Defines the signature for a handler function used by a Consumption API.\n * @template T The expected type of the request parameters or query parameters.\n * @template R The expected type of the response data.\n * @param params An object containing the validated request parameters, matching the structure of T.\n * @param utils Utility functions provided to the handler, e.g., for database access (`runSql`).\n * @returns A Promise resolving to the response data of type R.\n */\ntype ApiHandler<T, R> = (params: T, utils: ApiUtil) => Promise<R>;\n\n/**\n * @template T The data type of the request parameters.\n */\nexport interface ApiConfig<T> {\n /**\n * An optional version string for this configuration.\n */\n version?: string;\n /**\n * An optional custom path for the API endpoint.\n * If not specified, defaults to the API name.\n */\n path?: string;\n metadata?: { description?: string };\n}\n\n/**\n * Represents a Consumption API endpoint (API), used for querying data from a Moose system.\n * Exposes data, often from an OlapTable or derived through a custom handler function.\n *\n * @template T The data type defining the expected structure of the API's query parameters.\n * @template R The data type defining the expected structure of the API's response body. Defaults to `any`.\n */\nexport class Api<T, R = any> extends TypedBase<T, ApiConfig<T>> {\n /** @internal The handler function that processes requests and generates responses. */\n _handler: ApiHandler<T, R>;\n /** @internal The JSON schema definition for the response type R. */\n responseSchema: IJsonSchemaCollection.IV3_1;\n\n /**\n * Creates a new Api instance.\n * @param name The name of the consumption API endpoint.\n * @param handler The function to execute when the endpoint is called. It receives validated query parameters and utility functions.\n * @param config Optional configuration for the consumption API.\n */\n constructor(name: string, handler: ApiHandler<T, R>, config?: {});\n\n /** @internal **/\n constructor(\n name: string,\n handler: ApiHandler<T, R>,\n config: ApiConfig<T>,\n schema: IJsonSchemaCollection.IV3_1,\n columns: Column[],\n responseSchema: IJsonSchemaCollection.IV3_1,\n );\n\n constructor(\n name: string,\n handler: ApiHandler<T, R>,\n config?: ApiConfig<T>,\n schema?: IJsonSchemaCollection.IV3_1,\n columns?: Column[],\n responseSchema?: IJsonSchemaCollection.IV3_1,\n ) {\n super(name, config ?? {}, schema, columns);\n this._handler = handler;\n this.responseSchema = responseSchema ?? {\n version: \"3.1\",\n schemas: [{ type: \"array\", items: { type: \"object\" } }],\n components: { schemas: {} },\n };\n const apis = getMooseInternal().apis;\n const key = `${name}${config?.version ? `:${config.version}` : \"\"}`;\n if (apis.has(key)) {\n throw new Error(\n `Consumption API with name ${name} and version ${config?.version} already exists`,\n );\n }\n apis.set(key, this);\n\n // Also register by custom path if provided\n if (config?.path) {\n if (config.version) {\n // Check if the path already ends with the version\n const pathEndsWithVersion =\n config.path.endsWith(`/${config.version}`) ||\n config.path === config.version ||\n (config.path.endsWith(config.version) &&\n config.path.length > config.version.length &&\n config.path[config.path.length - config.version.length - 1] ===\n \"/\");\n\n if (pathEndsWithVersion) {\n // Path already contains version, register as-is\n if (apis.has(config.path)) {\n const existing = apis.get(config.path)!;\n throw new Error(\n `Cannot register API \"${name}\" with path \"${config.path}\" - this path is already used by API \"${existing.name}\"`,\n );\n }\n apis.set(config.path, this);\n } else {\n // Path doesn't contain version, register with version appended\n const versionedPath = `${config.path.replace(/\\/$/, \"\")}/${config.version}`;\n\n // Check for collision on versioned path\n if (apis.has(versionedPath)) {\n const existing = apis.get(versionedPath)!;\n throw new Error(\n `Cannot register API \"${name}\" with path \"${versionedPath}\" - this path is already used by API \"${existing.name}\"`,\n );\n }\n apis.set(versionedPath, this);\n\n // Also register the unversioned path if not already claimed\n // (This is intentionally more permissive - first API gets the unversioned path)\n if (!apis.has(config.path)) {\n apis.set(config.path, this);\n }\n }\n } else {\n // Unversioned API, check for collision and register\n if (apis.has(config.path)) {\n const existing = apis.get(config.path)!;\n throw new Error(\n `Cannot register API \"${name}\" with custom path \"${config.path}\" - this path is already used by API \"${existing.name}\"`,\n );\n }\n apis.set(config.path, this);\n }\n }\n }\n\n /**\n * Retrieves the handler function associated with this Consumption API.\n * @returns The handler function.\n */\n getHandler = (): ApiHandler<T, R> => {\n return this._handler;\n };\n\n async call(baseUrl: string, queryParams: T): Promise<R> {\n // Construct the API endpoint URL using custom path or default to name\n let path: string;\n if (this.config?.path) {\n // Check if the custom path already contains the version\n if (this.config.version) {\n const pathEndsWithVersion =\n this.config.path.endsWith(`/${this.config.version}`) ||\n this.config.path === this.config.version ||\n (this.config.path.endsWith(this.config.version) &&\n this.config.path.length > this.config.version.length &&\n this.config.path[\n this.config.path.length - this.config.version.length - 1\n ] === \"/\");\n\n if (pathEndsWithVersion) {\n path = this.config.path;\n } else {\n path = `${this.config.path.replace(/\\/$/, \"\")}/${this.config.version}`;\n }\n } else {\n path = this.config.path;\n }\n } else {\n // Default to name with optional version\n path =\n this.config?.version ?\n `${this.name}/${this.config.version}`\n : this.name;\n }\n const url = new URL(`${baseUrl.replace(/\\/$/, \"\")}/api/${path}`);\n\n const searchParams = url.searchParams;\n\n for (const [key, value] of Object.entries(queryParams as any)) {\n if (Array.isArray(value)) {\n // For array values, add each item as a separate query param\n for (const item of value) {\n if (item !== null && item !== undefined) {\n searchParams.append(key, String(item));\n }\n }\n } else if (value !== null && value !== undefined) {\n searchParams.append(key, String(value));\n }\n }\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n },\n });\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const data = await response.json();\n return data as R;\n }\n}\n\n/** @deprecated Use ApiConfig<T> directly instead. */\nexport type EgressConfig<T> = ApiConfig<T>;\n\n/** @deprecated Use Api directly instead. */\nexport const ConsumptionApi = Api;\n","import { IJsonSchemaCollection } from \"typia\";\nimport { TypedBase, TypiaValidators } from \"../typedBase\";\nimport { Column } from \"../../dataModels/dataModelTypes\";\nimport {\n DeadLetterModel,\n DeadLetterQueue,\n Stream,\n StreamConfig,\n} from \"./stream\";\nimport { OlapConfig, OlapTable } from \"./olapTable\";\nimport { IngestApi, IngestConfig } from \"./ingestApi\";\nimport { LifeCycle } from \"./lifeCycle\";\nimport { ClickHouseEngines } from \"../../dataModels/types\";\n\n/**\n * Configuration options for a complete ingestion pipeline, potentially including an Ingest API, a Stream, and an OLAP Table.\n *\n * @template T The data type of the records being ingested.\n *\n * @example\n * ```typescript\n * // Simple pipeline with all components enabled\n * const pipelineConfig: IngestPipelineConfig<UserData> = {\n * table: true,\n * stream: true,\n * ingestApi: true\n * };\n *\n * // Advanced pipeline with custom configurations\n * const advancedConfig: IngestPipelineConfig<UserData> = {\n * table: { orderByFields: ['timestamp', 'userId'], engine: ClickHouseEngines.ReplacingMergeTree },\n * stream: { parallelism: 4, retentionPeriod: 86400 },\n * ingestApi: true,\n * version: '1.2.0',\n * metadata: { description: 'User data ingestion pipeline' }\n * };\n * ```\n */\nexport type IngestPipelineConfig<T> = {\n /**\n * Configuration for the OLAP table component of the pipeline.\n *\n * - If `true`, a table with default settings is created.\n * - If an `OlapConfig` object is provided, it specifies the table's configuration.\n * - If `false`, no OLAP table is created.\n *\n * @default false\n */\n table: boolean | OlapConfig<T>;\n\n /**\n * Configuration for the stream component of the pipeline.\n *\n * - If `true`, a stream with default settings is created.\n * - Pass a config object to specify the stream's configuration.\n * - The stream's destination will automatically be set to the pipeline's table if one exists.\n * - If `false`, no stream is created.\n *\n * @default false\n */\n stream: boolean | Omit<StreamConfig<T>, \"destination\">;\n\n /**\n * Configuration for the ingest API component of the pipeline.\n *\n * - If `true`, an ingest API with default settings is created.\n * - If a partial `IngestConfig` object (excluding `destination`) is provided, it specifies the API's configuration.\n * - The API's destination will automatically be set to the pipeline's stream if one exists.\n * - If `false`, no ingest API is created.\n *\n * **Note:** Requires a stream to be configured when enabled.\n *\n * @default false\n */\n ingestApi: boolean | Omit<IngestConfig<T>, \"destination\">;\n\n /**\n * @deprecated Use `ingestApi` instead. This parameter will be removed in a future version.\n */\n ingest?: boolean | Omit<IngestConfig<T>, \"destination\">;\n\n /**\n * Configuration for the dead letter queue of the pipeline.\n * If `true`, a dead letter queue with default settings is created.\n * If a partial `StreamConfig` object (excluding `destination`) is provided, it specifies the dead letter queue's configuration.\n * The API's destination will automatically be set to the pipeline's stream if one exists.\n * If `false` or `undefined`, no dead letter queue is created.\n */\n deadLetterQueue?: boolean | StreamConfig<DeadLetterModel>;\n\n /**\n * An optional version string applying to all components (table, stream, ingest) created by this pipeline configuration.\n * This version will be used for schema versioning and component identification.\n *\n * @example \"v1.0.0\", \"2023-12\", \"prod\"\n */\n version?: string;\n\n /**\n * An optional custom path for the ingestion API endpoint.\n * This will be used as the HTTP path for the ingest API if one is created.\n *\n * @example \"pipelines/analytics\", \"data/events\"\n */\n path?: string;\n\n /**\n * Optional metadata for the pipeline.\n */\n metadata?: {\n /** Human-readable description of the pipeline's purpose */\n description?: string;\n };\n\n /** Determines how changes in code will propagate to the resources. */\n lifeCycle?: LifeCycle;\n};\n\n/**\n * Represents a complete ingestion pipeline, potentially combining an Ingest API, a Stream, and an OLAP Table\n * under a single name and configuration. Simplifies the setup of common ingestion patterns.\n *\n * This class provides a high-level abstraction for creating data ingestion workflows that can include:\n * - An HTTP API endpoint for receiving data\n * - A streaming component for real-time data processing\n * - An OLAP table for analytical queries\n *\n * @template T The data type of the records flowing through the pipeline. This type defines the schema for the\n * Ingest API input, the Stream messages, and the OLAP Table rows.\n *\n * @example\n * ```typescript\n * // Create a complete pipeline with all components\n * const userDataPipeline = new IngestPipeline('userData', {\n * table: true,\n * stream: true,\n * ingestApi: true,\n * version: '1.0.0',\n * metadata: { description: 'Pipeline for user registration data' }\n * });\n *\n * // Create a pipeline with only table and stream\n * const analyticsStream = new IngestPipeline('analytics', {\n * table: { orderByFields: ['timestamp'], engine: ClickHouseEngines.ReplacingMergeTree },\n * stream: { parallelism: 8, retentionPeriod: 604800 },\n * ingestApi: false\n * });\n * ```\n */\nexport class IngestPipeline<T> extends TypedBase<T, IngestPipelineConfig<T>> {\n /**\n * The OLAP table component of the pipeline, if configured.\n * Provides analytical query capabilities for the ingested data.\n * Only present when `config.table` is not `false`.\n */\n table?: OlapTable<T>;\n\n /**\n * The stream component of the pipeline, if configured.\n * Handles real-time data flow and processing between components.\n * Only present when `config.stream` is not `false`.\n */\n stream?: Stream<T>;\n\n /**\n * The ingest API component of the pipeline, if configured.\n * Provides HTTP endpoints for data ingestion.\n * Only present when `config.ingestApi` is not `false`.\n */\n ingestApi?: IngestApi<T>;\n\n /** The dead letter queue of the pipeline, if configured. */\n deadLetterQueue?: DeadLetterQueue<T>;\n\n /**\n * Creates a new IngestPipeline instance.\n * Based on the configuration, it automatically creates and links the IngestApi, Stream, and OlapTable components.\n *\n * @param name The base name for the pipeline components (e.g., \"userData\" could create \"userData\" table, \"userData\" stream, \"userData\" ingest API).\n * @param config Optional configuration for the ingestion pipeline.\n *\n * @throws {Error} When ingest API is enabled but no stream is configured, since the API requires a stream destination.\n *\n * @example\n * ```typescript\n * const pipeline = new IngestPipeline('events', {\n * table: { orderByFields: ['timestamp'], engine: ClickHouseEngines.ReplacingMergeTree },\n * stream: { parallelism: 2 },\n * ingestApi: true\n * });\n * ```\n */\n constructor(name: string, config: IngestPipelineConfig<T>);\n\n /**\n * Internal constructor used by the framework for advanced initialization.\n *\n * @internal\n * @param name The base name for the pipeline components.\n * @param config Configuration specifying which components to create and their settings.\n * @param schema JSON schema collection for type validation.\n * @param columns Column definitions for the data model.\n * @param validators Typia validation functions.\n * @param allowExtraFields Whether extra fields are allowed (injected when type has index signature).\n */\n constructor(\n name: string,\n config: IngestPipelineConfig<T>,\n schema: IJsonSchemaCollection.IV3_1,\n columns: Column[],\n validators: TypiaValidators<T>,\n allowExtraFields: boolean,\n );\n\n constructor(\n name: string,\n config: IngestPipelineConfig<T>,\n schema?: IJsonSchemaCollection.IV3_1,\n columns?: Column[],\n validators?: TypiaValidators<T>,\n allowExtraFields?: boolean,\n ) {\n super(name, config, schema, columns, validators, allowExtraFields);\n\n // Handle backwards compatibility for deprecated 'ingest' parameter\n if (config.ingest !== undefined) {\n console.warn(\n \"⚠️ DEPRECATION WARNING: The 'ingest' parameter is deprecated and will be removed in a future version. \" +\n \"Please use 'ingestApi' instead.\",\n );\n // If ingestApi is not explicitly set, use the ingest value\n if (config.ingestApi === undefined) {\n (config as any).ingestApi = config.ingest;\n }\n }\n\n // Create OLAP table if configured\n if (config.table) {\n // Validate that the engine is not read-only (Merge engine cannot be written to)\n if (\n typeof config.table === \"object\" &&\n \"engine\" in config.table &&\n config.table.engine === ClickHouseEngines.Merge\n ) {\n throw new Error(\n `IngestPipeline \"${name}\": Merge engine is read-only and cannot be used as a table destination.`,\n );\n }\n\n const tableConfig: OlapConfig<T> =\n typeof config.table === \"object\" ?\n {\n ...config.table,\n lifeCycle: config.table.lifeCycle ?? config.lifeCycle,\n ...(config.version && { version: config.version }),\n }\n : {\n lifeCycle: config.lifeCycle,\n engine: ClickHouseEngines.MergeTree,\n ...(config.version && { version: config.version }),\n };\n this.table = new OlapTable(\n name,\n tableConfig,\n this.schema,\n this.columnArray,\n this.validators,\n );\n }\n\n if (config.deadLetterQueue) {\n const streamConfig = {\n destination: undefined,\n ...(typeof config.deadLetterQueue === \"object\" ?\n {\n ...config.deadLetterQueue,\n lifeCycle: config.deadLetterQueue.lifeCycle ?? config.lifeCycle,\n }\n : { lifeCycle: config.lifeCycle }),\n ...(config.version && { version: config.version }),\n };\n this.deadLetterQueue = new DeadLetterQueue<T>(\n `${name}DeadLetterQueue`,\n streamConfig,\n validators!.assert!,\n );\n }\n\n // Create stream if configured, linking it to the table as destination\n if (config.stream) {\n const streamConfig: StreamConfig<T> = {\n destination: this.table,\n defaultDeadLetterQueue: this.deadLetterQueue,\n ...(typeof config.stream === \"object\" ?\n {\n ...config.stream,\n lifeCycle: config.stream.lifeCycle ?? config.lifeCycle,\n }\n : { lifeCycle: config.lifeCycle }),\n ...(config.version && { version: config.version }),\n };\n this.stream = new Stream(\n name,\n streamConfig,\n this.schema,\n this.columnArray,\n undefined,\n this.allowExtraFields,\n );\n // Set pipeline parent reference for internal framework use\n (this.stream as any).pipelineParent = this;\n }\n\n // Create ingest API if configured, requiring a stream as destination\n const effectiveIngestAPI =\n config.ingestApi !== undefined ? config.ingestApi : config.ingest;\n if (effectiveIngestAPI) {\n if (!this.stream) {\n throw new Error(\"Ingest API needs a stream to write to.\");\n }\n\n const ingestConfig = {\n destination: this.stream,\n deadLetterQueue: this.deadLetterQueue,\n ...(typeof effectiveIngestAPI === \"object\" ?\n (effectiveIngestAPI as object)\n : {}),\n ...(config.version && { version: config.version }),\n ...(config.path && { path: config.path }),\n };\n this.ingestApi = new IngestApi(\n name,\n ingestConfig,\n this.schema,\n this.columnArray,\n undefined,\n this.allowExtraFields,\n );\n // Set pipeline parent reference for internal framework use\n (this.ingestApi as any).pipelineParent = this;\n }\n }\n}\n","import { Workflow, Task } from \"./workflow\";\nimport { OlapTable } from \"./olapTable\";\n\ninterface BatchResult<T> {\n items: T[];\n hasMore: boolean;\n}\n\ninterface TransformedResult<U> {\n items: U[];\n}\n\ninterface TaskConfig {\n retries: number;\n timeout: string;\n}\n\ninterface ETLTasks<T, U> {\n extract: Task<null, BatchResult<T>>;\n transform: Task<BatchResult<T>, TransformedResult<U>>;\n load: Task<TransformedResult<U>, void>;\n}\n\nclass InternalBatcher<T> {\n private iterator: AsyncIterator<T>;\n private batchSize: number;\n\n constructor(asyncIterable: AsyncIterable<T>, batchSize = 20) {\n this.iterator = asyncIterable[Symbol.asyncIterator]();\n this.batchSize = batchSize;\n }\n\n async getNextBatch(): Promise<BatchResult<T>> {\n const items: T[] = [];\n\n for (let i = 0; i < this.batchSize; i++) {\n const { value, done } = await this.iterator.next();\n\n if (done) {\n return { items, hasMore: false };\n }\n\n items.push(value);\n }\n\n return { items, hasMore: true };\n }\n}\n\nexport interface ETLPipelineConfig<T, U> {\n extract: AsyncIterable<T> | (() => AsyncIterable<T>);\n transform: (sourceData: T) => Promise<U>;\n load: ((data: U[]) => Promise<void>) | OlapTable<U>;\n}\n\nexport class ETLPipeline<T, U> {\n private batcher!: InternalBatcher<T>;\n\n constructor(\n readonly name: string,\n readonly config: ETLPipelineConfig<T, U>,\n ) {\n this.setupPipeline();\n }\n\n private setupPipeline(): void {\n this.batcher = this.createBatcher();\n const tasks = this.createAllTasks();\n\n tasks.extract.config.onComplete = [tasks.transform];\n tasks.transform.config.onComplete = [tasks.load];\n\n new Workflow(this.name, {\n startingTask: tasks.extract,\n retries: 1,\n timeout: \"30m\",\n });\n }\n\n private createBatcher(): InternalBatcher<T> {\n const iterable =\n typeof this.config.extract === \"function\" ?\n this.config.extract()\n : this.config.extract;\n\n return new InternalBatcher(iterable);\n }\n\n private getDefaultTaskConfig(): TaskConfig {\n return {\n retries: 1,\n timeout: \"30m\",\n };\n }\n\n private createAllTasks(): ETLTasks<T, U> {\n const taskConfig = this.getDefaultTaskConfig();\n\n return {\n extract: this.createExtractTask(taskConfig),\n transform: this.createTransformTask(taskConfig),\n load: this.createLoadTask(taskConfig),\n };\n }\n\n private createExtractTask(\n taskConfig: TaskConfig,\n ): Task<null, BatchResult<T>> {\n return new Task<null, BatchResult<T>>(`${this.name}_extract`, {\n run: async ({}) => {\n console.log(`Running extract task for ${this.name}...`);\n const batch = await this.batcher.getNextBatch();\n console.log(`Extract task completed with ${batch.items.length} items`);\n return batch;\n },\n retries: taskConfig.retries,\n timeout: taskConfig.timeout,\n });\n }\n\n private createTransformTask(\n taskConfig: TaskConfig,\n ): Task<BatchResult<T>, TransformedResult<U>> {\n return new Task<BatchResult<T>, TransformedResult<U>>(\n `${this.name}_transform`,\n {\n // Use new single-parameter context API for handlers\n run: async ({ input }) => {\n const batch = input!;\n console.log(\n `Running transform task for ${this.name} with ${batch.items.length} items...`,\n );\n const transformedItems: U[] = [];\n\n for (const item of batch.items) {\n const transformed = await this.config.transform(item);\n transformedItems.push(transformed);\n }\n\n console.log(\n `Transform task completed with ${transformedItems.length} items`,\n );\n return { items: transformedItems };\n },\n retries: taskConfig.retries,\n timeout: taskConfig.timeout,\n },\n );\n }\n\n private createLoadTask(\n taskConfig: TaskConfig,\n ): Task<TransformedResult<U>, void> {\n return new Task<TransformedResult<U>, void>(`${this.name}_load`, {\n run: async ({ input: transformedItems }) => {\n console.log(\n `Running load task for ${this.name} with ${transformedItems.items.length} items...`,\n );\n\n // Handle both function and OlapTable\n if (\"insert\" in this.config.load) {\n // It's an OlapTable - insert entire batch\n await this.config.load.insert(transformedItems.items);\n } else {\n // It's a function - call with entire array\n await this.config.load(transformedItems.items);\n }\n\n console.log(`Load task completed`);\n },\n retries: taskConfig.retries,\n timeout: taskConfig.timeout,\n });\n }\n\n // Execute the entire ETL pipeline\n async run(): Promise<void> {\n console.log(`Starting ETL Pipeline: ${this.name}`);\n\n let batchNumber = 1;\n do {\n console.log(`Processing batch ${batchNumber}...`);\n const batch = await this.batcher.getNextBatch();\n\n if (batch.items.length === 0) {\n break;\n }\n\n // Transform all items in the batch\n const transformedItems: U[] = [];\n for (const extractedData of batch.items) {\n const transformedData = await this.config.transform(extractedData);\n transformedItems.push(transformedData);\n }\n\n // Load the entire batch\n if (\"insert\" in this.config.load) {\n // It's an OlapTable - insert entire batch\n await this.config.load.insert(transformedItems);\n } else {\n // It's a function - call with entire array\n await this.config.load(transformedItems);\n }\n\n console.log(\n `Completed batch ${batchNumber} with ${batch.items.length} items`,\n );\n batchNumber++;\n\n if (!batch.hasMore) {\n break;\n }\n } while (true);\n\n console.log(`Completed ETL Pipeline: ${this.name}`);\n }\n}\n","import { ClickHouseEngines } from \"../../dataModels/types\";\nimport { Sql, toStaticQuery } from \"../../sqlHelpers\";\nimport { OlapConfig, OlapTable } from \"./olapTable\";\nimport { View } from \"./view\";\nimport { LifeCycle } from \"./lifeCycle\";\nimport { IJsonSchemaCollection } from \"typia\";\nimport { Column } from \"../../dataModels/dataModelTypes\";\nimport { getMooseInternal, isClientOnlyMode } from \"../internal\";\nimport { getSourceFileFromStack } from \"../utils/stackTrace\";\n\n/**\n * Helper function to format a table reference as `database`.`table` or just `table`\n */\nfunction formatTableReference(table: OlapTable<any> | View): string {\n const database =\n table instanceof OlapTable ? table.config.database : undefined;\n if (database) {\n return `\\`${database}\\`.\\`${table.name}\\``;\n }\n return `\\`${table.name}\\``;\n}\n\n/**\n * Configuration options for creating a Materialized View.\n * @template T The data type of the records stored in the target table of the materialized view.\n */\nexport interface MaterializedViewConfig<T> {\n /** The SQL SELECT statement or `Sql` object defining the data to be materialized. Dynamic SQL (with parameters) is not allowed here. */\n selectStatement: string | Sql;\n /** An array of OlapTable or View objects that the `selectStatement` reads from. */\n selectTables: (OlapTable<any> | View)[];\n\n /** @deprecated See {@link targetTable}\n * The name for the underlying target OlapTable that stores the materialized data. */\n tableName?: string;\n\n /** The name for the ClickHouse MATERIALIZED VIEW object itself. */\n materializedViewName: string;\n\n /** @deprecated See {@link targetTable}\n * Optional ClickHouse engine for the target table (e.g., ReplacingMergeTree). Defaults to MergeTree. */\n engine?: ClickHouseEngines;\n\n targetTable?:\n | OlapTable<T> /** Target table if the OlapTable object is already constructed. */\n | {\n /** The name for the underlying target OlapTable that stores the materialized data. */\n name: string;\n /** Optional ClickHouse engine for the target table (e.g., ReplacingMergeTree). Defaults to MergeTree. */\n engine?: ClickHouseEngines;\n /** Optional ordering fields for the target table. Crucial if using ReplacingMergeTree. */\n orderByFields?: (keyof T & string)[];\n };\n\n /** @deprecated See {@link targetTable}\n * Optional ordering fields for the target table. Crucial if using ReplacingMergeTree. */\n orderByFields?: (keyof T & string)[];\n\n /** Optional metadata for the materialized view (e.g., description, source file). */\n metadata?: { [key: string]: any };\n\n /** Optional lifecycle management policy for the materialized view.\n * Controls whether Moose can drop or modify the MV automatically.\n * Defaults to FULLY_MANAGED if not specified. */\n lifeCycle?: LifeCycle;\n}\n\nconst requireTargetTableName = (tableName: string | undefined): string => {\n if (typeof tableName === \"string\") {\n return tableName;\n } else {\n throw new Error(\"Name of targetTable is not specified.\");\n }\n};\n\n/**\n * Represents a Materialized View in ClickHouse.\n * This encapsulates both the target OlapTable that stores the data and the MATERIALIZED VIEW definition\n * that populates the table based on inserts into the source tables.\n *\n * @template TargetTable The data type of the records stored in the underlying target OlapTable. The structure of T defines the target table schema.\n */\nexport class MaterializedView<TargetTable> {\n /** @internal */\n public readonly kind = \"MaterializedView\";\n\n /** The name of the materialized view */\n name: string;\n\n /** The target OlapTable instance where the materialized data is stored. */\n targetTable: OlapTable<TargetTable>;\n\n /** The SELECT SQL statement */\n selectSql: string;\n\n /** Names of source tables that the SELECT reads from */\n sourceTables: string[];\n\n /** Optional metadata for the materialized view */\n metadata: { [key: string]: any };\n\n /** Optional lifecycle management policy for the materialized view */\n lifeCycle?: LifeCycle;\n\n /**\n * Creates a new MaterializedView instance.\n * Requires the `TargetTable` type parameter to be explicitly provided or inferred,\n * as it's needed to define the schema of the underlying target table.\n *\n * @param options Configuration options for the materialized view.\n */\n constructor(options: MaterializedViewConfig<TargetTable>);\n\n /** @internal **/\n constructor(\n options: MaterializedViewConfig<TargetTable>,\n targetSchema: IJsonSchemaCollection.IV3_1,\n targetColumns: Column[],\n );\n constructor(\n options: MaterializedViewConfig<TargetTable>,\n targetSchema?: IJsonSchemaCollection.IV3_1,\n targetColumns?: Column[],\n ) {\n let selectStatement = options.selectStatement;\n if (typeof selectStatement !== \"string\") {\n selectStatement = toStaticQuery(selectStatement);\n }\n\n if (targetSchema === undefined || targetColumns === undefined) {\n throw new Error(\n \"Supply the type param T so that the schema is inserted by the compiler plugin.\",\n );\n }\n\n const targetTable =\n options.targetTable instanceof OlapTable ?\n options.targetTable\n : new OlapTable(\n requireTargetTableName(\n options.targetTable?.name ?? options.tableName,\n ),\n {\n orderByFields:\n options.targetTable?.orderByFields ?? options.orderByFields,\n engine:\n options.targetTable?.engine ??\n options.engine ??\n ClickHouseEngines.MergeTree,\n } as OlapConfig<TargetTable>,\n targetSchema,\n targetColumns,\n );\n\n if (targetTable.name === options.materializedViewName) {\n throw new Error(\n \"Materialized view name cannot be the same as the target table name.\",\n );\n }\n\n this.name = options.materializedViewName;\n this.targetTable = targetTable;\n this.selectSql = selectStatement;\n this.sourceTables = options.selectTables.map((t) =>\n formatTableReference(t),\n );\n this.lifeCycle = options.lifeCycle;\n\n // Initialize metadata, preserving user-provided metadata if any\n this.metadata = options.metadata ? { ...options.metadata } : {};\n\n // Capture source file from stack trace if not already provided\n if (!this.metadata.source) {\n const stack = new Error().stack;\n const sourceInfo = getSourceFileFromStack(stack);\n if (sourceInfo) {\n this.metadata.source = { file: sourceInfo };\n }\n }\n\n // Register in the materializedViews registry\n const materializedViews = getMooseInternal().materializedViews;\n if (!isClientOnlyMode() && materializedViews.has(this.name)) {\n throw new Error(`MaterializedView with name ${this.name} already exists`);\n }\n materializedViews.set(this.name, this);\n }\n}\n","import { getMooseInternal, isClientOnlyMode } from \"../internal\";\nimport { OlapTable } from \"./olapTable\";\nimport { Sql, toStaticQuery } from \"../../sqlHelpers\";\nimport { getSourceLocationFromStack } from \"../utils/stackTrace\";\nimport { MaterializedView } from \"./materializedView\";\nimport { View } from \"./view\";\n\ntype SqlObject = OlapTable<any> | SqlResource | View | MaterializedView<any>;\n\n/**\n * Represents a generic SQL resource that requires setup and teardown commands.\n * Base class for constructs like Views and Materialized Views. Tracks dependencies.\n */\nexport class SqlResource {\n /** @internal */\n public readonly kind = \"SqlResource\";\n\n /** Array of SQL statements to execute for setting up the resource. */\n setup: readonly string[];\n /** Array of SQL statements to execute for tearing down the resource. */\n teardown: readonly string[];\n /** The name of the SQL resource (e.g., view name, materialized view name). */\n name: string;\n\n /** List of OlapTables or Views that this resource reads data from. */\n pullsDataFrom: SqlObject[];\n /** List of OlapTables or Views that this resource writes data to. */\n pushesDataTo: SqlObject[];\n\n /** @internal Source file path where this resource was defined */\n sourceFile?: string;\n\n /** @internal Source line number where this resource was defined */\n sourceLine?: number;\n\n /** @internal Source column number where this resource was defined */\n sourceColumn?: number;\n\n /**\n * Creates a new SqlResource instance.\n * @param name The name of the resource.\n * @param setup An array of SQL DDL statements to create the resource.\n * @param teardown An array of SQL DDL statements to drop the resource.\n * @param options Optional configuration for specifying data dependencies.\n * @param options.pullsDataFrom Tables/Views this resource reads from.\n * @param options.pushesDataTo Tables/Views this resource writes to.\n */\n constructor(\n name: string,\n setup: readonly (string | Sql)[],\n teardown: readonly (string | Sql)[],\n options?: {\n pullsDataFrom?: SqlObject[];\n pushesDataTo?: SqlObject[];\n },\n ) {\n const sqlResources = getMooseInternal().sqlResources;\n // In client-only mode (MOOSE_CLIENT_ONLY=true), allow duplicate registrations\n // to support Next.js HMR which re-executes modules without clearing the registry\n if (!isClientOnlyMode() && sqlResources.has(name)) {\n throw new Error(`SqlResource with name ${name} already exists`);\n }\n sqlResources.set(name, this);\n\n this.name = name;\n this.setup = setup.map((sql) =>\n typeof sql === \"string\" ? sql : toStaticQuery(sql),\n );\n this.teardown = teardown.map((sql) =>\n typeof sql === \"string\" ? sql : toStaticQuery(sql),\n );\n this.pullsDataFrom = options?.pullsDataFrom ?? [];\n this.pushesDataTo = options?.pushesDataTo ?? [];\n\n // Capture source location from stack trace\n const stack = new Error().stack;\n const location = getSourceLocationFromStack(stack);\n\n if (location) {\n this.sourceFile = location.file;\n this.sourceLine = location.line;\n this.sourceColumn = location.column;\n }\n }\n}\n","import { Sql, toStaticQuery } from \"../../sqlHelpers\";\nimport { OlapTable } from \"./olapTable\";\nimport { getMooseInternal, isClientOnlyMode } from \"../internal\";\nimport { getSourceFileFromStack } from \"../utils/stackTrace\";\n\n/**\n * Helper function to format a table reference as `database`.`table` or just `table`\n */\nfunction formatTableReference(table: OlapTable<any> | View): string {\n const database =\n table instanceof OlapTable ? table.config.database : undefined;\n if (database) {\n return `\\`${database}\\`.\\`${table.name}\\``;\n }\n return `\\`${table.name}\\``;\n}\n\n/**\n * Represents a database View, defined by a SQL SELECT statement based on one or more base tables or other views.\n * Emits structured data for the Moose infrastructure system.\n */\nexport class View {\n /** @internal */\n public readonly kind = \"View\";\n\n /** The name of the view */\n name: string;\n\n /** The SELECT SQL statement that defines the view */\n selectSql: string;\n\n /** Names of source tables/views that the SELECT reads from */\n sourceTables: string[];\n\n /** Optional metadata for the view */\n metadata: { [key: string]: any };\n\n /**\n * Creates a new View instance.\n * @param name The name of the view to be created.\n * @param selectStatement The SQL SELECT statement that defines the view's logic.\n * @param baseTables An array of OlapTable or View objects that the `selectStatement` reads from. Used for dependency tracking.\n * @param metadata Optional metadata for the view (e.g., description, source file).\n */\n constructor(\n name: string,\n selectStatement: string | Sql,\n baseTables: (OlapTable<any> | View)[],\n metadata?: { [key: string]: any },\n ) {\n if (typeof selectStatement !== \"string\") {\n selectStatement = toStaticQuery(selectStatement);\n }\n\n this.name = name;\n this.selectSql = selectStatement;\n this.sourceTables = baseTables.map((t) => formatTableReference(t));\n\n // Initialize metadata, preserving user-provided metadata if any\n this.metadata = metadata ? { ...metadata } : {};\n\n // Capture source file from stack trace if not already provided\n if (!this.metadata.source) {\n const stack = new Error().stack;\n const sourceInfo = getSourceFileFromStack(stack);\n if (sourceInfo) {\n this.metadata.source = { file: sourceInfo };\n }\n }\n\n // Register in the views registry\n const views = getMooseInternal().views;\n if (!isClientOnlyMode() && views.has(this.name)) {\n throw new Error(`View with name ${this.name} already exists`);\n }\n views.set(this.name, this);\n }\n}\n","/**\n * Defines how Moose manages the lifecycle of database resources when your code changes.\n *\n * This enum controls the behavior when there are differences between your code definitions\n * and the actual database schema or structure.\n */\nexport enum LifeCycle {\n /**\n * Full automatic management (default behavior).\n * Moose will automatically modify database resources to match your code definitions,\n * including potentially destructive operations like dropping columns or tables.\n */\n FULLY_MANAGED = \"FULLY_MANAGED\",\n\n /**\n * Deletion-protected automatic management.\n * Moose will modify resources to match your code but will avoid destructive actions\n * such as dropping columns, or tables. Only additive changes are applied.\n */\n DELETION_PROTECTED = \"DELETION_PROTECTED\",\n\n /**\n * External management - no automatic changes.\n * Moose will not modify the database resources. You are responsible for managing\n * the schema and ensuring it matches your code definitions manually.\n */\n EXTERNALLY_MANAGED = \"EXTERNALLY_MANAGED\",\n}\n","import http from \"http\";\nimport { getMooseInternal } from \"../internal\";\nimport { getSourceLocationFromStack } from \"../utils/stackTrace\";\n\nexport type WebAppHandler = (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n) => void | Promise<void>;\n\nexport interface FrameworkApp {\n handle?: (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n next?: (err?: any) => void,\n ) => void;\n callback?: () => WebAppHandler;\n routing?: (req: http.IncomingMessage, res: http.ServerResponse) => void;\n ready?: () => PromiseLike<unknown>; // Fastify's ready method (returns FastifyInstance)\n}\n\nexport interface WebAppConfig {\n mountPath: string;\n metadata?: { description?: string };\n injectMooseUtils?: boolean;\n}\n\nconst RESERVED_MOUNT_PATHS = [\n \"/admin\",\n \"/api\",\n \"/consumption\",\n \"/health\",\n \"/ingest\",\n \"/liveness\",\n \"/moose\", // reserved for future use\n \"/ready\",\n \"/workflows\",\n] as const;\n\nexport class WebApp {\n name: string;\n handler: WebAppHandler;\n config: WebAppConfig;\n /** @internal Source file path where this web app was declared */\n sourceFile?: string;\n /** @internal Source line number where this web app was declared */\n sourceLine?: number;\n /** @internal Source column number where this web app was declared */\n sourceColumn?: number;\n private _rawApp?: FrameworkApp;\n\n constructor(\n name: string,\n appOrHandler: FrameworkApp | WebAppHandler,\n config: WebAppConfig,\n ) {\n this.name = name;\n this.config = config;\n\n const stack = new Error().stack;\n const location = getSourceLocationFromStack(stack);\n if (location) {\n this.sourceFile = location.file;\n this.sourceLine = location.line;\n this.sourceColumn = location.column;\n }\n\n // Validate mountPath - it is required\n if (!this.config.mountPath) {\n throw new Error(\n `mountPath is required. Please specify a mount path for your WebApp (e.g., \"/myapi\").`,\n );\n }\n\n const mountPath = this.config.mountPath;\n\n // Check for root path - not allowed as it would overlap reserved paths\n if (mountPath === \"/\") {\n throw new Error(\n `mountPath cannot be \"/\" as it would allow routes to overlap with reserved paths: ${RESERVED_MOUNT_PATHS.join(\", \")}`,\n );\n }\n\n // Check for trailing slash\n if (mountPath.endsWith(\"/\")) {\n throw new Error(\n `mountPath cannot end with a trailing slash. Remove the '/' from: \"${mountPath}\"`,\n );\n }\n\n // Check for reserved path prefixes\n for (const reserved of RESERVED_MOUNT_PATHS) {\n if (mountPath === reserved || mountPath.startsWith(`${reserved}/`)) {\n throw new Error(\n `mountPath cannot begin with a reserved path: ${RESERVED_MOUNT_PATHS.join(\", \")}. Got: \"${mountPath}\"`,\n );\n }\n }\n\n this.handler = this.toHandler(appOrHandler);\n this._rawApp =\n typeof appOrHandler === \"function\" ? undefined : appOrHandler;\n\n const webApps = getMooseInternal().webApps;\n if (webApps.has(name)) {\n throw new Error(`WebApp with name ${name} already exists`);\n }\n\n // Check for duplicate mountPath\n if (this.config.mountPath) {\n for (const [existingName, existingApp] of webApps) {\n if (existingApp.config.mountPath === this.config.mountPath) {\n throw new Error(\n `WebApp with mountPath \"${this.config.mountPath}\" already exists (used by WebApp \"${existingName}\")`,\n );\n }\n }\n }\n\n webApps.set(name, this);\n }\n\n private toHandler(appOrHandler: FrameworkApp | WebAppHandler): WebAppHandler {\n if (typeof appOrHandler === \"function\") {\n return appOrHandler as WebAppHandler;\n }\n\n const app = appOrHandler as FrameworkApp;\n\n if (typeof app.handle === \"function\") {\n return (req, res) => {\n app.handle!(req, res, (err?: any) => {\n if (err) {\n console.error(\"WebApp handler error:\", err);\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Internal Server Error\" }));\n }\n }\n });\n };\n }\n\n if (typeof app.callback === \"function\") {\n return app.callback();\n }\n\n // Fastify: routing is a function that handles requests directly\n // Fastify requires .ready() to be called before routes are available\n if (typeof app.routing === \"function\") {\n // Capture references to avoid TypeScript narrowing issues in closure\n const routing = app.routing;\n const appWithReady = app;\n\n // Use lazy initialization - don't call ready() during module loading\n // This prevents blocking the event loop when streaming functions import the app module\n // The ready() call is deferred to the first actual HTTP request\n let readyPromise: PromiseLike<unknown> | null = null;\n\n return async (req, res) => {\n // Lazy init - only call ready() when first request comes in\n if (readyPromise === null) {\n readyPromise =\n typeof appWithReady.ready === \"function\" ?\n appWithReady.ready()\n : Promise.resolve();\n }\n await readyPromise;\n routing(req, res);\n };\n }\n\n throw new Error(\n `Unable to convert app to handler. The provided object must be:\n - A function (raw Node.js handler)\n - An object with .handle() method (Express, Connect)\n - An object with .callback() method (Koa)\n - An object with .routing function (Fastify)\n \nExamples:\n Express: new WebApp(\"name\", expressApp)\n Koa: new WebApp(\"name\", koaApp)\n Fastify: new WebApp(\"name\", fastifyApp)\n Raw: new WebApp(\"name\", (req, res) => { ... })\n `,\n );\n }\n\n getRawApp(): FrameworkApp | undefined {\n return this._rawApp;\n }\n}\n","/**\n * @module registry\n * Public registry functions for accessing Moose Data Model v2 (dmv2) resources.\n *\n * This module provides functions to retrieve registered resources like tables, streams,\n * APIs, and more. These functions are part of the public API and can be used by\n * user applications to inspect and interact with registered Moose resources.\n */\n\nimport { OlapTable } from \"./sdk/olapTable\";\nimport { Stream } from \"./sdk/stream\";\nimport { IngestApi } from \"./sdk/ingestApi\";\nimport { Api } from \"./sdk/consumptionApi\";\nimport { SqlResource } from \"./sdk/sqlResource\";\nimport { Workflow } from \"./sdk/workflow\";\nimport { WebApp } from \"./sdk/webApp\";\nimport { MaterializedView } from \"./sdk/materializedView\";\nimport { View } from \"./sdk/view\";\nimport { getMooseInternal } from \"./internal\";\n\n/**\n * Get all registered OLAP tables.\n * @returns A Map of table name to OlapTable instance\n */\nexport function getTables(): Map<string, OlapTable<any>> {\n return getMooseInternal().tables;\n}\n\n/**\n * Get a registered OLAP table by name.\n * @param name - The name of the table\n * @returns The OlapTable instance or undefined if not found\n */\nexport function getTable(name: string): OlapTable<any> | undefined {\n return getMooseInternal().tables.get(name);\n}\n\n/**\n * Get all registered streams.\n * @returns A Map of stream name to Stream instance\n */\nexport function getStreams(): Map<string, Stream<any>> {\n return getMooseInternal().streams;\n}\n\n/**\n * Get a registered stream by name.\n * @param name - The name of the stream\n * @returns The Stream instance or undefined if not found\n */\nexport function getStream(name: string): Stream<any> | undefined {\n return getMooseInternal().streams.get(name);\n}\n\n/**\n * Get all registered ingestion APIs.\n * @returns A Map of API name to IngestApi instance\n */\nexport function getIngestApis(): Map<string, IngestApi<any>> {\n return getMooseInternal().ingestApis;\n}\n\n/**\n * Get a registered ingestion API by name.\n * @param name - The name of the ingestion API\n * @returns The IngestApi instance or undefined if not found\n */\nexport function getIngestApi(name: string): IngestApi<any> | undefined {\n return getMooseInternal().ingestApis.get(name);\n}\n\n/**\n * Get all registered APIs (consumption/egress APIs).\n * @returns A Map of API key to Api instance\n */\nexport function getApis(): Map<string, Api<any>> {\n return getMooseInternal().apis;\n}\n\n/**\n * Get a registered API by name, version, or path.\n *\n * Supports multiple lookup strategies:\n * 1. Direct lookup by full key (name:version or name for unversioned)\n * 2. Lookup by name with automatic version aliasing when only one versioned API exists\n * 3. Lookup by custom path (if configured)\n *\n * @param nameOrPath - The name, name:version, or custom path of the API\n * @returns The Api instance or undefined if not found\n */\nexport function getApi(nameOrPath: string): Api<any> | undefined {\n const registry = getMooseInternal();\n\n // Try direct lookup first (full key: name or name:version)\n const directMatch = registry.apis.get(nameOrPath);\n if (directMatch) {\n return directMatch;\n }\n\n // Build alias maps on-demand for unversioned lookups\n const versionedApis = new Map<string, Api<any>[]>();\n const pathMap = new Map<string, Api<any>>();\n\n registry.apis.forEach((api, key) => {\n // Track APIs by base name for aliasing\n const baseName = api.name;\n if (!versionedApis.has(baseName)) {\n versionedApis.set(baseName, []);\n }\n versionedApis.get(baseName)!.push(api);\n\n // Track APIs by custom path\n if (api.config.path) {\n pathMap.set(api.config.path, api);\n }\n });\n\n // Try alias lookup: if there's exactly one API with this base name, return it\n const candidates = versionedApis.get(nameOrPath);\n if (candidates && candidates.length === 1) {\n return candidates[0];\n }\n\n // Try path-based lookup\n return pathMap.get(nameOrPath);\n}\n\n/**\n * Get all registered SQL resources.\n * @returns A Map of resource name to SqlResource instance\n */\nexport function getSqlResources(): Map<string, SqlResource> {\n return getMooseInternal().sqlResources;\n}\n\n/**\n * Get a registered SQL resource by name.\n * @param name - The name of the SQL resource\n * @returns The SqlResource instance or undefined if not found\n */\nexport function getSqlResource(name: string): SqlResource | undefined {\n return getMooseInternal().sqlResources.get(name);\n}\n\n/**\n * Get all registered workflows.\n * @returns A Map of workflow name to Workflow instance\n */\nexport function getWorkflows(): Map<string, Workflow> {\n return getMooseInternal().workflows;\n}\n\n/**\n * Get a registered workflow by name.\n * @param name - The name of the workflow\n * @returns The Workflow instance or undefined if not found\n */\nexport function getWorkflow(name: string): Workflow | undefined {\n return getMooseInternal().workflows.get(name);\n}\n\n/**\n * Get all registered web apps.\n * @returns A Map of web app name to WebApp instance\n */\nexport function getWebApps(): Map<string, WebApp> {\n return getMooseInternal().webApps;\n}\n\n/**\n * Get a registered web app by name.\n * @param name - The name of the web app\n * @returns The WebApp instance or undefined if not found\n */\nexport function getWebApp(name: string): WebApp | undefined {\n return getMooseInternal().webApps.get(name);\n}\n\n/**\n * Get all registered materialized views.\n * @returns A Map of MV name to MaterializedView instance\n */\nexport function getMaterializedViews(): Map<string, MaterializedView<any>> {\n return getMooseInternal().materializedViews;\n}\n\n/**\n * Get a registered materialized view by name.\n * @param name - The name of the materialized view\n * @returns The MaterializedView instance or undefined if not found\n */\nexport function getMaterializedView(\n name: string,\n): MaterializedView<any> | undefined {\n return getMooseInternal().materializedViews.get(name);\n}\n\n/**\n * Get all registered views.\n * @returns A Map of view name to View instance\n */\nexport function getViews(): Map<string, View> {\n return getMooseInternal().views;\n}\n\n/**\n * Get a registered view by name.\n * @param name - The name of the view\n * @returns The View instance or undefined if not found\n */\nexport function getView(name: string): View | undefined {\n return getMooseInternal().views.get(name);\n}\n","/**\n * @module dmv2\n * This module defines the core Moose v2 data model constructs, including OlapTable, Stream, IngestApi, Api,\n * IngestPipeline, View, and MaterializedView. These classes provide a typed interface for defining and managing\n * data infrastructure components like ClickHouse tables, Redpanda streams, and data processing pipelines.\n */\n\n/**\n * A helper type used potentially for indicating aggregated fields in query results or schemas.\n * Captures the aggregation function name and argument types.\n * (Usage context might be specific to query builders or ORM features).\n *\n * @template AggregationFunction The name of the aggregation function (e.g., 'sum', 'avg', 'count').\n * @template ArgTypes An array type representing the types of the arguments passed to the aggregation function.\n */\nexport type Aggregated<\n AggregationFunction extends string,\n ArgTypes extends any[] = [],\n> = {\n _aggregationFunction?: AggregationFunction;\n _argTypes?: ArgTypes;\n};\n\n/**\n * A helper type for SimpleAggregateFunction in ClickHouse.\n * SimpleAggregateFunction stores the aggregated value directly instead of intermediate states,\n * offering better performance for functions like sum, max, min, any, anyLast, etc.\n *\n * @template AggregationFunction The name of the simple aggregation function (e.g., 'sum', 'max', 'anyLast').\n * @template ArgType The type of the argument (and result) of the aggregation function.\n *\n * @example\n * ```typescript\n * interface Stats {\n * rowCount: number & SimpleAggregated<'sum', number>;\n * maxValue: number & SimpleAggregated<'max', number>;\n * lastStatus: string & SimpleAggregated<'anyLast', string>;\n * }\n * ```\n */\nexport type SimpleAggregated<\n AggregationFunction extends string,\n ArgType = any,\n> = {\n _simpleAggregationFunction?: AggregationFunction;\n _argType?: ArgType;\n};\n\nexport { OlapTable, OlapConfig, S3QueueTableSettings } from \"./sdk/olapTable\";\nexport { ClickHouseEngines } from \"../dataModels/types\";\nexport {\n Stream,\n StreamConfig,\n DeadLetterModel,\n DeadLetter,\n DeadLetterQueue,\n ConsumerConfig,\n TransformConfig,\n} from \"./sdk/stream\";\n\nexport { Workflow, Task } from \"./sdk/workflow\";\nexport type { TaskContext, TaskConfig } from \"./sdk/workflow\";\n\nexport { IngestApi, IngestConfig } from \"./sdk/ingestApi\";\nexport {\n Api,\n ApiConfig,\n EgressConfig,\n ConsumptionApi,\n} from \"./sdk/consumptionApi\";\nexport { IngestPipeline, IngestPipelineConfig } from \"./sdk/ingestPipeline\";\nexport { ETLPipeline, ETLPipelineConfig } from \"./sdk/etlPipeline\";\nexport {\n MaterializedView,\n MaterializedViewConfig,\n} from \"./sdk/materializedView\";\nexport { SqlResource } from \"./sdk/sqlResource\";\nexport { View } from \"./sdk/view\";\nexport { LifeCycle } from \"./sdk/lifeCycle\";\nexport {\n WebApp,\n WebAppConfig,\n WebAppHandler,\n FrameworkApp,\n} from \"./sdk/webApp\";\n\nexport {\n getTables,\n getTable,\n getStreams,\n getStream,\n getIngestApis,\n getIngestApi,\n getApis,\n getApi,\n getSqlResources,\n getSqlResource,\n getWorkflows,\n getWorkflow,\n getWebApps,\n getWebApp,\n getMaterializedViews,\n getMaterializedView,\n getViews,\n getView,\n} from \"./registry\";\n","export type Key<T extends string | number | Date> = T;\n\nexport type JWT<T extends object> = T;\n\nexport {\n ClickHouseEngines,\n Aggregated,\n SimpleAggregated,\n OlapTable,\n OlapConfig,\n S3QueueTableSettings,\n Stream,\n StreamConfig,\n DeadLetterModel,\n DeadLetter,\n DeadLetterQueue,\n IngestApi,\n IngestConfig,\n Api,\n ApiConfig,\n ConsumptionApi,\n EgressConfig,\n IngestPipeline,\n SqlResource,\n View,\n MaterializedView,\n Task,\n Workflow,\n ETLPipeline,\n ETLPipelineConfig,\n LifeCycle,\n WebApp,\n WebAppConfig,\n WebAppHandler,\n FrameworkApp,\n // Registry functions\n getTables,\n getTable,\n getStreams,\n getStream,\n getIngestApis,\n getIngestApi,\n getApis,\n getApi,\n getSqlResources,\n getSqlResource,\n getWorkflows,\n getWorkflow,\n getWebApps,\n getWebApp,\n getView,\n getViews,\n getMaterializedView,\n getMaterializedViews,\n} from \"./dmv2\";\n\nexport {\n ClickHousePrecision,\n ClickHouseDecimal,\n ClickHouseByteSize,\n ClickHouseFixedStringSize,\n ClickHouseFloat,\n ClickHouseInt,\n ClickHouseJson,\n LowCardinality,\n ClickHouseNamedTuple,\n ClickHouseDefault,\n ClickHouseTTL,\n ClickHouseMaterialized,\n ClickHouseAlias,\n WithDefault,\n ClickHouseCodec,\n // Added friendly aliases and numeric helpers\n DateTime,\n DateTime64,\n DateTimeString,\n DateTime64String,\n FixedString,\n Float32,\n Float64,\n Int8,\n Int16,\n Int32,\n Int64,\n UInt8,\n UInt16,\n UInt32,\n UInt64,\n Decimal,\n} from \"./dataModels/types\";\n\nexport type { ApiUtil, ConsumptionUtil } from \"./consumption-apis/helpers\";\n\nexport * from \"./sqlHelpers\";\n","import {\n existsSync,\n readdirSync,\n readFileSync,\n statSync,\n writeFileSync,\n} from \"fs\";\nimport nodePath from \"path\";\nimport { createClient } from \"@clickhouse/client\";\nimport { KafkaJS } from \"@514labs/kafka-javascript\";\nimport { SASLOptions } from \"@514labs/kafka-javascript/types/kafkajs\";\nconst { Kafka } = KafkaJS;\ntype Kafka = KafkaJS.Kafka;\ntype Consumer = KafkaJS.Consumer;\nexport type Producer = KafkaJS.Producer;\n\n/**\n * Utility function for compiler-related logging that can be disabled via environment variable.\n * Set MOOSE_DISABLE_COMPILER_LOGS=true to suppress these logs (useful for testing environments).\n */\n\n/**\n * Returns true if the value is a common truthy string: \"1\", \"true\", \"yes\", \"on\" (case-insensitive).\n */\nfunction isTruthy(value: string | undefined): boolean {\n if (!value) return false;\n switch (value.trim().toLowerCase()) {\n case \"1\":\n case \"true\":\n case \"yes\":\n case \"on\":\n return true;\n default:\n return false;\n }\n}\n\nexport const compilerLog = (message: string) => {\n if (!isTruthy(process.env.MOOSE_DISABLE_COMPILER_LOGS)) {\n console.log(message);\n }\n};\n\nexport const antiCachePath = (path: string) =>\n `${path}?num=${Math.random().toString()}&time=${Date.now()}`;\n\nexport const getFileName = (filePath: string) => {\n const regex = /\\/([^\\/]+)\\.ts/;\n const matches = filePath.match(regex);\n if (matches && matches.length > 1) {\n return matches[1];\n }\n return \"\";\n};\n\ninterface ClientConfig {\n username: string;\n password: string;\n database: string;\n useSSL: string;\n host: string;\n port: string;\n}\n\nexport const getClickhouseClient = ({\n username,\n password,\n database,\n useSSL,\n host,\n port,\n}: ClientConfig) => {\n const protocol =\n useSSL === \"1\" || useSSL.toLowerCase() === \"true\" ? \"https\" : \"http\";\n console.log(`Connecting to Clickhouse at ${protocol}://${host}:${port}`);\n return createClient({\n url: `${protocol}://${host}:${port}`,\n username: username,\n password: password,\n database: database,\n application: \"moose\",\n // Note: wait_end_of_query is configured per operation type, not globally\n // to preserve SELECT query performance while ensuring INSERT/DDL reliability\n });\n};\n\nexport type CliLogData = {\n message_type?: \"Info\" | \"Success\" | \"Warning\" | \"Error\" | \"Highlight\";\n action: string;\n message: string;\n};\n\nexport const cliLog: (log: CliLogData) => void = (log) => {\n const level =\n log.message_type === \"Error\" ? \"error\"\n : log.message_type === \"Warning\" ? \"warn\"\n : \"info\";\n\n const structuredLog = {\n __moose_structured_log__: true,\n level,\n message: log.message,\n resource_type: \"runtime\",\n cli_action: log.action,\n cli_message_type: log.message_type ?? \"Info\",\n timestamp: new Date().toISOString(),\n };\n\n process.stderr.write(JSON.stringify(structuredLog) + \"\\n\");\n};\n\n/**\n * Method to change .ts, .cts, and .mts to .js, .cjs, and .mjs\n * This is needed because 'import' does not support .ts, .cts, and .mts\n */\nexport function mapTstoJs(filePath: string): string {\n return filePath\n .replace(/\\.ts$/, \".js\")\n .replace(/\\.cts$/, \".cjs\")\n .replace(/\\.mts$/, \".mjs\");\n}\n\n/**\n * Walks a directory recursively and returns all files matching the given extensions.\n * Skips node_modules directories.\n *\n * @param dir - Directory to walk\n * @param extensions - File extensions to include (e.g., [\".js\", \".mjs\"])\n * @returns Array of file paths\n */\nfunction walkDirectory(dir: string, extensions: string[]): string[] {\n const results: string[] = [];\n\n if (!existsSync(dir)) {\n return results;\n }\n\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = nodePath.join(dir, entry.name);\n\n if (entry.isDirectory()) {\n // Skip node_modules\n if (entry.name !== \"node_modules\") {\n results.push(...walkDirectory(fullPath, extensions));\n }\n } else if (entry.isFile()) {\n const ext = nodePath.extname(entry.name);\n if (extensions.includes(ext)) {\n results.push(fullPath);\n }\n }\n }\n } catch (e) {\n // Log error in case it is something the user can/should act on\n console.debug(`[moose] Failed to read directory ${dir}:`, e);\n }\n\n return results;\n}\n\n/**\n * Adds .js extension to relative import paths that don't have an extension.\n * Handles import/export from statements and dynamic imports.\n *\n * @param content - JavaScript file content\n * @param fileDir - Directory containing the file being processed (for resolving paths)\n * @returns Content with .js extensions added to relative imports\n */\nfunction addJsExtensionToImports(content: string, fileDir?: string): string {\n // Pattern for 'from' statements with relative paths\n // Matches: from './foo', from \"../bar\", from './utils/helper'\n const fromPattern = /(from\\s+['\"])(\\.\\.?\\/[^'\"]*?)(['\"])/g;\n\n // Pattern for side-effect-only imports with relative paths\n // Matches: import './foo', import \"../bar\" (no 'from' keyword, just importing for side effects)\n const bareImportPattern = /(import\\s+['\"])(\\.\\.?\\/[^'\"]*?)(['\"])/g;\n\n // Pattern for dynamic imports with relative paths\n // Matches: import('./foo'), import(\"../bar\")\n const dynamicPattern = /(import\\s*\\(\\s*['\"])(\\.\\.?\\/[^'\"]*?)(['\"])/g;\n\n let result = content;\n\n // Process 'from' statements\n result = result.replace(fromPattern, (match, prefix, importPath, quote) => {\n return rewriteImportPath(match, prefix, importPath, quote, fileDir);\n });\n\n // Process side-effect-only imports\n result = result.replace(\n bareImportPattern,\n (match, prefix, importPath, quote) => {\n return rewriteImportPath(match, prefix, importPath, quote, fileDir);\n },\n );\n\n // Process dynamic imports\n result = result.replace(\n dynamicPattern,\n (match, prefix, importPath, quote) => {\n return rewriteImportPath(match, prefix, importPath, quote, fileDir);\n },\n );\n\n return result;\n}\n\n/**\n * Rewrites a single import path to add .js extension if needed.\n * Handles directory imports with index files correctly.\n *\n * @param match - The full matched string\n * @param prefix - The prefix (from ' or import(')\n * @param importPath - The import path\n * @param quote - The closing quote\n * @param fileDir - Directory containing the file being processed (for resolving paths)\n * @returns The rewritten import or original if no change needed\n */\nfunction rewriteImportPath(\n match: string,\n prefix: string,\n importPath: string,\n quote: string,\n fileDir?: string,\n): string {\n // Skip if already has a JavaScript extension\n if (/\\.[cm]?js$/.test(importPath)) {\n return match;\n }\n\n // Skip if importing a JSON file\n if (/\\.json$/.test(importPath)) {\n return match;\n }\n\n // If we have the file directory, check if the import target is a directory with index.js\n if (fileDir) {\n const resolvedPath = nodePath.resolve(fileDir, importPath);\n\n // Check if path.js exists (regular file import)\n if (existsSync(`${resolvedPath}.js`)) {\n return `${prefix}${importPath}.js${quote}`;\n }\n\n // Check if path/index.js exists (directory import)\n if (existsSync(nodePath.join(resolvedPath, \"index.js\"))) {\n // For directory imports, rewrite to include /index.js\n return `${prefix}${importPath}/index.js${quote}`;\n }\n\n // Check for .mjs variants\n if (existsSync(`${resolvedPath}.mjs`)) {\n return `${prefix}${importPath}.mjs${quote}`;\n }\n if (existsSync(nodePath.join(resolvedPath, \"index.mjs\"))) {\n return `${prefix}${importPath}/index.mjs${quote}`;\n }\n\n // Check for .cjs variants\n if (existsSync(`${resolvedPath}.cjs`)) {\n return `${prefix}${importPath}.cjs${quote}`;\n }\n if (existsSync(nodePath.join(resolvedPath, \"index.cjs\"))) {\n return `${prefix}${importPath}/index.cjs${quote}`;\n }\n }\n\n // Default: add .js extension (fallback when no fileDir or file not found)\n return `${prefix}${importPath}.js${quote}`;\n}\n\n/**\n * Rewrites relative import paths in JavaScript files to include .js extensions.\n * This is required for Node.js ESM which requires explicit extensions.\n *\n * Handles:\n * - import statements: import { foo } from './bar' -> import { foo } from './bar.js'\n * - dynamic imports: import('./bar') -> import('./bar.js')\n * - re-exports: export { foo } from './bar' -> export { foo } from './bar.js'\n *\n * Does NOT modify:\n * - Package imports (no leading . or ..)\n * - Imports that already have extensions\n * - Imports from node_modules\n *\n * @param outDir - Directory containing compiled JavaScript files\n */\nexport function rewriteImportExtensions(outDir: string): void {\n const files = walkDirectory(outDir, [\".js\", \".mjs\"]);\n\n for (const filePath of files) {\n const content = readFileSync(filePath, \"utf-8\");\n const fileDir = nodePath.dirname(filePath);\n const rewritten = addJsExtensionToImports(content, fileDir);\n\n if (content !== rewritten) {\n writeFileSync(filePath, rewritten, \"utf-8\");\n }\n }\n}\n\nexport const MAX_RETRIES = 150;\nexport const MAX_RETRY_TIME_MS = 1000;\nexport const RETRY_INITIAL_TIME_MS = 100;\n\nexport const MAX_RETRIES_PRODUCER = 150;\nexport const RETRY_FACTOR_PRODUCER = 0.2;\n// Means all replicas need to acknowledge the message\nexport const ACKs = -1;\n\n/**\n * Creates the base producer configuration for Kafka.\n * Used by both the SDK stream publishing and streaming function workers.\n *\n * @param maxMessageBytes - Optional max message size in bytes (synced with topic config)\n * @returns Producer configuration object for the Confluent Kafka client\n */\nexport function createProducerConfig(maxMessageBytes?: number) {\n return {\n kafkaJS: {\n idempotent: false, // Not needed for at-least-once delivery\n acks: ACKs,\n retry: {\n retries: MAX_RETRIES_PRODUCER,\n maxRetryTime: MAX_RETRY_TIME_MS,\n },\n },\n \"linger.ms\": 0, // This is to make sure at least once delivery with immediate feedback on the send\n ...(maxMessageBytes && { \"message.max.bytes\": maxMessageBytes }),\n };\n}\n\n/**\n * Parses a comma-separated broker string into an array of valid broker addresses.\n * Handles whitespace trimming and filters out empty elements.\n *\n * @param brokerString - Comma-separated broker addresses (e.g., \"broker1:9092, broker2:9092, , broker3:9092\")\n * @returns Array of trimmed, non-empty broker addresses\n */\nconst parseBrokerString = (brokerString: string): string[] =>\n brokerString\n .split(\",\")\n .map((b) => b.trim())\n .filter((b) => b.length > 0);\n\nexport type KafkaClientConfig = {\n clientId: string;\n broker: string;\n securityProtocol?: string; // e.g. \"SASL_SSL\" or \"PLAINTEXT\"\n saslUsername?: string;\n saslPassword?: string;\n saslMechanism?: string; // e.g. \"scram-sha-256\", \"plain\"\n};\n\n/**\n * Dynamically creates and connects a KafkaJS producer using the provided configuration.\n * Returns a connected producer instance.\n *\n * @param cfg - Kafka client configuration\n * @param logger - Logger instance\n * @param maxMessageBytes - Optional max message size in bytes (synced with topic config)\n */\nexport async function getKafkaProducer(\n cfg: KafkaClientConfig,\n logger: Logger,\n maxMessageBytes?: number,\n): Promise<Producer> {\n const kafka = await getKafkaClient(cfg, logger);\n\n const producer = kafka.producer(createProducerConfig(maxMessageBytes));\n await producer.connect();\n return producer;\n}\n\n/**\n * Interface for logging functionality\n */\nexport interface Logger {\n logPrefix: string;\n log: (message: string) => void;\n error: (message: string) => void;\n warn: (message: string) => void;\n}\n\nexport const logError = (logger: Logger, e: Error): void => {\n logger.error(e.message);\n const stack = e.stack;\n if (stack) {\n logger.error(stack);\n }\n};\n\n/**\n * Builds SASL configuration for Kafka client authentication\n */\nconst buildSaslConfig = (\n logger: Logger,\n args: KafkaClientConfig,\n): SASLOptions | undefined => {\n const mechanism = args.saslMechanism ? args.saslMechanism.toLowerCase() : \"\";\n switch (mechanism) {\n case \"plain\":\n case \"scram-sha-256\":\n case \"scram-sha-512\":\n return {\n mechanism: mechanism,\n username: args.saslUsername || \"\",\n password: args.saslPassword || \"\",\n };\n default:\n logger.warn(`Unsupported SASL mechanism: ${args.saslMechanism}`);\n return undefined;\n }\n};\n\n/**\n * Dynamically creates a KafkaJS client configured with provided settings.\n * Use this to construct producers/consumers with custom options.\n */\nexport const getKafkaClient = async (\n cfg: KafkaClientConfig,\n logger: Logger,\n): Promise<Kafka> => {\n const brokers = parseBrokerString(cfg.broker || \"\");\n if (brokers.length === 0) {\n throw new Error(`No valid broker addresses found in: \"${cfg.broker}\"`);\n }\n\n logger.log(`Creating Kafka client with brokers: ${brokers.join(\", \")}`);\n logger.log(`Security protocol: ${cfg.securityProtocol || \"plaintext\"}`);\n logger.log(`Client ID: ${cfg.clientId}`);\n\n const saslConfig = buildSaslConfig(logger, cfg);\n\n return new Kafka({\n kafkaJS: {\n clientId: cfg.clientId,\n brokers,\n ssl: cfg.securityProtocol === \"SASL_SSL\",\n ...(saslConfig && { sasl: saslConfig }),\n retry: {\n initialRetryTime: RETRY_INITIAL_TIME_MS,\n maxRetryTime: MAX_RETRY_TIME_MS,\n retries: MAX_RETRIES,\n },\n },\n });\n};\n","/**\n * @module secrets\n * Utilities for runtime environment variable resolution.\n *\n * This module provides functionality to mark values that should be resolved\n * from environment variables at runtime by the Moose CLI, rather than being\n * embedded at build time.\n *\n * @example\n * ```typescript\n * import { S3QueueEngine, mooseRuntimeEnv } from 'moose-lib';\n *\n * const table = OlapTable<MyData>(\n * \"MyTable\",\n * OlapConfig({\n * engine: S3QueueEngine({\n * s3_path: \"s3://bucket/data/*.json\",\n * format: \"JSONEachRow\",\n * awsAccessKeyId: mooseRuntimeEnv.get(\"AWS_ACCESS_KEY_ID\"),\n * awsSecretAccessKey: mooseRuntimeEnv.get(\"AWS_SECRET_ACCESS_KEY\")\n * })\n * })\n * );\n * ```\n */\n\n/**\n * Prefix used to mark values for runtime environment variable resolution.\n * @internal\n */\nexport const MOOSE_RUNTIME_ENV_PREFIX = \"__MOOSE_RUNTIME_ENV__:\";\n\n/**\n * Utilities for marking values to be resolved from environment variables at runtime.\n *\n * When you use `mooseRuntimeEnv.get()`, the behavior depends on the context:\n * - During infrastructure map loading: Returns a marker string for later resolution\n * - During function/workflow execution: Returns the actual environment variable value\n *\n * This is useful for:\n * - Credentials that should never be embedded in Docker images\n * - Configuration that can be rotated without rebuilding\n * - Different values for different environments (dev, staging, prod)\n * - Any runtime configuration in infrastructure elements (Tables, Topics, etc.)\n */\nexport const mooseRuntimeEnv = {\n /**\n * Gets a value from an environment variable, with behavior depending on context.\n *\n * When IS_LOADING_INFRA_MAP=true (infrastructure loading):\n * Returns a marker string that Moose CLI will resolve later\n *\n * When IS_LOADING_INFRA_MAP is unset (function/workflow runtime):\n * Returns the actual value from the environment variable\n *\n * @param envVarName - Name of the environment variable to resolve\n * @returns Either a marker string or the actual environment variable value\n * @throws {Error} If the environment variable name is empty\n * @throws {Error} If the environment variable is not set (runtime mode only)\n *\n * @example\n * ```typescript\n * // Instead of this (evaluated at build time):\n * awsAccessKeyId: process.env.AWS_ACCESS_KEY_ID\n *\n * // Use this (evaluated at runtime):\n * awsAccessKeyId: mooseRuntimeEnv.get(\"AWS_ACCESS_KEY_ID\")\n * ```\n */\n get(envVarName: string): string {\n if (!envVarName || envVarName.trim() === \"\") {\n throw new Error(\"Environment variable name cannot be empty\");\n }\n\n // Check if we're loading infrastructure map\n const isLoadingInfraMap = process.env.IS_LOADING_INFRA_MAP === \"true\";\n\n if (isLoadingInfraMap) {\n // Return marker string for later resolution by Moose CLI\n return `${MOOSE_RUNTIME_ENV_PREFIX}${envVarName}`;\n } else {\n // Return actual value from environment for runtime execution\n const value = process.env[envVarName];\n if (value === undefined) {\n throw new Error(\n `Environment variable '${envVarName}' is not set. ` +\n `This is required for runtime execution of functions/workflows.`,\n );\n }\n return value;\n }\n },\n};\n\n// Legacy export for backwards compatibility\n/** @deprecated Use mooseRuntimeEnv instead */\nexport const mooseEnvSecrets = mooseRuntimeEnv;\n","import { ClickHouseClient, CommandResult, ResultSet } from \"@clickhouse/client\";\nimport {\n Client as TemporalClient,\n Connection,\n ConnectionOptions,\n} from \"@temporalio/client\";\nimport { StringValue } from \"@temporalio/common\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { performance } from \"perf_hooks\";\nimport * as fs from \"fs\";\nimport { getWorkflows } from \"../dmv2/internal\";\nimport { JWTPayload } from \"jose\";\nimport { Sql, sql, RawValue, toQuery, toQueryPreview } from \"../sqlHelpers\";\n\n/**\n * Format elapsed milliseconds into a human-readable string.\n * Matches Python's format_timespan behavior.\n */\nfunction formatElapsedTime(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)} ms`;\n }\n const seconds = ms / 1000;\n if (seconds < 60) {\n return `${seconds.toFixed(2)} seconds`;\n }\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes} minutes and ${remainingSeconds.toFixed(2)} seconds`;\n}\n\n/**\n * Utilities provided by getMooseUtils() for database access and SQL queries.\n * Works in both Moose runtime and standalone contexts.\n */\nexport interface MooseUtils {\n client: MooseClient;\n sql: typeof sql;\n jwt?: JWTPayload;\n}\n\n/**\n * @deprecated Use MooseUtils instead. ApiUtil is now a type alias to MooseUtils\n * and will be removed in a future version.\n *\n * Migration: Replace `ApiUtil` with `MooseUtils` in your type annotations.\n */\nexport type ApiUtil = MooseUtils;\n\n/** @deprecated Use MooseUtils instead. */\nexport type ConsumptionUtil = MooseUtils;\n\nexport class MooseClient {\n query: QueryClient;\n workflow: WorkflowClient;\n\n constructor(queryClient: QueryClient, temporalClient?: TemporalClient) {\n this.query = queryClient;\n this.workflow = new WorkflowClient(temporalClient);\n }\n}\n\nexport class QueryClient {\n client: ClickHouseClient;\n query_id_prefix: string;\n constructor(client: ClickHouseClient, query_id_prefix: string) {\n this.client = client;\n this.query_id_prefix = query_id_prefix;\n }\n\n async execute<T = any>(\n sql: Sql,\n ): Promise<ResultSet<\"JSONEachRow\"> & { __query_result_t?: T[] }> {\n const [query, query_params] = toQuery(sql);\n\n console.log(`[QueryClient] | Query: ${toQueryPreview(sql)}`);\n const start = performance.now();\n const result = await this.client.query({\n query,\n query_params,\n format: \"JSONEachRow\",\n query_id: this.query_id_prefix + randomUUID(),\n // Note: wait_end_of_query deliberately NOT set here as this is used for SELECT queries\n // where response buffering would harm streaming performance and concurrency\n });\n const elapsedMs = performance.now() - start;\n console.log(\n `[QueryClient] | Query completed: ${formatElapsedTime(elapsedMs)}`,\n );\n return result;\n }\n\n async command(sql: Sql): Promise<CommandResult> {\n const [query, query_params] = toQuery(sql);\n\n console.log(`[QueryClient] | Command: ${toQueryPreview(sql)}`);\n const start = performance.now();\n const result = await this.client.command({\n query,\n query_params,\n query_id: this.query_id_prefix + randomUUID(),\n });\n const elapsedMs = performance.now() - start;\n console.log(\n `[QueryClient] | Command completed: ${formatElapsedTime(elapsedMs)}`,\n );\n return result;\n }\n}\n\nexport class WorkflowClient {\n client: TemporalClient | undefined;\n\n constructor(temporalClient?: TemporalClient) {\n this.client = temporalClient;\n }\n\n async execute(name: string, input_data: any) {\n try {\n if (!this.client) {\n return {\n status: 404,\n body: `Temporal client not found. Is the feature flag enabled?`,\n };\n }\n\n // Get workflow configuration\n const config = await this.getWorkflowConfig(name);\n\n // Process input data and generate workflow ID\n const [processedInput, workflowId] = this.processInputData(\n name,\n input_data,\n );\n\n console.log(\n `WorkflowClient - starting workflow: ${name} with config ${JSON.stringify(config)} and input_data ${JSON.stringify(processedInput)}`,\n );\n\n const handle = await this.client.workflow.start(\"ScriptWorkflow\", {\n args: [\n { workflow_name: name, execution_mode: \"start\" as const },\n processedInput,\n ],\n taskQueue: \"typescript-script-queue\",\n workflowId,\n workflowIdConflictPolicy: \"FAIL\",\n workflowIdReusePolicy: \"ALLOW_DUPLICATE\",\n retry: {\n // Temporal's maximumAttempts = total attempts (initial + retries)\n maximumAttempts: config.retries + 1,\n },\n workflowRunTimeout: config.timeout as StringValue,\n });\n\n return {\n status: 200,\n body: `Workflow started: ${name}. View it in the Temporal dashboard: http://localhost:8080/namespaces/default/workflows/${workflowId}/${handle.firstExecutionRunId}/history`,\n };\n } catch (error) {\n return {\n status: 400,\n body: `Error starting workflow: ${error}`,\n };\n }\n }\n\n async terminate(workflowId: string) {\n try {\n if (!this.client) {\n return {\n status: 404,\n body: `Temporal client not found. Is the feature flag enabled?`,\n };\n }\n\n const handle = this.client.workflow.getHandle(workflowId);\n await handle.terminate();\n\n return {\n status: 200,\n body: `Workflow terminated: ${workflowId}`,\n };\n } catch (error) {\n return {\n status: 400,\n body: `Error terminating workflow: ${error}`,\n };\n }\n }\n\n private async getWorkflowConfig(\n name: string,\n ): Promise<{ retries: number; timeout: string }> {\n const workflows = await getWorkflows();\n const workflow = workflows.get(name);\n if (workflow) {\n return {\n retries: workflow.config.retries || 3,\n timeout: workflow.config.timeout || \"1h\",\n };\n }\n\n throw new Error(`Workflow config not found for ${name}`);\n }\n\n private processInputData(name: string, input_data: any): [any, string] {\n let workflowId = name;\n if (input_data) {\n const hash = createHash(\"sha256\")\n .update(JSON.stringify(input_data))\n .digest(\"hex\")\n .slice(0, 16);\n workflowId = `${name}-${hash}`;\n }\n return [input_data, workflowId];\n }\n}\n\n/**\n * This looks similar to the client in runner.ts which is a worker.\n * Temporal SDK uses similar looking connection options & client,\n * but there are different libraries for a worker & client like this one\n * that triggers workflows.\n */\nexport async function getTemporalClient(\n temporalUrl: string,\n namespace: string,\n clientCert: string,\n clientKey: string,\n apiKey: string,\n): Promise<TemporalClient | undefined> {\n try {\n console.info(\n `<api> Using temporal_url: ${temporalUrl} and namespace: ${namespace}`,\n );\n\n let connectionOptions: ConnectionOptions = {\n address: temporalUrl,\n connectTimeout: \"3s\",\n };\n\n if (clientCert && clientKey) {\n // URL with mTLS uses gRPC namespace endpoint which is what temporalUrl already is\n console.log(\"Using TLS for secure Temporal\");\n const cert = await fs.readFileSync(clientCert);\n const key = await fs.readFileSync(clientKey);\n\n connectionOptions.tls = {\n clientCertPair: { crt: cert, key: key },\n };\n } else if (apiKey) {\n console.log(\"Using API key for secure Temporal\");\n // URL with API key uses gRPC regional endpoint\n connectionOptions.address = \"us-west1.gcp.api.temporal.io:7233\";\n connectionOptions.apiKey = apiKey;\n connectionOptions.tls = {};\n connectionOptions.metadata = {\n \"temporal-namespace\": namespace,\n };\n }\n\n console.log(`<api> Connecting to Temporal at ${connectionOptions.address}`);\n const connection = await Connection.connect(connectionOptions);\n const client = new TemporalClient({ connection, namespace });\n console.log(\"<api> Connected to Temporal server\");\n\n return client;\n } catch (error) {\n console.warn(`Failed to connect to Temporal. Is the feature flag enabled?`);\n console.warn(error);\n return undefined;\n }\n}\n\nexport const ApiHelpers = {\n column: (value: string) => [\"Identifier\", value] as [string, string],\n table: (value: string) => [\"Identifier\", value] as [string, string],\n};\n\n/** @deprecated Use ApiHelpers instead. */\nexport const ConsumptionHelpers = ApiHelpers;\n\nexport function joinQueries({\n values,\n separator = \",\",\n prefix = \"\",\n suffix = \"\",\n}: {\n values: readonly RawValue[];\n separator?: string;\n prefix?: string;\n suffix?: string;\n}) {\n if (values.length === 0) {\n throw new TypeError(\n \"Expected `join([])` to be called with an array of multiple elements, but got an empty array\",\n );\n }\n\n return new Sql(\n [prefix, ...Array(values.length - 1).fill(separator), suffix],\n values,\n );\n}\n","import http from \"http\";\nimport type { MooseUtils } from \"./helpers\";\n\n/**\n * @deprecated Use `getMooseUtils()` from '@514labs/moose-lib' instead.\n *\n * This synchronous function extracts MooseUtils from a request object that was\n * injected by Moose runtime middleware. It returns undefined if not running\n * in a Moose-managed context.\n *\n * Migration: Replace with the async version:\n * ```typescript\n * // Old (sync, deprecated):\n * import { getMooseUtilsFromRequest } from '@514labs/moose-lib';\n * const moose = getMooseUtilsFromRequest(req);\n *\n * // New (async, recommended):\n * import { getMooseUtils } from '@514labs/moose-lib';\n * const moose = await getMooseUtils();\n * ```\n *\n * @param req - The HTTP request object containing injected moose utilities\n * @returns MooseUtils if available on the request, undefined otherwise\n */\nexport function getMooseUtilsFromRequest(\n req: http.IncomingMessage | any,\n): MooseUtils | undefined {\n console.warn(\n \"[DEPRECATED] getMooseUtilsFromRequest() is deprecated. \" +\n \"Import getMooseUtils from '@514labs/moose-lib' and call it without parameters: \" +\n \"const { client, sql } = await getMooseUtils();\",\n );\n return (req as any).moose;\n}\n\n/**\n * @deprecated Use `getMooseUtils()` from '@514labs/moose-lib' instead.\n *\n * This is a legacy alias for getMooseUtilsFromRequest. The main getMooseUtils\n * export from '@514labs/moose-lib' is now async and does not require a request parameter.\n *\n * BREAKING CHANGE WARNING: The new getMooseUtils() returns Promise<MooseUtils>,\n * not MooseUtils | undefined. You must await the result:\n * ```typescript\n * const moose = await getMooseUtils(); // New async API\n * ```\n */\nexport const getLegacyMooseUtils = getMooseUtilsFromRequest;\n\n/**\n * @deprecated No longer needed. Use getMooseUtils() directly instead.\n * Moose now handles utility injection automatically when injectMooseUtils is true.\n */\nexport function expressMiddleware() {\n console.warn(\n \"[DEPRECATED] expressMiddleware() is deprecated. \" +\n \"Use getMooseUtils() directly or rely on injectMooseUtils config.\",\n );\n return (req: any, res: any, next: any) => {\n // Maintain backwards compat: copy req.raw.moose to req.moose if present\n if (!req.moose && req.raw && (req.raw as any).moose) {\n req.moose = (req.raw as any).moose;\n }\n next();\n };\n}\n\n/**\n * @deprecated Use MooseUtils from helpers.ts instead.\n */\nexport interface ExpressRequestWithMoose {\n moose?: MooseUtils;\n}\n","export interface TaskFunction {\n (input?: any): Promise<{ task: string; data: any }>;\n}\n\nexport interface TaskConfig {\n retries: number;\n}\n\nexport interface TaskDefinition {\n task: TaskFunction;\n config?: TaskConfig;\n}\n","import { createClient, RedisClientType } from \"redis\";\n\n// Module-level singleton instance and initialization promise\nlet instance: MooseCache | null = null;\nlet initPromise: Promise<MooseCache> | null = null;\n\ntype SupportedTypes = string | object;\n\nexport class MooseCache {\n private client: RedisClientType;\n private isConnected: boolean = false;\n private readonly keyPrefix: string;\n private disconnectTimer: NodeJS.Timeout | null = null;\n private readonly idleTimeout: number;\n private connectPromise: Promise<void> | null = null;\n\n private constructor() {\n const redisUrl =\n process.env.MOOSE_REDIS_CONFIG__URL || \"redis://127.0.0.1:6379\";\n const prefix = process.env.MOOSE_REDIS_CONFIG__KEY_PREFIX || \"MS\";\n // 30 seconds of inactivity before disconnecting\n this.idleTimeout =\n parseInt(process.env.MOOSE_REDIS_CONFIG__IDLE_TIMEOUT || \"30\", 10) * 1000;\n this.keyPrefix = `${prefix}::moosecache::`;\n\n this.client = createClient({\n url: redisUrl,\n });\n\n process.on(\"SIGTERM\", this.gracefulShutdown);\n process.on(\"SIGINT\", this.gracefulShutdown);\n\n this.client.on(\"error\", async (err: Error) => {\n console.error(\"TS Redis client error:\", err);\n await this.disconnect();\n });\n\n this.client.on(\"connect\", () => {\n this.isConnected = true;\n console.log(\"TS Redis client connected\");\n });\n\n this.client.on(\"end\", () => {\n this.isConnected = false;\n console.log(\"TS Redis client disconnected\");\n this.clearDisconnectTimer();\n });\n }\n\n private clearDisconnectTimer(): void {\n if (this.disconnectTimer) {\n clearTimeout(this.disconnectTimer);\n this.disconnectTimer = null;\n }\n }\n\n private resetDisconnectTimer(): void {\n this.clearDisconnectTimer();\n this.disconnectTimer = setTimeout(async () => {\n if (this.isConnected) {\n console.log(\"TS Redis client disconnecting due to inactivity\");\n await this.disconnect();\n }\n }, this.idleTimeout);\n }\n\n private async ensureConnected(): Promise<void> {\n if (!this.isConnected) {\n await this.connect();\n }\n this.resetDisconnectTimer();\n }\n\n private async connect(): Promise<void> {\n // If already connected, return immediately\n if (this.isConnected) {\n return;\n }\n\n // If connection is in progress, wait for it\n // This prevents race conditions when multiple callers try to reconnect\n // simultaneously after a disconnection\n if (this.connectPromise) {\n return this.connectPromise;\n }\n\n // Start connection\n this.connectPromise = (async () => {\n try {\n await this.client.connect();\n this.resetDisconnectTimer();\n } catch (error) {\n // Reset the promise on error so retries can work\n this.connectPromise = null;\n throw error;\n }\n })();\n\n return this.connectPromise;\n }\n\n private async gracefulShutdown(): Promise<void> {\n if (this.isConnected) {\n await this.disconnect();\n }\n process.exit(0);\n }\n\n private getPrefixedKey(key: string): string {\n return `${this.keyPrefix}${key}`;\n }\n\n /**\n * Gets the singleton instance of MooseCache. Creates a new instance if one doesn't exist.\n * The client will automatically connect to Redis and handle reconnection if needed.\n *\n * @returns Promise<MooseCache> The singleton instance of MooseCache\n * @example\n * const cache = await MooseCache.get();\n */\n public static async get(): Promise<MooseCache> {\n // If we already have an instance, return it immediately\n if (instance) {\n return instance;\n }\n\n // If initialization is already in progress, wait for it\n // This prevents race conditions where multiple concurrent calls to get()\n // would each create their own instance and connection\n //\n // A simple singleton pattern (just checking if instance exists) isn't enough\n // because multiple async calls can check \"if (!instance)\" simultaneously,\n // find it's null, and each try to create their own instance before any\n // of them finish setting the instance variable\n if (initPromise) {\n return initPromise;\n }\n\n // Start initialization\n // We store the promise immediately so that any concurrent calls\n // will wait for this same initialization instead of starting their own\n initPromise = (async () => {\n try {\n const newInstance = new MooseCache();\n await newInstance.connect();\n instance = newInstance;\n return newInstance;\n } catch (error) {\n // Reset the promise on error so retries can work\n initPromise = null;\n throw error;\n }\n })();\n\n return initPromise;\n }\n\n /**\n * Sets a value in the cache. Objects are automatically JSON stringified.\n *\n * @param key - The key to store the value under\n * @param value - The value to store. Can be a string or any object (will be JSON stringified)\n * @param ttlSeconds - Optional time-to-live in seconds. If not provided, defaults to 1 hour (3600 seconds).\n * Must be a non-negative number. If 0, the key will expire immediately.\n * @example\n * // Store a string\n * await cache.set(\"foo\", \"bar\");\n *\n * // Store an object with custom TTL\n * await cache.set(\"foo:config\", { baz: 123, qux: true }, 60); // expires in 1 minute\n *\n * // This is essentially a get-set, which returns the previous value if it exists.\n * // You can create logic to only do work for the first time.\n * const value = await cache.set(\"testSessionId\", \"true\");\n * if (value) {\n * // Cache was set before, return\n * } else {\n * // Cache was set for first time, do work\n * }\n */\n public async set(\n key: string,\n value: string | object,\n ttlSeconds?: number,\n ): Promise<string | null> {\n try {\n // Validate TTL\n if (ttlSeconds !== undefined && ttlSeconds < 0) {\n throw new Error(\"ttlSeconds must be a non-negative number\");\n }\n\n await this.ensureConnected();\n const prefixedKey = this.getPrefixedKey(key);\n const stringValue =\n typeof value === \"object\" ? JSON.stringify(value) : value;\n\n // Use provided TTL or default to 1 hour\n const ttl = ttlSeconds ?? 3600;\n return await this.client.set(prefixedKey, stringValue, {\n EX: ttl,\n GET: true,\n });\n } catch (error) {\n console.error(`Error setting cache key ${key}:`, error);\n throw error;\n }\n }\n\n /**\n * Retrieves a value from the cache. Attempts to parse the value as JSON if possible.\n *\n * @param key - The key to retrieve\n * @returns Promise<T | null> The value, parsed as type T if it was JSON, or as string if not. Returns null if key doesn't exist\n * @example\n * // Get a string\n * const value = await cache.get(\"foo\");\n *\n * // Get and parse an object with type safety\n * interface Config { baz: number; qux: boolean; }\n * const config = await cache.get<Config>(\"foo:config\");\n */\n public async get<T extends SupportedTypes = string>(\n key: string,\n ): Promise<T | null> {\n try {\n await this.ensureConnected();\n const prefixedKey = this.getPrefixedKey(key);\n const value = await this.client.get(prefixedKey);\n\n if (value === null) return null;\n\n // Note: We can't check if T is string at runtime because TypeScript types are erased.\n // Instead, we try to parse as JSON and return the original string if that fails.\n try {\n const parsed = JSON.parse(value);\n // Only return parsed value if it's an object\n if (typeof parsed === \"object\" && parsed !== null) {\n return parsed as T;\n }\n // If parsed value isn't an object, return as string\n return value as T;\n } catch {\n // If JSON parse fails, return as string\n return value as T;\n }\n } catch (error) {\n console.error(`Error getting cache key ${key}:`, error);\n throw error;\n }\n }\n\n /**\n * Deletes a specific key from the cache.\n *\n * @param key - The key to delete\n * @example\n * await cache.delete(\"foo\");\n */\n public async delete(key: string): Promise<void> {\n try {\n await this.ensureConnected();\n const prefixedKey = this.getPrefixedKey(key);\n await this.client.del(prefixedKey);\n } catch (error) {\n console.error(`Error deleting cache key ${key}:`, error);\n throw error;\n }\n }\n\n /**\n * Deletes all keys that start with the given prefix.\n *\n * @param keyPrefix - The prefix of keys to delete\n * @example\n * // Delete all keys starting with \"foo\"\n * await cache.clearKeys(\"foo\");\n */\n public async clearKeys(keyPrefix: string): Promise<void> {\n try {\n await this.ensureConnected();\n const prefixedKey = this.getPrefixedKey(keyPrefix);\n const keys = await this.client.keys(`${prefixedKey}*`);\n if (keys.length > 0) {\n await this.client.del(keys);\n }\n } catch (error) {\n console.error(\n `Error clearing cache keys with prefix ${keyPrefix}:`,\n error,\n );\n throw error;\n }\n }\n\n /**\n * Deletes all keys in the cache\n *\n * @example\n * await cache.clear();\n */\n public async clear(): Promise<void> {\n try {\n await this.ensureConnected();\n const keys = await this.client.keys(`${this.keyPrefix}*`);\n if (keys.length > 0) {\n await this.client.del(keys);\n }\n } catch (error) {\n console.error(\"Error clearing cache:\", error);\n throw error;\n }\n }\n\n /**\n * Manually disconnects the Redis client. The client will automatically reconnect\n * when the next operation is performed.\n *\n * @example\n * await cache.disconnect();\n */\n public async disconnect(): Promise<void> {\n this.clearDisconnectTimer();\n this.connectPromise = null;\n if (this.isConnected) {\n await this.client.quit();\n }\n }\n}\n","import path from \"node:path\";\nimport * as toml from \"toml\";\n\n/**\n * ClickHouse configuration from moose.config.toml\n */\nexport interface ClickHouseConfig {\n host: string;\n host_port: number;\n user: string;\n password: string;\n db_name: string;\n use_ssl?: boolean;\n native_port?: number;\n}\n\n/**\n * Redpanda/Kafka configuration from moose.config.toml\n */\nexport interface KafkaConfig {\n /** Broker connection string (e.g., \"host:port\" or comma-separated list) */\n broker: string;\n /** Message timeout in milliseconds */\n message_timeout_ms: number;\n /** Default retention period in milliseconds */\n retention_ms: number;\n /** Topic replication factor */\n replication_factor?: number;\n /** SASL username for authentication, if required */\n sasl_username?: string;\n /** SASL password for authentication, if required */\n sasl_password?: string;\n /** SASL mechanism (e.g., \"PLAIN\", \"SCRAM-SHA-256\") */\n sasl_mechanism?: string;\n /** Security protocol (e.g., \"SASL_SSL\", \"PLAINTEXT\") */\n security_protocol?: string;\n /** Optional namespace used as a prefix for topics */\n namespace?: string;\n /** Optional Confluent Schema Registry URL */\n schema_registry_url?: string;\n}\n\n/**\n * Project configuration from moose.config.toml\n */\nexport interface ProjectConfig {\n language: string;\n clickhouse_config: ClickHouseConfig;\n redpanda_config?: KafkaConfig;\n /**\n * Redpanda/Kafka configuration. Previously named `redpanda_config` in some places.\n * Prefer `kafka_config` but support both for backward compatibility.\n */\n\n kafka_config?: KafkaConfig;\n}\n\n/**\n * Error thrown when configuration cannot be found or parsed\n */\nexport class ConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigError\";\n }\n}\n\n/**\n * Walks up the directory tree to find moose.config.toml\n */\nasync function findConfigFile(\n startDir: string = process.cwd(),\n): Promise<string | null> {\n const fs = await import(\"node:fs\");\n\n let currentDir = path.resolve(startDir);\n\n while (true) {\n const configPath = path.join(currentDir, \"moose.config.toml\");\n if (fs.existsSync(configPath)) {\n return configPath;\n }\n\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) {\n // Reached root directory\n break;\n }\n currentDir = parentDir;\n }\n\n return null;\n}\n\n/**\n * Reads and parses the project configuration from moose.config.toml\n */\nexport async function readProjectConfig(): Promise<ProjectConfig> {\n const fs = await import(\"node:fs\");\n const configPath = await findConfigFile();\n if (!configPath) {\n throw new ConfigError(\n \"moose.config.toml not found in current directory or any parent directory\",\n );\n }\n\n try {\n const configContent = fs.readFileSync(configPath, \"utf-8\");\n const config = toml.parse(configContent) as ProjectConfig;\n return config;\n } catch (error) {\n throw new ConfigError(`Failed to parse moose.config.toml: ${error}`);\n }\n}\n","import { readProjectConfig } from \"./configFile\";\n\ninterface RuntimeClickHouseConfig {\n host: string;\n port: string;\n username: string;\n password: string;\n database: string;\n useSSL: boolean;\n}\n\ninterface RuntimeKafkaConfig {\n broker: string;\n messageTimeoutMs: number;\n saslUsername?: string;\n saslPassword?: string;\n saslMechanism?: string;\n securityProtocol?: string;\n namespace?: string;\n schemaRegistryUrl?: string;\n}\n\nclass ConfigurationRegistry {\n private static instance: ConfigurationRegistry;\n private clickhouseConfig?: RuntimeClickHouseConfig;\n private kafkaConfig?: RuntimeKafkaConfig;\n\n static getInstance(): ConfigurationRegistry {\n if (!ConfigurationRegistry.instance) {\n ConfigurationRegistry.instance = new ConfigurationRegistry();\n }\n return ConfigurationRegistry.instance;\n }\n\n setClickHouseConfig(config: RuntimeClickHouseConfig): void {\n this.clickhouseConfig = config;\n }\n\n setKafkaConfig(config: RuntimeKafkaConfig): void {\n this.kafkaConfig = config;\n }\n\n private _env(name: string): string | undefined {\n const value = process.env[name];\n if (value === undefined) return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n }\n\n private _parseBool(value: string | undefined): boolean | undefined {\n if (value === undefined) return undefined;\n switch (value.trim().toLowerCase()) {\n case \"1\":\n case \"true\":\n case \"yes\":\n case \"on\":\n return true;\n case \"0\":\n case \"false\":\n case \"no\":\n case \"off\":\n return false;\n default:\n return undefined;\n }\n }\n\n async getClickHouseConfig(): Promise<RuntimeClickHouseConfig> {\n if (this.clickhouseConfig) {\n return this.clickhouseConfig;\n }\n\n // Fallback to reading from config file for backward compatibility\n const projectConfig = await readProjectConfig();\n const envHost = this._env(\"MOOSE_CLICKHOUSE_CONFIG__HOST\");\n const envPort = this._env(\"MOOSE_CLICKHOUSE_CONFIG__HOST_PORT\");\n const envUser = this._env(\"MOOSE_CLICKHOUSE_CONFIG__USER\");\n const envPassword = this._env(\"MOOSE_CLICKHOUSE_CONFIG__PASSWORD\");\n const envDb = this._env(\"MOOSE_CLICKHOUSE_CONFIG__DB_NAME\");\n const envUseSSL = this._parseBool(\n this._env(\"MOOSE_CLICKHOUSE_CONFIG__USE_SSL\"),\n );\n\n return {\n host: envHost ?? projectConfig.clickhouse_config.host,\n port: envPort ?? projectConfig.clickhouse_config.host_port.toString(),\n username: envUser ?? projectConfig.clickhouse_config.user,\n password: envPassword ?? projectConfig.clickhouse_config.password,\n database: envDb ?? projectConfig.clickhouse_config.db_name,\n useSSL:\n envUseSSL !== undefined ? envUseSSL : (\n projectConfig.clickhouse_config.use_ssl || false\n ),\n };\n }\n\n async getStandaloneClickhouseConfig(\n overrides?: Partial<RuntimeClickHouseConfig>,\n ): Promise<RuntimeClickHouseConfig> {\n if (this.clickhouseConfig) {\n return { ...this.clickhouseConfig, ...overrides };\n }\n\n const envHost = this._env(\"MOOSE_CLICKHOUSE_CONFIG__HOST\");\n const envPort = this._env(\"MOOSE_CLICKHOUSE_CONFIG__HOST_PORT\");\n const envUser = this._env(\"MOOSE_CLICKHOUSE_CONFIG__USER\");\n const envPassword = this._env(\"MOOSE_CLICKHOUSE_CONFIG__PASSWORD\");\n const envDb = this._env(\"MOOSE_CLICKHOUSE_CONFIG__DB_NAME\");\n const envUseSSL = this._parseBool(\n this._env(\"MOOSE_CLICKHOUSE_CONFIG__USE_SSL\"),\n );\n\n let projectConfig;\n try {\n projectConfig = await readProjectConfig();\n } catch (error) {\n projectConfig = null;\n }\n\n const defaults = {\n host: \"localhost\",\n port: \"18123\",\n username: \"default\",\n password: \"\",\n database: \"local\",\n useSSL: false,\n };\n\n return {\n host:\n overrides?.host ??\n envHost ??\n projectConfig?.clickhouse_config.host ??\n defaults.host,\n port:\n overrides?.port ??\n envPort ??\n projectConfig?.clickhouse_config.host_port.toString() ??\n defaults.port,\n username:\n overrides?.username ??\n envUser ??\n projectConfig?.clickhouse_config.user ??\n defaults.username,\n password:\n overrides?.password ??\n envPassword ??\n projectConfig?.clickhouse_config.password ??\n defaults.password,\n database:\n overrides?.database ??\n envDb ??\n projectConfig?.clickhouse_config.db_name ??\n defaults.database,\n useSSL:\n overrides?.useSSL ??\n envUseSSL ??\n projectConfig?.clickhouse_config.use_ssl ??\n defaults.useSSL,\n };\n }\n\n async getKafkaConfig(): Promise<RuntimeKafkaConfig> {\n if (this.kafkaConfig) {\n return this.kafkaConfig;\n }\n\n const projectConfig = await readProjectConfig();\n\n const envBroker =\n this._env(\"MOOSE_REDPANDA_CONFIG__BROKER\") ??\n this._env(\"MOOSE_KAFKA_CONFIG__BROKER\");\n const envMsgTimeout =\n this._env(\"MOOSE_REDPANDA_CONFIG__MESSAGE_TIMEOUT_MS\") ??\n this._env(\"MOOSE_KAFKA_CONFIG__MESSAGE_TIMEOUT_MS\");\n const envSaslUsername =\n this._env(\"MOOSE_REDPANDA_CONFIG__SASL_USERNAME\") ??\n this._env(\"MOOSE_KAFKA_CONFIG__SASL_USERNAME\");\n const envSaslPassword =\n this._env(\"MOOSE_REDPANDA_CONFIG__SASL_PASSWORD\") ??\n this._env(\"MOOSE_KAFKA_CONFIG__SASL_PASSWORD\");\n const envSaslMechanism =\n this._env(\"MOOSE_REDPANDA_CONFIG__SASL_MECHANISM\") ??\n this._env(\"MOOSE_KAFKA_CONFIG__SASL_MECHANISM\");\n const envSecurityProtocol =\n this._env(\"MOOSE_REDPANDA_CONFIG__SECURITY_PROTOCOL\") ??\n this._env(\"MOOSE_KAFKA_CONFIG__SECURITY_PROTOCOL\");\n const envNamespace =\n this._env(\"MOOSE_REDPANDA_CONFIG__NAMESPACE\") ??\n this._env(\"MOOSE_KAFKA_CONFIG__NAMESPACE\");\n const envSchemaRegistryUrl =\n this._env(\"MOOSE_REDPANDA_CONFIG__SCHEMA_REGISTRY_URL\") ??\n this._env(\"MOOSE_KAFKA_CONFIG__SCHEMA_REGISTRY_URL\");\n\n const fileKafka =\n projectConfig.kafka_config ?? projectConfig.redpanda_config;\n\n return {\n broker: envBroker ?? fileKafka?.broker ?? \"localhost:19092\",\n messageTimeoutMs:\n envMsgTimeout ?\n parseInt(envMsgTimeout, 10)\n : (fileKafka?.message_timeout_ms ?? 1000),\n saslUsername: envSaslUsername ?? fileKafka?.sasl_username,\n saslPassword: envSaslPassword ?? fileKafka?.sasl_password,\n saslMechanism: envSaslMechanism ?? fileKafka?.sasl_mechanism,\n securityProtocol: envSecurityProtocol ?? fileKafka?.security_protocol,\n namespace: envNamespace ?? fileKafka?.namespace,\n schemaRegistryUrl: envSchemaRegistryUrl ?? fileKafka?.schema_registry_url,\n };\n }\n\n hasRuntimeConfig(): boolean {\n return !!this.clickhouseConfig || !!this.kafkaConfig;\n }\n}\n\n(globalThis as any)._mooseConfigRegistry = ConfigurationRegistry.getInstance();\nexport type {\n ConfigurationRegistry,\n RuntimeClickHouseConfig,\n RuntimeKafkaConfig,\n};\n","import { MooseClient, QueryClient, MooseUtils } from \"./helpers\";\nimport { getClickhouseClient } from \"../commons\";\nimport { sql } from \"../sqlHelpers\";\nimport type { RuntimeClickHouseConfig } from \"../config/runtime\";\n\n// Cached utilities and initialization promise for standalone mode\nlet standaloneUtils: MooseUtils | null = null;\nlet initPromise: Promise<MooseUtils> | null = null;\n\n// Convert config to client config format\nconst toClientConfig = (config: {\n host: string;\n port: string;\n username: string;\n password: string;\n database: string;\n useSSL: boolean;\n}) => ({\n ...config,\n useSSL: config.useSSL ? \"true\" : \"false\",\n});\n\n/**\n * Get Moose utilities for database access and SQL queries.\n * Works in both Moose runtime and standalone contexts.\n *\n * **IMPORTANT**: This function is async and returns a Promise. You must await the result:\n * ```typescript\n * const moose = await getMooseUtils(); // Correct\n * const moose = getMooseUtils(); // WRONG - returns Promise, not MooseUtils!\n * ```\n *\n * **Breaking Change from v1.x**: This function signature changed from sync to async.\n * If you were using the old sync API that extracted utils from a request object,\n * use `getMooseUtilsFromRequest(req)` for backward compatibility (deprecated).\n *\n * @param req - DEPRECATED: Request parameter is no longer needed and will be ignored.\n * If you need to extract moose from a request, use getMooseUtilsFromRequest().\n * @returns Promise resolving to MooseUtils with client and sql utilities.\n *\n * @example\n * ```typescript\n * const { client, sql } = await getMooseUtils();\n * const result = await client.query.execute(sql`SELECT * FROM table`);\n * ```\n */\nexport async function getMooseUtils(req?: any): Promise<MooseUtils> {\n // Deprecation warning if req passed\n if (req !== undefined) {\n console.warn(\n \"[DEPRECATED] getMooseUtils(req) no longer requires a request parameter. \" +\n \"Use getMooseUtils() instead.\",\n );\n }\n\n // Check if running in Moose runtime\n const runtimeContext = (globalThis as any)._mooseRuntimeContext;\n\n if (runtimeContext) {\n // In Moose runtime - use existing connections\n return {\n client: runtimeContext.client,\n sql: sql,\n jwt: runtimeContext.jwt,\n };\n }\n\n // Standalone mode - use cached client or create new one\n if (standaloneUtils) {\n return standaloneUtils;\n }\n\n // If initialization is in progress, wait for it\n if (initPromise) {\n return initPromise;\n }\n\n // Start initialization\n initPromise = (async () => {\n await import(\"../config/runtime\");\n const configRegistry = (globalThis as any)._mooseConfigRegistry;\n\n if (!configRegistry) {\n throw new Error(\n \"Moose not initialized. Ensure you're running within a Moose app \" +\n \"or have proper configuration set up.\",\n );\n }\n\n const clickhouseConfig =\n await configRegistry.getStandaloneClickhouseConfig();\n\n const clickhouseClient = getClickhouseClient(\n toClientConfig(clickhouseConfig),\n );\n const queryClient = new QueryClient(clickhouseClient, \"standalone\");\n const mooseClient = new MooseClient(queryClient);\n\n standaloneUtils = {\n client: mooseClient,\n sql: sql,\n jwt: undefined,\n };\n return standaloneUtils;\n })();\n\n try {\n return await initPromise;\n } finally {\n initPromise = null;\n }\n}\n\n/**\n * @deprecated Use getMooseUtils() instead.\n * Creates a Moose client for database access.\n */\nexport async function getMooseClients(\n config?: Partial<RuntimeClickHouseConfig>,\n): Promise<{ client: MooseClient }> {\n console.warn(\n \"[DEPRECATED] getMooseClients() is deprecated. Use getMooseUtils() instead.\",\n );\n\n // If custom config provided, create a one-off client (don't cache)\n if (config && Object.keys(config).length > 0) {\n await import(\"../config/runtime\");\n const configRegistry = (globalThis as any)._mooseConfigRegistry;\n\n if (!configRegistry) {\n throw new Error(\n \"Configuration registry not initialized. Ensure the Moose framework is properly set up.\",\n );\n }\n\n const clickhouseConfig =\n await configRegistry.getStandaloneClickhouseConfig(config);\n\n const clickhouseClient = getClickhouseClient(\n toClientConfig(clickhouseConfig),\n );\n const queryClient = new QueryClient(clickhouseClient, \"standalone\");\n const mooseClient = new MooseClient(queryClient);\n\n return { client: mooseClient };\n }\n\n // No custom config - delegate to getMooseUtils\n const utils = await getMooseUtils();\n return { client: utils.client };\n}\n","import type {\n Column,\n DataType,\n Nested,\n ArrayType,\n} from \"../dataModels/dataModelTypes\";\n\n/**\n * Annotation key used to mark DateTime fields that should remain as strings\n * rather than being parsed into Date objects at runtime.\n */\nexport const STRING_DATE_ANNOTATION = \"stringDate\";\n\n/**\n * Type guard to check if a DataType is a nullable wrapper\n */\nfunction isNullableType(dt: DataType): dt is { nullable: DataType } {\n return (\n typeof dt === \"object\" &&\n dt !== null &&\n \"nullable\" in dt &&\n typeof dt.nullable !== \"undefined\"\n );\n}\n\n/**\n * Type guard to check if a DataType is a Nested type\n */\nfunction isNestedType(dt: DataType): dt is Nested {\n return (\n typeof dt === \"object\" &&\n dt !== null &&\n \"columns\" in dt &&\n Array.isArray(dt.columns)\n );\n}\n\n/**\n * Type guard to check if a DataType is an ArrayType\n */\nfunction isArrayType(dt: DataType): dt is ArrayType {\n return (\n typeof dt === \"object\" &&\n dt !== null &&\n \"elementType\" in dt &&\n typeof dt.elementType !== \"undefined\"\n );\n}\n\n/**\n * Revives ISO 8601 date strings into Date objects during JSON parsing\n * This is useful for automatically converting date strings to Date objects\n */\nexport function jsonDateReviver(key: string, value: unknown): unknown {\n const iso8601Format =\n /^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)$/;\n\n if (typeof value === \"string\" && iso8601Format.test(value)) {\n return new Date(value);\n }\n\n return value;\n}\n\n/**\n * Checks if a DataType represents a datetime column (not just date)\n * AND if the column should be parsed from string to Date at runtime\n *\n * Note: Date and Date16 are date-only types and should remain as strings.\n * Only DateTime types are candidates for parsing to JavaScript Date objects.\n */\nfunction isDateType(dataType: DataType, annotations: [string, any][]): boolean {\n // Check if this is marked as a string-based date (from typia.tags.Format)\n // If so, it should remain as a string, not be parsed to Date\n if (\n annotations.some(\n ([key, value]) => key === STRING_DATE_ANNOTATION && value === true,\n )\n ) {\n return false;\n }\n\n if (typeof dataType === \"string\") {\n // Only DateTime types should be parsed to Date objects\n // Date and Date16 are date-only and should stay as strings\n return dataType === \"DateTime\" || dataType.startsWith(\"DateTime(\");\n }\n // Handle nullable wrapper\n if (isNullableType(dataType)) {\n return isDateType(dataType.nullable, annotations);\n }\n return false;\n}\n\n/**\n * Type of mutation to apply to a field during parsing\n */\nexport type Mutation = \"parseDate\"; // | \"parseBigInt\" - to be added later\n\n/**\n * Recursive tuple array structure representing field mutation operations\n * Each entry is [fieldName, mutation]:\n * - mutation is Mutation[] for leaf fields that need operations applied\n * - mutation is FieldMutations for nested objects/arrays (auto-applies to array elements)\n */\nexport type FieldMutations = [string, Mutation[] | FieldMutations][];\n\n/**\n * Recursively builds field mutations from column definitions\n *\n * @param columns - Array of Column definitions\n * @returns Tuple array of field mutations\n */\nfunction buildFieldMutations(columns: Column[]): FieldMutations {\n const mutations: FieldMutations = [];\n\n for (const column of columns) {\n const dataType = column.data_type;\n\n // Check if this is a date field that should be converted\n if (isDateType(dataType, column.annotations)) {\n mutations.push([column.name, [\"parseDate\"]]);\n continue;\n }\n\n // Handle nested structures\n if (typeof dataType === \"object\" && dataType !== null) {\n // Handle nullable wrapper\n let unwrappedType: DataType = dataType;\n if (isNullableType(dataType)) {\n unwrappedType = dataType.nullable;\n }\n\n // Handle nested objects\n if (isNestedType(unwrappedType)) {\n const nestedMutations = buildFieldMutations(unwrappedType.columns);\n if (nestedMutations.length > 0) {\n mutations.push([column.name, nestedMutations]);\n }\n continue;\n }\n\n // Handle arrays with nested columns\n // The mutations will be auto-applied to each array element at runtime\n if (isArrayType(unwrappedType)) {\n const elementType = unwrappedType.elementType;\n if (isNestedType(elementType)) {\n const nestedMutations = buildFieldMutations(elementType.columns);\n if (nestedMutations.length > 0) {\n mutations.push([column.name, nestedMutations]);\n }\n continue;\n }\n }\n }\n }\n\n return mutations;\n}\n\n/**\n * Applies a mutation operation to a field value\n *\n * @param value - The value to handle\n * @param mutation - The mutation operation to apply\n * @returns The handled value\n */\nfunction applyMutation(value: any, mutation: Mutation): any {\n if (mutation === \"parseDate\") {\n if (typeof value === \"string\") {\n try {\n const date = new Date(value);\n return !isNaN(date.getTime()) ? date : value;\n } catch {\n return value;\n }\n }\n }\n return value;\n}\n\n/**\n * Recursively mutates an object by applying field mutations\n *\n * @param obj - The object to mutate\n * @param mutations - The field mutations to apply\n */\nfunction applyFieldMutations(obj: any, mutations: FieldMutations): void {\n if (!obj || typeof obj !== \"object\") {\n return;\n }\n\n for (const [fieldName, mutation] of mutations) {\n if (!(fieldName in obj)) {\n continue;\n }\n\n if (Array.isArray(mutation)) {\n // Check if it's Mutation[] (leaf) or FieldMutations (nested)\n if (mutation.length > 0 && typeof mutation[0] === \"string\") {\n // It's Mutation[] - apply operations to this field\n const operations = mutation as Mutation[];\n for (const operation of operations) {\n obj[fieldName] = applyMutation(obj[fieldName], operation);\n }\n } else {\n // It's FieldMutations - recurse into nested structure\n const nestedMutations = mutation as FieldMutations;\n const fieldValue = obj[fieldName];\n\n if (Array.isArray(fieldValue)) {\n // Auto-apply to each array element\n for (const item of fieldValue) {\n applyFieldMutations(item, nestedMutations);\n }\n } else if (fieldValue && typeof fieldValue === \"object\") {\n // Apply to nested object\n applyFieldMutations(fieldValue, nestedMutations);\n }\n }\n }\n }\n}\n\n/**\n * Pre-builds field mutations from column schema for efficient reuse\n *\n * @param columns - Column definitions from the Stream schema\n * @returns Field mutations tuple array, or undefined if no columns\n *\n * @example\n * ```typescript\n * const fieldMutations = buildFieldMutationsFromColumns(stream.columnArray);\n * // Reuse fieldMutations for every message\n * ```\n */\nexport function buildFieldMutationsFromColumns(\n columns: Column[] | undefined,\n): FieldMutations | undefined {\n if (!columns || columns.length === 0) {\n return undefined;\n }\n const mutations = buildFieldMutations(columns);\n return mutations.length > 0 ? mutations : undefined;\n}\n\n/**\n * Applies field mutations to parsed data\n * Mutates the object in place for performance\n *\n * @param data - The parsed JSON object to mutate\n * @param fieldMutations - Pre-built field mutations from buildFieldMutationsFromColumns\n *\n * @example\n * ```typescript\n * const fieldMutations = buildFieldMutationsFromColumns(stream.columnArray);\n * const data = JSON.parse(jsonString);\n * mutateParsedJson(data, fieldMutations);\n * // data now has transformations applied per the field mutations\n * ```\n */\nexport function mutateParsedJson(\n data: any,\n fieldMutations: FieldMutations | undefined,\n): void {\n if (!fieldMutations || !data) {\n return;\n }\n\n applyFieldMutations(data, fieldMutations);\n}\n","import { parse } from \"csv-parse\";\nimport { jsonDateReviver } from \"./json\";\n\n/**\n * Configuration for CSV parsing options\n */\nexport interface CSVParsingConfig {\n /** CSV delimiter character */\n delimiter: string;\n /** Whether to treat first row as headers */\n columns?: boolean;\n /** Whether to skip empty lines */\n skipEmptyLines?: boolean;\n /** Whether to trim whitespace from values */\n trim?: boolean;\n}\n\n/**\n * Configuration for JSON parsing options\n */\nexport interface JSONParsingConfig {\n /** Custom reviver function for JSON.parse */\n reviver?: (key: string, value: any) => any;\n}\n\n/**\n * Parses CSV content into an array of objects\n *\n * @param content - The CSV content as a string\n * @param config - CSV parsing configuration\n * @returns Promise resolving to an array of parsed objects\n */\nexport function parseCSV<T = Record<string, any>>(\n content: string,\n config: CSVParsingConfig,\n): Promise<T[]> {\n return new Promise((resolve, reject) => {\n const results: T[] = [];\n\n parse(content, {\n delimiter: config.delimiter,\n columns: config.columns ?? true,\n skip_empty_lines: config.skipEmptyLines ?? true,\n trim: config.trim ?? true,\n })\n .on(\"data\", (row) => {\n results.push(row as T);\n })\n .on(\"end\", () => {\n resolve(results);\n })\n .on(\"error\", (error) => {\n reject(error);\n });\n });\n}\n\n/**\n * Parses JSON content into an array of objects\n *\n * @param content - The JSON content as a string\n * @param config - JSON parsing configuration\n * @returns Array of parsed objects\n */\nexport function parseJSON<T = any>(\n content: string,\n config: JSONParsingConfig = {},\n): T[] {\n try {\n const parsed = JSON.parse(content, config.reviver);\n\n // Handle both array and single object cases\n if (Array.isArray(parsed)) {\n return parsed as T[];\n } else {\n return [parsed as T];\n }\n } catch (error) {\n throw new Error(\n `Failed to parse JSON: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n}\n\n/**\n * Parses JSON content with automatic date revival\n *\n * @param content - The JSON content as a string\n * @returns Array of parsed objects with Date objects for ISO 8601 strings\n */\nexport function parseJSONWithDates<T = any>(content: string): T[] {\n return parseJSON<T>(content, { reviver: jsonDateReviver });\n}\n\n/**\n * Type guard to check if a value is a valid CSV delimiter\n */\nexport function isValidCSVDelimiter(delimiter: string): boolean {\n return delimiter.length === 1 && !/\\s/.test(delimiter);\n}\n\n/**\n * Common CSV delimiters\n */\nexport const CSV_DELIMITERS = {\n COMMA: \",\",\n TAB: \"\\t\",\n SEMICOLON: \";\",\n PIPE: \"|\",\n} as const;\n\n/**\n * Default CSV parsing configuration\n */\nexport const DEFAULT_CSV_CONFIG: CSVParsingConfig = {\n delimiter: CSV_DELIMITERS.COMMA,\n columns: true,\n skipEmptyLines: true,\n trim: true,\n};\n\n/**\n * Default JSON parsing configuration with date revival\n */\nexport const DEFAULT_JSON_CONFIG: JSONParsingConfig = {\n reviver: jsonDateReviver,\n};\n","import { IsTuple } from \"typia/lib/typings/IsTuple\";\n\nexport * from \"./dataParser\";\n\ntype HasFunctionField<T> =\n T extends object ?\n {\n [K in keyof T]: T[K] extends Function ? true : false;\n }[keyof T] extends false | undefined ?\n false\n : true\n : false;\n\n// convert `field?: Type` to `field: Type | undefined` to avoid homomorphic type\ntype OptionalToUndefinedable<T> = { [K in {} & keyof T]: T[K] };\ntype StripInterfaceFields<T> = { [K in keyof T]: StripDateIntersection<T[K]> };\n\n/**\n * `Date & ...` is considered \"nonsensible intersection\" by typia,\n * causing JSON schema to fail.\n * This helper type recursively cleans up the intersection type tagging.\n */\nexport type StripDateIntersection<T> =\n T extends Date ?\n Date extends T ?\n Date\n : T\n : T extends ReadonlyArray<unknown> ?\n IsTuple<T> extends true ? StripDateFromTuple<T>\n : T extends ReadonlyArray<infer U> ?\n ReadonlyArray<U> extends T ?\n ReadonlyArray<StripDateIntersection<U>>\n : Array<StripDateIntersection<U>>\n : T extends Array<infer U> ? Array<StripDateIntersection<U>>\n : T // this catchall should be unreachable\n : // do not touch other classes\n true extends HasFunctionField<T> ? T\n : T extends object ? StripInterfaceFields<OptionalToUndefinedable<T>>\n : T;\n\n// infer fails in a recursive definition if an intersection type tag is present\ntype StripDateFromTuple<T extends readonly any[]> =\n T extends (\n [\n infer T1,\n infer T2,\n infer T3,\n infer T4,\n infer T5,\n infer T6,\n infer T7,\n infer T8,\n infer T9,\n infer T10,\n ]\n ) ?\n [\n StripDateIntersection<T1>,\n StripDateIntersection<T2>,\n StripDateIntersection<T3>,\n StripDateIntersection<T4>,\n StripDateIntersection<T5>,\n StripDateIntersection<T6>,\n StripDateIntersection<T7>,\n StripDateIntersection<T8>,\n StripDateIntersection<T9>,\n StripDateIntersection<T10>,\n ]\n : T extends (\n [\n infer T1,\n infer T2,\n infer T3,\n infer T4,\n infer T5,\n infer T6,\n infer T7,\n infer T8,\n infer T9,\n ]\n ) ?\n [\n StripDateIntersection<T1>,\n StripDateIntersection<T2>,\n StripDateIntersection<T3>,\n StripDateIntersection<T4>,\n StripDateIntersection<T5>,\n StripDateIntersection<T6>,\n StripDateIntersection<T7>,\n StripDateIntersection<T8>,\n StripDateIntersection<T9>,\n ]\n : T extends (\n [\n infer T1,\n infer T2,\n infer T3,\n infer T4,\n infer T5,\n infer T6,\n infer T7,\n infer T8,\n ]\n ) ?\n [\n StripDateIntersection<T1>,\n StripDateIntersection<T2>,\n StripDateIntersection<T3>,\n StripDateIntersection<T4>,\n StripDateIntersection<T5>,\n StripDateIntersection<T6>,\n StripDateIntersection<T7>,\n StripDateIntersection<T8>,\n ]\n : T extends (\n [infer T1, infer T2, infer T3, infer T4, infer T5, infer T6, infer T7]\n ) ?\n [\n StripDateIntersection<T1>,\n StripDateIntersection<T2>,\n StripDateIntersection<T3>,\n StripDateIntersection<T4>,\n StripDateIntersection<T5>,\n StripDateIntersection<T6>,\n StripDateIntersection<T7>,\n ]\n : T extends [infer T1, infer T2, infer T3, infer T4, infer T5, infer T6] ?\n [\n StripDateIntersection<T1>,\n StripDateIntersection<T2>,\n StripDateIntersection<T3>,\n StripDateIntersection<T4>,\n StripDateIntersection<T5>,\n StripDateIntersection<T6>,\n ]\n : T extends [infer T1, infer T2, infer T3, infer T4, infer T5] ?\n [\n StripDateIntersection<T1>,\n StripDateIntersection<T2>,\n StripDateIntersection<T3>,\n StripDateIntersection<T4>,\n StripDateIntersection<T5>,\n ]\n : T extends [infer T1, infer T2, infer T3, infer T4] ?\n [\n StripDateIntersection<T1>,\n StripDateIntersection<T2>,\n StripDateIntersection<T3>,\n StripDateIntersection<T4>,\n ]\n : T extends [infer T1, infer T2, infer T3] ?\n [\n StripDateIntersection<T1>,\n StripDateIntersection<T2>,\n StripDateIntersection<T3>,\n ]\n : T extends [infer T1, infer T2] ?\n [StripDateIntersection<T1>, StripDateIntersection<T2>]\n : T extends [infer T1] ? [StripDateIntersection<T1>]\n : [];\n","import { Readable } from \"node:stream\";\n\n/**\n * Configuration for a data source\n */\nexport interface DataSourceConfig {\n name: string;\n supportsIncremental?: boolean;\n}\n\n/**\n * DataSource is an abstract class that defines the interface for all data sources.\n * It is used to extract data from a source and test the connection to the source.\n */\nexport abstract class DataSource<T = any, ItemType = any> {\n protected name: string;\n protected supportsIncremental: boolean;\n\n constructor(config: DataSourceConfig) {\n this.name = config.name;\n this.supportsIncremental = config.supportsIncremental ?? false;\n }\n\n /**\n * Extract data from the source\n * Returns either ItemType (for single requests) or Readable (for paginated requests)\n */\n abstract extract(): Promise<ItemType | Readable>;\n\n /**\n * Test connection to the source\n */\n abstract testConnection(): Promise<{ success: boolean; message?: string }>;\n}\n\n/**\n * Result returned from extraction\n * For single requests: data is of type T\n * For paginated requests: data is a Readable stream yielding items of type T\n */\nexport interface ExtractionResult<T = any> {\n data: T | Readable;\n metadata: Record<string, any>;\n}\n","/**\n * SQL Utilities\n *\n * Low-level SQL building utilities for constructing type-safe queries.\n * These are the building blocks used by QueryModel and can also be used\n * directly for custom query construction.\n *\n * @module query-layer/sql-utils\n */\n\nimport { sql, Sql, quoteIdentifier } from \"../sqlHelpers\";\nimport type { SqlValue, ColRef, FilterOperator } from \"./types\";\n\n// --- Core SQL Utilities ---\n\n/**\n * Create raw SQL (literal string, no parameterization).\n * Delegates to sql.raw from sqlHelpers.\n *\n * Only for developer-defined constants (column names, expressions, sort\n * directions) that originate from model config — never for HTTP/user input.\n */\nexport const raw: (text: string) => Sql = sql.raw;\n\n/** Empty SQL fragment — useful as a no-op. */\nexport const empty = sql``;\n\n/**\n * Join SQL fragments with a separator.\n * Delegates to sql.join from sqlHelpers.\n */\nexport const join: (fragments: Sql[], separator?: string) => Sql = sql.join;\n\n/** Check if a Sql fragment is empty */\nexport function isEmpty(fragment: Sql): boolean {\n return (\n fragment.strings.every((s) => s.trim() === \"\") &&\n fragment.values.length === 0\n );\n}\n\n// --- Filter Function ---\n\n/**\n * Create a filter condition. Automatically skips if value is undefined/null.\n * This is the recommended way to build conditional WHERE clauses.\n *\n * @example\n * where(\n * filter(Events.columns.amount, \"gte\", params.minAmount),\n * filter(Events.columns.amount, \"lte\", params.maxAmount),\n * filter(Events.columns.status, \"eq\", params.status),\n * )\n */\nexport function filter(\n col: ColRef,\n op: \"between\",\n value: [SqlValue, SqlValue] | undefined,\n): Sql;\nexport function filter(\n col: ColRef,\n op: \"in\" | \"notIn\",\n value: SqlValue[] | undefined,\n): Sql;\nexport function filter(\n col: ColRef,\n op: \"isNull\" | \"isNotNull\",\n value: boolean | undefined,\n): Sql;\nexport function filter(\n col: ColRef,\n op: \"like\" | \"ilike\",\n value: string | undefined,\n): Sql;\nexport function filter(\n col: ColRef,\n op: Exclude<\n FilterOperator,\n \"between\" | \"in\" | \"notIn\" | \"isNull\" | \"isNotNull\" | \"like\" | \"ilike\"\n >,\n value: SqlValue | undefined,\n): Sql;\nexport function filter(col: ColRef, op: FilterOperator, value: unknown): Sql {\n if (value === undefined || value === null) return empty;\n\n switch (op) {\n case \"eq\":\n return eq(col, value as SqlValue);\n case \"ne\":\n return ne(col, value as SqlValue);\n case \"gt\":\n return gt(col, value as SqlValue);\n case \"gte\":\n return gte(col, value as SqlValue);\n case \"lt\":\n return lt(col, value as SqlValue);\n case \"lte\":\n return lte(col, value as SqlValue);\n case \"like\":\n return like(col, value as string);\n case \"ilike\":\n return ilike(col, value as string);\n case \"in\":\n return inList(col, value as SqlValue[]);\n case \"notIn\":\n return notIn(col, value as SqlValue[]);\n case \"between\": {\n const [low, high] = value as [SqlValue, SqlValue];\n return between(col, low, high);\n }\n case \"isNull\":\n return value ? isNull(col) : empty;\n case \"isNotNull\":\n return value ? isNotNull(col) : empty;\n }\n}\n\n// --- Comparison Operators ---\n\n/** Equal: column = value */\nexport function eq(col: ColRef, value: SqlValue): Sql {\n return sql`${col} = ${value}`;\n}\n\n/** Not equal: column != value */\nexport function ne(col: ColRef, value: SqlValue): Sql {\n return sql`${col} != ${value}`;\n}\n\n/** Greater than: column > value */\nexport function gt(col: ColRef, value: SqlValue): Sql {\n return sql`${col} > ${value}`;\n}\n\n/** Greater than or equal: column >= value */\nexport function gte(col: ColRef, value: SqlValue): Sql {\n return sql`${col} >= ${value}`;\n}\n\n/** Less than: column < value */\nexport function lt(col: ColRef, value: SqlValue): Sql {\n return sql`${col} < ${value}`;\n}\n\n/** Less than or equal: column <= value */\nexport function lte(col: ColRef, value: SqlValue): Sql {\n return sql`${col} <= ${value}`;\n}\n\n/** LIKE pattern match (case-sensitive) */\nexport function like(col: ColRef, pattern: string): Sql {\n return sql`${col} LIKE ${pattern}`;\n}\n\n/** ILIKE pattern match (case-insensitive, ClickHouse) */\nexport function ilike(col: ColRef, pattern: string): Sql {\n return sql`${col} ILIKE ${pattern}`;\n}\n\n/** IN list: column IN (a, b, c) */\nexport function inList(col: ColRef, values: SqlValue[]): Sql {\n if (values.length === 0) return sql`1 = 0`;\n return sql`${col} IN (${join(values.map((v) => sql`${v}`))})`;\n}\n\n/** NOT IN list */\nexport function notIn(col: ColRef, values: SqlValue[]): Sql {\n if (values.length === 0) return sql`1 = 1`;\n return sql`${col} NOT IN (${join(values.map((v) => sql`${v}`))})`;\n}\n\n/** BETWEEN: column BETWEEN low AND high */\nexport function between(col: ColRef, low: SqlValue, high: SqlValue): Sql {\n return sql`${col} BETWEEN ${low} AND ${high}`;\n}\n\n/** IS NULL */\nexport function isNull(col: ColRef): Sql {\n return sql`${col} IS NULL`;\n}\n\n/** IS NOT NULL */\nexport function isNotNull(col: ColRef): Sql {\n return sql`${col} IS NOT NULL`;\n}\n\n// --- Logical Combinators ---\n\n/** Combine conditions with AND, filtering out empty fragments */\nexport function and(...conditions: Sql[]): Sql {\n const nonEmpty = conditions.filter((c) => !isEmpty(c));\n return join(nonEmpty, \"AND\");\n}\n\n/** Combine conditions with OR, filtering out empty fragments */\nexport function or(...conditions: Sql[]): Sql {\n const nonEmpty = conditions.filter((c) => !isEmpty(c));\n if (nonEmpty.length === 0) return empty;\n if (nonEmpty.length === 1) return nonEmpty[0];\n return sql`(${join(nonEmpty, \"OR\")})`;\n}\n\n/** Negate a condition: NOT (condition) */\nexport function not(condition: Sql): Sql {\n if (isEmpty(condition)) return empty;\n return sql`NOT (${condition})`;\n}\n\n// --- SQL Clauses ---\n\n/** Build WHERE clause — returns empty if no conditions */\nexport function where(...conditions: Sql[]): Sql {\n const combined = and(...conditions);\n return isEmpty(combined) ? empty : sql`WHERE ${combined}`;\n}\n\n/** Build ORDER BY clause */\nexport function orderBy(\n ...cols: Array<ColRef | [ColRef, \"ASC\" | \"DESC\"]>\n): Sql {\n if (cols.length === 0) return empty;\n const parts = cols.map((c) => {\n if (Array.isArray(c)) {\n const [col, dir] = c;\n return sql`${col} ${raw(dir)}`;\n }\n return sql`${c}`;\n });\n return sql`ORDER BY ${join(parts)}`;\n}\n\n/** Build LIMIT clause */\nexport function limit(n: number): Sql {\n if (!Number.isInteger(n) || n < 0) {\n throw new Error(\"LIMIT must be a non-negative integer\");\n }\n return sql`LIMIT ${n}`;\n}\n\n/** Build OFFSET clause */\nexport function offset(n: number): Sql {\n if (!Number.isInteger(n) || n < 0) {\n throw new Error(\"OFFSET must be a non-negative integer\");\n }\n return sql`OFFSET ${n}`;\n}\n\n/** Build LIMIT + OFFSET for pagination */\nexport function paginate(pageSize: number, page: number = 0): Sql {\n if (!Number.isInteger(pageSize) || pageSize <= 0) {\n throw new Error(\"pageSize must be a positive integer\");\n }\n if (!Number.isInteger(page) || page < 0) {\n throw new Error(\"page must be a non-negative integer\");\n }\n const offsetVal = page * pageSize;\n return offsetVal > 0 ?\n sql`LIMIT ${pageSize} OFFSET ${offsetVal}`\n : sql`LIMIT ${pageSize}`;\n}\n\n/** Build GROUP BY clause */\nexport function groupBy(...cols: ColRef[]): Sql {\n if (cols.length === 0) return empty;\n return sql`GROUP BY ${join(cols.map((c) => sql`${c}`))}`;\n}\n\n/** Build HAVING clause */\nexport function having(...conditions: Sql[]): Sql {\n const combined = and(...conditions);\n return isEmpty(combined) ? empty : sql`HAVING ${combined}`;\n}\n\n// --- Identifier Safety ---\n\n/** Emit a safely-quoted SQL identifier using backticks (ClickHouse convention). */\nexport function identifier(name: string): Sql {\n return raw(quoteIdentifier(name));\n}\n\n// --- Expression with Fluent Alias ---\n\n/** SQL expression with fluent `.as()` method */\nexport interface Expr extends Sql {\n as(alias: string): Sql;\n}\n\n/** Augment a Sql fragment with a fluent .as() method */\nfunction expr(fragment: Sql): Expr {\n const e = Object.create(fragment) as Expr;\n e.as = (alias: string) => sql`${fragment} AS ${identifier(alias)}`;\n return e;\n}\n\n// --- Aggregation Functions ---\n\n/** COUNT(*) or COUNT(column) */\nexport function count(col?: ColRef): Expr {\n return expr(col ? sql`count(${col})` : sql`count(*)`);\n}\n\n/** COUNT(DISTINCT column) */\nexport function countDistinct(col: ColRef): Expr {\n return expr(sql`count(DISTINCT ${col})`);\n}\n\n/** SUM(column) */\nexport function sum(col: ColRef): Expr {\n return expr(sql`sum(${col})`);\n}\n\n/** AVG(column) */\nexport function avg(col: ColRef): Expr {\n return expr(sql`avg(${col})`);\n}\n\n/** MIN(column) */\nexport function min(col: ColRef): Expr {\n return expr(sql`min(${col})`);\n}\n\n/** MAX(column) */\nexport function max(col: ColRef): Expr {\n return expr(sql`max(${col})`);\n}\n\n// --- Select Helpers ---\n\n/** Build SELECT clause with columns */\nexport function select(...cols: Array<ColRef | [ColRef, string]>): Sql {\n if (cols.length === 0) return sql`SELECT *`;\n const parts = cols.map((c) => {\n if (Array.isArray(c)) {\n const [col, alias] = c;\n return sql`${col} AS ${identifier(alias)}`;\n }\n return sql`${c}`;\n });\n return sql`SELECT ${join(parts)}`;\n}\n\n/** Alias a column or expression */\nexport function as(expression: Sql, alias: string): Sql {\n return sql`${expression} AS ${identifier(alias)}`;\n}\n","/**\n * Composable helpers that leverage moose-lib table metadata to reduce\n * boilerplate in QueryModel definitions.\n *\n * @module query-layer/helpers\n */\n\nimport { sql } from \"../sqlHelpers\";\nimport type { Column, DataType } from \"../dataModels/dataModelTypes\";\nimport type { OlapTable } from \"../dmv2\";\nimport type {\n DimensionDef,\n ColumnDef,\n ModelFilterDef,\n FilterInputTypeHint,\n} from \"./types\";\n\n// --- Utility: snake_case → camelCase ---\n\nfunction toCamelCase(s: string): string {\n return s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());\n}\n\n// --- deriveInputTypeFromDataType ---\n\n/**\n * Prefix → FilterInputTypeHint map for ClickHouse string type names.\n *\n * Prefix matching is required because ClickHouse types carry parameters\n * (e.g. `DateTime64(3)`, `Decimal(18,4)`, `FixedString(32)`).\n * Entries are ordered longest-prefix-first so \"datetime\" matches before \"date\".\n */\nconst TYPE_PREFIX_MAP: readonly [string, FilterInputTypeHint][] = [\n [\"datetime\", \"date\"],\n [\"date\", \"date\"],\n [\"uint\", \"number\"],\n [\"int\", \"number\"],\n [\"float\", \"number\"],\n [\"decimal\", \"number\"],\n [\"enum\", \"select\"],\n [\"boolean\", \"select\"],\n [\"bool\", \"select\"],\n];\n\n/**\n * Derive FilterInputTypeHint from a ClickHouse column data_type.\n */\nexport function deriveInputTypeFromDataType(\n dataType: DataType,\n): FilterInputTypeHint {\n if (typeof dataType === \"string\") {\n const lower = dataType.toLowerCase();\n const match = TYPE_PREFIX_MAP.find(([prefix]) => lower.startsWith(prefix));\n return match ? match[1] : \"text\";\n }\n\n if (\"nullable\" in dataType) {\n return deriveInputTypeFromDataType(dataType.nullable);\n }\n if (\"name\" in dataType && \"values\" in dataType) {\n return \"select\";\n }\n if (\"elementType\" in dataType) {\n return \"text\";\n }\n return \"text\";\n}\n\n// --- timeDimensions ---\n\ntype DefaultTimePeriods = {\n day: DimensionDef;\n month: DimensionDef;\n week: DimensionDef;\n};\n\n/**\n * Generate day/month/week dimension definitions from a date column reference.\n *\n * @param dateColumn - A Column reference from `Table.columns.some_date`\n * @returns `{ day, month, week }` dimension definitions\n *\n * @example\n * dimensions: {\n * status: { column: \"status\" },\n * ...timeDimensions(VisitsTable.columns.start_date),\n * }\n */\nexport function timeDimensions(dateColumn: Column): DefaultTimePeriods;\nexport function timeDimensions(\n dateColumn: Column,\n options: { periods: string[] },\n): Record<string, DimensionDef>;\nexport function timeDimensions(\n dateColumn: Column,\n options?: { periods?: string[] },\n): DefaultTimePeriods | Record<string, DimensionDef> {\n const periods = options?.periods ?? [\"day\", \"month\", \"week\"];\n\n const fnMap: Record<string, (col: Column) => DimensionDef> = {\n day: (col) => ({ expression: sql`toDate(${col})`, as: \"day\" }),\n month: (col) => ({ expression: sql`toStartOfMonth(${col})`, as: \"month\" }),\n week: (col) => ({ expression: sql`toStartOfWeek(${col})`, as: \"week\" }),\n };\n\n const supported = Object.keys(fnMap);\n const result: Record<string, DimensionDef> = {};\n for (const period of periods) {\n const factory = fnMap[period];\n if (!factory) {\n throw new Error(\n `Unknown time period '${period}'. Supported: ${supported.join(\", \")}`,\n );\n }\n result[period] = factory(dateColumn);\n }\n\n return result;\n}\n\n// --- Table field helpers ---\n\ninterface TableFieldOptions {\n /** Only include these column names (snake_case as in the table) */\n include?: string[];\n /** Exclude these column names */\n exclude?: string[];\n /** Convert snake_case keys to camelCase (default: true) */\n camelCase?: boolean;\n}\n\n/**\n * Generate ColumnDef records from a table's columnArray metadata.\n *\n * @example\n * columns: {\n * ...columnsFromTable(VisitsTable, { include: [\"id\", \"name\", \"status\"] }),\n * firstName: { join: \"user\", column: \"first_name\" },\n * }\n */\nexport function columnsFromTable<T>(\n table: OlapTable<T>,\n options?: TableFieldOptions,\n): Record<string, ColumnDef<T>> {\n const { include, exclude, camelCase = true } = options ?? {};\n const result: Record<string, ColumnDef<T>> = {};\n\n for (const col of table.columnArray) {\n const colName = String(col.name);\n\n if (include && !include.includes(colName)) continue;\n if (exclude && exclude.includes(colName)) continue;\n\n const key = camelCase ? toCamelCase(colName) : colName;\n result[key] = { column: colName as keyof T & string };\n }\n\n return result;\n}\n\n/**\n * Generate ModelFilterDef records from a table's columnArray metadata.\n *\n * **Conservative defaults**: all filters get `[\"eq\"]` operators only.\n * Consumers widen operators explicitly via spread overrides.\n *\n * @example\n * filters: {\n * ...filtersFromTable(VisitsTable, { include: [\"studio_id\", \"start_date\", \"status\"] }),\n * status: { column: \"status\", operators: [\"eq\", \"ne\", \"in\"] as const },\n * }\n */\nexport function filtersFromTable<T>(\n table: OlapTable<T>,\n options?: TableFieldOptions,\n): Record<string, ModelFilterDef<T, keyof T>> {\n const { include, exclude, camelCase = true } = options ?? {};\n const result: Record<string, ModelFilterDef<T, keyof T>> = {};\n\n for (const col of table.columnArray) {\n const colName = String(col.name);\n\n if (include && !include.includes(colName)) continue;\n if (exclude && exclude.includes(colName)) continue;\n\n const key = camelCase ? toCamelCase(colName) : colName;\n result[key] = {\n column: colName as keyof T,\n operators: [\"eq\"] as const,\n inputType: deriveInputTypeFromDataType(col.data_type),\n };\n }\n\n return result;\n}\n","/**\n * Query Model — Core query building interface and implementation.\n *\n * @module query-layer/query-model\n */\n\nimport type { Column } from \"../dataModels/dataModelTypes\";\nimport { sql, Sql, quoteIdentifier } from \"../sqlHelpers\";\nimport { OlapTable } from \"../dmv2\";\nimport { QueryClient } from \"../consumption-apis/helpers\";\nimport {\n raw,\n empty,\n join,\n isEmpty,\n filter as filterSql,\n where,\n orderBy as orderByClause,\n groupBy as groupByClause,\n paginate,\n having as havingClause,\n identifier,\n} from \"./sql-utils\";\nimport {\n type FilterOperator,\n type SortDir,\n type SqlValue,\n type ColRef,\n type ColumnDef,\n type JoinDef,\n type DimensionDef,\n type MetricDef,\n type ModelFilterDef,\n type FilterDefBase,\n type FilterInputTypeHint,\n type Names,\n type QueryRequest,\n type QueryParts,\n type FilterParams,\n} from \"./types\";\nimport { deriveInputTypeFromDataType } from \"./helpers\";\n\n// Widen filterSql for dynamic operator dispatch (runtime iteration over ops).\nconst applyFilter = filterSql as (\n col: ColRef,\n op: FilterOperator,\n value: unknown,\n) => Sql;\n\nconst identity = (v: SqlValue): SqlValue => v;\n\n/**\n * Apply a transform function to a filter value, respecting operator-specific\n * value shapes (scalar, list, tuple, boolean).\n * @internal\n */\nfunction transformFilterValue(\n op: FilterOperator,\n value: unknown,\n transform: (v: SqlValue) => SqlValue,\n): unknown {\n switch (op) {\n case \"in\":\n case \"notIn\":\n return (value as SqlValue[]).map(transform);\n case \"between\": {\n const [low, high] = value as [SqlValue, SqlValue];\n return [transform(low), transform(high)];\n }\n case \"isNull\":\n case \"isNotNull\":\n return value;\n default:\n return transform(value as SqlValue);\n }\n}\n\n// --- Internal Types ---\n\n/**\n * Field definition for SELECT clauses (internal runtime type).\n * @internal\n */\ninterface FieldDef {\n column?: ColRef;\n expression?: Sql;\n agg?: Sql;\n as?: string;\n}\n\n/**\n * Runtime filter definition (internal).\n * @internal\n */\ninterface FilterDef<TValue = SqlValue> {\n column: ColRef;\n operators: readonly FilterOperator[];\n transform?: (value: TValue) => SqlValue;\n isHaving?: boolean;\n}\n\n/**\n * Resolved query specification (internal).\n * @internal\n */\ntype ResolvedQuerySpec<\n TMetrics extends string,\n TDimensions extends string,\n TFilters extends Record<string, FilterDefBase>,\n TSortable extends string,\n TTable = unknown,\n TColumns extends string = string,\n> = {\n filters?: FilterParams<TFilters, TTable>;\n select?: Array<TMetrics | TDimensions | TColumns>;\n groupBy?: TDimensions[];\n orderBy?: Array<[TSortable, SortDir]>;\n limit?: number;\n page?: number;\n offset?: number;\n detailMode?: boolean;\n};\n\n// --- Query Model Configuration ---\n\n/**\n * Configuration for defining a query model.\n *\n * @template TTable - The table's model type (row type)\n * @template TMetrics - Record of metric definitions\n * @template TDimensions - Record of dimension definitions\n * @template TFilters - Record of filter definitions\n * @template TSortable - Union type of sortable field names\n */\nexport interface QueryModelConfig<\n TTable,\n TMetrics extends Record<string, MetricDef>,\n TDimensions extends Record<string, DimensionDef<TTable, keyof TTable>>,\n TFilters extends Record<string, ModelFilterDef<TTable, keyof TTable>>,\n TSortable extends string,\n TColumns extends Record<string, ColumnDef<TTable>> = Record<string, never>,\n TJoins extends Record<string, JoinDef> = Record<string, never>,\n> {\n /** Tool name used by registerModelTools (e.g. \"query_visits\") */\n name?: string;\n /** Tool description used by registerModelTools */\n description?: string;\n /** The OlapTable to query */\n table: OlapTable<TTable>;\n\n /**\n * Dimension fields — columns used for grouping, filtering, and display.\n *\n * @example\n * dimensions: {\n * status: { column: \"status\" },\n * day: { expression: sql`toDate(timestamp)`, as: \"day\" },\n * }\n */\n dimensions?: TDimensions;\n\n /**\n * Metric fields — aggregate values computed over dimensions.\n *\n * @example\n * metrics: {\n * totalAmount: { agg: sum(Events.columns.amount), as: \"total_amount\" },\n * totalEvents: { agg: count(), as: \"total_events\" },\n * }\n */\n metrics?: TMetrics;\n\n /**\n * Column fields for detail (non-aggregated) queries.\n *\n * @example\n * columns: {\n * visitId: { column: \"id\" },\n * firstName: { join: \"user\", column: \"first_name\" },\n * }\n */\n columns?: TColumns;\n\n /**\n * Lookup JOIN definitions.\n *\n * @example\n * joins: {\n * user: {\n * table: UsersTable,\n * leftKey: \"user_id\",\n * rightKey: \"id\",\n * type: \"LEFT\",\n * },\n * }\n */\n joins?: TJoins;\n\n /**\n * Filterable fields with allowed operators.\n *\n * @example\n * filters: {\n * status: { column: \"status\", operators: [\"eq\", \"in\"] as const },\n * amount: { column: \"amount\", operators: [\"gte\", \"lte\"] as const },\n * }\n */\n filters: TFilters;\n\n /**\n * Which fields can be sorted.\n *\n * @example\n * sortable: [\"timestamp\", \"amount\", \"status\"] as const\n */\n sortable: readonly TSortable[];\n\n /** Default query behavior */\n defaults?: {\n orderBy?: Array<[TSortable, SortDir]>;\n groupBy?: string[];\n limit?: number;\n maxLimit?: number;\n dimensions?: string[];\n metrics?: string[];\n columns?: string[];\n };\n}\n\n// --- Query Model Interface ---\n\n/**\n * Query model interface providing type-safe query building and execution.\n */\nexport interface QueryModel<\n TTable,\n TMetrics extends Record<string, MetricDef>,\n TDimensions extends Record<string, DimensionDef>,\n TFilters extends Record<string, FilterDefBase>,\n TSortable extends string,\n TResult,\n TColumns extends Record<string, ColumnDef> = Record<string, never>,\n> {\n readonly name?: string;\n readonly description?: string;\n readonly defaults: {\n orderBy?: Array<[TSortable, SortDir]>;\n groupBy?: string[];\n limit?: number;\n maxLimit?: number;\n dimensions?: string[];\n metrics?: string[];\n columns?: string[];\n };\n readonly filters: TFilters;\n readonly sortable: readonly TSortable[];\n readonly dimensions?: TDimensions;\n readonly metrics?: TMetrics;\n readonly columns?: TColumns;\n\n readonly dimensionNames: readonly string[];\n readonly metricNames: readonly string[];\n readonly columnNames: readonly string[];\n\n /** Type inference helpers (similar to Drizzle's $inferSelect pattern). */\n readonly $inferDimensions: Names<TDimensions>;\n readonly $inferMetrics: Names<TMetrics>;\n readonly $inferColumns: Names<TColumns>;\n readonly $inferFilters: FilterParams<TFilters, TTable>;\n readonly $inferRequest: QueryRequest<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n Names<TColumns>,\n TTable\n >;\n readonly $inferResult: TResult;\n\n /** Execute query with Moose QueryClient. */\n query: (\n request: QueryRequest<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n Names<TColumns>,\n TTable\n >,\n client: QueryClient,\n ) => Promise<TResult[]>;\n\n /** Build complete SQL query from request. */\n toSql: (\n request: QueryRequest<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n Names<TColumns>,\n TTable\n >,\n ) => Sql;\n\n /** Get individual SQL parts for custom assembly. */\n toParts: (\n request: QueryRequest<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n Names<TColumns>,\n TTable\n >,\n ) => QueryParts;\n}\n\n// --- defineQueryModel Implementation ---\n\n/**\n * Define a query model with controlled field selection, filtering, and sorting.\n *\n * @example\n * const model = defineQueryModel({\n * table: Events,\n * dimensions: {\n * status: { column: \"status\" },\n * day: { expression: sql`toDate(timestamp)`, as: \"day\" },\n * },\n * metrics: {\n * totalEvents: { agg: count(), as: \"total_events\" },\n * totalAmount: { agg: sum(Events.columns.amount), as: \"total_amount\" },\n * },\n * filters: {\n * status: { column: \"status\", operators: [\"eq\", \"in\"] as const },\n * },\n * sortable: [\"amount\", \"timestamp\"] as const,\n * });\n */\nexport function defineQueryModel<\n TTable,\n TMetrics extends Record<string, MetricDef>,\n TDimensions extends Record<string, DimensionDef<TTable, keyof TTable>>,\n TFilters extends Record<string, ModelFilterDef<TTable, keyof TTable>>,\n TSortable extends string,\n TColumns extends Record<string, ColumnDef<TTable>> = Record<string, never>,\n TJoins extends Record<string, JoinDef> = Record<string, never>,\n TResult = TTable,\n>(\n config: QueryModelConfig<\n TTable,\n TMetrics,\n TDimensions,\n TFilters,\n TSortable,\n TColumns,\n TJoins\n >,\n): QueryModel<\n TTable,\n TMetrics,\n TDimensions,\n TFilters,\n TSortable,\n TResult,\n TColumns\n> {\n type Req = QueryRequest<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n Names<TColumns>,\n TTable\n >;\n\n const {\n table,\n dimensions,\n metrics,\n columns: columnDefs,\n joins: joinDefs,\n filters,\n sortable,\n defaults = {},\n } = config;\n const { maxLimit = 1000 } = defaults;\n\n const primaryTableName = table.name;\n const hasJoins = joinDefs != null && Object.keys(joinDefs).length > 0;\n\n // --- Normalize dimensions ---\n\n const normalizedDimensions: Record<string, FieldDef> = {};\n if (dimensions) {\n for (const [name, def] of Object.entries(dimensions)) {\n const column =\n def.column ?\n hasJoins ? undefined\n : table.columns[def.column]\n : undefined;\n const expression =\n def.expression ??\n (def.column && hasJoins ?\n raw(\n `${quoteIdentifier(primaryTableName)}.${quoteIdentifier(String(def.column))}`,\n )\n : undefined);\n normalizedDimensions[name] = { column, expression, as: def.as };\n }\n }\n\n // --- Normalize metrics ---\n\n const normalizedMetrics: Record<string, FieldDef> = {};\n if (metrics) {\n for (const [name, def] of Object.entries(metrics)) {\n normalizedMetrics[name] = { agg: def.agg, as: def.as };\n }\n }\n\n // --- Normalize columns (detail queries) ---\n\n const normalizedColumns: Record<string, FieldDef> = {};\n if (columnDefs) {\n for (const [name, def] of Object.entries(columnDefs)) {\n if (def.join && joinDefs) {\n const joinDef = joinDefs[def.join];\n if (!joinDef) {\n throw new Error(\n `Column '${name}' references unknown join '${def.join}'`,\n );\n }\n const joinTableName = joinDef.table.name;\n normalizedColumns[name] = {\n expression: raw(\n `${quoteIdentifier(joinTableName)}.${quoteIdentifier(String(def.column))}`,\n ),\n as: def.as,\n };\n } else if (hasJoins) {\n normalizedColumns[name] = {\n expression: raw(\n `${quoteIdentifier(primaryTableName)}.${quoteIdentifier(String(def.column))}`,\n ),\n as: def.as,\n };\n } else {\n normalizedColumns[name] = {\n column: table.columns[def.column as keyof TTable],\n as: def.as,\n };\n }\n }\n }\n\n // --- Resolve filters ---\n\n const resolvedFilters: Record<\n string,\n FilterDef & { inputType?: FilterInputTypeHint }\n > = {};\n const filtersWithInputType: Record<\n string,\n ModelFilterDef<TTable, keyof TTable> & { inputType?: FilterInputTypeHint }\n > = {};\n for (const [name, def] of Object.entries(filters)) {\n if (def.metric) {\n // HAVING filter — references a metric by name\n const metricDef = normalizedMetrics[def.metric];\n if (!metricDef) {\n throw new Error(\n `Filter '${name}' references unknown metric '${def.metric}'`,\n );\n }\n const alias = metricDef.as ?? def.metric;\n const inputType = def.inputType ?? \"number\";\n\n resolvedFilters[name] = {\n column: identifier(alias),\n operators: def.operators,\n transform: def.transform as ((value: SqlValue) => SqlValue) | undefined,\n inputType,\n isHaving: true,\n };\n\n filtersWithInputType[name] = { ...def, inputType };\n } else if (def.column != null) {\n // WHERE filter — references a table column\n const columnRef: Column = table.columns[def.column];\n if (!columnRef) {\n throw new Error(\n `Filter '${name}' references unknown column '${String(def.column)}' on table '${primaryTableName}'`,\n );\n }\n\n const inputType =\n def.inputType ??\n (columnRef.data_type ?\n deriveInputTypeFromDataType(columnRef.data_type)\n : undefined);\n\n const resolvedColumn: ColRef =\n hasJoins ?\n raw(\n `${quoteIdentifier(primaryTableName)}.${quoteIdentifier(String(def.column))}`,\n )\n : columnRef;\n\n resolvedFilters[name] = {\n column: resolvedColumn,\n operators: def.operators,\n transform: def.transform as ((value: SqlValue) => SqlValue) | undefined,\n inputType,\n };\n\n filtersWithInputType[name] = { ...def, inputType };\n } else {\n throw new Error(\n `Filter '${name}' must specify either 'column' or 'metric'`,\n );\n }\n }\n\n // --- Combined field map ---\n\n const normalizedFields: Record<string, FieldDef> = {\n ...normalizedDimensions,\n ...normalizedMetrics,\n ...normalizedColumns,\n };\n\n const dimensionNamesSet = new Set(Object.keys(normalizedDimensions));\n const metricNamesSet = new Set(Object.keys(normalizedMetrics));\n const columnNamesSet = new Set(Object.keys(normalizedColumns));\n\n const dimensionNames: readonly string[] = Object.keys(normalizedDimensions);\n const metricNames: readonly string[] = Object.keys(normalizedMetrics);\n const columnNames: readonly string[] = Object.keys(normalizedColumns);\n\n // --- SQL building helpers ---\n\n function buildFieldExpr(field: FieldDef, defaultAlias: string): Sql {\n const expr =\n field.agg ??\n field.expression ??\n (field.column ? sql`${field.column}` : empty);\n if (!expr || isEmpty(expr)) return empty;\n const alias = field.as ?? defaultAlias;\n return sql`${expr} AS ${identifier(String(alias))}`;\n }\n\n function buildFieldList(\n fieldDefs: Record<string, FieldDef>,\n selectFields?: string[],\n ): Sql[] {\n const fieldNames = selectFields ?? Object.keys(fieldDefs);\n return fieldNames\n .map((name) => {\n const field = fieldDefs[name];\n if (!field) return empty;\n return buildFieldExpr(field, name);\n })\n .filter((s) => !isEmpty(s));\n }\n\n function buildSelectClause(selectFields?: string[]): Sql {\n const parts = buildFieldList(normalizedFields, selectFields);\n return sql`SELECT ${join(parts)}`;\n }\n\n function buildFilterConditions(\n filterParams?: FilterParams<TFilters, TTable>,\n ): { where: Sql[]; having: Sql[] } {\n if (!filterParams) return { where: [], having: [] };\n\n const whereConds: Sql[] = [];\n const havingConds: Sql[] = [];\n for (const [filterName, ops] of Object.entries(filterParams)) {\n const filterDef = resolvedFilters[filterName];\n if (!filterDef) {\n throw new Error(`Unknown filter '${filterName}'`);\n }\n if (!ops) continue;\n\n for (const [op, value] of Object.entries(\n ops as Record<string, unknown>,\n )) {\n if (value === undefined) continue;\n if (!filterDef.operators.includes(op as FilterOperator)) {\n throw new Error(\n `Operator '${op}' not allowed for filter '${filterName}'`,\n );\n }\n\n const t = filterDef.transform ?? identity;\n const transformed = transformFilterValue(\n op as FilterOperator,\n value,\n t,\n );\n const condition = applyFilter(\n filterDef.column,\n op as FilterOperator,\n transformed,\n );\n if (!isEmpty(condition)) {\n if (filterDef.isHaving) {\n havingConds.push(condition);\n } else {\n whereConds.push(condition);\n }\n }\n }\n }\n return { where: whereConds, having: havingConds };\n }\n\n function buildOrderByClause(\n spec: ResolvedQuerySpec<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n TTable\n >,\n selectedFieldSet?: Set<string>,\n ): Sql {\n const orderBySpec =\n spec.orderBy && spec.orderBy.length > 0 ? spec.orderBy : defaults.orderBy;\n\n if (!orderBySpec || orderBySpec.length === 0) return empty;\n\n for (const [field] of orderBySpec) {\n if (!sortable.includes(field)) {\n throw new Error(`Field '${field}' is not sortable`);\n }\n }\n\n const parts = orderBySpec\n .map(([field, dir]) => {\n if (dir !== \"ASC\" && dir !== \"DESC\") {\n throw new Error(`Invalid sort direction '${dir}'`);\n }\n\n const fieldDef = normalizedFields[field];\n if (!fieldDef) return empty;\n\n // Skip dimension-based ORDER BY fields that aren't in the current\n // SELECT list — ClickHouse rejects non-aggregate expressions that\n // aren't part of GROUP BY.\n if (\n selectedFieldSet &&\n dimensionNamesSet.has(field) &&\n !selectedFieldSet.has(field)\n ) {\n return empty;\n }\n\n const alias = fieldDef.as ?? String(field);\n const col =\n fieldDef.expression ??\n (fieldDef.column ? sql`${fieldDef.column}` : empty);\n\n // For aggregate metrics, ORDER BY the SELECT alias.\n const orderExpr = fieldDef.agg ? identifier(alias) : col;\n if (isEmpty(orderExpr)) return empty;\n\n return sql`${orderExpr} ${raw(dir)}`;\n })\n .filter((p) => !isEmpty(p));\n\n return parts.length > 0 ? sql`ORDER BY ${join(parts)}` : empty;\n }\n\n function buildFromClause(): Sql {\n if (!hasJoins) {\n return sql`FROM ${table}`;\n }\n\n let fromClause = sql`FROM ${table}`;\n for (const [, joinDef] of Object.entries(joinDefs!)) {\n const joinType = joinDef.type ?? \"LEFT\";\n\n let onClause: Sql;\n if (joinDef.leftKey && joinDef.rightKey) {\n const joinTableName = joinDef.table.name;\n onClause = raw(\n `${quoteIdentifier(primaryTableName)}.${quoteIdentifier(joinDef.leftKey)} = ${quoteIdentifier(joinTableName)}.${quoteIdentifier(joinDef.rightKey)}`,\n );\n } else if (joinDef.on) {\n onClause = joinDef.on;\n } else {\n throw new Error(\"JoinDef must specify either leftKey/rightKey or on\");\n }\n\n fromClause = sql`${fromClause} ${raw(joinType)} JOIN ${joinDef.table} ON ${onClause}`;\n }\n return fromClause;\n }\n\n function resolveQuerySpec(\n request: Req,\n ): ResolvedQuerySpec<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n TTable,\n Names<TColumns>\n > {\n if (request.columns && request.columns.length > 0) {\n return {\n select: request.columns,\n groupBy: undefined,\n filters: request.filters,\n orderBy: request.orderBy,\n limit: request.limit,\n page: request.page,\n offset: request.offset,\n detailMode: true,\n };\n }\n\n const select = [...(request.dimensions ?? []), ...(request.metrics ?? [])];\n const groupBy =\n request.dimensions && request.dimensions.length > 0 ?\n request.dimensions\n : undefined;\n\n return {\n select: select.length > 0 ? select : undefined,\n groupBy,\n filters: request.filters,\n orderBy: request.orderBy,\n limit: request.limit,\n page: request.page,\n offset: request.offset,\n detailMode: false,\n };\n }\n\n function buildGroupByClause(\n spec: ResolvedQuerySpec<\n keyof TMetrics & string,\n keyof TDimensions & string,\n TFilters,\n TSortable,\n TTable\n >,\n ): Sql {\n const groupByFields = spec.groupBy ?? defaults.groupBy;\n if (!groupByFields || groupByFields.length === 0) return empty;\n\n const groupExprs = groupByFields.map((fieldName) => {\n if (!dimensionNamesSet.has(fieldName)) {\n throw new Error(`Field '${fieldName}' is not a valid dimension`);\n }\n\n const field = normalizedFields[fieldName];\n if (!field) {\n throw new Error(`Field '${fieldName}' is not a valid dimension`);\n }\n if (field.expression) return field.expression;\n if (field.column) return sql`${field.column}`;\n return raw(fieldName);\n });\n\n return groupByClause(...groupExprs);\n }\n\n function toParts(request: Req): QueryParts {\n const spec = resolveQuerySpec(request);\n\n if (spec.offset != null && spec.page != null) {\n throw new Error(\n \"Cannot specify both 'offset' and 'page' — they are mutually exclusive\",\n );\n }\n\n const limitVal = Math.min(spec.limit ?? defaults.limit ?? 100, maxLimit);\n const offsetVal = spec.offset ?? (spec.page ?? 0) * limitVal;\n const pagination =\n spec.offset != null ?\n sql`LIMIT ${limitVal} OFFSET ${offsetVal}`\n : paginate(limitVal, spec.page ?? 0);\n\n const selectedFields =\n spec.select ??\n (spec.detailMode ?\n Object.keys(normalizedFields)\n : [...dimensionNames, ...metricNames]);\n const selectedColumns = selectedFields.filter((f) => columnNamesSet.has(f));\n const selectedDimensions = selectedFields.filter((f) =>\n dimensionNamesSet.has(f),\n );\n const selectedMetrics = selectedFields.filter((f) => metricNamesSet.has(f));\n\n const columnParts = buildFieldList(\n normalizedColumns,\n selectedColumns.length > 0 ? selectedColumns : undefined,\n );\n const dimensionParts = buildFieldList(\n normalizedDimensions,\n selectedDimensions.length > 0 ? selectedDimensions : undefined,\n );\n const metricParts = buildFieldList(\n normalizedMetrics,\n selectedMetrics.length > 0 ? selectedMetrics : undefined,\n );\n\n const selectedFieldSet = new Set(selectedFields);\n const selectClause = buildSelectClause(selectedFields);\n const filterResult = buildFilterConditions(spec.filters);\n const whereClause =\n filterResult.where.length > 0 ? where(...filterResult.where) : empty;\n const havingPart =\n filterResult.having.length > 0 ?\n havingClause(...filterResult.having)\n : empty;\n const groupByPart = spec.detailMode ? empty : buildGroupByClause(spec);\n const orderByPart = buildOrderByClause(spec, selectedFieldSet);\n\n return {\n select: selectClause,\n dimensions: dimensionParts.length > 0 ? join(dimensionParts) : empty,\n metrics: metricParts.length > 0 ? join(metricParts) : empty,\n columns: columnParts.length > 0 ? join(columnParts) : empty,\n from: buildFromClause(),\n conditions: filterResult.where,\n where: whereClause,\n groupBy: groupByPart,\n having: havingPart,\n orderBy: orderByPart,\n pagination,\n };\n }\n\n function toSql(request: Req): Sql {\n const parts = toParts(request);\n return sql`\n ${parts.select}\n ${parts.from}\n ${parts.where}\n ${parts.groupBy}\n ${parts.having}\n ${parts.orderBy}\n ${parts.pagination}\n `;\n }\n\n const model = {\n name: config.name,\n description: config.description,\n defaults,\n filters: filtersWithInputType as TFilters &\n Record<string, { inputType?: FilterInputTypeHint }>,\n sortable,\n dimensions,\n metrics,\n columns: columnDefs,\n dimensionNames,\n metricNames,\n columnNames,\n query: async (request, client: QueryClient) => {\n const result = await client.execute(toSql(request));\n return result.json();\n },\n toSql,\n toParts,\n $inferDimensions: undefined as never,\n $inferMetrics: undefined as never,\n $inferColumns: undefined as never,\n $inferFilters: undefined as never,\n $inferRequest: undefined as never,\n $inferResult: undefined as never,\n } satisfies QueryModel<\n TTable,\n TMetrics,\n TDimensions,\n TFilters,\n TSortable,\n TResult,\n TColumns\n >;\n\n return model;\n}\n","/**\n * Fluent Query Builder API\n *\n * Provides a chainable API for building QueryRequest objects.\n *\n * @module query-layer/query-builder\n */\n\nimport type { Sql } from \"../sqlHelpers\";\nimport type { QueryClient } from \"../consumption-apis/helpers\";\nimport type {\n SortDir,\n SqlValue,\n FilterDefBase,\n QueryRequest,\n QueryParts,\n Names,\n OperatorValueType,\n ColumnDef,\n DimensionDef,\n MetricDef,\n} from \"./types\";\nimport type { QueryModel } from \"./query-model\";\n\n/**\n * Fluent builder for constructing query requests.\n *\n * @example\n * const results = await buildQuery(model)\n * .dimensions([\"status\"])\n * .metrics([\"totalEvents\", \"totalAmount\"])\n * .filter(\"status\", \"eq\", \"active\")\n * .orderBy([\"totalAmount\", \"DESC\"])\n * .limit(10)\n * .execute(client.query);\n */\nexport interface QueryBuilder<\n TMetrics extends string,\n TDimensions extends string,\n TFilters extends Record<string, FilterDefBase>,\n TSortable extends string,\n TResult,\n TTable = any,\n TColumns extends string = string,\n> {\n /** Add a filter condition. Automatically skips if value is undefined or null. */\n filter<K extends keyof TFilters, Op extends TFilters[K][\"operators\"][number]>(\n filterName: K,\n op: Op,\n value: OperatorValueType<Op, SqlValue> | undefined,\n ): this;\n\n /** Set dimensions to include in query (aggregate mode) */\n dimensions(fields: TDimensions[]): this;\n\n /** Set metrics to include in query (aggregate mode) */\n metrics(fields: TMetrics[]): this;\n\n /** Set columns for detail mode (no aggregation, no GROUP BY) */\n columns(fields: TColumns[]): this;\n\n /** Set multi-column sort */\n orderBy(...orders: Array<[TSortable, SortDir]>): this;\n\n /** Set maximum number of rows to return */\n limit(n: number): this;\n\n /** Set page number (0-indexed) for pagination */\n page(n: number): this;\n\n /** Set row offset for pagination */\n offset(n: number): this;\n\n /** Build the QueryRequest object */\n build(): QueryRequest<\n TMetrics,\n TDimensions,\n TFilters,\n TSortable,\n TColumns,\n TTable\n >;\n\n /** Build the SQL query */\n toSql(): Sql;\n\n /** Get query parts for custom assembly */\n toParts(): QueryParts;\n\n /** Build SQL with custom assembly function */\n assemble(fn: (parts: QueryParts) => Sql): Sql;\n\n /** Execute the query with Moose QueryClient. */\n execute(client: QueryClient): Promise<TResult[]>;\n}\n\n/**\n * Create a fluent query builder for a model.\n *\n * @param model - QueryModel instance to build queries for\n * @returns QueryBuilder instance with chainable methods\n *\n * @example\n * const results = await buildQuery(model)\n * .dimensions([\"status\"])\n * .metrics([\"totalEvents\", \"totalAmount\"])\n * .filter(\"status\", \"eq\", \"active\")\n * .orderBy([\"totalAmount\", \"DESC\"])\n * .limit(10)\n * .execute(client.query);\n */\nexport function buildQuery<\n TTable,\n TMetrics extends Record<string, MetricDef>,\n TDimensions extends Record<string, DimensionDef>,\n TFilters extends Record<string, FilterDefBase>,\n TSortable extends string,\n TResult,\n TColumns extends Record<string, ColumnDef> = Record<string, never>,\n>(\n model: QueryModel<\n TTable,\n TMetrics,\n TDimensions,\n TFilters,\n TSortable,\n TResult,\n TColumns\n >,\n): QueryBuilder<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n TResult,\n TTable,\n Names<TColumns>\n> {\n const state: {\n filters: Record<string, Record<string, unknown>>;\n dimensions?: Array<Names<TDimensions>>;\n metrics?: Array<Names<TMetrics>>;\n columns?: Array<Names<TColumns>>;\n orderBy?: Array<[TSortable, SortDir]>;\n limit?: number;\n page?: number;\n offset?: number;\n } = {\n filters: Object.create(null) as Record<string, Record<string, unknown>>,\n };\n\n const buildRequest = (): QueryRequest<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n Names<TColumns>,\n TTable\n > =>\n ({\n filters:\n Object.keys(state.filters).length > 0 ? state.filters : undefined,\n dimensions: state.dimensions,\n metrics: state.metrics,\n columns: state.columns,\n orderBy: state.orderBy,\n limit: state.limit,\n page: state.page,\n offset: state.offset,\n }) as QueryRequest<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n Names<TColumns>,\n TTable\n >;\n\n const builder: QueryBuilder<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n TResult,\n TTable,\n Names<TColumns>\n > = {\n filter(filterName, op, value) {\n if (value === undefined || value === null) return builder;\n const key = String(filterName);\n if (!Object.hasOwn(state.filters, key)) state.filters[key] = {};\n state.filters[key][op] = value;\n return builder;\n },\n\n dimensions(fields) {\n state.dimensions = fields;\n return builder;\n },\n\n metrics(fields) {\n state.metrics = fields;\n return builder;\n },\n\n columns(fields) {\n state.columns = fields;\n return builder;\n },\n\n orderBy(...orders) {\n state.orderBy = orders;\n return builder;\n },\n\n limit(n) {\n state.limit = n;\n return builder;\n },\n\n page(n) {\n state.page = n;\n state.offset = undefined;\n return builder;\n },\n\n offset(n) {\n state.offset = n;\n state.page = undefined;\n return builder;\n },\n\n build: buildRequest,\n toSql: () => model.toSql(buildRequest()),\n toParts: () => model.toParts(buildRequest()),\n assemble: (fn) => fn(model.toParts(buildRequest())),\n execute: (client) => model.query(buildRequest(), client),\n };\n\n return builder;\n}\n","/**\n * MCP Schema Generation from QueryModel\n *\n * Auto-generates Zod schemas and request builders for MCP tools\n * directly from QueryModel metadata (filters, dimensions, metrics, columns).\n *\n * @module query-layer/model-tools\n */\n\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { toQuery, type Sql } from \"../sqlHelpers\";\nimport { QueryClient } from \"../consumption-apis/helpers\";\nimport type { FilterInputTypeHint, SortDir } from \"./types\";\n// =============================================================================\n// QueryModelBase — Minimal structural interface for MCP utilities\n// =============================================================================\n\n/** Filter definition shape expected by MCP utilities. */\nexport interface QueryModelFilter {\n operators: readonly string[];\n inputType?: FilterInputTypeHint;\n required?: true;\n}\n\n/**\n * Minimal model interface consumed by createModelTool / registerModelTools.\n *\n * Any QueryModel from defineQueryModel() satisfies this structurally —\n * no explicit `implements` needed. This avoids propagating generic\n * type parameters into the MCP layer.\n */\nexport interface QueryModelBase {\n readonly name?: string;\n readonly description?: string;\n readonly defaults: {\n orderBy?: Array<[string, SortDir]>;\n groupBy?: string[];\n limit?: number;\n maxLimit?: number;\n dimensions?: string[];\n metrics?: string[];\n columns?: string[];\n };\n readonly filters: Record<string, QueryModelFilter>;\n readonly sortable: readonly string[];\n readonly dimensionNames: readonly string[];\n readonly metricNames: readonly string[];\n readonly columnNames: readonly string[];\n toSql(request: Record<string, unknown>): Sql;\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction camelToSnake(s: string): string {\n return s.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);\n}\n\nfunction titleFromName(name: string): string {\n return name\n .replace(/^query_/, \"Query \")\n .replace(/^list_/, \"List \")\n .replace(/_/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\n/** Map FilterInputTypeHint to a base Zod type */\nfunction zodBaseType(inputType?: FilterInputTypeHint): z.ZodType {\n if (inputType === \"number\") return z.number();\n return z.string();\n}\n\n/** Scalar operators use the base type; list operators use z.array(base) */\nconst SCALAR_OPS = new Set([\n \"eq\",\n \"ne\",\n \"gt\",\n \"gte\",\n \"lt\",\n \"lte\",\n \"like\",\n \"ilike\",\n]);\nconst LIST_OPS = new Set([\"in\", \"notIn\"]);\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface ModelToolOptions {\n /** Filter names whose `eq` param is required (not optional). Merged with model-level `required` flags. */\n requiredFilters?: string[];\n /** Maximum limit for the tool. Falls back to model.defaults.maxLimit, then 1000. */\n maxLimit?: number;\n /** Default limit for the tool. Falls back to model.defaults.limit, then 100. */\n defaultLimit?: number;\n /** Default values applied when params are absent. Merged with model.defaults. */\n defaults?: {\n dimensions?: string[];\n metrics?: string[];\n columns?: string[];\n limit?: number;\n };\n}\n\nexport interface ModelToolResult {\n /** Zod shape object to pass to server.tool() */\n schema: Record<string, z.ZodType>;\n /** Convert flat MCP params into a nested QueryRequest */\n buildRequest: (params: Record<string, unknown>) => Record<string, unknown>;\n}\n\n// =============================================================================\n// createModelTool\n// =============================================================================\n\n/**\n * Generate a Zod schema and request builder from a QueryModel.\n *\n * Required filters, maxLimit, and default selections are first read from the\n * model itself (via `required: true` on filter defs and `model.defaults`).\n * The optional `options` param can override or extend any of these.\n *\n * @param model - A QueryModel instance (from defineQueryModel)\n * @param options - Optional overrides for required filters, limits, defaults\n * @returns `{ schema, buildRequest }` ready for `server.tool()`\n */\nexport function createModelTool(\n model: QueryModelBase,\n options: ModelToolOptions = {},\n): ModelToolResult {\n // Derive required filters from model filter defs (where required === true)\n const modelRequiredFilters: string[] = [];\n for (const [filterName, filterDef] of Object.entries(model.filters)) {\n if (filterDef.required) {\n modelRequiredFilters.push(filterName);\n }\n }\n\n // Merge model defaults with option overrides (options win)\n const modelDefaults = model.defaults;\n const mergedDefaults = {\n dimensions: options.defaults?.dimensions ?? modelDefaults.dimensions,\n metrics: options.defaults?.metrics ?? modelDefaults.metrics,\n columns: options.defaults?.columns ?? modelDefaults.columns,\n limit: options.defaults?.limit ?? modelDefaults.limit,\n };\n\n const requiredFilters = [\n ...new Set([...modelRequiredFilters, ...(options.requiredFilters ?? [])]),\n ];\n const maxLimit = options.maxLimit ?? modelDefaults.maxLimit ?? 1000;\n const defaultLimit = options.defaultLimit ?? mergedDefaults.limit ?? 100;\n\n const requiredSet = new Set(requiredFilters);\n const schema: Record<string, z.ZodType> = {};\n\n // Map from MCP param name → { filterName, operator }\n const filterParamMap: Record<string, { filterName: string; op: string }> = {};\n\n // --- Dimensions ---\n if (model.dimensionNames.length > 0) {\n const names = model.dimensionNames as readonly [string, ...string[]];\n schema.dimensions = z.array(z.enum(names)).optional();\n }\n\n // --- Metrics ---\n if (model.metricNames.length > 0) {\n const names = model.metricNames as readonly [string, ...string[]];\n schema.metrics = z.array(z.enum(names)).optional();\n }\n\n // --- Columns ---\n if (model.columnNames.length > 0) {\n const names = model.columnNames as readonly [string, ...string[]];\n schema.columns = z.array(z.enum(names)).optional();\n }\n\n // --- Filters ---\n for (const [filterName, filterDef] of Object.entries(model.filters)) {\n const baseType = zodBaseType(filterDef.inputType);\n\n for (const op of filterDef.operators) {\n // Build the MCP param name\n const snakeName = camelToSnake(filterName);\n const paramName = op === \"eq\" ? snakeName : `${snakeName}_${op}`;\n\n // Determine Zod type for this operator\n let paramType: z.ZodType;\n if (SCALAR_OPS.has(op)) {\n paramType = baseType;\n } else if (LIST_OPS.has(op)) {\n paramType = z.array(baseType);\n } else if (op === \"between\") {\n paramType = z.array(baseType).length(2);\n } else if (op === \"isNull\" || op === \"isNotNull\") {\n paramType = z.boolean();\n } else {\n paramType = baseType;\n }\n\n // Required if filter is in requiredFilters AND op is eq\n if (requiredSet.has(filterName) && op === \"eq\") {\n schema[paramName] = paramType;\n } else {\n schema[paramName] = paramType.optional();\n }\n\n filterParamMap[paramName] = { filterName, op };\n }\n }\n\n // --- Limit ---\n schema.limit = z\n .number()\n .min(1)\n .max(maxLimit)\n .default(defaultLimit)\n .optional();\n\n // --- buildRequest ---\n function buildRequest(\n params: Record<string, unknown>,\n ): Record<string, unknown> {\n const request: Record<string, unknown> = {};\n\n // Dimensions\n if (model.dimensionNames.length > 0) {\n request.dimensions =\n (params.dimensions as string[] | undefined) ??\n mergedDefaults.dimensions;\n }\n\n // Metrics\n if (model.metricNames.length > 0) {\n request.metrics =\n (params.metrics as string[] | undefined) ?? mergedDefaults.metrics;\n }\n\n // Columns\n if (model.columnNames.length > 0) {\n request.columns =\n (params.columns as string[] | undefined) ?? mergedDefaults.columns;\n }\n\n // Filters: reverse-map flat params to nested { [filterName]: { [op]: value } }\n const filterObj: Record<string, Record<string, unknown>> = {};\n for (const [paramName, mapping] of Object.entries(filterParamMap)) {\n const value = params[paramName];\n if (value === undefined) continue;\n if (!filterObj[mapping.filterName]) {\n filterObj[mapping.filterName] = {};\n }\n filterObj[mapping.filterName][mapping.op] = value;\n }\n if (Object.keys(filterObj).length > 0) {\n request.filters = filterObj;\n }\n\n // Limit\n request.limit =\n (params.limit as number | undefined) ??\n mergedDefaults.limit ??\n defaultLimit;\n\n return request;\n }\n\n return { schema, buildRequest };\n}\n\n// =============================================================================\n// registerModelTools\n// =============================================================================\n\n/**\n * Register MCP tools for all models that have a `name` defined.\n *\n * Each model with a `name` property becomes an MCP tool. The library handles\n * everything: schema generation from model metadata, request building from\n * flat MCP params, SQL generation via `model.toSql()`, parameterized\n * execution with readonly enforcement, and MCP response formatting.\n *\n * Models without a `name` are silently skipped.\n *\n * @param server - McpServer instance\n * @param models - Array of QueryModel instances (from `defineQueryModel`)\n * @param queryClient - The QueryClient from `mooseUtils.client.query`.\n * Queries are executed in readonly mode with parameterized SQL.\n *\n * @example\n * import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n * import { getMooseUtils, MooseUtils } from \"@514labs/moose-lib\";\n * import { registerModelTools } from \"@514labs/moose-lib\";\n * import { visitsModel, usersModel } from \"./models\";\n *\n * const serverFactory = (mooseUtils: MooseUtils) => {\n * const server = new McpServer({ name: \"my-tools\", version: \"1.0.0\" });\n *\n * // One line registers all named models as MCP tools\n * registerModelTools(server, [visitsModel, usersModel], mooseUtils.client.query);\n *\n * return server;\n * };\n */\nexport function registerModelTools(\n server: McpServer,\n models: QueryModelBase[],\n queryClient: QueryClient,\n): void {\n for (const model of models) {\n if (!model.name) continue;\n\n const toolName = model.name;\n const toolDescription = model.description ?? toolName;\n const tool = createModelTool(model);\n const defaultLimit = model.defaults?.limit ?? 100;\n\n server.tool(\n toolName,\n toolDescription,\n // MCP SDK's server.tool() triggers TS2589 (infinite type instantiation)\n // when given Record<string, z.ZodType>. Cast to `any` to prevent the DTS\n // generator from expanding the SDK's deeply recursive overload signatures.\n // Tracked upstream: https://github.com/modelcontextprotocol/typescript-sdk/issues/205\n tool.schema as any, // eslint-disable-line @typescript-eslint/no-explicit-any\n { title: titleFromName(toolName) },\n async (params: Record<string, unknown>) => {\n try {\n const request = tool.buildRequest(params);\n const limit =\n typeof params.limit === \"number\" ? params.limit : defaultLimit;\n const sqlObj = model.toSql(request);\n const [query, queryParams] = toQuery(sqlObj);\n const result = await queryClient.client.query({\n query,\n query_params: queryParams,\n format: \"JSONEachRow\",\n clickhouse_settings: {\n readonly: \"2\",\n max_result_rows: limit.toString(),\n },\n });\n const data = await result.json();\n const rows = Array.isArray(data) ? data : [];\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({ rows, rowCount: rows.length }, null, 2),\n },\n ],\n };\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n const safeMsg = msg.length > 200 ? msg.slice(0, 200) + \"...\" : msg;\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Error in ${toolName}: ${safeMsg}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n }\n}\n","/**\n * Validation Utilities\n *\n * Request validation and error handling for API endpoints.\n *\n * @module query-layer/validation\n */\n\nimport type { IValidation } from \"typia\";\n\n// --- Error Types ---\n\n/** Frontend-friendly validation error structure */\nexport interface ValidationError {\n path: string;\n message: string;\n expected: string;\n received: string;\n}\n\n/** Error thrown when validation fails */\nexport class BadRequestError extends Error {\n public readonly errors: ValidationError[];\n\n constructor(typiaErrors: IValidation.IError[]) {\n super(\"Validation failed\");\n this.name = \"BadRequestError\";\n this.errors = typiaErrors.map((e) => ({\n path: e.path,\n message: `Expected ${e.expected}`,\n expected: e.expected,\n received: typeof e.value === \"undefined\" ? \"undefined\" : String(e.value),\n }));\n }\n\n toJSON() {\n return { error: this.message, details: this.errors };\n }\n}\n\n/** Assert validation result, throw BadRequestError if invalid */\nexport function assertValid<T>(result: IValidation<T>): T {\n if (!result.success) {\n throw new BadRequestError(result.errors);\n }\n return result.data;\n}\n\n// --- Query Handler ---\n\n/**\n * Query handler with three entry points for queries.\n * Use this when writing raw SQL without a query model.\n */\nexport interface QueryHandler<P, R> {\n run: (params: P) => Promise<R>;\n fromObject: (input: unknown) => Promise<R>;\n fromUrl: (url: string | URL) => Promise<R>;\n}\n\n/**\n * Create a simple query handler with validation.\n *\n * @example\n * const handler = createQueryHandler({\n * fromUrl: typia.http.createValidateQuery<MyParams>(),\n * fromObject: typia.createValidate<MyParams>(),\n * queryFn: async (params) => {\n * const query = sql`SELECT * FROM ${Table} ${where(...)}`;\n * return executeQuery(query);\n * },\n * });\n */\nexport function createQueryHandler<P, R>(config: {\n fromUrl: (input: string | URLSearchParams) => IValidation<P>;\n fromObject: (input: unknown) => IValidation<P>;\n queryFn: (params: P) => Promise<R>;\n}): QueryHandler<P, R> {\n return {\n run: config.queryFn,\n fromObject: (input) =>\n config.queryFn(assertValid(config.fromObject(input))),\n fromUrl: (url) => {\n // Dummy base required by URL constructor to parse relative paths\n // like \"/api?foo=bar\". Only .searchParams is used; the host is discarded.\n const searchParams =\n typeof url === \"string\" ?\n new URL(url, \"http://localhost\").searchParams\n : url.searchParams;\n return config.queryFn(assertValid(config.fromUrl(searchParams)));\n },\n };\n}\n","/**\n * Query Layer — Type-safe SQL query building for Moose + ClickHouse\n *\n * @example\n * import { defineQueryModel } from \"@514labs/moose-lib\";\n *\n * export const statsModel = defineQueryModel({\n * table: Events,\n * dimensions: { status: { column: \"status\" } },\n * metrics: { totalEvents: { agg: sql`count(*)`, as: \"total_events\" } },\n * filters: { status: { column: \"status\", operators: [\"eq\", \"in\"] as const } },\n * sortable: [\"total_events\"] as const,\n * });\n *\n * @module query-layer\n */\n\n// --- Primary API ---\n\nexport {\n defineQueryModel,\n type QueryModel,\n type QueryModelConfig,\n} from \"./query-model\";\n\n// --- Types ---\n\nexport type {\n QueryRequest,\n QueryParts,\n FilterParams,\n ColumnDef,\n JoinDef,\n DimensionDef,\n MetricDef,\n ModelFilterDef,\n FilterOperator,\n FilterInputTypeHint,\n FilterDefBase,\n SortDir,\n SqlValue,\n ColRef,\n Column,\n Names,\n OperatorValueType,\n} from \"./types\";\n\n// --- Composable Helpers ---\n\nexport {\n timeDimensions,\n columnsFromTable,\n filtersFromTable,\n deriveInputTypeFromDataType,\n} from \"./helpers\";\n\n// --- Fluent Query Builder ---\n\nexport { buildQuery, type QueryBuilder } from \"./query-builder\";\n\n// --- MCP Utilities ---\n\nexport {\n createModelTool,\n registerModelTools,\n type QueryModelBase,\n type QueryModelFilter,\n type ModelToolOptions,\n type ModelToolResult,\n} from \"./model-tools\";\n\n// --- SQL Utilities ---\n\nexport {\n raw,\n empty,\n join,\n isEmpty,\n\n // Filter function\n filter,\n\n // Comparison operators\n eq,\n ne,\n gt,\n gte,\n lt,\n lte,\n like,\n ilike,\n inList,\n notIn,\n between,\n isNull,\n isNotNull,\n\n // Logical combinators\n and,\n or,\n not,\n\n // SQL clauses\n where,\n orderBy,\n limit,\n offset,\n paginate,\n groupBy,\n having,\n\n // Aggregation functions\n count,\n countDistinct,\n sum,\n avg,\n min,\n max,\n\n // Select helpers\n select,\n as,\n type Expr,\n} from \"./sql-utils\";\n\n// --- Validation Utilities (re-exported from consumption-apis) ---\n\nexport {\n BadRequestError,\n assertValid,\n createQueryHandler,\n type ValidationError,\n type QueryHandler,\n} from \"../consumption-apis/validation\";\n","export * from \"./browserCompatible\";\n\nexport type DataModelConfig<T> = Partial<{\n ingestion: true;\n storage: {\n enabled?: boolean;\n order_by_fields?: (keyof T)[];\n deduplicate?: boolean;\n name?: string;\n };\n parallelism?: number;\n}>;\n\nexport * from \"./commons\";\nexport * from \"./secrets\";\nexport * from \"./consumption-apis/helpers\";\nexport {\n expressMiddleware,\n ExpressRequestWithMoose,\n getMooseUtilsFromRequest,\n getLegacyMooseUtils,\n} from \"./consumption-apis/webAppHelpers\";\nexport * from \"./scripts/task\";\n\nexport { MooseCache } from \"./clients/redisClient\";\n\nexport { ApiUtil, ConsumptionUtil } from \"./consumption-apis/helpers\";\n\nexport { getMooseUtils, getMooseClients } from \"./consumption-apis/standalone\";\nexport type { MooseUtils } from \"./consumption-apis/helpers\";\nexport { sql } from \"./sqlHelpers\";\n\nexport * from \"./utilities\";\nexport * from \"./connectors/dataSource\";\nexport {\n ClickHouseByteSize,\n ClickHouseInt,\n LowCardinality,\n ClickHouseNamedTuple,\n ClickHousePoint,\n ClickHouseRing,\n ClickHouseLineString,\n ClickHouseMultiLineString,\n ClickHousePolygon,\n ClickHouseMultiPolygon,\n} from \"./dataModels/types\";\n\nexport * from \"./query-layer\";\n","import { existsSync, readFileSync } from \"fs\";\nimport path from \"path\";\n\n/**\n * Shared TypeScript compiler configuration for moose projects.\n * Used by both moose-runner.ts (runtime) and moose-tspc.ts (pre-compilation).\n *\n * Moose now always uses pre-compiled JavaScript - no ts-node fallback.\n */\n\nexport const MOOSE_COMPILER_PLUGINS = [\n {\n transform: \"./node_modules/@514labs/moose-lib/dist/compilerPlugin.js\",\n // No longer using transformProgram - direct typia integration eliminates\n // the need for program replacement and the associated incremental compilation issues\n },\n {\n // Keep typia plugin for users who use typia directly (not through Moose resources)\n transform: \"typia/lib/transform\",\n },\n] as const;\n\n// Options required for moose compilation\n// Note: We only set what's absolutely necessary to avoid conflicts with user projects\nexport const MOOSE_COMPILER_OPTIONS = {\n experimentalDecorators: true,\n esModuleInterop: true,\n // Disable strict module syntax checking to avoid dual-package type conflicts\n // This prevents errors where the same type imported with different resolution\n // modes (CJS vs ESM) is treated as incompatible\n verbatimModuleSyntax: false,\n} as const;\n\n// Module resolution options - only applied if not already set in user's tsconfig\n// These help with ESM/CJS interop but can be overridden by user config\nexport const MOOSE_MODULE_OPTIONS = {\n module: \"NodeNext\",\n moduleResolution: \"NodeNext\",\n} as const;\n\n/**\n * Default source directory for user code.\n * Can be overridden via MOOSE_SOURCE_DIR environment variable.\n */\nexport function getSourceDir(): string {\n return process.env.MOOSE_SOURCE_DIR || \"app\";\n}\n\n/**\n * Default output directory for compiled code.\n */\nexport const DEFAULT_OUT_DIR = \".moose/compiled\";\n\n/**\n * Read the user's tsconfig.json and extract the outDir setting.\n * Supports JSONC (JSON with Comments) by evaluating as JavaScript\n * (JSON with comments is valid JS in ES2019+).\n *\n * Security note: This uses eval-like behavior (new Function), which means\n * malicious code in tsconfig.json would execute. This is acceptable because:\n * - The user controls their own tsconfig.json\n * - Same trust model as running `tsc` or any other tool that processes the file\n * - If you clone and run untrusted code, you're already at risk\n *\n * Returns the outDir if specified, or null if not.\n */\nexport function readUserOutDir(\n projectRoot: string = process.cwd(),\n): string | null {\n try {\n let content = readFileSync(\n path.join(projectRoot, \"tsconfig.json\"),\n \"utf-8\",\n );\n // Strip UTF-8 BOM if present\n if (content.charCodeAt(0) === 0xfeff) {\n content = content.slice(1);\n }\n // eslint-disable-next-line no-eval\n const tsconfig = eval(`(${content})`);\n return tsconfig.compilerOptions?.outDir || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get the output directory for compiled code.\n * Uses user's tsconfig outDir if specified, otherwise defaults to .moose/compiled\n */\nexport function getOutDir(projectRoot: string = process.cwd()): string {\n const userOutDir = readUserOutDir(projectRoot);\n return userOutDir || DEFAULT_OUT_DIR;\n}\n\n/**\n * Get the path to the compiled index.js file.\n */\nexport function getCompiledIndexPath(\n projectRoot: string = process.cwd(),\n): string {\n const outDir = getOutDir(projectRoot);\n const sourceDir = getSourceDir();\n // Resolve so that absolute outDir from tsconfig is handled correctly\n return path.resolve(projectRoot, outDir, sourceDir, \"index.js\");\n}\n\n/**\n * Check if pre-compiled artifacts exist for the current project.\n */\nexport function hasCompiledArtifacts(\n projectRoot: string = process.cwd(),\n): boolean {\n return existsSync(getCompiledIndexPath(projectRoot));\n}\n\n/**\n * Module system type for compilation output.\n */\nexport type ModuleSystem = \"esm\" | \"cjs\";\n\n/**\n * Detects the module system from the user's package.json.\n * Returns 'esm' if package.json has \"type\": \"module\", otherwise 'cjs'.\n *\n * @param projectRoot - Root directory containing package.json (defaults to cwd)\n * @returns The detected module system\n */\nexport function detectModuleSystem(\n projectRoot: string = process.cwd(),\n): ModuleSystem {\n const pkgPath = path.join(projectRoot, \"package.json\");\n\n if (existsSync(pkgPath)) {\n try {\n const pkgContent = readFileSync(pkgPath, \"utf-8\");\n const pkg = JSON.parse(pkgContent);\n if (pkg.type === \"module\") {\n return \"esm\";\n }\n } catch (e) {\n // If parsing fails, default to CJS\n console.debug(\n `[moose] Failed to parse package.json at ${pkgPath}, defaulting to CJS:`,\n e,\n );\n }\n }\n\n return \"cjs\";\n}\n\n/**\n * Get compiler module options based on detected module system.\n *\n * @param moduleSystem - The module system to get options for\n * @returns Compiler options for module and moduleResolution\n */\nexport function getModuleOptions(moduleSystem: ModuleSystem): {\n module: string;\n moduleResolution: string;\n} {\n if (moduleSystem === \"esm\") {\n return {\n module: \"ES2022\",\n moduleResolution: \"bundler\",\n };\n }\n return {\n module: \"CommonJS\",\n moduleResolution: \"Node\",\n };\n}\n\n/**\n * Dynamic module loader that works with both CJS and ESM.\n * Uses detected module system to determine loading strategy.\n *\n * @param modulePath - Path to the module to load\n * @param projectRoot - Root directory for module system detection\n * @returns The loaded module\n */\nexport async function loadModule<T = any>(\n modulePath: string,\n projectRoot: string = process.cwd(),\n): Promise<T> {\n const moduleSystem = detectModuleSystem(projectRoot);\n\n if (moduleSystem === \"esm\") {\n // Use dynamic import for ESM\n // pathToFileURL is needed for Windows compatibility with absolute paths\n const { pathToFileURL } = await import(\"url\");\n const fileUrl = pathToFileURL(modulePath).href;\n return await import(fileUrl);\n }\n\n // Use require for CJS\n // Note: In ESM builds (compiled by tsup), this code path is replaced with\n // the appropriate ESM imports. The dual-package build ensures compatibility.\n return require(modulePath);\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nconst DEFAULT_SOURCE_EXTENSIONS = new Set([\n \".ts\",\n \".tsx\",\n \".js\",\n \".jsx\",\n \".mts\",\n \".cts\",\n]);\n\n/**\n * Returns true when the file path matches supported source extensions.\n */\nexport function isSourceFilePath(filePath: string): boolean {\n const ext = path.extname(filePath).toLowerCase();\n return DEFAULT_SOURCE_EXTENSIONS.has(ext);\n}\n\n/**\n * Recursively finds source files under a directory.\n *\n * Skips:\n * - `node_modules` directories\n * - hidden directories\n * - TypeScript declaration files (`.d.ts`, `.d.mts`, `.d.cts`)\n */\nexport function findSourceFiles(\n dir: string,\n onReadError?: (directory: string, error: unknown) => void,\n): string[] {\n const files: string[] = [];\n if (!fs.existsSync(dir)) {\n return files;\n }\n\n const stack = [dir];\n while (stack.length > 0) {\n const current = stack.pop()!;\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(current, { withFileTypes: true });\n } catch (error) {\n onReadError?.(current, error);\n continue;\n }\n\n for (const entry of entries) {\n const fullPath = path.join(current, entry.name);\n if (entry.isDirectory()) {\n if (entry.name === \"node_modules\" || entry.name.startsWith(\".\")) {\n continue;\n }\n stack.push(fullPath);\n continue;\n }\n\n if (!entry.isFile() || !isSourceFilePath(fullPath)) {\n continue;\n }\n if (\n fullPath.endsWith(\".d.ts\") ||\n fullPath.endsWith(\".d.mts\") ||\n fullPath.endsWith(\".d.cts\")\n ) {\n continue;\n }\n files.push(path.resolve(fullPath));\n }\n }\n\n return files;\n}\n","/**\n * Utility functions for the DMv2 SDK.\n */\n\nexport {\n getSourceFileFromStack,\n getSourceFileInfo,\n type SourceFileInfo,\n} from \"./stackTrace\";\nexport { findSourceFiles, isSourceFilePath } from \"./sourceFiles\";\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport ts from \"typescript\";\n\nimport { getSourceDir } from \"../compiler-config\";\nimport { compilerLog } from \"../commons\";\nimport { findSourceFiles, isSourceFilePath } from \"./utils\";\n\ntype SignatureKind =\n | \"Table\"\n | \"Topic\"\n | \"ApiEndpoint\"\n | \"TopicToTableSyncProcess\"\n | \"View\"\n | \"MaterializedView\"\n | \"SqlResource\";\n\nexport interface InfrastructureSignatureJson {\n id: string;\n kind: SignatureKind;\n}\n\nexport interface DependencySignatures {\n pullsDataFrom: InfrastructureSignatureJson[];\n pushesDataTo: InfrastructureSignatureJson[];\n}\n\nexport interface DependencyAnalysisResult {\n apiByKey: Map<string, DependencySignatures>;\n workflowByName: Map<string, DependencySignatures>;\n webAppByName: Map<string, DependencySignatures>;\n}\n\ninterface RegistryLike {\n tables: Map<string, any>;\n streams: Map<string, any>;\n apis: Map<string, any>;\n workflows: Map<string, any>;\n webApps: Map<string, any>;\n}\n\ninterface RegistryIndex {\n tableIdsByName: Map<string, string[]>;\n topicIdsByName: Map<string, string[]>;\n tableIds: Set<string>;\n topicIds: Set<string>;\n warnedAmbiguousTableIds: Set<string>;\n}\n\ntype ResourceRef =\n | { kind: \"Table\"; id: string }\n | { kind: \"Topic\"; id: string }\n | { kind: \"View\"; id: string }\n | { kind: \"MaterializedView\"; id: string; targetTableId?: string }\n | { kind: \"SqlResource\"; id: string }\n | { kind: \"IngestPipeline\"; streamId?: string; tableId?: string };\n\ntype SqlResolvedResourceRef =\n | { kind: \"Table\"; id: string }\n | { kind: \"Topic\"; id: string };\n\ninterface AnalysisContext {\n checker: ts.TypeChecker;\n registryIndex: RegistryIndex;\n symbolResourceCache: Map<ts.Symbol, ResourceRef | null>;\n taskExpressionCache: Map<string, ts.NewExpression | null>;\n functionCache: Map<ts.Symbol, ts.FunctionLikeDeclaration[]>;\n}\n\nconst WRITE_METHODS = new Set([\"insert\", \"send\", \"publish\", \"emit\", \"write\"]);\n\nfunction createEmptyResult(): DependencyAnalysisResult {\n return {\n apiByKey: new Map<string, DependencySignatures>(),\n workflowByName: new Map<string, DependencySignatures>(),\n webAppByName: new Map<string, DependencySignatures>(),\n };\n}\n\nfunction buildRegistryIndex(registry: RegistryLike): RegistryIndex {\n const tableIdsByName = new Map<string, string[]>();\n const topicIdsByName = new Map<string, string[]>();\n const tableIds = new Set<string>();\n const topicIds = new Set<string>();\n\n registry.tables.forEach((table: any, id: string) => {\n tableIds.add(id);\n const baseName = typeof table?.name === \"string\" ? table.name : id;\n const existing = tableIdsByName.get(baseName) ?? [];\n existing.push(id);\n tableIdsByName.set(baseName, existing);\n });\n\n registry.streams.forEach((stream: any, id: string) => {\n topicIds.add(id);\n const streamName = typeof stream?.name === \"string\" ? stream.name : id;\n const existing = topicIdsByName.get(streamName) ?? [];\n existing.push(id);\n topicIdsByName.set(streamName, existing);\n });\n\n return {\n tableIdsByName,\n topicIdsByName,\n tableIds,\n topicIds,\n warnedAmbiguousTableIds: new Set<string>(),\n };\n}\n\nfunction resolveTableId(\n tableName: string,\n version: string | undefined,\n index: RegistryIndex,\n): string {\n if (version) {\n const candidates = new Set<string>([`${tableName}_${version}`]);\n if (version.includes(\".\")) {\n candidates.add(`${tableName}_${version.replace(/\\./g, \"_\")}`);\n }\n\n for (const candidate of candidates) {\n if (index.tableIds.has(candidate)) {\n return candidate;\n }\n\n const ids = index.tableIdsByName.get(candidate) ?? [];\n if (ids.length === 1) {\n return ids[0];\n }\n if (ids.length > 1) {\n if (ids.includes(candidate)) {\n return candidate;\n }\n return ids[0];\n }\n }\n\n return `${tableName}_${version}`;\n }\n\n const ids = index.tableIdsByName.get(tableName) ?? [];\n if (ids.length === 0) {\n return tableName;\n }\n if (ids.includes(tableName)) {\n return tableName;\n }\n if (ids.length > 1) {\n const warningKey = `${tableName}:${ids.join(\",\")}`;\n if (!index.warnedAmbiguousTableIds.has(warningKey)) {\n index.warnedAmbiguousTableIds.add(warningKey);\n compilerLog(\n `Warning: ambiguous table lineage reference '${tableName}' resolved to '${ids[0]}' from candidates [${ids.join(\", \")}]. Add an explicit version to disambiguate.`,\n );\n }\n }\n return ids[0];\n}\n\nfunction resolveTopicId(topicName: string, index: RegistryIndex): string {\n const ids = index.topicIdsByName.get(topicName) ?? [];\n if (ids.length === 0) {\n return topicName;\n }\n if (ids.includes(topicName)) {\n return topicName;\n }\n return ids[0];\n}\n\nfunction normalizeSqlIdentifier(token: string): string {\n let normalized = token.trim();\n normalized = normalized.replace(/^[`\"'\\[]+/, \"\");\n normalized = normalized.replace(/[`\"'\\]]+$/, \"\");\n return normalized;\n}\n\nfunction resolveResourceFromSqlIdentifier(\n identifier: string,\n index: RegistryIndex,\n): SqlResolvedResourceRef | undefined {\n const normalized = normalizeSqlIdentifier(identifier);\n if (!normalized) {\n return undefined;\n }\n\n const candidates = new Set<string>([normalized]);\n if (normalized.includes(\".\")) {\n const parts = normalized.split(\".\");\n const suffix = parts[parts.length - 1];\n if (suffix && suffix !== normalized) {\n candidates.add(suffix);\n }\n }\n\n for (const candidate of [...candidates]) {\n const versionedMatch = candidate.match(/^(.+)_\\d+_\\d+$/);\n if (versionedMatch?.[1]) {\n candidates.add(versionedMatch[1]);\n }\n }\n\n for (const candidate of candidates) {\n if (index.tableIds.has(candidate)) {\n return { kind: \"Table\", id: candidate };\n }\n if (index.tableIdsByName.has(candidate)) {\n return { kind: \"Table\", id: resolveTableId(candidate, undefined, index) };\n }\n if (index.topicIds.has(candidate)) {\n return { kind: \"Topic\", id: candidate };\n }\n if (index.topicIdsByName.has(candidate)) {\n return { kind: \"Topic\", id: resolveTopicId(candidate, index) };\n }\n }\n\n return undefined;\n}\n\nfunction inferResourcesFromSqlText(\n text: string,\n index: RegistryIndex,\n): SqlResolvedResourceRef[] {\n const refs = new Map<string, SqlResolvedResourceRef>();\n const addRef = (ref: SqlResolvedResourceRef | undefined) => {\n if (!ref) {\n return;\n }\n refs.set(`${ref.kind}:${ref.id}`, ref);\n };\n\n const trimmed = text.trim();\n if (trimmed && !/\\s/.test(trimmed)) {\n addRef(resolveResourceFromSqlIdentifier(trimmed, index));\n }\n\n const relationPattern =\n /\\b(?:from|join|into|update|table)\\s+([`\"'\\[]?[A-Za-z_][A-Za-z0-9_.]*[`\"'\\]]?)/gi;\n for (const match of text.matchAll(relationPattern)) {\n addRef(resolveResourceFromSqlIdentifier(match[1], index));\n }\n\n return [...refs.values()];\n}\n\nfunction collectSqlTextFragmentsFromExpression(\n expression: ts.Expression,\n ctx: AnalysisContext,\n fragments: string[],\n visitedSymbols = new Set<ts.Symbol>(),\n) {\n const unwrapped = unwrapExpression(expression);\n\n if (\n ts.isStringLiteral(unwrapped) ||\n ts.isNoSubstitutionTemplateLiteral(unwrapped)\n ) {\n fragments.push(unwrapped.text);\n return;\n }\n\n if (ts.isTemplateExpression(unwrapped)) {\n fragments.push(unwrapped.head.text);\n for (const span of unwrapped.templateSpans) {\n fragments.push(span.literal.text);\n }\n return;\n }\n\n if (ts.isTaggedTemplateExpression(unwrapped)) {\n if (isSqlTag(unwrapped.tag, ctx.checker)) {\n const resources = inferResourcesFromSqlTemplate(unwrapped.template, ctx);\n for (const resource of resources) {\n fragments.push(resource.id);\n }\n }\n return;\n }\n\n if (ts.isArrayLiteralExpression(unwrapped)) {\n for (const element of unwrapped.elements) {\n if (ts.isExpression(element)) {\n collectSqlTextFragmentsFromExpression(\n element,\n ctx,\n fragments,\n visitedSymbols,\n );\n }\n }\n return;\n }\n\n if (ts.isCallExpression(unwrapped)) {\n for (const arg of unwrapped.arguments) {\n collectSqlTextFragmentsFromExpression(\n arg,\n ctx,\n fragments,\n visitedSymbols,\n );\n }\n return;\n }\n\n if (ts.isObjectLiteralExpression(unwrapped)) {\n for (const property of unwrapped.properties) {\n if (ts.isPropertyAssignment(property)) {\n collectSqlTextFragmentsFromExpression(\n property.initializer,\n ctx,\n fragments,\n visitedSymbols,\n );\n }\n }\n return;\n }\n\n if (ts.isConditionalExpression(unwrapped)) {\n collectSqlTextFragmentsFromExpression(\n unwrapped.whenTrue,\n ctx,\n fragments,\n visitedSymbols,\n );\n collectSqlTextFragmentsFromExpression(\n unwrapped.whenFalse,\n ctx,\n fragments,\n visitedSymbols,\n );\n return;\n }\n\n if (ts.isBinaryExpression(unwrapped)) {\n collectSqlTextFragmentsFromExpression(\n unwrapped.left,\n ctx,\n fragments,\n visitedSymbols,\n );\n collectSqlTextFragmentsFromExpression(\n unwrapped.right,\n ctx,\n fragments,\n visitedSymbols,\n );\n return;\n }\n\n if (ts.isIdentifier(unwrapped) || ts.isPropertyAccessExpression(unwrapped)) {\n const staticValue = resolveStaticString(unwrapped, ctx);\n if (staticValue !== undefined) {\n fragments.push(staticValue);\n return;\n }\n\n const symbol = resolveAliasedSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx.checker,\n );\n if (!symbol || visitedSymbols.has(symbol)) {\n return;\n }\n visitedSymbols.add(symbol);\n\n for (const declaration of symbol.declarations ?? []) {\n const initializer = resolveSymbolInitializerExpression(declaration);\n if (!initializer) {\n continue;\n }\n collectSqlTextFragmentsFromExpression(\n initializer,\n ctx,\n fragments,\n visitedSymbols,\n );\n }\n }\n}\n\nfunction inferResourcesFromSqlTemplate(\n template: ts.TemplateLiteral,\n ctx: AnalysisContext,\n): SqlResolvedResourceRef[] {\n const refs = new Map<string, SqlResolvedResourceRef>();\n const addFromText = (text: string) => {\n for (const ref of inferResourcesFromSqlText(text, ctx.registryIndex)) {\n refs.set(`${ref.kind}:${ref.id}`, ref);\n }\n };\n\n if (ts.isNoSubstitutionTemplateLiteral(template)) {\n addFromText(template.text);\n return [...refs.values()];\n }\n\n addFromText(template.head.text);\n for (const span of template.templateSpans) {\n addFromText(span.literal.text);\n }\n\n return [...refs.values()];\n}\n\nfunction inferResourcesFromSqlCallArguments(\n argumentsList: readonly ts.Expression[],\n ctx: AnalysisContext,\n): SqlResolvedResourceRef[] {\n const refs = new Map<string, SqlResolvedResourceRef>();\n const fragments: string[] = [];\n\n for (const arg of argumentsList) {\n collectSqlTextFragmentsFromExpression(arg, ctx, fragments);\n }\n\n for (const fragment of fragments) {\n for (const ref of inferResourcesFromSqlText(fragment, ctx.registryIndex)) {\n refs.set(`${ref.kind}:${ref.id}`, ref);\n }\n }\n\n return [...refs.values()];\n}\n\nfunction getObjectPropertyExpression(\n objectLiteral: ts.ObjectLiteralExpression,\n propertyName: string,\n): ts.Expression | undefined {\n for (const property of objectLiteral.properties) {\n if (ts.isPropertyAssignment(property)) {\n const name =\n ts.isIdentifier(property.name) ? property.name.text\n : ts.isStringLiteral(property.name) ? property.name.text\n : undefined;\n if (name === propertyName) {\n return property.initializer;\n }\n }\n if (\n ts.isShorthandPropertyAssignment(property) &&\n property.name.text === propertyName\n ) {\n return property.name;\n }\n }\n return undefined;\n}\n\nfunction resolveAliasedSymbol(\n symbol: ts.Symbol | undefined,\n checker: ts.TypeChecker,\n): ts.Symbol | undefined {\n if (!symbol) {\n return undefined;\n }\n if ((symbol.flags & ts.SymbolFlags.Alias) !== 0) {\n try {\n return checker.getAliasedSymbol(symbol);\n } catch {\n return symbol;\n }\n }\n return symbol;\n}\n\nfunction unwrapExpression(expression: ts.Expression): ts.Expression {\n let current = expression;\n type WrapperExpression =\n | ts.ParenthesizedExpression\n | ts.AsExpression\n | ts.TypeAssertion\n | ts.NonNullExpression;\n while (\n ts.isParenthesizedExpression(current) ||\n ts.isAsExpression(current) ||\n ts.isTypeAssertionExpression(current) ||\n ts.isNonNullExpression(current)\n ) {\n current = (current as WrapperExpression).expression;\n }\n return current;\n}\n\nfunction unwrapCallTargetExpression(expression: ts.Expression): ts.Expression {\n let current = unwrapExpression(expression);\n while (\n ts.isBinaryExpression(current) &&\n current.operatorToken.kind === ts.SyntaxKind.CommaToken\n ) {\n current = unwrapExpression(current.right);\n }\n return current;\n}\n\nfunction resolveStaticString(\n expression: ts.Expression | undefined,\n ctx: AnalysisContext,\n visitedSymbols = new Set<ts.Symbol>(),\n): string | undefined {\n if (!expression) {\n return undefined;\n }\n\n const unwrapped = unwrapExpression(expression);\n if (\n ts.isStringLiteral(unwrapped) ||\n ts.isNoSubstitutionTemplateLiteral(unwrapped)\n ) {\n return unwrapped.text;\n }\n\n if (ts.isIdentifier(unwrapped) || ts.isPropertyAccessExpression(unwrapped)) {\n const symbol = resolveAliasedSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx.checker,\n );\n if (!symbol || visitedSymbols.has(symbol)) {\n return undefined;\n }\n visitedSymbols.add(symbol);\n for (const declaration of symbol.declarations ?? []) {\n if (ts.isVariableDeclaration(declaration) && declaration.initializer) {\n const value = resolveStaticString(\n declaration.initializer,\n ctx,\n visitedSymbols,\n );\n if (value !== undefined) {\n return value;\n }\n } else if (ts.isPropertyAssignment(declaration)) {\n const value = resolveStaticString(\n declaration.initializer,\n ctx,\n visitedSymbols,\n );\n if (value !== undefined) {\n return value;\n }\n }\n }\n }\n\n return undefined;\n}\n\nfunction resolveObjectLiteralExpression(\n expression: ts.Expression | undefined,\n ctx: AnalysisContext,\n visitedSymbols = new Set<ts.Symbol>(),\n): ts.ObjectLiteralExpression | undefined {\n if (!expression) {\n return undefined;\n }\n\n const unwrapped = unwrapExpression(expression);\n if (ts.isObjectLiteralExpression(unwrapped)) {\n return unwrapped;\n }\n\n if (ts.isIdentifier(unwrapped) || ts.isPropertyAccessExpression(unwrapped)) {\n const symbol = resolveAliasedSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx.checker,\n );\n if (!symbol || visitedSymbols.has(symbol)) {\n return undefined;\n }\n visitedSymbols.add(symbol);\n for (const declaration of symbol.declarations ?? []) {\n if (ts.isVariableDeclaration(declaration) && declaration.initializer) {\n const result = resolveObjectLiteralExpression(\n declaration.initializer,\n ctx,\n visitedSymbols,\n );\n if (result) {\n return result;\n }\n } else if (ts.isPropertyAssignment(declaration)) {\n const result = resolveObjectLiteralExpression(\n declaration.initializer,\n ctx,\n visitedSymbols,\n );\n if (result) {\n return result;\n }\n }\n }\n }\n\n return undefined;\n}\n\nfunction constructorNameFromNewExpression(\n expression: ts.Expression,\n checker: ts.TypeChecker,\n): string | undefined {\n const symbol = resolveAliasedSymbol(\n checker.getSymbolAtLocation(expression),\n checker,\n );\n if (symbol?.name) {\n return symbol.name;\n }\n if (ts.isIdentifier(expression)) {\n return expression.text;\n }\n if (ts.isPropertyAccessExpression(expression)) {\n return expression.name.text;\n }\n return undefined;\n}\n\nfunction parseOlapTableRef(\n newExpression: ts.NewExpression,\n ctx: AnalysisContext,\n): ResourceRef | undefined {\n const tableName = resolveStaticString(newExpression.arguments?.[0], ctx);\n if (!tableName) {\n return undefined;\n }\n const configLiteral = resolveObjectLiteralExpression(\n newExpression.arguments?.[1],\n ctx,\n );\n const version =\n configLiteral ?\n resolveStaticString(\n getObjectPropertyExpression(configLiteral, \"version\"),\n ctx,\n )\n : undefined;\n\n return {\n kind: \"Table\",\n id: resolveTableId(tableName, version, ctx.registryIndex),\n };\n}\n\nfunction parseStreamRef(\n newExpression: ts.NewExpression,\n ctx: AnalysisContext,\n): ResourceRef | undefined {\n const topicName = resolveStaticString(newExpression.arguments?.[0], ctx);\n if (!topicName) {\n return undefined;\n }\n return { kind: \"Topic\", id: resolveTopicId(topicName, ctx.registryIndex) };\n}\n\nfunction parseSimpleNamedRef(\n newExpression: ts.NewExpression,\n kind: \"View\" | \"SqlResource\",\n ctx: AnalysisContext,\n): ResourceRef | undefined {\n const name = resolveStaticString(newExpression.arguments?.[0], ctx);\n if (!name) {\n return undefined;\n }\n return { kind, id: name };\n}\n\nfunction parseMaterializedViewRef(\n newExpression: ts.NewExpression,\n ctx: AnalysisContext,\n): ResourceRef | undefined {\n const options = resolveObjectLiteralExpression(\n newExpression.arguments?.[0],\n ctx,\n );\n if (!options) {\n return undefined;\n }\n\n const mvName = resolveStaticString(\n getObjectPropertyExpression(options, \"materializedViewName\"),\n ctx,\n );\n if (!mvName) {\n return undefined;\n }\n\n let targetTableId: string | undefined;\n const targetTableExpression = getObjectPropertyExpression(\n options,\n \"targetTable\",\n );\n const targetTableObject = resolveObjectLiteralExpression(\n targetTableExpression,\n ctx,\n );\n if (targetTableObject) {\n const tableName = resolveStaticString(\n getObjectPropertyExpression(targetTableObject, \"name\"),\n ctx,\n );\n const version = resolveStaticString(\n getObjectPropertyExpression(targetTableObject, \"version\"),\n ctx,\n );\n if (tableName) {\n targetTableId = resolveTableId(tableName, version, ctx.registryIndex);\n }\n } else if (targetTableExpression) {\n const targetRef = resolveResourceFromExpression(\n targetTableExpression,\n ctx,\n new Map(),\n );\n if (targetRef?.kind === \"Table\") {\n targetTableId = targetRef.id;\n }\n }\n\n if (!targetTableId) {\n const legacyTableName = resolveStaticString(\n getObjectPropertyExpression(options, \"tableName\"),\n ctx,\n );\n if (legacyTableName) {\n targetTableId = resolveTableId(\n legacyTableName,\n undefined,\n ctx.registryIndex,\n );\n }\n }\n\n return {\n kind: \"MaterializedView\",\n id: mvName,\n targetTableId,\n };\n}\n\nfunction parseIngestPipelineRef(\n newExpression: ts.NewExpression,\n ctx: AnalysisContext,\n): ResourceRef | undefined {\n const pipelineName = resolveStaticString(newExpression.arguments?.[0], ctx);\n if (!pipelineName) {\n return undefined;\n }\n\n const config = resolveObjectLiteralExpression(\n newExpression.arguments?.[1],\n ctx,\n );\n let version: string | undefined;\n let streamEnabled = true;\n let tableEnabled = true;\n if (config) {\n const versionExpr = getObjectPropertyExpression(config, \"version\");\n version = resolveStaticString(versionExpr, ctx);\n\n const streamExpr = getObjectPropertyExpression(config, \"stream\");\n if (streamExpr && streamExpr.kind === ts.SyntaxKind.FalseKeyword) {\n streamEnabled = false;\n }\n\n const tableExpr = getObjectPropertyExpression(config, \"table\");\n if (tableExpr && tableExpr.kind === ts.SyntaxKind.FalseKeyword) {\n tableEnabled = false;\n }\n }\n\n return {\n kind: \"IngestPipeline\",\n streamId:\n streamEnabled ?\n resolveTopicId(pipelineName, ctx.registryIndex)\n : undefined,\n tableId:\n tableEnabled ?\n resolveTableId(pipelineName, version, ctx.registryIndex)\n : undefined,\n };\n}\n\nfunction resolveResourceFromNewExpression(\n newExpression: ts.NewExpression,\n ctx: AnalysisContext,\n): ResourceRef | undefined {\n const constructorName = constructorNameFromNewExpression(\n newExpression.expression,\n ctx.checker,\n );\n\n switch (constructorName) {\n case \"OlapTable\":\n return parseOlapTableRef(newExpression, ctx);\n case \"Stream\":\n return parseStreamRef(newExpression, ctx);\n case \"View\":\n return parseSimpleNamedRef(newExpression, \"View\", ctx);\n case \"SqlResource\":\n return parseSimpleNamedRef(newExpression, \"SqlResource\", ctx);\n case \"MaterializedView\":\n return parseMaterializedViewRef(newExpression, ctx);\n case \"IngestPipeline\":\n return parseIngestPipelineRef(newExpression, ctx);\n default:\n return undefined;\n }\n}\n\nfunction resolveResourceFromSymbol(\n symbol: ts.Symbol | undefined,\n ctx: AnalysisContext,\n bindings: Map<ts.Symbol, ResourceRef>,\n): ResourceRef | undefined {\n const resolvedSymbol = resolveAliasedSymbol(symbol, ctx.checker);\n if (!resolvedSymbol) {\n return undefined;\n }\n\n const bound = bindings.get(resolvedSymbol);\n if (bound) {\n return bound;\n }\n\n const cached = ctx.symbolResourceCache.get(resolvedSymbol);\n if (cached !== undefined) {\n return cached ?? undefined;\n }\n\n ctx.symbolResourceCache.set(resolvedSymbol, null);\n\n for (const declaration of resolvedSymbol.declarations ?? []) {\n const initializer = resolveSymbolInitializerExpression(declaration);\n if (!initializer) {\n continue;\n }\n\n const resource = resolveResourceFromExpression(initializer, ctx, bindings);\n if (resource) {\n ctx.symbolResourceCache.set(resolvedSymbol, resource);\n return resource;\n }\n }\n\n ctx.symbolResourceCache.set(resolvedSymbol, null);\n return undefined;\n}\n\nfunction resolveSymbolInitializerExpression(\n declaration: ts.Declaration,\n): ts.Expression | undefined {\n if (ts.isVariableDeclaration(declaration) && declaration.initializer) {\n return declaration.initializer;\n }\n\n if (ts.isPropertyAssignment(declaration)) {\n return declaration.initializer;\n }\n\n if (\n (ts.isPropertyAccessExpression(declaration) ||\n ts.isElementAccessExpression(declaration) ||\n ts.isIdentifier(declaration)) &&\n ts.isBinaryExpression(declaration.parent) &&\n declaration.parent.left === declaration &&\n declaration.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken\n ) {\n return declaration.parent.right;\n }\n\n return undefined;\n}\n\nfunction isColumnsProjection(expression: ts.Expression): boolean {\n const unwrapped = unwrapExpression(expression);\n if (ts.isPropertyAccessExpression(unwrapped)) {\n return unwrapped.name.text === \"columns\";\n }\n if (ts.isElementAccessExpression(unwrapped)) {\n return isColumnsProjection(unwrapped.expression);\n }\n return false;\n}\n\nfunction resolveResourceFromExpression(\n expression: ts.Expression,\n ctx: AnalysisContext,\n bindings: Map<ts.Symbol, ResourceRef>,\n): ResourceRef | undefined {\n const unwrapped = unwrapExpression(expression);\n\n if (ts.isNewExpression(unwrapped)) {\n return resolveResourceFromNewExpression(unwrapped, ctx);\n }\n\n if (\n ts.isTaggedTemplateExpression(unwrapped) &&\n isSqlTag(unwrapped.tag, ctx.checker)\n ) {\n const inferred = inferResourcesFromSqlTemplate(unwrapped.template, ctx);\n if (inferred.length === 1) {\n return inferred[0];\n }\n }\n\n if (ts.isCallExpression(unwrapped)) {\n const calleeExpression = unwrapCallTargetExpression(unwrapped.expression);\n if (isSqlTag(calleeExpression as ts.LeftHandSideExpression, ctx.checker)) {\n const inferred = inferResourcesFromSqlCallArguments(\n unwrapped.arguments,\n ctx,\n );\n if (inferred.length === 1) {\n return inferred[0];\n }\n }\n }\n\n if (ts.isIdentifier(unwrapped)) {\n return resolveResourceFromSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx,\n bindings,\n );\n }\n\n if (ts.isPropertyAccessExpression(unwrapped)) {\n const base = resolveResourceFromExpression(\n unwrapped.expression,\n ctx,\n bindings,\n );\n if (\n base?.kind === \"MaterializedView\" &&\n unwrapped.name.text === \"targetTable\"\n ) {\n if (base.targetTableId) {\n return { kind: \"Table\", id: base.targetTableId };\n }\n }\n if (base?.kind === \"IngestPipeline\") {\n if (unwrapped.name.text === \"stream\" && base.streamId) {\n return { kind: \"Topic\", id: base.streamId };\n }\n if (unwrapped.name.text === \"table\" && base.tableId) {\n return { kind: \"Table\", id: base.tableId };\n }\n }\n if (base?.kind === \"Table\" && unwrapped.name.text === \"columns\") {\n return base;\n }\n if (base?.kind === \"Table\" && isColumnsProjection(unwrapped.expression)) {\n return base;\n }\n\n return resolveResourceFromSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx,\n bindings,\n );\n }\n\n if (ts.isElementAccessExpression(unwrapped)) {\n const base = resolveResourceFromExpression(\n unwrapped.expression,\n ctx,\n bindings,\n );\n if (base) {\n return base;\n }\n return resolveResourceFromSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx,\n bindings,\n );\n }\n\n return undefined;\n}\n\nfunction toSignature(\n ref: ResourceRef,\n): InfrastructureSignatureJson | undefined {\n switch (ref.kind) {\n case \"Table\":\n return { kind: \"Table\", id: ref.id };\n case \"Topic\":\n return { kind: \"Topic\", id: ref.id };\n case \"View\":\n return { kind: \"View\", id: ref.id };\n case \"SqlResource\":\n return { kind: \"SqlResource\", id: ref.id };\n case \"MaterializedView\":\n if (ref.targetTableId) {\n return { kind: \"Table\", id: ref.targetTableId };\n }\n return { kind: \"MaterializedView\", id: ref.id };\n case \"IngestPipeline\":\n // Intentional: IngestPipeline is not a standalone infra node. It must be\n // decomposed through `.stream` or `.table` property access first.\n return undefined;\n default:\n return undefined;\n }\n}\n\nfunction getFunctionLikeDeclarations(\n symbol: ts.Symbol | undefined,\n ctx: AnalysisContext,\n): ts.FunctionLikeDeclaration[] {\n const resolvedSymbol = resolveAliasedSymbol(symbol, ctx.checker);\n if (!resolvedSymbol) {\n return [];\n }\n\n const cached = ctx.functionCache.get(resolvedSymbol);\n if (cached) {\n return cached;\n }\n\n const declarations: ts.FunctionLikeDeclaration[] = [];\n for (const declaration of resolvedSymbol.declarations ?? []) {\n if (\n ts.isFunctionDeclaration(declaration) ||\n ts.isMethodDeclaration(declaration) ||\n ts.isGetAccessorDeclaration(declaration) ||\n ts.isSetAccessorDeclaration(declaration)\n ) {\n declarations.push(declaration);\n continue;\n }\n if (ts.isVariableDeclaration(declaration) && declaration.initializer) {\n const initializer = unwrapExpression(declaration.initializer);\n if (\n ts.isArrowFunction(initializer) ||\n ts.isFunctionExpression(initializer)\n ) {\n declarations.push(initializer);\n continue;\n }\n if (\n ts.isIdentifier(initializer) ||\n ts.isPropertyAccessExpression(initializer)\n ) {\n declarations.push(\n ...getFunctionLikeDeclarations(\n ctx.checker.getSymbolAtLocation(initializer),\n ctx,\n ),\n );\n }\n }\n if (ts.isPropertyAssignment(declaration)) {\n const initializer = unwrapExpression(declaration.initializer);\n if (\n ts.isArrowFunction(initializer) ||\n ts.isFunctionExpression(initializer)\n ) {\n declarations.push(initializer);\n }\n }\n }\n\n ctx.functionCache.set(resolvedSymbol, declarations);\n return declarations;\n}\n\nfunction isSqlTag(\n tag: ts.LeftHandSideExpression,\n checker: ts.TypeChecker,\n): boolean {\n const unwrapped = unwrapExpression(tag);\n if (ts.isIdentifier(unwrapped) && unwrapped.text === \"sql\") {\n return true;\n }\n if (\n ts.isPropertyAccessExpression(unwrapped) &&\n unwrapped.name.text === \"sql\"\n ) {\n return true;\n }\n const symbol = resolveAliasedSymbol(\n checker.getSymbolAtLocation(unwrapped),\n checker,\n );\n return symbol?.name === \"sql\";\n}\n\nfunction isUserCodeFunction(fn: ts.FunctionLikeDeclaration): boolean {\n const fileName = path\n .resolve(fn.getSourceFile().fileName)\n .replace(/\\\\/g, \"/\");\n const cwd = path.resolve(process.cwd()).replace(/\\\\/g, \"/\");\n return (\n (fileName === cwd || fileName.startsWith(`${cwd}/`)) &&\n !fileName.includes(\"/node_modules/\")\n );\n}\n\nfunction functionIdentity(fn: ts.FunctionLikeDeclaration): string {\n const source = fn.getSourceFile().fileName;\n return `${source}:${fn.pos}`;\n}\n\nfunction bindingIdentityForFunction(\n fn: ts.FunctionLikeDeclaration,\n bindings: Map<ts.Symbol, ResourceRef>,\n checker: ts.TypeChecker,\n): string {\n const parts: string[] = [];\n for (const parameter of fn.parameters) {\n const symbol = resolveAliasedSymbol(\n checker.getSymbolAtLocation(parameter.name),\n checker,\n );\n if (!symbol) {\n continue;\n }\n const bound = bindings.get(symbol);\n if (!bound) {\n continue;\n }\n if (bound.kind === \"IngestPipeline\") {\n continue;\n }\n const signature = toSignature(bound);\n if (!signature) {\n continue;\n }\n parts.push(`${symbol.name}:${signature.kind}:${signature.id}`);\n }\n parts.sort();\n return parts.join(\"|\");\n}\n\nconst API_HELPER_IDENTIFIERS = new Set([\"ApiHelpers\", \"ConsumptionHelpers\"]);\n\nfunction isApiHelperObjectExpression(\n expression: ts.Expression,\n ctx: AnalysisContext,\n visitedSymbols = new Set<ts.Symbol>(),\n): boolean {\n const unwrapped = unwrapExpression(expression);\n\n if (ts.isIdentifier(unwrapped)) {\n if (API_HELPER_IDENTIFIERS.has(unwrapped.text)) {\n return true;\n }\n\n const symbol = resolveAliasedSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx.checker,\n );\n if (!symbol || visitedSymbols.has(symbol)) {\n return false;\n }\n visitedSymbols.add(symbol);\n\n if (API_HELPER_IDENTIFIERS.has(symbol.name)) {\n return true;\n }\n\n for (const declaration of symbol.declarations ?? []) {\n if (ts.isImportSpecifier(declaration)) {\n const importedName =\n declaration.propertyName?.text ?? declaration.name.text;\n if (API_HELPER_IDENTIFIERS.has(importedName)) {\n return true;\n }\n }\n\n if (ts.isVariableDeclaration(declaration) && declaration.initializer) {\n if (\n isApiHelperObjectExpression(\n declaration.initializer,\n ctx,\n visitedSymbols,\n )\n ) {\n return true;\n }\n }\n\n if (ts.isPropertyAssignment(declaration)) {\n if (\n isApiHelperObjectExpression(\n declaration.initializer,\n ctx,\n visitedSymbols,\n )\n ) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n if (ts.isPropertyAccessExpression(unwrapped)) {\n if (API_HELPER_IDENTIFIERS.has(unwrapped.name.text)) {\n return true;\n }\n return isApiHelperObjectExpression(\n unwrapped.expression,\n ctx,\n visitedSymbols,\n );\n }\n\n if (ts.isElementAccessExpression(unwrapped)) {\n return isApiHelperObjectExpression(\n unwrapped.expression,\n ctx,\n visitedSymbols,\n );\n }\n\n return false;\n}\n\nfunction analyzeFunctionGraph(\n roots: ts.FunctionLikeDeclaration[],\n ctx: AnalysisContext,\n): DependencySignatures {\n const pulls = new Map<string, InfrastructureSignatureJson>();\n const pushes = new Map<string, InfrastructureSignatureJson>();\n const visited = new Set<string>();\n\n const addPull = (signature: InfrastructureSignatureJson | undefined) => {\n if (!signature) {\n return;\n }\n pulls.set(`${signature.kind}:${signature.id}`, signature);\n };\n\n const addPush = (signature: InfrastructureSignatureJson | undefined) => {\n if (!signature) {\n return;\n }\n pushes.set(`${signature.kind}:${signature.id}`, signature);\n };\n\n const visitFunction = (\n fn: ts.FunctionLikeDeclaration,\n bindings: Map<ts.Symbol, ResourceRef>,\n ) => {\n const key = `${functionIdentity(fn)}|${bindingIdentityForFunction(fn, bindings, ctx.checker)}`;\n if (visited.has(key)) {\n return;\n }\n visited.add(key);\n\n const visitNode = (node: ts.Node) => {\n if (\n ts.isTaggedTemplateExpression(node) &&\n isSqlTag(node.tag, ctx.checker)\n ) {\n for (const inferred of inferResourcesFromSqlTemplate(\n node.template,\n ctx,\n )) {\n addPull(toSignature(inferred));\n }\n\n const template = node.template;\n if (ts.isTemplateExpression(template)) {\n for (const span of template.templateSpans) {\n const ref = resolveResourceFromExpression(\n span.expression,\n ctx,\n bindings,\n );\n const signature = ref ? toSignature(ref) : undefined;\n addPull(signature);\n }\n }\n }\n\n if (ts.isCallExpression(node)) {\n const calleeExpression = unwrapCallTargetExpression(node.expression);\n\n if (\n isSqlTag(calleeExpression as ts.LeftHandSideExpression, ctx.checker)\n ) {\n for (const inferred of inferResourcesFromSqlCallArguments(\n node.arguments,\n ctx,\n )) {\n addPull(toSignature(inferred));\n }\n\n for (const arg of node.arguments) {\n const ref = resolveResourceFromExpression(arg, ctx, bindings);\n const signature = ref ? toSignature(ref) : undefined;\n addPull(signature);\n }\n }\n\n if (ts.isPropertyAccessExpression(calleeExpression)) {\n const methodName = calleeExpression.name.text;\n\n if (\n methodName === \"table\" &&\n isApiHelperObjectExpression(calleeExpression.expression, ctx)\n ) {\n const tableName = resolveStaticString(node.arguments?.[0], ctx);\n if (tableName) {\n addPull({\n kind: \"Table\",\n id: resolveTableId(tableName, undefined, ctx.registryIndex),\n });\n }\n }\n\n const ref = resolveResourceFromExpression(\n calleeExpression.expression,\n ctx,\n bindings,\n );\n if (ref) {\n const signature = toSignature(ref);\n if (signature) {\n if (WRITE_METHODS.has(methodName)) {\n addPush(signature);\n } else {\n // Heuristic: any non-write resource method call counts as a read.\n // This may classify utility calls like `toString`/`valueOf` as pulls,\n // but it keeps lineage inference conservative for current SDK usage.\n addPull(signature);\n }\n }\n }\n }\n\n const calleeSymbol = ctx.checker.getSymbolAtLocation(calleeExpression);\n const callees = getFunctionLikeDeclarations(calleeSymbol, ctx).filter(\n isUserCodeFunction,\n );\n for (const callee of callees) {\n const nextBindings = new Map<ts.Symbol, ResourceRef>(bindings);\n for (let i = 0; i < callee.parameters.length; i++) {\n const param = callee.parameters[i];\n if (!node.arguments || i >= node.arguments.length) {\n continue;\n }\n const paramSymbol = resolveAliasedSymbol(\n ctx.checker.getSymbolAtLocation(param.name),\n ctx.checker,\n );\n if (!paramSymbol) {\n continue;\n }\n const argRef = resolveResourceFromExpression(\n node.arguments[i],\n ctx,\n bindings,\n );\n if (argRef) {\n nextBindings.set(paramSymbol, argRef);\n }\n }\n visitFunction(callee, nextBindings);\n }\n }\n\n ts.forEachChild(node, visitNode);\n };\n\n if (fn.body) {\n visitNode(fn.body);\n }\n };\n\n for (const root of roots) {\n if (!isUserCodeFunction(root)) {\n continue;\n }\n visitFunction(root, new Map<ts.Symbol, ResourceRef>());\n }\n\n return {\n pullsDataFrom: [...pulls.values()],\n pushesDataTo: [...pushes.values()],\n };\n}\n\nfunction resolveFunctionNodesFromExpression(\n expression: ts.Expression | undefined,\n ctx: AnalysisContext,\n): ts.FunctionLikeDeclaration[] {\n if (!expression) {\n return [];\n }\n const unwrapped = unwrapExpression(expression);\n if (ts.isArrowFunction(unwrapped) || ts.isFunctionExpression(unwrapped)) {\n return [unwrapped];\n }\n if (ts.isIdentifier(unwrapped) || ts.isPropertyAccessExpression(unwrapped)) {\n return getFunctionLikeDeclarations(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx,\n );\n }\n return [];\n}\n\nfunction resolveTaskExpression(\n expression: ts.Expression | undefined,\n ctx: AnalysisContext,\n): ts.NewExpression | undefined {\n if (!expression) {\n return undefined;\n }\n\n const unwrapped = unwrapExpression(expression);\n const cacheKey = `${unwrapped.getSourceFile().fileName}:${unwrapped.pos}`;\n const cached = ctx.taskExpressionCache.get(cacheKey);\n if (cached !== undefined) {\n return cached ?? undefined;\n }\n\n ctx.taskExpressionCache.set(cacheKey, null);\n\n if (ts.isNewExpression(unwrapped)) {\n const ctor = constructorNameFromNewExpression(\n unwrapped.expression,\n ctx.checker,\n );\n if (ctor === \"Task\") {\n ctx.taskExpressionCache.set(cacheKey, unwrapped);\n return unwrapped;\n }\n }\n\n if (ts.isIdentifier(unwrapped) || ts.isPropertyAccessExpression(unwrapped)) {\n const symbol = resolveAliasedSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx.checker,\n );\n for (const declaration of symbol?.declarations ?? []) {\n if (ts.isVariableDeclaration(declaration) && declaration.initializer) {\n const resolved = resolveTaskExpression(declaration.initializer, ctx);\n if (resolved) {\n ctx.taskExpressionCache.set(cacheKey, resolved);\n return resolved;\n }\n }\n }\n }\n\n return undefined;\n}\n\nfunction resolveArrayLiteralExpression(\n expression: ts.Expression | undefined,\n ctx: AnalysisContext,\n visitedSymbols = new Set<ts.Symbol>(),\n): ts.ArrayLiteralExpression | undefined {\n if (!expression) {\n return undefined;\n }\n\n const unwrapped = unwrapExpression(expression);\n if (ts.isArrayLiteralExpression(unwrapped)) {\n return unwrapped;\n }\n\n if (ts.isIdentifier(unwrapped) || ts.isPropertyAccessExpression(unwrapped)) {\n const symbol = resolveAliasedSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx.checker,\n );\n if (!symbol || visitedSymbols.has(symbol)) {\n return undefined;\n }\n visitedSymbols.add(symbol);\n\n for (const declaration of symbol.declarations ?? []) {\n const initializer = resolveSymbolInitializerExpression(declaration);\n if (!initializer) {\n continue;\n }\n const resolved = resolveArrayLiteralExpression(\n initializer,\n ctx,\n visitedSymbols,\n );\n if (resolved) {\n return resolved;\n }\n }\n }\n\n return undefined;\n}\n\nfunction collectTaskFunctionsFromOnCompleteElement(\n element: ts.Expression,\n ctx: AnalysisContext,\n visitedTasks: Set<string>,\n): ts.FunctionLikeDeclaration[] {\n if (ts.isSpreadElement(element)) {\n const spreadArray = resolveArrayLiteralExpression(element.expression, ctx);\n if (spreadArray) {\n return spreadArray.elements.flatMap((nestedElement) =>\n collectTaskFunctionsFromOnCompleteElement(\n nestedElement,\n ctx,\n visitedTasks,\n ),\n );\n }\n\n return collectTaskFunctions(element.expression, ctx, visitedTasks);\n }\n\n return collectTaskFunctions(element, ctx, visitedTasks);\n}\n\nfunction collectTaskFunctions(\n taskExpression: ts.Expression | undefined,\n ctx: AnalysisContext,\n visitedTasks: Set<string>,\n): ts.FunctionLikeDeclaration[] {\n const task = resolveTaskExpression(taskExpression, ctx);\n if (!task) {\n return [];\n }\n\n const taskKey = `${task.getSourceFile().fileName}:${task.pos}`;\n if (visitedTasks.has(taskKey)) {\n return [];\n }\n visitedTasks.add(taskKey);\n\n const configExpression = task.arguments?.[1];\n const configObject = resolveObjectLiteralExpression(configExpression, ctx);\n if (!configObject) {\n return [];\n }\n\n const functions: ts.FunctionLikeDeclaration[] = [];\n functions.push(\n ...resolveFunctionNodesFromExpression(\n getObjectPropertyExpression(configObject, \"run\"),\n ctx,\n ),\n );\n functions.push(\n ...resolveFunctionNodesFromExpression(\n getObjectPropertyExpression(configObject, \"onCancel\"),\n ctx,\n ),\n );\n\n const onComplete = getObjectPropertyExpression(configObject, \"onComplete\");\n if (onComplete) {\n const arrayLiteral = unwrapExpression(onComplete);\n if (ts.isArrayLiteralExpression(arrayLiteral)) {\n for (const element of arrayLiteral.elements) {\n functions.push(\n ...collectTaskFunctionsFromOnCompleteElement(\n element,\n ctx,\n visitedTasks,\n ),\n );\n }\n }\n }\n\n return functions;\n}\n\nfunction collectAnalysisFiles(registry: RegistryLike): string[] {\n const files = new Set<string>();\n let requiresFallbackScan = false;\n\n const uniqueApis = new Set<any>();\n for (const api of registry.apis.values()) {\n uniqueApis.add(api);\n }\n for (const api of uniqueApis) {\n const sourceFile = api?.metadata?.source?.file;\n if (\n typeof sourceFile === \"string\" &&\n fs.existsSync(sourceFile) &&\n isSourceFilePath(sourceFile)\n ) {\n files.add(path.resolve(sourceFile));\n } else {\n requiresFallbackScan = true;\n }\n }\n\n registry.workflows.forEach((workflow: any) => {\n const sourceFile = workflow?.sourceFile;\n if (\n typeof sourceFile === \"string\" &&\n fs.existsSync(sourceFile) &&\n isSourceFilePath(sourceFile)\n ) {\n files.add(path.resolve(sourceFile));\n } else {\n requiresFallbackScan = true;\n }\n });\n\n registry.webApps.forEach((webApp: any) => {\n const sourceFile = webApp?.sourceFile;\n if (\n typeof sourceFile === \"string\" &&\n fs.existsSync(sourceFile) &&\n isSourceFilePath(sourceFile)\n ) {\n files.add(path.resolve(sourceFile));\n } else {\n requiresFallbackScan = true;\n }\n });\n\n if (files.size === 0 || requiresFallbackScan) {\n const appDir = path.resolve(process.cwd(), getSourceDir());\n for (const file of findSourceFiles(appDir, (directory, error) => {\n compilerLog(`Warning: Could not read directory ${directory}: ${error}`);\n })) {\n files.add(file);\n }\n }\n\n return [...files];\n}\n\nfunction loadCompilerOptions(rootNames: string[]): {\n rootNames: string[];\n options: ts.CompilerOptions;\n} {\n const fallback: ts.CompilerOptions = {\n allowJs: true,\n target: ts.ScriptTarget.ES2020,\n module: ts.ModuleKind.NodeNext,\n moduleResolution: ts.ModuleResolutionKind.NodeNext,\n jsx: ts.JsxEmit.Preserve,\n skipLibCheck: true,\n };\n\n const configPath = ts.findConfigFile(\n process.cwd(),\n ts.sys.fileExists,\n \"tsconfig.json\",\n );\n if (!configPath) {\n return { rootNames, options: fallback };\n }\n\n const configFile = ts.readConfigFile(configPath, ts.sys.readFile);\n if (configFile.error) {\n return { rootNames, options: fallback };\n }\n\n const parsed = ts.parseJsonConfigFileContent(\n configFile.config,\n ts.sys,\n path.dirname(configPath),\n );\n const normalizedRoots = [\n ...new Set(rootNames.map((file) => path.resolve(file))),\n ];\n\n return {\n rootNames: normalizedRoots,\n options: { ...fallback, ...parsed.options },\n };\n}\n\nfunction createNameResolutionContext(checker: ts.TypeChecker): AnalysisContext {\n return {\n checker,\n registryIndex: {\n tableIdsByName: new Map(),\n topicIdsByName: new Map(),\n tableIds: new Set(),\n topicIds: new Set(),\n warnedAmbiguousTableIds: new Set(),\n },\n symbolResourceCache: new Map(),\n taskExpressionCache: new Map(),\n functionCache: new Map(),\n };\n}\n\nfunction collectLineageRootEntries(\n program: ts.Program,\n checker: ts.TypeChecker,\n): {\n apiEntries: Map<string, ts.Expression>;\n workflowEntries: Map<string, ts.Expression>;\n webAppEntries: Map<string, ts.Expression>;\n} {\n const apiEntries = new Map<string, ts.Expression>();\n const workflowEntries = new Map<string, ts.Expression>();\n const webAppEntries = new Map<string, ts.Expression>();\n const tempCtx = createNameResolutionContext(checker);\n\n const setIfNew = (\n entries: Map<string, ts.Expression>,\n key: string | undefined,\n expression: ts.Expression,\n ) => {\n if (!key || entries.has(key)) {\n return;\n }\n entries.set(key, expression);\n };\n\n const visit = (node: ts.Node) => {\n if (\n ts.isNewExpression(node) &&\n node.arguments &&\n node.arguments.length >= 2\n ) {\n const ctor = constructorNameFromNewExpression(node.expression, checker);\n if (ctor === \"Api\" || ctor === \"ConsumptionApi\") {\n const name = resolveStaticString(node.arguments[0], tempCtx);\n const config = resolveObjectLiteralExpression(\n node.arguments[2],\n tempCtx,\n );\n const version =\n config ?\n resolveStaticString(\n getObjectPropertyExpression(config, \"version\"),\n tempCtx,\n )\n : undefined;\n const key = apiKey(name, version);\n setIfNew(apiEntries, key, node.arguments[1]);\n } else if (ctor === \"Workflow\") {\n const key = resolveStaticString(node.arguments[0], tempCtx);\n setIfNew(workflowEntries, key, node.arguments[1]);\n } else if (ctor === \"WebApp\") {\n const key = resolveStaticString(node.arguments[0], tempCtx);\n setIfNew(webAppEntries, key, node.arguments[1]);\n }\n }\n ts.forEachChild(node, visit);\n };\n\n for (const sourceFile of program.getSourceFiles()) {\n if (sourceFile.fileName.includes(\"/node_modules/\")) {\n continue;\n }\n visit(sourceFile);\n }\n\n return { apiEntries, workflowEntries, webAppEntries };\n}\n\nfunction apiKey(\n name: string | undefined,\n version?: string,\n): string | undefined {\n if (!name) {\n return undefined;\n }\n\n return version ? `${name}:${version}` : name;\n}\n\nfunction resolveWebAppRootFunctions(\n expression: ts.Expression | undefined,\n ctx: AnalysisContext,\n): ts.FunctionLikeDeclaration[] {\n const roots = resolveFunctionNodesFromExpression(expression, ctx);\n const objectLiteral = resolveObjectLiteralExpression(expression, ctx);\n if (!objectLiteral) {\n return roots;\n }\n\n const deduped = new Map<string, ts.FunctionLikeDeclaration>();\n for (const root of roots) {\n deduped.set(functionIdentity(root), root);\n }\n\n for (const propertyName of [\"handle\", \"callback\", \"routing\"]) {\n const propertyExpression = getObjectPropertyExpression(\n objectLiteral,\n propertyName,\n );\n const propertyFunctions = resolveFunctionNodesFromExpression(\n propertyExpression,\n ctx,\n );\n for (const fn of propertyFunctions) {\n deduped.set(functionIdentity(fn), fn);\n }\n }\n\n return [...deduped.values()];\n}\n\nexport function analyzeRegistryLineage(\n registry: RegistryLike,\n): DependencyAnalysisResult {\n if (\n registry.apis.size === 0 &&\n registry.workflows.size === 0 &&\n registry.webApps.size === 0\n ) {\n return createEmptyResult();\n }\n\n try {\n const files = collectAnalysisFiles(registry);\n if (files.length === 0) {\n return createEmptyResult();\n }\n\n const { rootNames, options } = loadCompilerOptions(files);\n const program = ts.createProgram({\n rootNames,\n options,\n });\n const checker = program.getTypeChecker();\n\n const ctx: AnalysisContext = {\n checker,\n registryIndex: buildRegistryIndex(registry),\n symbolResourceCache: new Map<ts.Symbol, ResourceRef | null>(),\n taskExpressionCache: new Map<string, ts.NewExpression | null>(),\n functionCache: new Map<ts.Symbol, ts.FunctionLikeDeclaration[]>(),\n };\n\n const { apiEntries, workflowEntries, webAppEntries } =\n collectLineageRootEntries(program, checker);\n\n const apiByKey = new Map<string, DependencySignatures>();\n const seenApiKeys = new Set<string>();\n registry.apis.forEach((api: any) => {\n const key = apiKey(api?.name, api?.config?.version);\n if (!key || seenApiKeys.has(key)) {\n return;\n }\n seenApiKeys.add(key);\n\n const handlerExpression = apiEntries.get(key);\n if (!handlerExpression) {\n apiByKey.set(key, { pullsDataFrom: [], pushesDataTo: [] });\n return;\n }\n\n const roots = resolveFunctionNodesFromExpression(handlerExpression, ctx);\n apiByKey.set(key, analyzeFunctionGraph(roots, ctx));\n });\n\n const workflowByName = new Map<string, DependencySignatures>();\n registry.workflows.forEach((workflow: any, workflowName: string) => {\n const configExpression = workflowEntries.get(workflowName);\n if (!configExpression) {\n workflowByName.set(workflowName, {\n pullsDataFrom: [],\n pushesDataTo: [],\n });\n return;\n }\n const configObject = resolveObjectLiteralExpression(\n configExpression,\n ctx,\n );\n const startingTaskExpression =\n configObject ?\n getObjectPropertyExpression(configObject, \"startingTask\")\n : undefined;\n const roots = collectTaskFunctions(\n startingTaskExpression,\n ctx,\n new Set<string>(),\n );\n workflowByName.set(workflowName, analyzeFunctionGraph(roots, ctx));\n });\n\n const webAppByName = new Map<string, DependencySignatures>();\n registry.webApps.forEach((webApp: any, webAppName: string) => {\n const handlerExpression = webAppEntries.get(webAppName);\n if (!handlerExpression) {\n webAppByName.set(webAppName, { pullsDataFrom: [], pushesDataTo: [] });\n return;\n }\n\n const roots = resolveWebAppRootFunctions(handlerExpression, ctx);\n webAppByName.set(webAppName, analyzeFunctionGraph(roots, ctx));\n });\n\n return { apiByKey, workflowByName, webAppByName };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n compilerLog(\n `Warning: lineage analysis failed; returning empty lineage results. ${message}`,\n );\n return createEmptyResult();\n }\n}\n","/**\n * @module internal\n * Internal implementation details for the Moose v2 data model (dmv2).\n *\n * This module manages the registration of user-defined dmv2 resources (Tables, Streams, APIs, etc.)\n * and provides functions to serialize these resources into a JSON format (`InfrastructureMap`)\n * expected by the Moose infrastructure management system. It also includes helper functions\n * to retrieve registered handler functions (for streams and APIs) and the base class\n * (`TypedBase`) used by dmv2 resource classes.\n *\n * @internal This module is intended for internal use by the Moose library and compiler plugin.\n * Its API might change without notice.\n */\nimport process from \"process\";\nimport * as path from \"path\";\nimport { Api, IngestApi, SqlResource, Task, Workflow } from \"./index\";\nimport type { IJsonSchemaCollection } from \"typia\";\nimport { Column } from \"../dataModels/dataModelTypes\";\nimport { ClickHouseEngines, ApiUtil } from \"../index\";\nimport {\n OlapTable,\n OlapConfig,\n ReplacingMergeTreeConfig,\n SummingMergeTreeConfig,\n ReplicatedMergeTreeConfig,\n ReplicatedReplacingMergeTreeConfig,\n ReplicatedAggregatingMergeTreeConfig,\n ReplicatedSummingMergeTreeConfig,\n ReplicatedCollapsingMergeTreeConfig,\n ReplicatedVersionedCollapsingMergeTreeConfig,\n S3QueueConfig,\n} from \"./sdk/olapTable\";\nimport type { TableProjection } from \"./sdk/olapTable\";\nimport {\n ConsumerConfig,\n KafkaSchemaConfig,\n Stream,\n TransformConfig,\n} from \"./sdk/stream\";\nimport { compilerLog } from \"../commons\";\nimport { WebApp } from \"./sdk/webApp\";\nimport { MaterializedView } from \"./sdk/materializedView\";\nimport { View } from \"./sdk/view\";\nimport {\n getSourceDir,\n getCompiledIndexPath,\n getOutDir,\n hasCompiledArtifacts,\n loadModule,\n} from \"../compiler-config\";\nimport {\n analyzeRegistryLineage,\n type DependencyAnalysisResult,\n type InfrastructureSignatureJson,\n} from \"./dependencyAnalysis\";\nimport { findSourceFiles } from \"./utils\";\n\n/**\n * Strips the file extension from a path, returning the \"stem\".\n * Handles compound extensions like .d.ts by stripping only the last extension.\n */\nfunction pathStem(filePath: string): string {\n const ext = path.extname(filePath);\n return ext ? filePath.slice(0, -ext.length) : filePath;\n}\n\n/**\n * Checks for source files that exist but weren't loaded.\n *\n * Since we now load pre-compiled JS from the outDir (e.g. .moose/compiled/app/),\n * require.cache contains compiled paths, not source paths. We compare using\n * path stems relative to each root: for source files relative to appDir, and\n * for loaded files relative to compiledAppDir. This maps e.g.\n * source: app/models.ts -> stem \"models\"\n * compiled: .moose/compiled/app/models.js -> stem \"models\"\n */\nfunction findUnloadedFiles(): string[] {\n const cwd = process.cwd();\n const sourceDir = getSourceDir();\n const appDir = path.resolve(cwd, sourceDir);\n\n // The compiled equivalent of appDir lives under outDir/sourceDir\n const compiledAppDir = path.resolve(cwd, getOutDir(), sourceDir);\n\n // Find all source files in the source directory\n const allSourceFiles = findSourceFiles(appDir, (directory, error) => {\n compilerLog(`Warning: Could not read directory ${directory}: ${error}`);\n });\n\n // Build a set of stems from require.cache entries under the compiled directory.\n // e.g. \".moose/compiled/app/models.js\" -> stem \"models\"\n const loadedStems = new Set(\n Object.keys(require.cache)\n .filter((key) => key.startsWith(compiledAppDir))\n .map((key) => pathStem(path.relative(compiledAppDir, key))),\n );\n\n // A source file is unloaded if its stem (relative to appDir) is not in loadedStems.\n // e.g. \"app/unloaded_table.ts\" -> stem \"unloaded_table\" -> not in loadedStems\n const unloadedFiles = allSourceFiles\n .filter((file) => {\n const stem = pathStem(path.relative(appDir, file));\n return !loadedStems.has(stem);\n })\n .map((file) => path.relative(cwd, file));\n\n return unloadedFiles;\n}\n\n/**\n * Client-only mode check. When true, resource registration is permissive\n * (duplicates overwrite silently instead of throwing).\n * Set via MOOSE_CLIENT_ONLY=true environment variable.\n *\n * This enables Next.js apps to import OlapTable definitions for type-safe\n * queries without the Moose runtime, avoiding \"already exists\" errors on HMR.\n *\n * @returns true if MOOSE_CLIENT_ONLY environment variable is set to \"true\"\n */\nexport const isClientOnlyMode = (): boolean =>\n process.env.MOOSE_CLIENT_ONLY === \"true\";\n\nclass MutationTrackingMap<K, V> extends Map<K, V> {\n private onMutate: (() => void) | undefined;\n\n constructor(entries?: Iterable<readonly [K, V]>, onMutate?: () => void) {\n super(entries);\n this.onMutate = onMutate;\n }\n\n setMutationListener(onMutate: () => void): void {\n this.onMutate = onMutate;\n }\n\n override set(key: K, value: V): this {\n super.set(key, value);\n this.onMutate?.();\n return this;\n }\n\n override delete(key: K): boolean {\n const deleted = super.delete(key);\n if (deleted) {\n this.onMutate?.();\n }\n return deleted;\n }\n\n override clear(): void {\n if (this.size === 0) {\n return;\n }\n super.clear();\n this.onMutate?.();\n }\n}\n\ntype MooseInternalRegistry = {\n tables: Map<string, OlapTable<any>>;\n streams: Map<string, Stream<any>>;\n ingestApis: Map<string, IngestApi<any>>;\n apis: Map<string, Api<any>>;\n sqlResources: Map<string, SqlResource>;\n workflows: Map<string, Workflow>;\n webApps: Map<string, WebApp>;\n materializedViews: Map<string, MaterializedView<any>>;\n views: Map<string, View>;\n};\n\nlet registryMutationVersion = 0;\nlet lineageCache:\n | {\n registry: MooseInternalRegistry;\n version: number;\n result: DependencyAnalysisResult;\n }\n | undefined;\n\nconst markRegistryMutated = () => {\n registryMutationVersion += 1;\n lineageCache = undefined;\n};\n\nfunction toTrackingMap<V>(\n map: Map<string, V> | undefined,\n): MutationTrackingMap<string, V> {\n if (map instanceof MutationTrackingMap) {\n map.setMutationListener(markRegistryMutated);\n return map;\n }\n return new MutationTrackingMap<string, V>(\n map?.entries(),\n markRegistryMutated,\n );\n}\n\nfunction createRegistryFrom(\n existing?: Partial<MooseInternalRegistry>,\n): MooseInternalRegistry {\n return {\n tables: toTrackingMap(existing?.tables),\n streams: toTrackingMap(existing?.streams),\n ingestApis: toTrackingMap(existing?.ingestApis),\n apis: toTrackingMap(existing?.apis),\n sqlResources: toTrackingMap(existing?.sqlResources),\n workflows: toTrackingMap(existing?.workflows),\n webApps: toTrackingMap(existing?.webApps),\n materializedViews: toTrackingMap(existing?.materializedViews),\n views: toTrackingMap(existing?.views),\n };\n}\n\n/**\n * Internal registry holding all defined Moose dmv2 resources.\n * Populated by the constructors of OlapTable, Stream, IngestApi, etc.\n * Accessed via `getMooseInternal()`.\n */\nconst moose_internal: MooseInternalRegistry = {\n tables: new MutationTrackingMap<string, OlapTable<any>>(\n undefined,\n markRegistryMutated,\n ),\n streams: new MutationTrackingMap<string, Stream<any>>(\n undefined,\n markRegistryMutated,\n ),\n ingestApis: new MutationTrackingMap<string, IngestApi<any>>(\n undefined,\n markRegistryMutated,\n ),\n apis: new MutationTrackingMap<string, Api<any>>(\n undefined,\n markRegistryMutated,\n ),\n sqlResources: new MutationTrackingMap<string, SqlResource>(\n undefined,\n markRegistryMutated,\n ),\n workflows: new MutationTrackingMap<string, Workflow>(\n undefined,\n markRegistryMutated,\n ),\n webApps: new MutationTrackingMap<string, WebApp>(\n undefined,\n markRegistryMutated,\n ),\n materializedViews: new MutationTrackingMap<string, MaterializedView<any>>(\n undefined,\n markRegistryMutated,\n ),\n views: new MutationTrackingMap<string, View>(undefined, markRegistryMutated),\n};\n\nfunction getCachedLineage(\n registry: MooseInternalRegistry,\n): DependencyAnalysisResult {\n if (\n lineageCache &&\n lineageCache.registry === registry &&\n lineageCache.version === registryMutationVersion\n ) {\n return lineageCache.result;\n }\n\n const result = analyzeRegistryLineage(registry);\n lineageCache = {\n registry,\n version: registryMutationVersion,\n result,\n };\n return result;\n}\n/**\n * Default retention period for streams if not specified (7 days in seconds).\n */\nconst defaultRetentionPeriod = 60 * 60 * 24 * 7;\n\n/**\n * Engine-specific configuration types using discriminated union pattern\n */\ninterface MergeTreeEngineConfig {\n engine: \"MergeTree\";\n}\n\ninterface ReplacingMergeTreeEngineConfig {\n engine: \"ReplacingMergeTree\";\n ver?: string;\n isDeleted?: string;\n}\n\ninterface AggregatingMergeTreeEngineConfig {\n engine: \"AggregatingMergeTree\";\n}\n\ninterface SummingMergeTreeEngineConfig {\n engine: \"SummingMergeTree\";\n columns?: string[];\n}\n\ninterface CollapsingMergeTreeEngineConfig {\n engine: \"CollapsingMergeTree\";\n sign: string;\n}\n\ninterface VersionedCollapsingMergeTreeEngineConfig {\n engine: \"VersionedCollapsingMergeTree\";\n sign: string;\n ver: string;\n}\n\ninterface ReplicatedMergeTreeEngineConfig {\n engine: \"ReplicatedMergeTree\";\n keeperPath?: string;\n replicaName?: string;\n}\n\ninterface ReplicatedReplacingMergeTreeEngineConfig {\n engine: \"ReplicatedReplacingMergeTree\";\n keeperPath?: string;\n replicaName?: string;\n ver?: string;\n isDeleted?: string;\n}\n\ninterface ReplicatedAggregatingMergeTreeEngineConfig {\n engine: \"ReplicatedAggregatingMergeTree\";\n keeperPath?: string;\n replicaName?: string;\n}\n\ninterface ReplicatedSummingMergeTreeEngineConfig {\n engine: \"ReplicatedSummingMergeTree\";\n keeperPath?: string;\n replicaName?: string;\n columns?: string[];\n}\n\ninterface ReplicatedCollapsingMergeTreeEngineConfig {\n engine: \"ReplicatedCollapsingMergeTree\";\n keeperPath?: string;\n replicaName?: string;\n sign: string;\n}\n\ninterface ReplicatedVersionedCollapsingMergeTreeEngineConfig {\n engine: \"ReplicatedVersionedCollapsingMergeTree\";\n keeperPath?: string;\n replicaName?: string;\n sign: string;\n ver: string;\n}\n\ninterface S3QueueEngineConfig {\n engine: \"S3Queue\";\n s3Path: string;\n format: string;\n awsAccessKeyId?: string;\n awsSecretAccessKey?: string;\n compression?: string;\n headers?: { [key: string]: string };\n}\n\ninterface S3EngineConfig {\n engine: \"S3\";\n path: string;\n format: string;\n awsAccessKeyId?: string;\n awsSecretAccessKey?: string;\n compression?: string;\n partitionStrategy?: string;\n partitionColumnsInDataFile?: string;\n}\n\ninterface BufferEngineConfig {\n engine: \"Buffer\";\n targetDatabase: string;\n targetTable: string;\n numLayers: number;\n minTime: number;\n maxTime: number;\n minRows: number;\n maxRows: number;\n minBytes: number;\n maxBytes: number;\n flushTime?: number;\n flushRows?: number;\n flushBytes?: number;\n}\n\ninterface DistributedEngineConfig {\n engine: \"Distributed\";\n cluster: string;\n targetDatabase: string;\n targetTable: string;\n shardingKey?: string;\n policyName?: string;\n}\n\ninterface IcebergS3EngineConfig {\n engine: \"IcebergS3\";\n path: string;\n format: string;\n awsAccessKeyId?: string;\n awsSecretAccessKey?: string;\n compression?: string;\n}\n\ninterface KafkaEngineConfig {\n engine: \"Kafka\";\n brokerList: string;\n topicList: string;\n groupName: string;\n format: string;\n}\n\ninterface MergeEngineConfig {\n engine: \"Merge\";\n sourceDatabase: string;\n tablesRegexp: string;\n}\n\n/**\n * Union type for all supported engine configurations\n */\ntype EngineConfig =\n | MergeTreeEngineConfig\n | ReplacingMergeTreeEngineConfig\n | AggregatingMergeTreeEngineConfig\n | SummingMergeTreeEngineConfig\n | CollapsingMergeTreeEngineConfig\n | VersionedCollapsingMergeTreeEngineConfig\n | ReplicatedMergeTreeEngineConfig\n | ReplicatedReplacingMergeTreeEngineConfig\n | ReplicatedAggregatingMergeTreeEngineConfig\n | ReplicatedSummingMergeTreeEngineConfig\n | ReplicatedCollapsingMergeTreeEngineConfig\n | ReplicatedVersionedCollapsingMergeTreeEngineConfig\n | S3QueueEngineConfig\n | S3EngineConfig\n | BufferEngineConfig\n | DistributedEngineConfig\n | IcebergS3EngineConfig\n | KafkaEngineConfig\n | MergeEngineConfig;\n\n/**\n * JSON representation of an OLAP table configuration.\n */\ninterface TableJson {\n /** The name of the table. */\n name: string;\n /** Array defining the table's columns and their types. */\n columns: Column[];\n /** ORDER BY clause: either array of column names or a single ClickHouse expression. */\n orderBy: string[] | string;\n /** The column name used for the PARTITION BY clause. */\n partitionBy?: string;\n /** SAMPLE BY expression for approximate query processing. */\n sampleByExpression?: string;\n /** PRIMARY KEY expression (overrides column-level primary_key flags when specified). */\n primaryKeyExpression?: string;\n /** Engine configuration with type-safe, engine-specific parameters */\n engineConfig?: EngineConfig;\n /** Optional version string for the table configuration. */\n version?: string;\n /** Optional metadata for the table (e.g., description). */\n metadata?: { description?: string };\n /** Lifecycle management setting for the table. */\n lifeCycle?: string;\n /** Optional table-level settings that can be modified with ALTER TABLE MODIFY SETTING. */\n tableSettings?: { [key: string]: string };\n /** Optional table indexes */\n indexes?: {\n name: string;\n expression: string;\n type: string;\n arguments: string[];\n granularity: number;\n }[];\n /** Optional table projections */\n projections?: TableProjection[];\n /** Optional table-level TTL expression (without leading 'TTL'). */\n ttl?: string;\n /** Optional database name for multi-database support. */\n database?: string;\n /** Optional cluster name for ON CLUSTER support. */\n cluster?: string;\n /** Optional seed filter for `moose seed clickhouse`. */\n seedFilter?: { limit?: number; where?: string };\n}\n/**\n * Represents a target destination for data flow, typically a stream.\n */\ninterface Target {\n /** The name of the target resource (e.g., stream name). */\n name: string;\n /** The kind of the target resource. */\n kind: \"stream\"; // may add `| \"table\"` in the future\n /** Optional version string of the target resource's configuration. */\n version?: string;\n /** Optional metadata for the target (e.g., description for function processes). */\n metadata?: { description?: string };\n /** Optional source file path where this transform was declared. */\n sourceFile?: string;\n}\n\n/**\n * Represents a consumer attached to a stream.\n */\ninterface Consumer {\n /** Optional version string for the consumer configuration. */\n version?: string;\n /** Optional source file path where this consumer was declared. */\n sourceFile?: string;\n}\n\n/**\n * JSON representation of a Stream/Topic configuration.\n */\ninterface StreamJson {\n /** The name of the stream/topic. */\n name: string;\n /** Array defining the message schema (columns/fields). */\n columns: Column[];\n /** Data retention period in seconds. */\n retentionPeriod: number;\n /** Number of partitions for the stream/topic. */\n partitionCount: number;\n /** Optional name of the OLAP table this stream automatically syncs to. */\n targetTable?: string;\n /** Optional version of the target OLAP table configuration. */\n targetTableVersion?: string;\n /** Optional version string for the stream configuration. */\n version?: string;\n /** List of target streams this stream transforms data into. */\n transformationTargets: Target[];\n /** Flag indicating if a multi-transform function (`_multipleTransformations`) is defined. */\n hasMultiTransform: boolean;\n /** List of consumers attached to this stream. */\n consumers: Consumer[];\n /** Optional description for the stream. */\n metadata?: { description?: string };\n /** Lifecycle management setting for the stream. */\n lifeCycle?: string;\n /** Optional Schema Registry config */\n schemaConfig?: KafkaSchemaConfig;\n}\n/**\n * JSON representation of an Ingest API configuration.\n */\ninterface IngestApiJson {\n /** The name of the Ingest API endpoint. */\n name: string;\n /** Array defining the expected input schema (columns/fields). */\n columns: Column[];\n\n /** The target stream where ingested data is written. */\n writeTo: Target;\n /** The DLQ if the data does not fit the schema. */\n deadLetterQueue?: string;\n /** Optional version string for the API configuration. */\n version?: string;\n /** Optional custom path for the ingestion endpoint. */\n path?: string;\n /** Optional description for the API. */\n metadata?: { description?: string };\n /** JSON schema */\n schema: IJsonSchemaCollection.IV3_1;\n /**\n * Whether this API allows extra fields beyond the defined columns.\n * When true, extra fields in payloads are passed through to streaming functions.\n */\n allowExtraFields?: boolean;\n}\n\n/**\n * JSON representation of an API configuration.\n */\ninterface ApiJson {\n /** The name of the API endpoint. */\n name: string;\n /** Array defining the expected query parameters schema. */\n queryParams: Column[];\n /** JSON schema definition of the API's response body. */\n responseSchema: IJsonSchemaCollection.IV3_1;\n /** Optional version string for the API configuration. */\n version?: string;\n /** Optional custom path for the API endpoint. */\n path?: string;\n /** Optional description for the API. */\n metadata?: { description?: string };\n /** Components that this API reads from. */\n pullsDataFrom: InfrastructureSignatureJson[];\n /** Components that this API writes to. */\n pushesDataTo: InfrastructureSignatureJson[];\n}\n\ninterface WorkflowJson {\n name: string;\n retries?: number;\n timeout?: string;\n schedule?: string;\n pullsDataFrom: InfrastructureSignatureJson[];\n pushesDataTo: InfrastructureSignatureJson[];\n}\n\ninterface WebAppJson {\n name: string;\n mountPath: string;\n metadata?: { description?: string };\n pullsDataFrom: InfrastructureSignatureJson[];\n pushesDataTo: InfrastructureSignatureJson[];\n}\n\ninterface SqlResourceJson {\n /** The name of the SQL resource. */\n name: string;\n /** Array of SQL DDL statements required to create the resource. */\n setup: readonly string[];\n /** Array of SQL DDL statements required to drop the resource. */\n teardown: readonly string[];\n\n /** List of infrastructure components (by signature) that this resource reads from. */\n pullsDataFrom: InfrastructureSignatureJson[];\n /** List of infrastructure components (by signature) that this resource writes to. */\n pushesDataTo: InfrastructureSignatureJson[];\n /** Optional source file path where this resource is defined. */\n sourceFile?: string;\n /** Optional source line number where this resource is defined. */\n sourceLine?: number;\n /** Optional source column number where this resource is defined. */\n sourceColumn?: number;\n}\n\n/**\n * JSON representation of a structured Materialized View.\n */\ninterface MaterializedViewJson {\n /** Name of the materialized view */\n name: string;\n /** Database where the MV is created (optional, uses default if not set) */\n database?: string;\n /** The SELECT SQL statement */\n selectSql: string;\n /** Source tables that the SELECT reads from */\n sourceTables: string[];\n /** Target table where transformed data is written */\n targetTable: string;\n /** Target table database (optional) */\n targetDatabase?: string;\n /** Optional metadata for the materialized view (e.g., description, source file) */\n metadata?: { [key: string]: any };\n /** Optional lifecycle management policy */\n lifeCycle?: string;\n}\n\n/**\n * JSON representation of a structured View.\n */\ninterface ViewJson {\n /** Name of the view */\n name: string;\n /** Database where the view is created (optional, uses default if not set) */\n database?: string;\n /** The SELECT SQL statement */\n selectSql: string;\n /** Source tables that the SELECT reads from */\n sourceTables: string[];\n /** Optional metadata for the view (e.g., description, source file) */\n metadata?: { [key: string]: any };\n}\n\n/**\n * Type guard: Check if config is S3QueueConfig\n */\nfunction isS3QueueConfig(\n config: OlapConfig<any>,\n): config is S3QueueConfig<any> {\n return \"engine\" in config && config.engine === ClickHouseEngines.S3Queue;\n}\n\n/**\n * Type guard: Check if config has a replicated engine\n * Checks if the engine value is one of the replicated engine types\n */\nfunction hasReplicatedEngine(\n config: OlapConfig<any>,\n): config is\n | ReplicatedMergeTreeConfig<any>\n | ReplicatedReplacingMergeTreeConfig<any>\n | ReplicatedAggregatingMergeTreeConfig<any>\n | ReplicatedSummingMergeTreeConfig<any>\n | ReplicatedCollapsingMergeTreeConfig<any>\n | ReplicatedVersionedCollapsingMergeTreeConfig<any> {\n if (!(\"engine\" in config)) {\n return false;\n }\n\n const engine = config.engine as ClickHouseEngines;\n // Check if engine is one of the replicated engine types\n return (\n engine === ClickHouseEngines.ReplicatedMergeTree ||\n engine === ClickHouseEngines.ReplicatedReplacingMergeTree ||\n engine === ClickHouseEngines.ReplicatedAggregatingMergeTree ||\n engine === ClickHouseEngines.ReplicatedSummingMergeTree ||\n engine === ClickHouseEngines.ReplicatedCollapsingMergeTree ||\n engine === ClickHouseEngines.ReplicatedVersionedCollapsingMergeTree\n );\n}\n\n/**\n * Extract engine value from table config, handling both legacy and new formats\n */\nfunction extractEngineValue(config: OlapConfig<any>): ClickHouseEngines {\n // Legacy config without engine property defaults to MergeTree\n if (!(\"engine\" in config)) {\n return ClickHouseEngines.MergeTree;\n }\n\n // All engines (replicated and non-replicated) have engine as direct value\n return config.engine as ClickHouseEngines;\n}\n\n/**\n * Convert engine config for basic MergeTree engines\n */\nfunction convertBasicEngineConfig(\n engine: ClickHouseEngines,\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n switch (engine) {\n case ClickHouseEngines.MergeTree:\n return { engine: \"MergeTree\" };\n\n case ClickHouseEngines.AggregatingMergeTree:\n return { engine: \"AggregatingMergeTree\" };\n\n case ClickHouseEngines.ReplacingMergeTree: {\n const replacingConfig = config as ReplacingMergeTreeConfig<any>;\n return {\n engine: \"ReplacingMergeTree\",\n ver: replacingConfig.ver,\n isDeleted: replacingConfig.isDeleted,\n };\n }\n\n case ClickHouseEngines.SummingMergeTree: {\n const summingConfig = config as SummingMergeTreeConfig<any>;\n return {\n engine: \"SummingMergeTree\",\n columns: summingConfig.columns,\n };\n }\n\n case ClickHouseEngines.CollapsingMergeTree: {\n const collapsingConfig = config as any; // CollapsingMergeTreeConfig<any>\n return {\n engine: \"CollapsingMergeTree\",\n sign: collapsingConfig.sign,\n };\n }\n\n case ClickHouseEngines.VersionedCollapsingMergeTree: {\n const versionedConfig = config as any; // VersionedCollapsingMergeTreeConfig<any>\n return {\n engine: \"VersionedCollapsingMergeTree\",\n sign: versionedConfig.sign,\n ver: versionedConfig.ver,\n };\n }\n\n default:\n return undefined;\n }\n}\n\n/**\n * Convert engine config for replicated MergeTree engines\n */\nfunction convertReplicatedEngineConfig(\n engine: ClickHouseEngines,\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n // First check if this is a replicated engine config\n if (!hasReplicatedEngine(config)) {\n return undefined;\n }\n\n switch (engine) {\n case ClickHouseEngines.ReplicatedMergeTree: {\n const replicatedConfig = config as ReplicatedMergeTreeConfig<any>;\n return {\n engine: \"ReplicatedMergeTree\",\n keeperPath: replicatedConfig.keeperPath,\n replicaName: replicatedConfig.replicaName,\n };\n }\n\n case ClickHouseEngines.ReplicatedReplacingMergeTree: {\n const replicatedConfig =\n config as ReplicatedReplacingMergeTreeConfig<any>;\n return {\n engine: \"ReplicatedReplacingMergeTree\",\n keeperPath: replicatedConfig.keeperPath,\n replicaName: replicatedConfig.replicaName,\n ver: replicatedConfig.ver,\n isDeleted: replicatedConfig.isDeleted,\n };\n }\n\n case ClickHouseEngines.ReplicatedAggregatingMergeTree: {\n const replicatedConfig =\n config as ReplicatedAggregatingMergeTreeConfig<any>;\n return {\n engine: \"ReplicatedAggregatingMergeTree\",\n keeperPath: replicatedConfig.keeperPath,\n replicaName: replicatedConfig.replicaName,\n };\n }\n\n case ClickHouseEngines.ReplicatedSummingMergeTree: {\n const replicatedConfig = config as ReplicatedSummingMergeTreeConfig<any>;\n return {\n engine: \"ReplicatedSummingMergeTree\",\n keeperPath: replicatedConfig.keeperPath,\n replicaName: replicatedConfig.replicaName,\n columns: replicatedConfig.columns,\n };\n }\n\n case ClickHouseEngines.ReplicatedCollapsingMergeTree: {\n const replicatedConfig = config as any; // ReplicatedCollapsingMergeTreeConfig<any>\n return {\n engine: \"ReplicatedCollapsingMergeTree\",\n keeperPath: replicatedConfig.keeperPath,\n replicaName: replicatedConfig.replicaName,\n sign: replicatedConfig.sign,\n };\n }\n\n case ClickHouseEngines.ReplicatedVersionedCollapsingMergeTree: {\n const replicatedConfig = config as any; // ReplicatedVersionedCollapsingMergeTreeConfig<any>\n return {\n engine: \"ReplicatedVersionedCollapsingMergeTree\",\n keeperPath: replicatedConfig.keeperPath,\n replicaName: replicatedConfig.replicaName,\n sign: replicatedConfig.sign,\n ver: replicatedConfig.ver,\n };\n }\n\n default:\n return undefined;\n }\n}\n\n/**\n * Convert S3Queue engine config\n * Uses type guard for fully type-safe property access\n */\nfunction convertS3QueueEngineConfig(\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n if (!isS3QueueConfig(config)) {\n return undefined;\n }\n\n return {\n engine: \"S3Queue\",\n s3Path: config.s3Path,\n format: config.format,\n awsAccessKeyId: config.awsAccessKeyId,\n awsSecretAccessKey: config.awsSecretAccessKey,\n compression: config.compression,\n headers: config.headers,\n };\n}\n\n/**\n * Convert S3 engine config\n */\nfunction convertS3EngineConfig(\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n if (!(\"engine\" in config) || config.engine !== ClickHouseEngines.S3) {\n return undefined;\n }\n\n return {\n engine: \"S3\",\n path: config.path,\n format: config.format,\n awsAccessKeyId: config.awsAccessKeyId,\n awsSecretAccessKey: config.awsSecretAccessKey,\n compression: config.compression,\n partitionStrategy: config.partitionStrategy,\n partitionColumnsInDataFile: config.partitionColumnsInDataFile,\n };\n}\n\n/**\n * Convert Buffer engine config\n */\nfunction convertBufferEngineConfig(\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n if (!(\"engine\" in config) || config.engine !== ClickHouseEngines.Buffer) {\n return undefined;\n }\n\n return {\n engine: \"Buffer\",\n targetDatabase: config.targetDatabase,\n targetTable: config.targetTable,\n numLayers: config.numLayers,\n minTime: config.minTime,\n maxTime: config.maxTime,\n minRows: config.minRows,\n maxRows: config.maxRows,\n minBytes: config.minBytes,\n maxBytes: config.maxBytes,\n flushTime: config.flushTime,\n flushRows: config.flushRows,\n flushBytes: config.flushBytes,\n };\n}\n\n/**\n * Convert Distributed engine config\n */\nfunction convertDistributedEngineConfig(\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n if (\n !(\"engine\" in config) ||\n config.engine !== ClickHouseEngines.Distributed\n ) {\n return undefined;\n }\n\n return {\n engine: \"Distributed\",\n cluster: config.cluster,\n targetDatabase: config.targetDatabase,\n targetTable: config.targetTable,\n shardingKey: config.shardingKey,\n policyName: config.policyName,\n };\n}\n\n/**\n * Convert IcebergS3 engine config\n */\nfunction convertIcebergS3EngineConfig(\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n if (!(\"engine\" in config) || config.engine !== ClickHouseEngines.IcebergS3) {\n return undefined;\n }\n\n return {\n engine: \"IcebergS3\",\n path: config.path,\n format: config.format,\n awsAccessKeyId: config.awsAccessKeyId,\n awsSecretAccessKey: config.awsSecretAccessKey,\n compression: config.compression,\n };\n}\n\n/**\n * Convert Kafka engine configuration\n */\nfunction convertKafkaEngineConfig(\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n if (!(\"engine\" in config) || config.engine !== ClickHouseEngines.Kafka) {\n return undefined;\n }\n\n return {\n engine: \"Kafka\",\n brokerList: config.brokerList,\n topicList: config.topicList,\n groupName: config.groupName,\n format: config.format,\n };\n}\n\n/**\n * Convert Merge engine config\n */\nfunction convertMergeEngineConfig(\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n if (!(\"engine\" in config) || config.engine !== ClickHouseEngines.Merge) {\n return undefined;\n }\n\n return {\n engine: \"Merge\",\n sourceDatabase: config.sourceDatabase,\n tablesRegexp: config.tablesRegexp,\n };\n}\n\n/**\n * Convert table configuration to engine config\n */\nfunction convertTableConfigToEngineConfig(\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n const engine = extractEngineValue(config);\n\n // Try basic engines first\n const basicConfig = convertBasicEngineConfig(engine, config);\n if (basicConfig) {\n return basicConfig;\n }\n\n // Try replicated engines\n const replicatedConfig = convertReplicatedEngineConfig(engine, config);\n if (replicatedConfig) {\n return replicatedConfig;\n }\n\n // Handle S3Queue\n if (engine === ClickHouseEngines.S3Queue) {\n return convertS3QueueEngineConfig(config);\n }\n\n // Handle S3\n if (engine === ClickHouseEngines.S3) {\n return convertS3EngineConfig(config);\n }\n\n // Handle Buffer\n if (engine === ClickHouseEngines.Buffer) {\n return convertBufferEngineConfig(config);\n }\n\n // Handle Distributed\n if (engine === ClickHouseEngines.Distributed) {\n return convertDistributedEngineConfig(config);\n }\n\n // Handle IcebergS3\n if (engine === ClickHouseEngines.IcebergS3) {\n return convertIcebergS3EngineConfig(config);\n }\n\n // Handle Kafka\n if (engine === ClickHouseEngines.Kafka) {\n return convertKafkaEngineConfig(config);\n }\n\n // Handle Merge\n if (engine === ClickHouseEngines.Merge) {\n return convertMergeEngineConfig(config);\n }\n\n return undefined;\n}\n\nexport const toInfraMap = (registry: MooseInternalRegistry) => {\n const tables: { [key: string]: TableJson } = {};\n const topics: { [key: string]: StreamJson } = {};\n const ingestApis: { [key: string]: IngestApiJson } = {};\n const apis: { [key: string]: ApiJson } = {};\n const sqlResources: { [key: string]: SqlResourceJson } = {};\n const workflows: { [key: string]: WorkflowJson } = {};\n const webApps: { [key: string]: WebAppJson } = {};\n const materializedViews: { [key: string]: MaterializedViewJson } = {};\n const views: { [key: string]: ViewJson } = {};\n const lineage = getCachedLineage(registry);\n\n registry.tables.forEach((table) => {\n const id =\n table.config.version ?\n `${table.name}_${table.config.version}`\n : table.name;\n // If the table is part of an IngestPipeline, inherit metadata if not set\n let metadata = (table as any).metadata;\n if (!metadata && table.config && (table as any).pipelineParent) {\n metadata = (table as any).pipelineParent.metadata;\n }\n // Create type-safe engine configuration\n const engineConfig: EngineConfig | undefined =\n convertTableConfigToEngineConfig(table.config);\n\n // Get table settings, applying defaults for S3Queue\n let tableSettings: { [key: string]: string } | undefined = undefined;\n\n if (table.config.settings) {\n // Convert all settings to strings, filtering out undefined values\n tableSettings = Object.entries(table.config.settings).reduce(\n (acc, [key, value]) => {\n if (value !== undefined) {\n acc[key] = String(value);\n }\n return acc;\n },\n {} as { [key: string]: string },\n );\n }\n\n // Apply default settings for S3Queue if not already specified\n if (engineConfig?.engine === \"S3Queue\") {\n if (!tableSettings) {\n tableSettings = {};\n }\n // Set default mode to 'unordered' if not specified\n if (!tableSettings.mode) {\n tableSettings.mode = \"unordered\";\n }\n }\n\n // Determine ORDER BY from config\n // Note: engines like Buffer and Distributed don't support orderBy/partitionBy/sampleBy\n const hasOrderByFields =\n \"orderByFields\" in table.config &&\n Array.isArray(table.config.orderByFields) &&\n table.config.orderByFields.length > 0;\n const hasOrderByExpression =\n \"orderByExpression\" in table.config &&\n typeof table.config.orderByExpression === \"string\" &&\n table.config.orderByExpression.length > 0;\n if (hasOrderByFields && hasOrderByExpression) {\n throw new Error(\n `Table ${table.name}: Provide either orderByFields or orderByExpression, not both.`,\n );\n }\n const orderBy: string[] | string =\n hasOrderByExpression && \"orderByExpression\" in table.config ?\n (table.config.orderByExpression ?? \"\")\n : \"orderByFields\" in table.config ? (table.config.orderByFields ?? [])\n : [];\n\n tables[id] = {\n name: table.name,\n columns: table.columnArray,\n orderBy,\n partitionBy:\n \"partitionBy\" in table.config ? table.config.partitionBy : undefined,\n sampleByExpression:\n \"sampleByExpression\" in table.config ?\n table.config.sampleByExpression\n : undefined,\n primaryKeyExpression:\n \"primaryKeyExpression\" in table.config ?\n table.config.primaryKeyExpression\n : undefined,\n engineConfig,\n version: table.config.version,\n metadata,\n lifeCycle: table.config.lifeCycle,\n // Map 'settings' to 'tableSettings' for internal use\n tableSettings:\n tableSettings && Object.keys(tableSettings).length > 0 ?\n tableSettings\n : undefined,\n indexes:\n table.config.indexes?.map((i) => ({\n ...i,\n granularity: i.granularity === undefined ? 1 : i.granularity,\n arguments: i.arguments === undefined ? [] : i.arguments,\n })) || [],\n projections:\n (\"projections\" in table.config && table.config.projections) || [],\n ttl: table.config.ttl,\n database: table.config.database,\n cluster: table.config.cluster,\n seedFilter:\n \"seedFilter\" in table.config ? table.config.seedFilter : undefined,\n };\n });\n\n registry.streams.forEach((stream) => {\n // If the stream is part of an IngestPipeline, inherit metadata if not set\n let metadata = stream.metadata;\n if (!metadata && stream.config && (stream as any).pipelineParent) {\n metadata = (stream as any).pipelineParent.metadata;\n }\n const transformationTargets: Target[] = [];\n const consumers: Consumer[] = [];\n\n stream._transformations.forEach((transforms, destinationName) => {\n transforms.forEach(([destination, _, config]) => {\n transformationTargets.push({\n kind: \"stream\",\n name: destinationName,\n version: config.version,\n metadata: config.metadata,\n sourceFile: config.sourceFile,\n });\n });\n });\n\n stream._consumers.forEach((consumer) => {\n consumers.push({\n version: consumer.config.version,\n sourceFile: consumer.config.sourceFile,\n });\n });\n\n topics[stream.name] = {\n name: stream.name,\n columns: stream.columnArray,\n targetTable: stream.config.destination?.name,\n targetTableVersion: stream.config.destination?.config.version,\n retentionPeriod: stream.config.retentionPeriod ?? defaultRetentionPeriod,\n partitionCount: stream.config.parallelism ?? 1,\n version: stream.config.version,\n transformationTargets,\n hasMultiTransform: stream._multipleTransformations === undefined,\n consumers,\n metadata,\n lifeCycle: stream.config.lifeCycle,\n schemaConfig: stream.config.schemaConfig,\n };\n });\n\n registry.ingestApis.forEach((api) => {\n // If the ingestApi is part of an IngestPipeline, inherit metadata if not set\n let metadata = api.metadata;\n if (!metadata && api.config && (api as any).pipelineParent) {\n metadata = (api as any).pipelineParent.metadata;\n }\n ingestApis[api.name] = {\n name: api.name,\n columns: api.columnArray,\n version: api.config.version,\n path: api.config.path,\n writeTo: {\n kind: \"stream\",\n name: api.config.destination.name,\n },\n deadLetterQueue: api.config.deadLetterQueue?.name,\n metadata,\n schema: api.schema,\n allowExtraFields: api.allowExtraFields,\n };\n });\n\n registry.apis.forEach((api, key) => {\n const rustKey =\n api.config.version ? `${api.name}:${api.config.version}` : api.name;\n const apiLineage = lineage.apiByKey.get(rustKey);\n apis[rustKey] = {\n name: api.name,\n queryParams: api.columnArray,\n responseSchema: api.responseSchema,\n version: api.config.version,\n path: api.config.path,\n metadata: api.metadata,\n pullsDataFrom: apiLineage?.pullsDataFrom ?? [],\n pushesDataTo: apiLineage?.pushesDataTo ?? [],\n };\n });\n\n registry.sqlResources.forEach((sqlResource) => {\n sqlResources[sqlResource.name] = {\n name: sqlResource.name,\n setup: sqlResource.setup,\n teardown: sqlResource.teardown,\n sourceFile: sqlResource.sourceFile,\n sourceLine: sqlResource.sourceLine,\n sourceColumn: sqlResource.sourceColumn,\n\n pullsDataFrom: sqlResource.pullsDataFrom.map((r) => {\n if (r.kind === \"OlapTable\") {\n const table = r as OlapTable<any>;\n const id =\n table.config.version ?\n `${table.name}_${table.config.version}`\n : table.name;\n return {\n id,\n kind: \"Table\",\n };\n } else if (r.kind === \"SqlResource\") {\n const resource = r as SqlResource;\n return {\n id: resource.name,\n kind: \"SqlResource\",\n };\n } else if (r.kind === \"View\") {\n const view = r as View;\n return {\n id: view.name,\n kind: \"View\",\n };\n } else if (r.kind === \"MaterializedView\") {\n const mv = r as MaterializedView<any>;\n return {\n id: mv.name,\n kind: \"MaterializedView\",\n };\n } else {\n throw new Error(`Unknown sql resource dependency type: ${r}`);\n }\n }),\n pushesDataTo: sqlResource.pushesDataTo.map((r) => {\n if (r.kind === \"OlapTable\") {\n const table = r as OlapTable<any>;\n const id =\n table.config.version ?\n `${table.name}_${table.config.version}`\n : table.name;\n return {\n id,\n kind: \"Table\",\n };\n } else if (r.kind === \"SqlResource\") {\n const resource = r as SqlResource;\n return {\n id: resource.name,\n kind: \"SqlResource\",\n };\n } else if (r.kind === \"View\") {\n const view = r as View;\n return {\n id: view.name,\n kind: \"View\",\n };\n } else if (r.kind === \"MaterializedView\") {\n const mv = r as MaterializedView<any>;\n return {\n id: mv.name,\n kind: \"MaterializedView\",\n };\n } else {\n throw new Error(`Unknown sql resource dependency type: ${r}`);\n }\n }),\n };\n });\n\n registry.workflows.forEach((workflow) => {\n const workflowLineage = lineage.workflowByName.get(workflow.name);\n workflows[workflow.name] = {\n name: workflow.name,\n retries: workflow.config.retries,\n timeout: workflow.config.timeout,\n schedule: workflow.config.schedule,\n pullsDataFrom: workflowLineage?.pullsDataFrom ?? [],\n pushesDataTo: workflowLineage?.pushesDataTo ?? [],\n };\n });\n\n registry.webApps.forEach((webApp) => {\n const webAppLineage = lineage.webAppByName.get(webApp.name);\n webApps[webApp.name] = {\n name: webApp.name,\n mountPath: webApp.config.mountPath || \"/\",\n metadata: webApp.config.metadata,\n pullsDataFrom: webAppLineage?.pullsDataFrom ?? [],\n pushesDataTo: webAppLineage?.pushesDataTo ?? [],\n };\n });\n\n // Serialize materialized views with structured data\n registry.materializedViews.forEach((mv) => {\n materializedViews[mv.name] = {\n name: mv.name,\n selectSql: mv.selectSql,\n sourceTables: mv.sourceTables,\n targetTable: mv.targetTable.name,\n targetDatabase: mv.targetTable.config.database,\n metadata: mv.metadata,\n lifeCycle: mv.lifeCycle,\n };\n });\n\n // Serialize views with structured data\n registry.views.forEach((view) => {\n views[view.name] = {\n name: view.name,\n selectSql: view.selectSql,\n sourceTables: view.sourceTables,\n metadata: view.metadata,\n };\n });\n\n return {\n topics,\n tables,\n ingestApis,\n apis,\n sqlResources,\n workflows,\n webApps,\n materializedViews,\n views,\n unloadedFiles: [] as string[], // Will be populated by dumpMooseInternal\n };\n};\n\n/**\n * Retrieves the global internal Moose resource registry.\n * Uses `globalThis` to ensure a single registry instance.\n *\n * @returns The internal Moose resource registry.\n */\nconst initializeMooseInternalRegistry = () => {\n const existing = (globalThis as any).moose_internal as\n | Partial<MooseInternalRegistry>\n | undefined;\n\n if (existing === undefined) {\n (globalThis as any).moose_internal = moose_internal;\n return;\n }\n\n (globalThis as any).moose_internal = createRegistryFrom(existing);\n};\n\ninitializeMooseInternalRegistry();\n\nexport const getMooseInternal = (): MooseInternalRegistry =>\n (globalThis as any).moose_internal;\n\n/**\n * Loads the user's application entry point (`app/index.ts`) to register resources,\n * then generates and prints the infrastructure map as JSON.\n *\n * This function is the main entry point used by the Moose infrastructure system\n * to discover the defined resources.\n * It prints the JSON map surrounded by specific delimiters (`___MOOSE_STUFF___start`\n * and `end___MOOSE_STUFF___`) for easy extraction by the calling process.\n */\nexport const dumpMooseInternal = async () => {\n await loadIndex();\n\n const infraMap = toInfraMap(getMooseInternal());\n\n // Check for unloaded files\n const unloadedFiles = findUnloadedFiles();\n infraMap.unloadedFiles = unloadedFiles;\n\n console.log(\n \"___MOOSE_STUFF___start\",\n JSON.stringify(infraMap),\n \"end___MOOSE_STUFF___\",\n );\n};\n\nconst loadIndex = async () => {\n // Always use pre-compiled JavaScript - no ts-node fallback.\n // Compilation is handled by moose-tspc before this runs.\n\n // Check if compiled artifacts exist\n if (!hasCompiledArtifacts()) {\n const outDir = getOutDir();\n const sourceDir = getSourceDir();\n throw new Error(\n `Compiled artifacts not found at ${outDir}/${sourceDir}/index.js. ` +\n `Run 'npx moose-tspc' to compile your TypeScript first.`,\n );\n }\n\n // Clear registry and require.cache for hot reloading\n const registry = getMooseInternal();\n registry.tables.clear();\n registry.streams.clear();\n registry.ingestApis.clear();\n registry.apis.clear();\n registry.sqlResources.clear();\n registry.workflows.clear();\n registry.webApps.clear();\n registry.materializedViews.clear();\n registry.views.clear();\n\n // Clear require cache for compiled directory to pick up changes\n const outDir = getOutDir();\n const compiledDir =\n path.isAbsolute(outDir) ? outDir : path.join(process.cwd(), outDir);\n Object.keys(require.cache).forEach((key) => {\n if (key.startsWith(compiledDir)) {\n delete require.cache[key];\n }\n });\n\n try {\n // Load pre-compiled JavaScript from the configured outDir\n const indexPath = getCompiledIndexPath();\n await loadModule(indexPath);\n } catch (error) {\n let hint: string | undefined;\n let includeDetails = true;\n const details = error instanceof Error ? error.message : String(error);\n\n // Check for typia configuration errors\n if (\n details.includes(\"no transform has been configured\") ||\n details.includes(\"NoTransformConfigurationError\")\n ) {\n hint =\n \"🔴 Typia Transformation Error\\n\\n\" +\n \"This is likely a bug in Moose. The Typia type transformer failed to process your code.\\n\\n\" +\n \"Please report this issue:\\n\" +\n \" • Moose Slack: https://join.slack.com/t/moose-community/shared_invite/zt-2fjh5n3wz-cnOmM9Xe9DYAgQrNu8xKxg\\n\" +\n \" • Include the stack trace below and the file being processed\\n\\n\";\n includeDetails = false;\n } else if (\n details.includes(\"ERR_REQUIRE_ESM\") ||\n details.includes(\"ES Module\")\n ) {\n hint =\n \"The file or its dependencies are ESM-only. Switch to packages that dual-support CJS & ESM, or upgrade to Node 22.12+. \" +\n \"If you must use Node 20, you may try Node 20.19\\n\\n\";\n }\n\n if (hint === undefined) {\n throw error;\n } else {\n const errorMsg = includeDetails ? `${hint}${details}` : hint;\n const cause = error instanceof Error ? error : undefined;\n throw new Error(errorMsg, { cause });\n }\n }\n};\n\n/**\n * Loads the user's application entry point and extracts all registered stream\n * transformation and consumer functions.\n *\n * @returns A Map where keys are unique identifiers for transformations/consumers\n * (e.g., \"sourceStream_destStream_version\", \"sourceStream_<no-target>_version\")\n * and values are tuples containing: [handler function, config, source stream columns]\n */\nexport const getStreamingFunctions = async () => {\n await loadIndex();\n\n const registry = getMooseInternal();\n const transformFunctions = new Map<\n string,\n [\n (data: unknown) => unknown,\n TransformConfig<any> | ConsumerConfig<any>,\n Column[],\n ]\n >();\n\n registry.streams.forEach((stream) => {\n stream._transformations.forEach((transforms, destinationName) => {\n transforms.forEach(([_, transform, config]) => {\n const transformFunctionKey = `${stream.name}_${destinationName}${config.version ? `_${config.version}` : \"\"}`;\n compilerLog(`getStreamingFunctions: ${transformFunctionKey}`);\n transformFunctions.set(transformFunctionKey, [\n transform,\n config,\n stream.columnArray,\n ]);\n });\n });\n\n stream._consumers.forEach((consumer) => {\n const consumerFunctionKey = `${stream.name}_<no-target>${consumer.config.version ? `_${consumer.config.version}` : \"\"}`;\n transformFunctions.set(consumerFunctionKey, [\n consumer.consumer,\n consumer.config,\n stream.columnArray,\n ]);\n });\n });\n\n return transformFunctions;\n};\n\n/**\n * Loads the user's application entry point and extracts all registered\n * API handler functions.\n *\n * @returns A Map where keys are the names of the APIs and values\n * are their corresponding handler functions.\n */\nexport const getApis = async () => {\n await loadIndex();\n const apiFunctions = new Map<\n string,\n (params: unknown, utils: ApiUtil) => unknown\n >();\n\n const registry = getMooseInternal();\n // Single pass: store full keys, track aliasing decisions\n const versionCountByName = new Map<string, number>();\n const nameToSoleVersionHandler = new Map<\n string,\n (params: unknown, utils: ApiUtil) => unknown\n >();\n\n registry.apis.forEach((api, key) => {\n const handler = api.getHandler();\n apiFunctions.set(key, handler);\n\n if (!api.config.version) {\n // Explicit unversioned takes precedence for alias\n if (!apiFunctions.has(api.name)) {\n apiFunctions.set(api.name, handler);\n }\n nameToSoleVersionHandler.delete(api.name);\n versionCountByName.delete(api.name);\n } else if (!apiFunctions.has(api.name)) {\n // Only track versioned for alias if no explicit unversioned present\n const count = (versionCountByName.get(api.name) ?? 0) + 1;\n versionCountByName.set(api.name, count);\n if (count === 1) {\n nameToSoleVersionHandler.set(api.name, handler);\n } else {\n nameToSoleVersionHandler.delete(api.name);\n }\n }\n });\n\n // Finalize aliases for names that have exactly one versioned API and no unversioned\n nameToSoleVersionHandler.forEach((handler, name) => {\n if (!apiFunctions.has(name)) {\n apiFunctions.set(name, handler);\n }\n });\n\n return apiFunctions;\n};\n\nexport const dlqSchema: IJsonSchemaCollection.IV3_1 = {\n version: \"3.1\",\n components: {\n schemas: {\n DeadLetterModel: {\n type: \"object\",\n properties: {\n originalRecord: {\n $ref: \"#/components/schemas/Recordstringany\",\n },\n errorMessage: {\n type: \"string\",\n },\n errorType: {\n type: \"string\",\n },\n failedAt: {\n type: \"string\",\n format: \"date-time\",\n },\n source: {\n oneOf: [\n {\n const: \"api\",\n },\n {\n const: \"transform\",\n },\n {\n const: \"table\",\n },\n ],\n },\n },\n required: [\n \"originalRecord\",\n \"errorMessage\",\n \"errorType\",\n \"failedAt\",\n \"source\",\n ],\n },\n Recordstringany: {\n type: \"object\",\n properties: {},\n required: [],\n description: \"Construct a type with a set of properties K of type T\",\n additionalProperties: {},\n },\n },\n },\n schemas: [\n {\n $ref: \"#/components/schemas/DeadLetterModel\",\n },\n ],\n};\n\nexport const dlqColumns: Column[] = [\n {\n name: \"originalRecord\",\n data_type: \"Json\",\n primary_key: false,\n required: true,\n unique: false,\n default: null,\n annotations: [],\n ttl: null,\n codec: null,\n materialized: null,\n comment: null,\n },\n {\n name: \"errorMessage\",\n data_type: \"String\",\n primary_key: false,\n required: true,\n unique: false,\n default: null,\n annotations: [],\n ttl: null,\n codec: null,\n materialized: null,\n comment: null,\n },\n {\n name: \"errorType\",\n data_type: \"String\",\n primary_key: false,\n required: true,\n unique: false,\n default: null,\n annotations: [],\n ttl: null,\n codec: null,\n materialized: null,\n comment: null,\n },\n {\n name: \"failedAt\",\n data_type: \"DateTime\",\n primary_key: false,\n required: true,\n unique: false,\n default: null,\n annotations: [],\n ttl: null,\n codec: null,\n materialized: null,\n comment: null,\n },\n {\n name: \"source\",\n data_type: \"String\",\n primary_key: false,\n required: true,\n unique: false,\n default: null,\n annotations: [],\n ttl: null,\n codec: null,\n materialized: null,\n comment: null,\n },\n];\n\nexport const getWorkflows = async () => {\n await loadIndex();\n\n const registry = getMooseInternal();\n return registry.workflows;\n};\n\nfunction findTaskInTree(\n task: Task<any, any>,\n targetName: string,\n): Task<any, any> | undefined {\n if (task.name === targetName) {\n return task;\n }\n\n if (task.config.onComplete?.length) {\n for (const childTask of task.config.onComplete) {\n const found = findTaskInTree(childTask, targetName);\n if (found) {\n return found;\n }\n }\n }\n\n return undefined;\n}\n\nexport const getTaskForWorkflow = async (\n workflowName: string,\n taskName: string,\n): Promise<Task<any, any>> => {\n const workflows = await getWorkflows();\n const workflow = workflows.get(workflowName);\n if (!workflow) {\n throw new Error(`Workflow ${workflowName} not found`);\n }\n\n const task = findTaskInTree(\n workflow.config.startingTask as Task<any, any>,\n taskName,\n );\n if (!task) {\n throw new Error(`Task ${taskName} not found in workflow ${workflowName}`);\n }\n\n return task;\n};\n\nexport const getWebApps = async () => {\n await loadIndex();\n return getMooseInternal().webApps;\n};\n","#!/usr/bin/env node\n\n// This file is used to run the proper runners for moose based on the\n// arguments passed to the file.\n// It loads pre-compiled JavaScript - no ts-node required.\n\nimport { readFileSync } from \"fs\";\nimport { join } from \"path\";\n\nimport { dumpMooseInternal } from \"./dmv2/internal\";\nimport { runApis } from \"./consumption-apis/runner\";\nimport { runStreamingFunctions } from \"./streaming-functions/runner\";\nimport { runExportSerializer } from \"./moduleExportSerializer\";\nimport { runScripts } from \"./scripts/runner\";\n\nimport { Command } from \"commander\";\n\nimport type { StreamingFunctionArgs } from \"./streaming-functions/runner\";\n\nconst packageJson = JSON.parse(\n readFileSync(join(__dirname, \"..\", \"package.json\"), \"utf-8\"),\n);\n\nconst program = new Command();\n\nprogram\n .name(\"moose-runner\")\n .description(\"Moose runner for various operations\")\n .version(packageJson.version);\n\nprogram\n .command(\"print-version\")\n .description(\"Print the installed moose-lib version\")\n .action(() => {\n process.stdout.write(packageJson.version);\n });\n\nprogram\n .command(\"dmv2-serializer\")\n .description(\"Load DMv2 index\")\n .action(async () => {\n await dumpMooseInternal();\n });\n\nprogram\n .command(\"export-serializer\")\n .description(\"Run export serializer\")\n .argument(\"<target-model>\", \"Target model to serialize\")\n .action(async (targetModel) => {\n await runExportSerializer(targetModel);\n });\n\nprogram\n .command(\"consumption-apis\")\n .description(\"Run consumption APIs\")\n .argument(\"<clickhouse-db>\", \"Clickhouse database name\")\n .argument(\"<clickhouse-host>\", \"Clickhouse host\")\n .argument(\"<clickhouse-port>\", \"Clickhouse port\")\n .argument(\"<clickhouse-username>\", \"Clickhouse username\")\n .argument(\"<clickhouse-password>\", \"Clickhouse password\")\n .option(\"--clickhouse-use-ssl\", \"Use SSL for Clickhouse connection\", false)\n .option(\"--jwt-secret <secret>\", \"JWT public key for verification\")\n .option(\"--jwt-issuer <issuer>\", \"Expected JWT issuer\")\n .option(\"--jwt-audience <audience>\", \"Expected JWT audience\")\n .option(\n \"--enforce-auth\",\n \"Enforce authentication on all consumption APIs\",\n false,\n )\n .option(\"--temporal-url <url>\", \"Temporal server URL\")\n .option(\"--temporal-namespace <namespace>\", \"Temporal namespace\")\n .option(\"--client-cert <path>\", \"Path to client certificate\")\n .option(\"--client-key <path>\", \"Path to client key\")\n .option(\"--api-key <key>\", \"API key for authentication\")\n .option(\"--proxy-port <port>\", \"Port to run the proxy server on\", parseInt)\n .option(\n \"--worker-count <count>\",\n \"Number of worker processes for the consumption API cluster\",\n parseInt,\n )\n .action(\n (\n clickhouseDb,\n clickhouseHost,\n clickhousePort,\n clickhouseUsername,\n clickhousePassword,\n options,\n ) => {\n runApis({\n clickhouseConfig: {\n database: clickhouseDb,\n host: clickhouseHost,\n port: clickhousePort,\n username: clickhouseUsername,\n password: clickhousePassword,\n useSSL: options.clickhouseUseSsl,\n },\n jwtConfig: {\n secret: options.jwtSecret,\n issuer: options.jwtIssuer,\n audience: options.jwtAudience,\n },\n temporalConfig:\n options.temporalUrl ?\n {\n url: options.temporalUrl,\n namespace: options.temporalNamespace,\n clientCert: options.clientCert,\n clientKey: options.clientKey,\n apiKey: options.apiKey,\n }\n : undefined,\n enforceAuth: options.enforceAuth,\n proxyPort: options.proxyPort,\n workerCount: options.workerCount,\n });\n },\n );\n\nprogram\n .command(\"streaming-functions\")\n .description(\"Run streaming functions\")\n .argument(\"<source-topic>\", \"Source topic configuration as JSON\")\n .argument(\"<function-file-path>\", \"Path to the function file\")\n .argument(\n \"<broker>\",\n \"Kafka broker address(es) - comma-separated for multiple brokers (e.g., 'broker1:9092, broker2:9092'). Whitespace around commas is automatically trimmed.\",\n )\n .argument(\"<max-subscriber-count>\", \"Maximum number of subscribers\")\n .option(\"--target-topic <target-topic>\", \"Target topic configuration as JSON\")\n .option(\"--sasl-username <username>\", \"SASL username\")\n .option(\"--sasl-password <password>\", \"SASL password\")\n .option(\"--sasl-mechanism <mechanism>\", \"SASL mechanism\")\n .option(\"--security-protocol <protocol>\", \"Security protocol\")\n .option(\"--log-payloads\", \"Log payloads for debugging\", false)\n .action(\n (sourceTopic, functionFilePath, broker, maxSubscriberCount, options) => {\n const config: StreamingFunctionArgs = {\n sourceTopic: JSON.parse(sourceTopic),\n targetTopic:\n options.targetTopic ? JSON.parse(options.targetTopic) : undefined,\n functionFilePath,\n broker,\n maxSubscriberCount: parseInt(maxSubscriberCount),\n logPayloads: options.logPayloads,\n saslUsername: options.saslUsername,\n saslPassword: options.saslPassword,\n saslMechanism: options.saslMechanism,\n securityProtocol: options.securityProtocol,\n };\n runStreamingFunctions(config);\n },\n );\n\nprogram\n .command(\"scripts\")\n .description(\"Run scripts\")\n .option(\"--temporal-url <url>\", \"Temporal server URL\")\n .option(\"--temporal-namespace <namespace>\", \"Temporal namespace\")\n .option(\"--client-cert <path>\", \"Path to client certificate\")\n .option(\"--client-key <path>\", \"Path to client key\")\n .option(\"--api-key <key>\", \"API key for authentication\")\n .action((options) => {\n runScripts({\n temporalConfig:\n options.temporalUrl ?\n {\n url: options.temporalUrl,\n namespace: options.temporalNamespace,\n clientCert: options.clientCert,\n clientKey: options.clientKey,\n apiKey: options.apiKey,\n }\n : undefined,\n });\n });\n\nprogram.parse();\n","import http from \"http\";\nimport * as path from \"path\";\nimport { getClickhouseClient } from \"../commons\";\nimport { MooseClient, QueryClient, getTemporalClient } from \"./helpers\";\nimport * as jose from \"jose\";\nimport { ClickHouseClient } from \"@clickhouse/client\";\nimport { Cluster } from \"../cluster-utils\";\nimport { sql } from \"../sqlHelpers\";\nimport { Client as TemporalClient } from \"@temporalio/client\";\nimport { getApis, getWebApps } from \"../dmv2/internal\";\nimport { getSourceDir, getOutDir } from \"../compiler-config\";\nimport { setupStructuredConsole } from \"../utils/structured-logging\";\n\ninterface ClickhouseConfig {\n database: string;\n host: string;\n port: string;\n username: string;\n password: string;\n useSSL: boolean;\n}\n\ninterface JwtConfig {\n secret?: string;\n issuer: string;\n audience: string;\n}\n\ninterface TemporalConfig {\n url: string;\n namespace: string;\n clientCert: string;\n clientKey: string;\n apiKey: string;\n}\n\ninterface ApisConfig {\n clickhouseConfig: ClickhouseConfig;\n jwtConfig?: JwtConfig;\n temporalConfig?: TemporalConfig;\n enforceAuth: boolean;\n proxyPort?: number;\n workerCount?: number;\n}\n\n// Convert our config to Clickhouse client config\nconst toClientConfig = (config: ClickhouseConfig) => ({\n ...config,\n useSSL: config.useSSL ? \"true\" : \"false\",\n});\n\nconst createPath = (apisDir: string, path: string) => {\n // Always use compiled JavaScript\n return `${apisDir}${path}.js`;\n};\n\nconst httpLogger = (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n startMs: number,\n apiName?: string,\n) => {\n const logFn = () =>\n console.log(\n `${req.method} ${req.url} ${res.statusCode} ${Date.now() - startMs}ms`,\n );\n\n if (apiName) {\n apiContextStorage.run({ apiName }, logFn);\n } else {\n logFn();\n }\n};\n\n// Cache stores both the module and the matched API name for structured logging\ninterface CachedApiEntry {\n module: any;\n apiName: string;\n}\nconst modulesCache = new Map<string, CachedApiEntry>();\n\n// Set up structured console logging for API context\nconst apiContextStorage = setupStructuredConsole<{ apiName: string }>(\n (ctx) => ctx.apiName,\n \"api_name\",\n);\n\nconst apiHandler = async (\n publicKey: jose.KeyLike | undefined,\n clickhouseClient: ClickHouseClient,\n temporalClient: TemporalClient | undefined,\n enforceAuth: boolean,\n jwtConfig?: JwtConfig,\n) => {\n // Always use compiled JavaScript\n const sourceDir = getSourceDir();\n const outDir = getOutDir();\n const outRoot =\n path.isAbsolute(outDir) ? outDir : path.join(process.cwd(), outDir);\n const actualApisDir = path.join(outRoot, sourceDir, \"apis\");\n\n const apis = await getApis();\n return async (req: http.IncomingMessage, res: http.ServerResponse) => {\n const start = Date.now();\n // Track matched API name for structured logging - declared outside try\n // so it's accessible in catch block for error logging context\n let matchedApiName: string | undefined;\n\n try {\n const url = new URL(req.url || \"\", \"http://localhost\");\n const fileName = url.pathname;\n\n let jwtPayload: jose.JWTPayload | undefined;\n if (publicKey && jwtConfig) {\n const jwt = req.headers.authorization?.split(\" \")[1]; // Bearer <token>\n if (jwt) {\n try {\n const { payload } = await jose.jwtVerify(jwt, publicKey, {\n issuer: jwtConfig.issuer,\n audience: jwtConfig.audience,\n });\n jwtPayload = payload;\n } catch (error) {\n console.log(\"JWT verification failed\");\n if (enforceAuth) {\n res.writeHead(401, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Unauthorized\" }));\n httpLogger(req, res, start);\n return;\n }\n }\n } else if (enforceAuth) {\n res.writeHead(401, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Unauthorized\" }));\n httpLogger(req, res, start);\n return;\n }\n } else if (enforceAuth) {\n res.writeHead(401, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Unauthorized\" }));\n httpLogger(req, res, start);\n return;\n }\n\n const pathName = createPath(actualApisDir, fileName);\n const paramsObject = Array.from(url.searchParams.entries()).reduce(\n (obj: { [key: string]: string[] | string }, [key, value]) => {\n const existingValue = obj[key];\n if (existingValue) {\n if (Array.isArray(existingValue)) {\n existingValue.push(value);\n } else {\n obj[key] = [existingValue, value];\n }\n } else {\n obj[key] = value;\n }\n return obj;\n },\n {},\n );\n\n // Include version query param in cache key to avoid collisions\n // Path-based versions (/myapi/1) are already in pathName, but query versions (?version=1) are not\n const versionParam = url.searchParams.get(\"version\");\n const cacheKey = versionParam ? `${pathName}:${versionParam}` : pathName;\n\n let userFuncModule: any;\n\n const cachedEntry = modulesCache.get(cacheKey);\n if (cachedEntry !== undefined) {\n userFuncModule = cachedEntry.module;\n matchedApiName = cachedEntry.apiName;\n } else {\n let lookupName = fileName.replace(/^\\/+|\\/+$/g, \"\");\n let version: string | null = null;\n\n // First, try to find the API by the full path (for custom paths)\n userFuncModule = apis.get(lookupName);\n if (userFuncModule) {\n // For custom path lookup, the key IS the API name\n matchedApiName = lookupName;\n }\n\n if (!userFuncModule) {\n // Fall back to the old name:version parsing\n version = url.searchParams.get(\"version\");\n\n // Check if version is in the path (e.g., /bar/1)\n if (!version && lookupName.includes(\"/\")) {\n const pathParts = lookupName.split(\"/\");\n if (pathParts.length >= 2) {\n // Treat as name/version since full path lookup already failed at line 184\n lookupName = pathParts[0];\n version = pathParts.slice(1).join(\"/\");\n }\n }\n\n // Only do versioned lookup if we still haven't found it\n if (!userFuncModule && version) {\n const versionedKey = `${lookupName}:${version}`;\n userFuncModule = apis.get(versionedKey);\n if (userFuncModule) {\n // The API name is the base name without version\n matchedApiName = lookupName;\n }\n }\n\n // Try unversioned lookup if still not found\n if (!userFuncModule) {\n userFuncModule = apis.get(lookupName);\n if (userFuncModule) {\n matchedApiName = lookupName;\n }\n }\n }\n\n if (!userFuncModule || matchedApiName === undefined) {\n const availableApis = Array.from(apis.keys()).map((key) =>\n key.replace(\":\", \"/\"),\n );\n const errorMessage =\n version ?\n `API ${lookupName} with version ${version} not found. Available APIs: ${availableApis.join(\", \")}`\n : `API ${lookupName} not found. Available APIs: ${availableApis.join(\", \")}`;\n throw new Error(errorMessage);\n }\n\n // Cache both the module and API name for future requests\n modulesCache.set(cacheKey, {\n module: userFuncModule,\n apiName: matchedApiName,\n });\n apiContextStorage.run({ apiName: matchedApiName }, () => {\n console.log(`[API] | Executing API: ${matchedApiName}`);\n });\n }\n\n const queryClient = new QueryClient(clickhouseClient, fileName);\n\n // Use matched API name for structured logging context\n // This matches source_primitive.name in the infrastructure map\n // Note: matchedApiName is guaranteed to be defined here (either from cache or lookup)\n const apiName = matchedApiName!;\n\n // Use AsyncLocalStorage to set context for this API call.\n // This avoids race conditions from concurrent API requests - each async\n // execution chain has its own isolated context value\n const result = await apiContextStorage.run({ apiName }, async () => {\n return await userFuncModule(paramsObject, {\n client: new MooseClient(queryClient, temporalClient),\n sql: sql,\n jwt: jwtPayload,\n });\n });\n let body: string;\n let status: number | undefined;\n\n // TODO investigate why these prototypes are different\n if (Object.getPrototypeOf(result).constructor.name === \"ResultSet\") {\n body = JSON.stringify(await result.json());\n } else {\n if (\"body\" in result && \"status\" in result) {\n body = JSON.stringify(result.body);\n status = result.status;\n } else {\n body = JSON.stringify(result);\n }\n }\n\n if (status) {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n httpLogger(req, res, start, apiName);\n } else {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n httpLogger(req, res, start, apiName);\n }\n\n res.end(body);\n } catch (error: any) {\n // Log error with API context if we know which API was being called\n const logError = () => console.log(\"error in path \", req.url, error);\n if (matchedApiName) {\n apiContextStorage.run({ apiName: matchedApiName }, logError);\n } else {\n logError();\n }\n\n // todo: same workaround as ResultSet\n if (Object.getPrototypeOf(error).constructor.name === \"TypeGuardError\") {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: error.message }));\n httpLogger(req, res, start, matchedApiName);\n } else if (error?.name === \"BadRequestError\") {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(error.toJSON?.() ?? { error: error.message }));\n httpLogger(req, res, start, matchedApiName);\n } else if (error instanceof Error) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: error.message }));\n httpLogger(req, res, start, matchedApiName);\n } else {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end();\n httpLogger(req, res, start, matchedApiName);\n }\n }\n };\n};\n\nconst createMainRouter = async (\n publicKey: jose.KeyLike | undefined,\n clickhouseClient: ClickHouseClient,\n temporalClient: TemporalClient | undefined,\n enforceAuth: boolean,\n jwtConfig?: JwtConfig,\n) => {\n const apiRequestHandler = await apiHandler(\n publicKey,\n clickhouseClient,\n temporalClient,\n enforceAuth,\n jwtConfig,\n );\n\n const webApps = await getWebApps();\n\n const sortedWebApps = Array.from(webApps.values()).sort((a, b) => {\n const pathA = a.config.mountPath || \"/\";\n const pathB = b.config.mountPath || \"/\";\n return pathB.length - pathA.length;\n });\n\n return async (req: http.IncomingMessage, res: http.ServerResponse) => {\n const start = Date.now();\n\n const url = new URL(req.url || \"\", \"http://localhost\");\n const pathname = url.pathname;\n\n // Health check - checked before all other routes\n if (pathname === \"/_moose_internal/health\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n status: \"healthy\",\n timestamp: new Date().toISOString(),\n }),\n );\n return;\n }\n\n let jwtPayload: jose.JWTPayload | undefined;\n if (publicKey && jwtConfig) {\n const jwt = req.headers.authorization?.split(\" \")[1];\n if (jwt) {\n try {\n const { payload } = await jose.jwtVerify(jwt, publicKey, {\n issuer: jwtConfig.issuer,\n audience: jwtConfig.audience,\n });\n jwtPayload = payload;\n } catch (error) {\n console.log(\"JWT verification failed for WebApp route\");\n }\n }\n }\n\n for (const webApp of sortedWebApps) {\n const mountPath = webApp.config.mountPath || \"/\";\n const normalizedMount =\n mountPath.endsWith(\"/\") && mountPath !== \"/\" ?\n mountPath.slice(0, -1)\n : mountPath;\n\n const matches =\n pathname === normalizedMount ||\n pathname.startsWith(normalizedMount + \"/\");\n\n if (matches) {\n if (webApp.config.injectMooseUtils !== false) {\n // Import getMooseUtils dynamically to avoid circular deps\n const { getMooseUtils } = await import(\"./standalone\");\n (req as any).moose = await getMooseUtils();\n }\n\n let proxiedUrl = req.url;\n if (normalizedMount !== \"/\") {\n const pathWithoutMount =\n pathname.substring(normalizedMount.length) || \"/\";\n proxiedUrl = pathWithoutMount + url.search;\n }\n\n try {\n // Create a modified request preserving all properties including headers\n // A shallow clone (like { ...req }) generally will not work since headers and other\n // members are not cloned.\n const modifiedReq = Object.assign(\n Object.create(Object.getPrototypeOf(req)),\n req,\n {\n url: proxiedUrl,\n },\n );\n await webApp.handler(modifiedReq, res);\n return;\n } catch (error) {\n console.error(`Error in WebApp ${webApp.name}:`, error);\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Internal Server Error\" }));\n }\n return;\n }\n }\n }\n\n // If no WebApp matched, check if it's an Api request\n // Strip /api or /consumption prefix for Api routing\n let apiPath = pathname;\n if (pathname.startsWith(\"/api/\")) {\n apiPath = pathname.substring(4); // Remove \"/api\"\n } else if (pathname.startsWith(\"/consumption/\")) {\n apiPath = pathname.substring(13); // Remove \"/consumption\"\n }\n\n // If we stripped a prefix, it's an Api request\n if (apiPath !== pathname) {\n // Create a modified request with the rewritten URL for the apiHandler\n // Preserve all properties including headers by using Object.assign with prototype chain\n // A shallow clone (like { ...req }) generally will not work since headers and other\n // members are not cloned.\n const modifiedReq = Object.assign(\n Object.create(Object.getPrototypeOf(req)),\n req,\n {\n url: apiPath + url.search,\n },\n );\n await apiRequestHandler(modifiedReq as http.IncomingMessage, res);\n return;\n }\n\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not Found\" }));\n httpLogger(req, res, start);\n };\n};\n\nexport const runApis = async (config: ApisConfig) => {\n const apisCluster = new Cluster({\n maxWorkerCount:\n (config.workerCount ?? 0) > 0 ? config.workerCount : undefined,\n workerStart: async () => {\n let temporalClient: TemporalClient | undefined;\n if (config.temporalConfig) {\n temporalClient = await getTemporalClient(\n config.temporalConfig.url,\n config.temporalConfig.namespace,\n config.temporalConfig.clientCert,\n config.temporalConfig.clientKey,\n config.temporalConfig.apiKey,\n );\n }\n const clickhouseClient = getClickhouseClient(\n toClientConfig(config.clickhouseConfig),\n );\n let publicKey: jose.KeyLike | undefined;\n if (config.jwtConfig?.secret) {\n console.log(\"Importing JWT public key...\");\n publicKey = await jose.importSPKI(config.jwtConfig.secret, \"RS256\");\n }\n\n // Set runtime context for getMooseUtils() to detect\n const runtimeQueryClient = new QueryClient(clickhouseClient, \"runtime\");\n (globalThis as any)._mooseRuntimeContext = {\n client: new MooseClient(runtimeQueryClient, temporalClient),\n };\n\n const server = http.createServer(\n await createMainRouter(\n publicKey,\n clickhouseClient,\n temporalClient,\n config.enforceAuth,\n config.jwtConfig,\n ),\n );\n // port is now passed via config.proxyPort or defaults to 4001\n const port = config.proxyPort !== undefined ? config.proxyPort : 4001;\n server.listen(port, \"localhost\", () => {\n console.log(`Server running on port ${port}`);\n });\n\n return server;\n },\n workerStop: async (server) => {\n return new Promise<void>((resolve) => {\n server.close(() => resolve());\n });\n },\n });\n\n apisCluster.start();\n};\n","import cluster from \"node:cluster\";\nimport { availableParallelism } from \"node:os\";\nimport { exit } from \"node:process\";\nimport { Worker } from \"node:cluster\";\n\nconst DEFAULT_MAX_CPU_USAGE_RATIO = 0.7;\n// Time to restart the worker when it exits unexpectedly\n// This value is not too high to avoid the worker to be stuck in a bad state\n// but also not too low to avoid restarting the worker too often\nconst RESTART_TIME_MS = 10000;\nconst SIGTERM = \"SIGTERM\";\nconst SIGINT = \"SIGINT\";\nconst SHUTDOWN_WORKERS_INTERVAL = 500;\n\n/**\n * Manages a cluster of worker processes, handling their lifecycle including startup,\n * shutdown, and error handling.\n *\n * @typeParam C - The type of output produced during worker startup\n */\nexport class Cluster<C> {\n // Tracks if shutdown is currently in progress\n private shutdownInProgress: boolean = false;\n // Tracks if workers exited cleanly during shutdown\n private hasCleanWorkerExit: boolean = true;\n\n // String identifying if this is primary or worker process\n private processStr = `${cluster.isPrimary ? \"primary\" : \"worker\"} process ${process.pid}`;\n\n // Functions for starting and stopping workers\n private workerStart: (w: Worker, paralelism: number) => Promise<C>;\n private workerStop: (c: C) => Promise<void>;\n\n // Result from starting worker, needed for cleanup\n private startOutput: C | undefined;\n private maxCpuUsageRatio: number;\n private usedCpuCount: number;\n\n /**\n * Creates a new cluster manager instance.\n *\n * @param options - Configuration options for the cluster\n * @param options.workerStart - Async function to execute when starting a worker\n * @param options.workerStop - Async function to execute when stopping a worker\n * @param options.maxCpuUsageRatio - Maximum ratio of CPU cores to utilize (0-1)\n * @param options.maxWorkerCount - Maximum number of workers to spawn\n * @throws {Error} If maxCpuUsageRatio is not between 0 and 1\n */\n constructor(options: {\n workerStart: (w: Worker, paralelism: number) => Promise<C>;\n workerStop: (c: C) => Promise<void>;\n maxCpuUsageRatio?: number;\n maxWorkerCount?: number;\n }) {\n this.workerStart = options.workerStart;\n this.workerStop = options.workerStop;\n if (\n options.maxCpuUsageRatio &&\n (options.maxCpuUsageRatio > 1 || options.maxCpuUsageRatio < 0)\n ) {\n throw new Error(\"maxCpuUsageRatio must be between 0 and 1\");\n }\n this.maxCpuUsageRatio =\n options.maxCpuUsageRatio || DEFAULT_MAX_CPU_USAGE_RATIO;\n this.usedCpuCount = this.computeCPUUsageCount(\n this.maxCpuUsageRatio,\n options.maxWorkerCount,\n );\n }\n\n /**\n * Calculates the number of CPU cores to utilize based on available parallelism and constraints.\n *\n * @param cpuUsageRatio - Ratio of CPU cores to use (0-1)\n * @param maxWorkerCount - Optional maximum number of workers\n * @returns The number of CPU cores to utilize\n */\n computeCPUUsageCount(cpuUsageRatio: number, maxWorkerCount?: number) {\n const cpuCount = availableParallelism();\n const maxWorkers = maxWorkerCount || cpuCount;\n return Math.min(\n maxWorkers,\n Math.max(1, Math.floor(cpuCount * cpuUsageRatio)),\n );\n }\n\n /**\n * Initializes the cluster by spawning worker processes and setting up signal handlers.\n * For the primary process, spawns workers and monitors parent process.\n * For worker processes, executes the worker startup function.\n *\n * @throws {Error} If worker is undefined in worker process\n */\n async start() {\n process.on(SIGTERM, this.gracefulClusterShutdown(SIGTERM));\n process.on(SIGINT, this.gracefulClusterShutdown(SIGINT));\n\n if (cluster.isPrimary) {\n const parentPid = process.ppid;\n\n setInterval(() => {\n try {\n process.kill(parentPid, 0);\n } catch (e) {\n console.log(\"Parent process has exited.\");\n this.gracefulClusterShutdown(SIGTERM)();\n }\n }, 1000);\n\n await this.bootWorkers(this.usedCpuCount);\n } else {\n if (!cluster.worker) {\n throw new Error(\n \"Worker is not defined, it should be defined in worker process\",\n );\n }\n\n this.startOutput = await this.workerStart(\n cluster.worker,\n this.usedCpuCount,\n );\n }\n }\n\n /**\n * Spawns worker processes and configures their lifecycle event handlers.\n * Handles worker online, exit and disconnect events.\n * Automatically restarts failed workers during normal operation.\n *\n * @param numWorkers - Number of worker processes to spawn\n */\n bootWorkers = async (numWorkers: number) => {\n console.info(`Setting ${numWorkers} workers...`);\n\n for (let i = 0; i < numWorkers; i++) {\n cluster.fork();\n }\n\n cluster.on(\"online\", (worker) => {\n console.info(`worker process ${worker.process.pid} is online`);\n });\n\n cluster.on(\"exit\", (worker, code, signal) => {\n console.info(\n `worker ${worker.process.pid} exited with code ${code} and signal ${signal}`,\n );\n\n if (!this.shutdownInProgress) {\n setTimeout(() => cluster.fork(), RESTART_TIME_MS);\n }\n\n if (this.shutdownInProgress && code != 0) {\n this.hasCleanWorkerExit = false;\n }\n });\n\n cluster.on(\"disconnect\", (worker) => {\n console.info(`worker process ${worker.process.pid} has disconnected`);\n });\n };\n\n /**\n * Creates a handler function for graceful shutdown on receipt of a signal.\n * Ensures only one shutdown can occur at a time.\n * Handles shutdown differently for primary and worker processes.\n *\n * @param signal - The signal triggering the shutdown (e.g. SIGTERM)\n * @returns An async function that performs the shutdown\n */\n gracefulClusterShutdown = (signal: NodeJS.Signals) => async () => {\n if (this.shutdownInProgress) {\n return;\n }\n\n this.shutdownInProgress = true;\n this.hasCleanWorkerExit = true;\n\n console.info(\n `Got ${signal} on ${this.processStr}. Graceful shutdown start at ${new Date().toISOString()}`,\n );\n\n try {\n if (cluster.isPrimary) {\n await this.shutdownWorkers(signal);\n console.info(`${this.processStr} - worker shutdown successful`);\n exit(0);\n } else {\n // Only attempt to stop if the worker has finished starting\n if (this.startOutput) {\n await this.workerStop(this.startOutput);\n } else {\n console.info(\n `${this.processStr} - shutdown before worker fully started`,\n );\n }\n console.info(`${this.processStr} shutdown successful`);\n this.hasCleanWorkerExit ? exit(0) : exit(1);\n }\n } catch (e) {\n console.error(`${this.processStr} - shutdown failed`, e);\n exit(1);\n }\n };\n\n /**\n * Gracefully terminates all worker processes.\n * Monitors workers until they all exit or timeout occurs.\n * Only relevant for the primary process.\n *\n * @param signal - The signal to send to worker processes\n * @returns A promise that resolves when all workers have terminated\n */\n shutdownWorkers = (signal: NodeJS.Signals) => {\n return new Promise<void>((resolve, reject) => {\n if (!cluster.isPrimary) {\n return resolve();\n }\n\n if (!cluster.workers) {\n return resolve();\n }\n\n const workerIds = Object.keys(cluster.workers);\n if (workerIds.length == 0) {\n return resolve();\n }\n\n let workersAlive = 0;\n let funcRun = 0;\n\n const cleanWorkers = () => {\n ++funcRun;\n workersAlive = 0;\n\n Object.values(cluster.workers || {})\n .filter((worker) => !!worker)\n .forEach((worker) => {\n if (worker && !worker.isDead()) {\n ++workersAlive;\n if (funcRun == 1) {\n worker.kill(signal);\n }\n }\n });\n\n console.info(workersAlive + \" workers alive\");\n if (workersAlive == 0) {\n clearInterval(interval);\n return resolve();\n }\n };\n\n const interval = setInterval(cleanWorkers, SHUTDOWN_WORKERS_INTERVAL);\n });\n };\n}\n","import * as util from \"util\";\nimport { AsyncLocalStorage } from \"async_hooks\";\n\n/**\n * Sets up structured console logging by wrapping all console methods.\n * Returns the AsyncLocalStorage for use with .run() during execution.\n *\n * @template TContext - The type of context stored in AsyncLocalStorage\n * @param getContextField - Function to extract the identifying field from context\n * @param contextFieldName - The JSON field name for the context (e.g., \"api_name\", \"task_name\")\n * @returns The AsyncLocalStorage instance to use with .run() for setting context\n *\n * @example\n * ```ts\n * const taskContextStorage = setupStructuredConsole<{ taskName: string }>(\n * (ctx) => ctx.taskName,\n * \"task_name\"\n * );\n *\n * // Use with .run() to set context for user code execution\n * await taskContextStorage.run({ taskName: \"myTask\" }, async () => {\n * console.log(\"Hello\"); // Emits structured JSON log\n * });\n * ```\n */\nexport function setupStructuredConsole<TContext>(\n getContextField: (context: TContext) => string,\n contextFieldName: string,\n): AsyncLocalStorage<TContext> {\n const contextStorage = new AsyncLocalStorage<TContext>();\n\n const originalConsole = {\n log: console.log,\n info: console.info,\n warn: console.warn,\n error: console.error,\n debug: console.debug,\n };\n\n console.log = createStructuredConsoleWrapper(\n contextStorage,\n getContextField,\n contextFieldName,\n originalConsole.log,\n \"info\",\n );\n console.info = createStructuredConsoleWrapper(\n contextStorage,\n getContextField,\n contextFieldName,\n originalConsole.info,\n \"info\",\n );\n console.warn = createStructuredConsoleWrapper(\n contextStorage,\n getContextField,\n contextFieldName,\n originalConsole.warn,\n \"warn\",\n );\n console.error = createStructuredConsoleWrapper(\n contextStorage,\n getContextField,\n contextFieldName,\n originalConsole.error,\n \"error\",\n );\n console.debug = createStructuredConsoleWrapper(\n contextStorage,\n getContextField,\n contextFieldName,\n originalConsole.debug,\n \"debug\",\n );\n\n return contextStorage;\n}\n\n/**\n * Directly emits a structured log if currently in a context, without formatting.\n * Returns true if structured log was emitted, false if not in context.\n *\n * This is useful for framework code that needs to emit structured logs\n * but doesn't go through console.log() wrapper (e.g., Temporal's logger).\n *\n * @template TContext - The type of context stored in AsyncLocalStorage\n * @param contextStorage - The AsyncLocalStorage instance containing execution context\n * @param getContextField - Function to extract the identifying field from context\n * @param contextFieldName - The JSON field name for the context (e.g., \"task_name\")\n * @param level - The log level (info, warn, error, debug)\n * @param message - The log message\n * @returns true if structured log was emitted, false otherwise\n */\nexport function emitStructuredLog<TContext>(\n contextStorage: AsyncLocalStorage<TContext>,\n getContextField: (context: TContext) => string,\n contextFieldName: string,\n level: string,\n message: string,\n): boolean {\n const context = contextStorage.getStore();\n if (!context) {\n return false;\n }\n\n let ctxValue: string;\n try {\n ctxValue = getContextField(context);\n } catch {\n ctxValue = \"unknown\";\n }\n\n try {\n process.stderr.write(\n JSON.stringify({\n __moose_structured_log__: true,\n level,\n message,\n [contextFieldName]: ctxValue,\n timestamp: new Date().toISOString(),\n }) + \"\\n\",\n );\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Safely serializes a value to a string, handling circular references, BigInt, Symbols, and Error objects.\n *\n * This function:\n * - Preserves Error objects (message and stack) via util.inspect\n * - Attempts JSON.stringify for plain objects, then falls back to util.inspect\n * - Uses util.inspect for non-object types (Symbols, functions, etc.)\n *\n * @param arg - The value to serialize (can be any type)\n * @returns A string representation of the value\n */\nfunction safeStringify(arg: unknown): string {\n if (typeof arg === \"object\" && arg !== null) {\n // Special-case Error objects: JSON.stringify(new Error(\"x\")) returns \"{}\"\n // Use util.inspect to preserve message and stack trace\n if (arg instanceof Error) {\n return util.inspect(arg, { depth: 2, breakLength: Infinity });\n }\n try {\n return JSON.stringify(arg);\n } catch (e) {\n // Fall back to util.inspect for circular references or BigInt\n return util.inspect(arg, { depth: 2, breakLength: Infinity });\n }\n }\n // Return strings directly without util.inspect to avoid unwanted quotes\n if (typeof arg === \"string\") {\n return arg;\n }\n // Use util.inspect for all other non-object types to handle Symbols, functions, etc.\n // String(Symbol()) throws TypeError, but util.inspect handles it correctly\n return util.inspect(arg);\n}\n\n/**\n * Creates a structured console wrapper that emits JSON logs when in a context.\n *\n * This factory function creates a console method wrapper that:\n * - Checks if the current execution is within a context (using AsyncLocalStorage)\n * - If in context: emits a structured JSON log to stderr\n * - If not in context: delegates to the original console method\n *\n * This pattern ensures structured logs are only emitted when running user code\n * (APIs, streaming functions, workflow tasks), while preserving normal console\n * behavior for framework code.\n *\n * @template TContext - The type of context stored in AsyncLocalStorage\n * @param contextStorage - The AsyncLocalStorage instance containing execution context\n * @param getContextField - Function to extract the identifying field from context\n * @param contextFieldName - The JSON field name for the context (e.g., \"api_name\")\n * @param originalMethod - The original console method to call when not in context\n * @param level - The log level (info, warn, error, debug)\n * @returns A wrapped console method that emits structured logs\n */\nexport function createStructuredConsoleWrapper<TContext>(\n contextStorage: AsyncLocalStorage<TContext>,\n getContextField: (context: TContext) => string,\n contextFieldName: string,\n originalMethod: (...args: unknown[]) => void,\n level: string,\n) {\n return (...args: unknown[]) => {\n const context = contextStorage.getStore();\n if (!context) {\n originalMethod(...args);\n return;\n }\n\n // Safely extract context field - never throws\n let ctxValue: string;\n try {\n ctxValue = getContextField(context);\n } catch {\n ctxValue = \"unknown\";\n }\n\n // Emit structured log, fall back to original on any failure\n try {\n const message = args.map((arg) => safeStringify(arg)).join(\" \");\n process.stderr.write(\n JSON.stringify({\n __moose_structured_log__: true,\n level,\n message,\n [contextFieldName]: ctxValue,\n timestamp: new Date().toISOString(),\n }) + \"\\n\",\n );\n } catch {\n originalMethod(...args);\n }\n };\n}\n","import { Readable } from \"node:stream\";\nimport { KafkaJS } from \"@514labs/kafka-javascript\";\nconst { Kafka } = KafkaJS;\n\ntype Consumer = KafkaJS.Consumer;\ntype Producer = KafkaJS.Producer;\n\ntype KafkaMessage = {\n value: Buffer | string | null;\n key?: Buffer | string | null;\n partition?: number;\n offset?: string;\n timestamp?: string;\n headers?: Record<string, Buffer | string | undefined>;\n};\n\ntype SASLOptions = {\n mechanism: \"plain\" | \"scram-sha-256\" | \"scram-sha-512\";\n username: string;\n password: string;\n};\nimport { Buffer } from \"node:buffer\";\nimport * as process from \"node:process\";\nimport * as http from \"node:http\";\nimport {\n cliLog,\n getKafkaClient,\n createProducerConfig,\n Logger,\n logError,\n} from \"../commons\";\nimport { Cluster } from \"../cluster-utils\";\nimport { getStreamingFunctions } from \"../dmv2/internal\";\nimport type { ConsumerConfig, TransformConfig, DeadLetterQueue } from \"../dmv2\";\nimport {\n buildFieldMutationsFromColumns,\n mutateParsedJson,\n type FieldMutations,\n} from \"../utilities/json\";\nimport type { Column } from \"../dataModels/dataModelTypes\";\nimport { setupStructuredConsole } from \"../utils/structured-logging\";\n\nconst HOSTNAME = process.env.HOSTNAME;\nconst AUTO_COMMIT_INTERVAL_MS = 5000;\nconst PARTITIONS_CONSUMED_CONCURRENTLY = 3;\nconst MAX_RETRIES_CONSUMER = 150;\nconst SESSION_TIMEOUT_CONSUMER = 30000;\nconst HEARTBEAT_INTERVAL_CONSUMER = 3000;\nconst DEFAULT_MAX_STREAMING_CONCURRENCY = 100;\n// Max messages per eachBatch call - Confluent client defaults to 32, increase for throughput\nconst CONSUMER_MAX_BATCH_SIZE = 1000;\n\n// Set up structured console logging for streaming function context\nconst functionContextStorage = setupStructuredConsole<{ functionName: string }>(\n (ctx) => ctx.functionName,\n \"function_name\",\n);\n\n/**\n * Data structure for metrics logging containing counts and metadata\n */\ntype MetricsData = {\n count_in: number;\n count_out: number;\n bytes: number;\n function_name: string;\n timestamp: Date;\n};\n\n/**\n * Interface for tracking message processing metrics\n */\ninterface Metrics {\n count_in: number;\n count_out: number;\n bytes: number;\n}\n\n/**\n * Type definition for streaming transformation function\n */\ntype StreamingFunction = (data: unknown) => unknown | Promise<unknown>;\n\n/**\n * Simplified Kafka message type containing only value\n */\ntype KafkaMessageWithLineage = {\n value: string;\n originalValue: object;\n originalMessage: KafkaMessage;\n dlq?: DeadLetterQueue<any>;\n};\n\n/**\n * Configuration interface for Kafka topics including namespace and version support\n */\nexport interface TopicConfig {\n name: string; // Full topic name including namespace if present\n partitions: number;\n retention_ms: number;\n max_message_bytes: number;\n namespace?: string;\n version?: string;\n}\n\n/**\n * Configuration interface for streaming function arguments\n */\nexport interface StreamingFunctionArgs {\n sourceTopic: TopicConfig;\n targetTopic?: TopicConfig;\n functionFilePath: string;\n broker: string; // Comma-separated list of Kafka broker addresses (e.g., \"broker1:9092, broker2:9092\"). Whitespace around commas is automatically trimmed.\n maxSubscriberCount: number;\n logPayloads?: boolean;\n saslUsername?: string;\n saslPassword?: string;\n saslMechanism?: string;\n securityProtocol?: string;\n}\n\n/**\n * Maximum number of concurrent streaming operations, configurable via environment\n */\nconst MAX_STREAMING_CONCURRENCY =\n process.env.MAX_STREAMING_CONCURRENCY ?\n parseInt(process.env.MAX_STREAMING_CONCURRENCY, 10)\n : DEFAULT_MAX_STREAMING_CONCURRENCY;\n\n/**\n * Logs metrics data to HTTP endpoint\n */\nexport const metricsLog: (log: MetricsData) => void = (log) => {\n const req = http.request({\n port: parseInt(process.env.MOOSE_MANAGEMENT_PORT ?? \"5001\", 10),\n method: \"POST\",\n path: \"/metrics-logs\",\n });\n\n req.on(\"error\", (err: Error) => {\n console.log(\n `Error ${err.name} sending metrics to management port.`,\n err.message,\n );\n });\n\n req.write(JSON.stringify({ ...log }));\n req.end();\n};\n\n/**\n * Initializes and connects Kafka producer\n */\nconst startProducer = async (\n logger: Logger,\n producer: Producer,\n): Promise<void> => {\n try {\n logger.log(\"Connecting producer...\");\n await producer.connect();\n logger.log(\"Producer is running...\");\n } catch (error) {\n logger.error(\"Failed to connect producer:\");\n if (error instanceof Error) {\n logError(logger, error);\n }\n throw error;\n }\n};\n\n/**\n * Disconnects a Kafka producer and logs the shutdown\n *\n * @param logger - Logger instance for outputting producer status\n * @param producer - KafkaJS Producer instance to disconnect\n * @returns Promise that resolves when producer is disconnected\n * @example\n * ```ts\n * await stopProducer(logger, producer); // Disconnects producer and logs shutdown\n * ```\n */\nconst stopProducer = async (\n logger: Logger,\n producer: Producer,\n): Promise<void> => {\n await producer.disconnect();\n logger.log(\"Producer is shutting down...\");\n};\n\n/**\n * Gracefully stops a Kafka consumer by pausing all partitions and then disconnecting\n *\n * @param logger - Logger instance for outputting consumer status\n * @param consumer - KafkaJS Consumer instance to disconnect\n * @param sourceTopic - Topic configuration containing name and partition count\n * @returns Promise that resolves when consumer is disconnected\n * @example\n * ```ts\n * await stopConsumer(logger, consumer, sourceTopic); // Pauses all partitions and disconnects consumer\n * ```\n */\nconst stopConsumer = async (\n logger: Logger,\n consumer: Consumer,\n sourceTopic: TopicConfig,\n): Promise<void> => {\n try {\n // Try to pause the consumer first if the method exists\n logger.log(\"Pausing consumer...\");\n\n // Generate partition numbers array based on the topic's partition count\n const partitionNumbers = Array.from(\n { length: sourceTopic.partitions },\n (_, i) => i,\n );\n\n await consumer.pause([\n {\n topic: sourceTopic.name,\n partitions: partitionNumbers,\n },\n ]);\n\n logger.log(\"Disconnecting consumer...\");\n await consumer.disconnect();\n logger.log(\"Consumer is shutting down...\");\n } catch (error) {\n logger.error(`Error during consumer shutdown: ${error}`);\n // Continue with disconnect even if pause fails\n try {\n await consumer.disconnect();\n logger.log(\"Consumer disconnected after error\");\n } catch (disconnectError) {\n logger.error(`Failed to disconnect consumer: ${disconnectError}`);\n }\n }\n};\n\n/**\n * Processes a single Kafka message through a streaming function and returns transformed message(s)\n *\n * @param logger - Logger instance for outputting message processing status and errors\n * @param streamingFunctionWithConfigList - functions (with their configs) that transforms input message data\n * @param message - Kafka message to be processed\n * @param producer - Kafka producer for sending dead letter\n * @param fieldMutations - Pre-built field mutations for data transformations\n * @returns Promise resolving to array of transformed messages or undefined if processing fails\n *\n * The function will:\n * 1. Check for null/undefined message values\n * 2. Parse the message value as JSON\n * 3. Apply field mutations (e.g., date parsing) using pre-built configuration\n * 4. Pass parsed data through the streaming function\n * 5. Convert transformed data back to string format\n * 6. Handle both single and array return values\n * 7. Log any processing errors\n */\nconst handleMessage = async (\n logger: Logger,\n // Note: TransformConfig<any> is intentionally generic here as it handles\n // various data model types that are determined at runtime\n streamingFunctionWithConfigList: [StreamingFunction, TransformConfig<any>][],\n message: KafkaMessage,\n producer: Producer,\n fieldMutations?: FieldMutations,\n logPayloads?: boolean,\n): Promise<KafkaMessageWithLineage[] | undefined> => {\n if (message.value === undefined || message.value === null) {\n logger.log(`Received message with no value, skipping...`);\n return undefined;\n }\n\n try {\n // Detect Schema Registry JSON envelope: 0x00 + 4-byte schema ID (big-endian) + JSON bytes\n let payloadBuffer = message.value as Buffer;\n if (\n payloadBuffer &&\n payloadBuffer.length >= 5 &&\n payloadBuffer[0] === 0x00\n ) {\n payloadBuffer = payloadBuffer.subarray(5);\n }\n // Parse JSON then apply field mutations using pre-built configuration\n const parsedData = JSON.parse(payloadBuffer.toString());\n mutateParsedJson(parsedData, fieldMutations);\n\n // Log payload before transformation if enabled\n if (logPayloads) {\n logger.log(`[PAYLOAD:STREAM_IN] ${JSON.stringify(parsedData)}`);\n }\n\n // Context is already set at batch level via functionContextStorage.run()\n const transformedData = await Promise.all(\n streamingFunctionWithConfigList.map(async ([fn, config]) => {\n try {\n return await fn(parsedData);\n } catch (e) {\n // Check if there's a deadLetterQueue configured\n const deadLetterQueue = config.deadLetterQueue;\n\n if (deadLetterQueue) {\n // Create a dead letter record\n const deadLetterRecord = {\n originalRecord: {\n ...parsedData,\n // Include original Kafka message metadata\n __sourcePartition: message.partition,\n __sourceOffset: message.offset,\n __sourceTimestamp: message.timestamp,\n },\n errorMessage: e instanceof Error ? e.message : String(e),\n errorType: e instanceof Error ? e.constructor.name : \"Unknown\",\n failedAt: new Date(),\n source: \"transform\",\n };\n\n cliLog({\n action: \"DeadLetter\",\n message: `Sending message to DLQ ${deadLetterQueue.name}: ${e instanceof Error ? e.message : String(e)}`,\n message_type: \"Error\",\n });\n // Send to the DLQ\n try {\n await producer.send({\n topic: deadLetterQueue.name,\n messages: [{ value: JSON.stringify(deadLetterRecord) }],\n });\n } catch (dlqError) {\n logger.error(`Failed to send to dead letter queue: ${dlqError}`);\n }\n } else {\n // No DLQ configured, just log the error\n cliLog({\n action: \"Function\",\n message: `Error processing message (no DLQ configured): ${e instanceof Error ? e.message : String(e)}`,\n message_type: \"Error\",\n });\n }\n\n // rethrow for the outside error handling\n throw e;\n }\n }),\n );\n\n const processedMessages = transformedData\n .map((userFunctionOutput, i) => {\n const [_, config] = streamingFunctionWithConfigList[i];\n if (userFunctionOutput) {\n if (Array.isArray(userFunctionOutput)) {\n // We Promise.all streamingFunctionWithConfigList above.\n // Promise.all always wraps results in an array, even for single transforms.\n // When a transform returns an array (e.g., [msg1, msg2] to emit multiple messages),\n // we get [[msg1, msg2]]. flat() unwraps one level so each item becomes its own message.\n // Without flat(), the entire array would be JSON.stringify'd as a single message.\n return userFunctionOutput\n .flat()\n .filter((item) => item !== undefined && item !== null)\n .map((item) => ({\n value: JSON.stringify(item),\n originalValue: parsedData,\n originalMessage: message,\n dlq: config.deadLetterQueue ?? undefined,\n }));\n } else {\n return [\n {\n value: JSON.stringify(userFunctionOutput),\n originalValue: parsedData,\n originalMessage: message,\n dlq: config.deadLetterQueue ?? undefined,\n },\n ];\n }\n }\n })\n .flat()\n .filter((item) => item !== undefined && item !== null);\n\n // Log payload after transformation if enabled (what we're actually sending to Kafka)\n if (logPayloads) {\n if (processedMessages.length > 0) {\n // msg.value is already JSON stringified, just construct array format\n const outgoingJsonStrings = processedMessages.map((msg) => msg.value);\n logger.log(`[PAYLOAD:STREAM_OUT] [${outgoingJsonStrings.join(\",\")}]`);\n } else {\n logger.log(`[PAYLOAD:STREAM_OUT] (no output from streaming function)`);\n }\n }\n\n return processedMessages;\n } catch (e) {\n // TODO: Track failure rate\n logger.error(`Failed to transform data`);\n if (e instanceof Error) {\n logError(logger, e);\n }\n }\n\n return undefined;\n};\n\n/**\n * Handles sending failed messages to their configured Dead Letter Queues\n *\n * @param logger - Logger instance for outputting DLQ status\n * @param producer - Kafka producer for sending to DLQ topics\n * @param messages - Array of failed messages with DLQ configuration\n * @param error - The error that caused the failure\n * @returns true if ALL messages were successfully sent to their DLQs, false otherwise\n */\nconst handleDLQForFailedMessages = async (\n logger: Logger,\n producer: Producer,\n messages: KafkaMessageWithLineage[],\n error: unknown,\n): Promise<boolean> => {\n let messagesHandledByDLQ = 0;\n let messagesWithoutDLQ = 0;\n let dlqErrors = 0;\n\n for (const msg of messages) {\n if (msg.dlq && msg.originalValue) {\n const deadLetterRecord = {\n originalRecord: {\n ...msg.originalValue,\n // Include original Kafka message metadata\n __sourcePartition: msg.originalMessage.partition,\n __sourceOffset: msg.originalMessage.offset,\n __sourceTimestamp: msg.originalMessage.timestamp,\n },\n errorMessage: error instanceof Error ? error.message : String(error),\n errorType: error instanceof Error ? error.constructor.name : \"Unknown\",\n failedAt: new Date(),\n source: \"transform\",\n };\n\n cliLog({\n action: \"DeadLetter\",\n message: `Sending failed message to DLQ ${msg.dlq.name}: ${error instanceof Error ? error.message : String(error)}`,\n message_type: \"Error\",\n });\n\n try {\n await producer.send({\n topic: msg.dlq.name,\n messages: [{ value: JSON.stringify(deadLetterRecord) }],\n });\n logger.log(`Sent failed message to DLQ ${msg.dlq.name}`);\n messagesHandledByDLQ++;\n } catch (dlqError) {\n logger.error(`Failed to send to DLQ: ${dlqError}`);\n dlqErrors++;\n }\n } else if (!msg.dlq) {\n messagesWithoutDLQ++;\n logger.warn(`Cannot send to DLQ: no DLQ configured for message`);\n } else {\n messagesWithoutDLQ++;\n logger.warn(`Cannot send to DLQ: original message value not available`);\n }\n }\n\n // Check if ALL messages were successfully handled by DLQ\n const allMessagesHandled =\n messagesHandledByDLQ === messages.length &&\n messagesWithoutDLQ === 0 &&\n dlqErrors === 0;\n\n if (allMessagesHandled) {\n logger.log(\n `All ${messagesHandledByDLQ} failed message(s) sent to DLQ, suppressing original error`,\n );\n } else if (messagesHandledByDLQ > 0) {\n // Log summary of partial DLQ handling\n logger.warn(\n `Partial DLQ success: ${messagesHandledByDLQ}/${messages.length} message(s) sent to DLQ`,\n );\n if (messagesWithoutDLQ > 0) {\n logger.error(\n `Cannot handle batch failure: ${messagesWithoutDLQ} message(s) have no DLQ configured or missing original value`,\n );\n }\n if (dlqErrors > 0) {\n logger.error(`${dlqErrors} message(s) failed to send to DLQ`);\n }\n }\n\n return allMessagesHandled;\n};\n\n/**\n * Sends processed messages to a target Kafka topic\n *\n * @param logger - Logger instance for outputting send status and errors\n * @param metrics - Metrics object for tracking message counts and bytes sent\n * @param targetTopic - Target topic configuration\n * @param producer - Kafka producer instance for sending messages\n * @param messages - Array of processed messages to send (messages carry their own DLQ config)\n * @returns Promise that resolves when all messages are sent\n *\n * The Confluent Kafka library handles batching internally via message.max.bytes\n * and retries transient failures automatically. This function simply sends all\n * messages and handles permanent failures by routing to DLQ.\n */\nconst sendMessages = async (\n logger: Logger,\n metrics: Metrics,\n targetTopic: TopicConfig,\n producer: Producer,\n messages: KafkaMessageWithLineage[],\n): Promise<void> => {\n if (messages.length === 0) return;\n\n try {\n // Library handles batching and retries internally\n await producer.send({\n topic: targetTopic.name,\n messages: messages,\n });\n\n // Track metrics only after successful send to target topic\n // Messages routed to DLQ should NOT be counted as successful sends\n for (const msg of messages) {\n metrics.bytes += Buffer.byteLength(msg.value, \"utf8\");\n }\n metrics.count_out += messages.length;\n\n logger.log(`Sent ${messages.length} messages to ${targetTopic.name}`);\n } catch (e) {\n // Library already retried - this is a permanent failure\n logger.error(`Failed to send transformed data`);\n if (e instanceof Error) {\n logError(logger, e);\n }\n\n // Handle DLQ for failed messages\n // Only throw if not all messages were successfully routed to DLQ\n const allHandledByDLQ = await handleDLQForFailedMessages(\n logger,\n producer,\n messages,\n e,\n );\n if (!allHandledByDLQ) {\n throw e;\n }\n }\n};\n\n/**\n * Periodically sends metrics about message processing to a metrics logging endpoint.\n * Resets metrics counters after each send. Runs every second via setTimeout.\n *\n * @param logger - Logger instance containing the function name prefix\n * @param metrics - Metrics object tracking message counts and bytes processed\n * @example\n * ```ts\n * const metrics = { count_in: 10, count_out: 8, bytes: 1024 };\n * sendMessageMetrics(logger, metrics); // Sends metrics and resets counters\n * ```\n */\nconst sendMessageMetrics = (logger: Logger, metrics: Metrics) => {\n if (metrics.count_in > 0 || metrics.count_out > 0 || metrics.bytes > 0) {\n metricsLog({\n count_in: metrics.count_in,\n count_out: metrics.count_out,\n function_name: logger.logPrefix,\n bytes: metrics.bytes,\n timestamp: new Date(),\n });\n }\n metrics.count_in = 0;\n metrics.bytes = 0;\n metrics.count_out = 0;\n setTimeout(() => sendMessageMetrics(logger, metrics), 1000);\n};\n\nasync function loadStreamingFunction(\n sourceTopic: TopicConfig,\n targetTopic?: TopicConfig,\n): Promise<{\n functions: [StreamingFunction, TransformConfig<any> | ConsumerConfig<any>][];\n fieldMutations: FieldMutations | undefined;\n}> {\n const transformFunctions = await getStreamingFunctions();\n const transformFunctionKey = `${topicNameToStreamName(sourceTopic)}_${targetTopic ? topicNameToStreamName(targetTopic) : \"<no-target>\"}`;\n\n const matchingEntries = Array.from(transformFunctions.entries()).filter(\n ([key]) => key.startsWith(transformFunctionKey),\n );\n\n if (matchingEntries.length === 0) {\n const message = `No functions found for ${transformFunctionKey}`;\n cliLog({\n action: \"Function\",\n message: `${message}`,\n message_type: \"Error\",\n });\n throw new Error(message);\n }\n\n // Extract functions and configs, and get columns from the first entry\n // (all functions for the same source topic will have the same columns)\n const functions = matchingEntries.map(([_, [fn, config]]) => [\n fn,\n config,\n ]) as [StreamingFunction, TransformConfig<any> | ConsumerConfig<any>][];\n const [_key, firstEntry] = matchingEntries[0];\n const sourceColumns = firstEntry[2];\n\n // Pre-build field mutations once for all messages\n const fieldMutations = buildFieldMutationsFromColumns(sourceColumns);\n\n return { functions, fieldMutations };\n}\n\n/**\n * Initializes and starts a Kafka consumer that processes messages using a streaming function\n *\n * @param logger - Logger instance for outputting consumer status and errors\n * @param metrics - Metrics object for tracking message counts and bytes processed\n * @param parallelism - Number of parallel workers processing messages\n * @param args - Configuration arguments for source/target topics and streaming function\n * @param consumer - KafkaJS Consumer instance\n * @param producer - KafkaJS Producer instance for sending processed messages\n * @param streamingFuncId - Unique identifier for this consumer group\n * @param maxMessageSize - Maximum message size in bytes allowed by Kafka broker\n * @returns Promise that resolves when consumer is started\n *\n * The consumer will:\n * 1. Connect to Kafka\n * 2. Subscribe to the source topic\n * 3. Process messages in batches using the streaming function\n * 4. Send processed messages to target topic (if configured)\n * 5. Commit offsets after successful processing\n */\nconst startConsumer = async (\n args: StreamingFunctionArgs,\n logger: Logger,\n metrics: Metrics,\n _parallelism: number,\n consumer: Consumer,\n producer: Producer,\n streamingFuncId: string,\n): Promise<void> => {\n // Validate topic configurations\n validateTopicConfig(args.sourceTopic);\n if (args.targetTopic) {\n validateTopicConfig(args.targetTopic);\n }\n\n try {\n logger.log(\"Connecting consumer...\");\n await consumer.connect();\n logger.log(\"Consumer connected successfully\");\n } catch (error) {\n logger.error(\"Failed to connect consumer:\");\n if (error instanceof Error) {\n logError(logger, error);\n }\n throw error;\n }\n\n logger.log(\n `Starting consumer group '${streamingFuncId}' with source topic: ${args.sourceTopic.name} and target topic: ${args.targetTopic?.name || \"none\"}`,\n );\n\n // We preload the function to not have to load it for each message\n // Note: Config types use 'any' as generics because they handle various\n // data model types determined at runtime, not compile time\n // Since dmv1 was removed, always use loadStreamingFunction which loads\n // transforms from the registry (populated via addTransform() calls).\n // The loadIndex() inside getStreamingFunctions() handles compiled vs\n // non-compiled mode internally.\n const result = await loadStreamingFunction(\n args.sourceTopic,\n args.targetTopic,\n );\n const streamingFunctions = result.functions;\n const fieldMutations = result.fieldMutations;\n\n await consumer.subscribe({\n topics: [args.sourceTopic.name], // Use full topic name for Kafka operations\n });\n\n await consumer.run({\n eachBatchAutoResolve: true,\n // Enable parallel processing of partitions\n partitionsConsumedConcurrently: PARTITIONS_CONSUMED_CONCURRENTLY, // To be adjusted\n eachBatch: async ({ batch, heartbeat, isRunning, isStale }) => {\n if (!isRunning() || isStale()) {\n return;\n }\n\n // Get function name for structured logging context\n const functionName = logger.logPrefix;\n\n // Wrap entire batch processing in context so all logs have resource_name\n await functionContextStorage.run({ functionName }, async () => {\n metrics.count_in += batch.messages.length;\n\n cliLog({\n action: \"Received\",\n message: `${logger.logPrefix.replace(\"__\", \" -> \")} ${batch.messages.length} message(s)`,\n });\n logger.log(`Received ${batch.messages.length} message(s)`);\n\n let index = 0;\n const readableStream = Readable.from(batch.messages);\n\n const processedMessages: (KafkaMessageWithLineage[] | undefined)[] =\n await readableStream\n .map(\n async (message) => {\n index++;\n if (\n (batch.messages.length > DEFAULT_MAX_STREAMING_CONCURRENCY &&\n index % DEFAULT_MAX_STREAMING_CONCURRENCY) ||\n index - 1 === batch.messages.length\n ) {\n await heartbeat();\n }\n return handleMessage(\n logger,\n streamingFunctions,\n message,\n producer,\n fieldMutations,\n args.logPayloads,\n );\n },\n {\n concurrency: MAX_STREAMING_CONCURRENCY,\n },\n )\n .toArray();\n\n const filteredMessages = processedMessages\n .flat()\n .filter((msg) => msg !== undefined && msg.value !== undefined);\n\n if (args.targetTopic === undefined || processedMessages.length === 0) {\n return;\n }\n\n await heartbeat();\n\n if (filteredMessages.length > 0) {\n // Messages now carry their own DLQ configuration in the lineage\n await sendMessages(\n logger,\n metrics,\n args.targetTopic,\n producer,\n filteredMessages as KafkaMessageWithLineage[],\n );\n }\n });\n },\n });\n\n logger.log(\"Consumer is running...\");\n};\n\n/**\n * Creates a Logger instance that prefixes all log messages with the source and target topic\n *\n * @param args - The streaming function arguments containing source and target topics\n * @returns A Logger instance with standard log, error and warn methods\n *\n * The logPrefix is set to match source_primitive.name in the infrastructure map:\n * - For transforms: `{source}__{target}` (double underscore)\n * - For consumers: just `{source}` (no suffix)\n *\n * This ensures structured logs can be correlated with the infrastructure map.\n *\n * @example\n * ```ts\n * const logger = buildLogger({sourceTopic: {..., name: 'events'}, targetTopic: {..., name: 'processed'}}, 0);\n * logger.log('message'); // Outputs: \"events__processed (worker 0): message\"\n * ```\n */\nconst buildLogger = (args: StreamingFunctionArgs, workerId: number): Logger => {\n // Get base stream names without namespace prefix or version suffix\n const sourceBaseName = topicNameToStreamName(args.sourceTopic);\n const targetBaseName =\n args.targetTopic ? topicNameToStreamName(args.targetTopic) : undefined;\n\n // Function name matches source_primitive.name in infrastructure map\n // Uses double underscore separator for transforms, plain name for consumers\n const functionName =\n targetBaseName ? `${sourceBaseName}__${targetBaseName}` : sourceBaseName;\n\n // Human-readable log prefix includes worker ID for debugging\n const logPrefix = `${functionName} (worker ${workerId})`;\n\n return {\n // logPrefix is used for structured logging (function_name field)\n // Must match source_primitive.name format for log correlation\n logPrefix: functionName,\n log: (message: string): void => {\n console.log(`${logPrefix}: ${message}`);\n },\n error: (message: string): void => {\n console.error(`${logPrefix}: ${message}`);\n },\n warn: (message: string): void => {\n console.warn(`${logPrefix}: ${message}`);\n },\n };\n};\n\n/**\n * Formats a version string into a topic suffix format by replacing dots with underscores\n * Example: \"1.2.3\" -> \"_1_2_3\"\n */\nexport function formatVersionSuffix(version: string): string {\n return `_${version.replace(/\\./g, \"_\")}`;\n}\n\n/**\n * Transforms a topic name by removing namespace prefix and version suffix\n * to get the base stream name for function mapping\n */\nexport function topicNameToStreamName(config: TopicConfig): string {\n let name = config.name;\n\n // Handle version suffix if present\n if (config.version) {\n const versionSuffix = formatVersionSuffix(config.version);\n if (name.endsWith(versionSuffix)) {\n name = name.slice(0, -versionSuffix.length);\n } else {\n throw new Error(\n `Version suffix ${versionSuffix} not found in topic name ${name}`,\n );\n }\n }\n\n // Handle namespace prefix if present\n if (config.namespace && config.namespace !== \"\") {\n const prefix = `${config.namespace}.`;\n if (name.startsWith(prefix)) {\n name = name.slice(prefix.length);\n } else {\n throw new Error(\n `Namespace prefix ${prefix} not found in topic name ${name}`,\n );\n }\n }\n\n return name;\n}\n\n/**\n * Validates a topic configuration for proper namespace and version formatting\n */\nexport function validateTopicConfig(config: TopicConfig): void {\n if (config.namespace && !config.name.startsWith(`${config.namespace}.`)) {\n throw new Error(\n `Topic name ${config.name} must start with namespace ${config.namespace}`,\n );\n }\n\n if (config.version) {\n const versionSuffix = formatVersionSuffix(config.version);\n if (!config.name.endsWith(versionSuffix)) {\n throw new Error(\n `Topic name ${config.name} must end with version ${config.version}`,\n );\n }\n }\n}\n\n/**\n * Initializes and runs a clustered streaming function system that processes messages from Kafka\n *\n * This function:\n * 1. Creates a cluster of workers to handle Kafka message processing\n * 2. Sets up Kafka producers and consumers for each worker\n * 3. Configures logging and metrics collection\n * 4. Handles graceful shutdown on termination\n *\n * The system supports:\n * - Multiple workers processing messages in parallel\n * - Dynamic CPU usage control via maxCpuUsageRatio\n * - SASL authentication for Kafka\n * - Metrics tracking for message counts and bytes processed\n * - Graceful shutdown of Kafka connections\n *\n * @returns Promise that resolves when the cluster is started\n * @throws Will log errors if Kafka connections fail\n *\n * @example\n * ```ts\n * await runStreamingFunctions({\n * sourceTopic: { name: 'source', partitions: 3, retentionPeriod: 86400, maxMessageBytes: 1048576 },\n * targetTopic: { name: 'target', partitions: 3, retentionPeriod: 86400, maxMessageBytes: 1048576 },\n * functionFilePath: './transform.js',\n * broker: 'localhost:9092',\n * maxSubscriberCount: 3\n * }); // Starts the streaming function cluster\n * ```\n */\nexport const runStreamingFunctions = async (\n args: StreamingFunctionArgs,\n): Promise<void> => {\n // Validate topic configurations at startup\n validateTopicConfig(args.sourceTopic);\n if (args.targetTopic) {\n validateTopicConfig(args.targetTopic);\n }\n\n // Use base stream names (without namespace/version) for function ID\n // We use flow- instead of function- because that's what the ACLs in boreal are linked with\n // When migrating - make sure the ACLs are updated to use the new prefix.\n const streamingFuncId = `flow-${args.sourceTopic.name}-${args.targetTopic?.name || \"\"}`;\n\n const cluster = new Cluster({\n maxCpuUsageRatio: 0.5,\n maxWorkerCount: args.maxSubscriberCount,\n workerStart: async (worker, parallelism) => {\n const logger = buildLogger(args, worker.id);\n const functionName = logger.logPrefix;\n\n // Wrap entire startup in context so all logs have function_name\n return await functionContextStorage.run({ functionName }, async () => {\n const metrics = {\n count_in: 0,\n count_out: 0,\n bytes: 0,\n };\n\n setTimeout(() => sendMessageMetrics(logger, metrics), 1000);\n\n const clientIdPrefix = HOSTNAME ? `${HOSTNAME}-` : \"\";\n const processId = `${clientIdPrefix}${streamingFuncId}-ts-${worker.id}`;\n\n const kafka = await getKafkaClient(\n {\n clientId: processId,\n broker: args.broker,\n securityProtocol: args.securityProtocol,\n saslUsername: args.saslUsername,\n saslPassword: args.saslPassword,\n saslMechanism: args.saslMechanism,\n },\n logger,\n );\n\n // Note: \"js.consumer.max.batch.size\" is a librdkafka native config not in TS types\n const consumer: Consumer = kafka.consumer({\n kafkaJS: {\n groupId: streamingFuncId,\n sessionTimeout: SESSION_TIMEOUT_CONSUMER,\n heartbeatInterval: HEARTBEAT_INTERVAL_CONSUMER,\n retry: {\n retries: MAX_RETRIES_CONSUMER,\n },\n autoCommit: true,\n autoCommitInterval: AUTO_COMMIT_INTERVAL_MS,\n fromBeginning: true,\n },\n \"js.consumer.max.batch.size\": CONSUMER_MAX_BATCH_SIZE,\n });\n\n // Sync producer message.max.bytes with topic config\n const maxMessageBytes =\n args.targetTopic?.max_message_bytes || 1024 * 1024;\n\n const producer: Producer = kafka.producer(\n createProducerConfig(maxMessageBytes),\n );\n\n try {\n logger.log(\"Starting producer...\");\n await startProducer(logger, producer);\n\n try {\n logger.log(\"Starting consumer...\");\n await startConsumer(\n args,\n logger,\n metrics,\n parallelism,\n consumer,\n producer,\n streamingFuncId,\n );\n } catch (e) {\n logger.error(\"Failed to start kafka consumer: \");\n if (e instanceof Error) {\n logError(logger, e);\n }\n // Re-throw to ensure proper error handling\n throw e;\n }\n } catch (e) {\n logger.error(\"Failed to start kafka producer: \");\n if (e instanceof Error) {\n logError(logger, e);\n }\n // Re-throw to ensure proper error handling\n throw e;\n }\n\n return [logger, producer, consumer] as [Logger, Producer, Consumer];\n });\n },\n workerStop: async ([logger, producer, consumer]) => {\n const functionName = logger.logPrefix;\n\n // Wrap entire shutdown in context so all logs have function_name\n await functionContextStorage.run({ functionName }, async () => {\n logger.log(`Received SIGTERM, shutting down gracefully...`);\n\n // First stop the consumer to prevent new messages\n logger.log(\"Stopping consumer first...\");\n await stopConsumer(logger, consumer, args.sourceTopic);\n\n // Wait a bit for in-flight messages to complete processing\n logger.log(\"Waiting for in-flight messages to complete...\");\n await new Promise((resolve) => setTimeout(resolve, 2000));\n\n // Then stop the producer\n logger.log(\"Stopping producer...\");\n await stopProducer(logger, producer);\n\n logger.log(\"Graceful shutdown completed\");\n });\n },\n });\n\n cluster.start();\n};\n","import { getSourceDir, getOutDir, loadModule } from \"./compiler-config\";\n\nexport async function runExportSerializer(targetModel: string) {\n const sourceDir = getSourceDir();\n const outDir = getOutDir();\n\n // Always use compiled paths - replace source directory with compiled path\n let modulePath = targetModel;\n\n // Replace source directory with compiled path and .ts with .js\n // Handle both absolute paths (starting with /) and relative paths\n // Use string replacement instead of RegExp to avoid ReDoS risk\n const sourcePattern = `/${sourceDir}/`;\n if (modulePath.includes(sourcePattern)) {\n modulePath = modulePath.replace(sourcePattern, `/${outDir}/${sourceDir}/`);\n }\n // Replace .ts extension with .js\n modulePath = modulePath.replace(/\\.ts$/, \".js\");\n\n // Use dynamic loader that handles both CJS and ESM\n const exports_list = await loadModule(modulePath);\n console.log(JSON.stringify(exports_list));\n}\n","import {\n DefaultLogger,\n NativeConnection,\n NativeConnectionOptions,\n Worker,\n bundleWorkflowCode,\n} from \"@temporalio/worker\";\nimport * as path from \"path\";\nimport * as fs from \"fs\";\nimport { Workflow } from \"../dmv2\";\nimport { getWorkflows } from \"../dmv2/internal\";\nimport { createActivityForScript } from \"./activity\";\nimport { activities } from \"./activity\";\nimport { initializeLogger } from \"./logger\";\n\ninterface TemporalConfig {\n url: string;\n namespace: string;\n clientCert?: string;\n clientKey?: string;\n apiKey?: string;\n}\n\ninterface ScriptsConfig {\n temporalConfig?: TemporalConfig;\n}\n\n// Maintain a global set of activity names we've already registered\nconst ALREADY_REGISTERED = new Set<string>();\n\nfunction collectActivities(\n logger: DefaultLogger,\n workflows: Map<string, Workflow>,\n) {\n logger.info(`Collecting tasks from workflows`);\n const scriptNames: string[] = [];\n for (const [name, workflow] of workflows.entries()) {\n logger.info(\n `Registering workflow: ${name} with starting task: ${workflow.config.startingTask.name}`,\n );\n scriptNames.push(`${name}/${workflow.config.startingTask.name}`);\n }\n return scriptNames;\n}\n\n/**\n * This looks similar to the client in apis.\n * Temporal SDK uses similar looking connection options & client,\n * but there are different libraries for a worker like this & a client\n * like in the apis.\n */\nasync function createTemporalConnection(\n logger: DefaultLogger,\n temporalConfig: TemporalConfig,\n): Promise<NativeConnection> {\n logger.info(\n `Using temporal_url: ${temporalConfig.url} and namespace: ${temporalConfig.namespace}`,\n );\n\n let connectionOptions: NativeConnectionOptions = {\n address: temporalConfig.url,\n };\n\n if (temporalConfig.clientCert && temporalConfig.clientKey) {\n logger.info(\"Using TLS for secure Temporal\");\n const cert = await fs.readFileSync(temporalConfig.clientCert);\n const key = await fs.readFileSync(temporalConfig.clientKey);\n\n connectionOptions.tls = {\n clientCertPair: {\n crt: cert,\n key: key,\n },\n };\n } else if (temporalConfig.apiKey) {\n logger.info(`Using API key for secure Temporal`);\n // URL with API key uses gRPC regional endpoint\n connectionOptions.address = \"us-west1.gcp.api.temporal.io:7233\";\n connectionOptions.apiKey = temporalConfig.apiKey;\n connectionOptions.tls = {};\n connectionOptions.metadata = {\n \"temporal-namespace\": temporalConfig.namespace,\n };\n }\n\n logger.info(`Connecting to Temporal at ${connectionOptions.address}`);\n\n const maxRetries = 5;\n const baseDelay = 1000;\n let attempt = 0;\n\n while (true) {\n try {\n const connection = await NativeConnection.connect(connectionOptions);\n logger.info(\"Connected to Temporal server\");\n return connection;\n } catch (err) {\n attempt++;\n logger.error(`Connection attempt ${attempt} failed: ${err}`);\n\n if (attempt >= maxRetries) {\n logger.error(`Failed to connect after ${attempt} attempts`);\n throw err;\n }\n\n const backoff = baseDelay * Math.pow(2, attempt - 1);\n logger.warn(`Retrying connection in ${backoff}ms...`);\n await new Promise((resolve) => setTimeout(resolve, backoff));\n }\n }\n}\n\nasync function registerWorkflows(\n logger: DefaultLogger,\n config: ScriptsConfig,\n): Promise<Worker | null> {\n logger.info(`Registering workflows`);\n\n // If temporalConfig is not provided, workflows are disabled\n if (!config.temporalConfig) {\n logger.info(`Temporal config not provided, skipping workflow registration`);\n return null;\n }\n\n // Collect all TypeScript activities from registered workflows\n const allScriptPaths: string[] = [];\n const dynamicActivities: any[] = [];\n\n try {\n const workflows = await getWorkflows();\n if (workflows.size > 0) {\n logger.info(`Found ${workflows.size} workflows`);\n allScriptPaths.push(...collectActivities(logger, workflows));\n\n if (allScriptPaths.length === 0) {\n logger.info(`No tasks found in workflows`);\n return null;\n }\n\n logger.info(`Found ${allScriptPaths.length} tasks in workflows`);\n\n for (const activityName of allScriptPaths) {\n if (!ALREADY_REGISTERED.has(activityName)) {\n const activity = await createActivityForScript(activityName);\n dynamicActivities.push(activity);\n ALREADY_REGISTERED.add(activityName);\n logger.info(`Registered task ${activityName}`);\n }\n }\n\n if (dynamicActivities.length === 0) {\n logger.info(`No dynamic activities found in workflows`);\n return null;\n }\n\n logger.info(\n `Found ${dynamicActivities.length} dynamic activities in workflows`,\n );\n }\n\n if (allScriptPaths.length === 0) {\n logger.info(`No workflows found`);\n return null;\n }\n\n logger.info(`Found ${allScriptPaths.length} workflows`);\n\n if (dynamicActivities.length === 0) {\n logger.info(`No tasks found`);\n return null;\n }\n\n logger.info(`Found ${dynamicActivities.length} task(s)`);\n\n const connection = await createTemporalConnection(\n logger,\n config.temporalConfig,\n );\n\n // Create a custom logger that suppresses webpack output\n const silentLogger = {\n info: () => {}, // Suppress info logs (webpack output)\n debug: () => {}, // Suppress debug logs\n warn: () => {}, // Suppress warnings if desired\n log: () => {}, // Suppress general logs\n trace: () => {}, // Suppress trace logs\n error: (message: string, meta?: any) => {\n // Keep error logs but forward to the main logger\n logger.error(message, meta);\n },\n };\n\n // Pre-bundle workflows with silent logger to suppress webpack output\n // https://github.com/temporalio/sdk-typescript/issues/1740\n const workflowBundle = await bundleWorkflowCode({\n workflowsPath: path.resolve(__dirname, \"scripts/workflow.js\"),\n logger: silentLogger,\n });\n\n const worker = await Worker.create({\n connection,\n namespace: config.temporalConfig.namespace,\n taskQueue: \"typescript-script-queue\",\n workflowBundle,\n activities: {\n ...activities,\n ...Object.fromEntries(\n dynamicActivities.map((activity) => [\n Object.keys(activity)[0],\n Object.values(activity)[0],\n ]),\n ),\n },\n });\n\n return worker;\n } catch (error) {\n logger.error(`Error registering workflows: ${error}`);\n throw error;\n }\n}\n\n/**\n * Start a Temporal worker that handles TypeScript script execution workflows.\n */\nexport async function runScripts(\n config: ScriptsConfig,\n): Promise<Worker | null> {\n const logger = initializeLogger();\n\n // Add process-level uncaught exception handler\n process.on(\"uncaughtException\", (error) => {\n console.error(`[PROCESS] Uncaught Exception: ${error}`);\n process.exit(1);\n });\n\n const worker = await registerWorkflows(logger, config);\n\n if (!worker) {\n logger.warn(\n `No workflows found. To disable workflow infrastructure, set workflows=false in moose.config.toml`,\n );\n process.exit(0);\n }\n\n let isShuttingDown = false;\n\n // Handle shutdown signals\n async function handleSignal(signal: string) {\n console.log(`[SHUTDOWN] Received ${signal}`);\n\n if (isShuttingDown) {\n return;\n }\n\n isShuttingDown = true;\n\n try {\n if (!worker) {\n process.exit(0);\n }\n await Promise.race([\n worker.shutdown(),\n new Promise((_, reject) =>\n setTimeout(() => reject(new Error(\"Shutdown timeout\")), 3000),\n ),\n ]);\n process.exit(0);\n } catch (error) {\n console.log(`[SHUTDOWN] Error: ${error}`);\n process.exit(1);\n }\n }\n\n // Register signal handlers immediately\n [\"SIGTERM\", \"SIGINT\", \"SIGHUP\", \"SIGQUIT\"].forEach((signal) => {\n process.on(signal, () => {\n handleSignal(signal).catch((error) => {\n console.log(`[SHUTDOWN] Error: ${error}`);\n process.exit(1);\n });\n });\n });\n\n logger.info(\"Starting TypeScript worker...\");\n try {\n await worker.run();\n } catch (error) {\n console.log(`[SHUTDOWN] Error: ${error}`);\n process.exit(1);\n }\n\n return worker;\n}\n","import { log as logger, Context } from \"@temporalio/activity\";\nimport { isCancellation } from \"@temporalio/workflow\";\nimport { Task, Workflow } from \"../dmv2\";\nimport { getWorkflows, getTaskForWorkflow } from \"../dmv2/internal\";\nimport { jsonDateReviver } from \"../utilities/json\";\nimport { taskContextStorage } from \"./task-context\";\n\nexport const activities = {\n async hasWorkflow(name: string): Promise<boolean> {\n try {\n const workflows = await getWorkflows();\n const hasWorkflow = workflows.has(name);\n logger.info(`Found workflow:: ${hasWorkflow}`);\n return hasWorkflow;\n } catch (error) {\n logger.error(`Failed to check if workflow ${name} exists: ${error}`);\n return false;\n }\n },\n\n async getWorkflowByName(name: string): Promise<Workflow> {\n try {\n logger.info(`Getting workflow ${name}`);\n\n const workflows = await getWorkflows();\n\n if (workflows.has(name)) {\n logger.info(`Workflow ${name} found`);\n return workflows.get(name)!;\n } else {\n const errorData = {\n error: \"Workflow not found\",\n details: `Workflow ${name} not found`,\n stack: undefined,\n };\n const errorMsg = JSON.stringify(errorData);\n logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n } catch (error) {\n const errorData = {\n error: \"Failed to get workflow\",\n details: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n const errorMsg = JSON.stringify(errorData);\n logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n },\n\n async getTaskForWorkflow(\n workflowName: string,\n taskName: string,\n ): Promise<Task<any, any>> {\n try {\n logger.info(`Getting task ${taskName} from workflow ${workflowName}`);\n const task = await getTaskForWorkflow(workflowName, taskName);\n logger.info(`Task ${taskName} found in workflow ${workflowName}`);\n return task;\n } catch (error) {\n const errorData = {\n error: \"Failed to get task\",\n details: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n const errorMsg = JSON.stringify(errorData);\n logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n },\n\n async executeTask(\n workflow: Workflow,\n task: Task<any, any>,\n inputData: any,\n ): Promise<any[]> {\n // Get context for heartbeat (required for cancellation detection)\n const context = Context.current();\n const taskState = {};\n const taskIdentifier = workflow.name;\n\n // Wrap entire executeTask in task context so ALL logs (framework + user) have task_name\n return await taskContextStorage.run(\n { taskName: taskIdentifier },\n async () => {\n // Periodic heartbeat is required for cancellation detection\n // https://docs.temporal.io/develop/typescript/cancellation#cancel-an-activity\n // - Temporal activities can only receive cancellation if they send heartbeats\n // - Heartbeats are the communication channel between activity and Temporal server\n // - Server sends cancellation signals back in heartbeat responses\n // - Without heartbeats, context.cancelled will never resolve and cancellation is impossible\n let heartbeatInterval: NodeJS.Timeout | null = null;\n const startPeriodicHeartbeat = () => {\n heartbeatInterval = setInterval(() => {\n context.heartbeat(`Task ${task.name} in progress`);\n }, 5000);\n };\n const stopPeriodicHeartbeat = () => {\n if (heartbeatInterval) {\n clearInterval(heartbeatInterval);\n heartbeatInterval = null;\n }\n };\n\n try {\n logger.info(\n `Task ${task.name} received input: ${JSON.stringify(inputData)}`,\n );\n\n // Send initial heartbeat to enable cancellation detection\n context.heartbeat(`Starting task: ${task.name}`);\n\n // Data between temporal workflow & activities are serialized so we\n // have to get it again to access the user's run function\n const fullTask = await getTaskForWorkflow(workflow.name, task.name);\n\n // Revive any JSON serialized dates in the input data\n const revivedInputData =\n inputData ?\n JSON.parse(JSON.stringify(inputData), jsonDateReviver)\n : inputData;\n\n try {\n startPeriodicHeartbeat();\n\n // Race user code against cancellation detection\n // - context.cancelled Promise rejects when server signals cancellation via heartbeat response\n // - This allows immediate cancellation detection rather than waiting for user code to finish\n // - If cancellation happens first, we catch it below and call onCancel cleanup\n const result = await Promise.race([\n fullTask.config.run({\n state: taskState,\n input: revivedInputData,\n }),\n context.cancelled,\n ]);\n return result;\n } catch (error) {\n if (isCancellation(error)) {\n logger.info(\n `Task ${task.name} cancelled, calling onCancel handler if it exists`,\n );\n if (fullTask.config.onCancel) {\n await fullTask.config.onCancel({\n state: taskState,\n input: revivedInputData,\n });\n }\n return [];\n } else {\n throw error;\n }\n } finally {\n stopPeriodicHeartbeat();\n }\n } catch (error) {\n const errorData = {\n error: \"Task execution failed\",\n details: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n const errorMsg = JSON.stringify(errorData);\n logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n },\n );\n },\n};\n\n// Helper function to create activity for a specific script\nexport function createActivityForScript(scriptName: string) {\n return {\n [scriptName]: activities.executeTask,\n };\n}\n","import { setupStructuredConsole } from \"../utils/structured-logging\";\n\n// Task context storage - shared across logger and activity modules\nexport const taskContextStorage = setupStructuredConsole<{ taskName: string }>(\n (ctx) => ctx.taskName,\n \"task_name\",\n);\n\n// Re-export constants for use in logger\nexport const TASK_CONTEXT_FIELD_NAME = \"task_name\";\nexport const getTaskContextField = (ctx: { taskName: string }) => ctx.taskName;\n","import {\n makeTelemetryFilterString,\n DefaultLogger,\n Runtime,\n} from \"@temporalio/worker\";\nimport { emitStructuredLog } from \"../utils/structured-logging\";\nimport {\n taskContextStorage,\n getTaskContextField,\n TASK_CONTEXT_FIELD_NAME,\n} from \"./task-context\";\n\nclass LoggerSingleton {\n private static instance: DefaultLogger | null = null;\n\n private constructor() {}\n\n public static initializeLogger(): DefaultLogger {\n if (!LoggerSingleton.instance) {\n LoggerSingleton.instance = new DefaultLogger(\n \"DEBUG\",\n ({ level, message }) => {\n const structuredLevel = level.toLowerCase();\n\n // Try to emit as structured log if in task context\n const emitted = emitStructuredLog(\n taskContextStorage,\n getTaskContextField,\n TASK_CONTEXT_FIELD_NAME,\n structuredLevel,\n message,\n );\n\n // If not in context, emit as regular log\n if (!emitted) {\n console.log(`${level} | ${message}`);\n }\n },\n );\n\n Runtime.install({\n logger: LoggerSingleton.instance,\n telemetryOptions: {\n logging: {\n filter: makeTelemetryFilterString({ core: \"INFO\", other: \"INFO\" }),\n forward: {},\n },\n },\n });\n }\n\n return LoggerSingleton.instance;\n }\n\n public static getInstance(): DefaultLogger {\n return LoggerSingleton.instance!;\n }\n}\n\nexport const initializeLogger = LoggerSingleton.initializeLogger;\n"],"mappings":"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","names":["sql","createHash","logger","createHash","apiKey","sql","getWorkflows","createClient","fs","isNestedType","parse","init_helpers","init_helpers","init_helpers","readFileSync","path","projectRoot","fs","path","fs","path","process","ids","identifier","program","process","path","getApis","getWorkflows","getWebApps","orderBy","outDir","count","readFileSync","join","path","resolve","toClientConfig","path","getApis","logError","getWebApps","getMooseUtils","resolve","Readable","KafkaJS","Buffer","process","http","Kafka","KafkaJS","logger","Buffer","Readable","cluster","resolve","path","fs","getWorkflows","logger","resolve","getWorkflows","readFileSync","join"]}
1
+ {"version":3,"sources":["../src/dmv2/utils/stackTrace.ts","../src/dmv2/typedBase.ts","../src/dataModels/dataModelTypes.ts","../src/dataModels/types.ts","../src/sqlHelpers.ts","../src/dmv2/sdk/olapTable.ts","../src/dmv2/sdk/stream.ts","../src/dmv2/sdk/workflow.ts","../src/dmv2/sdk/ingestApi.ts","../src/dmv2/sdk/consumptionApi.ts","../src/dmv2/sdk/ingestPipeline.ts","../src/dmv2/sdk/etlPipeline.ts","../src/dmv2/sdk/materializedView.ts","../src/dmv2/sdk/sqlResource.ts","../src/dmv2/sdk/view.ts","../src/dmv2/sdk/lifeCycle.ts","../src/dmv2/sdk/webApp.ts","../src/dmv2/registry.ts","../src/dmv2/index.ts","../src/browserCompatible.ts","../src/commons.ts","../src/secrets.ts","../src/consumption-apis/helpers.ts","../src/consumption-apis/webAppHelpers.ts","../src/scripts/task.ts","../src/clients/redisClient.ts","../src/config/configFile.ts","../src/config/runtime.ts","../src/consumption-apis/standalone.ts","../src/utilities/json.ts","../src/utilities/dataParser.ts","../src/utilities/index.ts","../src/connectors/dataSource.ts","../src/query-layer/sql-utils.ts","../src/query-layer/helpers.ts","../src/query-layer/query-model.ts","../src/query-layer/query-builder.ts","../src/query-layer/model-tools.ts","../src/consumption-apis/validation.ts","../src/query-layer/index.ts","../src/index.ts","../src/compiler-config.ts","../src/dmv2/utils/sourceFiles.ts","../src/dmv2/utils/index.ts","../src/dmv2/dependencyAnalysis.ts","../src/dmv2/internal.ts","../src/moose-runner.ts","../src/consumption-apis/runner.ts","../src/cluster-utils.ts","../src/utils/structured-logging.ts","../src/streaming-functions/runner.ts","../src/moduleExportSerializer.ts","../src/scripts/runner.ts","../src/scripts/activity.ts","../src/scripts/task-context.ts","../src/scripts/logger.ts"],"sourcesContent":["/**\n * Stack trace utilities for extracting source file information.\n *\n * This module provides functions for parsing stack traces to determine\n * where user code is located, filtering out internal library paths.\n */\n\n/**\n * Information extracted from a stack trace about source file location.\n */\nexport interface SourceFileInfo {\n /** The file path */\n file?: string;\n /** The line number (as a string) */\n line?: string;\n}\n\n/**\n * Source location with file, line, and column information.\n * Used for precise error location tracking.\n */\nexport interface SourceLocation {\n /** The file path */\n file: string;\n /** The line number */\n line: number;\n /** The column number (optional - may not always be available from stack trace) */\n column?: number;\n}\n\n/**\n * Check if a stack trace line should be skipped (internal/library code).\n * @internal\n */\nfunction shouldSkipStackLine(line: string): boolean {\n return (\n line.includes(\"node_modules\") || // Skip npm installed packages (prod)\n line.includes(\"node:internal\") || // Skip Node.js internals (modern format)\n line.includes(\"internal/modules\") || // Skip Node.js internals (older format)\n line.includes(\"ts-node\") || // Skip TypeScript execution\n line.includes(\"/ts-moose-lib/src/\") || // Skip dev/linked moose-lib src (Unix)\n line.includes(\"\\\\ts-moose-lib\\\\src\\\\\") || // Skip dev/linked moose-lib src (Windows)\n line.includes(\"/ts-moose-lib/dist/\") || // Skip dev/linked moose-lib dist (Unix)\n line.includes(\"\\\\ts-moose-lib\\\\dist\\\\\") // Skip dev/linked moose-lib dist (Windows)\n );\n}\n\n/**\n * Extract file path and line number from a stack trace line.\n * @internal\n */\nfunction parseStackLine(line: string): SourceFileInfo | undefined {\n const match =\n line.match(/\\((.*):(\\d+):(\\d+)\\)/) || line.match(/at (.*):(\\d+):(\\d+)/);\n if (match && match[1]) {\n return {\n file: match[1],\n line: match[2],\n };\n }\n return undefined;\n}\n\n/**\n * Extract source file information from a stack trace.\n * Works in both development (npm link) and production (npm install) environments.\n *\n * @param stack - The stack trace string from an Error object\n * @returns Object with file path and line number, or empty object if not found\n */\nexport function getSourceFileInfo(stack?: string): SourceFileInfo {\n if (!stack) return {};\n const lines = stack.split(\"\\n\");\n for (const line of lines) {\n if (shouldSkipStackLine(line)) continue;\n const info = parseStackLine(line);\n if (info) return info;\n }\n return {};\n}\n\n/**\n * Extracts source location (file, line, column) from a stack trace.\n *\n * Stack trace formats vary by environment:\n * - V8 (Node/Chrome): \" at Function (file.ts:10:15)\"\n * - SpiderMonkey (Firefox): \"Function@file.ts:10:15\"\n *\n * @param stack - Error stack trace string\n * @returns SourceLocation or undefined if parsing fails\n */\nexport function getSourceLocationFromStack(\n stack: string | undefined,\n): SourceLocation | undefined {\n if (!stack) return undefined;\n\n const lines = stack.split(\"\\n\");\n\n // Skip first line (error message) and internal frames\n for (const line of lines.slice(1)) {\n // Skip node_modules and internal moose-lib frames\n if (shouldSkipStackLine(line)) {\n continue;\n }\n\n // V8 format: \" at Function (file.ts:10:15)\" or \" at file.ts:10:15\"\n const v8Match = line.match(/at\\s+(?:.*?\\s+\\()?(.+):(\\d+):(\\d+)\\)?/);\n if (v8Match) {\n return {\n file: v8Match[1],\n line: parseInt(v8Match[2], 10),\n column: parseInt(v8Match[3], 10),\n };\n }\n\n // SpiderMonkey format: \"Function@file.ts:10:15\"\n const smMatch = line.match(/(?:.*@)?(.+):(\\d+):(\\d+)/);\n if (smMatch) {\n return {\n file: smMatch[1],\n line: parseInt(smMatch[2], 10),\n column: parseInt(smMatch[3], 10),\n };\n }\n }\n\n return undefined;\n}\n\n/**\n * Extract the first file path outside moose-lib internals from a stack trace.\n * Works in both development (npm link) and production (npm install) environments.\n *\n * @deprecated Use getSourceLocationFromStack instead\n * @param stack - The stack trace string from an Error object\n * @returns The first user-code file path, or undefined if not found\n */\nexport function getSourceFileFromStack(stack?: string): string | undefined {\n const location = getSourceLocationFromStack(stack);\n return location?.file;\n}\n","import type { IJsonSchemaCollection } from \"typia\";\nimport { Column } from \"../dataModels/dataModelTypes\";\nimport { getSourceFileInfo } from \"./utils/stackTrace\";\n\n/**\n * Type definition for typia validation functions\n */\nexport interface TypiaValidators<T> {\n /** Typia validator function: returns { success: boolean, data?: T, errors?: any[] } */\n validate?: (data: unknown) => { success: boolean; data?: T; errors?: any[] };\n /** Typia assert function: throws on validation failure, returns T on success */\n assert?: (data: unknown) => T;\n /** Typia is function: returns boolean indicating if data matches type T */\n is?: (data: unknown) => data is T;\n}\n\n/**\n * Base class for all typed Moose dmv2 resources (OlapTable, Stream, etc.).\n * Handles the storage and injection of schema information (JSON schema and Column array)\n * provided by the Moose compiler plugin.\n *\n * @template T The data type (interface or type alias) defining the schema of the resource.\n * @template C The specific configuration type for the resource (e.g., OlapConfig, StreamConfig).\n */\nexport class TypedBase<T, C> {\n /** The JSON schema representation of type T. Injected by the compiler plugin. */\n schema: IJsonSchemaCollection.IV3_1;\n /** The name assigned to this resource instance. */\n name: string;\n\n /** A dictionary mapping column names (keys of T) to their Column definitions. */\n columns: {\n [columnName in keyof Required<T>]: Column;\n };\n /** An array containing the Column definitions for this resource. Injected by the compiler plugin. */\n columnArray: Column[];\n\n /** The configuration object specific to this resource type. */\n config: C;\n\n /** Typia validation functions for type T. Injected by the compiler plugin for OlapTable. */\n validators?: TypiaValidators<T>;\n\n /** Optional metadata for the resource, always present as an object. */\n metadata!: { [key: string]: any };\n\n /**\n * Whether this resource allows extra fields beyond the defined columns.\n * When true, extra fields in payloads are passed through to streaming functions.\n * Injected by the compiler plugin when the type has an index signature.\n */\n allowExtraFields: boolean;\n\n /**\n * @internal Constructor intended for internal use by subclasses and the compiler plugin.\n * It expects the schema and columns to be provided, typically injected by the compiler.\n *\n * @param name The name for the resource instance.\n * @param config The configuration object for the resource.\n * @param schema The JSON schema for the resource's data type T (injected).\n * @param columns The array of Column definitions for T (injected).\n * @param allowExtraFields Whether extra fields are allowed (injected when type has index signature).\n */\n constructor(\n name: string,\n config: C,\n schema?: IJsonSchemaCollection.IV3_1,\n columns?: Column[],\n validators?: TypiaValidators<T>,\n allowExtraFields?: boolean,\n ) {\n if (schema === undefined || columns === undefined) {\n throw new Error(\n \"Supply the type param T so that the schema is inserted by the compiler plugin.\",\n );\n }\n\n this.schema = schema;\n this.columnArray = columns;\n const columnsObj = {} as any;\n columns.forEach((column) => {\n columnsObj[column.name] = column;\n });\n this.columns = columnsObj;\n\n this.name = name;\n this.config = config;\n this.validators = validators;\n this.allowExtraFields = allowExtraFields ?? false;\n\n // Always ensure metadata is an object and attach stackTrace (last 10 lines only)\n this.metadata =\n (config as any)?.metadata ? { ...(config as any).metadata } : {};\n\n if (!this.metadata.source) {\n const stack = new Error().stack;\n if (stack) {\n const info = getSourceFileInfo(stack);\n this.metadata.source = { file: info.file, line: info.line };\n }\n }\n }\n}\n","import ts from \"typescript\";\nimport { IdentifierBrandedString } from \"../sqlHelpers\";\n\nexport type EnumValues =\n | { name: string; value: { Int: number } }[]\n | { name: string; value: { String: string } }[];\nexport type DataEnum = { name: string; values: EnumValues };\nexport type Nested = { name: string; columns: Column[]; jwt: boolean };\nexport type ArrayType = { elementType: DataType; elementNullable: boolean };\nexport type NamedTupleType = { fields: Array<[string, DataType]> };\nexport type MapType = { keyType: DataType; valueType: DataType };\nexport type JsonOptions = {\n max_dynamic_paths?: number;\n max_dynamic_types?: number;\n typed_paths?: Array<[string, DataType]>;\n skip_paths?: string[];\n skip_regexps?: string[];\n};\nexport type DataType =\n | string\n | DataEnum\n | ArrayType\n | Nested\n | NamedTupleType\n | MapType\n | JsonOptions\n | { nullable: DataType };\nexport interface Column {\n name: IdentifierBrandedString;\n data_type: DataType;\n required: boolean;\n unique: false; // what is this for?\n primary_key: boolean;\n default: string | null;\n materialized: string | null;\n ttl: string | null;\n codec: string | null;\n annotations: [string, any][];\n comment: string | null;\n}\n\nexport interface DataModel {\n columns: Column[];\n name: string;\n}\n\nexport class UnknownType extends Error {\n t: ts.Type;\n fieldName: string;\n typeName: string;\n constructor(t: ts.Type, fieldName: string, typeName: string) {\n super();\n this.t = t;\n this.fieldName = fieldName;\n this.typeName = typeName;\n }\n}\n\nexport class NullType extends Error {\n fieldName: string;\n typeName: string;\n constructor(fieldName: string, typeName: string) {\n super();\n this.fieldName = fieldName;\n this.typeName = typeName;\n }\n}\n\nexport class UnsupportedEnum extends Error {\n enumName: string;\n constructor(enumName: string) {\n super();\n this.enumName = enumName;\n }\n}\n\nexport class UnsupportedFeature extends Error {\n featureName: string;\n constructor(featureName: string) {\n super();\n this.featureName = featureName;\n }\n}\n\nexport class IndexType extends Error {\n typeName: string;\n indexSignatures: string[];\n\n constructor(typeName: string, indexSignatures: string[]) {\n const explanation =\n \"Index signatures (e.g. [key: string]: value) are not supported in data models.\";\n\n const suggestion =\n \"Consider splitting this into separate types or using a single Record<K, V> type.\";\n\n const signatures = `Found index signatures: ${indexSignatures.join(\", \")}`;\n\n super(\n `${explanation}\\n\\nType: ${typeName}\\n\\n${signatures}\\n\\nSuggestion: ${suggestion}`,\n );\n\n this.typeName = typeName;\n this.indexSignatures = indexSignatures;\n }\n}\n\n/**\n * Type guard: is this DataType an Array(Nested(...))?\n * Uses the ArrayType and Nested types for type safety.\n */\nexport function isArrayNestedType(\n dt: DataType,\n): dt is ArrayType & { elementType: Nested } {\n return (\n typeof dt === \"object\" &&\n dt !== null &&\n (dt as ArrayType).elementType !== null &&\n typeof (dt as ArrayType).elementType === \"object\" &&\n (dt as ArrayType).elementType.hasOwnProperty(\"columns\") &&\n Array.isArray(((dt as ArrayType).elementType as Nested).columns)\n );\n}\n\n/**\n * Type guard: is this DataType a Nested struct (not array)?\n */\nexport function isNestedType(dt: DataType): dt is Nested {\n return (\n typeof dt === \"object\" &&\n dt !== null &&\n Array.isArray((dt as Nested).columns)\n );\n}\n","import { Pattern, TagBase } from \"typia/lib/tags\";\nimport { tags } from \"typia\";\n\nexport type ClickHousePrecision<P extends number> = {\n _clickhouse_precision?: P;\n};\n\nexport const DecimalRegex: \"^-?\\\\d+(\\\\.\\\\d+)?$\" = \"^-?\\\\d+(\\\\.\\\\d+)?$\";\n\nexport type ClickHouseDecimal<P extends number, S extends number> = {\n _clickhouse_precision?: P;\n _clickhouse_scale?: S;\n} & Pattern<typeof DecimalRegex>;\n\nexport type ClickHouseFixedStringSize<N extends number> = {\n _clickhouse_fixed_string_size?: N;\n};\n\n/**\n * FixedString(N) - Fixed-length string of exactly N bytes.\n *\n * ClickHouse stores exactly N bytes, padding shorter values with null bytes.\n * Values exceeding N bytes will throw an exception.\n *\n * Use for binary data: hashes, IP addresses, UUIDs, MAC addresses.\n *\n * @example\n * interface BinaryData {\n * md5_hash: string & FixedString<16>; // 16-byte MD5\n * sha256_hash: string & FixedString<32>; // 32-byte SHA256\n * }\n */\nexport type FixedString<N extends number> = string &\n ClickHouseFixedStringSize<N>;\n\nexport type ClickHouseByteSize<N extends number> = {\n _clickhouse_byte_size?: N;\n};\n\nexport type LowCardinality = {\n _LowCardinality?: true;\n};\n\n// ClickHouse-friendly helper aliases for clarity in user schemas\n// These are erased at compile time but guide the ClickHouse mapping logic.\nexport type DateTime = Date;\nexport type DateTime64<P extends number> = Date & ClickHousePrecision<P>;\n\nexport type DateTimeString = string & tags.Format<\"date-time\">;\n/**\n * JS Date objects cannot hold microsecond precision.\n * Use string as the runtime type to avoid losing information.\n */\nexport type DateTime64String<P extends number> = string &\n tags.Format<\"date-time\"> &\n ClickHousePrecision<P>;\n\n// Numeric convenience tags mirroring ClickHouse integer and float families\nexport type Float32 = number & ClickHouseFloat<\"float32\">;\nexport type Float64 = number & ClickHouseFloat<\"float64\">;\n\nexport type Int8 = number & ClickHouseInt<\"int8\">;\nexport type Int16 = number & ClickHouseInt<\"int16\">;\nexport type Int32 = number & ClickHouseInt<\"int32\">;\nexport type Int64 = number & ClickHouseInt<\"int64\">;\n\nexport type UInt8 = number & ClickHouseInt<\"uint8\">;\nexport type UInt16 = number & ClickHouseInt<\"uint16\">;\nexport type UInt32 = number & ClickHouseInt<\"uint32\">;\nexport type UInt64 = number & ClickHouseInt<\"uint64\">;\n\n// Decimal(P, S) annotation\nexport type Decimal<P extends number, S extends number> = string &\n ClickHouseDecimal<P, S>;\n\n/**\n * Attach compression codec to a column type.\n *\n * Any valid ClickHouse codec expression is allowed. ClickHouse validates the codec at runtime.\n *\n * @template T The base data type\n * @template CodecExpr The codec expression (single codec or chain)\n *\n * @example\n * interface Metrics {\n * // Single codec\n * log_blob: string & ClickHouseCodec<\"ZSTD(3)\">;\n *\n * // Codec chain (processed left-to-right)\n * timestamp: Date & ClickHouseCodec<\"Delta, LZ4\">;\n * temperature: number & ClickHouseCodec<\"Gorilla, ZSTD\">;\n *\n * // Specialized codecs\n * counter: number & ClickHouseCodec<\"DoubleDelta\">;\n *\n * // Can combine with other annotations\n * count: UInt64 & ClickHouseCodec<\"DoubleDelta, LZ4\">;\n * }\n */\nexport type ClickHouseCodec<CodecExpr extends string> = {\n _clickhouse_codec?: CodecExpr;\n};\n\nexport type ClickHouseFloat<Value extends \"float32\" | \"float64\"> = tags.Type<\n Value extends \"float32\" ? \"float\" : \"double\"\n>;\n\nexport type ClickHouseInt<\n Value extends\n | \"int8\"\n | \"int16\"\n | \"int32\"\n | \"int64\"\n // | \"int128\"\n // | \"int256\"\n | \"uint8\"\n | \"uint16\"\n | \"uint32\"\n | \"uint64\",\n // | \"uint128\"\n // | \"uint256\",\n> =\n Value extends \"int32\" | \"int64\" | \"uint32\" | \"uint64\" ? tags.Type<Value>\n : TagBase<{\n target: \"number\";\n kind: \"type\";\n value: Value;\n validate: Value extends \"int8\" ? \"-128 <= $input && $input <= 127\"\n : Value extends \"int16\" ? \"-32768 <= $input && $input <= 32767\"\n : Value extends \"uint8\" ? \"0 <= $input && $input <= 255\"\n : Value extends \"uint16\" ? \"0 <= $input && $input <= 65535\"\n : never;\n exclusive: true;\n schema: {\n type: \"integer\";\n };\n }>;\n\n/**\n * By default, nested objects map to the `Nested` type in clickhouse.\n * Write `nestedObject: AnotherInterfaceType & ClickHouseNamedTuple`\n * to map AnotherInterfaceType to the named tuple type.\n */\nexport type ClickHouseNamedTuple = {\n _clickhouse_mapped_type?: \"namedTuple\";\n};\n\nexport type ClickHouseJson<\n maxDynamicPaths extends number | undefined = undefined,\n maxDynamicTypes extends number | undefined = undefined,\n skipPaths extends string[] = [],\n skipRegexes extends string[] = [],\n> = {\n _clickhouse_mapped_type?: \"JSON\";\n _clickhouse_json_settings?: {\n maxDynamicPaths?: maxDynamicPaths;\n maxDynamicTypes?: maxDynamicTypes;\n skipPaths?: skipPaths;\n skipRegexes?: skipRegexes;\n };\n};\n\n// Geometry helper types\nexport type ClickHousePoint = [number, number] & {\n _clickhouse_mapped_type?: \"Point\";\n};\nexport type ClickHouseRing = ClickHousePoint[] & {\n _clickhouse_mapped_type?: \"Ring\";\n};\nexport type ClickHouseLineString = ClickHousePoint[] & {\n _clickhouse_mapped_type?: \"LineString\";\n};\nexport type ClickHouseMultiLineString = ClickHouseLineString[] & {\n _clickhouse_mapped_type?: \"MultiLineString\";\n};\nexport type ClickHousePolygon = ClickHouseRing[] & {\n _clickhouse_mapped_type?: \"Polygon\";\n};\nexport type ClickHouseMultiPolygon = ClickHousePolygon[] & {\n _clickhouse_mapped_type?: \"MultiPolygon\";\n};\n\n/**\n * typia may have trouble handling this type.\n * In which case, use {@link WithDefault} as a workaround\n *\n * @example\n * { field: number & ClickHouseDefault<\"0\"> }\n */\nexport type ClickHouseDefault<SqlExpression extends string> = {\n _clickhouse_default?: SqlExpression;\n};\n\n/**\n * @example\n * {\n * ...\n * timestamp: Date;\n * debugMessage: string & ClickHouseTTL<\"timestamp + INTERVAL 1 WEEK\">;\n * }\n */\nexport type ClickHouseTTL<SqlExpression extends string> = {\n _clickhouse_ttl?: SqlExpression;\n};\n\n/**\n * ClickHouse MATERIALIZED column annotation.\n * The column value is computed at INSERT time and physically stored.\n * Cannot be explicitly inserted by users.\n *\n * @example\n * interface Events {\n * eventTime: DateTime;\n * // Extract date component - computed and stored at insert time\n * eventDate: Date & ClickHouseMaterialized<\"toDate(event_time)\">;\n *\n * userId: string;\n * // Precompute hash for fast lookups\n * userHash: UInt64 & ClickHouseMaterialized<\"cityHash64(userId)\">;\n * }\n *\n * @remarks\n * - MATERIALIZED and DEFAULT are mutually exclusive\n * - Can be combined with ClickHouseCodec for compression\n * - Changing the expression modifies the column in-place (existing values preserved)\n */\nexport type ClickHouseMaterialized<SqlExpression extends string> = {\n _clickhouse_materialized?: SqlExpression;\n};\n\n/**\n * ClickHouse ALIAS column annotation.\n * The column value is computed on-the-fly at SELECT time and NOT physically stored.\n * Cannot be explicitly inserted by users.\n *\n * @example\n * interface Events {\n * eventTime: DateTime;\n * // Computed at query time, not stored on disk\n * eventDate: Date & ClickHouseAlias<\"toDate(event_time)\">;\n *\n * firstName: string;\n * lastName: string;\n * // Virtual computed column\n * fullName: string & ClickHouseAlias<\"concat(first_name, ' ', last_name)\">;\n * }\n *\n * @remarks\n * - ALIAS, MATERIALIZED, and DEFAULT are mutually exclusive\n * - ALIAS columns are NOT stored on disk (saves storage, costs CPU at query time)\n * - Cannot be used in ORDER BY, PRIMARY KEY, or PARTITION BY\n * - Can be combined with ClickHouseCodec (though rarely useful since not stored)\n */\nexport type ClickHouseAlias<SqlExpression extends string> = {\n _clickhouse_alias?: SqlExpression;\n};\n\n/**\n * See also {@link ClickHouseDefault}\n *\n * @example{ updated_at: WithDefault<Date, \"now()\"> }\n */\nexport type WithDefault<T, _SqlExpression extends string> = T;\n\n/**\n * ClickHouse table engine types supported by Moose.\n */\nexport enum ClickHouseEngines {\n MergeTree = \"MergeTree\",\n ReplacingMergeTree = \"ReplacingMergeTree\",\n SummingMergeTree = \"SummingMergeTree\",\n AggregatingMergeTree = \"AggregatingMergeTree\",\n CollapsingMergeTree = \"CollapsingMergeTree\",\n VersionedCollapsingMergeTree = \"VersionedCollapsingMergeTree\",\n GraphiteMergeTree = \"GraphiteMergeTree\",\n S3Queue = \"S3Queue\",\n S3 = \"S3\",\n Buffer = \"Buffer\",\n Distributed = \"Distributed\",\n IcebergS3 = \"IcebergS3\",\n Kafka = \"Kafka\",\n Merge = \"Merge\",\n ReplicatedMergeTree = \"ReplicatedMergeTree\",\n ReplicatedReplacingMergeTree = \"ReplicatedReplacingMergeTree\",\n ReplicatedAggregatingMergeTree = \"ReplicatedAggregatingMergeTree\",\n ReplicatedSummingMergeTree = \"ReplicatedSummingMergeTree\",\n ReplicatedCollapsingMergeTree = \"ReplicatedCollapsingMergeTree\",\n ReplicatedVersionedCollapsingMergeTree = \"ReplicatedVersionedCollapsingMergeTree\",\n}\n","// source https://github.com/blakeembrey/sql-template-tag/blob/main/src/index.ts\nimport { Column } from \"./dataModels/dataModelTypes\";\nimport { OlapTable, View } from \"./dmv2\";\n\nimport { AggregationFunction } from \"./dataModels/typeConvert\";\n\n/**\n * Quote a ClickHouse identifier with backticks if not already quoted.\n * Backticks allow special characters (e.g., hyphens) in identifiers.\n */\nexport const quoteIdentifier = (name: string): string => {\n return name.startsWith(\"`\") && name.endsWith(\"`\") ? name : `\\`${name}\\``;\n};\n\nconst isTable = (\n value: RawValue | Column | OlapTable<any> | View,\n): value is OlapTable<any> =>\n typeof value === \"object\" &&\n value !== null &&\n \"kind\" in value &&\n value.kind === \"OlapTable\";\n\nconst isView = (\n value: RawValue | Column | OlapTable<any> | View,\n): value is View =>\n typeof value === \"object\" &&\n value !== null &&\n \"kind\" in value &&\n value.kind === \"View\";\n\nexport type IdentifierBrandedString = string & {\n readonly __identifier_brand?: unique symbol;\n};\nexport type NonIdentifierBrandedString = string & {\n readonly __identifier_brand?: unique symbol;\n};\n\n/**\n * Values supported by SQL engine.\n */\nexport type Value =\n | NonIdentifierBrandedString\n | number\n | boolean\n | Date\n | [string, string];\n\n/**\n * Supported value or SQL instance.\n */\nexport type RawValue = Value | Sql;\n\nconst isColumn = (\n value: RawValue | Column | OlapTable<any> | View,\n): value is Column =>\n typeof value === \"object\" &&\n value !== null &&\n !(\"kind\" in value) &&\n \"name\" in value &&\n \"annotations\" in value;\n\n/**\n * Sql template tag interface with attached helper methods.\n */\nexport interface SqlTemplateTag {\n /**\n * @deprecated Use `sql.statement` for full SQL statements or `sql.fragment` for SQL fragments.\n */\n (\n strings: readonly string[],\n ...values: readonly (RawValue | Column | OlapTable<any> | View)[]\n ): Sql;\n\n /**\n * Template literal tag for complete SQL statements (e.g. SELECT, INSERT, CREATE).\n * Produces a Sql instance with `isFragment = false`.\n */\n statement(\n strings: readonly string[],\n ...values: readonly (RawValue | Column | OlapTable<any> | View)[]\n ): Sql;\n\n /**\n * Template literal tag for SQL fragments (e.g. expressions, conditions, partial clauses).\n * Produces a Sql instance with `isFragment = true`.\n */\n fragment(\n strings: readonly string[],\n ...values: readonly (RawValue | Column | OlapTable<any> | View)[]\n ): Sql;\n\n /**\n * Join an array of Sql fragments with a separator.\n * @param fragments - Array of Sql fragments to join\n * @param separator - Optional separator string (defaults to \", \")\n */\n join(fragments: Sql[], separator?: string): Sql;\n\n /**\n * Create raw SQL from a string without parameterization.\n * WARNING: SQL injection risk if used with untrusted input.\n */\n raw(text: string): Sql;\n}\n\nfunction sqlImpl(\n strings: readonly string[],\n ...values: readonly (RawValue | Column | OlapTable<any> | View)[]\n): Sql {\n return new Sql(strings, values);\n}\n\nexport const sql: SqlTemplateTag = sqlImpl as SqlTemplateTag;\n\nsql.statement = function (\n strings: readonly string[],\n ...values: readonly (RawValue | Column | OlapTable<any> | View)[]\n): Sql {\n return new Sql(strings, values, false);\n};\n\nsql.fragment = function (\n strings: readonly string[],\n ...values: readonly (RawValue | Column | OlapTable<any> | View)[]\n): Sql {\n return new Sql(strings, values, true);\n};\n\nconst instanceofSql = (\n value: RawValue | Column | OlapTable<any> | View,\n): value is Sql =>\n typeof value === \"object\" && \"values\" in value && \"strings\" in value;\n\n/**\n * A SQL instance can be nested within each other to build SQL strings.\n */\nexport class Sql {\n readonly values: Value[];\n readonly strings: string[];\n readonly isFragment: boolean | undefined;\n\n constructor(\n rawStrings: readonly string[],\n rawValues: readonly (RawValue | Column | OlapTable<any> | View | Sql)[],\n isFragment?: boolean,\n ) {\n if (rawStrings.length - 1 !== rawValues.length) {\n if (rawStrings.length === 0) {\n throw new TypeError(\"Expected at least 1 string\");\n }\n\n throw new TypeError(\n `Expected ${rawStrings.length} strings to have ${\n rawStrings.length - 1\n } values`,\n );\n }\n\n const valuesLength = rawValues.reduce<number>(\n (len: number, value: RawValue | Column | OlapTable<any> | View | Sql) =>\n len +\n (instanceofSql(value) ? value.values.length\n : isColumn(value) || isTable(value) || isView(value) ? 0\n : 1),\n 0,\n );\n\n this.values = new Array(valuesLength);\n this.strings = new Array(valuesLength + 1);\n this.isFragment = isFragment;\n\n this.strings[0] = rawStrings[0];\n\n // Iterate over raw values, strings, and children. The value is always\n // positioned between two strings, e.g. `index + 1`.\n let i = 0,\n pos = 0;\n while (i < rawValues.length) {\n const child = rawValues[i++];\n const rawString = rawStrings[i];\n\n // Check for nested `sql` queries.\n if (instanceofSql(child)) {\n // Append child prefix text to current string.\n this.strings[pos] += child.strings[0];\n\n let childIndex = 0;\n while (childIndex < child.values.length) {\n this.values[pos++] = child.values[childIndex++];\n this.strings[pos] = child.strings[childIndex];\n }\n\n // Append raw string to current string.\n this.strings[pos] += rawString;\n } else if (isColumn(child)) {\n const aggregationFunction = child.annotations.find(\n ([k, _]) => k === \"aggregationFunction\",\n );\n if (aggregationFunction !== undefined) {\n const funcName = (aggregationFunction[1] as AggregationFunction)\n .functionName;\n const parenIdx = funcName.indexOf(\"(\");\n const mergedName =\n parenIdx !== -1 ?\n `${funcName.slice(0, parenIdx)}Merge${funcName.slice(parenIdx)}`\n : `${funcName}Merge`;\n this.strings[pos] += `${mergedName}(\\`${child.name}\\`)`;\n } else {\n this.strings[pos] += `\\`${child.name}\\``;\n }\n this.strings[pos] += rawString;\n } else if (isTable(child)) {\n if (child.config.database) {\n this.strings[pos] += `\\`${child.config.database}\\`.\\`${child.name}\\``;\n } else {\n this.strings[pos] += `\\`${child.name}\\``;\n }\n this.strings[pos] += rawString;\n } else if (isView(child)) {\n this.strings[pos] += `\\`${child.name}\\``;\n this.strings[pos] += rawString;\n } else {\n this.values[pos++] = child;\n this.strings[pos] = rawString;\n }\n }\n }\n\n /**\n * Append another Sql fragment, returning a new Sql instance.\n */\n append(other: Sql): Sql {\n return new Sql(\n [...this.strings, \"\"],\n [...this.values, other],\n this.isFragment,\n );\n }\n}\n\nsql.join = function (fragments: Sql[], separator?: string): Sql {\n if (fragments.length === 0) return new Sql([\"\"], [], true);\n if (fragments.length === 1) {\n const frag = fragments[0];\n return new Sql(frag.strings, frag.values, true);\n }\n const sep = separator ?? \", \";\n const normalized = sep.includes(\" \") ? sep : ` ${sep} `;\n const strings = [\"\", ...Array(fragments.length - 1).fill(normalized), \"\"];\n return new Sql(strings, fragments, true);\n};\n\nsql.raw = function (text: string): Sql {\n return new Sql([text], [], true);\n};\n\nexport const toStaticQuery = (sql: Sql): string => {\n const [query, params] = toQuery(sql);\n if (Object.keys(params).length !== 0) {\n throw new Error(\n \"Dynamic SQL is not allowed in the select statement in view creation.\",\n );\n }\n return query;\n};\n\nexport const toQuery = (sql: Sql): [string, { [pN: string]: any }] => {\n const parameterizedStubs = sql.values.map((v, i) =>\n createClickhouseParameter(i, v),\n );\n\n const query = sql.strings\n .map((s, i) =>\n s != \"\" ? `${s}${emptyIfUndefined(parameterizedStubs[i])}` : \"\",\n )\n .join(\"\");\n\n const query_params = sql.values.reduce(\n (acc: Record<string, unknown>, v, i) => ({\n ...acc,\n [`p${i}`]: getValueFromParameter(v),\n }),\n {},\n );\n return [query, query_params];\n};\n\n/**\n * Build a display-only SQL string with values inlined for logging/debugging.\n * Does not alter execution behavior; use toQuery for actual execution.\n */\nexport const toQueryPreview = (sql: Sql): string => {\n try {\n const formatValue = (v: Value): string => {\n // Unwrap identifiers: [\"Identifier\", name]\n if (Array.isArray(v)) {\n const [type, val] = v as unknown as [string, any];\n if (type === \"Identifier\") {\n // Quote identifiers with backticks like other helpers\n return `\\`${String(val)}\\``;\n }\n // Fallback for unexpected arrays\n return `[${(v as unknown as any[]).map((x) => formatValue(x as Value)).join(\", \")}]`;\n }\n if (v === null || v === undefined) return \"NULL\";\n if (typeof v === \"string\") return `'${v.replace(/'/g, \"''\")}'`;\n if (typeof v === \"number\") return String(v);\n if (typeof v === \"boolean\") return v ? \"true\" : \"false\";\n if (v instanceof Date)\n return `'${v.toISOString().replace(\"T\", \" \").slice(0, 19)}'`;\n try {\n return JSON.stringify(v as unknown as any);\n } catch {\n return String(v);\n }\n };\n\n let out = sql.strings[0] ?? \"\";\n for (let i = 0; i < sql.values.length; i++) {\n const val = getValueFromParameter(sql.values[i] as any);\n out += formatValue(val as Value);\n out += sql.strings[i + 1] ?? \"\";\n }\n return out.replace(/\\s+/g, \" \").trim();\n } catch (error) {\n console.log(`toQueryPreview error: ${error}`);\n return \"/* query preview unavailable */\";\n }\n};\n\nexport const getValueFromParameter = (value: any) => {\n if (Array.isArray(value)) {\n const [type, val] = value;\n if (type === \"Identifier\") return val;\n }\n return value;\n};\nexport function createClickhouseParameter(\n parameterIndex: number,\n value: Value,\n) {\n // ClickHouse use {name:type} be a placeholder, so if we only use number string as name e.g: {1:Unit8}\n // it will face issue when converting to the query params => {1: value1}, because the key is value not string type, so here add prefix \"p\" to avoid this issue.\n return `{p${parameterIndex}:${mapToClickHouseType(value)}}`;\n}\n\n/**\n * Convert the JS type (source is JSON format by API query parameter) to the corresponding ClickHouse type for generating named placeholder of parameterized query.\n * Only support to convert number to Int or Float, boolean to Bool, string to String, other types will convert to String.\n * If exist complex type e.g: object, Array, null, undefined, Date, Record.. etc, just convert to string type by ClickHouse function in SQL.\n * ClickHouse support converting string to other types function.\n * Please see Each section of the https://clickhouse.com/docs/en/sql-reference/functions and https://clickhouse.com/docs/en/sql-reference/functions/type-conversion-functions\n * @param value\n * @returns 'Float', 'Int', 'Bool', 'String'\n */\nexport const mapToClickHouseType = (value: Value) => {\n if (typeof value === \"number\") {\n // infer the float or int according to exist remainder or not\n return Number.isInteger(value) ? \"Int\" : \"Float\";\n }\n // When define column type or query result with parameterized query, The Bool or Boolean type both supported.\n // But the column type of query result only return Bool, so we only support Bool type for safety.\n if (typeof value === \"boolean\") return \"Bool\";\n if (value instanceof Date) return \"DateTime\";\n if (Array.isArray(value)) {\n const [type, _] = value;\n return type;\n }\n return \"String\";\n};\nfunction emptyIfUndefined(value: string | undefined): string {\n return value === undefined ? \"\" : value;\n}\n","import { IJsonSchemaCollection } from \"typia\";\nimport { TypedBase, TypiaValidators } from \"../typedBase\";\nimport {\n Column,\n isArrayNestedType,\n isNestedType,\n} from \"../../dataModels/dataModelTypes\";\nimport { ClickHouseEngines } from \"../../dataModels/types\";\nimport { getMooseInternal, isClientOnlyMode } from \"../internal\";\nimport { Readable } from \"node:stream\";\nimport { createHash } from \"node:crypto\";\nimport type {\n ConfigurationRegistry,\n RuntimeClickHouseConfig,\n} from \"../../config/runtime\";\nimport { LifeCycle } from \"./lifeCycle\";\nimport { IdentifierBrandedString, quoteIdentifier } from \"../../sqlHelpers\";\nimport type { NodeClickHouseClient } from \"@clickhouse/client/dist/client\";\n\nexport interface TableIndex {\n name: string;\n expression: string;\n type: string;\n arguments?: string[];\n granularity?: number;\n}\n\nexport interface TableProjection {\n name: string;\n body: string;\n}\n\n/**\n * Represents a failed record during insertion with error details\n */\nexport interface FailedRecord<T> {\n /** The original record that failed to insert */\n record: T;\n /** The error message describing why the insertion failed */\n error: string;\n /** Optional: The index of this record in the original batch */\n index?: number;\n}\n\n/**\n * Result of an insert operation with detailed success/failure information\n */\nexport interface InsertResult<T> {\n /** Number of records successfully inserted */\n successful: number;\n /** Number of records that failed to insert */\n failed: number;\n /** Total number of records processed */\n total: number;\n /** Detailed information about failed records (if record isolation was used) */\n failedRecords?: FailedRecord<T>[];\n}\n\n/**\n * Error handling strategy for insert operations\n */\nexport type ErrorStrategy =\n | \"fail-fast\" // Fail immediately on any error (default)\n | \"discard\" // Discard bad records and continue with good ones\n | \"isolate\"; // Retry individual records to isolate failures\n\n/**\n * Options for insert operations\n */\nexport interface InsertOptions {\n /** Maximum number of bad records to tolerate before failing */\n allowErrors?: number;\n /** Maximum ratio of bad records to tolerate (0.0 to 1.0) before failing */\n allowErrorsRatio?: number;\n /** Error handling strategy */\n strategy?: ErrorStrategy;\n /** Whether to enable dead letter queue for failed records (future feature) */\n deadLetterQueue?: boolean;\n /** Whether to validate data against schema before insertion (default: true) */\n validate?: boolean;\n /** Whether to skip validation for individual records during 'isolate' strategy retries (default: false) */\n skipValidationOnRetry?: boolean;\n}\n\n/**\n * Validation result for a record with detailed error information\n */\nexport interface ValidationError {\n /** The original record that failed validation */\n record: any;\n /** Detailed validation error message */\n error: string;\n /** Optional: The index of this record in the original batch */\n index?: number;\n /** The path to the field that failed validation */\n path?: string;\n}\n\n/**\n * Result of data validation with success/failure breakdown\n */\nexport interface ValidationResult<T> {\n /** Records that passed validation */\n valid: T[];\n /** Records that failed validation with detailed error information */\n invalid: ValidationError[];\n /** Total number of records processed */\n total: number;\n}\n\n/**\n * S3Queue-specific table settings that can be modified with ALTER TABLE MODIFY SETTING\n * Note: Since ClickHouse 24.7, settings no longer require the 's3queue_' prefix\n */\nexport interface S3QueueTableSettings {\n /** Processing mode: \"ordered\" for sequential or \"unordered\" for parallel processing */\n mode?: \"ordered\" | \"unordered\";\n /** What to do with files after processing: 'keep' or 'delete' */\n after_processing?: \"keep\" | \"delete\";\n /** ZooKeeper/Keeper path for coordination between replicas */\n keeper_path?: string;\n /** Number of retry attempts for failed files */\n loading_retries?: string;\n /** Number of threads for parallel processing */\n processing_threads_num?: string;\n /** Enable parallel inserts */\n parallel_inserts?: string;\n /** Enable logging to system.s3queue_log table */\n enable_logging_to_queue_log?: string;\n /** Last processed file path (for ordered mode) */\n last_processed_path?: string;\n /** Maximum number of tracked files in ZooKeeper */\n tracked_files_limit?: string;\n /** TTL for tracked files in seconds */\n tracked_file_ttl_sec?: string;\n /** Minimum polling timeout in milliseconds */\n polling_min_timeout_ms?: string;\n /** Maximum polling timeout in milliseconds */\n polling_max_timeout_ms?: string;\n /** Polling backoff in milliseconds */\n polling_backoff_ms?: string;\n /** Minimum cleanup interval in milliseconds */\n cleanup_interval_min_ms?: string;\n /** Maximum cleanup interval in milliseconds */\n cleanup_interval_max_ms?: string;\n /** Number of buckets for sharding (0 = disabled) */\n buckets?: string;\n /** Batch size for listing objects */\n list_objects_batch_size?: string;\n /** Enable hash ring filtering for distributed processing */\n enable_hash_ring_filtering?: string;\n /** Maximum files to process before committing */\n max_processed_files_before_commit?: string;\n /** Maximum rows to process before committing */\n max_processed_rows_before_commit?: string;\n /** Maximum bytes to process before committing */\n max_processed_bytes_before_commit?: string;\n /** Maximum processing time in seconds before committing */\n max_processing_time_sec_before_commit?: string;\n /** Use persistent processing nodes (available from 25.8) */\n use_persistent_processing_nodes?: string;\n /** TTL for persistent processing nodes in seconds */\n persistent_processing_nodes_ttl_seconds?: string;\n /** Additional settings */\n [key: string]: string | undefined;\n}\n\n/**\n * Base configuration shared by all table engines\n * @template T The data type of the records stored in the table.\n */\n\nexport type BaseOlapConfig<T> = (\n | {\n /**\n * Specifies the fields to use for ordering data within the ClickHouse table.\n * This is crucial for optimizing query performance.\n */\n orderByFields: (keyof T & string)[];\n orderByExpression?: undefined;\n }\n | {\n orderByFields?: undefined;\n /**\n * An arbitrary ClickHouse SQL expression for the order by clause.\n *\n * `orderByExpression: \"(id, name)\"` is equivalent to `orderByFields: [\"id\", \"name\"]`\n * `orderByExpression: \"tuple()\"` means no sorting\n */\n orderByExpression: string;\n }\n // specify either or leave both unspecified\n | { orderByFields?: undefined; orderByExpression?: undefined }\n) & {\n partitionBy?: string;\n /**\n * SAMPLE BY expression for approximate query processing.\n *\n * Examples:\n * ```typescript\n * // Single unsigned integer field\n * sampleByExpression: \"userId\"\n *\n * // Hash function on any field type\n * sampleByExpression: \"cityHash64(id)\"\n *\n * // Multiple fields with hash\n * sampleByExpression: \"cityHash64(userId, timestamp)\"\n * ```\n *\n * Requirements:\n * - Expression must evaluate to an unsigned integer (UInt8/16/32/64)\n * - Expression must be present in the ORDER BY clause\n * - If using hash functions, the same expression must appear in orderByExpression\n */\n sampleByExpression?: string;\n /**\n * Optional PRIMARY KEY expression.\n * When specified, this overrides the primary key inferred from Key<T> column annotations.\n *\n * This allows for:\n * - Complex primary keys using functions (e.g., \"cityHash64(id)\")\n * - Different column ordering in primary key vs schema definition\n * - Primary keys that differ from ORDER BY\n *\n * Example: primaryKeyExpression: \"(userId, cityHash64(eventId))\"\n *\n * Note: When this is set, any Key<T> annotations on columns are ignored for PRIMARY KEY generation.\n */\n primaryKeyExpression?: string;\n version?: string;\n lifeCycle?: LifeCycle;\n settings?: { [key: string]: string };\n /**\n * Optional TTL configuration for the table.\n * e.g., \"TTL timestamp + INTERVAL 90 DAY DELETE\"\n *\n * Use the {@link ClickHouseTTL} type to configure column level TTL\n */\n ttl?: string;\n /** Optional secondary/data-skipping indexes */\n indexes?: TableIndex[];\n /** Optional projections for alternative data ordering within parts */\n projections?: TableProjection[];\n /**\n * Optional database name for multi-database support.\n * When not specified, uses the global ClickHouse config database.\n */\n database?: string;\n /**\n * Optional cluster name for ON CLUSTER support.\n * Use this to enable replicated tables across ClickHouse clusters.\n * The cluster must be defined in config.toml (dev environment only).\n * Example: cluster: \"prod_cluster\"\n */\n cluster?: string;\n /**\n * Optional seed filter applied when `moose seed clickhouse` populates a\n * local/testing database from a remote source.\n *\n * Example:\n * ```typescript\n * seedFilter: { limit: 100, where: \"user_id = 10\" }\n * ```\n */\n seedFilter?: {\n /** Maximum number of rows to seed for this table. */\n limit?: number;\n /** ClickHouse SQL WHERE expression to filter seeded rows. */\n where?: string;\n };\n};\n\n/**\n * Configuration for MergeTree engine\n * @template T The data type of the records stored in the table.\n */\nexport type MergeTreeConfig<T> = BaseOlapConfig<T> & {\n engine: ClickHouseEngines.MergeTree;\n};\n\n/**\n * Configuration for ReplacingMergeTree engine (deduplication)\n * @template T The data type of the records stored in the table.\n */\nexport type ReplacingMergeTreeConfig<T> = BaseOlapConfig<T> & {\n engine: ClickHouseEngines.ReplacingMergeTree;\n ver?: keyof T & string; // Optional version column\n isDeleted?: keyof T & string; // Optional is_deleted column\n};\n\n/**\n * Configuration for AggregatingMergeTree engine\n * @template T The data type of the records stored in the table.\n */\nexport type AggregatingMergeTreeConfig<T> = BaseOlapConfig<T> & {\n engine: ClickHouseEngines.AggregatingMergeTree;\n};\n\n/**\n * Configuration for SummingMergeTree engine\n * @template T The data type of the records stored in the table.\n */\nexport type SummingMergeTreeConfig<T> = BaseOlapConfig<T> & {\n engine: ClickHouseEngines.SummingMergeTree;\n columns?: string[];\n};\n\n/**\n * Configuration for CollapsingMergeTree engine\n * @template T The data type of the records stored in the table.\n */\nexport type CollapsingMergeTreeConfig<T> = BaseOlapConfig<T> & {\n engine: ClickHouseEngines.CollapsingMergeTree;\n sign: keyof T & string; // Sign column (1 = state, -1 = cancel)\n};\n\n/**\n * Configuration for VersionedCollapsingMergeTree engine\n * @template T The data type of the records stored in the table.\n */\nexport type VersionedCollapsingMergeTreeConfig<T> = BaseOlapConfig<T> & {\n engine: ClickHouseEngines.VersionedCollapsingMergeTree;\n sign: keyof T & string; // Sign column (1 = state, -1 = cancel)\n ver: keyof T & string; // Version column for ordering state changes\n};\n\ninterface ReplicatedEngineProperties {\n keeperPath?: string;\n replicaName?: string;\n}\n\n/**\n * Configuration for ReplicatedMergeTree engine\n * @template T The data type of the records stored in the table.\n *\n * Note: keeperPath and replicaName are optional. Omit them for ClickHouse Cloud,\n * which manages replication automatically. For self-hosted with ClickHouse Keeper,\n * provide both parameters or neither (to use server defaults).\n */\nexport type ReplicatedMergeTreeConfig<T> = Omit<MergeTreeConfig<T>, \"engine\"> &\n ReplicatedEngineProperties & {\n engine: ClickHouseEngines.ReplicatedMergeTree;\n };\n\n/**\n * Configuration for ReplicatedReplacingMergeTree engine\n * @template T The data type of the records stored in the table.\n *\n * Note: keeperPath and replicaName are optional. Omit them for ClickHouse Cloud,\n * which manages replication automatically. For self-hosted with ClickHouse Keeper,\n * provide both parameters or neither (to use server defaults).\n */\nexport type ReplicatedReplacingMergeTreeConfig<T> = Omit<\n ReplacingMergeTreeConfig<T>,\n \"engine\"\n> &\n ReplicatedEngineProperties & {\n engine: ClickHouseEngines.ReplicatedReplacingMergeTree;\n };\n\n/**\n * Configuration for ReplicatedAggregatingMergeTree engine\n * @template T The data type of the records stored in the table.\n *\n * Note: keeperPath and replicaName are optional. Omit them for ClickHouse Cloud,\n * which manages replication automatically. For self-hosted with ClickHouse Keeper,\n * provide both parameters or neither (to use server defaults).\n */\nexport type ReplicatedAggregatingMergeTreeConfig<T> = Omit<\n AggregatingMergeTreeConfig<T>,\n \"engine\"\n> &\n ReplicatedEngineProperties & {\n engine: ClickHouseEngines.ReplicatedAggregatingMergeTree;\n };\n\n/**\n * Configuration for ReplicatedSummingMergeTree engine\n * @template T The data type of the records stored in the table.\n *\n * Note: keeperPath and replicaName are optional. Omit them for ClickHouse Cloud,\n * which manages replication automatically. For self-hosted with ClickHouse Keeper,\n * provide both parameters or neither (to use server defaults).\n */\nexport type ReplicatedSummingMergeTreeConfig<T> = Omit<\n SummingMergeTreeConfig<T>,\n \"engine\"\n> &\n ReplicatedEngineProperties & {\n engine: ClickHouseEngines.ReplicatedSummingMergeTree;\n };\n\n/**\n * Configuration for ReplicatedCollapsingMergeTree engine\n * @template T The data type of the records stored in the table.\n *\n * Note: keeperPath and replicaName are optional. Omit them for ClickHouse Cloud,\n * which manages replication automatically. For self-hosted with ClickHouse Keeper,\n * provide both parameters or neither (to use server defaults).\n */\nexport type ReplicatedCollapsingMergeTreeConfig<T> = Omit<\n CollapsingMergeTreeConfig<T>,\n \"engine\"\n> &\n ReplicatedEngineProperties & {\n engine: ClickHouseEngines.ReplicatedCollapsingMergeTree;\n };\n\n/**\n * Configuration for ReplicatedVersionedCollapsingMergeTree engine\n * @template T The data type of the records stored in the table.\n *\n * Note: keeperPath and replicaName are optional. Omit them for ClickHouse Cloud,\n * which manages replication automatically. For self-hosted with ClickHouse Keeper,\n * provide both parameters or neither (to use server defaults).\n */\nexport type ReplicatedVersionedCollapsingMergeTreeConfig<T> = Omit<\n VersionedCollapsingMergeTreeConfig<T>,\n \"engine\"\n> &\n ReplicatedEngineProperties & {\n engine: ClickHouseEngines.ReplicatedVersionedCollapsingMergeTree;\n };\n\n/**\n * Configuration for S3Queue engine - only non-alterable constructor parameters.\n * S3Queue-specific settings like 'mode', 'keeper_path', etc. should be specified\n * in the settings field, not here.\n * @template T The data type of the records stored in the table.\n */\nexport type S3QueueConfig<T> = Omit<\n BaseOlapConfig<T>,\n | \"settings\"\n | \"orderByFields\"\n | \"partitionBy\"\n | \"sampleByExpression\"\n | \"projections\"\n> & {\n engine: ClickHouseEngines.S3Queue;\n /** S3 bucket path with wildcards (e.g., 's3://bucket/data/*.json') */\n s3Path: string;\n /** Data format (e.g., 'JSONEachRow', 'CSV', 'Parquet') */\n format: string;\n /** AWS access key ID (optional, omit for NOSIGN/public buckets) */\n awsAccessKeyId?: string;\n /** AWS secret access key */\n awsSecretAccessKey?: string;\n /** Compression type (e.g., 'gzip', 'zstd') */\n compression?: string;\n /** Custom HTTP headers */\n headers?: { [key: string]: string };\n /**\n * S3Queue-specific table settings that can be modified with ALTER TABLE MODIFY SETTING.\n * These settings control the behavior of the S3Queue engine.\n */\n settings?: S3QueueTableSettings;\n};\n\n/**\n * Configuration for S3 engine\n * Note: S3 engine supports ORDER BY clause, unlike S3Queue, Buffer, and Distributed engines\n * @template T The data type of the records stored in the table.\n */\nexport type S3Config<T> = Omit<\n BaseOlapConfig<T>,\n \"sampleByExpression\" | \"projections\"\n> & {\n engine: ClickHouseEngines.S3;\n /** S3 path (e.g., 's3://bucket/path/file.json') */\n path: string;\n /** Data format (e.g., 'JSONEachRow', 'CSV', 'Parquet') */\n format: string;\n /** AWS access key ID (optional, omit for NOSIGN/public buckets) */\n awsAccessKeyId?: string;\n /** AWS secret access key */\n awsSecretAccessKey?: string;\n /** Compression type (e.g., 'gzip', 'zstd', 'auto') */\n compression?: string;\n /** Partition strategy (optional) */\n partitionStrategy?: string;\n /** Partition columns in data file (optional) */\n partitionColumnsInDataFile?: string;\n};\n\n/**\n * Configuration for Buffer engine\n * @template T The data type of the records stored in the table.\n */\nexport type BufferConfig<T> = Omit<\n BaseOlapConfig<T>,\n | \"orderByFields\"\n | \"orderByExpression\"\n | \"partitionBy\"\n | \"sampleByExpression\"\n | \"projections\"\n> & {\n engine: ClickHouseEngines.Buffer;\n /** Target database name for the destination table */\n targetDatabase: string;\n /** Target table name where data will be flushed */\n targetTable: string;\n /** Number of buffer layers (typically 16) */\n numLayers: number;\n /** Minimum time in seconds before flushing */\n minTime: number;\n /** Maximum time in seconds before flushing */\n maxTime: number;\n /** Minimum number of rows before flushing */\n minRows: number;\n /** Maximum number of rows before flushing */\n maxRows: number;\n /** Minimum bytes before flushing */\n minBytes: number;\n /** Maximum bytes before flushing */\n maxBytes: number;\n /** Optional: Flush time in seconds */\n flushTime?: number;\n /** Optional: Flush number of rows */\n flushRows?: number;\n /** Optional: Flush number of bytes */\n flushBytes?: number;\n};\n\n/**\n * Configuration for Distributed engine\n * @template T The data type of the records stored in the table.\n */\nexport type DistributedConfig<T> = Omit<\n BaseOlapConfig<T>,\n | \"orderByFields\"\n | \"orderByExpression\"\n | \"partitionBy\"\n | \"sampleByExpression\"\n | \"projections\"\n> & {\n engine: ClickHouseEngines.Distributed;\n /** Cluster name from the ClickHouse configuration */\n cluster: string;\n /** Database name on the cluster */\n targetDatabase: string;\n /** Table name on the cluster */\n targetTable: string;\n /** Optional: Sharding key expression for data distribution */\n shardingKey?: string;\n /** Optional: Policy name for data distribution */\n policyName?: string;\n};\n\n/** Kafka table settings. See: https://clickhouse.com/docs/engines/table-engines/integrations/kafka */\nexport interface KafkaTableSettings {\n kafka_security_protocol?: \"PLAINTEXT\" | \"SSL\" | \"SASL_PLAINTEXT\" | \"SASL_SSL\";\n kafka_sasl_mechanism?:\n | \"GSSAPI\"\n | \"PLAIN\"\n | \"SCRAM-SHA-256\"\n | \"SCRAM-SHA-512\"\n | \"OAUTHBEARER\";\n kafka_sasl_username?: string;\n kafka_sasl_password?: string;\n kafka_schema?: string;\n kafka_num_consumers?: string;\n kafka_max_block_size?: string;\n kafka_skip_broken_messages?: string;\n kafka_commit_every_batch?: string;\n kafka_client_id?: string;\n kafka_poll_timeout_ms?: string;\n kafka_poll_max_batch_size?: string;\n kafka_flush_interval_ms?: string;\n kafka_consumer_reschedule_ms?: string;\n kafka_thread_per_consumer?: string;\n kafka_handle_error_mode?: \"default\" | \"stream\";\n kafka_commit_on_select?: string;\n kafka_max_rows_per_message?: string;\n kafka_compression_codec?: string;\n kafka_compression_level?: string;\n}\n\n/** Kafka engine for streaming data from Kafka topics. Additional settings go in `settings`. */\nexport type KafkaConfig<T> = Omit<\n BaseOlapConfig<T>,\n | \"orderByFields\"\n | \"orderByExpression\"\n | \"partitionBy\"\n | \"sampleByExpression\"\n | \"projections\"\n> & {\n engine: ClickHouseEngines.Kafka;\n brokerList: string;\n topicList: string;\n groupName: string;\n format: string;\n settings?: KafkaTableSettings;\n};\n\n/**\n * Configuration for IcebergS3 engine - read-only Iceberg table access\n *\n * Provides direct querying of Apache Iceberg tables stored on S3.\n * Data is not copied; queries stream directly from Parquet/ORC files.\n *\n * @template T The data type of the records stored in the table.\n *\n * @example\n * ```typescript\n * const lakeEvents = new OlapTable<Event>(\"lake_events\", {\n * engine: ClickHouseEngines.IcebergS3,\n * path: \"s3://datalake/events/\",\n * format: \"Parquet\",\n * awsAccessKeyId: mooseRuntimeEnv.get(\"AWS_ACCESS_KEY_ID\"),\n * awsSecretAccessKey: mooseRuntimeEnv.get(\"AWS_SECRET_ACCESS_KEY\")\n * });\n * ```\n *\n * @remarks\n * - IcebergS3 engine is read-only\n * - Does not support ORDER BY, PARTITION BY, or SAMPLE BY clauses\n * - Queries always see the latest Iceberg snapshot (with metadata cache)\n */\nexport type IcebergS3Config<T> = Omit<\n BaseOlapConfig<T>,\n | \"orderByFields\"\n | \"orderByExpression\"\n | \"partitionBy\"\n | \"sampleByExpression\"\n | \"projections\"\n> & {\n engine: ClickHouseEngines.IcebergS3;\n /** S3 path to Iceberg table root (e.g., 's3://bucket/warehouse/events/') */\n path: string;\n /** Data format - 'Parquet' or 'ORC' */\n format: \"Parquet\" | \"ORC\";\n /** AWS access key ID (optional, omit for NOSIGN/public buckets) */\n awsAccessKeyId?: string;\n /** AWS secret access key (optional) */\n awsSecretAccessKey?: string;\n /** Compression type (optional: 'gzip', 'zstd', 'auto') */\n compression?: string;\n};\n\n/**\n * Configuration for Merge engine - read-only view over multiple tables matching a regex pattern.\n *\n * @template T The data type of the records in the source tables.\n *\n * @example\n * ```typescript\n * const allEvents = new OlapTable<Event>(\"all_events\", {\n * engine: ClickHouseEngines.Merge,\n * sourceDatabase: \"currentDatabase()\",\n * tablesRegexp: \"^events_\\\\d+$\",\n * });\n * ```\n *\n * @remarks\n * - Merge engine is read-only; INSERT operations are not supported\n * - Cannot be used as a destination in IngestPipeline\n * - Does not support ORDER BY, PARTITION BY, or SAMPLE BY clauses\n */\nexport type MergeConfig<T> = Omit<\n BaseOlapConfig<T>,\n | \"orderByFields\"\n | \"orderByExpression\"\n | \"partitionBy\"\n | \"sampleByExpression\"\n | \"projections\"\n> & {\n engine: ClickHouseEngines.Merge;\n /** Database to scan for source tables (literal name, currentDatabase(), or REGEXP(...)) */\n sourceDatabase: string;\n /** Regex pattern to match table names in the source database */\n tablesRegexp: string;\n};\n\n/**\n * Legacy configuration (backward compatibility) - defaults to MergeTree engine\n * @template T The data type of the records stored in the table.\n */\nexport type LegacyOlapConfig<T> = BaseOlapConfig<T>;\n\ntype EngineConfig<T> =\n | MergeTreeConfig<T>\n | ReplacingMergeTreeConfig<T>\n | AggregatingMergeTreeConfig<T>\n | SummingMergeTreeConfig<T>\n | CollapsingMergeTreeConfig<T>\n | VersionedCollapsingMergeTreeConfig<T>\n | ReplicatedMergeTreeConfig<T>\n | ReplicatedReplacingMergeTreeConfig<T>\n | ReplicatedAggregatingMergeTreeConfig<T>\n | ReplicatedSummingMergeTreeConfig<T>\n | ReplicatedCollapsingMergeTreeConfig<T>\n | ReplicatedVersionedCollapsingMergeTreeConfig<T>\n | S3QueueConfig<T>\n | S3Config<T>\n | BufferConfig<T>\n | DistributedConfig<T>\n | IcebergS3Config<T>\n | KafkaConfig<T>\n | MergeConfig<T>;\n\n/**\n * Union of all engine-specific configurations (new API)\n * @template T The data type of the records stored in the table.\n */\nexport type OlapConfig<T> = EngineConfig<T> | LegacyOlapConfig<T>;\n\n/**\n * Represents an OLAP (Online Analytical Processing) table, typically corresponding to a ClickHouse table.\n * Provides a typed interface for interacting with the table.\n *\n * @template T The data type of the records stored in the table. The structure of T defines the table schema.\n */\nexport class OlapTable<T> extends TypedBase<T, OlapConfig<T>> {\n name: IdentifierBrandedString;\n\n /** @internal */\n public readonly kind = \"OlapTable\";\n\n /** @internal Memoized ClickHouse client for reusing connections across insert calls */\n private _memoizedClient?: any;\n /** @internal Hash of the configuration used to create the memoized client */\n private _configHash?: string;\n /** @internal Cached table name to avoid repeated generation */\n private _cachedTableName?: string;\n\n /**\n * Creates a new OlapTable instance.\n * @param name The name of the table. This name is used for the underlying ClickHouse table.\n * @param config Optional configuration for the OLAP table.\n */\n constructor(name: string, config?: OlapConfig<T>);\n\n /** @internal **/\n constructor(\n name: string,\n config: OlapConfig<T>,\n schema: IJsonSchemaCollection.IV3_1,\n columns: Column[],\n validators?: TypiaValidators<T>,\n );\n\n constructor(\n name: string,\n config?: OlapConfig<T>,\n schema?: IJsonSchemaCollection.IV3_1,\n columns?: Column[],\n validators?: TypiaValidators<T>,\n ) {\n // Handle legacy configuration by defaulting to MergeTree when no engine is specified\n const resolvedConfig =\n config ?\n \"engine\" in config ?\n config\n : { ...config, engine: ClickHouseEngines.MergeTree }\n : { engine: ClickHouseEngines.MergeTree };\n\n // Enforce mutual exclusivity at runtime as well\n const hasFields =\n Array.isArray((resolvedConfig as any).orderByFields) &&\n (resolvedConfig as any).orderByFields.length > 0;\n const hasExpr =\n typeof (resolvedConfig as any).orderByExpression === \"string\" &&\n (resolvedConfig as any).orderByExpression.length > 0;\n if (hasFields && hasExpr) {\n throw new Error(\n `OlapTable ${name}: Provide either orderByFields or orderByExpression, not both.`,\n );\n }\n\n // Validate cluster and explicit replication params are not both specified\n const hasCluster = typeof (resolvedConfig as any).cluster === \"string\";\n const hasKeeperPath =\n typeof (resolvedConfig as any).keeperPath === \"string\";\n const hasReplicaName =\n typeof (resolvedConfig as any).replicaName === \"string\";\n\n if (hasCluster && (hasKeeperPath || hasReplicaName)) {\n throw new Error(\n `OlapTable ${name}: Cannot specify both 'cluster' and explicit replication params ('keeperPath' or 'replicaName'). ` +\n `Use 'cluster' for auto-injected params, or use explicit 'keeperPath' and 'replicaName' without 'cluster'.`,\n );\n }\n\n super(name, resolvedConfig, schema, columns, validators);\n this.name = name;\n\n const tables = getMooseInternal().tables;\n const registryKey =\n this.config.version ? `${name}_${this.config.version}` : name;\n // In client-only mode (MOOSE_CLIENT_ONLY=true), allow duplicate registrations\n // to support Next.js HMR which re-executes modules without clearing the registry\n if (!isClientOnlyMode() && tables.has(registryKey)) {\n throw new Error(\n `OlapTable with name ${name} and version ${config?.version ?? \"unversioned\"} already exists`,\n );\n }\n tables.set(registryKey, this);\n }\n\n /**\n * Generates the versioned table name following Moose's naming convention\n * Format: {tableName}_{version_with_dots_replaced_by_underscores}\n */\n private generateTableName(): string {\n // Cache the table name since version rarely changes\n if (this._cachedTableName) {\n return this._cachedTableName;\n }\n\n const tableVersion = this.config.version;\n if (!tableVersion) {\n this._cachedTableName = this.name;\n } else {\n const versionSuffix = tableVersion.replace(/\\./g, \"_\");\n this._cachedTableName = `${this.name}_${versionSuffix}`;\n }\n\n return this._cachedTableName;\n }\n\n /**\n * Creates a fast hash of the ClickHouse configuration.\n * Uses crypto.createHash for better performance than JSON.stringify.\n *\n * @private\n */\n private createConfigHash(clickhouseConfig: any): string {\n // Use per-table database if specified, otherwise fall back to global config\n const effectiveDatabase = this.config.database ?? clickhouseConfig.database;\n const configString = `${clickhouseConfig.host}:${clickhouseConfig.port}:${clickhouseConfig.username}:${clickhouseConfig.password}:${effectiveDatabase}:${clickhouseConfig.useSSL}`;\n return createHash(\"sha256\")\n .update(configString)\n .digest(\"hex\")\n .substring(0, 16);\n }\n\n /**\n * Gets or creates a memoized ClickHouse client.\n * The client is cached and reused across multiple insert calls for better performance.\n * If the configuration changes, a new client will be created.\n *\n * @private\n */\n private async getMemoizedClient(): Promise<{\n client: NodeClickHouseClient;\n config: RuntimeClickHouseConfig;\n }> {\n await import(\"../../config/runtime\");\n const configRegistry = (globalThis as any)\n ._mooseConfigRegistry as ConfigurationRegistry;\n const { getClickhouseClient } = await import(\"../../commons\");\n\n const clickhouseConfig = await configRegistry.getClickHouseConfig();\n const currentConfigHash = this.createConfigHash(clickhouseConfig);\n\n // If we have a cached client and the config hasn't changed, reuse it\n if (this._memoizedClient && this._configHash === currentConfigHash) {\n return { client: this._memoizedClient, config: clickhouseConfig };\n }\n\n // Close existing client if config changed\n if (this._memoizedClient && this._configHash !== currentConfigHash) {\n try {\n await this._memoizedClient.close();\n } catch (error) {\n // Ignore errors when closing old client\n }\n }\n\n // Create new client with standard configuration\n // Use per-table database if specified, otherwise fall back to global config\n const effectiveDatabase = this.config.database ?? clickhouseConfig.database;\n const client = getClickhouseClient({\n username: clickhouseConfig.username,\n password: clickhouseConfig.password,\n database: effectiveDatabase,\n useSSL: clickhouseConfig.useSSL ? \"true\" : \"false\",\n host: clickhouseConfig.host,\n port: clickhouseConfig.port,\n });\n\n // Cache the new client and config hash\n this._memoizedClient = client;\n this._configHash = currentConfigHash;\n\n return { client, config: clickhouseConfig };\n }\n\n /**\n * Closes the memoized ClickHouse client if it exists.\n * This is useful for cleaning up connections when the table instance is no longer needed.\n * The client will be automatically recreated on the next insert call if needed.\n */\n async closeClient(): Promise<void> {\n if (this._memoizedClient) {\n try {\n await this._memoizedClient.close();\n } catch (error) {\n // Ignore errors when closing\n } finally {\n this._memoizedClient = undefined;\n this._configHash = undefined;\n }\n }\n }\n\n /**\n * Validates a single record using typia's comprehensive type checking.\n * This provides the most accurate validation as it uses the exact TypeScript type information.\n *\n * @param record The record to validate\n * @returns Validation result with detailed error information\n */\n validateRecord(record: unknown): {\n success: boolean;\n data?: T;\n errors?: string[];\n } {\n // Use injected typia validator if available\n if (this.validators?.validate) {\n try {\n const result = this.validators.validate(record);\n return {\n success: result.success,\n data: result.data,\n errors: result.errors?.map((err) =>\n typeof err === \"string\" ? err : JSON.stringify(err),\n ),\n };\n } catch (error) {\n return {\n success: false,\n errors: [error instanceof Error ? error.message : String(error)],\n };\n }\n }\n\n throw new Error(\"No typia validator found\");\n }\n\n /**\n * Type guard function using typia's is() function.\n * Provides compile-time type narrowing for TypeScript.\n *\n * @param record The record to check\n * @returns True if record matches type T, with type narrowing\n */\n isValidRecord(record: unknown): record is T {\n if (this.validators?.is) {\n return this.validators.is(record);\n }\n\n throw new Error(\"No typia validator found\");\n }\n\n /**\n * Assert that a record matches type T, throwing detailed errors if not.\n * Uses typia's assert() function for the most detailed error reporting.\n *\n * @param record The record to assert\n * @returns The validated and typed record\n * @throws Detailed validation error if record doesn't match type T\n */\n assertValidRecord(record: unknown): T {\n if (this.validators?.assert) {\n return this.validators.assert(record);\n }\n\n throw new Error(\"No typia validator found\");\n }\n\n /**\n * Validates an array of records with comprehensive error reporting.\n * Uses the most appropriate validation method available (typia or basic).\n *\n * @param data Array of records to validate\n * @returns Detailed validation results\n */\n async validateRecords(data: unknown[]): Promise<ValidationResult<T>> {\n const valid: T[] = [];\n const invalid: ValidationError[] = [];\n\n // Pre-allocate arrays with estimated sizes to reduce reallocations\n valid.length = 0;\n invalid.length = 0;\n\n // Use for loop instead of forEach for better performance\n const dataLength = data.length;\n for (let i = 0; i < dataLength; i++) {\n const record = data[i];\n\n try {\n // Fast path: use typia's is() function first for type checking\n if (this.isValidRecord(record)) {\n valid.push(this.mapToClickhouseRecord(record));\n } else {\n // Only use expensive validateRecord for detailed errors when needed\n const result = this.validateRecord(record);\n if (result.success) {\n valid.push(this.mapToClickhouseRecord(record));\n } else {\n invalid.push({\n record,\n error: result.errors?.join(\", \") || \"Validation failed\",\n index: i,\n path: \"root\",\n });\n }\n }\n } catch (error) {\n invalid.push({\n record,\n error: error instanceof Error ? error.message : String(error),\n index: i,\n path: \"root\",\n });\n }\n }\n\n return {\n valid,\n invalid,\n total: dataLength,\n };\n }\n\n /**\n * Optimized batch retry that minimizes individual insert operations.\n * Groups records into smaller batches to reduce round trips while still isolating failures.\n *\n * @private\n */\n private async retryIndividualRecords(\n client: any,\n tableName: string,\n records: T[],\n ): Promise<{ successful: T[]; failed: FailedRecord<T>[] }> {\n const successful: T[] = [];\n const failed: FailedRecord<T>[] = [];\n\n // Instead of individual inserts, try smaller batches first (batches of 10)\n const RETRY_BATCH_SIZE = 10;\n const totalRecords = records.length;\n\n for (let i = 0; i < totalRecords; i += RETRY_BATCH_SIZE) {\n const batchEnd = Math.min(i + RETRY_BATCH_SIZE, totalRecords);\n const batch = records.slice(i, batchEnd);\n\n try {\n await client.insert({\n table: quoteIdentifier(tableName),\n values: batch,\n format: \"JSONEachRow\",\n clickhouse_settings: {\n date_time_input_format: \"best_effort\",\n // Add performance settings for retries\n max_insert_block_size: RETRY_BATCH_SIZE,\n max_block_size: RETRY_BATCH_SIZE,\n },\n });\n successful.push(...batch);\n } catch (batchError) {\n // If small batch fails, fall back to individual records\n for (let j = 0; j < batch.length; j++) {\n const record = batch[j];\n try {\n await client.insert({\n table: quoteIdentifier(tableName),\n values: [record],\n format: \"JSONEachRow\",\n clickhouse_settings: {\n date_time_input_format: \"best_effort\",\n },\n });\n successful.push(record);\n } catch (error) {\n failed.push({\n record,\n error: error instanceof Error ? error.message : String(error),\n index: i + j,\n });\n }\n }\n }\n }\n\n return { successful, failed };\n }\n\n /**\n * Validates input parameters and strategy compatibility\n * @private\n */\n private validateInsertParameters(\n data: T[] | Readable,\n options?: InsertOptions,\n ): { isStream: boolean; strategy: string; shouldValidate: boolean } {\n const isStream = data instanceof Readable;\n const strategy = options?.strategy || \"fail-fast\";\n const shouldValidate = options?.validate !== false;\n\n // Validate strategy compatibility with streams\n if (isStream && strategy === \"isolate\") {\n throw new Error(\n \"The 'isolate' error strategy is not supported with stream input. Use 'fail-fast' or 'discard' instead.\",\n );\n }\n\n // Validate that validation is not attempted on streams\n if (isStream && shouldValidate) {\n console.warn(\n \"Validation is not supported with stream input. Validation will be skipped.\",\n );\n }\n\n return { isStream, strategy, shouldValidate };\n }\n\n /**\n * Handles early return cases for empty data\n * @private\n */\n private handleEmptyData(\n data: T[] | Readable,\n isStream: boolean,\n ): InsertResult<T> | null {\n if (isStream && !data) {\n return {\n successful: 0,\n failed: 0,\n total: 0,\n };\n }\n\n if (!isStream && (!data || (data as T[]).length === 0)) {\n return {\n successful: 0,\n failed: 0,\n total: 0,\n };\n }\n\n return null;\n }\n\n /**\n * Performs pre-insertion validation for array data\n * @private\n */\n private async performPreInsertionValidation(\n data: T[],\n shouldValidate: boolean,\n strategy: string,\n options?: InsertOptions,\n ): Promise<{ validatedData: T[]; validationErrors: ValidationError[] }> {\n if (!shouldValidate) {\n return { validatedData: data, validationErrors: [] };\n }\n\n try {\n const validationResult = await this.validateRecords(data as unknown[]);\n const validatedData = validationResult.valid;\n const validationErrors = validationResult.invalid;\n\n if (validationErrors.length > 0) {\n this.handleValidationErrors(validationErrors, strategy, data, options);\n\n // Return appropriate data based on strategy\n switch (strategy) {\n case \"discard\":\n return { validatedData, validationErrors };\n case \"isolate\":\n return { validatedData: data, validationErrors };\n default:\n return { validatedData, validationErrors };\n }\n }\n\n return { validatedData, validationErrors };\n } catch (validationError) {\n if (strategy === \"fail-fast\") {\n throw validationError;\n }\n console.warn(\"Validation error:\", validationError);\n return { validatedData: data, validationErrors: [] };\n }\n }\n\n /**\n * Handles validation errors based on the specified strategy\n * @private\n */\n private handleValidationErrors(\n validationErrors: ValidationError[],\n strategy: string,\n data: T[],\n options?: InsertOptions,\n ): void {\n switch (strategy) {\n case \"fail-fast\":\n const firstError = validationErrors[0];\n throw new Error(\n `Validation failed for record at index ${firstError.index}: ${firstError.error}`,\n );\n\n case \"discard\":\n this.checkValidationThresholds(validationErrors, data.length, options);\n break;\n\n case \"isolate\":\n // For isolate strategy, validation errors will be handled in the final result\n break;\n }\n }\n\n /**\n * Checks if validation errors exceed configured thresholds\n * @private\n */\n private checkValidationThresholds(\n validationErrors: ValidationError[],\n totalRecords: number,\n options?: InsertOptions,\n ): void {\n const validationFailedCount = validationErrors.length;\n const validationFailedRatio = validationFailedCount / totalRecords;\n\n if (\n options?.allowErrors !== undefined &&\n validationFailedCount > options.allowErrors\n ) {\n throw new Error(\n `Too many validation failures: ${validationFailedCount} > ${options.allowErrors}. Errors: ${validationErrors.map((e) => e.error).join(\", \")}`,\n );\n }\n\n if (\n options?.allowErrorsRatio !== undefined &&\n validationFailedRatio > options.allowErrorsRatio\n ) {\n throw new Error(\n `Validation failure ratio too high: ${validationFailedRatio.toFixed(3)} > ${options.allowErrorsRatio}. Errors: ${validationErrors.map((e) => e.error).join(\", \")}`,\n );\n }\n }\n\n /**\n * Optimized insert options preparation with better memory management\n * @private\n */\n private prepareInsertOptions(\n tableName: string,\n data: T[] | Readable,\n validatedData: T[],\n isStream: boolean,\n strategy: string,\n options?: InsertOptions,\n ): any {\n const insertOptions: any = {\n table: quoteIdentifier(tableName),\n format: \"JSONEachRow\",\n clickhouse_settings: {\n date_time_input_format: \"best_effort\",\n wait_end_of_query: 1, // Ensure at least once delivery for INSERT operations\n // Performance optimizations\n max_insert_block_size:\n isStream ? 100000 : Math.min(validatedData.length, 100000),\n max_block_size: 65536,\n // Use async inserts for better performance with large datasets\n async_insert: validatedData.length > 1000 ? 1 : 0,\n wait_for_async_insert: 1, // For at least once delivery\n },\n };\n\n // Handle stream vs array input\n if (isStream) {\n insertOptions.values = data;\n } else {\n insertOptions.values = validatedData;\n }\n\n // For discard strategy, add optimized ClickHouse error tolerance settings\n if (\n strategy === \"discard\" &&\n (options?.allowErrors !== undefined ||\n options?.allowErrorsRatio !== undefined)\n ) {\n if (options.allowErrors !== undefined) {\n insertOptions.clickhouse_settings.input_format_allow_errors_num =\n options.allowErrors;\n }\n\n if (options.allowErrorsRatio !== undefined) {\n insertOptions.clickhouse_settings.input_format_allow_errors_ratio =\n options.allowErrorsRatio;\n }\n }\n\n return insertOptions;\n }\n\n /**\n * Creates success result for completed insertions\n * @private\n */\n private createSuccessResult(\n data: T[] | Readable,\n validatedData: T[],\n validationErrors: ValidationError[],\n isStream: boolean,\n shouldValidate: boolean,\n strategy: string,\n ): InsertResult<T> {\n if (isStream) {\n return {\n successful: -1, // -1 indicates stream mode where count is unknown\n failed: 0,\n total: -1,\n };\n }\n\n const insertedCount = validatedData.length;\n const totalProcessed =\n shouldValidate ? (data as T[]).length : insertedCount;\n\n const result: InsertResult<T> = {\n successful: insertedCount,\n failed: shouldValidate ? validationErrors.length : 0,\n total: totalProcessed,\n };\n\n // Add failed records if there are validation errors and using discard strategy\n if (\n shouldValidate &&\n validationErrors.length > 0 &&\n strategy === \"discard\"\n ) {\n result.failedRecords = validationErrors.map((ve) => ({\n record: ve.record as T,\n error: `Validation error: ${ve.error}`,\n index: ve.index,\n }));\n }\n\n return result;\n }\n\n /**\n * Handles insertion errors based on the specified strategy\n * @private\n */\n private async handleInsertionError(\n batchError: any,\n strategy: string,\n tableName: string,\n data: T[] | Readable,\n validatedData: T[],\n validationErrors: ValidationError[],\n isStream: boolean,\n shouldValidate: boolean,\n options?: InsertOptions,\n ): Promise<InsertResult<T>> {\n switch (strategy) {\n case \"fail-fast\":\n throw new Error(\n `Failed to insert data into table ${tableName}: ${batchError}`,\n );\n\n case \"discard\":\n throw new Error(\n `Too many errors during insert into table ${tableName}. Error threshold exceeded: ${batchError}`,\n );\n\n case \"isolate\":\n return await this.handleIsolateStrategy(\n batchError,\n tableName,\n data,\n validatedData,\n validationErrors,\n isStream,\n shouldValidate,\n options,\n );\n\n default:\n throw new Error(`Unknown error strategy: ${strategy}`);\n }\n }\n\n /**\n * Handles the isolate strategy for insertion errors\n * @private\n */\n private async handleIsolateStrategy(\n batchError: any,\n tableName: string,\n data: T[] | Readable,\n validatedData: T[],\n validationErrors: ValidationError[],\n isStream: boolean,\n shouldValidate: boolean,\n options?: InsertOptions,\n ): Promise<InsertResult<T>> {\n if (isStream) {\n throw new Error(\n `Isolate strategy is not supported with stream input: ${batchError}`,\n );\n }\n\n try {\n const { client } = await this.getMemoizedClient();\n const skipValidationOnRetry = options?.skipValidationOnRetry || false;\n const retryData = skipValidationOnRetry ? (data as T[]) : validatedData;\n\n const { successful, failed } = await this.retryIndividualRecords(\n client,\n tableName,\n retryData,\n );\n\n // Combine validation errors with insertion errors\n const allFailedRecords: FailedRecord<T>[] = [\n // Validation errors (if any and not skipping validation on retry)\n ...(shouldValidate && !skipValidationOnRetry ?\n validationErrors.map((ve) => ({\n record: ve.record as T,\n error: `Validation error: ${ve.error}`,\n index: ve.index,\n }))\n : []),\n // Insertion errors\n ...failed,\n ];\n\n this.checkInsertionThresholds(\n allFailedRecords,\n (data as T[]).length,\n options,\n );\n\n return {\n successful: successful.length,\n failed: allFailedRecords.length,\n total: (data as T[]).length,\n failedRecords: allFailedRecords,\n };\n } catch (isolationError) {\n throw new Error(\n `Failed to insert data into table ${tableName} during record isolation: ${isolationError}`,\n );\n }\n }\n\n /**\n * Checks if insertion errors exceed configured thresholds\n * @private\n */\n private checkInsertionThresholds(\n failedRecords: FailedRecord<T>[],\n totalRecords: number,\n options?: InsertOptions,\n ): void {\n const totalFailed = failedRecords.length;\n const failedRatio = totalFailed / totalRecords;\n\n if (\n options?.allowErrors !== undefined &&\n totalFailed > options.allowErrors\n ) {\n throw new Error(\n `Too many failed records: ${totalFailed} > ${options.allowErrors}. Failed records: ${failedRecords.map((f) => f.error).join(\", \")}`,\n );\n }\n\n if (\n options?.allowErrorsRatio !== undefined &&\n failedRatio > options.allowErrorsRatio\n ) {\n throw new Error(\n `Failed record ratio too high: ${failedRatio.toFixed(3)} > ${options.allowErrorsRatio}. Failed records: ${failedRecords.map((f) => f.error).join(\", \")}`,\n );\n }\n }\n\n /**\n * Recursively transforms a record to match ClickHouse's JSONEachRow requirements\n *\n * - For every Array(Nested(...)) field at any depth, each item is wrapped in its own array and recursively processed.\n * - For every Nested struct (not array), it recurses into the struct.\n * - This ensures compatibility with kafka_clickhouse_sync\n *\n * @param record The input record to transform (may be deeply nested)\n * @param columns The schema columns for this level (defaults to this.columnArray at the top level)\n * @returns The transformed record, ready for ClickHouse JSONEachRow insertion\n */\n private mapToClickhouseRecord(\n record: any,\n columns: Column[] = this.columnArray,\n ): any {\n const result = { ...record };\n for (const col of columns) {\n const value = record[col.name];\n const dt = col.data_type;\n\n if (isArrayNestedType(dt)) {\n // For Array(Nested(...)), wrap each item in its own array and recurse\n if (\n Array.isArray(value) &&\n (value.length === 0 || typeof value[0] === \"object\")\n ) {\n result[col.name] = value.map((item) => [\n this.mapToClickhouseRecord(item, dt.elementType.columns),\n ]);\n }\n } else if (isNestedType(dt)) {\n // For Nested struct (not array), recurse into it\n if (value && typeof value === \"object\") {\n result[col.name] = this.mapToClickhouseRecord(value, dt.columns);\n }\n }\n // All other types: leave as is for now\n }\n return result;\n }\n\n /**\n * Inserts data directly into the ClickHouse table with enhanced error handling and validation.\n * This method establishes a direct connection to ClickHouse using the project configuration\n * and inserts the provided data into the versioned table.\n *\n * PERFORMANCE OPTIMIZATIONS:\n * - Memoized client connections with fast config hashing\n * - Single-pass validation with pre-allocated arrays\n * - Batch-optimized retry strategy (batches of 10, then individual)\n * - Optimized ClickHouse settings for large datasets\n * - Reduced memory allocations and object creation\n *\n * Uses advanced typia validation when available for comprehensive type checking,\n * with fallback to basic validation for compatibility.\n *\n * The ClickHouse client is memoized and reused across multiple insert calls for better performance.\n * If the configuration changes, a new client will be automatically created.\n *\n * @param data Array of objects conforming to the table schema, or a Node.js Readable stream\n * @param options Optional configuration for error handling, validation, and insertion behavior\n * @returns Promise resolving to detailed insertion results\n * @throws {ConfigError} When configuration cannot be read or parsed\n * @throws {ClickHouseError} When insertion fails based on the error strategy\n * @throws {ValidationError} When validation fails and strategy is 'fail-fast'\n *\n * @example\n * ```typescript\n * // Create an OlapTable instance (typia validators auto-injected)\n * const userTable = new OlapTable<User>('users');\n *\n * // Insert with comprehensive typia validation\n * const result1 = await userTable.insert([\n * { id: 1, name: 'John', email: 'john@example.com' },\n * { id: 2, name: 'Jane', email: 'jane@example.com' }\n * ]);\n *\n * // Insert data with stream input (validation not available for streams)\n * const dataStream = new Readable({\n * objectMode: true,\n * read() { // Stream implementation }\n * });\n * const result2 = await userTable.insert(dataStream, { strategy: 'fail-fast' });\n *\n * // Insert with validation disabled for performance\n * const result3 = await userTable.insert(data, { validate: false });\n *\n * // Insert with error handling strategies\n * const result4 = await userTable.insert(mixedData, {\n * strategy: 'isolate',\n * allowErrorsRatio: 0.1,\n * validate: true // Use typia validation (default)\n * });\n *\n * // Optional: Clean up connection when completely done\n * await userTable.closeClient();\n * ```\n */\n async insert(\n data: T[] | Readable,\n options?: InsertOptions,\n ): Promise<InsertResult<T>> {\n // Validate input parameters and strategy compatibility\n const { isStream, strategy, shouldValidate } =\n this.validateInsertParameters(data, options);\n\n // Handle early return cases for empty data\n const emptyResult = this.handleEmptyData(data, isStream);\n if (emptyResult) {\n return emptyResult;\n }\n\n // Pre-insertion validation for arrays (optimized single-pass)\n let validatedData: T[] = [];\n let validationErrors: ValidationError[] = [];\n\n if (!isStream && shouldValidate) {\n const validationResult = await this.performPreInsertionValidation(\n data as T[],\n shouldValidate,\n strategy,\n options,\n );\n validatedData = validationResult.validatedData;\n validationErrors = validationResult.validationErrors;\n } else {\n // No validation or stream input\n validatedData = isStream ? [] : (data as T[]);\n }\n\n // Get memoized client and generate cached table name\n const { client } = await this.getMemoizedClient();\n const tableName = this.generateTableName();\n\n try {\n // Prepare and execute insertion with optimized settings\n const insertOptions = this.prepareInsertOptions(\n tableName,\n data,\n validatedData,\n isStream,\n strategy,\n options,\n );\n\n await client.insert(insertOptions);\n\n // Return success result\n return this.createSuccessResult(\n data,\n validatedData,\n validationErrors,\n isStream,\n shouldValidate,\n strategy,\n );\n } catch (batchError) {\n // Handle insertion failure based on strategy with optimized retry\n return await this.handleInsertionError(\n batchError,\n strategy,\n tableName,\n data,\n validatedData,\n validationErrors,\n isStream,\n shouldValidate,\n options,\n );\n }\n // Note: We don't close the client here since it's memoized for reuse\n // Use closeClient() method if you need to explicitly close the connection\n }\n\n // Note: Static factory methods (withS3Queue, withReplacingMergeTree, withMergeTree)\n // were removed in ENG-856. Use direct configuration instead, e.g.:\n // new OlapTable(name, { engine: ClickHouseEngines.ReplacingMergeTree, orderByFields: [\"id\"], ver: \"updated_at\" })\n}\n","/**\n * @fileoverview Stream SDK for data streaming operations in Moose.\n *\n * This module provides the core streaming functionality including:\n * - Stream creation and configuration\n * - Message transformations between streams\n * - Consumer registration for message processing\n * - Dead letter queue handling for error recovery\n *\n * @module Stream\n */\n\nimport { IJsonSchemaCollection } from \"typia\";\nimport { TypedBase } from \"../typedBase\";\nimport { Column } from \"../../dataModels/dataModelTypes\";\nimport { dlqColumns, dlqSchema, getMooseInternal } from \"../internal\";\nimport { OlapTable } from \"./olapTable\";\nimport { LifeCycle } from \"./lifeCycle\";\nimport type {\n RuntimeKafkaConfig,\n ConfigurationRegistry,\n} from \"../../config/runtime\";\nimport { createHash } from \"node:crypto\";\nimport { Logger, Producer } from \"../../commons\";\nimport { getSourceFileFromStack } from \"../utils/stackTrace\";\n\n/**\n * Represents zero, one, or many values of type T.\n * Used for flexible return types in transformations where a single input\n * can produce no output, one output, or multiple outputs.\n *\n * @template T The type of the value(s)\n * @example\n * ```typescript\n * // Can return a single value\n * const single: ZeroOrMany<string> = \"hello\";\n *\n * // Can return an array\n * const multiple: ZeroOrMany<string> = [\"hello\", \"world\"];\n *\n * // Can return null/undefined to filter out\n * const filtered: ZeroOrMany<string> = null;\n * ```\n */\nexport type ZeroOrMany<T> = T | T[] | undefined | null;\n\n/**\n * Function type for transforming records from one type to another.\n * Supports both synchronous and asynchronous transformations.\n *\n * @template T The input record type\n * @template U The output record type\n * @param record The input record to transform\n * @returns The transformed record(s), or null/undefined to filter out\n *\n * @example\n * ```typescript\n * const transform: SyncOrAsyncTransform<InputType, OutputType> = (record) => {\n * return { ...record, processed: true };\n * };\n * ```\n */\nexport type SyncOrAsyncTransform<T, U> = (\n record: T,\n) => ZeroOrMany<U> | Promise<ZeroOrMany<U>>;\n\n/**\n * Function type for consuming records without producing output.\n * Used for side effects like logging, external API calls, or database writes.\n *\n * @template T The record type to consume\n * @param record The record to process\n * @returns Promise<void> or void\n *\n * @example\n * ```typescript\n * const consumer: Consumer<UserEvent> = async (event) => {\n * await sendToAnalytics(event);\n * };\n * ```\n */\nexport type Consumer<T> = (record: T) => Promise<void> | void;\n\n/**\n * Configuration options for stream transformations.\n *\n * @template T The type of records being transformed\n */\nexport interface TransformConfig<T> {\n /**\n * Optional version identifier for this transformation.\n * Multiple transformations to the same destination can coexist with different versions.\n */\n version?: string;\n\n /**\n * Optional metadata for documentation and tracking purposes.\n */\n metadata?: { description?: string };\n\n /**\n * Optional dead letter queue for handling transformation failures.\n * Failed records will be sent to this queue for manual inspection or reprocessing.\n * Uses {@link Stream.defaultDeadLetterQueue} by default\n * unless a DeadLetterQueue is provided, or it is explicitly disabled with a null value\n */\n deadLetterQueue?: DeadLetterQueue<T> | null;\n\n /**\n * @internal Source file path where this transform was declared.\n * Automatically captured from stack trace.\n */\n sourceFile?: string;\n}\n\n/**\n * Configuration options for stream consumers.\n *\n * @template T The type of records being consumed\n */\nexport interface ConsumerConfig<T> {\n /**\n * Optional version identifier for this consumer.\n * Multiple consumers can coexist with different versions.\n */\n version?: string;\n\n /**\n * Optional dead letter queue for handling consumer failures.\n * Failed records will be sent to this queue for manual inspection or reprocessing.\n * Uses {@link Stream.defaultDeadLetterQueue} by default\n * unless a DeadLetterQueue is provided, or it is explicitly disabled with a null value\n */\n deadLetterQueue?: DeadLetterQueue<T> | null;\n\n /**\n * @internal Source file path where this consumer was declared.\n * Automatically captured from stack trace.\n */\n sourceFile?: string;\n}\n\nexport type SchemaRegistryEncoding = \"JSON\" | \"AVRO\" | \"PROTOBUF\";\n\nexport type SchemaRegistryReference =\n | { id: number }\n | { subjectLatest: string }\n | { subject: string; version: number };\n\nexport interface KafkaSchemaConfig {\n kind: SchemaRegistryEncoding;\n reference: SchemaRegistryReference;\n}\n\n/**\n * Represents a message routed to a specific destination stream.\n * Used internally by the multi-transform functionality to specify\n * where transformed messages should be sent.\n *\n * @internal\n */\nclass RoutedMessage {\n /** The destination stream for the message */\n destination: Stream<any>;\n\n /** The message value(s) to send */\n values: ZeroOrMany<any>;\n\n /**\n * Creates a new routed message.\n *\n * @param destination The target stream\n * @param values The message(s) to route\n */\n constructor(destination: Stream<any>, values: ZeroOrMany<any>) {\n this.destination = destination;\n this.values = values;\n }\n}\n\n/**\n * Configuration options for a data stream (e.g., a Redpanda topic).\n * @template T The data type of the messages in the stream.\n */\nexport interface StreamConfig<T> {\n /**\n * Specifies the number of partitions for the stream. Affects parallelism and throughput.\n */\n parallelism?: number;\n /**\n * Specifies the data retention period for the stream in seconds. Messages older than this may be deleted.\n */\n retentionPeriod?: number;\n /**\n * An optional destination OLAP table where messages from this stream should be automatically ingested.\n */\n destination?: OlapTable<T>;\n /**\n * An optional version string for this configuration. Can be used for tracking changes or managing deployments.\n */\n version?: string;\n metadata?: { description?: string };\n lifeCycle?: LifeCycle;\n\n defaultDeadLetterQueue?: DeadLetterQueue<T>;\n\n /** Optional Schema Registry configuration for this stream */\n schemaConfig?: KafkaSchemaConfig;\n}\n\n/**\n * Represents a data stream, typically corresponding to a Redpanda topic.\n * Provides a typed interface for producing to and consuming from the stream, and defining transformations.\n *\n * @template T The data type of the messages flowing through the stream. The structure of T defines the message schema.\n */\nexport class Stream<T> extends TypedBase<T, StreamConfig<T>> {\n defaultDeadLetterQueue?: DeadLetterQueue<T>;\n /** @internal Memoized KafkaJS producer for reusing connections across sends */\n private _memoizedProducer?: Producer;\n /** @internal Hash of the configuration used to create the memoized Kafka producer */\n private _kafkaConfigHash?: string;\n\n /**\n * Creates a new Stream instance.\n * @param name The name of the stream. This name is used for the underlying Redpanda topic.\n * @param config Optional configuration for the stream.\n */\n constructor(name: string, config?: StreamConfig<T>);\n\n /**\n * @internal\n * Note: `validators` parameter is a positional placeholder (always undefined for Stream).\n * It exists because TypedBase has validators as the 5th param, and we need to pass\n * allowExtraFields as the 6th param. Stream doesn't use validators.\n */\n constructor(\n name: string,\n config: StreamConfig<T>,\n schema: IJsonSchemaCollection.IV3_1,\n columns: Column[],\n validators: undefined,\n allowExtraFields: boolean,\n );\n\n constructor(\n name: string,\n config?: StreamConfig<T>,\n schema?: IJsonSchemaCollection.IV3_1,\n columns?: Column[],\n validators?: undefined,\n allowExtraFields?: boolean,\n ) {\n super(name, config ?? {}, schema, columns, undefined, allowExtraFields);\n const streams = getMooseInternal().streams;\n if (streams.has(name)) {\n throw new Error(`Stream with name ${name} already exists`);\n }\n streams.set(name, this);\n this.defaultDeadLetterQueue = this.config.defaultDeadLetterQueue;\n }\n\n /**\n * Internal map storing transformation configurations.\n * Maps destination stream names to arrays of transformation functions and their configs.\n *\n * @internal\n */\n _transformations = new Map<\n string,\n [Stream<any>, SyncOrAsyncTransform<T, any>, TransformConfig<T>][]\n >();\n\n /**\n * Internal function for multi-stream transformations.\n * Allows a single transformation to route messages to multiple destinations.\n *\n * @internal\n */\n _multipleTransformations?: (record: T) => [RoutedMessage];\n\n /**\n * Internal array storing consumer configurations.\n *\n * @internal\n */\n _consumers = new Array<{\n consumer: Consumer<T>;\n config: ConsumerConfig<T>;\n }>();\n\n /**\n * Builds the full Kafka topic name including optional namespace and version suffix.\n * Version suffix is appended as _x_y_z where dots in version are replaced with underscores.\n */\n private buildFullTopicName(namespace?: string): string {\n const versionSuffix =\n this.config.version ? `_${this.config.version.replace(/\\./g, \"_\")}` : \"\";\n const base = `${this.name}${versionSuffix}`;\n return namespace !== undefined && namespace.length > 0 ?\n `${namespace}.${base}`\n : base;\n }\n\n /**\n * Creates a fast hash string from relevant Kafka configuration fields.\n */\n private createConfigHash(kafkaConfig: RuntimeKafkaConfig): string {\n const configString = [\n kafkaConfig.broker,\n kafkaConfig.messageTimeoutMs,\n kafkaConfig.saslUsername,\n kafkaConfig.saslPassword,\n kafkaConfig.saslMechanism,\n kafkaConfig.securityProtocol,\n kafkaConfig.namespace,\n ].join(\":\");\n return createHash(\"sha256\")\n .update(configString)\n .digest(\"hex\")\n .substring(0, 16);\n }\n\n /**\n * Gets or creates a memoized KafkaJS producer using runtime configuration.\n */\n private async getMemoizedProducer(): Promise<{\n producer: Producer;\n kafkaConfig: RuntimeKafkaConfig;\n }> {\n // dynamic import to keep Stream objects browser compatible\n await import(\"../../config/runtime\");\n const configRegistry = (globalThis as any)\n ._mooseConfigRegistry as ConfigurationRegistry;\n const { getKafkaProducer } = await import(\"../../commons\");\n\n const kafkaConfig = await (configRegistry as any).getKafkaConfig();\n const currentHash = this.createConfigHash(kafkaConfig);\n\n if (this._memoizedProducer && this._kafkaConfigHash === currentHash) {\n return { producer: this._memoizedProducer, kafkaConfig };\n }\n\n // Close existing producer if config changed\n if (this._memoizedProducer && this._kafkaConfigHash !== currentHash) {\n try {\n await this._memoizedProducer.disconnect();\n } catch {\n // ignore\n }\n this._memoizedProducer = undefined;\n }\n\n const clientId = `moose-sdk-stream-${this.name}`;\n const logger: Logger = {\n logPrefix: clientId,\n log: (message: string): void => {\n console.log(`${clientId}: ${message}`);\n },\n error: (message: string): void => {\n console.error(`${clientId}: ${message}`);\n },\n warn: (message: string): void => {\n console.warn(`${clientId}: ${message}`);\n },\n };\n\n const producer = await getKafkaProducer(\n {\n clientId,\n broker: kafkaConfig.broker,\n securityProtocol: kafkaConfig.securityProtocol,\n saslUsername: kafkaConfig.saslUsername,\n saslPassword: kafkaConfig.saslPassword,\n saslMechanism: kafkaConfig.saslMechanism,\n },\n logger,\n );\n\n this._memoizedProducer = producer;\n this._kafkaConfigHash = currentHash;\n\n return { producer, kafkaConfig };\n }\n\n /**\n * Closes the memoized Kafka producer if it exists.\n */\n async closeProducer(): Promise<void> {\n if (this._memoizedProducer) {\n try {\n await this._memoizedProducer.disconnect();\n } catch {\n // ignore\n } finally {\n this._memoizedProducer = undefined;\n this._kafkaConfigHash = undefined;\n }\n }\n }\n\n /**\n * Sends one or more records to this stream's Kafka topic.\n * Values are JSON-serialized as message values.\n */\n async send(values: ZeroOrMany<T>): Promise<void> {\n // Normalize to flat array of records\n const flat: T[] =\n Array.isArray(values) ? values\n : values !== undefined && values !== null ? [values as T]\n : [];\n\n if (flat.length === 0) return;\n\n const { producer, kafkaConfig } = await this.getMemoizedProducer();\n const topic = this.buildFullTopicName(kafkaConfig.namespace);\n\n // Use Schema Registry JSON envelope if configured\n const sr = this.config.schemaConfig;\n if (sr && sr.kind === \"JSON\") {\n const schemaRegistryUrl = kafkaConfig.schemaRegistryUrl;\n if (!schemaRegistryUrl) {\n throw new Error(\"Schema Registry URL not configured\");\n }\n\n const {\n default: { SchemaRegistry },\n } = await import(\"@kafkajs/confluent-schema-registry\");\n const registry = new SchemaRegistry({ host: schemaRegistryUrl });\n\n let schemaId: undefined | number = undefined;\n\n if (\"id\" in sr.reference) {\n schemaId = sr.reference.id;\n } else if (\"subjectLatest\" in sr.reference) {\n schemaId = await registry.getLatestSchemaId(sr.reference.subjectLatest);\n } else if (\"subject\" in sr.reference) {\n schemaId = await registry.getRegistryId(\n sr.reference.subject,\n sr.reference.version,\n );\n }\n\n if (schemaId === undefined) {\n throw new Error(\"Malformed schema reference.\");\n }\n\n const encoded = await Promise.all(\n flat.map((v) =>\n registry.encode(schemaId, v as unknown as Record<string, unknown>),\n ),\n );\n await producer.send({\n topic,\n messages: encoded.map((value) => ({ value })),\n });\n return;\n } else if (sr !== undefined) {\n throw new Error(\"Currently only JSON Schema is supported.\");\n }\n\n await producer.send({\n topic,\n messages: flat.map((v) => ({ value: JSON.stringify(v) })),\n });\n }\n\n /**\n * Adds a transformation step that processes messages from this stream and sends the results to a destination stream.\n * Multiple transformations to the same destination stream can be added if they have distinct `version` identifiers in their config.\n *\n * @template U The data type of the messages in the destination stream.\n * @param destination The destination stream for the transformed messages.\n * @param transformation A function that takes a message of type T and returns zero or more messages of type U (or a Promise thereof).\n * Return `null` or `undefined` or an empty array `[]` to filter out a message. Return an array to emit multiple messages.\n * @param config Optional configuration for this specific transformation step, like a version.\n */\n addTransform<U>(\n destination: Stream<U>,\n transformation: SyncOrAsyncTransform<T, U>,\n config?: TransformConfig<T>,\n ) {\n // Capture source file from call stack at this exact moment\n const sourceFile = getSourceFileFromStack(new Error().stack);\n\n const transformConfig: TransformConfig<T> = {\n ...(config ?? {}),\n sourceFile,\n };\n if (transformConfig.deadLetterQueue === undefined) {\n transformConfig.deadLetterQueue = this.defaultDeadLetterQueue;\n }\n\n if (this._transformations.has(destination.name)) {\n const existingTransforms = this._transformations.get(destination.name)!;\n const hasVersion = existingTransforms.some(\n ([_, __, cfg]) => cfg.version === transformConfig.version,\n );\n\n if (!hasVersion) {\n existingTransforms.push([destination, transformation, transformConfig]);\n }\n } else {\n this._transformations.set(destination.name, [\n [destination, transformation, transformConfig],\n ]);\n }\n }\n\n /**\n * Adds a consumer function that processes messages from this stream.\n * Multiple consumers can be added if they have distinct `version` identifiers in their config.\n *\n * @param consumer A function that takes a message of type T and performs an action (e.g., side effect, logging). Should return void or Promise<void>.\n * @param config Optional configuration for this specific consumer, like a version.\n */\n addConsumer(consumer: Consumer<T>, config?: ConsumerConfig<T>) {\n // Capture source file from call stack at this exact moment\n const sourceFile = getSourceFileFromStack(new Error().stack);\n\n const consumerConfig: ConsumerConfig<T> = {\n ...(config ?? {}),\n sourceFile,\n };\n if (consumerConfig.deadLetterQueue === undefined) {\n consumerConfig.deadLetterQueue = this.defaultDeadLetterQueue;\n }\n const hasVersion = this._consumers.some(\n (existing) => existing.config.version === consumerConfig.version,\n );\n\n if (!hasVersion) {\n this._consumers.push({ consumer, config: consumerConfig });\n }\n }\n\n /**\n * Helper method for `addMultiTransform` to specify the destination and values for a routed message.\n * @param values The value or values to send to this stream.\n * @returns A `RoutedMessage` object associating the values with this stream.\n *\n * @example\n * ```typescript\n * sourceStream.addMultiTransform((record) => [\n * destinationStream1.routed(transformedRecord1),\n * destinationStream2.routed([record2a, record2b])\n * ]);\n * ```\n */\n routed = (values: ZeroOrMany<T>) => new RoutedMessage(this, values);\n\n /**\n * Adds a single transformation function that can route messages to multiple destination streams.\n * This is an alternative to adding multiple individual `addTransform` calls.\n * Only one multi-transform function can be added per stream.\n *\n * @param transformation A function that takes a message of type T and returns an array of `RoutedMessage` objects,\n * each specifying a destination stream and the message(s) to send to it.\n */\n addMultiTransform(transformation: (record: T) => [RoutedMessage]) {\n this._multipleTransformations = transformation;\n }\n}\n\n/**\n * Base model for dead letter queue entries.\n * Contains the original failed record along with error information.\n */\nexport interface DeadLetterModel {\n /** The original record that failed processing */\n originalRecord: Record<string, any>;\n\n /** Human-readable error message describing the failure */\n errorMessage: string;\n\n /** Classification of the error type (e.g., \"ValidationError\", \"TransformError\") */\n errorType: string;\n\n /** Timestamp when the failure occurred */\n failedAt: Date;\n\n /** The source component where the failure occurred */\n source: \"api\" | \"transform\" | \"table\";\n}\n\n/**\n * Enhanced dead letter model with type recovery functionality.\n * Extends the base model with the ability to recover the original typed record.\n *\n * @template T The original record type before failure\n */\nexport interface DeadLetter<T> extends DeadLetterModel {\n /**\n * Recovers the original record as its typed form.\n * Useful for reprocessing failed records with proper type safety.\n *\n * @returns The original record cast to type T\n */\n asTyped: () => T;\n}\n\n/**\n * Internal function to attach type guard functionality to dead letter records.\n *\n * @internal\n * @template T The original record type\n * @param dl The dead letter model to enhance\n * @param typeGuard Function to validate and cast the original record\n */\nfunction attachTypeGuard<T>(\n dl: DeadLetterModel,\n typeGuard: (input: any) => T,\n): asserts dl is DeadLetter<T> {\n (dl as any).asTyped = () => typeGuard(dl.originalRecord);\n}\n\n/**\n * Specialized stream for handling failed records (dead letters).\n * Provides type-safe access to failed records for reprocessing or analysis.\n *\n * @template T The original record type that failed processing\n *\n * @example\n * ```typescript\n * const dlq = new DeadLetterQueue<UserEvent>(\"user-events-dlq\");\n *\n * dlq.addConsumer(async (deadLetter) => {\n * const originalEvent = deadLetter.asTyped();\n * console.log(`Failed event: ${deadLetter.errorMessage}`);\n * // Potentially reprocess or alert\n * });\n * ```\n */\nexport class DeadLetterQueue<T> extends Stream<DeadLetterModel> {\n /**\n * Creates a new DeadLetterQueue instance.\n * @param name The name of the dead letter queue stream\n * @param config Optional configuration for the stream. The metadata property is always present and includes stackTrace.\n */\n constructor(name: string, config?: StreamConfig<DeadLetterModel>);\n\n /** @internal **/\n constructor(\n name: string,\n config: StreamConfig<DeadLetterModel>,\n validate: (originalRecord: any) => T,\n );\n\n constructor(\n name: string,\n config?: StreamConfig<DeadLetterModel>,\n typeGuard?: (originalRecord: any) => T,\n ) {\n if (typeGuard === undefined) {\n throw new Error(\n \"Supply the type param T so that the schema is inserted by the compiler plugin.\",\n );\n }\n\n super(name, config ?? {}, dlqSchema, dlqColumns, undefined, false);\n this.typeGuard = typeGuard;\n getMooseInternal().streams.set(name, this);\n }\n\n /**\n * Internal type guard function for validating and casting original records.\n *\n * @internal\n */\n private typeGuard: (originalRecord: any) => T;\n\n /**\n * Adds a transformation step for dead letter records.\n * The transformation function receives a DeadLetter<T> with type recovery capabilities.\n *\n * @template U The output type for the transformation\n * @param destination The destination stream for transformed messages\n * @param transformation Function to transform dead letter records\n * @param config Optional transformation configuration\n */\n addTransform<U>(\n destination: Stream<U>,\n transformation: SyncOrAsyncTransform<DeadLetter<T>, U>,\n config?: TransformConfig<DeadLetterModel>,\n ) {\n const withValidate: SyncOrAsyncTransform<DeadLetterModel, U> = (\n deadLetter,\n ) => {\n attachTypeGuard<T>(deadLetter, this.typeGuard);\n return transformation(deadLetter);\n };\n super.addTransform(destination, withValidate, config);\n }\n\n /**\n * Adds a consumer for dead letter records.\n * The consumer function receives a DeadLetter<T> with type recovery capabilities.\n *\n * @param consumer Function to process dead letter records\n * @param config Optional consumer configuration\n */\n addConsumer(\n consumer: Consumer<DeadLetter<T>>,\n config?: ConsumerConfig<DeadLetterModel>,\n ) {\n const withValidate: Consumer<DeadLetterModel> = (deadLetter) => {\n attachTypeGuard<T>(deadLetter, this.typeGuard);\n return consumer(deadLetter);\n };\n super.addConsumer(withValidate, config);\n }\n\n /**\n * Adds a multi-stream transformation for dead letter records.\n * The transformation function receives a DeadLetter<T> with type recovery capabilities.\n *\n * @param transformation Function to route dead letter records to multiple destinations\n */\n addMultiTransform(\n transformation: (record: DeadLetter<T>) => [RoutedMessage],\n ) {\n const withValidate: (record: DeadLetterModel) => [RoutedMessage] = (\n deadLetter,\n ) => {\n attachTypeGuard<T>(deadLetter, this.typeGuard);\n return transformation(deadLetter);\n };\n super.addMultiTransform(withValidate);\n }\n}\n","import { getMooseInternal } from \"../internal\";\nimport { getSourceLocationFromStack } from \"../utils/stackTrace\";\n\n/**\n * Context passed to task handlers. Single param to future-proof API changes.\n *\n * - state: shared mutable state for the task and its lifecycle hooks\n * - input: optional typed input for the task (undefined when task has no input)\n */\n/**\n * Task handler context. If the task declares an input type (T != null),\n * `input` is required and strongly typed. For no-input tasks (T = null),\n * `input` is omitted/optional.\n */\nexport type TaskContext<TInput> =\n TInput extends null ? { state: any; input?: null }\n : { state: any; input: TInput };\n\n/**\n * Configuration options for defining a task within a workflow.\n *\n * @template T - The input type for the task\n * @template R - The return type for the task\n */\nexport interface TaskConfig<T, R> {\n /** The main function that executes the task logic */\n run: (context: TaskContext<T>) => Promise<R>;\n\n /**\n * Optional array of tasks to execute after this task completes successfully.\n * Supports all combinations of input types (real type or null) and output types (real type or void).\n * When this task returns void, onComplete tasks expect null as input.\n * When this task returns a real type, onComplete tasks expect that type as input.\n */\n onComplete?: (\n | Task<R extends void ? null : R, any>\n | Task<R extends void ? null : R, void>\n )[];\n\n /**\n * Optional function that is called when the task is cancelled.\n */\n /** Optional function that is called when the task is cancelled. */\n onCancel?: (context: TaskContext<T>) => Promise<void>;\n\n /** Optional timeout duration for the task execution (e.g., \"30s\", \"5m\") */\n timeout?: string;\n\n /** Optional number of retry attempts if the task fails */\n retries?: number;\n}\n\n/**\n * Represents a single task within a workflow system.\n *\n * A Task encapsulates the execution logic, completion handlers, and configuration\n * for a unit of work that can be chained with other tasks in a workflow.\n *\n * @template T - The input type that this task expects\n * @template R - The return type that this task produces\n */\nexport class Task<T, R> {\n /**\n * Creates a new Task instance.\n *\n * @param name - Unique identifier for the task\n * @param config - Configuration object defining the task behavior\n *\n * @example\n * ```typescript\n * // No input, no output\n * const task1 = new Task<null, void>(\"task1\", {\n * run: async () => {\n * console.log(\"No input/output\");\n * }\n * });\n *\n * // No input, but has output\n * const task2 = new Task<null, OutputType>(\"task2\", {\n * run: async () => {\n * return someOutput;\n * }\n * });\n *\n * // Has input, no output\n * const task3 = new Task<InputType, void>(\"task3\", {\n * run: async (input: InputType) => {\n * // process input but return nothing\n * }\n * });\n *\n * // Has both input and output\n * const task4 = new Task<InputType, OutputType>(\"task4\", {\n * run: async (input: InputType) => {\n * return process(input);\n * }\n * });\n * ```\n */\n constructor(\n readonly name: string,\n readonly config: TaskConfig<T, R>,\n ) {}\n}\n\n/**\n * Configuration options for defining a workflow.\n *\n * A workflow orchestrates the execution of multiple tasks in a defined sequence\n * or pattern, with support for scheduling, retries, and timeouts.\n */\nexport interface WorkflowConfig {\n /**\n * The initial task that begins the workflow execution.\n * Supports all combinations of input types (real type or null) and output types (real type or void):\n * - Task<null, OutputType>: No input, returns a type\n * - Task<null, void>: No input, returns nothing\n * - Task<InputType, OutputType>: Has input, returns a type\n * - Task<InputType, void>: Has input, returns nothing\n */\n startingTask:\n | Task<null, any>\n | Task<null, void>\n | Task<any, any>\n | Task<any, void>;\n\n /** Optional number of retry attempts if the entire workflow fails */\n retries?: number;\n\n /** Optional timeout duration for the entire workflow execution (e.g., \"10m\", \"1h\") */\n timeout?: string;\n\n /** Optional cron-style schedule string for automated workflow execution */\n schedule?: string;\n}\n\n/**\n * Represents a complete workflow composed of interconnected tasks.\n *\n * A Workflow manages the execution flow of multiple tasks, handling scheduling,\n * error recovery, and task orchestration. Once created, workflows are automatically\n * registered with the internal Moose system.\n *\n * @example\n * ```typescript\n * const dataProcessingWorkflow = new Workflow(\"dataProcessing\", {\n * startingTask: extractDataTask,\n * schedule: \"0 2 * * *\", // Run daily at 2 AM\n * timeout: \"1h\",\n * retries: 2\n * });\n * ```\n */\nexport class Workflow {\n /** @internal Source file path where this workflow was declared */\n sourceFile?: string;\n\n /** @internal Source line number where this workflow was declared */\n sourceLine?: number;\n\n /** @internal Source column number where this workflow was declared */\n sourceColumn?: number;\n\n /**\n * Creates a new Workflow instance and registers it with the Moose system.\n *\n * @param name - Unique identifier for the workflow\n * @param config - Configuration object defining the workflow behavior and task orchestration\n * @throws {Error} When the workflow contains null/undefined tasks or infinite loops\n */\n constructor(\n readonly name: string,\n readonly config: WorkflowConfig,\n ) {\n const stack = new Error().stack;\n const location = getSourceLocationFromStack(stack);\n if (location) {\n this.sourceFile = location.file;\n this.sourceLine = location.line;\n this.sourceColumn = location.column;\n }\n\n const workflows = getMooseInternal().workflows;\n if (workflows.has(name)) {\n throw new Error(`Workflow with name ${name} already exists`);\n }\n this.validateTaskGraph(config.startingTask, name);\n workflows.set(name, this);\n }\n\n /**\n * Validates the task graph to ensure there are no null tasks or infinite loops.\n *\n * @private\n * @param startingTask - The starting task to begin validation from\n * @param workflowName - The name of the workflow being validated (for error messages)\n * @throws {Error} When null/undefined tasks are found or infinite loops are detected\n */\n private validateTaskGraph(\n startingTask: Task<any, any> | null | undefined,\n workflowName: string,\n ): void {\n if (startingTask === null || startingTask === undefined) {\n throw new Error(\n `Workflow \"${workflowName}\" has a null or undefined starting task`,\n );\n }\n\n const visited = new Set<string>();\n const recursionStack = new Set<string>();\n\n const validateTask = (\n task: Task<any, any> | null | undefined,\n currentPath: string[],\n ): void => {\n if (task === null || task === undefined) {\n const pathStr =\n currentPath.length > 0 ? currentPath.join(\" -> \") + \" -> \" : \"\";\n throw new Error(\n `Workflow \"${workflowName}\" contains a null or undefined task in the task chain: ${pathStr}null`,\n );\n }\n\n const taskName = task.name;\n\n if (recursionStack.has(taskName)) {\n const cycleStartIndex = currentPath.indexOf(taskName);\n const cyclePath =\n cycleStartIndex >= 0 ?\n currentPath.slice(cycleStartIndex).concat(taskName)\n : currentPath.concat(taskName);\n throw new Error(\n `Workflow \"${workflowName}\" contains an infinite loop in task chain: ${cyclePath.join(\" -> \")}`,\n );\n }\n\n if (visited.has(taskName)) {\n // Already processed this task and its children\n return;\n }\n\n visited.add(taskName);\n recursionStack.add(taskName);\n\n if (task.config.onComplete) {\n for (const nextTask of task.config.onComplete) {\n validateTask(nextTask, [...currentPath, taskName]);\n }\n }\n\n recursionStack.delete(taskName);\n };\n\n validateTask(startingTask, []);\n }\n}\n","import { IJsonSchemaCollection } from \"typia\";\nimport { TypedBase } from \"../typedBase\";\nimport { Column } from \"../../dataModels/dataModelTypes\";\nimport { getMooseInternal } from \"../internal\";\nimport { DeadLetterQueue, Stream } from \"./stream\";\n\n/**\n * @template T The data type of the messages expected by the destination stream.\n */\nexport interface IngestConfig<T> {\n /**\n * The destination stream where the ingested data should be sent.\n */\n destination: Stream<T>;\n\n deadLetterQueue?: DeadLetterQueue<T>;\n /**\n * An optional version string for this configuration.\n */\n version?: string;\n /**\n * An optional custom path for the ingestion endpoint.\n */\n path?: string;\n metadata?: { description?: string };\n}\n\n/**\n * Represents an Ingest API endpoint, used for sending data into a Moose system, typically writing to a Stream.\n * Provides a typed interface for the expected data format.\n *\n * @template T The data type of the records that this API endpoint accepts. The structure of T defines the expected request body schema.\n */\nexport class IngestApi<T> extends TypedBase<T, IngestConfig<T>> {\n /**\n * Creates a new IngestApi instance.\n * @param name The name of the ingest API endpoint.\n * @param config Optional configuration for the ingest API.\n */\n constructor(name: string, config?: IngestConfig<T>);\n\n /**\n * @internal\n * Note: `validators` parameter is a positional placeholder (always undefined for IngestApi).\n * It exists because TypedBase has validators as the 5th param, and we need to pass\n * allowExtraFields as the 6th param. IngestApi doesn't use validators.\n */\n constructor(\n name: string,\n config: IngestConfig<T>,\n schema: IJsonSchemaCollection.IV3_1,\n columns: Column[],\n validators: undefined,\n allowExtraFields: boolean,\n );\n\n constructor(\n name: string,\n config: IngestConfig<T>,\n schema?: IJsonSchemaCollection.IV3_1,\n columns?: Column[],\n validators?: undefined,\n allowExtraFields?: boolean,\n ) {\n super(name, config, schema, columns, undefined, allowExtraFields);\n const ingestApis = getMooseInternal().ingestApis;\n if (ingestApis.has(name)) {\n throw new Error(`Ingest API with name ${name} already exists`);\n }\n ingestApis.set(name, this);\n }\n}\n","import { IJsonSchemaCollection } from \"typia\";\nimport { TypedBase } from \"../typedBase\";\nimport { Column } from \"../../dataModels/dataModelTypes\";\nimport { getMooseInternal } from \"../internal\";\nimport type { ApiUtil } from \"../../consumption-apis/helpers\";\n\n/**\n * Defines the signature for a handler function used by a Consumption API.\n * @template T The expected type of the request parameters or query parameters.\n * @template R The expected type of the response data.\n * @param params An object containing the validated request parameters, matching the structure of T.\n * @param utils Utility functions provided to the handler, e.g., for database access (`runSql`).\n * @returns A Promise resolving to the response data of type R.\n */\ntype ApiHandler<T, R> = (params: T, utils: ApiUtil) => Promise<R>;\n\n/**\n * @template T The data type of the request parameters.\n */\nexport interface ApiConfig<T> {\n /**\n * An optional version string for this configuration.\n */\n version?: string;\n /**\n * An optional custom path for the API endpoint.\n * If not specified, defaults to the API name.\n */\n path?: string;\n metadata?: { description?: string };\n}\n\n/**\n * Represents a Consumption API endpoint (API), used for querying data from a Moose system.\n * Exposes data, often from an OlapTable or derived through a custom handler function.\n *\n * @template T The data type defining the expected structure of the API's query parameters.\n * @template R The data type defining the expected structure of the API's response body. Defaults to `any`.\n */\nexport class Api<T, R = any> extends TypedBase<T, ApiConfig<T>> {\n /** @internal The handler function that processes requests and generates responses. */\n _handler: ApiHandler<T, R>;\n /** @internal The JSON schema definition for the response type R. */\n responseSchema: IJsonSchemaCollection.IV3_1;\n\n /**\n * Creates a new Api instance.\n * @param name The name of the consumption API endpoint.\n * @param handler The function to execute when the endpoint is called. It receives validated query parameters and utility functions.\n * @param config Optional configuration for the consumption API.\n */\n constructor(name: string, handler: ApiHandler<T, R>, config?: {});\n\n /** @internal **/\n constructor(\n name: string,\n handler: ApiHandler<T, R>,\n config: ApiConfig<T>,\n schema: IJsonSchemaCollection.IV3_1,\n columns: Column[],\n responseSchema: IJsonSchemaCollection.IV3_1,\n );\n\n constructor(\n name: string,\n handler: ApiHandler<T, R>,\n config?: ApiConfig<T>,\n schema?: IJsonSchemaCollection.IV3_1,\n columns?: Column[],\n responseSchema?: IJsonSchemaCollection.IV3_1,\n ) {\n super(name, config ?? {}, schema, columns);\n this._handler = handler;\n this.responseSchema = responseSchema ?? {\n version: \"3.1\",\n schemas: [{ type: \"array\", items: { type: \"object\" } }],\n components: { schemas: {} },\n };\n const apis = getMooseInternal().apis;\n const key = `${name}${config?.version ? `:${config.version}` : \"\"}`;\n if (apis.has(key)) {\n throw new Error(\n `Consumption API with name ${name} and version ${config?.version} already exists`,\n );\n }\n apis.set(key, this);\n\n // Also register by custom path if provided\n if (config?.path) {\n if (config.version) {\n // Check if the path already ends with the version\n const pathEndsWithVersion =\n config.path.endsWith(`/${config.version}`) ||\n config.path === config.version ||\n (config.path.endsWith(config.version) &&\n config.path.length > config.version.length &&\n config.path[config.path.length - config.version.length - 1] ===\n \"/\");\n\n if (pathEndsWithVersion) {\n // Path already contains version, register as-is\n if (apis.has(config.path)) {\n const existing = apis.get(config.path)!;\n throw new Error(\n `Cannot register API \"${name}\" with path \"${config.path}\" - this path is already used by API \"${existing.name}\"`,\n );\n }\n apis.set(config.path, this);\n } else {\n // Path doesn't contain version, register with version appended\n const versionedPath = `${config.path.replace(/\\/$/, \"\")}/${config.version}`;\n\n // Check for collision on versioned path\n if (apis.has(versionedPath)) {\n const existing = apis.get(versionedPath)!;\n throw new Error(\n `Cannot register API \"${name}\" with path \"${versionedPath}\" - this path is already used by API \"${existing.name}\"`,\n );\n }\n apis.set(versionedPath, this);\n\n // Also register the unversioned path if not already claimed\n // (This is intentionally more permissive - first API gets the unversioned path)\n if (!apis.has(config.path)) {\n apis.set(config.path, this);\n }\n }\n } else {\n // Unversioned API, check for collision and register\n if (apis.has(config.path)) {\n const existing = apis.get(config.path)!;\n throw new Error(\n `Cannot register API \"${name}\" with custom path \"${config.path}\" - this path is already used by API \"${existing.name}\"`,\n );\n }\n apis.set(config.path, this);\n }\n }\n }\n\n /**\n * Retrieves the handler function associated with this Consumption API.\n * @returns The handler function.\n */\n getHandler = (): ApiHandler<T, R> => {\n return this._handler;\n };\n\n async call(baseUrl: string, queryParams: T): Promise<R> {\n // Construct the API endpoint URL using custom path or default to name\n let path: string;\n if (this.config?.path) {\n // Check if the custom path already contains the version\n if (this.config.version) {\n const pathEndsWithVersion =\n this.config.path.endsWith(`/${this.config.version}`) ||\n this.config.path === this.config.version ||\n (this.config.path.endsWith(this.config.version) &&\n this.config.path.length > this.config.version.length &&\n this.config.path[\n this.config.path.length - this.config.version.length - 1\n ] === \"/\");\n\n if (pathEndsWithVersion) {\n path = this.config.path;\n } else {\n path = `${this.config.path.replace(/\\/$/, \"\")}/${this.config.version}`;\n }\n } else {\n path = this.config.path;\n }\n } else {\n // Default to name with optional version\n path =\n this.config?.version ?\n `${this.name}/${this.config.version}`\n : this.name;\n }\n const url = new URL(`${baseUrl.replace(/\\/$/, \"\")}/api/${path}`);\n\n const searchParams = url.searchParams;\n\n for (const [key, value] of Object.entries(queryParams as any)) {\n if (Array.isArray(value)) {\n // For array values, add each item as a separate query param\n for (const item of value) {\n if (item !== null && item !== undefined) {\n searchParams.append(key, String(item));\n }\n }\n } else if (value !== null && value !== undefined) {\n searchParams.append(key, String(value));\n }\n }\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Accept: \"application/json\",\n },\n });\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const data = await response.json();\n return data as R;\n }\n}\n\n/** @deprecated Use ApiConfig<T> directly instead. */\nexport type EgressConfig<T> = ApiConfig<T>;\n\n/** @deprecated Use Api directly instead. */\nexport const ConsumptionApi = Api;\n","import { IJsonSchemaCollection } from \"typia\";\nimport { TypedBase, TypiaValidators } from \"../typedBase\";\nimport { Column } from \"../../dataModels/dataModelTypes\";\nimport {\n DeadLetterModel,\n DeadLetterQueue,\n Stream,\n StreamConfig,\n} from \"./stream\";\nimport { OlapConfig, OlapTable } from \"./olapTable\";\nimport { IngestApi, IngestConfig } from \"./ingestApi\";\nimport { LifeCycle } from \"./lifeCycle\";\nimport { ClickHouseEngines } from \"../../dataModels/types\";\n\n/**\n * Configuration options for a complete ingestion pipeline, potentially including an Ingest API, a Stream, and an OLAP Table.\n *\n * @template T The data type of the records being ingested.\n *\n * @example\n * ```typescript\n * // Simple pipeline with all components enabled\n * const pipelineConfig: IngestPipelineConfig<UserData> = {\n * table: true,\n * stream: true,\n * ingestApi: true\n * };\n *\n * // Advanced pipeline with custom configurations\n * const advancedConfig: IngestPipelineConfig<UserData> = {\n * table: { orderByFields: ['timestamp', 'userId'], engine: ClickHouseEngines.ReplacingMergeTree },\n * stream: { parallelism: 4, retentionPeriod: 86400 },\n * ingestApi: true,\n * version: '1.2.0',\n * metadata: { description: 'User data ingestion pipeline' }\n * };\n * ```\n */\nexport type IngestPipelineConfig<T> = {\n /**\n * Configuration for the OLAP table component of the pipeline.\n *\n * - If `true`, a table with default settings is created.\n * - If an `OlapConfig` object is provided, it specifies the table's configuration.\n * - If `false`, no OLAP table is created.\n *\n * @default false\n */\n table: boolean | OlapConfig<T>;\n\n /**\n * Configuration for the stream component of the pipeline.\n *\n * - If `true`, a stream with default settings is created.\n * - Pass a config object to specify the stream's configuration.\n * - The stream's destination will automatically be set to the pipeline's table if one exists.\n * - If `false`, no stream is created.\n *\n * @default false\n */\n stream: boolean | Omit<StreamConfig<T>, \"destination\">;\n\n /**\n * Configuration for the ingest API component of the pipeline.\n *\n * - If `true`, an ingest API with default settings is created.\n * - If a partial `IngestConfig` object (excluding `destination`) is provided, it specifies the API's configuration.\n * - The API's destination will automatically be set to the pipeline's stream if one exists.\n * - If `false`, no ingest API is created.\n *\n * **Note:** Requires a stream to be configured when enabled.\n *\n * @default false\n */\n ingestApi: boolean | Omit<IngestConfig<T>, \"destination\">;\n\n /**\n * @deprecated Use `ingestApi` instead. This parameter will be removed in a future version.\n */\n ingest?: boolean | Omit<IngestConfig<T>, \"destination\">;\n\n /**\n * Configuration for the dead letter queue of the pipeline.\n * If `true`, a dead letter queue with default settings is created.\n * If a partial `StreamConfig` object (excluding `destination`) is provided, it specifies the dead letter queue's configuration.\n * The API's destination will automatically be set to the pipeline's stream if one exists.\n * If `false` or `undefined`, no dead letter queue is created.\n */\n deadLetterQueue?: boolean | StreamConfig<DeadLetterModel>;\n\n /**\n * An optional version string applying to all components (table, stream, ingest) created by this pipeline configuration.\n * This version will be used for schema versioning and component identification.\n *\n * @example \"v1.0.0\", \"2023-12\", \"prod\"\n */\n version?: string;\n\n /**\n * An optional custom path for the ingestion API endpoint.\n * This will be used as the HTTP path for the ingest API if one is created.\n *\n * @example \"pipelines/analytics\", \"data/events\"\n */\n path?: string;\n\n /**\n * Optional metadata for the pipeline.\n */\n metadata?: {\n /** Human-readable description of the pipeline's purpose */\n description?: string;\n };\n\n /** Determines how changes in code will propagate to the resources. */\n lifeCycle?: LifeCycle;\n};\n\n/**\n * Represents a complete ingestion pipeline, potentially combining an Ingest API, a Stream, and an OLAP Table\n * under a single name and configuration. Simplifies the setup of common ingestion patterns.\n *\n * This class provides a high-level abstraction for creating data ingestion workflows that can include:\n * - An HTTP API endpoint for receiving data\n * - A streaming component for real-time data processing\n * - An OLAP table for analytical queries\n *\n * @template T The data type of the records flowing through the pipeline. This type defines the schema for the\n * Ingest API input, the Stream messages, and the OLAP Table rows.\n *\n * @example\n * ```typescript\n * // Create a complete pipeline with all components\n * const userDataPipeline = new IngestPipeline('userData', {\n * table: true,\n * stream: true,\n * ingestApi: true,\n * version: '1.0.0',\n * metadata: { description: 'Pipeline for user registration data' }\n * });\n *\n * // Create a pipeline with only table and stream\n * const analyticsStream = new IngestPipeline('analytics', {\n * table: { orderByFields: ['timestamp'], engine: ClickHouseEngines.ReplacingMergeTree },\n * stream: { parallelism: 8, retentionPeriod: 604800 },\n * ingestApi: false\n * });\n * ```\n */\nexport class IngestPipeline<T> extends TypedBase<T, IngestPipelineConfig<T>> {\n /**\n * The OLAP table component of the pipeline, if configured.\n * Provides analytical query capabilities for the ingested data.\n * Only present when `config.table` is not `false`.\n */\n table?: OlapTable<T>;\n\n /**\n * The stream component of the pipeline, if configured.\n * Handles real-time data flow and processing between components.\n * Only present when `config.stream` is not `false`.\n */\n stream?: Stream<T>;\n\n /**\n * The ingest API component of the pipeline, if configured.\n * Provides HTTP endpoints for data ingestion.\n * Only present when `config.ingestApi` is not `false`.\n */\n ingestApi?: IngestApi<T>;\n\n /** The dead letter queue of the pipeline, if configured. */\n deadLetterQueue?: DeadLetterQueue<T>;\n\n /**\n * Creates a new IngestPipeline instance.\n * Based on the configuration, it automatically creates and links the IngestApi, Stream, and OlapTable components.\n *\n * @param name The base name for the pipeline components (e.g., \"userData\" could create \"userData\" table, \"userData\" stream, \"userData\" ingest API).\n * @param config Optional configuration for the ingestion pipeline.\n *\n * @throws {Error} When ingest API is enabled but no stream is configured, since the API requires a stream destination.\n *\n * @example\n * ```typescript\n * const pipeline = new IngestPipeline('events', {\n * table: { orderByFields: ['timestamp'], engine: ClickHouseEngines.ReplacingMergeTree },\n * stream: { parallelism: 2 },\n * ingestApi: true\n * });\n * ```\n */\n constructor(name: string, config: IngestPipelineConfig<T>);\n\n /**\n * Internal constructor used by the framework for advanced initialization.\n *\n * @internal\n * @param name The base name for the pipeline components.\n * @param config Configuration specifying which components to create and their settings.\n * @param schema JSON schema collection for type validation.\n * @param columns Column definitions for the data model.\n * @param validators Typia validation functions.\n * @param allowExtraFields Whether extra fields are allowed (injected when type has index signature).\n */\n constructor(\n name: string,\n config: IngestPipelineConfig<T>,\n schema: IJsonSchemaCollection.IV3_1,\n columns: Column[],\n validators: TypiaValidators<T>,\n allowExtraFields: boolean,\n );\n\n constructor(\n name: string,\n config: IngestPipelineConfig<T>,\n schema?: IJsonSchemaCollection.IV3_1,\n columns?: Column[],\n validators?: TypiaValidators<T>,\n allowExtraFields?: boolean,\n ) {\n super(name, config, schema, columns, validators, allowExtraFields);\n\n // Handle backwards compatibility for deprecated 'ingest' parameter\n if (config.ingest !== undefined) {\n console.warn(\n \"⚠️ DEPRECATION WARNING: The 'ingest' parameter is deprecated and will be removed in a future version. \" +\n \"Please use 'ingestApi' instead.\",\n );\n // If ingestApi is not explicitly set, use the ingest value\n if (config.ingestApi === undefined) {\n (config as any).ingestApi = config.ingest;\n }\n }\n\n // Create OLAP table if configured\n if (config.table) {\n // Validate that the engine is not read-only (Merge engine cannot be written to)\n if (\n typeof config.table === \"object\" &&\n \"engine\" in config.table &&\n config.table.engine === ClickHouseEngines.Merge\n ) {\n throw new Error(\n `IngestPipeline \"${name}\": Merge engine is read-only and cannot be used as a table destination.`,\n );\n }\n\n const tableConfig: OlapConfig<T> =\n typeof config.table === \"object\" ?\n {\n ...config.table,\n lifeCycle: config.table.lifeCycle ?? config.lifeCycle,\n ...(config.version && { version: config.version }),\n }\n : {\n lifeCycle: config.lifeCycle,\n engine: ClickHouseEngines.MergeTree,\n ...(config.version && { version: config.version }),\n };\n this.table = new OlapTable(\n name,\n tableConfig,\n this.schema,\n this.columnArray,\n this.validators,\n );\n }\n\n if (config.deadLetterQueue) {\n const streamConfig = {\n destination: undefined,\n ...(typeof config.deadLetterQueue === \"object\" ?\n {\n ...config.deadLetterQueue,\n lifeCycle: config.deadLetterQueue.lifeCycle ?? config.lifeCycle,\n }\n : { lifeCycle: config.lifeCycle }),\n ...(config.version && { version: config.version }),\n };\n this.deadLetterQueue = new DeadLetterQueue<T>(\n `${name}DeadLetterQueue`,\n streamConfig,\n validators!.assert!,\n );\n }\n\n // Create stream if configured, linking it to the table as destination\n if (config.stream) {\n const streamConfig: StreamConfig<T> = {\n destination: this.table,\n defaultDeadLetterQueue: this.deadLetterQueue,\n ...(typeof config.stream === \"object\" ?\n {\n ...config.stream,\n lifeCycle: config.stream.lifeCycle ?? config.lifeCycle,\n }\n : { lifeCycle: config.lifeCycle }),\n ...(config.version && { version: config.version }),\n };\n this.stream = new Stream(\n name,\n streamConfig,\n this.schema,\n this.columnArray,\n undefined,\n this.allowExtraFields,\n );\n // Set pipeline parent reference for internal framework use\n (this.stream as any).pipelineParent = this;\n }\n\n // Create ingest API if configured, requiring a stream as destination\n const effectiveIngestAPI =\n config.ingestApi !== undefined ? config.ingestApi : config.ingest;\n if (effectiveIngestAPI) {\n if (!this.stream) {\n throw new Error(\"Ingest API needs a stream to write to.\");\n }\n\n const ingestConfig = {\n destination: this.stream,\n deadLetterQueue: this.deadLetterQueue,\n ...(typeof effectiveIngestAPI === \"object\" ?\n (effectiveIngestAPI as object)\n : {}),\n ...(config.version && { version: config.version }),\n ...(config.path && { path: config.path }),\n };\n this.ingestApi = new IngestApi(\n name,\n ingestConfig,\n this.schema,\n this.columnArray,\n undefined,\n this.allowExtraFields,\n );\n // Set pipeline parent reference for internal framework use\n (this.ingestApi as any).pipelineParent = this;\n }\n }\n}\n","import { Workflow, Task } from \"./workflow\";\nimport { OlapTable } from \"./olapTable\";\n\ninterface BatchResult<T> {\n items: T[];\n hasMore: boolean;\n}\n\ninterface TransformedResult<U> {\n items: U[];\n}\n\ninterface TaskConfig {\n retries: number;\n timeout: string;\n}\n\ninterface ETLTasks<T, U> {\n extract: Task<null, BatchResult<T>>;\n transform: Task<BatchResult<T>, TransformedResult<U>>;\n load: Task<TransformedResult<U>, void>;\n}\n\nclass InternalBatcher<T> {\n private iterator: AsyncIterator<T>;\n private batchSize: number;\n\n constructor(asyncIterable: AsyncIterable<T>, batchSize = 20) {\n this.iterator = asyncIterable[Symbol.asyncIterator]();\n this.batchSize = batchSize;\n }\n\n async getNextBatch(): Promise<BatchResult<T>> {\n const items: T[] = [];\n\n for (let i = 0; i < this.batchSize; i++) {\n const { value, done } = await this.iterator.next();\n\n if (done) {\n return { items, hasMore: false };\n }\n\n items.push(value);\n }\n\n return { items, hasMore: true };\n }\n}\n\nexport interface ETLPipelineConfig<T, U> {\n extract: AsyncIterable<T> | (() => AsyncIterable<T>);\n transform: (sourceData: T) => Promise<U>;\n load: ((data: U[]) => Promise<void>) | OlapTable<U>;\n}\n\nexport class ETLPipeline<T, U> {\n private batcher!: InternalBatcher<T>;\n\n constructor(\n readonly name: string,\n readonly config: ETLPipelineConfig<T, U>,\n ) {\n this.setupPipeline();\n }\n\n private setupPipeline(): void {\n this.batcher = this.createBatcher();\n const tasks = this.createAllTasks();\n\n tasks.extract.config.onComplete = [tasks.transform];\n tasks.transform.config.onComplete = [tasks.load];\n\n new Workflow(this.name, {\n startingTask: tasks.extract,\n retries: 1,\n timeout: \"30m\",\n });\n }\n\n private createBatcher(): InternalBatcher<T> {\n const iterable =\n typeof this.config.extract === \"function\" ?\n this.config.extract()\n : this.config.extract;\n\n return new InternalBatcher(iterable);\n }\n\n private getDefaultTaskConfig(): TaskConfig {\n return {\n retries: 1,\n timeout: \"30m\",\n };\n }\n\n private createAllTasks(): ETLTasks<T, U> {\n const taskConfig = this.getDefaultTaskConfig();\n\n return {\n extract: this.createExtractTask(taskConfig),\n transform: this.createTransformTask(taskConfig),\n load: this.createLoadTask(taskConfig),\n };\n }\n\n private createExtractTask(\n taskConfig: TaskConfig,\n ): Task<null, BatchResult<T>> {\n return new Task<null, BatchResult<T>>(`${this.name}_extract`, {\n run: async ({}) => {\n console.log(`Running extract task for ${this.name}...`);\n const batch = await this.batcher.getNextBatch();\n console.log(`Extract task completed with ${batch.items.length} items`);\n return batch;\n },\n retries: taskConfig.retries,\n timeout: taskConfig.timeout,\n });\n }\n\n private createTransformTask(\n taskConfig: TaskConfig,\n ): Task<BatchResult<T>, TransformedResult<U>> {\n return new Task<BatchResult<T>, TransformedResult<U>>(\n `${this.name}_transform`,\n {\n // Use new single-parameter context API for handlers\n run: async ({ input }) => {\n const batch = input!;\n console.log(\n `Running transform task for ${this.name} with ${batch.items.length} items...`,\n );\n const transformedItems: U[] = [];\n\n for (const item of batch.items) {\n const transformed = await this.config.transform(item);\n transformedItems.push(transformed);\n }\n\n console.log(\n `Transform task completed with ${transformedItems.length} items`,\n );\n return { items: transformedItems };\n },\n retries: taskConfig.retries,\n timeout: taskConfig.timeout,\n },\n );\n }\n\n private createLoadTask(\n taskConfig: TaskConfig,\n ): Task<TransformedResult<U>, void> {\n return new Task<TransformedResult<U>, void>(`${this.name}_load`, {\n run: async ({ input: transformedItems }) => {\n console.log(\n `Running load task for ${this.name} with ${transformedItems.items.length} items...`,\n );\n\n // Handle both function and OlapTable\n if (\"insert\" in this.config.load) {\n // It's an OlapTable - insert entire batch\n await this.config.load.insert(transformedItems.items);\n } else {\n // It's a function - call with entire array\n await this.config.load(transformedItems.items);\n }\n\n console.log(`Load task completed`);\n },\n retries: taskConfig.retries,\n timeout: taskConfig.timeout,\n });\n }\n\n // Execute the entire ETL pipeline\n async run(): Promise<void> {\n console.log(`Starting ETL Pipeline: ${this.name}`);\n\n let batchNumber = 1;\n do {\n console.log(`Processing batch ${batchNumber}...`);\n const batch = await this.batcher.getNextBatch();\n\n if (batch.items.length === 0) {\n break;\n }\n\n // Transform all items in the batch\n const transformedItems: U[] = [];\n for (const extractedData of batch.items) {\n const transformedData = await this.config.transform(extractedData);\n transformedItems.push(transformedData);\n }\n\n // Load the entire batch\n if (\"insert\" in this.config.load) {\n // It's an OlapTable - insert entire batch\n await this.config.load.insert(transformedItems);\n } else {\n // It's a function - call with entire array\n await this.config.load(transformedItems);\n }\n\n console.log(\n `Completed batch ${batchNumber} with ${batch.items.length} items`,\n );\n batchNumber++;\n\n if (!batch.hasMore) {\n break;\n }\n } while (true);\n\n console.log(`Completed ETL Pipeline: ${this.name}`);\n }\n}\n","import { ClickHouseEngines } from \"../../dataModels/types\";\nimport { Sql, toStaticQuery } from \"../../sqlHelpers\";\nimport { OlapConfig, OlapTable } from \"./olapTable\";\nimport { View } from \"./view\";\nimport { LifeCycle } from \"./lifeCycle\";\nimport { IJsonSchemaCollection } from \"typia\";\nimport { Column } from \"../../dataModels/dataModelTypes\";\nimport { getMooseInternal, isClientOnlyMode } from \"../internal\";\nimport { getSourceFileFromStack } from \"../utils/stackTrace\";\n\n/**\n * Helper function to format a table reference as `database`.`table` or just `table`\n */\nfunction formatTableReference(table: OlapTable<any> | View): string {\n const database =\n table instanceof OlapTable ? table.config.database : undefined;\n if (database) {\n return `\\`${database}\\`.\\`${table.name}\\``;\n }\n return `\\`${table.name}\\``;\n}\n\n/**\n * Configuration options for creating a Materialized View.\n * @template T The data type of the records stored in the target table of the materialized view.\n */\nexport interface MaterializedViewConfig<T> {\n /** The SQL SELECT statement or `Sql` object defining the data to be materialized. Dynamic SQL (with parameters) is not allowed here. */\n selectStatement: string | Sql;\n /** An array of OlapTable or View objects that the `selectStatement` reads from. */\n selectTables: (OlapTable<any> | View)[];\n\n /** @deprecated See {@link targetTable}\n * The name for the underlying target OlapTable that stores the materialized data. */\n tableName?: string;\n\n /** The name for the ClickHouse MATERIALIZED VIEW object itself. */\n materializedViewName: string;\n\n /** @deprecated See {@link targetTable}\n * Optional ClickHouse engine for the target table (e.g., ReplacingMergeTree). Defaults to MergeTree. */\n engine?: ClickHouseEngines;\n\n targetTable?:\n | OlapTable<T> /** Target table if the OlapTable object is already constructed. */\n | {\n /** The name for the underlying target OlapTable that stores the materialized data. */\n name: string;\n /** Optional ClickHouse engine for the target table (e.g., ReplacingMergeTree). Defaults to MergeTree. */\n engine?: ClickHouseEngines;\n /** Optional ordering fields for the target table. Crucial if using ReplacingMergeTree. */\n orderByFields?: (keyof T & string)[];\n };\n\n /** @deprecated See {@link targetTable}\n * Optional ordering fields for the target table. Crucial if using ReplacingMergeTree. */\n orderByFields?: (keyof T & string)[];\n\n /** Optional metadata for the materialized view (e.g., description, source file). */\n metadata?: { [key: string]: any };\n\n /** Optional lifecycle management policy for the materialized view.\n * Controls whether Moose can drop or modify the MV automatically.\n * Defaults to FULLY_MANAGED if not specified. */\n lifeCycle?: LifeCycle;\n}\n\nconst requireTargetTableName = (tableName: string | undefined): string => {\n if (typeof tableName === \"string\") {\n return tableName;\n } else {\n throw new Error(\"Name of targetTable is not specified.\");\n }\n};\n\n/**\n * Represents a Materialized View in ClickHouse.\n * This encapsulates both the target OlapTable that stores the data and the MATERIALIZED VIEW definition\n * that populates the table based on inserts into the source tables.\n *\n * @template TargetTable The data type of the records stored in the underlying target OlapTable. The structure of T defines the target table schema.\n */\nexport class MaterializedView<TargetTable> {\n /** @internal */\n public readonly kind = \"MaterializedView\";\n\n /** The name of the materialized view */\n name: string;\n\n /** The target OlapTable instance where the materialized data is stored. */\n targetTable: OlapTable<TargetTable>;\n\n /** The SELECT SQL statement */\n selectSql: string;\n\n /** Names of source tables that the SELECT reads from */\n sourceTables: string[];\n\n /** Optional metadata for the materialized view */\n metadata: { [key: string]: any };\n\n /** Optional lifecycle management policy for the materialized view */\n lifeCycle?: LifeCycle;\n\n /**\n * Creates a new MaterializedView instance.\n * Requires the `TargetTable` type parameter to be explicitly provided or inferred,\n * as it's needed to define the schema of the underlying target table.\n *\n * @param options Configuration options for the materialized view.\n */\n constructor(options: MaterializedViewConfig<TargetTable>);\n\n /** @internal **/\n constructor(\n options: MaterializedViewConfig<TargetTable>,\n targetSchema: IJsonSchemaCollection.IV3_1,\n targetColumns: Column[],\n );\n constructor(\n options: MaterializedViewConfig<TargetTable>,\n targetSchema?: IJsonSchemaCollection.IV3_1,\n targetColumns?: Column[],\n ) {\n let selectStatement = options.selectStatement;\n if (typeof selectStatement !== \"string\") {\n selectStatement = toStaticQuery(selectStatement);\n }\n\n if (targetSchema === undefined || targetColumns === undefined) {\n throw new Error(\n \"Supply the type param T so that the schema is inserted by the compiler plugin.\",\n );\n }\n\n const targetTable =\n options.targetTable instanceof OlapTable ?\n options.targetTable\n : new OlapTable(\n requireTargetTableName(\n options.targetTable?.name ?? options.tableName,\n ),\n {\n orderByFields:\n options.targetTable?.orderByFields ?? options.orderByFields,\n engine:\n options.targetTable?.engine ??\n options.engine ??\n ClickHouseEngines.MergeTree,\n } as OlapConfig<TargetTable>,\n targetSchema,\n targetColumns,\n );\n\n if (targetTable.name === options.materializedViewName) {\n throw new Error(\n \"Materialized view name cannot be the same as the target table name.\",\n );\n }\n\n this.name = options.materializedViewName;\n this.targetTable = targetTable;\n this.selectSql = selectStatement;\n this.sourceTables = options.selectTables.map((t) =>\n formatTableReference(t),\n );\n this.lifeCycle = options.lifeCycle;\n\n // Initialize metadata, preserving user-provided metadata if any\n this.metadata = options.metadata ? { ...options.metadata } : {};\n\n // Capture source file from stack trace if not already provided\n if (!this.metadata.source) {\n const stack = new Error().stack;\n const sourceInfo = getSourceFileFromStack(stack);\n if (sourceInfo) {\n this.metadata.source = { file: sourceInfo };\n }\n }\n\n // Register in the materializedViews registry\n const materializedViews = getMooseInternal().materializedViews;\n if (!isClientOnlyMode() && materializedViews.has(this.name)) {\n throw new Error(`MaterializedView with name ${this.name} already exists`);\n }\n materializedViews.set(this.name, this);\n }\n}\n","import { getMooseInternal, isClientOnlyMode } from \"../internal\";\nimport { OlapTable } from \"./olapTable\";\nimport { Sql, toStaticQuery } from \"../../sqlHelpers\";\nimport { getSourceLocationFromStack } from \"../utils/stackTrace\";\nimport { MaterializedView } from \"./materializedView\";\nimport { View } from \"./view\";\n\ntype SqlObject = OlapTable<any> | SqlResource | View | MaterializedView<any>;\n\n/**\n * Represents a generic SQL resource that requires setup and teardown commands.\n * Base class for constructs like Views and Materialized Views. Tracks dependencies.\n */\nexport class SqlResource {\n /** @internal */\n public readonly kind = \"SqlResource\";\n\n /** Array of SQL statements to execute for setting up the resource. */\n setup: readonly string[];\n /** Array of SQL statements to execute for tearing down the resource. */\n teardown: readonly string[];\n /** The name of the SQL resource (e.g., view name, materialized view name). */\n name: string;\n\n /** List of OlapTables or Views that this resource reads data from. */\n pullsDataFrom: SqlObject[];\n /** List of OlapTables or Views that this resource writes data to. */\n pushesDataTo: SqlObject[];\n\n /** @internal Source file path where this resource was defined */\n sourceFile?: string;\n\n /** @internal Source line number where this resource was defined */\n sourceLine?: number;\n\n /** @internal Source column number where this resource was defined */\n sourceColumn?: number;\n\n /**\n * Creates a new SqlResource instance.\n * @param name The name of the resource.\n * @param setup An array of SQL DDL statements to create the resource.\n * @param teardown An array of SQL DDL statements to drop the resource.\n * @param options Optional configuration for specifying data dependencies.\n * @param options.pullsDataFrom Tables/Views this resource reads from.\n * @param options.pushesDataTo Tables/Views this resource writes to.\n */\n constructor(\n name: string,\n setup: readonly (string | Sql)[],\n teardown: readonly (string | Sql)[],\n options?: {\n pullsDataFrom?: SqlObject[];\n pushesDataTo?: SqlObject[];\n },\n ) {\n const sqlResources = getMooseInternal().sqlResources;\n // In client-only mode (MOOSE_CLIENT_ONLY=true), allow duplicate registrations\n // to support Next.js HMR which re-executes modules without clearing the registry\n if (!isClientOnlyMode() && sqlResources.has(name)) {\n throw new Error(`SqlResource with name ${name} already exists`);\n }\n sqlResources.set(name, this);\n\n this.name = name;\n this.setup = setup.map((sql) =>\n typeof sql === \"string\" ? sql : toStaticQuery(sql),\n );\n this.teardown = teardown.map((sql) =>\n typeof sql === \"string\" ? sql : toStaticQuery(sql),\n );\n this.pullsDataFrom = options?.pullsDataFrom ?? [];\n this.pushesDataTo = options?.pushesDataTo ?? [];\n\n // Capture source location from stack trace\n const stack = new Error().stack;\n const location = getSourceLocationFromStack(stack);\n\n if (location) {\n this.sourceFile = location.file;\n this.sourceLine = location.line;\n this.sourceColumn = location.column;\n }\n }\n}\n","import { Sql, toStaticQuery } from \"../../sqlHelpers\";\nimport { OlapTable } from \"./olapTable\";\nimport { getMooseInternal, isClientOnlyMode } from \"../internal\";\nimport { getSourceFileFromStack } from \"../utils/stackTrace\";\n\n/**\n * Helper function to format a table reference as `database`.`table` or just `table`\n */\nfunction formatTableReference(table: OlapTable<any> | View): string {\n const database =\n table instanceof OlapTable ? table.config.database : undefined;\n if (database) {\n return `\\`${database}\\`.\\`${table.name}\\``;\n }\n return `\\`${table.name}\\``;\n}\n\n/**\n * Represents a database View, defined by a SQL SELECT statement based on one or more base tables or other views.\n * Emits structured data for the Moose infrastructure system.\n */\nexport class View {\n /** @internal */\n public readonly kind = \"View\";\n\n /** The name of the view */\n name: string;\n\n /** The SELECT SQL statement that defines the view */\n selectSql: string;\n\n /** Names of source tables/views that the SELECT reads from */\n sourceTables: string[];\n\n /** Optional metadata for the view */\n metadata: { [key: string]: any };\n\n /**\n * Creates a new View instance.\n * @param name The name of the view to be created.\n * @param selectStatement The SQL SELECT statement that defines the view's logic.\n * @param baseTables An array of OlapTable or View objects that the `selectStatement` reads from. Used for dependency tracking.\n * @param metadata Optional metadata for the view (e.g., description, source file).\n */\n constructor(\n name: string,\n selectStatement: string | Sql,\n baseTables: (OlapTable<any> | View)[],\n metadata?: { [key: string]: any },\n ) {\n if (typeof selectStatement !== \"string\") {\n selectStatement = toStaticQuery(selectStatement);\n }\n\n this.name = name;\n this.selectSql = selectStatement;\n this.sourceTables = baseTables.map((t) => formatTableReference(t));\n\n // Initialize metadata, preserving user-provided metadata if any\n this.metadata = metadata ? { ...metadata } : {};\n\n // Capture source file from stack trace if not already provided\n if (!this.metadata.source) {\n const stack = new Error().stack;\n const sourceInfo = getSourceFileFromStack(stack);\n if (sourceInfo) {\n this.metadata.source = { file: sourceInfo };\n }\n }\n\n // Register in the views registry\n const views = getMooseInternal().views;\n if (!isClientOnlyMode() && views.has(this.name)) {\n throw new Error(`View with name ${this.name} already exists`);\n }\n views.set(this.name, this);\n }\n}\n","/**\n * Defines how Moose manages the lifecycle of database resources when your code changes.\n *\n * This enum controls the behavior when there are differences between your code definitions\n * and the actual database schema or structure.\n */\nexport enum LifeCycle {\n /**\n * Full automatic management (default behavior).\n * Moose will automatically modify database resources to match your code definitions,\n * including potentially destructive operations like dropping columns or tables.\n */\n FULLY_MANAGED = \"FULLY_MANAGED\",\n\n /**\n * Deletion-protected automatic management.\n * Moose will modify resources to match your code but will avoid destructive actions\n * such as dropping columns, or tables. Only additive changes are applied.\n */\n DELETION_PROTECTED = \"DELETION_PROTECTED\",\n\n /**\n * External management - no automatic changes.\n * Moose will not modify the database resources. You are responsible for managing\n * the schema and ensuring it matches your code definitions manually.\n */\n EXTERNALLY_MANAGED = \"EXTERNALLY_MANAGED\",\n}\n","import http from \"http\";\nimport { getMooseInternal } from \"../internal\";\nimport { getSourceLocationFromStack } from \"../utils/stackTrace\";\n\nexport type WebAppHandler = (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n) => void | Promise<void>;\n\nexport interface FrameworkApp {\n handle?: (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n next?: (err?: any) => void,\n ) => void;\n callback?: () => WebAppHandler;\n routing?: (req: http.IncomingMessage, res: http.ServerResponse) => void;\n ready?: () => PromiseLike<unknown>; // Fastify's ready method (returns FastifyInstance)\n}\n\nexport interface WebAppConfig {\n mountPath: string;\n metadata?: { description?: string };\n injectMooseUtils?: boolean;\n}\n\nconst RESERVED_MOUNT_PATHS = [\n \"/admin\",\n \"/api\",\n \"/consumption\",\n \"/health\",\n \"/ingest\",\n \"/liveness\",\n \"/moose\", // reserved for future use\n \"/ready\",\n \"/workflows\",\n] as const;\n\nexport class WebApp {\n name: string;\n handler: WebAppHandler;\n config: WebAppConfig;\n /** @internal Source file path where this web app was declared */\n sourceFile?: string;\n /** @internal Source line number where this web app was declared */\n sourceLine?: number;\n /** @internal Source column number where this web app was declared */\n sourceColumn?: number;\n private _rawApp?: FrameworkApp;\n\n constructor(\n name: string,\n appOrHandler: FrameworkApp | WebAppHandler,\n config: WebAppConfig,\n ) {\n this.name = name;\n this.config = config;\n\n const stack = new Error().stack;\n const location = getSourceLocationFromStack(stack);\n if (location) {\n this.sourceFile = location.file;\n this.sourceLine = location.line;\n this.sourceColumn = location.column;\n }\n\n // Validate mountPath - it is required\n if (!this.config.mountPath) {\n throw new Error(\n `mountPath is required. Please specify a mount path for your WebApp (e.g., \"/myapi\").`,\n );\n }\n\n const mountPath = this.config.mountPath;\n\n // Check for root path - not allowed as it would overlap reserved paths\n if (mountPath === \"/\") {\n throw new Error(\n `mountPath cannot be \"/\" as it would allow routes to overlap with reserved paths: ${RESERVED_MOUNT_PATHS.join(\", \")}`,\n );\n }\n\n // Check for trailing slash\n if (mountPath.endsWith(\"/\")) {\n throw new Error(\n `mountPath cannot end with a trailing slash. Remove the '/' from: \"${mountPath}\"`,\n );\n }\n\n // Check for reserved path prefixes\n for (const reserved of RESERVED_MOUNT_PATHS) {\n if (mountPath === reserved || mountPath.startsWith(`${reserved}/`)) {\n throw new Error(\n `mountPath cannot begin with a reserved path: ${RESERVED_MOUNT_PATHS.join(\", \")}. Got: \"${mountPath}\"`,\n );\n }\n }\n\n this.handler = this.toHandler(appOrHandler);\n this._rawApp =\n typeof appOrHandler === \"function\" ? undefined : appOrHandler;\n\n const webApps = getMooseInternal().webApps;\n if (webApps.has(name)) {\n throw new Error(`WebApp with name ${name} already exists`);\n }\n\n // Check for duplicate mountPath\n if (this.config.mountPath) {\n for (const [existingName, existingApp] of webApps) {\n if (existingApp.config.mountPath === this.config.mountPath) {\n throw new Error(\n `WebApp with mountPath \"${this.config.mountPath}\" already exists (used by WebApp \"${existingName}\")`,\n );\n }\n }\n }\n\n webApps.set(name, this);\n }\n\n private toHandler(appOrHandler: FrameworkApp | WebAppHandler): WebAppHandler {\n if (typeof appOrHandler === \"function\") {\n return appOrHandler as WebAppHandler;\n }\n\n const app = appOrHandler as FrameworkApp;\n\n if (typeof app.handle === \"function\") {\n return (req, res) => {\n app.handle!(req, res, (err?: any) => {\n if (err) {\n console.error(\"WebApp handler error:\", err);\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Internal Server Error\" }));\n }\n }\n });\n };\n }\n\n if (typeof app.callback === \"function\") {\n return app.callback();\n }\n\n // Fastify: routing is a function that handles requests directly\n // Fastify requires .ready() to be called before routes are available\n if (typeof app.routing === \"function\") {\n // Capture references to avoid TypeScript narrowing issues in closure\n const routing = app.routing;\n const appWithReady = app;\n\n // Use lazy initialization - don't call ready() during module loading\n // This prevents blocking the event loop when streaming functions import the app module\n // The ready() call is deferred to the first actual HTTP request\n let readyPromise: PromiseLike<unknown> | null = null;\n\n return async (req, res) => {\n // Lazy init - only call ready() when first request comes in\n if (readyPromise === null) {\n readyPromise =\n typeof appWithReady.ready === \"function\" ?\n appWithReady.ready()\n : Promise.resolve();\n }\n await readyPromise;\n routing(req, res);\n };\n }\n\n throw new Error(\n `Unable to convert app to handler. The provided object must be:\n - A function (raw Node.js handler)\n - An object with .handle() method (Express, Connect)\n - An object with .callback() method (Koa)\n - An object with .routing function (Fastify)\n \nExamples:\n Express: new WebApp(\"name\", expressApp)\n Koa: new WebApp(\"name\", koaApp)\n Fastify: new WebApp(\"name\", fastifyApp)\n Raw: new WebApp(\"name\", (req, res) => { ... })\n `,\n );\n }\n\n getRawApp(): FrameworkApp | undefined {\n return this._rawApp;\n }\n}\n","/**\n * @module registry\n * Public registry functions for accessing Moose Data Model v2 (dmv2) resources.\n *\n * This module provides functions to retrieve registered resources like tables, streams,\n * APIs, and more. These functions are part of the public API and can be used by\n * user applications to inspect and interact with registered Moose resources.\n */\n\nimport { OlapTable } from \"./sdk/olapTable\";\nimport { Stream } from \"./sdk/stream\";\nimport { IngestApi } from \"./sdk/ingestApi\";\nimport { Api } from \"./sdk/consumptionApi\";\nimport { SqlResource } from \"./sdk/sqlResource\";\nimport { Workflow } from \"./sdk/workflow\";\nimport { WebApp } from \"./sdk/webApp\";\nimport { MaterializedView } from \"./sdk/materializedView\";\nimport { View } from \"./sdk/view\";\nimport { getMooseInternal } from \"./internal\";\n\n/**\n * Get all registered OLAP tables.\n * @returns A Map of table name to OlapTable instance\n */\nexport function getTables(): Map<string, OlapTable<any>> {\n return getMooseInternal().tables;\n}\n\n/**\n * Get a registered OLAP table by name.\n * @param name - The name of the table\n * @returns The OlapTable instance or undefined if not found\n */\nexport function getTable(name: string): OlapTable<any> | undefined {\n return getMooseInternal().tables.get(name);\n}\n\n/**\n * Get all registered streams.\n * @returns A Map of stream name to Stream instance\n */\nexport function getStreams(): Map<string, Stream<any>> {\n return getMooseInternal().streams;\n}\n\n/**\n * Get a registered stream by name.\n * @param name - The name of the stream\n * @returns The Stream instance or undefined if not found\n */\nexport function getStream(name: string): Stream<any> | undefined {\n return getMooseInternal().streams.get(name);\n}\n\n/**\n * Get all registered ingestion APIs.\n * @returns A Map of API name to IngestApi instance\n */\nexport function getIngestApis(): Map<string, IngestApi<any>> {\n return getMooseInternal().ingestApis;\n}\n\n/**\n * Get a registered ingestion API by name.\n * @param name - The name of the ingestion API\n * @returns The IngestApi instance or undefined if not found\n */\nexport function getIngestApi(name: string): IngestApi<any> | undefined {\n return getMooseInternal().ingestApis.get(name);\n}\n\n/**\n * Get all registered APIs (consumption/egress APIs).\n * @returns A Map of API key to Api instance\n */\nexport function getApis(): Map<string, Api<any>> {\n return getMooseInternal().apis;\n}\n\n/**\n * Get a registered API by name, version, or path.\n *\n * Supports multiple lookup strategies:\n * 1. Direct lookup by full key (name:version or name for unversioned)\n * 2. Lookup by name with automatic version aliasing when only one versioned API exists\n * 3. Lookup by custom path (if configured)\n *\n * @param nameOrPath - The name, name:version, or custom path of the API\n * @returns The Api instance or undefined if not found\n */\nexport function getApi(nameOrPath: string): Api<any> | undefined {\n const registry = getMooseInternal();\n\n // Try direct lookup first (full key: name or name:version)\n const directMatch = registry.apis.get(nameOrPath);\n if (directMatch) {\n return directMatch;\n }\n\n // Build alias maps on-demand for unversioned lookups\n const versionedApis = new Map<string, Api<any>[]>();\n const pathMap = new Map<string, Api<any>>();\n\n registry.apis.forEach((api, key) => {\n // Track APIs by base name for aliasing\n const baseName = api.name;\n if (!versionedApis.has(baseName)) {\n versionedApis.set(baseName, []);\n }\n versionedApis.get(baseName)!.push(api);\n\n // Track APIs by custom path\n if (api.config.path) {\n pathMap.set(api.config.path, api);\n }\n });\n\n // Try alias lookup: if there's exactly one API with this base name, return it\n const candidates = versionedApis.get(nameOrPath);\n if (candidates && candidates.length === 1) {\n return candidates[0];\n }\n\n // Try path-based lookup\n return pathMap.get(nameOrPath);\n}\n\n/**\n * Get all registered SQL resources.\n * @returns A Map of resource name to SqlResource instance\n */\nexport function getSqlResources(): Map<string, SqlResource> {\n return getMooseInternal().sqlResources;\n}\n\n/**\n * Get a registered SQL resource by name.\n * @param name - The name of the SQL resource\n * @returns The SqlResource instance or undefined if not found\n */\nexport function getSqlResource(name: string): SqlResource | undefined {\n return getMooseInternal().sqlResources.get(name);\n}\n\n/**\n * Get all registered workflows.\n * @returns A Map of workflow name to Workflow instance\n */\nexport function getWorkflows(): Map<string, Workflow> {\n return getMooseInternal().workflows;\n}\n\n/**\n * Get a registered workflow by name.\n * @param name - The name of the workflow\n * @returns The Workflow instance or undefined if not found\n */\nexport function getWorkflow(name: string): Workflow | undefined {\n return getMooseInternal().workflows.get(name);\n}\n\n/**\n * Get all registered web apps.\n * @returns A Map of web app name to WebApp instance\n */\nexport function getWebApps(): Map<string, WebApp> {\n return getMooseInternal().webApps;\n}\n\n/**\n * Get a registered web app by name.\n * @param name - The name of the web app\n * @returns The WebApp instance or undefined if not found\n */\nexport function getWebApp(name: string): WebApp | undefined {\n return getMooseInternal().webApps.get(name);\n}\n\n/**\n * Get all registered materialized views.\n * @returns A Map of MV name to MaterializedView instance\n */\nexport function getMaterializedViews(): Map<string, MaterializedView<any>> {\n return getMooseInternal().materializedViews;\n}\n\n/**\n * Get a registered materialized view by name.\n * @param name - The name of the materialized view\n * @returns The MaterializedView instance or undefined if not found\n */\nexport function getMaterializedView(\n name: string,\n): MaterializedView<any> | undefined {\n return getMooseInternal().materializedViews.get(name);\n}\n\n/**\n * Get all registered views.\n * @returns A Map of view name to View instance\n */\nexport function getViews(): Map<string, View> {\n return getMooseInternal().views;\n}\n\n/**\n * Get a registered view by name.\n * @param name - The name of the view\n * @returns The View instance or undefined if not found\n */\nexport function getView(name: string): View | undefined {\n return getMooseInternal().views.get(name);\n}\n","/**\n * @module dmv2\n * This module defines the core Moose v2 data model constructs, including OlapTable, Stream, IngestApi, Api,\n * IngestPipeline, View, and MaterializedView. These classes provide a typed interface for defining and managing\n * data infrastructure components like ClickHouse tables, Redpanda streams, and data processing pipelines.\n */\n\n/**\n * A helper type used potentially for indicating aggregated fields in query results or schemas.\n * Captures the aggregation function name and argument types.\n * (Usage context might be specific to query builders or ORM features).\n *\n * @template AggregationFunction The name of the aggregation function (e.g., 'sum', 'avg', 'count').\n * @template ArgTypes An array type representing the types of the arguments passed to the aggregation function.\n */\nexport type Aggregated<\n AggregationFunction extends string,\n ArgTypes extends any[] = [],\n> = {\n _aggregationFunction?: AggregationFunction;\n _argTypes?: ArgTypes;\n};\n\n/**\n * A helper type for SimpleAggregateFunction in ClickHouse.\n * SimpleAggregateFunction stores the aggregated value directly instead of intermediate states,\n * offering better performance for functions like sum, max, min, any, anyLast, etc.\n *\n * @template AggregationFunction The name of the simple aggregation function (e.g., 'sum', 'max', 'anyLast').\n * @template ArgType The type of the argument (and result) of the aggregation function.\n *\n * @example\n * ```typescript\n * interface Stats {\n * rowCount: number & SimpleAggregated<'sum', number>;\n * maxValue: number & SimpleAggregated<'max', number>;\n * lastStatus: string & SimpleAggregated<'anyLast', string>;\n * }\n * ```\n */\nexport type SimpleAggregated<\n AggregationFunction extends string,\n ArgType = any,\n> = {\n _simpleAggregationFunction?: AggregationFunction;\n _argType?: ArgType;\n};\n\nexport { OlapTable, OlapConfig, S3QueueTableSettings } from \"./sdk/olapTable\";\nexport { ClickHouseEngines } from \"../dataModels/types\";\nexport {\n Stream,\n StreamConfig,\n DeadLetterModel,\n DeadLetter,\n DeadLetterQueue,\n ConsumerConfig,\n TransformConfig,\n} from \"./sdk/stream\";\n\nexport { Workflow, Task } from \"./sdk/workflow\";\nexport type { TaskContext, TaskConfig } from \"./sdk/workflow\";\n\nexport { IngestApi, IngestConfig } from \"./sdk/ingestApi\";\nexport {\n Api,\n ApiConfig,\n EgressConfig,\n ConsumptionApi,\n} from \"./sdk/consumptionApi\";\nexport { IngestPipeline, IngestPipelineConfig } from \"./sdk/ingestPipeline\";\nexport { ETLPipeline, ETLPipelineConfig } from \"./sdk/etlPipeline\";\nexport {\n MaterializedView,\n MaterializedViewConfig,\n} from \"./sdk/materializedView\";\nexport { SqlResource } from \"./sdk/sqlResource\";\nexport { View } from \"./sdk/view\";\nexport { LifeCycle } from \"./sdk/lifeCycle\";\nexport {\n WebApp,\n WebAppConfig,\n WebAppHandler,\n FrameworkApp,\n} from \"./sdk/webApp\";\n\nexport {\n getTables,\n getTable,\n getStreams,\n getStream,\n getIngestApis,\n getIngestApi,\n getApis,\n getApi,\n getSqlResources,\n getSqlResource,\n getWorkflows,\n getWorkflow,\n getWebApps,\n getWebApp,\n getMaterializedViews,\n getMaterializedView,\n getViews,\n getView,\n} from \"./registry\";\n","export type Key<T extends string | number | Date> = T;\n\nexport type JWT<T extends object> = T;\n\nexport {\n ClickHouseEngines,\n Aggregated,\n SimpleAggregated,\n OlapTable,\n OlapConfig,\n S3QueueTableSettings,\n Stream,\n StreamConfig,\n DeadLetterModel,\n DeadLetter,\n DeadLetterQueue,\n IngestApi,\n IngestConfig,\n Api,\n ApiConfig,\n ConsumptionApi,\n EgressConfig,\n IngestPipeline,\n SqlResource,\n View,\n MaterializedView,\n Task,\n Workflow,\n ETLPipeline,\n ETLPipelineConfig,\n LifeCycle,\n WebApp,\n WebAppConfig,\n WebAppHandler,\n FrameworkApp,\n // Registry functions\n getTables,\n getTable,\n getStreams,\n getStream,\n getIngestApis,\n getIngestApi,\n getApis,\n getApi,\n getSqlResources,\n getSqlResource,\n getWorkflows,\n getWorkflow,\n getWebApps,\n getWebApp,\n getView,\n getViews,\n getMaterializedView,\n getMaterializedViews,\n} from \"./dmv2\";\n\nexport {\n ClickHousePrecision,\n ClickHouseDecimal,\n ClickHouseByteSize,\n ClickHouseFixedStringSize,\n ClickHouseFloat,\n ClickHouseInt,\n ClickHouseJson,\n LowCardinality,\n ClickHouseNamedTuple,\n ClickHouseDefault,\n ClickHouseTTL,\n ClickHouseMaterialized,\n ClickHouseAlias,\n WithDefault,\n ClickHouseCodec,\n // Added friendly aliases and numeric helpers\n DateTime,\n DateTime64,\n DateTimeString,\n DateTime64String,\n FixedString,\n Float32,\n Float64,\n Int8,\n Int16,\n Int32,\n Int64,\n UInt8,\n UInt16,\n UInt32,\n UInt64,\n Decimal,\n} from \"./dataModels/types\";\n\nexport type { ApiUtil, ConsumptionUtil } from \"./consumption-apis/helpers\";\n\nexport * from \"./sqlHelpers\";\n","import {\n existsSync,\n readdirSync,\n readFileSync,\n statSync,\n writeFileSync,\n} from \"fs\";\nimport nodePath from \"path\";\nimport { createClient } from \"@clickhouse/client\";\nimport { KafkaJS } from \"@514labs/kafka-javascript\";\nimport { SASLOptions } from \"@514labs/kafka-javascript/types/kafkajs\";\nconst { Kafka } = KafkaJS;\ntype Kafka = KafkaJS.Kafka;\ntype Consumer = KafkaJS.Consumer;\nexport type Producer = KafkaJS.Producer;\n\n/**\n * Utility function for compiler-related logging that can be disabled via environment variable.\n * Set MOOSE_DISABLE_COMPILER_LOGS=true to suppress these logs (useful for testing environments).\n */\n\n/**\n * Returns true if the value is a common truthy string: \"1\", \"true\", \"yes\", \"on\" (case-insensitive).\n */\nfunction isTruthy(value: string | undefined): boolean {\n if (!value) return false;\n switch (value.trim().toLowerCase()) {\n case \"1\":\n case \"true\":\n case \"yes\":\n case \"on\":\n return true;\n default:\n return false;\n }\n}\n\nexport const compilerLog = (message: string) => {\n if (!isTruthy(process.env.MOOSE_DISABLE_COMPILER_LOGS)) {\n console.log(message);\n }\n};\n\nexport const antiCachePath = (path: string) =>\n `${path}?num=${Math.random().toString()}&time=${Date.now()}`;\n\nexport const getFileName = (filePath: string) => {\n const regex = /\\/([^\\/]+)\\.ts/;\n const matches = filePath.match(regex);\n if (matches && matches.length > 1) {\n return matches[1];\n }\n return \"\";\n};\n\ninterface ClientConfig {\n username: string;\n password: string;\n database: string;\n useSSL: string;\n host: string;\n port: string;\n}\n\nexport const getClickhouseClient = ({\n username,\n password,\n database,\n useSSL,\n host,\n port,\n}: ClientConfig) => {\n const protocol =\n useSSL === \"1\" || useSSL.toLowerCase() === \"true\" ? \"https\" : \"http\";\n console.log(`Connecting to Clickhouse at ${protocol}://${host}:${port}`);\n return createClient({\n url: `${protocol}://${host}:${port}`,\n username: username,\n password: password,\n database: database,\n application: \"moose\",\n // Note: wait_end_of_query is configured per operation type, not globally\n // to preserve SELECT query performance while ensuring INSERT/DDL reliability\n });\n};\n\nexport type CliLogData = {\n message_type?: \"Info\" | \"Success\" | \"Warning\" | \"Error\" | \"Highlight\";\n action: string;\n message: string;\n};\n\nexport const cliLog: (log: CliLogData) => void = (log) => {\n const level =\n log.message_type === \"Error\" ? \"error\"\n : log.message_type === \"Warning\" ? \"warn\"\n : \"info\";\n\n const structuredLog = {\n __moose_structured_log__: true,\n level,\n message: log.message,\n resource_type: \"runtime\",\n cli_action: log.action,\n cli_message_type: log.message_type ?? \"Info\",\n timestamp: new Date().toISOString(),\n };\n\n process.stderr.write(JSON.stringify(structuredLog) + \"\\n\");\n};\n\n/**\n * Method to change .ts, .cts, and .mts to .js, .cjs, and .mjs\n * This is needed because 'import' does not support .ts, .cts, and .mts\n */\nexport function mapTstoJs(filePath: string): string {\n return filePath\n .replace(/\\.ts$/, \".js\")\n .replace(/\\.cts$/, \".cjs\")\n .replace(/\\.mts$/, \".mjs\");\n}\n\n/**\n * Walks a directory recursively and returns all files matching the given extensions.\n * Skips node_modules directories.\n *\n * @param dir - Directory to walk\n * @param extensions - File extensions to include (e.g., [\".js\", \".mjs\"])\n * @returns Array of file paths\n */\nfunction walkDirectory(dir: string, extensions: string[]): string[] {\n const results: string[] = [];\n\n if (!existsSync(dir)) {\n return results;\n }\n\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = nodePath.join(dir, entry.name);\n\n if (entry.isDirectory()) {\n // Skip node_modules\n if (entry.name !== \"node_modules\") {\n results.push(...walkDirectory(fullPath, extensions));\n }\n } else if (entry.isFile()) {\n const ext = nodePath.extname(entry.name);\n if (extensions.includes(ext)) {\n results.push(fullPath);\n }\n }\n }\n } catch (e) {\n // Log error in case it is something the user can/should act on\n console.debug(`[moose] Failed to read directory ${dir}:`, e);\n }\n\n return results;\n}\n\n/**\n * Adds .js extension to relative import paths that don't have an extension.\n * Handles import/export from statements and dynamic imports.\n *\n * @param content - JavaScript file content\n * @param fileDir - Directory containing the file being processed (for resolving paths)\n * @returns Content with .js extensions added to relative imports\n */\nfunction addJsExtensionToImports(content: string, fileDir?: string): string {\n // Pattern for 'from' statements with relative paths\n // Matches: from './foo', from \"../bar\", from './utils/helper'\n const fromPattern = /(from\\s+['\"])(\\.\\.?\\/[^'\"]*?)(['\"])/g;\n\n // Pattern for side-effect-only imports with relative paths\n // Matches: import './foo', import \"../bar\" (no 'from' keyword, just importing for side effects)\n const bareImportPattern = /(import\\s+['\"])(\\.\\.?\\/[^'\"]*?)(['\"])/g;\n\n // Pattern for dynamic imports with relative paths\n // Matches: import('./foo'), import(\"../bar\")\n const dynamicPattern = /(import\\s*\\(\\s*['\"])(\\.\\.?\\/[^'\"]*?)(['\"])/g;\n\n let result = content;\n\n // Process 'from' statements\n result = result.replace(fromPattern, (match, prefix, importPath, quote) => {\n return rewriteImportPath(match, prefix, importPath, quote, fileDir);\n });\n\n // Process side-effect-only imports\n result = result.replace(\n bareImportPattern,\n (match, prefix, importPath, quote) => {\n return rewriteImportPath(match, prefix, importPath, quote, fileDir);\n },\n );\n\n // Process dynamic imports\n result = result.replace(\n dynamicPattern,\n (match, prefix, importPath, quote) => {\n return rewriteImportPath(match, prefix, importPath, quote, fileDir);\n },\n );\n\n return result;\n}\n\n/**\n * Rewrites a single import path to add .js extension if needed.\n * Handles directory imports with index files correctly.\n *\n * @param match - The full matched string\n * @param prefix - The prefix (from ' or import(')\n * @param importPath - The import path\n * @param quote - The closing quote\n * @param fileDir - Directory containing the file being processed (for resolving paths)\n * @returns The rewritten import or original if no change needed\n */\nfunction rewriteImportPath(\n match: string,\n prefix: string,\n importPath: string,\n quote: string,\n fileDir?: string,\n): string {\n // Skip if already has a JavaScript extension\n if (/\\.[cm]?js$/.test(importPath)) {\n return match;\n }\n\n // Skip if importing a JSON file\n if (/\\.json$/.test(importPath)) {\n return match;\n }\n\n // If we have the file directory, check if the import target is a directory with index.js\n if (fileDir) {\n const resolvedPath = nodePath.resolve(fileDir, importPath);\n\n // Check if path.js exists (regular file import)\n if (existsSync(`${resolvedPath}.js`)) {\n return `${prefix}${importPath}.js${quote}`;\n }\n\n // Check if path/index.js exists (directory import)\n if (existsSync(nodePath.join(resolvedPath, \"index.js\"))) {\n // For directory imports, rewrite to include /index.js\n return `${prefix}${importPath}/index.js${quote}`;\n }\n\n // Check for .mjs variants\n if (existsSync(`${resolvedPath}.mjs`)) {\n return `${prefix}${importPath}.mjs${quote}`;\n }\n if (existsSync(nodePath.join(resolvedPath, \"index.mjs\"))) {\n return `${prefix}${importPath}/index.mjs${quote}`;\n }\n\n // Check for .cjs variants\n if (existsSync(`${resolvedPath}.cjs`)) {\n return `${prefix}${importPath}.cjs${quote}`;\n }\n if (existsSync(nodePath.join(resolvedPath, \"index.cjs\"))) {\n return `${prefix}${importPath}/index.cjs${quote}`;\n }\n }\n\n // Default: add .js extension (fallback when no fileDir or file not found)\n return `${prefix}${importPath}.js${quote}`;\n}\n\n/**\n * Rewrites relative import paths in JavaScript files to include .js extensions.\n * This is required for Node.js ESM which requires explicit extensions.\n *\n * Handles:\n * - import statements: import { foo } from './bar' -> import { foo } from './bar.js'\n * - dynamic imports: import('./bar') -> import('./bar.js')\n * - re-exports: export { foo } from './bar' -> export { foo } from './bar.js'\n *\n * Does NOT modify:\n * - Package imports (no leading . or ..)\n * - Imports that already have extensions\n * - Imports from node_modules\n *\n * @param outDir - Directory containing compiled JavaScript files\n */\nexport function rewriteImportExtensions(outDir: string): void {\n const files = walkDirectory(outDir, [\".js\", \".mjs\"]);\n\n for (const filePath of files) {\n const content = readFileSync(filePath, \"utf-8\");\n const fileDir = nodePath.dirname(filePath);\n const rewritten = addJsExtensionToImports(content, fileDir);\n\n if (content !== rewritten) {\n writeFileSync(filePath, rewritten, \"utf-8\");\n }\n }\n}\n\nexport const MAX_RETRIES = 150;\nexport const MAX_RETRY_TIME_MS = 1000;\nexport const RETRY_INITIAL_TIME_MS = 100;\n\nexport const MAX_RETRIES_PRODUCER = 150;\nexport const RETRY_FACTOR_PRODUCER = 0.2;\n// Means all replicas need to acknowledge the message\nexport const ACKs = -1;\n\n/**\n * Creates the base producer configuration for Kafka.\n * Used by both the SDK stream publishing and streaming function workers.\n *\n * @param maxMessageBytes - Optional max message size in bytes (synced with topic config)\n * @returns Producer configuration object for the Confluent Kafka client\n */\nexport function createProducerConfig(maxMessageBytes?: number) {\n return {\n kafkaJS: {\n idempotent: false, // Not needed for at-least-once delivery\n acks: ACKs,\n retry: {\n retries: MAX_RETRIES_PRODUCER,\n maxRetryTime: MAX_RETRY_TIME_MS,\n },\n },\n \"linger.ms\": 0, // This is to make sure at least once delivery with immediate feedback on the send\n ...(maxMessageBytes && { \"message.max.bytes\": maxMessageBytes }),\n };\n}\n\n/**\n * Parses a comma-separated broker string into an array of valid broker addresses.\n * Handles whitespace trimming and filters out empty elements.\n *\n * @param brokerString - Comma-separated broker addresses (e.g., \"broker1:9092, broker2:9092, , broker3:9092\")\n * @returns Array of trimmed, non-empty broker addresses\n */\nconst parseBrokerString = (brokerString: string): string[] =>\n brokerString\n .split(\",\")\n .map((b) => b.trim())\n .filter((b) => b.length > 0);\n\nexport type KafkaClientConfig = {\n clientId: string;\n broker: string;\n securityProtocol?: string; // e.g. \"SASL_SSL\" or \"PLAINTEXT\"\n saslUsername?: string;\n saslPassword?: string;\n saslMechanism?: string; // e.g. \"scram-sha-256\", \"plain\"\n};\n\n/**\n * Dynamically creates and connects a KafkaJS producer using the provided configuration.\n * Returns a connected producer instance.\n *\n * @param cfg - Kafka client configuration\n * @param logger - Logger instance\n * @param maxMessageBytes - Optional max message size in bytes (synced with topic config)\n */\nexport async function getKafkaProducer(\n cfg: KafkaClientConfig,\n logger: Logger,\n maxMessageBytes?: number,\n): Promise<Producer> {\n const kafka = await getKafkaClient(cfg, logger);\n\n const producer = kafka.producer(createProducerConfig(maxMessageBytes));\n await producer.connect();\n return producer;\n}\n\n/**\n * Interface for logging functionality\n */\nexport interface Logger {\n logPrefix: string;\n log: (message: string) => void;\n error: (message: string) => void;\n warn: (message: string) => void;\n}\n\nexport const logError = (logger: Logger, e: Error): void => {\n logger.error(e.message);\n const stack = e.stack;\n if (stack) {\n logger.error(stack);\n }\n};\n\n/**\n * Builds SASL configuration for Kafka client authentication\n */\nconst buildSaslConfig = (\n logger: Logger,\n args: KafkaClientConfig,\n): SASLOptions | undefined => {\n const mechanism = args.saslMechanism ? args.saslMechanism.toLowerCase() : \"\";\n switch (mechanism) {\n case \"plain\":\n case \"scram-sha-256\":\n case \"scram-sha-512\":\n return {\n mechanism: mechanism,\n username: args.saslUsername || \"\",\n password: args.saslPassword || \"\",\n };\n default:\n logger.warn(`Unsupported SASL mechanism: ${args.saslMechanism}`);\n return undefined;\n }\n};\n\n/**\n * Dynamically creates a KafkaJS client configured with provided settings.\n * Use this to construct producers/consumers with custom options.\n */\nexport const getKafkaClient = async (\n cfg: KafkaClientConfig,\n logger: Logger,\n): Promise<Kafka> => {\n const brokers = parseBrokerString(cfg.broker || \"\");\n if (brokers.length === 0) {\n throw new Error(`No valid broker addresses found in: \"${cfg.broker}\"`);\n }\n\n logger.log(`Creating Kafka client with brokers: ${brokers.join(\", \")}`);\n logger.log(`Security protocol: ${cfg.securityProtocol || \"plaintext\"}`);\n logger.log(`Client ID: ${cfg.clientId}`);\n\n const saslConfig = buildSaslConfig(logger, cfg);\n\n return new Kafka({\n kafkaJS: {\n clientId: cfg.clientId,\n brokers,\n ssl: cfg.securityProtocol === \"SASL_SSL\",\n ...(saslConfig && { sasl: saslConfig }),\n retry: {\n initialRetryTime: RETRY_INITIAL_TIME_MS,\n maxRetryTime: MAX_RETRY_TIME_MS,\n retries: MAX_RETRIES,\n },\n },\n });\n};\n","/**\n * @module secrets\n * Utilities for runtime environment variable resolution.\n *\n * This module provides functionality to mark values that should be resolved\n * from environment variables at runtime by the Moose CLI, rather than being\n * embedded at build time.\n *\n * @example\n * ```typescript\n * import { S3QueueEngine, mooseRuntimeEnv } from 'moose-lib';\n *\n * const table = OlapTable<MyData>(\n * \"MyTable\",\n * OlapConfig({\n * engine: S3QueueEngine({\n * s3_path: \"s3://bucket/data/*.json\",\n * format: \"JSONEachRow\",\n * awsAccessKeyId: mooseRuntimeEnv.get(\"AWS_ACCESS_KEY_ID\"),\n * awsSecretAccessKey: mooseRuntimeEnv.get(\"AWS_SECRET_ACCESS_KEY\")\n * })\n * })\n * );\n * ```\n */\n\n/**\n * Prefix used to mark values for runtime environment variable resolution.\n * @internal\n */\nexport const MOOSE_RUNTIME_ENV_PREFIX = \"__MOOSE_RUNTIME_ENV__:\";\n\n/**\n * Utilities for marking values to be resolved from environment variables at runtime.\n *\n * When you use `mooseRuntimeEnv.get()`, the behavior depends on the context:\n * - During infrastructure map loading: Returns a marker string for later resolution\n * - During function/workflow execution: Returns the actual environment variable value\n *\n * This is useful for:\n * - Credentials that should never be embedded in Docker images\n * - Configuration that can be rotated without rebuilding\n * - Different values for different environments (dev, staging, prod)\n * - Any runtime configuration in infrastructure elements (Tables, Topics, etc.)\n */\nexport const mooseRuntimeEnv = {\n /**\n * Gets a value from an environment variable, with behavior depending on context.\n *\n * When IS_LOADING_INFRA_MAP=true (infrastructure loading):\n * Returns a marker string that Moose CLI will resolve later\n *\n * When IS_LOADING_INFRA_MAP is unset (function/workflow runtime):\n * Returns the actual value from the environment variable\n *\n * @param envVarName - Name of the environment variable to resolve\n * @returns Either a marker string or the actual environment variable value\n * @throws {Error} If the environment variable name is empty\n * @throws {Error} If the environment variable is not set (runtime mode only)\n *\n * @example\n * ```typescript\n * // Instead of this (evaluated at build time):\n * awsAccessKeyId: process.env.AWS_ACCESS_KEY_ID\n *\n * // Use this (evaluated at runtime):\n * awsAccessKeyId: mooseRuntimeEnv.get(\"AWS_ACCESS_KEY_ID\")\n * ```\n */\n get(envVarName: string): string {\n if (!envVarName || envVarName.trim() === \"\") {\n throw new Error(\"Environment variable name cannot be empty\");\n }\n\n // Check if we're loading infrastructure map\n const isLoadingInfraMap = process.env.IS_LOADING_INFRA_MAP === \"true\";\n\n if (isLoadingInfraMap) {\n // Return marker string for later resolution by Moose CLI\n return `${MOOSE_RUNTIME_ENV_PREFIX}${envVarName}`;\n } else {\n // Return actual value from environment for runtime execution\n const value = process.env[envVarName];\n if (value === undefined) {\n throw new Error(\n `Environment variable '${envVarName}' is not set. ` +\n `This is required for runtime execution of functions/workflows.`,\n );\n }\n return value;\n }\n },\n};\n\n// Legacy export for backwards compatibility\n/** @deprecated Use mooseRuntimeEnv instead */\nexport const mooseEnvSecrets = mooseRuntimeEnv;\n","import { ClickHouseClient, CommandResult, ResultSet } from \"@clickhouse/client\";\nimport {\n Client as TemporalClient,\n Connection,\n ConnectionOptions,\n} from \"@temporalio/client\";\nimport { StringValue } from \"@temporalio/common\";\nimport { createHash, randomUUID } from \"node:crypto\";\nimport { performance } from \"perf_hooks\";\nimport * as fs from \"fs\";\nimport { getWorkflows } from \"../dmv2/internal\";\nimport { JWTPayload } from \"jose\";\nimport { Sql, sql, RawValue, toQuery, toQueryPreview } from \"../sqlHelpers\";\n\n/**\n * Format elapsed milliseconds into a human-readable string.\n * Matches Python's format_timespan behavior.\n */\nfunction formatElapsedTime(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)} ms`;\n }\n const seconds = ms / 1000;\n if (seconds < 60) {\n return `${seconds.toFixed(2)} seconds`;\n }\n const minutes = Math.floor(seconds / 60);\n const remainingSeconds = seconds % 60;\n return `${minutes} minutes and ${remainingSeconds.toFixed(2)} seconds`;\n}\n\n/**\n * Utilities provided by getMooseUtils() for database access and SQL queries.\n * Works in both Moose runtime and standalone contexts.\n */\nexport interface MooseUtils {\n client: MooseClient;\n sql: typeof sql;\n jwt?: JWTPayload;\n}\n\n/**\n * @deprecated Use MooseUtils instead. ApiUtil is now a type alias to MooseUtils\n * and will be removed in a future version.\n *\n * Migration: Replace `ApiUtil` with `MooseUtils` in your type annotations.\n */\nexport type ApiUtil = MooseUtils;\n\n/** @deprecated Use MooseUtils instead. */\nexport type ConsumptionUtil = MooseUtils;\n\nexport class MooseClient {\n query: QueryClient;\n workflow: WorkflowClient;\n\n constructor(queryClient: QueryClient, temporalClient?: TemporalClient) {\n this.query = queryClient;\n this.workflow = new WorkflowClient(temporalClient);\n }\n}\n\nexport class QueryClient {\n client: ClickHouseClient;\n query_id_prefix: string;\n constructor(client: ClickHouseClient, query_id_prefix: string) {\n this.client = client;\n this.query_id_prefix = query_id_prefix;\n }\n\n async execute<T = any>(\n sql: Sql,\n ): Promise<ResultSet<\"JSONEachRow\"> & { __query_result_t?: T[] }> {\n const [query, query_params] = toQuery(sql);\n\n console.log(`[QueryClient] | Query: ${toQueryPreview(sql)}`);\n const start = performance.now();\n const result = await this.client.query({\n query,\n query_params,\n format: \"JSONEachRow\",\n query_id: this.query_id_prefix + randomUUID(),\n // Note: wait_end_of_query deliberately NOT set here as this is used for SELECT queries\n // where response buffering would harm streaming performance and concurrency\n });\n const elapsedMs = performance.now() - start;\n console.log(\n `[QueryClient] | Query completed: ${formatElapsedTime(elapsedMs)}`,\n );\n return result;\n }\n\n async command(sql: Sql): Promise<CommandResult> {\n const [query, query_params] = toQuery(sql);\n\n console.log(`[QueryClient] | Command: ${toQueryPreview(sql)}`);\n const start = performance.now();\n const result = await this.client.command({\n query,\n query_params,\n query_id: this.query_id_prefix + randomUUID(),\n });\n const elapsedMs = performance.now() - start;\n console.log(\n `[QueryClient] | Command completed: ${formatElapsedTime(elapsedMs)}`,\n );\n return result;\n }\n}\n\nexport class WorkflowClient {\n client: TemporalClient | undefined;\n\n constructor(temporalClient?: TemporalClient) {\n this.client = temporalClient;\n }\n\n async execute(name: string, input_data: any) {\n try {\n if (!this.client) {\n return {\n status: 404,\n body: `Temporal client not found. Is the feature flag enabled?`,\n };\n }\n\n // Get workflow configuration\n const config = await this.getWorkflowConfig(name);\n\n // Process input data and generate workflow ID\n const [processedInput, workflowId] = this.processInputData(\n name,\n input_data,\n );\n\n console.log(\n `WorkflowClient - starting workflow: ${name} with config ${JSON.stringify(config)} and input_data ${JSON.stringify(processedInput)}`,\n );\n\n const handle = await this.client.workflow.start(\"ScriptWorkflow\", {\n args: [\n { workflow_name: name, execution_mode: \"start\" as const },\n processedInput,\n ],\n taskQueue: \"typescript-script-queue\",\n workflowId,\n workflowIdConflictPolicy: \"FAIL\",\n workflowIdReusePolicy: \"ALLOW_DUPLICATE\",\n retry: {\n // Temporal's maximumAttempts = total attempts (initial + retries)\n maximumAttempts: config.retries + 1,\n },\n workflowRunTimeout: config.timeout as StringValue,\n });\n\n return {\n status: 200,\n body: `Workflow started: ${name}. View it in the Temporal dashboard: http://localhost:8080/namespaces/default/workflows/${workflowId}/${handle.firstExecutionRunId}/history`,\n };\n } catch (error) {\n return {\n status: 400,\n body: `Error starting workflow: ${error}`,\n };\n }\n }\n\n async terminate(workflowId: string) {\n try {\n if (!this.client) {\n return {\n status: 404,\n body: `Temporal client not found. Is the feature flag enabled?`,\n };\n }\n\n const handle = this.client.workflow.getHandle(workflowId);\n await handle.terminate();\n\n return {\n status: 200,\n body: `Workflow terminated: ${workflowId}`,\n };\n } catch (error) {\n return {\n status: 400,\n body: `Error terminating workflow: ${error}`,\n };\n }\n }\n\n private async getWorkflowConfig(\n name: string,\n ): Promise<{ retries: number; timeout: string }> {\n const workflows = await getWorkflows();\n const workflow = workflows.get(name);\n if (workflow) {\n return {\n retries: workflow.config.retries || 3,\n timeout: workflow.config.timeout || \"1h\",\n };\n }\n\n throw new Error(`Workflow config not found for ${name}`);\n }\n\n private processInputData(name: string, input_data: any): [any, string] {\n let workflowId = name;\n if (input_data) {\n const hash = createHash(\"sha256\")\n .update(JSON.stringify(input_data))\n .digest(\"hex\")\n .slice(0, 16);\n workflowId = `${name}-${hash}`;\n }\n return [input_data, workflowId];\n }\n}\n\n/**\n * This looks similar to the client in runner.ts which is a worker.\n * Temporal SDK uses similar looking connection options & client,\n * but there are different libraries for a worker & client like this one\n * that triggers workflows.\n */\nexport async function getTemporalClient(\n temporalUrl: string,\n namespace: string,\n clientCert: string,\n clientKey: string,\n apiKey: string,\n): Promise<TemporalClient | undefined> {\n try {\n console.info(\n `<api> Using temporal_url: ${temporalUrl} and namespace: ${namespace}`,\n );\n\n let connectionOptions: ConnectionOptions = {\n address: temporalUrl,\n connectTimeout: \"3s\",\n };\n\n if (clientCert && clientKey) {\n // URL with mTLS uses gRPC namespace endpoint which is what temporalUrl already is\n console.log(\"Using TLS for secure Temporal\");\n const cert = await fs.readFileSync(clientCert);\n const key = await fs.readFileSync(clientKey);\n\n connectionOptions.tls = {\n clientCertPair: { crt: cert, key: key },\n };\n } else if (apiKey) {\n console.log(\"Using API key for secure Temporal\");\n // URL with API key uses gRPC regional endpoint\n connectionOptions.address = \"us-west1.gcp.api.temporal.io:7233\";\n connectionOptions.apiKey = apiKey;\n connectionOptions.tls = {};\n connectionOptions.metadata = {\n \"temporal-namespace\": namespace,\n };\n }\n\n console.log(`<api> Connecting to Temporal at ${connectionOptions.address}`);\n const connection = await Connection.connect(connectionOptions);\n const client = new TemporalClient({ connection, namespace });\n console.log(\"<api> Connected to Temporal server\");\n\n return client;\n } catch (error) {\n console.warn(`Failed to connect to Temporal. Is the feature flag enabled?`);\n console.warn(error);\n return undefined;\n }\n}\n\nexport const ApiHelpers = {\n column: (value: string) => [\"Identifier\", value] as [string, string],\n table: (value: string) => [\"Identifier\", value] as [string, string],\n};\n\n/** @deprecated Use ApiHelpers instead. */\nexport const ConsumptionHelpers = ApiHelpers;\n\nexport function joinQueries({\n values,\n separator = \",\",\n prefix = \"\",\n suffix = \"\",\n}: {\n values: readonly RawValue[];\n separator?: string;\n prefix?: string;\n suffix?: string;\n}) {\n if (values.length === 0) {\n throw new TypeError(\n \"Expected `join([])` to be called with an array of multiple elements, but got an empty array\",\n );\n }\n\n return new Sql(\n [prefix, ...Array(values.length - 1).fill(separator), suffix],\n values,\n );\n}\n","import http from \"http\";\nimport type { MooseUtils } from \"./helpers\";\n\n/**\n * @deprecated Use `getMooseUtils()` from '@514labs/moose-lib' instead.\n *\n * This synchronous function extracts MooseUtils from a request object that was\n * injected by Moose runtime middleware. It returns undefined if not running\n * in a Moose-managed context.\n *\n * Migration: Replace with the async version:\n * ```typescript\n * // Old (sync, deprecated):\n * import { getMooseUtilsFromRequest } from '@514labs/moose-lib';\n * const moose = getMooseUtilsFromRequest(req);\n *\n * // New (async, recommended):\n * import { getMooseUtils } from '@514labs/moose-lib';\n * const moose = await getMooseUtils();\n * ```\n *\n * @param req - The HTTP request object containing injected moose utilities\n * @returns MooseUtils if available on the request, undefined otherwise\n */\nexport function getMooseUtilsFromRequest(\n req: http.IncomingMessage | any,\n): MooseUtils | undefined {\n console.warn(\n \"[DEPRECATED] getMooseUtilsFromRequest() is deprecated. \" +\n \"Import getMooseUtils from '@514labs/moose-lib' and call it without parameters: \" +\n \"const { client, sql } = await getMooseUtils();\",\n );\n return (req as any).moose;\n}\n\n/**\n * @deprecated Use `getMooseUtils()` from '@514labs/moose-lib' instead.\n *\n * This is a legacy alias for getMooseUtilsFromRequest. The main getMooseUtils\n * export from '@514labs/moose-lib' is now async and does not require a request parameter.\n *\n * BREAKING CHANGE WARNING: The new getMooseUtils() returns Promise<MooseUtils>,\n * not MooseUtils | undefined. You must await the result:\n * ```typescript\n * const moose = await getMooseUtils(); // New async API\n * ```\n */\nexport const getLegacyMooseUtils = getMooseUtilsFromRequest;\n\n/**\n * @deprecated No longer needed. Use getMooseUtils() directly instead.\n * Moose now handles utility injection automatically when injectMooseUtils is true.\n */\nexport function expressMiddleware() {\n console.warn(\n \"[DEPRECATED] expressMiddleware() is deprecated. \" +\n \"Use getMooseUtils() directly or rely on injectMooseUtils config.\",\n );\n return (req: any, res: any, next: any) => {\n // Maintain backwards compat: copy req.raw.moose to req.moose if present\n if (!req.moose && req.raw && (req.raw as any).moose) {\n req.moose = (req.raw as any).moose;\n }\n next();\n };\n}\n\n/**\n * @deprecated Use MooseUtils from helpers.ts instead.\n */\nexport interface ExpressRequestWithMoose {\n moose?: MooseUtils;\n}\n","export interface TaskFunction {\n (input?: any): Promise<{ task: string; data: any }>;\n}\n\nexport interface TaskConfig {\n retries: number;\n}\n\nexport interface TaskDefinition {\n task: TaskFunction;\n config?: TaskConfig;\n}\n","import { createClient, RedisClientType } from \"redis\";\n\n// Module-level singleton instance and initialization promise\nlet instance: MooseCache | null = null;\nlet initPromise: Promise<MooseCache> | null = null;\n\ntype SupportedTypes = string | object;\n\nexport class MooseCache {\n private client: RedisClientType;\n private isConnected: boolean = false;\n private readonly keyPrefix: string;\n private disconnectTimer: NodeJS.Timeout | null = null;\n private readonly idleTimeout: number;\n private connectPromise: Promise<void> | null = null;\n\n private constructor() {\n const redisUrl =\n process.env.MOOSE_REDIS_CONFIG__URL || \"redis://127.0.0.1:6379\";\n const prefix = process.env.MOOSE_REDIS_CONFIG__KEY_PREFIX || \"MS\";\n // 30 seconds of inactivity before disconnecting\n this.idleTimeout =\n parseInt(process.env.MOOSE_REDIS_CONFIG__IDLE_TIMEOUT || \"30\", 10) * 1000;\n this.keyPrefix = `${prefix}::moosecache::`;\n\n this.client = createClient({\n url: redisUrl,\n });\n\n process.on(\"SIGTERM\", this.gracefulShutdown);\n process.on(\"SIGINT\", this.gracefulShutdown);\n\n this.client.on(\"error\", async (err: Error) => {\n console.error(\"TS Redis client error:\", err);\n await this.disconnect();\n });\n\n this.client.on(\"connect\", () => {\n this.isConnected = true;\n console.log(\"TS Redis client connected\");\n });\n\n this.client.on(\"end\", () => {\n this.isConnected = false;\n console.log(\"TS Redis client disconnected\");\n this.clearDisconnectTimer();\n });\n }\n\n private clearDisconnectTimer(): void {\n if (this.disconnectTimer) {\n clearTimeout(this.disconnectTimer);\n this.disconnectTimer = null;\n }\n }\n\n private resetDisconnectTimer(): void {\n this.clearDisconnectTimer();\n this.disconnectTimer = setTimeout(async () => {\n if (this.isConnected) {\n console.log(\"TS Redis client disconnecting due to inactivity\");\n await this.disconnect();\n }\n }, this.idleTimeout);\n }\n\n private async ensureConnected(): Promise<void> {\n if (!this.isConnected) {\n await this.connect();\n }\n this.resetDisconnectTimer();\n }\n\n private async connect(): Promise<void> {\n // If already connected, return immediately\n if (this.isConnected) {\n return;\n }\n\n // If connection is in progress, wait for it\n // This prevents race conditions when multiple callers try to reconnect\n // simultaneously after a disconnection\n if (this.connectPromise) {\n return this.connectPromise;\n }\n\n // Start connection\n this.connectPromise = (async () => {\n try {\n await this.client.connect();\n this.resetDisconnectTimer();\n } catch (error) {\n // Reset the promise on error so retries can work\n this.connectPromise = null;\n throw error;\n }\n })();\n\n return this.connectPromise;\n }\n\n private async gracefulShutdown(): Promise<void> {\n if (this.isConnected) {\n await this.disconnect();\n }\n process.exit(0);\n }\n\n private getPrefixedKey(key: string): string {\n return `${this.keyPrefix}${key}`;\n }\n\n /**\n * Gets the singleton instance of MooseCache. Creates a new instance if one doesn't exist.\n * The client will automatically connect to Redis and handle reconnection if needed.\n *\n * @returns Promise<MooseCache> The singleton instance of MooseCache\n * @example\n * const cache = await MooseCache.get();\n */\n public static async get(): Promise<MooseCache> {\n // If we already have an instance, return it immediately\n if (instance) {\n return instance;\n }\n\n // If initialization is already in progress, wait for it\n // This prevents race conditions where multiple concurrent calls to get()\n // would each create their own instance and connection\n //\n // A simple singleton pattern (just checking if instance exists) isn't enough\n // because multiple async calls can check \"if (!instance)\" simultaneously,\n // find it's null, and each try to create their own instance before any\n // of them finish setting the instance variable\n if (initPromise) {\n return initPromise;\n }\n\n // Start initialization\n // We store the promise immediately so that any concurrent calls\n // will wait for this same initialization instead of starting their own\n initPromise = (async () => {\n try {\n const newInstance = new MooseCache();\n await newInstance.connect();\n instance = newInstance;\n return newInstance;\n } catch (error) {\n // Reset the promise on error so retries can work\n initPromise = null;\n throw error;\n }\n })();\n\n return initPromise;\n }\n\n /**\n * Sets a value in the cache. Objects are automatically JSON stringified.\n *\n * @param key - The key to store the value under\n * @param value - The value to store. Can be a string or any object (will be JSON stringified)\n * @param ttlSeconds - Optional time-to-live in seconds. If not provided, defaults to 1 hour (3600 seconds).\n * Must be a non-negative number. If 0, the key will expire immediately.\n * @example\n * // Store a string\n * await cache.set(\"foo\", \"bar\");\n *\n * // Store an object with custom TTL\n * await cache.set(\"foo:config\", { baz: 123, qux: true }, 60); // expires in 1 minute\n *\n * // This is essentially a get-set, which returns the previous value if it exists.\n * // You can create logic to only do work for the first time.\n * const value = await cache.set(\"testSessionId\", \"true\");\n * if (value) {\n * // Cache was set before, return\n * } else {\n * // Cache was set for first time, do work\n * }\n */\n public async set(\n key: string,\n value: string | object,\n ttlSeconds?: number,\n ): Promise<string | null> {\n try {\n // Validate TTL\n if (ttlSeconds !== undefined && ttlSeconds < 0) {\n throw new Error(\"ttlSeconds must be a non-negative number\");\n }\n\n await this.ensureConnected();\n const prefixedKey = this.getPrefixedKey(key);\n const stringValue =\n typeof value === \"object\" ? JSON.stringify(value) : value;\n\n // Use provided TTL or default to 1 hour\n const ttl = ttlSeconds ?? 3600;\n return await this.client.set(prefixedKey, stringValue, {\n EX: ttl,\n GET: true,\n });\n } catch (error) {\n console.error(`Error setting cache key ${key}:`, error);\n throw error;\n }\n }\n\n /**\n * Retrieves a value from the cache. Attempts to parse the value as JSON if possible.\n *\n * @param key - The key to retrieve\n * @returns Promise<T | null> The value, parsed as type T if it was JSON, or as string if not. Returns null if key doesn't exist\n * @example\n * // Get a string\n * const value = await cache.get(\"foo\");\n *\n * // Get and parse an object with type safety\n * interface Config { baz: number; qux: boolean; }\n * const config = await cache.get<Config>(\"foo:config\");\n */\n public async get<T extends SupportedTypes = string>(\n key: string,\n ): Promise<T | null> {\n try {\n await this.ensureConnected();\n const prefixedKey = this.getPrefixedKey(key);\n const value = await this.client.get(prefixedKey);\n\n if (value === null) return null;\n\n // Note: We can't check if T is string at runtime because TypeScript types are erased.\n // Instead, we try to parse as JSON and return the original string if that fails.\n try {\n const parsed = JSON.parse(value);\n // Only return parsed value if it's an object\n if (typeof parsed === \"object\" && parsed !== null) {\n return parsed as T;\n }\n // If parsed value isn't an object, return as string\n return value as T;\n } catch {\n // If JSON parse fails, return as string\n return value as T;\n }\n } catch (error) {\n console.error(`Error getting cache key ${key}:`, error);\n throw error;\n }\n }\n\n /**\n * Deletes a specific key from the cache.\n *\n * @param key - The key to delete\n * @example\n * await cache.delete(\"foo\");\n */\n public async delete(key: string): Promise<void> {\n try {\n await this.ensureConnected();\n const prefixedKey = this.getPrefixedKey(key);\n await this.client.del(prefixedKey);\n } catch (error) {\n console.error(`Error deleting cache key ${key}:`, error);\n throw error;\n }\n }\n\n /**\n * Deletes all keys that start with the given prefix.\n *\n * @param keyPrefix - The prefix of keys to delete\n * @example\n * // Delete all keys starting with \"foo\"\n * await cache.clearKeys(\"foo\");\n */\n public async clearKeys(keyPrefix: string): Promise<void> {\n try {\n await this.ensureConnected();\n const prefixedKey = this.getPrefixedKey(keyPrefix);\n const keys = await this.client.keys(`${prefixedKey}*`);\n if (keys.length > 0) {\n await this.client.del(keys);\n }\n } catch (error) {\n console.error(\n `Error clearing cache keys with prefix ${keyPrefix}:`,\n error,\n );\n throw error;\n }\n }\n\n /**\n * Deletes all keys in the cache\n *\n * @example\n * await cache.clear();\n */\n public async clear(): Promise<void> {\n try {\n await this.ensureConnected();\n const keys = await this.client.keys(`${this.keyPrefix}*`);\n if (keys.length > 0) {\n await this.client.del(keys);\n }\n } catch (error) {\n console.error(\"Error clearing cache:\", error);\n throw error;\n }\n }\n\n /**\n * Manually disconnects the Redis client. The client will automatically reconnect\n * when the next operation is performed.\n *\n * @example\n * await cache.disconnect();\n */\n public async disconnect(): Promise<void> {\n this.clearDisconnectTimer();\n this.connectPromise = null;\n if (this.isConnected) {\n await this.client.quit();\n }\n }\n}\n","import path from \"node:path\";\nimport * as toml from \"toml\";\n\n/**\n * ClickHouse configuration from moose.config.toml\n */\nexport interface ClickHouseConfig {\n host: string;\n host_port: number;\n user: string;\n password: string;\n db_name: string;\n use_ssl?: boolean;\n native_port?: number;\n}\n\n/**\n * Redpanda/Kafka configuration from moose.config.toml\n */\nexport interface KafkaConfig {\n /** Broker connection string (e.g., \"host:port\" or comma-separated list) */\n broker: string;\n /** Message timeout in milliseconds */\n message_timeout_ms: number;\n /** Default retention period in milliseconds */\n retention_ms: number;\n /** Topic replication factor */\n replication_factor?: number;\n /** SASL username for authentication, if required */\n sasl_username?: string;\n /** SASL password for authentication, if required */\n sasl_password?: string;\n /** SASL mechanism (e.g., \"PLAIN\", \"SCRAM-SHA-256\") */\n sasl_mechanism?: string;\n /** Security protocol (e.g., \"SASL_SSL\", \"PLAINTEXT\") */\n security_protocol?: string;\n /** Optional namespace used as a prefix for topics */\n namespace?: string;\n /** Optional Confluent Schema Registry URL */\n schema_registry_url?: string;\n}\n\n/**\n * Project configuration from moose.config.toml\n */\nexport interface ProjectConfig {\n language: string;\n clickhouse_config: ClickHouseConfig;\n redpanda_config?: KafkaConfig;\n /**\n * Redpanda/Kafka configuration. Previously named `redpanda_config` in some places.\n * Prefer `kafka_config` but support both for backward compatibility.\n */\n\n kafka_config?: KafkaConfig;\n}\n\n/**\n * Error thrown when configuration cannot be found or parsed\n */\nexport class ConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigError\";\n }\n}\n\n/**\n * Walks up the directory tree to find moose.config.toml\n */\nasync function findConfigFile(\n startDir: string = process.cwd(),\n): Promise<string | null> {\n const fs = await import(\"node:fs\");\n\n let currentDir = path.resolve(startDir);\n\n while (true) {\n const configPath = path.join(currentDir, \"moose.config.toml\");\n if (fs.existsSync(configPath)) {\n return configPath;\n }\n\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) {\n // Reached root directory\n break;\n }\n currentDir = parentDir;\n }\n\n return null;\n}\n\n/**\n * Reads and parses the project configuration from moose.config.toml\n */\nexport async function readProjectConfig(): Promise<ProjectConfig> {\n const fs = await import(\"node:fs\");\n const configPath = await findConfigFile();\n if (!configPath) {\n throw new ConfigError(\n \"moose.config.toml not found in current directory or any parent directory\",\n );\n }\n\n try {\n const configContent = fs.readFileSync(configPath, \"utf-8\");\n const config = toml.parse(configContent) as ProjectConfig;\n return config;\n } catch (error) {\n throw new ConfigError(`Failed to parse moose.config.toml: ${error}`);\n }\n}\n","import { readProjectConfig } from \"./configFile\";\n\ninterface RuntimeClickHouseConfig {\n host: string;\n port: string;\n username: string;\n password: string;\n database: string;\n useSSL: boolean;\n}\n\ninterface RuntimeKafkaConfig {\n broker: string;\n messageTimeoutMs: number;\n saslUsername?: string;\n saslPassword?: string;\n saslMechanism?: string;\n securityProtocol?: string;\n namespace?: string;\n schemaRegistryUrl?: string;\n}\n\nclass ConfigurationRegistry {\n private static instance: ConfigurationRegistry;\n private clickhouseConfig?: RuntimeClickHouseConfig;\n private kafkaConfig?: RuntimeKafkaConfig;\n\n static getInstance(): ConfigurationRegistry {\n if (!ConfigurationRegistry.instance) {\n ConfigurationRegistry.instance = new ConfigurationRegistry();\n }\n return ConfigurationRegistry.instance;\n }\n\n setClickHouseConfig(config: RuntimeClickHouseConfig): void {\n this.clickhouseConfig = config;\n }\n\n setKafkaConfig(config: RuntimeKafkaConfig): void {\n this.kafkaConfig = config;\n }\n\n private _env(name: string): string | undefined {\n const value = process.env[name];\n if (value === undefined) return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n }\n\n private _parseBool(value: string | undefined): boolean | undefined {\n if (value === undefined) return undefined;\n switch (value.trim().toLowerCase()) {\n case \"1\":\n case \"true\":\n case \"yes\":\n case \"on\":\n return true;\n case \"0\":\n case \"false\":\n case \"no\":\n case \"off\":\n return false;\n default:\n return undefined;\n }\n }\n\n async getClickHouseConfig(): Promise<RuntimeClickHouseConfig> {\n if (this.clickhouseConfig) {\n return this.clickhouseConfig;\n }\n\n // Fallback to reading from config file for backward compatibility\n const projectConfig = await readProjectConfig();\n const envHost = this._env(\"MOOSE_CLICKHOUSE_CONFIG__HOST\");\n const envPort = this._env(\"MOOSE_CLICKHOUSE_CONFIG__HOST_PORT\");\n const envUser = this._env(\"MOOSE_CLICKHOUSE_CONFIG__USER\");\n const envPassword = this._env(\"MOOSE_CLICKHOUSE_CONFIG__PASSWORD\");\n const envDb = this._env(\"MOOSE_CLICKHOUSE_CONFIG__DB_NAME\");\n const envUseSSL = this._parseBool(\n this._env(\"MOOSE_CLICKHOUSE_CONFIG__USE_SSL\"),\n );\n\n return {\n host: envHost ?? projectConfig.clickhouse_config.host,\n port: envPort ?? projectConfig.clickhouse_config.host_port.toString(),\n username: envUser ?? projectConfig.clickhouse_config.user,\n password: envPassword ?? projectConfig.clickhouse_config.password,\n database: envDb ?? projectConfig.clickhouse_config.db_name,\n useSSL:\n envUseSSL !== undefined ? envUseSSL : (\n projectConfig.clickhouse_config.use_ssl || false\n ),\n };\n }\n\n async getStandaloneClickhouseConfig(\n overrides?: Partial<RuntimeClickHouseConfig>,\n ): Promise<RuntimeClickHouseConfig> {\n if (this.clickhouseConfig) {\n return { ...this.clickhouseConfig, ...overrides };\n }\n\n const envHost = this._env(\"MOOSE_CLICKHOUSE_CONFIG__HOST\");\n const envPort = this._env(\"MOOSE_CLICKHOUSE_CONFIG__HOST_PORT\");\n const envUser = this._env(\"MOOSE_CLICKHOUSE_CONFIG__USER\");\n const envPassword = this._env(\"MOOSE_CLICKHOUSE_CONFIG__PASSWORD\");\n const envDb = this._env(\"MOOSE_CLICKHOUSE_CONFIG__DB_NAME\");\n const envUseSSL = this._parseBool(\n this._env(\"MOOSE_CLICKHOUSE_CONFIG__USE_SSL\"),\n );\n\n let projectConfig;\n try {\n projectConfig = await readProjectConfig();\n } catch (error) {\n projectConfig = null;\n }\n\n const defaults = {\n host: \"localhost\",\n port: \"18123\",\n username: \"default\",\n password: \"\",\n database: \"local\",\n useSSL: false,\n };\n\n return {\n host:\n overrides?.host ??\n envHost ??\n projectConfig?.clickhouse_config.host ??\n defaults.host,\n port:\n overrides?.port ??\n envPort ??\n projectConfig?.clickhouse_config.host_port.toString() ??\n defaults.port,\n username:\n overrides?.username ??\n envUser ??\n projectConfig?.clickhouse_config.user ??\n defaults.username,\n password:\n overrides?.password ??\n envPassword ??\n projectConfig?.clickhouse_config.password ??\n defaults.password,\n database:\n overrides?.database ??\n envDb ??\n projectConfig?.clickhouse_config.db_name ??\n defaults.database,\n useSSL:\n overrides?.useSSL ??\n envUseSSL ??\n projectConfig?.clickhouse_config.use_ssl ??\n defaults.useSSL,\n };\n }\n\n async getKafkaConfig(): Promise<RuntimeKafkaConfig> {\n if (this.kafkaConfig) {\n return this.kafkaConfig;\n }\n\n const projectConfig = await readProjectConfig();\n\n const envBroker =\n this._env(\"MOOSE_REDPANDA_CONFIG__BROKER\") ??\n this._env(\"MOOSE_KAFKA_CONFIG__BROKER\");\n const envMsgTimeout =\n this._env(\"MOOSE_REDPANDA_CONFIG__MESSAGE_TIMEOUT_MS\") ??\n this._env(\"MOOSE_KAFKA_CONFIG__MESSAGE_TIMEOUT_MS\");\n const envSaslUsername =\n this._env(\"MOOSE_REDPANDA_CONFIG__SASL_USERNAME\") ??\n this._env(\"MOOSE_KAFKA_CONFIG__SASL_USERNAME\");\n const envSaslPassword =\n this._env(\"MOOSE_REDPANDA_CONFIG__SASL_PASSWORD\") ??\n this._env(\"MOOSE_KAFKA_CONFIG__SASL_PASSWORD\");\n const envSaslMechanism =\n this._env(\"MOOSE_REDPANDA_CONFIG__SASL_MECHANISM\") ??\n this._env(\"MOOSE_KAFKA_CONFIG__SASL_MECHANISM\");\n const envSecurityProtocol =\n this._env(\"MOOSE_REDPANDA_CONFIG__SECURITY_PROTOCOL\") ??\n this._env(\"MOOSE_KAFKA_CONFIG__SECURITY_PROTOCOL\");\n const envNamespace =\n this._env(\"MOOSE_REDPANDA_CONFIG__NAMESPACE\") ??\n this._env(\"MOOSE_KAFKA_CONFIG__NAMESPACE\");\n const envSchemaRegistryUrl =\n this._env(\"MOOSE_REDPANDA_CONFIG__SCHEMA_REGISTRY_URL\") ??\n this._env(\"MOOSE_KAFKA_CONFIG__SCHEMA_REGISTRY_URL\");\n\n const fileKafka =\n projectConfig.kafka_config ?? projectConfig.redpanda_config;\n\n return {\n broker: envBroker ?? fileKafka?.broker ?? \"localhost:19092\",\n messageTimeoutMs:\n envMsgTimeout ?\n parseInt(envMsgTimeout, 10)\n : (fileKafka?.message_timeout_ms ?? 1000),\n saslUsername: envSaslUsername ?? fileKafka?.sasl_username,\n saslPassword: envSaslPassword ?? fileKafka?.sasl_password,\n saslMechanism: envSaslMechanism ?? fileKafka?.sasl_mechanism,\n securityProtocol: envSecurityProtocol ?? fileKafka?.security_protocol,\n namespace: envNamespace ?? fileKafka?.namespace,\n schemaRegistryUrl: envSchemaRegistryUrl ?? fileKafka?.schema_registry_url,\n };\n }\n\n hasRuntimeConfig(): boolean {\n return !!this.clickhouseConfig || !!this.kafkaConfig;\n }\n}\n\n(globalThis as any)._mooseConfigRegistry = ConfigurationRegistry.getInstance();\nexport type {\n ConfigurationRegistry,\n RuntimeClickHouseConfig,\n RuntimeKafkaConfig,\n};\n","import { MooseClient, QueryClient, MooseUtils } from \"./helpers\";\nimport { getClickhouseClient } from \"../commons\";\nimport { sql } from \"../sqlHelpers\";\nimport type { RuntimeClickHouseConfig } from \"../config/runtime\";\n\n// Cached utilities and initialization promise for standalone mode\nlet standaloneUtils: MooseUtils | null = null;\nlet initPromise: Promise<MooseUtils> | null = null;\n\n// Convert config to client config format\nconst toClientConfig = (config: {\n host: string;\n port: string;\n username: string;\n password: string;\n database: string;\n useSSL: boolean;\n}) => ({\n ...config,\n useSSL: config.useSSL ? \"true\" : \"false\",\n});\n\n/**\n * Get Moose utilities for database access and SQL queries.\n * Works in both Moose runtime and standalone contexts.\n *\n * **IMPORTANT**: This function is async and returns a Promise. You must await the result:\n * ```typescript\n * const moose = await getMooseUtils(); // Correct\n * const moose = getMooseUtils(); // WRONG - returns Promise, not MooseUtils!\n * ```\n *\n * **Breaking Change from v1.x**: This function signature changed from sync to async.\n * If you were using the old sync API that extracted utils from a request object,\n * use `getMooseUtilsFromRequest(req)` for backward compatibility (deprecated).\n *\n * @param req - DEPRECATED: Request parameter is no longer needed and will be ignored.\n * If you need to extract moose from a request, use getMooseUtilsFromRequest().\n * @returns Promise resolving to MooseUtils with client and sql utilities.\n *\n * @example\n * ```typescript\n * const { client, sql } = await getMooseUtils();\n * const result = await client.query.execute(sql`SELECT * FROM table`);\n * ```\n */\nexport async function getMooseUtils(req?: any): Promise<MooseUtils> {\n // Deprecation warning if req passed\n if (req !== undefined) {\n console.warn(\n \"[DEPRECATED] getMooseUtils(req) no longer requires a request parameter. \" +\n \"Use getMooseUtils() instead.\",\n );\n }\n\n // Check if running in Moose runtime\n const runtimeContext = (globalThis as any)._mooseRuntimeContext;\n\n if (runtimeContext) {\n // In Moose runtime - use existing connections\n return {\n client: runtimeContext.client,\n sql: sql,\n jwt: runtimeContext.jwt,\n };\n }\n\n // Standalone mode - use cached client or create new one\n if (standaloneUtils) {\n return standaloneUtils;\n }\n\n // If initialization is in progress, wait for it\n if (initPromise) {\n return initPromise;\n }\n\n // Start initialization\n initPromise = (async () => {\n await import(\"../config/runtime\");\n const configRegistry = (globalThis as any)._mooseConfigRegistry;\n\n if (!configRegistry) {\n throw new Error(\n \"Moose not initialized. Ensure you're running within a Moose app \" +\n \"or have proper configuration set up.\",\n );\n }\n\n const clickhouseConfig =\n await configRegistry.getStandaloneClickhouseConfig();\n\n const clickhouseClient = getClickhouseClient(\n toClientConfig(clickhouseConfig),\n );\n const queryClient = new QueryClient(clickhouseClient, \"standalone\");\n const mooseClient = new MooseClient(queryClient);\n\n standaloneUtils = {\n client: mooseClient,\n sql: sql,\n jwt: undefined,\n };\n return standaloneUtils;\n })();\n\n try {\n return await initPromise;\n } finally {\n initPromise = null;\n }\n}\n\n/**\n * @deprecated Use getMooseUtils() instead.\n * Creates a Moose client for database access.\n */\nexport async function getMooseClients(\n config?: Partial<RuntimeClickHouseConfig>,\n): Promise<{ client: MooseClient }> {\n console.warn(\n \"[DEPRECATED] getMooseClients() is deprecated. Use getMooseUtils() instead.\",\n );\n\n // If custom config provided, create a one-off client (don't cache)\n if (config && Object.keys(config).length > 0) {\n await import(\"../config/runtime\");\n const configRegistry = (globalThis as any)._mooseConfigRegistry;\n\n if (!configRegistry) {\n throw new Error(\n \"Configuration registry not initialized. Ensure the Moose framework is properly set up.\",\n );\n }\n\n const clickhouseConfig =\n await configRegistry.getStandaloneClickhouseConfig(config);\n\n const clickhouseClient = getClickhouseClient(\n toClientConfig(clickhouseConfig),\n );\n const queryClient = new QueryClient(clickhouseClient, \"standalone\");\n const mooseClient = new MooseClient(queryClient);\n\n return { client: mooseClient };\n }\n\n // No custom config - delegate to getMooseUtils\n const utils = await getMooseUtils();\n return { client: utils.client };\n}\n","import type {\n Column,\n DataType,\n Nested,\n ArrayType,\n} from \"../dataModels/dataModelTypes\";\n\n/**\n * Annotation key used to mark DateTime fields that should remain as strings\n * rather than being parsed into Date objects at runtime.\n */\nexport const STRING_DATE_ANNOTATION = \"stringDate\";\n\n/**\n * Type guard to check if a DataType is a nullable wrapper\n */\nfunction isNullableType(dt: DataType): dt is { nullable: DataType } {\n return (\n typeof dt === \"object\" &&\n dt !== null &&\n \"nullable\" in dt &&\n typeof dt.nullable !== \"undefined\"\n );\n}\n\n/**\n * Type guard to check if a DataType is a Nested type\n */\nfunction isNestedType(dt: DataType): dt is Nested {\n return (\n typeof dt === \"object\" &&\n dt !== null &&\n \"columns\" in dt &&\n Array.isArray(dt.columns)\n );\n}\n\n/**\n * Type guard to check if a DataType is an ArrayType\n */\nfunction isArrayType(dt: DataType): dt is ArrayType {\n return (\n typeof dt === \"object\" &&\n dt !== null &&\n \"elementType\" in dt &&\n typeof dt.elementType !== \"undefined\"\n );\n}\n\n/**\n * Revives ISO 8601 date strings into Date objects during JSON parsing\n * This is useful for automatically converting date strings to Date objects\n */\nexport function jsonDateReviver(key: string, value: unknown): unknown {\n const iso8601Format =\n /^([\\+-]?\\d{4}(?!\\d{2}\\b))((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)[0-5]\\d)?|24\\:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)$/;\n\n if (typeof value === \"string\" && iso8601Format.test(value)) {\n return new Date(value);\n }\n\n return value;\n}\n\n/**\n * Checks if a DataType represents a datetime column (not just date)\n * AND if the column should be parsed from string to Date at runtime\n *\n * Note: Date and Date16 are date-only types and should remain as strings.\n * Only DateTime types are candidates for parsing to JavaScript Date objects.\n */\nfunction isDateType(dataType: DataType, annotations: [string, any][]): boolean {\n // Check if this is marked as a string-based date (from typia.tags.Format)\n // If so, it should remain as a string, not be parsed to Date\n if (\n annotations.some(\n ([key, value]) => key === STRING_DATE_ANNOTATION && value === true,\n )\n ) {\n return false;\n }\n\n if (typeof dataType === \"string\") {\n // Only DateTime types should be parsed to Date objects\n // Date and Date16 are date-only and should stay as strings\n return dataType === \"DateTime\" || dataType.startsWith(\"DateTime(\");\n }\n // Handle nullable wrapper\n if (isNullableType(dataType)) {\n return isDateType(dataType.nullable, annotations);\n }\n return false;\n}\n\n/**\n * Type of mutation to apply to a field during parsing\n */\nexport type Mutation = \"parseDate\"; // | \"parseBigInt\" - to be added later\n\n/**\n * Recursive tuple array structure representing field mutation operations\n * Each entry is [fieldName, mutation]:\n * - mutation is Mutation[] for leaf fields that need operations applied\n * - mutation is FieldMutations for nested objects/arrays (auto-applies to array elements)\n */\nexport type FieldMutations = [string, Mutation[] | FieldMutations][];\n\n/**\n * Recursively builds field mutations from column definitions\n *\n * @param columns - Array of Column definitions\n * @returns Tuple array of field mutations\n */\nfunction buildFieldMutations(columns: Column[]): FieldMutations {\n const mutations: FieldMutations = [];\n\n for (const column of columns) {\n const dataType = column.data_type;\n\n // Check if this is a date field that should be converted\n if (isDateType(dataType, column.annotations)) {\n mutations.push([column.name, [\"parseDate\"]]);\n continue;\n }\n\n // Handle nested structures\n if (typeof dataType === \"object\" && dataType !== null) {\n // Handle nullable wrapper\n let unwrappedType: DataType = dataType;\n if (isNullableType(dataType)) {\n unwrappedType = dataType.nullable;\n }\n\n // Handle nested objects\n if (isNestedType(unwrappedType)) {\n const nestedMutations = buildFieldMutations(unwrappedType.columns);\n if (nestedMutations.length > 0) {\n mutations.push([column.name, nestedMutations]);\n }\n continue;\n }\n\n // Handle arrays with nested columns\n // The mutations will be auto-applied to each array element at runtime\n if (isArrayType(unwrappedType)) {\n const elementType = unwrappedType.elementType;\n if (isNestedType(elementType)) {\n const nestedMutations = buildFieldMutations(elementType.columns);\n if (nestedMutations.length > 0) {\n mutations.push([column.name, nestedMutations]);\n }\n continue;\n }\n }\n }\n }\n\n return mutations;\n}\n\n/**\n * Applies a mutation operation to a field value\n *\n * @param value - The value to handle\n * @param mutation - The mutation operation to apply\n * @returns The handled value\n */\nfunction applyMutation(value: any, mutation: Mutation): any {\n if (mutation === \"parseDate\") {\n if (typeof value === \"string\") {\n try {\n const date = new Date(value);\n return !isNaN(date.getTime()) ? date : value;\n } catch {\n return value;\n }\n }\n }\n return value;\n}\n\n/**\n * Recursively mutates an object by applying field mutations\n *\n * @param obj - The object to mutate\n * @param mutations - The field mutations to apply\n */\nfunction applyFieldMutations(obj: any, mutations: FieldMutations): void {\n if (!obj || typeof obj !== \"object\") {\n return;\n }\n\n for (const [fieldName, mutation] of mutations) {\n if (!(fieldName in obj)) {\n continue;\n }\n\n if (Array.isArray(mutation)) {\n // Check if it's Mutation[] (leaf) or FieldMutations (nested)\n if (mutation.length > 0 && typeof mutation[0] === \"string\") {\n // It's Mutation[] - apply operations to this field\n const operations = mutation as Mutation[];\n for (const operation of operations) {\n obj[fieldName] = applyMutation(obj[fieldName], operation);\n }\n } else {\n // It's FieldMutations - recurse into nested structure\n const nestedMutations = mutation as FieldMutations;\n const fieldValue = obj[fieldName];\n\n if (Array.isArray(fieldValue)) {\n // Auto-apply to each array element\n for (const item of fieldValue) {\n applyFieldMutations(item, nestedMutations);\n }\n } else if (fieldValue && typeof fieldValue === \"object\") {\n // Apply to nested object\n applyFieldMutations(fieldValue, nestedMutations);\n }\n }\n }\n }\n}\n\n/**\n * Pre-builds field mutations from column schema for efficient reuse\n *\n * @param columns - Column definitions from the Stream schema\n * @returns Field mutations tuple array, or undefined if no columns\n *\n * @example\n * ```typescript\n * const fieldMutations = buildFieldMutationsFromColumns(stream.columnArray);\n * // Reuse fieldMutations for every message\n * ```\n */\nexport function buildFieldMutationsFromColumns(\n columns: Column[] | undefined,\n): FieldMutations | undefined {\n if (!columns || columns.length === 0) {\n return undefined;\n }\n const mutations = buildFieldMutations(columns);\n return mutations.length > 0 ? mutations : undefined;\n}\n\n/**\n * Applies field mutations to parsed data\n * Mutates the object in place for performance\n *\n * @param data - The parsed JSON object to mutate\n * @param fieldMutations - Pre-built field mutations from buildFieldMutationsFromColumns\n *\n * @example\n * ```typescript\n * const fieldMutations = buildFieldMutationsFromColumns(stream.columnArray);\n * const data = JSON.parse(jsonString);\n * mutateParsedJson(data, fieldMutations);\n * // data now has transformations applied per the field mutations\n * ```\n */\nexport function mutateParsedJson(\n data: any,\n fieldMutations: FieldMutations | undefined,\n): void {\n if (!fieldMutations || !data) {\n return;\n }\n\n applyFieldMutations(data, fieldMutations);\n}\n","import { parse } from \"csv-parse\";\nimport { jsonDateReviver } from \"./json\";\n\n/**\n * Configuration for CSV parsing options\n */\nexport interface CSVParsingConfig {\n /** CSV delimiter character */\n delimiter: string;\n /** Whether to treat first row as headers */\n columns?: boolean;\n /** Whether to skip empty lines */\n skipEmptyLines?: boolean;\n /** Whether to trim whitespace from values */\n trim?: boolean;\n}\n\n/**\n * Configuration for JSON parsing options\n */\nexport interface JSONParsingConfig {\n /** Custom reviver function for JSON.parse */\n reviver?: (key: string, value: any) => any;\n}\n\n/**\n * Parses CSV content into an array of objects\n *\n * @param content - The CSV content as a string\n * @param config - CSV parsing configuration\n * @returns Promise resolving to an array of parsed objects\n */\nexport function parseCSV<T = Record<string, any>>(\n content: string,\n config: CSVParsingConfig,\n): Promise<T[]> {\n return new Promise((resolve, reject) => {\n const results: T[] = [];\n\n parse(content, {\n delimiter: config.delimiter,\n columns: config.columns ?? true,\n skip_empty_lines: config.skipEmptyLines ?? true,\n trim: config.trim ?? true,\n })\n .on(\"data\", (row) => {\n results.push(row as T);\n })\n .on(\"end\", () => {\n resolve(results);\n })\n .on(\"error\", (error) => {\n reject(error);\n });\n });\n}\n\n/**\n * Parses JSON content into an array of objects\n *\n * @param content - The JSON content as a string\n * @param config - JSON parsing configuration\n * @returns Array of parsed objects\n */\nexport function parseJSON<T = any>(\n content: string,\n config: JSONParsingConfig = {},\n): T[] {\n try {\n const parsed = JSON.parse(content, config.reviver);\n\n // Handle both array and single object cases\n if (Array.isArray(parsed)) {\n return parsed as T[];\n } else {\n return [parsed as T];\n }\n } catch (error) {\n throw new Error(\n `Failed to parse JSON: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n}\n\n/**\n * Parses JSON content with automatic date revival\n *\n * @param content - The JSON content as a string\n * @returns Array of parsed objects with Date objects for ISO 8601 strings\n */\nexport function parseJSONWithDates<T = any>(content: string): T[] {\n return parseJSON<T>(content, { reviver: jsonDateReviver });\n}\n\n/**\n * Type guard to check if a value is a valid CSV delimiter\n */\nexport function isValidCSVDelimiter(delimiter: string): boolean {\n return delimiter.length === 1 && !/\\s/.test(delimiter);\n}\n\n/**\n * Common CSV delimiters\n */\nexport const CSV_DELIMITERS = {\n COMMA: \",\",\n TAB: \"\\t\",\n SEMICOLON: \";\",\n PIPE: \"|\",\n} as const;\n\n/**\n * Default CSV parsing configuration\n */\nexport const DEFAULT_CSV_CONFIG: CSVParsingConfig = {\n delimiter: CSV_DELIMITERS.COMMA,\n columns: true,\n skipEmptyLines: true,\n trim: true,\n};\n\n/**\n * Default JSON parsing configuration with date revival\n */\nexport const DEFAULT_JSON_CONFIG: JSONParsingConfig = {\n reviver: jsonDateReviver,\n};\n","import { IsTuple } from \"typia/lib/typings/IsTuple\";\n\nexport * from \"./dataParser\";\n\ntype HasFunctionField<T> =\n T extends object ?\n {\n [K in keyof T]: T[K] extends Function ? true : false;\n }[keyof T] extends false | undefined ?\n false\n : true\n : false;\n\n// convert `field?: Type` to `field: Type | undefined` to avoid homomorphic type\ntype OptionalToUndefinedable<T> = { [K in {} & keyof T]: T[K] };\ntype StripInterfaceFields<T> = { [K in keyof T]: StripDateIntersection<T[K]> };\n\n/**\n * `Date & ...` is considered \"nonsensible intersection\" by typia,\n * causing JSON schema to fail.\n * This helper type recursively cleans up the intersection type tagging.\n */\nexport type StripDateIntersection<T> =\n T extends Date ?\n Date extends T ?\n Date\n : T\n : T extends ReadonlyArray<unknown> ?\n IsTuple<T> extends true ? StripDateFromTuple<T>\n : T extends ReadonlyArray<infer U> ?\n ReadonlyArray<U> extends T ?\n ReadonlyArray<StripDateIntersection<U>>\n : Array<StripDateIntersection<U>>\n : T extends Array<infer U> ? Array<StripDateIntersection<U>>\n : T // this catchall should be unreachable\n : // do not touch other classes\n true extends HasFunctionField<T> ? T\n : T extends object ? StripInterfaceFields<OptionalToUndefinedable<T>>\n : T;\n\n// infer fails in a recursive definition if an intersection type tag is present\ntype StripDateFromTuple<T extends readonly any[]> =\n T extends (\n [\n infer T1,\n infer T2,\n infer T3,\n infer T4,\n infer T5,\n infer T6,\n infer T7,\n infer T8,\n infer T9,\n infer T10,\n ]\n ) ?\n [\n StripDateIntersection<T1>,\n StripDateIntersection<T2>,\n StripDateIntersection<T3>,\n StripDateIntersection<T4>,\n StripDateIntersection<T5>,\n StripDateIntersection<T6>,\n StripDateIntersection<T7>,\n StripDateIntersection<T8>,\n StripDateIntersection<T9>,\n StripDateIntersection<T10>,\n ]\n : T extends (\n [\n infer T1,\n infer T2,\n infer T3,\n infer T4,\n infer T5,\n infer T6,\n infer T7,\n infer T8,\n infer T9,\n ]\n ) ?\n [\n StripDateIntersection<T1>,\n StripDateIntersection<T2>,\n StripDateIntersection<T3>,\n StripDateIntersection<T4>,\n StripDateIntersection<T5>,\n StripDateIntersection<T6>,\n StripDateIntersection<T7>,\n StripDateIntersection<T8>,\n StripDateIntersection<T9>,\n ]\n : T extends (\n [\n infer T1,\n infer T2,\n infer T3,\n infer T4,\n infer T5,\n infer T6,\n infer T7,\n infer T8,\n ]\n ) ?\n [\n StripDateIntersection<T1>,\n StripDateIntersection<T2>,\n StripDateIntersection<T3>,\n StripDateIntersection<T4>,\n StripDateIntersection<T5>,\n StripDateIntersection<T6>,\n StripDateIntersection<T7>,\n StripDateIntersection<T8>,\n ]\n : T extends (\n [infer T1, infer T2, infer T3, infer T4, infer T5, infer T6, infer T7]\n ) ?\n [\n StripDateIntersection<T1>,\n StripDateIntersection<T2>,\n StripDateIntersection<T3>,\n StripDateIntersection<T4>,\n StripDateIntersection<T5>,\n StripDateIntersection<T6>,\n StripDateIntersection<T7>,\n ]\n : T extends [infer T1, infer T2, infer T3, infer T4, infer T5, infer T6] ?\n [\n StripDateIntersection<T1>,\n StripDateIntersection<T2>,\n StripDateIntersection<T3>,\n StripDateIntersection<T4>,\n StripDateIntersection<T5>,\n StripDateIntersection<T6>,\n ]\n : T extends [infer T1, infer T2, infer T3, infer T4, infer T5] ?\n [\n StripDateIntersection<T1>,\n StripDateIntersection<T2>,\n StripDateIntersection<T3>,\n StripDateIntersection<T4>,\n StripDateIntersection<T5>,\n ]\n : T extends [infer T1, infer T2, infer T3, infer T4] ?\n [\n StripDateIntersection<T1>,\n StripDateIntersection<T2>,\n StripDateIntersection<T3>,\n StripDateIntersection<T4>,\n ]\n : T extends [infer T1, infer T2, infer T3] ?\n [\n StripDateIntersection<T1>,\n StripDateIntersection<T2>,\n StripDateIntersection<T3>,\n ]\n : T extends [infer T1, infer T2] ?\n [StripDateIntersection<T1>, StripDateIntersection<T2>]\n : T extends [infer T1] ? [StripDateIntersection<T1>]\n : [];\n","import { Readable } from \"node:stream\";\n\n/**\n * Configuration for a data source\n */\nexport interface DataSourceConfig {\n name: string;\n supportsIncremental?: boolean;\n}\n\n/**\n * DataSource is an abstract class that defines the interface for all data sources.\n * It is used to extract data from a source and test the connection to the source.\n */\nexport abstract class DataSource<T = any, ItemType = any> {\n protected name: string;\n protected supportsIncremental: boolean;\n\n constructor(config: DataSourceConfig) {\n this.name = config.name;\n this.supportsIncremental = config.supportsIncremental ?? false;\n }\n\n /**\n * Extract data from the source\n * Returns either ItemType (for single requests) or Readable (for paginated requests)\n */\n abstract extract(): Promise<ItemType | Readable>;\n\n /**\n * Test connection to the source\n */\n abstract testConnection(): Promise<{ success: boolean; message?: string }>;\n}\n\n/**\n * Result returned from extraction\n * For single requests: data is of type T\n * For paginated requests: data is a Readable stream yielding items of type T\n */\nexport interface ExtractionResult<T = any> {\n data: T | Readable;\n metadata: Record<string, any>;\n}\n","/**\n * SQL Utilities\n *\n * Low-level SQL building utilities for constructing type-safe queries.\n * These are the building blocks used by QueryModel and can also be used\n * directly for custom query construction.\n *\n * @module query-layer/sql-utils\n */\n\nimport { sql, Sql, quoteIdentifier } from \"../sqlHelpers\";\nimport type { SqlValue, ColRef, FilterOperator } from \"./types\";\n\n// --- Core SQL Utilities ---\n\n/**\n * Create raw SQL (literal string, no parameterization).\n * Delegates to sql.raw from sqlHelpers.\n *\n * Only for developer-defined constants (column names, expressions, sort\n * directions) that originate from model config — never for HTTP/user input.\n */\nexport const raw: (text: string) => Sql = sql.raw;\n\n/** Empty SQL fragment — useful as a no-op. */\nexport const empty = sql``;\n\n/**\n * Join SQL fragments with a separator.\n * Delegates to sql.join from sqlHelpers.\n */\nexport const join: (fragments: Sql[], separator?: string) => Sql = sql.join;\n\n/** Check if a Sql fragment is empty */\nexport function isEmpty(fragment: Sql): boolean {\n return (\n fragment.strings.every((s) => s.trim() === \"\") &&\n fragment.values.length === 0\n );\n}\n\n// --- Filter Function ---\n\n/**\n * Create a filter condition. Automatically skips if value is undefined/null.\n * This is the recommended way to build conditional WHERE clauses.\n *\n * @example\n * where(\n * filter(Events.columns.amount, \"gte\", params.minAmount),\n * filter(Events.columns.amount, \"lte\", params.maxAmount),\n * filter(Events.columns.status, \"eq\", params.status),\n * )\n */\nexport function filter(\n col: ColRef,\n op: \"between\",\n value: [SqlValue, SqlValue] | undefined,\n): Sql;\nexport function filter(\n col: ColRef,\n op: \"in\" | \"notIn\",\n value: SqlValue[] | undefined,\n): Sql;\nexport function filter(\n col: ColRef,\n op: \"isNull\" | \"isNotNull\",\n value: boolean | undefined,\n): Sql;\nexport function filter(\n col: ColRef,\n op: \"like\" | \"ilike\",\n value: string | undefined,\n): Sql;\nexport function filter(\n col: ColRef,\n op: Exclude<\n FilterOperator,\n \"between\" | \"in\" | \"notIn\" | \"isNull\" | \"isNotNull\" | \"like\" | \"ilike\"\n >,\n value: SqlValue | undefined,\n): Sql;\nexport function filter(col: ColRef, op: FilterOperator, value: unknown): Sql {\n if (value === undefined || value === null) return empty;\n\n switch (op) {\n case \"eq\":\n return eq(col, value as SqlValue);\n case \"ne\":\n return ne(col, value as SqlValue);\n case \"gt\":\n return gt(col, value as SqlValue);\n case \"gte\":\n return gte(col, value as SqlValue);\n case \"lt\":\n return lt(col, value as SqlValue);\n case \"lte\":\n return lte(col, value as SqlValue);\n case \"like\":\n return like(col, value as string);\n case \"ilike\":\n return ilike(col, value as string);\n case \"in\":\n return inList(col, value as SqlValue[]);\n case \"notIn\":\n return notIn(col, value as SqlValue[]);\n case \"between\": {\n const [low, high] = value as [SqlValue, SqlValue];\n return between(col, low, high);\n }\n case \"isNull\":\n return value ? isNull(col) : empty;\n case \"isNotNull\":\n return value ? isNotNull(col) : empty;\n }\n}\n\n// --- Comparison Operators ---\n\n/** Equal: column = value */\nexport function eq(col: ColRef, value: SqlValue): Sql {\n return sql`${col} = ${value}`;\n}\n\n/** Not equal: column != value */\nexport function ne(col: ColRef, value: SqlValue): Sql {\n return sql`${col} != ${value}`;\n}\n\n/** Greater than: column > value */\nexport function gt(col: ColRef, value: SqlValue): Sql {\n return sql`${col} > ${value}`;\n}\n\n/** Greater than or equal: column >= value */\nexport function gte(col: ColRef, value: SqlValue): Sql {\n return sql`${col} >= ${value}`;\n}\n\n/** Less than: column < value */\nexport function lt(col: ColRef, value: SqlValue): Sql {\n return sql`${col} < ${value}`;\n}\n\n/** Less than or equal: column <= value */\nexport function lte(col: ColRef, value: SqlValue): Sql {\n return sql`${col} <= ${value}`;\n}\n\n/** LIKE pattern match (case-sensitive) */\nexport function like(col: ColRef, pattern: string): Sql {\n return sql`${col} LIKE ${pattern}`;\n}\n\n/** ILIKE pattern match (case-insensitive, ClickHouse) */\nexport function ilike(col: ColRef, pattern: string): Sql {\n return sql`${col} ILIKE ${pattern}`;\n}\n\n/** IN list: column IN (a, b, c) */\nexport function inList(col: ColRef, values: SqlValue[]): Sql {\n if (values.length === 0) return sql`1 = 0`;\n return sql`${col} IN (${join(values.map((v) => sql`${v}`))})`;\n}\n\n/** NOT IN list */\nexport function notIn(col: ColRef, values: SqlValue[]): Sql {\n if (values.length === 0) return sql`1 = 1`;\n return sql`${col} NOT IN (${join(values.map((v) => sql`${v}`))})`;\n}\n\n/** BETWEEN: column BETWEEN low AND high */\nexport function between(col: ColRef, low: SqlValue, high: SqlValue): Sql {\n return sql`${col} BETWEEN ${low} AND ${high}`;\n}\n\n/** IS NULL */\nexport function isNull(col: ColRef): Sql {\n return sql`${col} IS NULL`;\n}\n\n/** IS NOT NULL */\nexport function isNotNull(col: ColRef): Sql {\n return sql`${col} IS NOT NULL`;\n}\n\n// --- Logical Combinators ---\n\n/** Combine conditions with AND, filtering out empty fragments */\nexport function and(...conditions: Sql[]): Sql {\n const nonEmpty = conditions.filter((c) => !isEmpty(c));\n return join(nonEmpty, \"AND\");\n}\n\n/** Combine conditions with OR, filtering out empty fragments */\nexport function or(...conditions: Sql[]): Sql {\n const nonEmpty = conditions.filter((c) => !isEmpty(c));\n if (nonEmpty.length === 0) return empty;\n if (nonEmpty.length === 1) return nonEmpty[0];\n return sql`(${join(nonEmpty, \"OR\")})`;\n}\n\n/** Negate a condition: NOT (condition) */\nexport function not(condition: Sql): Sql {\n if (isEmpty(condition)) return empty;\n return sql`NOT (${condition})`;\n}\n\n// --- SQL Clauses ---\n\n/** Build WHERE clause — returns empty if no conditions */\nexport function where(...conditions: Sql[]): Sql {\n const combined = and(...conditions);\n return isEmpty(combined) ? empty : sql`WHERE ${combined}`;\n}\n\n/** Build ORDER BY clause */\nexport function orderBy(\n ...cols: Array<ColRef | [ColRef, \"ASC\" | \"DESC\"]>\n): Sql {\n if (cols.length === 0) return empty;\n const parts = cols.map((c) => {\n if (Array.isArray(c)) {\n const [col, dir] = c;\n return sql`${col} ${raw(dir)}`;\n }\n return sql`${c}`;\n });\n return sql`ORDER BY ${join(parts)}`;\n}\n\n/** Build LIMIT clause */\nexport function limit(n: number): Sql {\n if (!Number.isInteger(n) || n < 0) {\n throw new Error(\"LIMIT must be a non-negative integer\");\n }\n return sql`LIMIT ${n}`;\n}\n\n/** Build OFFSET clause */\nexport function offset(n: number): Sql {\n if (!Number.isInteger(n) || n < 0) {\n throw new Error(\"OFFSET must be a non-negative integer\");\n }\n return sql`OFFSET ${n}`;\n}\n\n/** Build LIMIT + OFFSET for pagination */\nexport function paginate(pageSize: number, page: number = 0): Sql {\n if (!Number.isInteger(pageSize) || pageSize <= 0) {\n throw new Error(\"pageSize must be a positive integer\");\n }\n if (!Number.isInteger(page) || page < 0) {\n throw new Error(\"page must be a non-negative integer\");\n }\n const offsetVal = page * pageSize;\n return offsetVal > 0 ?\n sql`LIMIT ${pageSize} OFFSET ${offsetVal}`\n : sql`LIMIT ${pageSize}`;\n}\n\n/** Build GROUP BY clause */\nexport function groupBy(...cols: ColRef[]): Sql {\n if (cols.length === 0) return empty;\n return sql`GROUP BY ${join(cols.map((c) => sql`${c}`))}`;\n}\n\n/** Build HAVING clause */\nexport function having(...conditions: Sql[]): Sql {\n const combined = and(...conditions);\n return isEmpty(combined) ? empty : sql`HAVING ${combined}`;\n}\n\n// --- Identifier Safety ---\n\n/** Emit a safely-quoted SQL identifier using backticks (ClickHouse convention). */\nexport function identifier(name: string): Sql {\n return raw(quoteIdentifier(name));\n}\n\n// --- Expression with Fluent Alias ---\n\n/** SQL expression with fluent `.as()` method */\nexport interface Expr extends Sql {\n as(alias: string): Sql;\n}\n\n/** Augment a Sql fragment with a fluent .as() method */\nfunction expr(fragment: Sql): Expr {\n const e = Object.create(fragment) as Expr;\n e.as = (alias: string) => sql`${fragment} AS ${identifier(alias)}`;\n return e;\n}\n\n// --- Aggregation Functions ---\n\n/** COUNT(*) or COUNT(column) */\nexport function count(col?: ColRef): Expr {\n return expr(col ? sql`count(${col})` : sql`count(*)`);\n}\n\n/** COUNT(DISTINCT column) */\nexport function countDistinct(col: ColRef): Expr {\n return expr(sql`count(DISTINCT ${col})`);\n}\n\n/** SUM(column) */\nexport function sum(col: ColRef): Expr {\n return expr(sql`sum(${col})`);\n}\n\n/** AVG(column) */\nexport function avg(col: ColRef): Expr {\n return expr(sql`avg(${col})`);\n}\n\n/** MIN(column) */\nexport function min(col: ColRef): Expr {\n return expr(sql`min(${col})`);\n}\n\n/** MAX(column) */\nexport function max(col: ColRef): Expr {\n return expr(sql`max(${col})`);\n}\n\n// --- Select Helpers ---\n\n/** Build SELECT clause with columns */\nexport function select(...cols: Array<ColRef | [ColRef, string]>): Sql {\n if (cols.length === 0) return sql`SELECT *`;\n const parts = cols.map((c) => {\n if (Array.isArray(c)) {\n const [col, alias] = c;\n return sql`${col} AS ${identifier(alias)}`;\n }\n return sql`${c}`;\n });\n return sql`SELECT ${join(parts)}`;\n}\n\n/** Alias a column or expression */\nexport function as(expression: Sql, alias: string): Sql {\n return sql`${expression} AS ${identifier(alias)}`;\n}\n","/**\n * Composable helpers that leverage moose-lib table metadata to reduce\n * boilerplate in QueryModel definitions.\n *\n * @module query-layer/helpers\n */\n\nimport { sql } from \"../sqlHelpers\";\nimport type { Column, DataType } from \"../dataModels/dataModelTypes\";\nimport type { OlapTable } from \"../dmv2\";\nimport type {\n DimensionDef,\n ColumnDef,\n ModelFilterDef,\n FilterInputTypeHint,\n} from \"./types\";\n\n// --- Utility: snake_case → camelCase ---\n\nfunction toCamelCase(s: string): string {\n return s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());\n}\n\n// --- deriveInputTypeFromDataType ---\n\n/**\n * Prefix → FilterInputTypeHint map for ClickHouse string type names.\n *\n * Prefix matching is required because ClickHouse types carry parameters\n * (e.g. `DateTime64(3)`, `Decimal(18,4)`, `FixedString(32)`).\n * Entries are ordered longest-prefix-first so \"datetime\" matches before \"date\".\n */\nconst TYPE_PREFIX_MAP: readonly [string, FilterInputTypeHint][] = [\n [\"datetime\", \"date\"],\n [\"date\", \"date\"],\n [\"uint\", \"number\"],\n [\"int\", \"number\"],\n [\"float\", \"number\"],\n [\"decimal\", \"number\"],\n [\"enum\", \"select\"],\n [\"boolean\", \"select\"],\n [\"bool\", \"select\"],\n];\n\n/**\n * Derive FilterInputTypeHint from a ClickHouse column data_type.\n */\nexport function deriveInputTypeFromDataType(\n dataType: DataType,\n): FilterInputTypeHint {\n if (typeof dataType === \"string\") {\n const lower = dataType.toLowerCase();\n const match = TYPE_PREFIX_MAP.find(([prefix]) => lower.startsWith(prefix));\n return match ? match[1] : \"text\";\n }\n\n if (\"nullable\" in dataType) {\n return deriveInputTypeFromDataType(dataType.nullable);\n }\n if (\"name\" in dataType && \"values\" in dataType) {\n return \"select\";\n }\n if (\"elementType\" in dataType) {\n return \"text\";\n }\n return \"text\";\n}\n\n// --- timeDimensions ---\n\ntype DefaultTimePeriods = {\n day: DimensionDef;\n month: DimensionDef;\n week: DimensionDef;\n};\n\n/**\n * Generate day/month/week dimension definitions from a date column reference.\n *\n * @param dateColumn - A Column reference from `Table.columns.some_date`\n * @returns `{ day, month, week }` dimension definitions\n *\n * @example\n * dimensions: {\n * status: { column: \"status\" },\n * ...timeDimensions(VisitsTable.columns.start_date),\n * }\n */\nexport function timeDimensions(dateColumn: Column): DefaultTimePeriods;\nexport function timeDimensions(\n dateColumn: Column,\n options: { periods: string[] },\n): Record<string, DimensionDef>;\nexport function timeDimensions(\n dateColumn: Column,\n options?: { periods?: string[] },\n): DefaultTimePeriods | Record<string, DimensionDef> {\n const periods = options?.periods ?? [\"day\", \"month\", \"week\"];\n\n const fnMap: Record<string, (col: Column) => DimensionDef> = {\n day: (col) => ({ expression: sql`toDate(${col})`, as: \"day\" }),\n month: (col) => ({ expression: sql`toStartOfMonth(${col})`, as: \"month\" }),\n week: (col) => ({ expression: sql`toStartOfWeek(${col})`, as: \"week\" }),\n };\n\n const supported = Object.keys(fnMap);\n const result: Record<string, DimensionDef> = {};\n for (const period of periods) {\n const factory = fnMap[period];\n if (!factory) {\n throw new Error(\n `Unknown time period '${period}'. Supported: ${supported.join(\", \")}`,\n );\n }\n result[period] = factory(dateColumn);\n }\n\n return result;\n}\n\n// --- Table field helpers ---\n\ninterface TableFieldOptions {\n /** Only include these column names (snake_case as in the table) */\n include?: string[];\n /** Exclude these column names */\n exclude?: string[];\n /** Convert snake_case keys to camelCase (default: true) */\n camelCase?: boolean;\n}\n\n/**\n * Generate ColumnDef records from a table's columnArray metadata.\n *\n * @example\n * columns: {\n * ...columnsFromTable(VisitsTable, { include: [\"id\", \"name\", \"status\"] }),\n * firstName: { join: \"user\", column: \"first_name\" },\n * }\n */\nexport function columnsFromTable<T>(\n table: OlapTable<T>,\n options?: TableFieldOptions,\n): Record<string, ColumnDef<T>> {\n const { include, exclude, camelCase = true } = options ?? {};\n const result: Record<string, ColumnDef<T>> = {};\n\n for (const col of table.columnArray) {\n const colName = String(col.name);\n\n if (include && !include.includes(colName)) continue;\n if (exclude && exclude.includes(colName)) continue;\n\n const key = camelCase ? toCamelCase(colName) : colName;\n result[key] = { column: colName as keyof T & string };\n }\n\n return result;\n}\n\n/**\n * Generate ModelFilterDef records from a table's columnArray metadata.\n *\n * **Conservative defaults**: all filters get `[\"eq\"]` operators only.\n * Consumers widen operators explicitly via spread overrides.\n *\n * @example\n * filters: {\n * ...filtersFromTable(VisitsTable, { include: [\"studio_id\", \"start_date\", \"status\"] }),\n * status: { column: \"status\", operators: [\"eq\", \"ne\", \"in\"] as const },\n * }\n */\nexport function filtersFromTable<T>(\n table: OlapTable<T>,\n options?: TableFieldOptions,\n): Record<string, ModelFilterDef<T, keyof T>> {\n const { include, exclude, camelCase = true } = options ?? {};\n const result: Record<string, ModelFilterDef<T, keyof T>> = {};\n\n for (const col of table.columnArray) {\n const colName = String(col.name);\n\n if (include && !include.includes(colName)) continue;\n if (exclude && exclude.includes(colName)) continue;\n\n const key = camelCase ? toCamelCase(colName) : colName;\n result[key] = {\n column: colName as keyof T,\n operators: [\"eq\"] as const,\n inputType: deriveInputTypeFromDataType(col.data_type),\n };\n }\n\n return result;\n}\n","/**\n * Query Model — Core query building interface and implementation.\n *\n * @module query-layer/query-model\n */\n\nimport type { Column } from \"../dataModels/dataModelTypes\";\nimport { sql, Sql, quoteIdentifier } from \"../sqlHelpers\";\nimport { OlapTable } from \"../dmv2\";\nimport { QueryClient } from \"../consumption-apis/helpers\";\nimport {\n raw,\n empty,\n join,\n isEmpty,\n filter as filterSql,\n where,\n orderBy as orderByClause,\n groupBy as groupByClause,\n paginate,\n having as havingClause,\n identifier,\n} from \"./sql-utils\";\nimport {\n type FilterOperator,\n type SortDir,\n type SqlValue,\n type ColRef,\n type ColumnDef,\n type JoinDef,\n type DimensionDef,\n type MetricDef,\n type ModelFilterDef,\n type FilterDefBase,\n type FilterInputTypeHint,\n type Names,\n type QueryRequest,\n type QueryParts,\n type FilterParams,\n} from \"./types\";\nimport { deriveInputTypeFromDataType } from \"./helpers\";\n\n// Widen filterSql for dynamic operator dispatch (runtime iteration over ops).\nconst applyFilter = filterSql as (\n col: ColRef,\n op: FilterOperator,\n value: unknown,\n) => Sql;\n\nconst identity = (v: SqlValue): SqlValue => v;\n\n/**\n * Apply a transform function to a filter value, respecting operator-specific\n * value shapes (scalar, list, tuple, boolean).\n * @internal\n */\nfunction transformFilterValue(\n op: FilterOperator,\n value: unknown,\n transform: (v: SqlValue) => SqlValue,\n): unknown {\n switch (op) {\n case \"in\":\n case \"notIn\":\n return (value as SqlValue[]).map(transform);\n case \"between\": {\n const [low, high] = value as [SqlValue, SqlValue];\n return [transform(low), transform(high)];\n }\n case \"isNull\":\n case \"isNotNull\":\n return value;\n default:\n return transform(value as SqlValue);\n }\n}\n\n// --- Internal Types ---\n\n/**\n * Field definition for SELECT clauses (internal runtime type).\n * @internal\n */\ninterface FieldDef {\n column?: ColRef;\n expression?: Sql;\n agg?: Sql;\n as?: string;\n}\n\n/**\n * Runtime filter definition (internal).\n * @internal\n */\ninterface FilterDef<TValue = SqlValue> {\n column: ColRef;\n operators: readonly FilterOperator[];\n transform?: (value: TValue) => SqlValue;\n isHaving?: boolean;\n}\n\n/**\n * Resolved query specification (internal).\n * @internal\n */\ntype ResolvedQuerySpec<\n TMetrics extends string,\n TDimensions extends string,\n TFilters extends Record<string, FilterDefBase>,\n TSortable extends string,\n TTable = unknown,\n TColumns extends string = string,\n> = {\n filters?: FilterParams<TFilters, TTable>;\n select?: Array<TMetrics | TDimensions | TColumns>;\n groupBy?: TDimensions[];\n orderBy?: Array<[TSortable, SortDir]>;\n limit?: number;\n page?: number;\n offset?: number;\n detailMode?: boolean;\n};\n\n// --- Query Model Configuration ---\n\n/**\n * Configuration for defining a query model.\n *\n * @template TTable - The table's model type (row type)\n * @template TMetrics - Record of metric definitions\n * @template TDimensions - Record of dimension definitions\n * @template TFilters - Record of filter definitions\n * @template TSortable - Union type of sortable field names\n */\nexport interface QueryModelConfig<\n TTable,\n TMetrics extends Record<string, MetricDef>,\n TDimensions extends Record<string, DimensionDef<TTable, keyof TTable>>,\n TFilters extends Record<string, ModelFilterDef<TTable, keyof TTable>>,\n TSortable extends string,\n TColumns extends Record<string, ColumnDef<TTable>> = Record<string, never>,\n TJoins extends Record<string, JoinDef> = Record<string, never>,\n> {\n /** Tool name used by registerModelTools (e.g. \"query_visits\") */\n name?: string;\n /** Tool description used by registerModelTools */\n description?: string;\n /** The OlapTable to query */\n table: OlapTable<TTable>;\n\n /**\n * Dimension fields — columns used for grouping, filtering, and display.\n *\n * @example\n * dimensions: {\n * status: { column: \"status\" },\n * day: { expression: sql`toDate(timestamp)`, as: \"day\" },\n * }\n */\n dimensions?: TDimensions;\n\n /**\n * Metric fields — aggregate values computed over dimensions.\n *\n * @example\n * metrics: {\n * totalAmount: { agg: sum(Events.columns.amount), as: \"total_amount\" },\n * totalEvents: { agg: count(), as: \"total_events\" },\n * }\n */\n metrics?: TMetrics;\n\n /**\n * Column fields for detail (non-aggregated) queries.\n *\n * @example\n * columns: {\n * visitId: { column: \"id\" },\n * firstName: { join: \"user\", column: \"first_name\" },\n * }\n */\n columns?: TColumns;\n\n /**\n * Lookup JOIN definitions.\n *\n * @example\n * joins: {\n * user: {\n * table: UsersTable,\n * leftKey: \"user_id\",\n * rightKey: \"id\",\n * type: \"LEFT\",\n * },\n * }\n */\n joins?: TJoins;\n\n /**\n * Filterable fields with allowed operators.\n *\n * @example\n * filters: {\n * status: { column: \"status\", operators: [\"eq\", \"in\"] as const },\n * amount: { column: \"amount\", operators: [\"gte\", \"lte\"] as const },\n * }\n */\n filters: TFilters;\n\n /**\n * Which fields can be sorted.\n *\n * @example\n * sortable: [\"timestamp\", \"amount\", \"status\"] as const\n */\n sortable: readonly TSortable[];\n\n /** Default query behavior */\n defaults?: {\n orderBy?: Array<[TSortable, SortDir]>;\n groupBy?: string[];\n limit?: number;\n maxLimit?: number;\n dimensions?: string[];\n metrics?: string[];\n columns?: string[];\n };\n}\n\n// --- Query Model Interface ---\n\n/**\n * Query model interface providing type-safe query building and execution.\n */\nexport interface QueryModel<\n TTable,\n TMetrics extends Record<string, MetricDef>,\n TDimensions extends Record<string, DimensionDef>,\n TFilters extends Record<string, FilterDefBase>,\n TSortable extends string,\n TResult,\n TColumns extends Record<string, ColumnDef> = Record<string, never>,\n> {\n readonly name?: string;\n readonly description?: string;\n readonly defaults: {\n orderBy?: Array<[TSortable, SortDir]>;\n groupBy?: string[];\n limit?: number;\n maxLimit?: number;\n dimensions?: string[];\n metrics?: string[];\n columns?: string[];\n };\n readonly filters: TFilters;\n readonly sortable: readonly TSortable[];\n readonly dimensions?: TDimensions;\n readonly metrics?: TMetrics;\n readonly columns?: TColumns;\n\n readonly columnNames: readonly string[];\n\n /** Type inference helpers (similar to Drizzle's $inferSelect pattern). */\n readonly $inferDimensions: Names<TDimensions>;\n readonly $inferMetrics: Names<TMetrics>;\n readonly $inferColumns: Names<TColumns>;\n readonly $inferFilters: FilterParams<TFilters, TTable>;\n readonly $inferRequest: QueryRequest<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n Names<TColumns>,\n TTable\n >;\n readonly $inferResult: TResult;\n\n /** Execute query with Moose QueryClient. */\n query: (\n request: QueryRequest<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n Names<TColumns>,\n TTable\n >,\n client: QueryClient,\n ) => Promise<TResult[]>;\n\n /** Build complete SQL query from request. */\n toSql: (\n request: QueryRequest<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n Names<TColumns>,\n TTable\n >,\n ) => Sql;\n\n /** Get individual SQL parts for custom assembly. */\n toParts: (\n request: QueryRequest<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n Names<TColumns>,\n TTable\n >,\n ) => QueryParts;\n}\n\n// --- defineQueryModel Implementation ---\n\n/**\n * Define a query model with controlled field selection, filtering, and sorting.\n *\n * @example\n * const model = defineQueryModel({\n * table: Events,\n * dimensions: {\n * status: { column: \"status\" },\n * day: { expression: sql`toDate(timestamp)`, as: \"day\" },\n * },\n * metrics: {\n * totalEvents: { agg: count(), as: \"total_events\" },\n * totalAmount: { agg: sum(Events.columns.amount), as: \"total_amount\" },\n * },\n * filters: {\n * status: { column: \"status\", operators: [\"eq\", \"in\"] as const },\n * },\n * sortable: [\"amount\", \"timestamp\"] as const,\n * });\n */\nexport function defineQueryModel<\n TTable,\n TMetrics extends Record<string, MetricDef>,\n TDimensions extends Record<string, DimensionDef<TTable, keyof TTable>>,\n TFilters extends Record<string, ModelFilterDef<TTable, keyof TTable>>,\n TSortable extends string,\n TColumns extends Record<string, ColumnDef<TTable>> = Record<string, never>,\n TJoins extends Record<string, JoinDef> = Record<string, never>,\n TResult = TTable,\n>(\n config: QueryModelConfig<\n TTable,\n TMetrics,\n TDimensions,\n TFilters,\n TSortable,\n TColumns,\n TJoins\n >,\n): QueryModel<\n TTable,\n TMetrics,\n TDimensions,\n TFilters,\n TSortable,\n TResult,\n TColumns\n> {\n type Req = QueryRequest<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n Names<TColumns>,\n TTable\n >;\n\n const {\n table,\n dimensions,\n metrics,\n columns: columnDefs,\n joins: joinDefs,\n filters,\n sortable,\n defaults = {},\n } = config;\n const { maxLimit = 1000 } = defaults;\n\n const primaryTableName = table.name;\n const hasJoins = joinDefs != null && Object.keys(joinDefs).length > 0;\n\n // --- Normalize dimensions ---\n\n const normalizedDimensions: Record<string, FieldDef> = {};\n if (dimensions) {\n for (const [name, def] of Object.entries(dimensions)) {\n const column =\n def.column ?\n hasJoins ? undefined\n : table.columns[def.column]\n : undefined;\n const expression =\n def.expression ??\n (def.column && hasJoins ?\n raw(\n `${quoteIdentifier(primaryTableName)}.${quoteIdentifier(String(def.column))}`,\n )\n : undefined);\n normalizedDimensions[name] = { column, expression, as: def.as };\n }\n }\n\n // --- Normalize metrics ---\n\n const normalizedMetrics: Record<string, FieldDef> = {};\n if (metrics) {\n for (const [name, def] of Object.entries(metrics)) {\n normalizedMetrics[name] = { agg: def.agg, as: def.as };\n }\n }\n\n // --- Normalize columns (detail queries) ---\n\n const normalizedColumns: Record<string, FieldDef> = {};\n if (columnDefs) {\n for (const [name, def] of Object.entries(columnDefs)) {\n if (def.join && joinDefs) {\n const joinDef = joinDefs[def.join];\n if (!joinDef) {\n throw new Error(\n `Column '${name}' references unknown join '${def.join}'`,\n );\n }\n const joinTableName = joinDef.table.name;\n normalizedColumns[name] = {\n expression: raw(\n `${quoteIdentifier(joinTableName)}.${quoteIdentifier(String(def.column))}`,\n ),\n as: def.as,\n };\n } else if (hasJoins) {\n normalizedColumns[name] = {\n expression: raw(\n `${quoteIdentifier(primaryTableName)}.${quoteIdentifier(String(def.column))}`,\n ),\n as: def.as,\n };\n } else {\n normalizedColumns[name] = {\n column: table.columns[def.column as keyof TTable],\n as: def.as,\n };\n }\n }\n }\n\n // --- Resolve filters ---\n\n const resolvedFilters: Record<\n string,\n FilterDef & { inputType?: FilterInputTypeHint }\n > = {};\n const filtersWithInputType: Record<\n string,\n ModelFilterDef<TTable, keyof TTable> & { inputType?: FilterInputTypeHint }\n > = {};\n for (const [name, def] of Object.entries(filters)) {\n if (def.metric) {\n // HAVING filter — references a metric by name\n const metricDef = normalizedMetrics[def.metric];\n if (!metricDef) {\n throw new Error(\n `Filter '${name}' references unknown metric '${def.metric}'`,\n );\n }\n const alias = metricDef.as ?? def.metric;\n const inputType = def.inputType ?? \"number\";\n\n resolvedFilters[name] = {\n column: identifier(alias),\n operators: def.operators,\n transform: def.transform as ((value: SqlValue) => SqlValue) | undefined,\n inputType,\n isHaving: true,\n };\n\n filtersWithInputType[name] = { ...def, inputType };\n } else if (def.column != null) {\n // WHERE filter — references a table column\n const columnRef: Column = table.columns[def.column];\n if (!columnRef) {\n throw new Error(\n `Filter '${name}' references unknown column '${String(def.column)}' on table '${primaryTableName}'`,\n );\n }\n\n const inputType =\n def.inputType ??\n (columnRef.data_type ?\n deriveInputTypeFromDataType(columnRef.data_type)\n : undefined);\n\n const resolvedColumn: ColRef =\n hasJoins ?\n raw(\n `${quoteIdentifier(primaryTableName)}.${quoteIdentifier(String(def.column))}`,\n )\n : columnRef;\n\n resolvedFilters[name] = {\n column: resolvedColumn,\n operators: def.operators,\n transform: def.transform as ((value: SqlValue) => SqlValue) | undefined,\n inputType,\n };\n\n filtersWithInputType[name] = { ...def, inputType };\n } else {\n throw new Error(\n `Filter '${name}' must specify either 'column' or 'metric'`,\n );\n }\n }\n\n // --- Combined field map ---\n\n const normalizedFields: Record<string, FieldDef> = {\n ...normalizedDimensions,\n ...normalizedMetrics,\n ...normalizedColumns,\n };\n\n const dimensionNamesSet = new Set(Object.keys(normalizedDimensions));\n const metricNamesSet = new Set(Object.keys(normalizedMetrics));\n const columnNamesSet = new Set(Object.keys(normalizedColumns));\n\n const dimensionNames: readonly string[] = Object.keys(normalizedDimensions);\n const metricNames: readonly string[] = Object.keys(normalizedMetrics);\n const columnNames: readonly string[] = Object.keys(normalizedColumns);\n\n // --- SQL building helpers ---\n\n function buildFieldExpr(field: FieldDef, defaultAlias: string): Sql {\n const expr =\n field.agg ??\n field.expression ??\n (field.column ? sql`${field.column}` : empty);\n if (!expr || isEmpty(expr)) return empty;\n const alias = field.as ?? defaultAlias;\n return sql`${expr} AS ${identifier(String(alias))}`;\n }\n\n function buildFieldList(\n fieldDefs: Record<string, FieldDef>,\n selectFields?: string[],\n ): Sql[] {\n const fieldNames = selectFields ?? Object.keys(fieldDefs);\n return fieldNames\n .map((name) => {\n const field = fieldDefs[name];\n if (!field) return empty;\n return buildFieldExpr(field, name);\n })\n .filter((s) => !isEmpty(s));\n }\n\n function buildSelectClause(selectFields?: string[]): Sql {\n const parts = buildFieldList(normalizedFields, selectFields);\n return sql`SELECT ${join(parts)}`;\n }\n\n function buildFilterConditions(\n filterParams?: FilterParams<TFilters, TTable>,\n ): { where: Sql[]; having: Sql[] } {\n if (!filterParams) return { where: [], having: [] };\n\n const whereConds: Sql[] = [];\n const havingConds: Sql[] = [];\n for (const [filterName, ops] of Object.entries(filterParams)) {\n const filterDef = resolvedFilters[filterName];\n if (!filterDef) {\n throw new Error(`Unknown filter '${filterName}'`);\n }\n if (!ops) continue;\n\n for (const [op, value] of Object.entries(\n ops as Record<string, unknown>,\n )) {\n if (value === undefined) continue;\n if (!filterDef.operators.includes(op as FilterOperator)) {\n throw new Error(\n `Operator '${op}' not allowed for filter '${filterName}'`,\n );\n }\n\n const t = filterDef.transform ?? identity;\n const transformed = transformFilterValue(\n op as FilterOperator,\n value,\n t,\n );\n const condition = applyFilter(\n filterDef.column,\n op as FilterOperator,\n transformed,\n );\n if (!isEmpty(condition)) {\n if (filterDef.isHaving) {\n havingConds.push(condition);\n } else {\n whereConds.push(condition);\n }\n }\n }\n }\n return { where: whereConds, having: havingConds };\n }\n\n function buildOrderByClause(\n spec: ResolvedQuerySpec<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n TTable\n >,\n selectedFieldSet?: Set<string>,\n ): Sql {\n const orderBySpec =\n spec.orderBy && spec.orderBy.length > 0 ? spec.orderBy : defaults.orderBy;\n\n if (!orderBySpec || orderBySpec.length === 0) return empty;\n\n for (const [field] of orderBySpec) {\n if (!sortable.includes(field)) {\n throw new Error(`Field '${field}' is not sortable`);\n }\n }\n\n const parts = orderBySpec\n .map(([field, dir]) => {\n if (dir !== \"ASC\" && dir !== \"DESC\") {\n throw new Error(`Invalid sort direction '${dir}'`);\n }\n\n const fieldDef = normalizedFields[field];\n if (!fieldDef) return empty;\n\n // Skip dimension-based ORDER BY fields that aren't in the current\n // SELECT list — ClickHouse rejects non-aggregate expressions that\n // aren't part of GROUP BY.\n if (\n selectedFieldSet &&\n dimensionNamesSet.has(field) &&\n !selectedFieldSet.has(field)\n ) {\n return empty;\n }\n\n const alias = fieldDef.as ?? String(field);\n const col =\n fieldDef.expression ??\n (fieldDef.column ? sql`${fieldDef.column}` : empty);\n\n // For aggregate metrics, ORDER BY the SELECT alias.\n const orderExpr = fieldDef.agg ? identifier(alias) : col;\n if (isEmpty(orderExpr)) return empty;\n\n return sql`${orderExpr} ${raw(dir)}`;\n })\n .filter((p) => !isEmpty(p));\n\n return parts.length > 0 ? sql`ORDER BY ${join(parts)}` : empty;\n }\n\n function buildFromClause(): Sql {\n if (!hasJoins) {\n return sql`FROM ${table}`;\n }\n\n let fromClause = sql`FROM ${table}`;\n for (const [, joinDef] of Object.entries(joinDefs!)) {\n const joinType = joinDef.type ?? \"LEFT\";\n\n let onClause: Sql;\n if (joinDef.leftKey && joinDef.rightKey) {\n const joinTableName = joinDef.table.name;\n onClause = raw(\n `${quoteIdentifier(primaryTableName)}.${quoteIdentifier(joinDef.leftKey)} = ${quoteIdentifier(joinTableName)}.${quoteIdentifier(joinDef.rightKey)}`,\n );\n } else if (joinDef.on) {\n onClause = joinDef.on;\n } else {\n throw new Error(\"JoinDef must specify either leftKey/rightKey or on\");\n }\n\n fromClause = sql`${fromClause} ${raw(joinType)} JOIN ${joinDef.table} ON ${onClause}`;\n }\n return fromClause;\n }\n\n function resolveQuerySpec(\n request: Req,\n ): ResolvedQuerySpec<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n TTable,\n Names<TColumns>\n > {\n if (request.columns && request.columns.length > 0) {\n return {\n select: request.columns,\n groupBy: undefined,\n filters: request.filters,\n orderBy: request.orderBy,\n limit: request.limit,\n page: request.page,\n offset: request.offset,\n detailMode: true,\n };\n }\n\n const select = [...(request.dimensions ?? []), ...(request.metrics ?? [])];\n const groupBy =\n request.dimensions && request.dimensions.length > 0 ?\n request.dimensions\n : undefined;\n\n return {\n select: select.length > 0 ? select : undefined,\n groupBy,\n filters: request.filters,\n orderBy: request.orderBy,\n limit: request.limit,\n page: request.page,\n offset: request.offset,\n detailMode: false,\n };\n }\n\n function buildGroupByClause(\n spec: ResolvedQuerySpec<\n keyof TMetrics & string,\n keyof TDimensions & string,\n TFilters,\n TSortable,\n TTable\n >,\n ): Sql {\n const groupByFields = spec.groupBy ?? defaults.groupBy;\n if (!groupByFields || groupByFields.length === 0) return empty;\n\n const groupExprs = groupByFields.map((fieldName) => {\n if (!dimensionNamesSet.has(fieldName)) {\n throw new Error(`Field '${fieldName}' is not a valid dimension`);\n }\n\n const field = normalizedFields[fieldName];\n if (!field) {\n throw new Error(`Field '${fieldName}' is not a valid dimension`);\n }\n if (field.expression) return field.expression;\n if (field.column) return sql`${field.column}`;\n return raw(fieldName);\n });\n\n return groupByClause(...groupExprs);\n }\n\n function toParts(request: Req): QueryParts {\n const spec = resolveQuerySpec(request);\n\n if (spec.offset != null && spec.page != null) {\n throw new Error(\n \"Cannot specify both 'offset' and 'page' — they are mutually exclusive\",\n );\n }\n\n const limitVal = Math.min(spec.limit ?? defaults.limit ?? 100, maxLimit);\n const offsetVal = spec.offset ?? (spec.page ?? 0) * limitVal;\n const pagination =\n spec.offset != null ?\n sql`LIMIT ${limitVal} OFFSET ${offsetVal}`\n : paginate(limitVal, spec.page ?? 0);\n\n const selectedFields =\n spec.select ??\n (spec.detailMode ?\n Object.keys(normalizedFields)\n : [...dimensionNames, ...metricNames]);\n const selectedColumns = selectedFields.filter((f) => columnNamesSet.has(f));\n const selectedDimensions = selectedFields.filter((f) =>\n dimensionNamesSet.has(f),\n );\n const selectedMetrics = selectedFields.filter((f) => metricNamesSet.has(f));\n\n const columnParts = buildFieldList(\n normalizedColumns,\n selectedColumns.length > 0 ? selectedColumns : undefined,\n );\n const dimensionParts = buildFieldList(\n normalizedDimensions,\n selectedDimensions.length > 0 ? selectedDimensions : undefined,\n );\n const metricParts = buildFieldList(\n normalizedMetrics,\n selectedMetrics.length > 0 ? selectedMetrics : undefined,\n );\n\n const selectedFieldSet = new Set(selectedFields);\n const selectClause = buildSelectClause(selectedFields);\n const filterResult = buildFilterConditions(spec.filters);\n const whereClause =\n filterResult.where.length > 0 ? where(...filterResult.where) : empty;\n const havingPart =\n filterResult.having.length > 0 ?\n havingClause(...filterResult.having)\n : empty;\n const groupByPart = spec.detailMode ? empty : buildGroupByClause(spec);\n const orderByPart = buildOrderByClause(spec, selectedFieldSet);\n\n return {\n select: selectClause,\n dimensions: dimensionParts.length > 0 ? join(dimensionParts) : empty,\n metrics: metricParts.length > 0 ? join(metricParts) : empty,\n columns: columnParts.length > 0 ? join(columnParts) : empty,\n from: buildFromClause(),\n conditions: filterResult.where,\n where: whereClause,\n groupBy: groupByPart,\n having: havingPart,\n orderBy: orderByPart,\n pagination,\n };\n }\n\n function toSql(request: Req): Sql {\n const parts = toParts(request);\n return sql`\n ${parts.select}\n ${parts.from}\n ${parts.where}\n ${parts.groupBy}\n ${parts.having}\n ${parts.orderBy}\n ${parts.pagination}\n `;\n }\n\n const model = {\n name: config.name,\n description: config.description,\n defaults,\n filters: filtersWithInputType as TFilters &\n Record<string, { inputType?: FilterInputTypeHint }>,\n sortable,\n dimensions,\n metrics,\n columns: columnDefs,\n columnNames,\n query: async (request, client: QueryClient) => {\n const result = await client.execute(toSql(request));\n return result.json();\n },\n toSql,\n toParts,\n $inferDimensions: undefined as never,\n $inferMetrics: undefined as never,\n $inferColumns: undefined as never,\n $inferFilters: undefined as never,\n $inferRequest: undefined as never,\n $inferResult: undefined as never,\n } satisfies QueryModel<\n TTable,\n TMetrics,\n TDimensions,\n TFilters,\n TSortable,\n TResult,\n TColumns\n >;\n\n return model;\n}\n","/**\n * Fluent Query Builder API\n *\n * Provides a chainable API for building QueryRequest objects.\n *\n * @module query-layer/query-builder\n */\n\nimport type { Sql } from \"../sqlHelpers\";\nimport type { QueryClient } from \"../consumption-apis/helpers\";\nimport type {\n SortDir,\n SqlValue,\n FilterDefBase,\n QueryRequest,\n QueryParts,\n Names,\n OperatorValueType,\n ColumnDef,\n DimensionDef,\n MetricDef,\n} from \"./types\";\nimport type { QueryModel } from \"./query-model\";\n\n/**\n * Fluent builder for constructing query requests.\n *\n * @example\n * const results = await buildQuery(model)\n * .dimensions([\"status\"])\n * .metrics([\"totalEvents\", \"totalAmount\"])\n * .filter(\"status\", \"eq\", \"active\")\n * .orderBy([\"totalAmount\", \"DESC\"])\n * .limit(10)\n * .execute(client.query);\n */\nexport interface QueryBuilder<\n TMetrics extends string,\n TDimensions extends string,\n TFilters extends Record<string, FilterDefBase>,\n TSortable extends string,\n TResult,\n TTable = any,\n TColumns extends string = string,\n> {\n /** Add a filter condition. Automatically skips if value is undefined or null. */\n filter<K extends keyof TFilters, Op extends TFilters[K][\"operators\"][number]>(\n filterName: K,\n op: Op,\n value: OperatorValueType<Op, SqlValue> | undefined,\n ): this;\n\n /** Set dimensions to include in query (aggregate mode) */\n dimensions(fields: TDimensions[]): this;\n\n /** Set metrics to include in query (aggregate mode) */\n metrics(fields: TMetrics[]): this;\n\n /** Set columns for detail mode (no aggregation, no GROUP BY) */\n columns(fields: TColumns[]): this;\n\n /** Set multi-column sort */\n orderBy(...orders: Array<[TSortable, SortDir]>): this;\n\n /** Set maximum number of rows to return */\n limit(n: number): this;\n\n /** Set page number (0-indexed) for pagination */\n page(n: number): this;\n\n /** Set row offset for pagination */\n offset(n: number): this;\n\n /** Build the QueryRequest object */\n build(): QueryRequest<\n TMetrics,\n TDimensions,\n TFilters,\n TSortable,\n TColumns,\n TTable\n >;\n\n /** Build the SQL query */\n toSql(): Sql;\n\n /** Get query parts for custom assembly */\n toParts(): QueryParts;\n\n /** Build SQL with custom assembly function */\n assemble(fn: (parts: QueryParts) => Sql): Sql;\n\n /** Execute the query with Moose QueryClient. */\n execute(client: QueryClient): Promise<TResult[]>;\n}\n\n/**\n * Create a fluent query builder for a model.\n *\n * @param model - QueryModel instance to build queries for\n * @returns QueryBuilder instance with chainable methods\n *\n * @example\n * const results = await buildQuery(model)\n * .dimensions([\"status\"])\n * .metrics([\"totalEvents\", \"totalAmount\"])\n * .filter(\"status\", \"eq\", \"active\")\n * .orderBy([\"totalAmount\", \"DESC\"])\n * .limit(10)\n * .execute(client.query);\n */\nexport function buildQuery<\n TTable,\n TMetrics extends Record<string, MetricDef>,\n TDimensions extends Record<string, DimensionDef>,\n TFilters extends Record<string, FilterDefBase>,\n TSortable extends string,\n TResult,\n TColumns extends Record<string, ColumnDef> = Record<string, never>,\n>(\n model: QueryModel<\n TTable,\n TMetrics,\n TDimensions,\n TFilters,\n TSortable,\n TResult,\n TColumns\n >,\n): QueryBuilder<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n TResult,\n TTable,\n Names<TColumns>\n> {\n const state: {\n filters: Record<string, Record<string, unknown>>;\n dimensions?: Array<Names<TDimensions>>;\n metrics?: Array<Names<TMetrics>>;\n columns?: Array<Names<TColumns>>;\n orderBy?: Array<[TSortable, SortDir]>;\n limit?: number;\n page?: number;\n offset?: number;\n } = {\n filters: Object.create(null) as Record<string, Record<string, unknown>>,\n };\n\n const buildRequest = (): QueryRequest<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n Names<TColumns>,\n TTable\n > =>\n ({\n filters:\n Object.keys(state.filters).length > 0 ? state.filters : undefined,\n dimensions: state.dimensions,\n metrics: state.metrics,\n columns: state.columns,\n orderBy: state.orderBy,\n limit: state.limit,\n page: state.page,\n offset: state.offset,\n }) as QueryRequest<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n Names<TColumns>,\n TTable\n >;\n\n const builder: QueryBuilder<\n Names<TMetrics>,\n Names<TDimensions>,\n TFilters,\n TSortable,\n TResult,\n TTable,\n Names<TColumns>\n > = {\n filter(filterName, op, value) {\n if (value === undefined || value === null) return builder;\n const key = String(filterName);\n if (!Object.hasOwn(state.filters, key)) state.filters[key] = {};\n state.filters[key][op] = value;\n return builder;\n },\n\n dimensions(fields) {\n state.dimensions = fields;\n return builder;\n },\n\n metrics(fields) {\n state.metrics = fields;\n return builder;\n },\n\n columns(fields) {\n state.columns = fields;\n return builder;\n },\n\n orderBy(...orders) {\n state.orderBy = orders;\n return builder;\n },\n\n limit(n) {\n state.limit = n;\n return builder;\n },\n\n page(n) {\n state.page = n;\n state.offset = undefined;\n return builder;\n },\n\n offset(n) {\n state.offset = n;\n state.page = undefined;\n return builder;\n },\n\n build: buildRequest,\n toSql: () => model.toSql(buildRequest()),\n toParts: () => model.toParts(buildRequest()),\n assemble: (fn) => fn(model.toParts(buildRequest())),\n execute: (client) => model.query(buildRequest(), client),\n };\n\n return builder;\n}\n","/**\n * MCP Schema Generation from QueryModel\n *\n * Auto-generates Zod schemas and request builders for MCP tools\n * directly from QueryModel metadata (filters, dimensions, metrics, columns).\n *\n * @module query-layer/model-tools\n */\n\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { toQuery, type Sql } from \"../sqlHelpers\";\nimport { QueryClient } from \"../consumption-apis/helpers\";\nimport type { FilterInputTypeHint, SortDir } from \"./types\";\n// =============================================================================\n// QueryModelBase — Minimal structural interface for MCP utilities\n// =============================================================================\n\n/** Filter definition shape expected by MCP utilities. */\nexport interface QueryModelFilter {\n operators: readonly string[];\n inputType?: FilterInputTypeHint;\n required?: true;\n description?: string;\n}\n\n/**\n * Minimal model interface consumed by createModelTool / registerModelTools.\n *\n * Any QueryModel from defineQueryModel() satisfies this structurally —\n * no explicit `implements` needed. This avoids propagating generic\n * type parameters into the MCP layer.\n */\nexport interface QueryModelBase {\n readonly name?: string;\n readonly description?: string;\n readonly defaults: {\n orderBy?: Array<[string, SortDir]>;\n groupBy?: string[];\n limit?: number;\n maxLimit?: number;\n dimensions?: string[];\n metrics?: string[];\n columns?: string[];\n };\n readonly filters: Record<string, QueryModelFilter>;\n readonly sortable: readonly string[];\n readonly dimensions?: Record<string, { description?: string }>;\n readonly metrics?: Record<string, { description?: string }>;\n readonly columnNames: readonly string[];\n toSql(request: Record<string, unknown>): Sql;\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\nfunction camelToSnake(s: string): string {\n return s.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);\n}\n\nfunction titleFromName(name: string): string {\n return name\n .replace(/^query_/, \"Query \")\n .replace(/^list_/, \"List \")\n .replace(/_/g, \" \")\n .replace(/\\b\\w/g, (c) => c.toUpperCase());\n}\n\nfunction buildEnumDescription(\n metadata: Record<string, { description?: string }>,\n): string | undefined {\n const entries = Object.entries(metadata);\n if (entries.length === 0) return undefined;\n const lines = entries.map(([name, def]) => {\n return def.description ? `- ${name}: ${def.description}` : `- ${name}`;\n });\n return lines.join(\"\\n\");\n}\n\n/** Map FilterInputTypeHint to a base Zod type */\nfunction zodBaseType(inputType?: FilterInputTypeHint): z.ZodType {\n if (inputType === \"number\") return z.number();\n return z.string();\n}\n\n/** Scalar operators use the base type; list operators use z.array(base) */\nconst SCALAR_OPS = new Set([\n \"eq\",\n \"ne\",\n \"gt\",\n \"gte\",\n \"lt\",\n \"lte\",\n \"like\",\n \"ilike\",\n]);\nconst LIST_OPS = new Set([\"in\", \"notIn\"]);\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface ModelToolOptions {\n /** Filter names whose `eq` param is required (not optional). Merged with model-level `required` flags. */\n requiredFilters?: string[];\n /** Maximum limit for the tool. Falls back to model.defaults.maxLimit, then 1000. */\n maxLimit?: number;\n /** Default limit for the tool. Falls back to model.defaults.limit, then 100. */\n defaultLimit?: number;\n /** Default values applied when params are absent. Merged with model.defaults. */\n defaults?: {\n dimensions?: string[];\n metrics?: string[];\n columns?: string[];\n limit?: number;\n };\n}\n\nexport interface ModelToolResult {\n /** Zod shape object to pass to server.tool() */\n schema: Record<string, z.ZodType>;\n /** Convert flat MCP params into a nested QueryRequest */\n buildRequest: (params: Record<string, unknown>) => Record<string, unknown>;\n}\n\n// =============================================================================\n// createModelTool\n// =============================================================================\n\n/**\n * Generate a Zod schema and request builder from a QueryModel.\n *\n * Required filters, maxLimit, and default selections are first read from the\n * model itself (via `required: true` on filter defs and `model.defaults`).\n * The optional `options` param can override or extend any of these.\n *\n * @param model - A QueryModel instance (from defineQueryModel)\n * @param options - Optional overrides for required filters, limits, defaults\n * @returns `{ schema, buildRequest }` ready for `server.tool()`\n */\nexport function createModelTool(\n model: QueryModelBase,\n options: ModelToolOptions = {},\n): ModelToolResult {\n // Derive required filters from model filter defs (where required === true)\n const modelRequiredFilters: string[] = [];\n for (const [filterName, filterDef] of Object.entries(model.filters)) {\n if (filterDef.required) {\n modelRequiredFilters.push(filterName);\n }\n }\n\n // Merge model defaults with option overrides (options win)\n const modelDefaults = model.defaults;\n const mergedDefaults = {\n dimensions: options.defaults?.dimensions ?? modelDefaults.dimensions,\n metrics: options.defaults?.metrics ?? modelDefaults.metrics,\n columns: options.defaults?.columns ?? modelDefaults.columns,\n limit: options.defaults?.limit ?? modelDefaults.limit,\n };\n\n const requiredFilters = [\n ...new Set([...modelRequiredFilters, ...(options.requiredFilters ?? [])]),\n ];\n const maxLimit = options.maxLimit ?? modelDefaults.maxLimit ?? 1000;\n const defaultLimit = options.defaultLimit ?? mergedDefaults.limit ?? 100;\n\n const requiredSet = new Set(requiredFilters);\n const schema: Record<string, z.ZodType> = {};\n\n // Map from MCP param name → { filterName, operator }\n const filterParamMap: Record<string, { filterName: string; op: string }> = {};\n\n // --- Dimensions ---\n const dimensionNames = Object.keys(model.dimensions ?? {});\n if (dimensionNames.length > 0) {\n const names = dimensionNames as [string, ...string[]];\n const desc = buildEnumDescription(model.dimensions!);\n const dimSchema = z.array(z.enum(names)).optional();\n schema.dimensions = desc ? dimSchema.describe(desc) : dimSchema;\n }\n\n // --- Metrics ---\n const metricNames = Object.keys(model.metrics ?? {});\n if (metricNames.length > 0) {\n const names = metricNames as [string, ...string[]];\n const desc = buildEnumDescription(model.metrics!);\n const metSchema = z.array(z.enum(names)).optional();\n schema.metrics = desc ? metSchema.describe(desc) : metSchema;\n }\n\n // --- Columns ---\n if (model.columnNames.length > 0) {\n const names = model.columnNames as readonly [string, ...string[]];\n schema.columns = z.array(z.enum(names)).optional();\n }\n\n // --- Filters ---\n for (const [filterName, filterDef] of Object.entries(model.filters)) {\n const baseType = zodBaseType(filterDef.inputType);\n\n for (const op of filterDef.operators) {\n // Build the MCP param name\n const snakeName = camelToSnake(filterName);\n const paramName = op === \"eq\" ? snakeName : `${snakeName}_${op}`;\n\n // Determine Zod type for this operator\n let paramType: z.ZodType;\n if (SCALAR_OPS.has(op)) {\n paramType = baseType;\n } else if (LIST_OPS.has(op)) {\n paramType = z.array(baseType);\n } else if (op === \"between\") {\n paramType = z.array(baseType).length(2);\n } else if (op === \"isNull\" || op === \"isNotNull\") {\n paramType = z.boolean();\n } else {\n paramType = baseType;\n }\n\n // Required if filter is in requiredFilters AND op is eq\n if (requiredSet.has(filterName) && op === \"eq\") {\n schema[paramName] =\n filterDef.description ?\n paramType.describe(filterDef.description)\n : paramType;\n } else {\n const opt = paramType.optional();\n schema[paramName] =\n filterDef.description ? opt.describe(filterDef.description) : opt;\n }\n\n filterParamMap[paramName] = { filterName, op };\n }\n }\n\n // --- Limit ---\n schema.limit = z\n .number()\n .min(1)\n .max(maxLimit)\n .default(defaultLimit)\n .optional();\n\n // --- buildRequest ---\n function buildRequest(\n params: Record<string, unknown>,\n ): Record<string, unknown> {\n const request: Record<string, unknown> = {};\n\n // Dimensions\n if (dimensionNames.length > 0) {\n request.dimensions =\n (params.dimensions as string[] | undefined) ??\n mergedDefaults.dimensions;\n }\n\n // Metrics\n if (metricNames.length > 0) {\n request.metrics =\n (params.metrics as string[] | undefined) ?? mergedDefaults.metrics;\n }\n\n // Columns\n if (model.columnNames.length > 0) {\n request.columns =\n (params.columns as string[] | undefined) ?? mergedDefaults.columns;\n }\n\n // Filters: reverse-map flat params to nested { [filterName]: { [op]: value } }\n const filterObj: Record<string, Record<string, unknown>> = {};\n for (const [paramName, mapping] of Object.entries(filterParamMap)) {\n const value = params[paramName];\n if (value === undefined) continue;\n if (!filterObj[mapping.filterName]) {\n filterObj[mapping.filterName] = {};\n }\n filterObj[mapping.filterName][mapping.op] = value;\n }\n if (Object.keys(filterObj).length > 0) {\n request.filters = filterObj;\n }\n\n // Limit\n request.limit =\n (params.limit as number | undefined) ??\n mergedDefaults.limit ??\n defaultLimit;\n\n return request;\n }\n\n return { schema, buildRequest };\n}\n\n// =============================================================================\n// registerModelTools\n// =============================================================================\n\n/**\n * Register MCP tools for all models that have a `name` defined.\n *\n * Each model with a `name` property becomes an MCP tool. The library handles\n * everything: schema generation from model metadata, request building from\n * flat MCP params, SQL generation via `model.toSql()`, parameterized\n * execution with readonly enforcement, and MCP response formatting.\n *\n * Models without a `name` are silently skipped.\n *\n * @param server - McpServer instance\n * @param models - Array of QueryModel instances (from `defineQueryModel`)\n * @param queryClient - The QueryClient from `mooseUtils.client.query`.\n * Queries are executed in readonly mode with parameterized SQL.\n *\n * @example\n * import { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n * import { getMooseUtils, MooseUtils } from \"@514labs/moose-lib\";\n * import { registerModelTools } from \"@514labs/moose-lib\";\n * import { visitsModel, usersModel } from \"./models\";\n *\n * const serverFactory = (mooseUtils: MooseUtils) => {\n * const server = new McpServer({ name: \"my-tools\", version: \"1.0.0\" });\n *\n * // One line registers all named models as MCP tools\n * registerModelTools(server, [visitsModel, usersModel], mooseUtils.client.query);\n *\n * return server;\n * };\n */\nexport function registerModelTools(\n server: McpServer,\n models: QueryModelBase[],\n queryClient: QueryClient,\n): void {\n for (const model of models) {\n if (!model.name) continue;\n\n const toolName = model.name;\n const toolDescription = model.description ?? toolName;\n const tool = createModelTool(model);\n const defaultLimit = model.defaults?.limit ?? 100;\n\n server.tool(\n toolName,\n toolDescription,\n // MCP SDK's server.tool() triggers TS2589 (infinite type instantiation)\n // when given Record<string, z.ZodType>. Cast to `any` to prevent the DTS\n // generator from expanding the SDK's deeply recursive overload signatures.\n // Tracked upstream: https://github.com/modelcontextprotocol/typescript-sdk/issues/205\n tool.schema as any, // eslint-disable-line @typescript-eslint/no-explicit-any\n { title: titleFromName(toolName) },\n async (params: Record<string, unknown>) => {\n try {\n const request = tool.buildRequest(params);\n const limit =\n typeof params.limit === \"number\" ? params.limit : defaultLimit;\n const sqlObj = model.toSql(request);\n const [query, queryParams] = toQuery(sqlObj);\n const result = await queryClient.client.query({\n query,\n query_params: queryParams,\n format: \"JSONEachRow\",\n clickhouse_settings: {\n readonly: \"2\",\n max_result_rows: limit.toString(),\n },\n });\n const data = await result.json();\n const rows = Array.isArray(data) ? data : [];\n return {\n content: [\n {\n type: \"text\" as const,\n text: JSON.stringify({ rows, rowCount: rows.length }, null, 2),\n },\n ],\n };\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n const safeMsg = msg.length > 200 ? msg.slice(0, 200) + \"...\" : msg;\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Error in ${toolName}: ${safeMsg}`,\n },\n ],\n isError: true,\n };\n }\n },\n );\n }\n}\n","/**\n * Validation Utilities\n *\n * Request validation and error handling for API endpoints.\n *\n * @module query-layer/validation\n */\n\nimport type { IValidation } from \"typia\";\n\n// --- Error Types ---\n\n/** Frontend-friendly validation error structure */\nexport interface ValidationError {\n path: string;\n message: string;\n expected: string;\n received: string;\n}\n\n/** Error thrown when validation fails */\nexport class BadRequestError extends Error {\n public readonly errors: ValidationError[];\n\n constructor(typiaErrors: IValidation.IError[]) {\n super(\"Validation failed\");\n this.name = \"BadRequestError\";\n this.errors = typiaErrors.map((e) => ({\n path: e.path,\n message: `Expected ${e.expected}`,\n expected: e.expected,\n received: typeof e.value === \"undefined\" ? \"undefined\" : String(e.value),\n }));\n }\n\n toJSON() {\n return { error: this.message, details: this.errors };\n }\n}\n\n/** Assert validation result, throw BadRequestError if invalid */\nexport function assertValid<T>(result: IValidation<T>): T {\n if (!result.success) {\n throw new BadRequestError(result.errors);\n }\n return result.data;\n}\n\n// --- Query Handler ---\n\n/**\n * Query handler with three entry points for queries.\n * Use this when writing raw SQL without a query model.\n */\nexport interface QueryHandler<P, R> {\n run: (params: P) => Promise<R>;\n fromObject: (input: unknown) => Promise<R>;\n fromUrl: (url: string | URL) => Promise<R>;\n}\n\n/**\n * Create a simple query handler with validation.\n *\n * @example\n * const handler = createQueryHandler({\n * fromUrl: typia.http.createValidateQuery<MyParams>(),\n * fromObject: typia.createValidate<MyParams>(),\n * queryFn: async (params) => {\n * const query = sql`SELECT * FROM ${Table} ${where(...)}`;\n * return executeQuery(query);\n * },\n * });\n */\nexport function createQueryHandler<P, R>(config: {\n fromUrl: (input: string | URLSearchParams) => IValidation<P>;\n fromObject: (input: unknown) => IValidation<P>;\n queryFn: (params: P) => Promise<R>;\n}): QueryHandler<P, R> {\n return {\n run: config.queryFn,\n fromObject: (input) =>\n config.queryFn(assertValid(config.fromObject(input))),\n fromUrl: (url) => {\n // Dummy base required by URL constructor to parse relative paths\n // like \"/api?foo=bar\". Only .searchParams is used; the host is discarded.\n const searchParams =\n typeof url === \"string\" ?\n new URL(url, \"http://localhost\").searchParams\n : url.searchParams;\n return config.queryFn(assertValid(config.fromUrl(searchParams)));\n },\n };\n}\n","/**\n * Query Layer — Type-safe SQL query building for Moose + ClickHouse\n *\n * @example\n * import { defineQueryModel } from \"@514labs/moose-lib\";\n *\n * export const statsModel = defineQueryModel({\n * table: Events,\n * dimensions: { status: { column: \"status\" } },\n * metrics: { totalEvents: { agg: sql`count(*)`, as: \"total_events\" } },\n * filters: { status: { column: \"status\", operators: [\"eq\", \"in\"] as const } },\n * sortable: [\"total_events\"] as const,\n * });\n *\n * @module query-layer\n */\n\n// --- Primary API ---\n\nexport {\n defineQueryModel,\n type QueryModel,\n type QueryModelConfig,\n} from \"./query-model\";\n\n// --- Types ---\n\nexport type {\n QueryRequest,\n QueryParts,\n FilterParams,\n ColumnDef,\n JoinDef,\n DimensionDef,\n MetricDef,\n ModelFilterDef,\n FilterOperator,\n FilterInputTypeHint,\n FilterDefBase,\n SortDir,\n SqlValue,\n ColRef,\n Column,\n Names,\n OperatorValueType,\n} from \"./types\";\n\n// --- Composable Helpers ---\n\nexport {\n timeDimensions,\n columnsFromTable,\n filtersFromTable,\n deriveInputTypeFromDataType,\n} from \"./helpers\";\n\n// --- Fluent Query Builder ---\n\nexport { buildQuery, type QueryBuilder } from \"./query-builder\";\n\n// --- MCP Utilities ---\n\nexport {\n createModelTool,\n registerModelTools,\n type QueryModelBase,\n type QueryModelFilter,\n type ModelToolOptions,\n type ModelToolResult,\n} from \"./model-tools\";\n\n// --- SQL Utilities ---\n\nexport {\n raw,\n empty,\n join,\n isEmpty,\n\n // Filter function\n filter,\n\n // Comparison operators\n eq,\n ne,\n gt,\n gte,\n lt,\n lte,\n like,\n ilike,\n inList,\n notIn,\n between,\n isNull,\n isNotNull,\n\n // Logical combinators\n and,\n or,\n not,\n\n // SQL clauses\n where,\n orderBy,\n limit,\n offset,\n paginate,\n groupBy,\n having,\n\n // Aggregation functions\n count,\n countDistinct,\n sum,\n avg,\n min,\n max,\n\n // Select helpers\n select,\n as,\n type Expr,\n} from \"./sql-utils\";\n\n// --- Validation Utilities (re-exported from consumption-apis) ---\n\nexport {\n BadRequestError,\n assertValid,\n createQueryHandler,\n type ValidationError,\n type QueryHandler,\n} from \"../consumption-apis/validation\";\n","export * from \"./browserCompatible\";\n\nexport type DataModelConfig<T> = Partial<{\n ingestion: true;\n storage: {\n enabled?: boolean;\n order_by_fields?: (keyof T)[];\n deduplicate?: boolean;\n name?: string;\n };\n parallelism?: number;\n}>;\n\nexport * from \"./commons\";\nexport * from \"./secrets\";\nexport * from \"./consumption-apis/helpers\";\nexport {\n expressMiddleware,\n ExpressRequestWithMoose,\n getMooseUtilsFromRequest,\n getLegacyMooseUtils,\n} from \"./consumption-apis/webAppHelpers\";\nexport * from \"./scripts/task\";\n\nexport { MooseCache } from \"./clients/redisClient\";\n\nexport { ApiUtil, ConsumptionUtil } from \"./consumption-apis/helpers\";\n\nexport { getMooseUtils, getMooseClients } from \"./consumption-apis/standalone\";\nexport type { MooseUtils } from \"./consumption-apis/helpers\";\nexport { sql } from \"./sqlHelpers\";\n\nexport * from \"./utilities\";\nexport * from \"./connectors/dataSource\";\nexport {\n ClickHouseByteSize,\n ClickHouseInt,\n LowCardinality,\n ClickHouseNamedTuple,\n ClickHousePoint,\n ClickHouseRing,\n ClickHouseLineString,\n ClickHouseMultiLineString,\n ClickHousePolygon,\n ClickHouseMultiPolygon,\n} from \"./dataModels/types\";\n\nexport * from \"./query-layer\";\n","import { existsSync, readFileSync } from \"fs\";\nimport path from \"path\";\n\n/**\n * Shared TypeScript compiler configuration for moose projects.\n * Used by both moose-runner.ts (runtime) and moose-tspc.ts (pre-compilation).\n *\n * Moose now always uses pre-compiled JavaScript - no ts-node fallback.\n */\n\nexport const MOOSE_COMPILER_PLUGINS = [\n {\n transform: \"./node_modules/@514labs/moose-lib/dist/compilerPlugin.js\",\n // No longer using transformProgram - direct typia integration eliminates\n // the need for program replacement and the associated incremental compilation issues\n },\n {\n // Keep typia plugin for users who use typia directly (not through Moose resources)\n transform: \"typia/lib/transform\",\n },\n] as const;\n\n// Options required for moose compilation\n// Note: We only set what's absolutely necessary to avoid conflicts with user projects\nexport const MOOSE_COMPILER_OPTIONS = {\n experimentalDecorators: true,\n esModuleInterop: true,\n // Disable strict module syntax checking to avoid dual-package type conflicts\n // This prevents errors where the same type imported with different resolution\n // modes (CJS vs ESM) is treated as incompatible\n verbatimModuleSyntax: false,\n} as const;\n\n// Module resolution options - only applied if not already set in user's tsconfig\n// These help with ESM/CJS interop but can be overridden by user config\nexport const MOOSE_MODULE_OPTIONS = {\n module: \"NodeNext\",\n moduleResolution: \"NodeNext\",\n} as const;\n\n/**\n * Default source directory for user code.\n * Can be overridden via MOOSE_SOURCE_DIR environment variable.\n */\nexport function getSourceDir(): string {\n return process.env.MOOSE_SOURCE_DIR || \"app\";\n}\n\n/**\n * Default output directory for compiled code.\n */\nexport const DEFAULT_OUT_DIR = \".moose/compiled\";\n\n/**\n * Read the user's tsconfig.json and extract the outDir setting.\n * Supports JSONC (JSON with Comments) by evaluating as JavaScript\n * (JSON with comments is valid JS in ES2019+).\n *\n * Security note: This uses eval-like behavior (new Function), which means\n * malicious code in tsconfig.json would execute. This is acceptable because:\n * - The user controls their own tsconfig.json\n * - Same trust model as running `tsc` or any other tool that processes the file\n * - If you clone and run untrusted code, you're already at risk\n *\n * Returns the outDir if specified, or null if not.\n */\nexport function readUserOutDir(\n projectRoot: string = process.cwd(),\n): string | null {\n try {\n let content = readFileSync(\n path.join(projectRoot, \"tsconfig.json\"),\n \"utf-8\",\n );\n // Strip UTF-8 BOM if present\n if (content.charCodeAt(0) === 0xfeff) {\n content = content.slice(1);\n }\n // eslint-disable-next-line no-eval\n const tsconfig = eval(`(${content})`);\n return tsconfig.compilerOptions?.outDir || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get the output directory for compiled code.\n * Uses user's tsconfig outDir if specified, otherwise defaults to .moose/compiled\n */\nexport function getOutDir(projectRoot: string = process.cwd()): string {\n const userOutDir = readUserOutDir(projectRoot);\n return userOutDir || DEFAULT_OUT_DIR;\n}\n\n/**\n * Get the path to the compiled index.js file.\n */\nexport function getCompiledIndexPath(\n projectRoot: string = process.cwd(),\n): string {\n const outDir = getOutDir(projectRoot);\n const sourceDir = getSourceDir();\n // Resolve so that absolute outDir from tsconfig is handled correctly\n return path.resolve(projectRoot, outDir, sourceDir, \"index.js\");\n}\n\n/**\n * Check if pre-compiled artifacts exist for the current project.\n */\nexport function hasCompiledArtifacts(\n projectRoot: string = process.cwd(),\n): boolean {\n return existsSync(getCompiledIndexPath(projectRoot));\n}\n\n/**\n * Module system type for compilation output.\n */\nexport type ModuleSystem = \"esm\" | \"cjs\";\n\n/**\n * Detects the module system from the user's package.json.\n * Returns 'esm' if package.json has \"type\": \"module\", otherwise 'cjs'.\n *\n * @param projectRoot - Root directory containing package.json (defaults to cwd)\n * @returns The detected module system\n */\nexport function detectModuleSystem(\n projectRoot: string = process.cwd(),\n): ModuleSystem {\n const pkgPath = path.join(projectRoot, \"package.json\");\n\n if (existsSync(pkgPath)) {\n try {\n const pkgContent = readFileSync(pkgPath, \"utf-8\");\n const pkg = JSON.parse(pkgContent);\n if (pkg.type === \"module\") {\n return \"esm\";\n }\n } catch (e) {\n // If parsing fails, default to CJS\n console.debug(\n `[moose] Failed to parse package.json at ${pkgPath}, defaulting to CJS:`,\n e,\n );\n }\n }\n\n return \"cjs\";\n}\n\n/**\n * Get compiler module options based on detected module system.\n *\n * @param moduleSystem - The module system to get options for\n * @returns Compiler options for module and moduleResolution\n */\nexport function getModuleOptions(moduleSystem: ModuleSystem): {\n module: string;\n moduleResolution: string;\n} {\n if (moduleSystem === \"esm\") {\n return {\n module: \"ES2022\",\n moduleResolution: \"bundler\",\n };\n }\n return {\n module: \"CommonJS\",\n moduleResolution: \"Node\",\n };\n}\n\n/**\n * Dynamic module loader that works with both CJS and ESM.\n * Uses detected module system to determine loading strategy.\n *\n * @param modulePath - Path to the module to load\n * @param projectRoot - Root directory for module system detection\n * @returns The loaded module\n */\nexport async function loadModule<T = any>(\n modulePath: string,\n projectRoot: string = process.cwd(),\n): Promise<T> {\n const moduleSystem = detectModuleSystem(projectRoot);\n\n if (moduleSystem === \"esm\") {\n // Use dynamic import for ESM\n // pathToFileURL is needed for Windows compatibility with absolute paths\n const { pathToFileURL } = await import(\"url\");\n const fileUrl = pathToFileURL(modulePath).href;\n return await import(fileUrl);\n }\n\n // Use require for CJS\n // Note: In ESM builds (compiled by tsup), this code path is replaced with\n // the appropriate ESM imports. The dual-package build ensures compatibility.\n return require(modulePath);\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nconst DEFAULT_SOURCE_EXTENSIONS = new Set([\n \".ts\",\n \".tsx\",\n \".js\",\n \".jsx\",\n \".mts\",\n \".cts\",\n]);\n\n/**\n * Returns true when the file path matches supported source extensions.\n */\nexport function isSourceFilePath(filePath: string): boolean {\n const ext = path.extname(filePath).toLowerCase();\n return DEFAULT_SOURCE_EXTENSIONS.has(ext);\n}\n\n/**\n * Recursively finds source files under a directory.\n *\n * Skips:\n * - `node_modules` directories\n * - hidden directories\n * - TypeScript declaration files (`.d.ts`, `.d.mts`, `.d.cts`)\n */\nexport function findSourceFiles(\n dir: string,\n onReadError?: (directory: string, error: unknown) => void,\n): string[] {\n const files: string[] = [];\n if (!fs.existsSync(dir)) {\n return files;\n }\n\n const stack = [dir];\n while (stack.length > 0) {\n const current = stack.pop()!;\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(current, { withFileTypes: true });\n } catch (error) {\n onReadError?.(current, error);\n continue;\n }\n\n for (const entry of entries) {\n const fullPath = path.join(current, entry.name);\n if (entry.isDirectory()) {\n if (entry.name === \"node_modules\" || entry.name.startsWith(\".\")) {\n continue;\n }\n stack.push(fullPath);\n continue;\n }\n\n if (!entry.isFile() || !isSourceFilePath(fullPath)) {\n continue;\n }\n if (\n fullPath.endsWith(\".d.ts\") ||\n fullPath.endsWith(\".d.mts\") ||\n fullPath.endsWith(\".d.cts\")\n ) {\n continue;\n }\n files.push(path.resolve(fullPath));\n }\n }\n\n return files;\n}\n","/**\n * Utility functions for the DMv2 SDK.\n */\n\nexport {\n getSourceFileFromStack,\n getSourceFileInfo,\n type SourceFileInfo,\n} from \"./stackTrace\";\nexport { findSourceFiles, isSourceFilePath } from \"./sourceFiles\";\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport ts from \"typescript\";\n\nimport { getSourceDir } from \"../compiler-config\";\nimport { compilerLog } from \"../commons\";\nimport { findSourceFiles, isSourceFilePath } from \"./utils\";\n\ntype SignatureKind =\n | \"Table\"\n | \"Topic\"\n | \"ApiEndpoint\"\n | \"TopicToTableSyncProcess\"\n | \"View\"\n | \"MaterializedView\"\n | \"SqlResource\";\n\nexport interface InfrastructureSignatureJson {\n id: string;\n kind: SignatureKind;\n}\n\nexport interface DependencySignatures {\n pullsDataFrom: InfrastructureSignatureJson[];\n pushesDataTo: InfrastructureSignatureJson[];\n}\n\nexport interface DependencyAnalysisResult {\n apiByKey: Map<string, DependencySignatures>;\n workflowByName: Map<string, DependencySignatures>;\n webAppByName: Map<string, DependencySignatures>;\n}\n\ninterface RegistryLike {\n tables: Map<string, any>;\n streams: Map<string, any>;\n apis: Map<string, any>;\n workflows: Map<string, any>;\n webApps: Map<string, any>;\n}\n\ninterface RegistryIndex {\n tableIdsByName: Map<string, string[]>;\n topicIdsByName: Map<string, string[]>;\n tableIds: Set<string>;\n topicIds: Set<string>;\n warnedAmbiguousTableIds: Set<string>;\n}\n\ntype ResourceRef =\n | { kind: \"Table\"; id: string }\n | { kind: \"Topic\"; id: string }\n | { kind: \"View\"; id: string }\n | { kind: \"MaterializedView\"; id: string; targetTableId?: string }\n | { kind: \"SqlResource\"; id: string }\n | { kind: \"IngestPipeline\"; streamId?: string; tableId?: string };\n\ntype SqlResolvedResourceRef =\n | { kind: \"Table\"; id: string }\n | { kind: \"Topic\"; id: string };\n\ninterface AnalysisContext {\n checker: ts.TypeChecker;\n registryIndex: RegistryIndex;\n symbolResourceCache: Map<ts.Symbol, ResourceRef | null>;\n taskExpressionCache: Map<string, ts.NewExpression | null>;\n functionCache: Map<ts.Symbol, ts.FunctionLikeDeclaration[]>;\n}\n\nconst WRITE_METHODS = new Set([\"insert\", \"send\", \"publish\", \"emit\", \"write\"]);\n\nfunction createEmptyResult(): DependencyAnalysisResult {\n return {\n apiByKey: new Map<string, DependencySignatures>(),\n workflowByName: new Map<string, DependencySignatures>(),\n webAppByName: new Map<string, DependencySignatures>(),\n };\n}\n\nfunction buildRegistryIndex(registry: RegistryLike): RegistryIndex {\n const tableIdsByName = new Map<string, string[]>();\n const topicIdsByName = new Map<string, string[]>();\n const tableIds = new Set<string>();\n const topicIds = new Set<string>();\n\n registry.tables.forEach((table: any, id: string) => {\n tableIds.add(id);\n const baseName = typeof table?.name === \"string\" ? table.name : id;\n const existing = tableIdsByName.get(baseName) ?? [];\n existing.push(id);\n tableIdsByName.set(baseName, existing);\n });\n\n registry.streams.forEach((stream: any, id: string) => {\n topicIds.add(id);\n const streamName = typeof stream?.name === \"string\" ? stream.name : id;\n const existing = topicIdsByName.get(streamName) ?? [];\n existing.push(id);\n topicIdsByName.set(streamName, existing);\n });\n\n return {\n tableIdsByName,\n topicIdsByName,\n tableIds,\n topicIds,\n warnedAmbiguousTableIds: new Set<string>(),\n };\n}\n\nfunction resolveTableId(\n tableName: string,\n version: string | undefined,\n index: RegistryIndex,\n): string {\n if (version) {\n const candidates = new Set<string>([`${tableName}_${version}`]);\n if (version.includes(\".\")) {\n candidates.add(`${tableName}_${version.replace(/\\./g, \"_\")}`);\n }\n\n for (const candidate of candidates) {\n if (index.tableIds.has(candidate)) {\n return candidate;\n }\n\n const ids = index.tableIdsByName.get(candidate) ?? [];\n if (ids.length === 1) {\n return ids[0];\n }\n if (ids.length > 1) {\n if (ids.includes(candidate)) {\n return candidate;\n }\n return ids[0];\n }\n }\n\n return `${tableName}_${version}`;\n }\n\n const ids = index.tableIdsByName.get(tableName) ?? [];\n if (ids.length === 0) {\n return tableName;\n }\n if (ids.includes(tableName)) {\n return tableName;\n }\n if (ids.length > 1) {\n const warningKey = `${tableName}:${ids.join(\",\")}`;\n if (!index.warnedAmbiguousTableIds.has(warningKey)) {\n index.warnedAmbiguousTableIds.add(warningKey);\n compilerLog(\n `Warning: ambiguous table lineage reference '${tableName}' resolved to '${ids[0]}' from candidates [${ids.join(\", \")}]. Add an explicit version to disambiguate.`,\n );\n }\n }\n return ids[0];\n}\n\nfunction resolveTopicId(topicName: string, index: RegistryIndex): string {\n const ids = index.topicIdsByName.get(topicName) ?? [];\n if (ids.length === 0) {\n return topicName;\n }\n if (ids.includes(topicName)) {\n return topicName;\n }\n return ids[0];\n}\n\nfunction normalizeSqlIdentifier(token: string): string {\n let normalized = token.trim();\n normalized = normalized.replace(/^[`\"'\\[]+/, \"\");\n normalized = normalized.replace(/[`\"'\\]]+$/, \"\");\n return normalized;\n}\n\nfunction resolveResourceFromSqlIdentifier(\n identifier: string,\n index: RegistryIndex,\n): SqlResolvedResourceRef | undefined {\n const normalized = normalizeSqlIdentifier(identifier);\n if (!normalized) {\n return undefined;\n }\n\n const candidates = new Set<string>([normalized]);\n if (normalized.includes(\".\")) {\n const parts = normalized.split(\".\");\n const suffix = parts[parts.length - 1];\n if (suffix && suffix !== normalized) {\n candidates.add(suffix);\n }\n }\n\n for (const candidate of [...candidates]) {\n const versionedMatch = candidate.match(/^(.+)_\\d+_\\d+$/);\n if (versionedMatch?.[1]) {\n candidates.add(versionedMatch[1]);\n }\n }\n\n for (const candidate of candidates) {\n if (index.tableIds.has(candidate)) {\n return { kind: \"Table\", id: candidate };\n }\n if (index.tableIdsByName.has(candidate)) {\n return { kind: \"Table\", id: resolveTableId(candidate, undefined, index) };\n }\n if (index.topicIds.has(candidate)) {\n return { kind: \"Topic\", id: candidate };\n }\n if (index.topicIdsByName.has(candidate)) {\n return { kind: \"Topic\", id: resolveTopicId(candidate, index) };\n }\n }\n\n return undefined;\n}\n\nfunction inferResourcesFromSqlText(\n text: string,\n index: RegistryIndex,\n): SqlResolvedResourceRef[] {\n const refs = new Map<string, SqlResolvedResourceRef>();\n const addRef = (ref: SqlResolvedResourceRef | undefined) => {\n if (!ref) {\n return;\n }\n refs.set(`${ref.kind}:${ref.id}`, ref);\n };\n\n const trimmed = text.trim();\n if (trimmed && !/\\s/.test(trimmed)) {\n addRef(resolveResourceFromSqlIdentifier(trimmed, index));\n }\n\n const relationPattern =\n /\\b(?:from|join|into|update|table)\\s+([`\"'\\[]?[A-Za-z_][A-Za-z0-9_.]*[`\"'\\]]?)/gi;\n for (const match of text.matchAll(relationPattern)) {\n addRef(resolveResourceFromSqlIdentifier(match[1], index));\n }\n\n return [...refs.values()];\n}\n\nfunction collectSqlTextFragmentsFromExpression(\n expression: ts.Expression,\n ctx: AnalysisContext,\n fragments: string[],\n visitedSymbols = new Set<ts.Symbol>(),\n) {\n const unwrapped = unwrapExpression(expression);\n\n if (\n ts.isStringLiteral(unwrapped) ||\n ts.isNoSubstitutionTemplateLiteral(unwrapped)\n ) {\n fragments.push(unwrapped.text);\n return;\n }\n\n if (ts.isTemplateExpression(unwrapped)) {\n fragments.push(unwrapped.head.text);\n for (const span of unwrapped.templateSpans) {\n fragments.push(span.literal.text);\n }\n return;\n }\n\n if (ts.isTaggedTemplateExpression(unwrapped)) {\n if (isSqlTag(unwrapped.tag, ctx.checker)) {\n const resources = inferResourcesFromSqlTemplate(unwrapped.template, ctx);\n for (const resource of resources) {\n fragments.push(resource.id);\n }\n }\n return;\n }\n\n if (ts.isArrayLiteralExpression(unwrapped)) {\n for (const element of unwrapped.elements) {\n if (ts.isExpression(element)) {\n collectSqlTextFragmentsFromExpression(\n element,\n ctx,\n fragments,\n visitedSymbols,\n );\n }\n }\n return;\n }\n\n if (ts.isCallExpression(unwrapped)) {\n for (const arg of unwrapped.arguments) {\n collectSqlTextFragmentsFromExpression(\n arg,\n ctx,\n fragments,\n visitedSymbols,\n );\n }\n return;\n }\n\n if (ts.isObjectLiteralExpression(unwrapped)) {\n for (const property of unwrapped.properties) {\n if (ts.isPropertyAssignment(property)) {\n collectSqlTextFragmentsFromExpression(\n property.initializer,\n ctx,\n fragments,\n visitedSymbols,\n );\n }\n }\n return;\n }\n\n if (ts.isConditionalExpression(unwrapped)) {\n collectSqlTextFragmentsFromExpression(\n unwrapped.whenTrue,\n ctx,\n fragments,\n visitedSymbols,\n );\n collectSqlTextFragmentsFromExpression(\n unwrapped.whenFalse,\n ctx,\n fragments,\n visitedSymbols,\n );\n return;\n }\n\n if (ts.isBinaryExpression(unwrapped)) {\n collectSqlTextFragmentsFromExpression(\n unwrapped.left,\n ctx,\n fragments,\n visitedSymbols,\n );\n collectSqlTextFragmentsFromExpression(\n unwrapped.right,\n ctx,\n fragments,\n visitedSymbols,\n );\n return;\n }\n\n if (ts.isIdentifier(unwrapped) || ts.isPropertyAccessExpression(unwrapped)) {\n const staticValue = resolveStaticString(unwrapped, ctx);\n if (staticValue !== undefined) {\n fragments.push(staticValue);\n return;\n }\n\n const symbol = resolveAliasedSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx.checker,\n );\n if (!symbol || visitedSymbols.has(symbol)) {\n return;\n }\n visitedSymbols.add(symbol);\n\n for (const declaration of symbol.declarations ?? []) {\n const initializer = resolveSymbolInitializerExpression(declaration);\n if (!initializer) {\n continue;\n }\n collectSqlTextFragmentsFromExpression(\n initializer,\n ctx,\n fragments,\n visitedSymbols,\n );\n }\n }\n}\n\nfunction inferResourcesFromSqlTemplate(\n template: ts.TemplateLiteral,\n ctx: AnalysisContext,\n): SqlResolvedResourceRef[] {\n const refs = new Map<string, SqlResolvedResourceRef>();\n const addFromText = (text: string) => {\n for (const ref of inferResourcesFromSqlText(text, ctx.registryIndex)) {\n refs.set(`${ref.kind}:${ref.id}`, ref);\n }\n };\n\n if (ts.isNoSubstitutionTemplateLiteral(template)) {\n addFromText(template.text);\n return [...refs.values()];\n }\n\n addFromText(template.head.text);\n for (const span of template.templateSpans) {\n addFromText(span.literal.text);\n }\n\n return [...refs.values()];\n}\n\nfunction inferResourcesFromSqlCallArguments(\n argumentsList: readonly ts.Expression[],\n ctx: AnalysisContext,\n): SqlResolvedResourceRef[] {\n const refs = new Map<string, SqlResolvedResourceRef>();\n const fragments: string[] = [];\n\n for (const arg of argumentsList) {\n collectSqlTextFragmentsFromExpression(arg, ctx, fragments);\n }\n\n for (const fragment of fragments) {\n for (const ref of inferResourcesFromSqlText(fragment, ctx.registryIndex)) {\n refs.set(`${ref.kind}:${ref.id}`, ref);\n }\n }\n\n return [...refs.values()];\n}\n\nfunction getObjectPropertyExpression(\n objectLiteral: ts.ObjectLiteralExpression,\n propertyName: string,\n): ts.Expression | undefined {\n for (const property of objectLiteral.properties) {\n if (ts.isPropertyAssignment(property)) {\n const name =\n ts.isIdentifier(property.name) ? property.name.text\n : ts.isStringLiteral(property.name) ? property.name.text\n : undefined;\n if (name === propertyName) {\n return property.initializer;\n }\n }\n if (\n ts.isShorthandPropertyAssignment(property) &&\n property.name.text === propertyName\n ) {\n return property.name;\n }\n }\n return undefined;\n}\n\nfunction resolveAliasedSymbol(\n symbol: ts.Symbol | undefined,\n checker: ts.TypeChecker,\n): ts.Symbol | undefined {\n if (!symbol) {\n return undefined;\n }\n if ((symbol.flags & ts.SymbolFlags.Alias) !== 0) {\n try {\n return checker.getAliasedSymbol(symbol);\n } catch {\n return symbol;\n }\n }\n return symbol;\n}\n\nfunction unwrapExpression(expression: ts.Expression): ts.Expression {\n let current = expression;\n type WrapperExpression =\n | ts.ParenthesizedExpression\n | ts.AsExpression\n | ts.TypeAssertion\n | ts.NonNullExpression;\n while (\n ts.isParenthesizedExpression(current) ||\n ts.isAsExpression(current) ||\n ts.isTypeAssertionExpression(current) ||\n ts.isNonNullExpression(current)\n ) {\n current = (current as WrapperExpression).expression;\n }\n return current;\n}\n\nfunction unwrapCallTargetExpression(expression: ts.Expression): ts.Expression {\n let current = unwrapExpression(expression);\n while (\n ts.isBinaryExpression(current) &&\n current.operatorToken.kind === ts.SyntaxKind.CommaToken\n ) {\n current = unwrapExpression(current.right);\n }\n return current;\n}\n\nfunction resolveStaticString(\n expression: ts.Expression | undefined,\n ctx: AnalysisContext,\n visitedSymbols = new Set<ts.Symbol>(),\n): string | undefined {\n if (!expression) {\n return undefined;\n }\n\n const unwrapped = unwrapExpression(expression);\n if (\n ts.isStringLiteral(unwrapped) ||\n ts.isNoSubstitutionTemplateLiteral(unwrapped)\n ) {\n return unwrapped.text;\n }\n\n if (ts.isIdentifier(unwrapped) || ts.isPropertyAccessExpression(unwrapped)) {\n const symbol = resolveAliasedSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx.checker,\n );\n if (!symbol || visitedSymbols.has(symbol)) {\n return undefined;\n }\n visitedSymbols.add(symbol);\n for (const declaration of symbol.declarations ?? []) {\n if (ts.isVariableDeclaration(declaration) && declaration.initializer) {\n const value = resolveStaticString(\n declaration.initializer,\n ctx,\n visitedSymbols,\n );\n if (value !== undefined) {\n return value;\n }\n } else if (ts.isPropertyAssignment(declaration)) {\n const value = resolveStaticString(\n declaration.initializer,\n ctx,\n visitedSymbols,\n );\n if (value !== undefined) {\n return value;\n }\n }\n }\n }\n\n return undefined;\n}\n\nfunction resolveObjectLiteralExpression(\n expression: ts.Expression | undefined,\n ctx: AnalysisContext,\n visitedSymbols = new Set<ts.Symbol>(),\n): ts.ObjectLiteralExpression | undefined {\n if (!expression) {\n return undefined;\n }\n\n const unwrapped = unwrapExpression(expression);\n if (ts.isObjectLiteralExpression(unwrapped)) {\n return unwrapped;\n }\n\n if (ts.isIdentifier(unwrapped) || ts.isPropertyAccessExpression(unwrapped)) {\n const symbol = resolveAliasedSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx.checker,\n );\n if (!symbol || visitedSymbols.has(symbol)) {\n return undefined;\n }\n visitedSymbols.add(symbol);\n for (const declaration of symbol.declarations ?? []) {\n if (ts.isVariableDeclaration(declaration) && declaration.initializer) {\n const result = resolveObjectLiteralExpression(\n declaration.initializer,\n ctx,\n visitedSymbols,\n );\n if (result) {\n return result;\n }\n } else if (ts.isPropertyAssignment(declaration)) {\n const result = resolveObjectLiteralExpression(\n declaration.initializer,\n ctx,\n visitedSymbols,\n );\n if (result) {\n return result;\n }\n }\n }\n }\n\n return undefined;\n}\n\nfunction constructorNameFromNewExpression(\n expression: ts.Expression,\n checker: ts.TypeChecker,\n): string | undefined {\n const symbol = resolveAliasedSymbol(\n checker.getSymbolAtLocation(expression),\n checker,\n );\n if (symbol?.name) {\n return symbol.name;\n }\n if (ts.isIdentifier(expression)) {\n return expression.text;\n }\n if (ts.isPropertyAccessExpression(expression)) {\n return expression.name.text;\n }\n return undefined;\n}\n\nfunction parseOlapTableRef(\n newExpression: ts.NewExpression,\n ctx: AnalysisContext,\n): ResourceRef | undefined {\n const tableName = resolveStaticString(newExpression.arguments?.[0], ctx);\n if (!tableName) {\n return undefined;\n }\n const configLiteral = resolveObjectLiteralExpression(\n newExpression.arguments?.[1],\n ctx,\n );\n const version =\n configLiteral ?\n resolveStaticString(\n getObjectPropertyExpression(configLiteral, \"version\"),\n ctx,\n )\n : undefined;\n\n return {\n kind: \"Table\",\n id: resolveTableId(tableName, version, ctx.registryIndex),\n };\n}\n\nfunction parseStreamRef(\n newExpression: ts.NewExpression,\n ctx: AnalysisContext,\n): ResourceRef | undefined {\n const topicName = resolveStaticString(newExpression.arguments?.[0], ctx);\n if (!topicName) {\n return undefined;\n }\n return { kind: \"Topic\", id: resolveTopicId(topicName, ctx.registryIndex) };\n}\n\nfunction parseSimpleNamedRef(\n newExpression: ts.NewExpression,\n kind: \"View\" | \"SqlResource\",\n ctx: AnalysisContext,\n): ResourceRef | undefined {\n const name = resolveStaticString(newExpression.arguments?.[0], ctx);\n if (!name) {\n return undefined;\n }\n return { kind, id: name };\n}\n\nfunction parseMaterializedViewRef(\n newExpression: ts.NewExpression,\n ctx: AnalysisContext,\n): ResourceRef | undefined {\n const options = resolveObjectLiteralExpression(\n newExpression.arguments?.[0],\n ctx,\n );\n if (!options) {\n return undefined;\n }\n\n const mvName = resolveStaticString(\n getObjectPropertyExpression(options, \"materializedViewName\"),\n ctx,\n );\n if (!mvName) {\n return undefined;\n }\n\n let targetTableId: string | undefined;\n const targetTableExpression = getObjectPropertyExpression(\n options,\n \"targetTable\",\n );\n const targetTableObject = resolveObjectLiteralExpression(\n targetTableExpression,\n ctx,\n );\n if (targetTableObject) {\n const tableName = resolveStaticString(\n getObjectPropertyExpression(targetTableObject, \"name\"),\n ctx,\n );\n const version = resolveStaticString(\n getObjectPropertyExpression(targetTableObject, \"version\"),\n ctx,\n );\n if (tableName) {\n targetTableId = resolveTableId(tableName, version, ctx.registryIndex);\n }\n } else if (targetTableExpression) {\n const targetRef = resolveResourceFromExpression(\n targetTableExpression,\n ctx,\n new Map(),\n );\n if (targetRef?.kind === \"Table\") {\n targetTableId = targetRef.id;\n }\n }\n\n if (!targetTableId) {\n const legacyTableName = resolveStaticString(\n getObjectPropertyExpression(options, \"tableName\"),\n ctx,\n );\n if (legacyTableName) {\n targetTableId = resolveTableId(\n legacyTableName,\n undefined,\n ctx.registryIndex,\n );\n }\n }\n\n return {\n kind: \"MaterializedView\",\n id: mvName,\n targetTableId,\n };\n}\n\nfunction parseIngestPipelineRef(\n newExpression: ts.NewExpression,\n ctx: AnalysisContext,\n): ResourceRef | undefined {\n const pipelineName = resolveStaticString(newExpression.arguments?.[0], ctx);\n if (!pipelineName) {\n return undefined;\n }\n\n const config = resolveObjectLiteralExpression(\n newExpression.arguments?.[1],\n ctx,\n );\n let version: string | undefined;\n let streamEnabled = true;\n let tableEnabled = true;\n if (config) {\n const versionExpr = getObjectPropertyExpression(config, \"version\");\n version = resolveStaticString(versionExpr, ctx);\n\n const streamExpr = getObjectPropertyExpression(config, \"stream\");\n if (streamExpr && streamExpr.kind === ts.SyntaxKind.FalseKeyword) {\n streamEnabled = false;\n }\n\n const tableExpr = getObjectPropertyExpression(config, \"table\");\n if (tableExpr && tableExpr.kind === ts.SyntaxKind.FalseKeyword) {\n tableEnabled = false;\n }\n }\n\n return {\n kind: \"IngestPipeline\",\n streamId:\n streamEnabled ?\n resolveTopicId(pipelineName, ctx.registryIndex)\n : undefined,\n tableId:\n tableEnabled ?\n resolveTableId(pipelineName, version, ctx.registryIndex)\n : undefined,\n };\n}\n\nfunction resolveResourceFromNewExpression(\n newExpression: ts.NewExpression,\n ctx: AnalysisContext,\n): ResourceRef | undefined {\n const constructorName = constructorNameFromNewExpression(\n newExpression.expression,\n ctx.checker,\n );\n\n switch (constructorName) {\n case \"OlapTable\":\n return parseOlapTableRef(newExpression, ctx);\n case \"Stream\":\n return parseStreamRef(newExpression, ctx);\n case \"View\":\n return parseSimpleNamedRef(newExpression, \"View\", ctx);\n case \"SqlResource\":\n return parseSimpleNamedRef(newExpression, \"SqlResource\", ctx);\n case \"MaterializedView\":\n return parseMaterializedViewRef(newExpression, ctx);\n case \"IngestPipeline\":\n return parseIngestPipelineRef(newExpression, ctx);\n default:\n return undefined;\n }\n}\n\nfunction resolveResourceFromSymbol(\n symbol: ts.Symbol | undefined,\n ctx: AnalysisContext,\n bindings: Map<ts.Symbol, ResourceRef>,\n): ResourceRef | undefined {\n const resolvedSymbol = resolveAliasedSymbol(symbol, ctx.checker);\n if (!resolvedSymbol) {\n return undefined;\n }\n\n const bound = bindings.get(resolvedSymbol);\n if (bound) {\n return bound;\n }\n\n const cached = ctx.symbolResourceCache.get(resolvedSymbol);\n if (cached !== undefined) {\n return cached ?? undefined;\n }\n\n ctx.symbolResourceCache.set(resolvedSymbol, null);\n\n for (const declaration of resolvedSymbol.declarations ?? []) {\n const initializer = resolveSymbolInitializerExpression(declaration);\n if (!initializer) {\n continue;\n }\n\n const resource = resolveResourceFromExpression(initializer, ctx, bindings);\n if (resource) {\n ctx.symbolResourceCache.set(resolvedSymbol, resource);\n return resource;\n }\n }\n\n ctx.symbolResourceCache.set(resolvedSymbol, null);\n return undefined;\n}\n\nfunction resolveSymbolInitializerExpression(\n declaration: ts.Declaration,\n): ts.Expression | undefined {\n if (ts.isVariableDeclaration(declaration) && declaration.initializer) {\n return declaration.initializer;\n }\n\n if (ts.isPropertyAssignment(declaration)) {\n return declaration.initializer;\n }\n\n if (\n (ts.isPropertyAccessExpression(declaration) ||\n ts.isElementAccessExpression(declaration) ||\n ts.isIdentifier(declaration)) &&\n ts.isBinaryExpression(declaration.parent) &&\n declaration.parent.left === declaration &&\n declaration.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken\n ) {\n return declaration.parent.right;\n }\n\n return undefined;\n}\n\nfunction isColumnsProjection(expression: ts.Expression): boolean {\n const unwrapped = unwrapExpression(expression);\n if (ts.isPropertyAccessExpression(unwrapped)) {\n return unwrapped.name.text === \"columns\";\n }\n if (ts.isElementAccessExpression(unwrapped)) {\n return isColumnsProjection(unwrapped.expression);\n }\n return false;\n}\n\nfunction resolveResourceFromExpression(\n expression: ts.Expression,\n ctx: AnalysisContext,\n bindings: Map<ts.Symbol, ResourceRef>,\n): ResourceRef | undefined {\n const unwrapped = unwrapExpression(expression);\n\n if (ts.isNewExpression(unwrapped)) {\n return resolveResourceFromNewExpression(unwrapped, ctx);\n }\n\n if (\n ts.isTaggedTemplateExpression(unwrapped) &&\n isSqlTag(unwrapped.tag, ctx.checker)\n ) {\n const inferred = inferResourcesFromSqlTemplate(unwrapped.template, ctx);\n if (inferred.length === 1) {\n return inferred[0];\n }\n }\n\n if (ts.isCallExpression(unwrapped)) {\n const calleeExpression = unwrapCallTargetExpression(unwrapped.expression);\n if (isSqlTag(calleeExpression as ts.LeftHandSideExpression, ctx.checker)) {\n const inferred = inferResourcesFromSqlCallArguments(\n unwrapped.arguments,\n ctx,\n );\n if (inferred.length === 1) {\n return inferred[0];\n }\n }\n }\n\n if (ts.isIdentifier(unwrapped)) {\n return resolveResourceFromSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx,\n bindings,\n );\n }\n\n if (ts.isPropertyAccessExpression(unwrapped)) {\n const base = resolveResourceFromExpression(\n unwrapped.expression,\n ctx,\n bindings,\n );\n if (\n base?.kind === \"MaterializedView\" &&\n unwrapped.name.text === \"targetTable\"\n ) {\n if (base.targetTableId) {\n return { kind: \"Table\", id: base.targetTableId };\n }\n }\n if (base?.kind === \"IngestPipeline\") {\n if (unwrapped.name.text === \"stream\" && base.streamId) {\n return { kind: \"Topic\", id: base.streamId };\n }\n if (unwrapped.name.text === \"table\" && base.tableId) {\n return { kind: \"Table\", id: base.tableId };\n }\n }\n if (base?.kind === \"Table\" && unwrapped.name.text === \"columns\") {\n return base;\n }\n if (base?.kind === \"Table\" && isColumnsProjection(unwrapped.expression)) {\n return base;\n }\n\n return resolveResourceFromSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx,\n bindings,\n );\n }\n\n if (ts.isElementAccessExpression(unwrapped)) {\n const base = resolveResourceFromExpression(\n unwrapped.expression,\n ctx,\n bindings,\n );\n if (base) {\n return base;\n }\n return resolveResourceFromSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx,\n bindings,\n );\n }\n\n return undefined;\n}\n\nfunction toSignature(\n ref: ResourceRef,\n): InfrastructureSignatureJson | undefined {\n switch (ref.kind) {\n case \"Table\":\n return { kind: \"Table\", id: ref.id };\n case \"Topic\":\n return { kind: \"Topic\", id: ref.id };\n case \"View\":\n return { kind: \"View\", id: ref.id };\n case \"SqlResource\":\n return { kind: \"SqlResource\", id: ref.id };\n case \"MaterializedView\":\n if (ref.targetTableId) {\n return { kind: \"Table\", id: ref.targetTableId };\n }\n return { kind: \"MaterializedView\", id: ref.id };\n case \"IngestPipeline\":\n // Intentional: IngestPipeline is not a standalone infra node. It must be\n // decomposed through `.stream` or `.table` property access first.\n return undefined;\n default:\n return undefined;\n }\n}\n\nfunction getFunctionLikeDeclarations(\n symbol: ts.Symbol | undefined,\n ctx: AnalysisContext,\n): ts.FunctionLikeDeclaration[] {\n const resolvedSymbol = resolveAliasedSymbol(symbol, ctx.checker);\n if (!resolvedSymbol) {\n return [];\n }\n\n const cached = ctx.functionCache.get(resolvedSymbol);\n if (cached) {\n return cached;\n }\n\n const declarations: ts.FunctionLikeDeclaration[] = [];\n for (const declaration of resolvedSymbol.declarations ?? []) {\n if (\n ts.isFunctionDeclaration(declaration) ||\n ts.isMethodDeclaration(declaration) ||\n ts.isGetAccessorDeclaration(declaration) ||\n ts.isSetAccessorDeclaration(declaration)\n ) {\n declarations.push(declaration);\n continue;\n }\n if (ts.isVariableDeclaration(declaration) && declaration.initializer) {\n const initializer = unwrapExpression(declaration.initializer);\n if (\n ts.isArrowFunction(initializer) ||\n ts.isFunctionExpression(initializer)\n ) {\n declarations.push(initializer);\n continue;\n }\n if (\n ts.isIdentifier(initializer) ||\n ts.isPropertyAccessExpression(initializer)\n ) {\n declarations.push(\n ...getFunctionLikeDeclarations(\n ctx.checker.getSymbolAtLocation(initializer),\n ctx,\n ),\n );\n }\n }\n if (ts.isPropertyAssignment(declaration)) {\n const initializer = unwrapExpression(declaration.initializer);\n if (\n ts.isArrowFunction(initializer) ||\n ts.isFunctionExpression(initializer)\n ) {\n declarations.push(initializer);\n }\n }\n }\n\n ctx.functionCache.set(resolvedSymbol, declarations);\n return declarations;\n}\n\nfunction isSqlTag(\n tag: ts.LeftHandSideExpression,\n checker: ts.TypeChecker,\n): boolean {\n const unwrapped = unwrapExpression(tag);\n if (ts.isIdentifier(unwrapped) && unwrapped.text === \"sql\") {\n return true;\n }\n if (\n ts.isPropertyAccessExpression(unwrapped) &&\n unwrapped.name.text === \"sql\"\n ) {\n return true;\n }\n const symbol = resolveAliasedSymbol(\n checker.getSymbolAtLocation(unwrapped),\n checker,\n );\n return symbol?.name === \"sql\";\n}\n\nfunction isUserCodeFunction(fn: ts.FunctionLikeDeclaration): boolean {\n const fileName = path\n .resolve(fn.getSourceFile().fileName)\n .replace(/\\\\/g, \"/\");\n const cwd = path.resolve(process.cwd()).replace(/\\\\/g, \"/\");\n return (\n (fileName === cwd || fileName.startsWith(`${cwd}/`)) &&\n !fileName.includes(\"/node_modules/\")\n );\n}\n\nfunction functionIdentity(fn: ts.FunctionLikeDeclaration): string {\n const source = fn.getSourceFile().fileName;\n return `${source}:${fn.pos}`;\n}\n\nfunction bindingIdentityForFunction(\n fn: ts.FunctionLikeDeclaration,\n bindings: Map<ts.Symbol, ResourceRef>,\n checker: ts.TypeChecker,\n): string {\n const parts: string[] = [];\n for (const parameter of fn.parameters) {\n const symbol = resolveAliasedSymbol(\n checker.getSymbolAtLocation(parameter.name),\n checker,\n );\n if (!symbol) {\n continue;\n }\n const bound = bindings.get(symbol);\n if (!bound) {\n continue;\n }\n if (bound.kind === \"IngestPipeline\") {\n continue;\n }\n const signature = toSignature(bound);\n if (!signature) {\n continue;\n }\n parts.push(`${symbol.name}:${signature.kind}:${signature.id}`);\n }\n parts.sort();\n return parts.join(\"|\");\n}\n\nconst API_HELPER_IDENTIFIERS = new Set([\"ApiHelpers\", \"ConsumptionHelpers\"]);\n\nfunction isApiHelperObjectExpression(\n expression: ts.Expression,\n ctx: AnalysisContext,\n visitedSymbols = new Set<ts.Symbol>(),\n): boolean {\n const unwrapped = unwrapExpression(expression);\n\n if (ts.isIdentifier(unwrapped)) {\n if (API_HELPER_IDENTIFIERS.has(unwrapped.text)) {\n return true;\n }\n\n const symbol = resolveAliasedSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx.checker,\n );\n if (!symbol || visitedSymbols.has(symbol)) {\n return false;\n }\n visitedSymbols.add(symbol);\n\n if (API_HELPER_IDENTIFIERS.has(symbol.name)) {\n return true;\n }\n\n for (const declaration of symbol.declarations ?? []) {\n if (ts.isImportSpecifier(declaration)) {\n const importedName =\n declaration.propertyName?.text ?? declaration.name.text;\n if (API_HELPER_IDENTIFIERS.has(importedName)) {\n return true;\n }\n }\n\n if (ts.isVariableDeclaration(declaration) && declaration.initializer) {\n if (\n isApiHelperObjectExpression(\n declaration.initializer,\n ctx,\n visitedSymbols,\n )\n ) {\n return true;\n }\n }\n\n if (ts.isPropertyAssignment(declaration)) {\n if (\n isApiHelperObjectExpression(\n declaration.initializer,\n ctx,\n visitedSymbols,\n )\n ) {\n return true;\n }\n }\n }\n\n return false;\n }\n\n if (ts.isPropertyAccessExpression(unwrapped)) {\n if (API_HELPER_IDENTIFIERS.has(unwrapped.name.text)) {\n return true;\n }\n return isApiHelperObjectExpression(\n unwrapped.expression,\n ctx,\n visitedSymbols,\n );\n }\n\n if (ts.isElementAccessExpression(unwrapped)) {\n return isApiHelperObjectExpression(\n unwrapped.expression,\n ctx,\n visitedSymbols,\n );\n }\n\n return false;\n}\n\nfunction analyzeFunctionGraph(\n roots: ts.FunctionLikeDeclaration[],\n ctx: AnalysisContext,\n): DependencySignatures {\n const pulls = new Map<string, InfrastructureSignatureJson>();\n const pushes = new Map<string, InfrastructureSignatureJson>();\n const visited = new Set<string>();\n\n const addPull = (signature: InfrastructureSignatureJson | undefined) => {\n if (!signature) {\n return;\n }\n pulls.set(`${signature.kind}:${signature.id}`, signature);\n };\n\n const addPush = (signature: InfrastructureSignatureJson | undefined) => {\n if (!signature) {\n return;\n }\n pushes.set(`${signature.kind}:${signature.id}`, signature);\n };\n\n const visitFunction = (\n fn: ts.FunctionLikeDeclaration,\n bindings: Map<ts.Symbol, ResourceRef>,\n ) => {\n const key = `${functionIdentity(fn)}|${bindingIdentityForFunction(fn, bindings, ctx.checker)}`;\n if (visited.has(key)) {\n return;\n }\n visited.add(key);\n\n const visitNode = (node: ts.Node) => {\n if (\n ts.isTaggedTemplateExpression(node) &&\n isSqlTag(node.tag, ctx.checker)\n ) {\n for (const inferred of inferResourcesFromSqlTemplate(\n node.template,\n ctx,\n )) {\n addPull(toSignature(inferred));\n }\n\n const template = node.template;\n if (ts.isTemplateExpression(template)) {\n for (const span of template.templateSpans) {\n const ref = resolveResourceFromExpression(\n span.expression,\n ctx,\n bindings,\n );\n const signature = ref ? toSignature(ref) : undefined;\n addPull(signature);\n }\n }\n }\n\n if (ts.isCallExpression(node)) {\n const calleeExpression = unwrapCallTargetExpression(node.expression);\n\n if (\n isSqlTag(calleeExpression as ts.LeftHandSideExpression, ctx.checker)\n ) {\n for (const inferred of inferResourcesFromSqlCallArguments(\n node.arguments,\n ctx,\n )) {\n addPull(toSignature(inferred));\n }\n\n for (const arg of node.arguments) {\n const ref = resolveResourceFromExpression(arg, ctx, bindings);\n const signature = ref ? toSignature(ref) : undefined;\n addPull(signature);\n }\n }\n\n if (ts.isPropertyAccessExpression(calleeExpression)) {\n const methodName = calleeExpression.name.text;\n\n if (\n methodName === \"table\" &&\n isApiHelperObjectExpression(calleeExpression.expression, ctx)\n ) {\n const tableName = resolveStaticString(node.arguments?.[0], ctx);\n if (tableName) {\n addPull({\n kind: \"Table\",\n id: resolveTableId(tableName, undefined, ctx.registryIndex),\n });\n }\n }\n\n const ref = resolveResourceFromExpression(\n calleeExpression.expression,\n ctx,\n bindings,\n );\n if (ref) {\n const signature = toSignature(ref);\n if (signature) {\n if (WRITE_METHODS.has(methodName)) {\n addPush(signature);\n } else {\n // Heuristic: any non-write resource method call counts as a read.\n // This may classify utility calls like `toString`/`valueOf` as pulls,\n // but it keeps lineage inference conservative for current SDK usage.\n addPull(signature);\n }\n }\n }\n }\n\n const calleeSymbol = ctx.checker.getSymbolAtLocation(calleeExpression);\n const callees = getFunctionLikeDeclarations(calleeSymbol, ctx).filter(\n isUserCodeFunction,\n );\n for (const callee of callees) {\n const nextBindings = new Map<ts.Symbol, ResourceRef>(bindings);\n for (let i = 0; i < callee.parameters.length; i++) {\n const param = callee.parameters[i];\n if (!node.arguments || i >= node.arguments.length) {\n continue;\n }\n const paramSymbol = resolveAliasedSymbol(\n ctx.checker.getSymbolAtLocation(param.name),\n ctx.checker,\n );\n if (!paramSymbol) {\n continue;\n }\n const argRef = resolveResourceFromExpression(\n node.arguments[i],\n ctx,\n bindings,\n );\n if (argRef) {\n nextBindings.set(paramSymbol, argRef);\n }\n }\n visitFunction(callee, nextBindings);\n }\n }\n\n ts.forEachChild(node, visitNode);\n };\n\n if (fn.body) {\n visitNode(fn.body);\n }\n };\n\n for (const root of roots) {\n if (!isUserCodeFunction(root)) {\n continue;\n }\n visitFunction(root, new Map<ts.Symbol, ResourceRef>());\n }\n\n return {\n pullsDataFrom: [...pulls.values()],\n pushesDataTo: [...pushes.values()],\n };\n}\n\nfunction resolveFunctionNodesFromExpression(\n expression: ts.Expression | undefined,\n ctx: AnalysisContext,\n): ts.FunctionLikeDeclaration[] {\n if (!expression) {\n return [];\n }\n const unwrapped = unwrapExpression(expression);\n if (ts.isArrowFunction(unwrapped) || ts.isFunctionExpression(unwrapped)) {\n return [unwrapped];\n }\n if (ts.isIdentifier(unwrapped) || ts.isPropertyAccessExpression(unwrapped)) {\n return getFunctionLikeDeclarations(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx,\n );\n }\n return [];\n}\n\nfunction resolveTaskExpression(\n expression: ts.Expression | undefined,\n ctx: AnalysisContext,\n): ts.NewExpression | undefined {\n if (!expression) {\n return undefined;\n }\n\n const unwrapped = unwrapExpression(expression);\n const cacheKey = `${unwrapped.getSourceFile().fileName}:${unwrapped.pos}`;\n const cached = ctx.taskExpressionCache.get(cacheKey);\n if (cached !== undefined) {\n return cached ?? undefined;\n }\n\n ctx.taskExpressionCache.set(cacheKey, null);\n\n if (ts.isNewExpression(unwrapped)) {\n const ctor = constructorNameFromNewExpression(\n unwrapped.expression,\n ctx.checker,\n );\n if (ctor === \"Task\") {\n ctx.taskExpressionCache.set(cacheKey, unwrapped);\n return unwrapped;\n }\n }\n\n if (ts.isIdentifier(unwrapped) || ts.isPropertyAccessExpression(unwrapped)) {\n const symbol = resolveAliasedSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx.checker,\n );\n for (const declaration of symbol?.declarations ?? []) {\n if (ts.isVariableDeclaration(declaration) && declaration.initializer) {\n const resolved = resolveTaskExpression(declaration.initializer, ctx);\n if (resolved) {\n ctx.taskExpressionCache.set(cacheKey, resolved);\n return resolved;\n }\n }\n }\n }\n\n return undefined;\n}\n\nfunction resolveArrayLiteralExpression(\n expression: ts.Expression | undefined,\n ctx: AnalysisContext,\n visitedSymbols = new Set<ts.Symbol>(),\n): ts.ArrayLiteralExpression | undefined {\n if (!expression) {\n return undefined;\n }\n\n const unwrapped = unwrapExpression(expression);\n if (ts.isArrayLiteralExpression(unwrapped)) {\n return unwrapped;\n }\n\n if (ts.isIdentifier(unwrapped) || ts.isPropertyAccessExpression(unwrapped)) {\n const symbol = resolveAliasedSymbol(\n ctx.checker.getSymbolAtLocation(unwrapped),\n ctx.checker,\n );\n if (!symbol || visitedSymbols.has(symbol)) {\n return undefined;\n }\n visitedSymbols.add(symbol);\n\n for (const declaration of symbol.declarations ?? []) {\n const initializer = resolveSymbolInitializerExpression(declaration);\n if (!initializer) {\n continue;\n }\n const resolved = resolveArrayLiteralExpression(\n initializer,\n ctx,\n visitedSymbols,\n );\n if (resolved) {\n return resolved;\n }\n }\n }\n\n return undefined;\n}\n\nfunction collectTaskFunctionsFromOnCompleteElement(\n element: ts.Expression,\n ctx: AnalysisContext,\n visitedTasks: Set<string>,\n): ts.FunctionLikeDeclaration[] {\n if (ts.isSpreadElement(element)) {\n const spreadArray = resolveArrayLiteralExpression(element.expression, ctx);\n if (spreadArray) {\n return spreadArray.elements.flatMap((nestedElement) =>\n collectTaskFunctionsFromOnCompleteElement(\n nestedElement,\n ctx,\n visitedTasks,\n ),\n );\n }\n\n return collectTaskFunctions(element.expression, ctx, visitedTasks);\n }\n\n return collectTaskFunctions(element, ctx, visitedTasks);\n}\n\nfunction collectTaskFunctions(\n taskExpression: ts.Expression | undefined,\n ctx: AnalysisContext,\n visitedTasks: Set<string>,\n): ts.FunctionLikeDeclaration[] {\n const task = resolveTaskExpression(taskExpression, ctx);\n if (!task) {\n return [];\n }\n\n const taskKey = `${task.getSourceFile().fileName}:${task.pos}`;\n if (visitedTasks.has(taskKey)) {\n return [];\n }\n visitedTasks.add(taskKey);\n\n const configExpression = task.arguments?.[1];\n const configObject = resolveObjectLiteralExpression(configExpression, ctx);\n if (!configObject) {\n return [];\n }\n\n const functions: ts.FunctionLikeDeclaration[] = [];\n functions.push(\n ...resolveFunctionNodesFromExpression(\n getObjectPropertyExpression(configObject, \"run\"),\n ctx,\n ),\n );\n functions.push(\n ...resolveFunctionNodesFromExpression(\n getObjectPropertyExpression(configObject, \"onCancel\"),\n ctx,\n ),\n );\n\n const onComplete = getObjectPropertyExpression(configObject, \"onComplete\");\n if (onComplete) {\n const arrayLiteral = unwrapExpression(onComplete);\n if (ts.isArrayLiteralExpression(arrayLiteral)) {\n for (const element of arrayLiteral.elements) {\n functions.push(\n ...collectTaskFunctionsFromOnCompleteElement(\n element,\n ctx,\n visitedTasks,\n ),\n );\n }\n }\n }\n\n return functions;\n}\n\nfunction collectAnalysisFiles(registry: RegistryLike): string[] {\n const files = new Set<string>();\n let requiresFallbackScan = false;\n\n const uniqueApis = new Set<any>();\n for (const api of registry.apis.values()) {\n uniqueApis.add(api);\n }\n for (const api of uniqueApis) {\n const sourceFile = api?.metadata?.source?.file;\n if (\n typeof sourceFile === \"string\" &&\n fs.existsSync(sourceFile) &&\n isSourceFilePath(sourceFile)\n ) {\n files.add(path.resolve(sourceFile));\n } else {\n requiresFallbackScan = true;\n }\n }\n\n registry.workflows.forEach((workflow: any) => {\n const sourceFile = workflow?.sourceFile;\n if (\n typeof sourceFile === \"string\" &&\n fs.existsSync(sourceFile) &&\n isSourceFilePath(sourceFile)\n ) {\n files.add(path.resolve(sourceFile));\n } else {\n requiresFallbackScan = true;\n }\n });\n\n registry.webApps.forEach((webApp: any) => {\n const sourceFile = webApp?.sourceFile;\n if (\n typeof sourceFile === \"string\" &&\n fs.existsSync(sourceFile) &&\n isSourceFilePath(sourceFile)\n ) {\n files.add(path.resolve(sourceFile));\n } else {\n requiresFallbackScan = true;\n }\n });\n\n if (files.size === 0 || requiresFallbackScan) {\n const appDir = path.resolve(process.cwd(), getSourceDir());\n for (const file of findSourceFiles(appDir, (directory, error) => {\n compilerLog(`Warning: Could not read directory ${directory}: ${error}`);\n })) {\n files.add(file);\n }\n }\n\n return [...files];\n}\n\nfunction loadCompilerOptions(rootNames: string[]): {\n rootNames: string[];\n options: ts.CompilerOptions;\n} {\n const fallback: ts.CompilerOptions = {\n allowJs: true,\n target: ts.ScriptTarget.ES2020,\n module: ts.ModuleKind.NodeNext,\n moduleResolution: ts.ModuleResolutionKind.NodeNext,\n jsx: ts.JsxEmit.Preserve,\n skipLibCheck: true,\n };\n\n const configPath = ts.findConfigFile(\n process.cwd(),\n ts.sys.fileExists,\n \"tsconfig.json\",\n );\n if (!configPath) {\n return { rootNames, options: fallback };\n }\n\n const configFile = ts.readConfigFile(configPath, ts.sys.readFile);\n if (configFile.error) {\n return { rootNames, options: fallback };\n }\n\n const parsed = ts.parseJsonConfigFileContent(\n configFile.config,\n ts.sys,\n path.dirname(configPath),\n );\n const normalizedRoots = [\n ...new Set(rootNames.map((file) => path.resolve(file))),\n ];\n\n return {\n rootNames: normalizedRoots,\n options: { ...fallback, ...parsed.options },\n };\n}\n\nfunction createNameResolutionContext(checker: ts.TypeChecker): AnalysisContext {\n return {\n checker,\n registryIndex: {\n tableIdsByName: new Map(),\n topicIdsByName: new Map(),\n tableIds: new Set(),\n topicIds: new Set(),\n warnedAmbiguousTableIds: new Set(),\n },\n symbolResourceCache: new Map(),\n taskExpressionCache: new Map(),\n functionCache: new Map(),\n };\n}\n\nfunction collectLineageRootEntries(\n program: ts.Program,\n checker: ts.TypeChecker,\n): {\n apiEntries: Map<string, ts.Expression>;\n workflowEntries: Map<string, ts.Expression>;\n webAppEntries: Map<string, ts.Expression>;\n} {\n const apiEntries = new Map<string, ts.Expression>();\n const workflowEntries = new Map<string, ts.Expression>();\n const webAppEntries = new Map<string, ts.Expression>();\n const tempCtx = createNameResolutionContext(checker);\n\n const setIfNew = (\n entries: Map<string, ts.Expression>,\n key: string | undefined,\n expression: ts.Expression,\n ) => {\n if (!key || entries.has(key)) {\n return;\n }\n entries.set(key, expression);\n };\n\n const visit = (node: ts.Node) => {\n if (\n ts.isNewExpression(node) &&\n node.arguments &&\n node.arguments.length >= 2\n ) {\n const ctor = constructorNameFromNewExpression(node.expression, checker);\n if (ctor === \"Api\" || ctor === \"ConsumptionApi\") {\n const name = resolveStaticString(node.arguments[0], tempCtx);\n const config = resolveObjectLiteralExpression(\n node.arguments[2],\n tempCtx,\n );\n const version =\n config ?\n resolveStaticString(\n getObjectPropertyExpression(config, \"version\"),\n tempCtx,\n )\n : undefined;\n const key = apiKey(name, version);\n setIfNew(apiEntries, key, node.arguments[1]);\n } else if (ctor === \"Workflow\") {\n const key = resolveStaticString(node.arguments[0], tempCtx);\n setIfNew(workflowEntries, key, node.arguments[1]);\n } else if (ctor === \"WebApp\") {\n const key = resolveStaticString(node.arguments[0], tempCtx);\n setIfNew(webAppEntries, key, node.arguments[1]);\n }\n }\n ts.forEachChild(node, visit);\n };\n\n for (const sourceFile of program.getSourceFiles()) {\n if (sourceFile.fileName.includes(\"/node_modules/\")) {\n continue;\n }\n visit(sourceFile);\n }\n\n return { apiEntries, workflowEntries, webAppEntries };\n}\n\nfunction apiKey(\n name: string | undefined,\n version?: string,\n): string | undefined {\n if (!name) {\n return undefined;\n }\n\n return version ? `${name}:${version}` : name;\n}\n\nfunction resolveWebAppRootFunctions(\n expression: ts.Expression | undefined,\n ctx: AnalysisContext,\n): ts.FunctionLikeDeclaration[] {\n const roots = resolveFunctionNodesFromExpression(expression, ctx);\n const objectLiteral = resolveObjectLiteralExpression(expression, ctx);\n if (!objectLiteral) {\n return roots;\n }\n\n const deduped = new Map<string, ts.FunctionLikeDeclaration>();\n for (const root of roots) {\n deduped.set(functionIdentity(root), root);\n }\n\n for (const propertyName of [\"handle\", \"callback\", \"routing\"]) {\n const propertyExpression = getObjectPropertyExpression(\n objectLiteral,\n propertyName,\n );\n const propertyFunctions = resolveFunctionNodesFromExpression(\n propertyExpression,\n ctx,\n );\n for (const fn of propertyFunctions) {\n deduped.set(functionIdentity(fn), fn);\n }\n }\n\n return [...deduped.values()];\n}\n\nexport function analyzeRegistryLineage(\n registry: RegistryLike,\n): DependencyAnalysisResult {\n if (\n registry.apis.size === 0 &&\n registry.workflows.size === 0 &&\n registry.webApps.size === 0\n ) {\n return createEmptyResult();\n }\n\n try {\n const files = collectAnalysisFiles(registry);\n if (files.length === 0) {\n return createEmptyResult();\n }\n\n const { rootNames, options } = loadCompilerOptions(files);\n const program = ts.createProgram({\n rootNames,\n options,\n });\n const checker = program.getTypeChecker();\n\n const ctx: AnalysisContext = {\n checker,\n registryIndex: buildRegistryIndex(registry),\n symbolResourceCache: new Map<ts.Symbol, ResourceRef | null>(),\n taskExpressionCache: new Map<string, ts.NewExpression | null>(),\n functionCache: new Map<ts.Symbol, ts.FunctionLikeDeclaration[]>(),\n };\n\n const { apiEntries, workflowEntries, webAppEntries } =\n collectLineageRootEntries(program, checker);\n\n const apiByKey = new Map<string, DependencySignatures>();\n const seenApiKeys = new Set<string>();\n registry.apis.forEach((api: any) => {\n const key = apiKey(api?.name, api?.config?.version);\n if (!key || seenApiKeys.has(key)) {\n return;\n }\n seenApiKeys.add(key);\n\n const handlerExpression = apiEntries.get(key);\n if (!handlerExpression) {\n apiByKey.set(key, { pullsDataFrom: [], pushesDataTo: [] });\n return;\n }\n\n const roots = resolveFunctionNodesFromExpression(handlerExpression, ctx);\n apiByKey.set(key, analyzeFunctionGraph(roots, ctx));\n });\n\n const workflowByName = new Map<string, DependencySignatures>();\n registry.workflows.forEach((workflow: any, workflowName: string) => {\n const configExpression = workflowEntries.get(workflowName);\n if (!configExpression) {\n workflowByName.set(workflowName, {\n pullsDataFrom: [],\n pushesDataTo: [],\n });\n return;\n }\n const configObject = resolveObjectLiteralExpression(\n configExpression,\n ctx,\n );\n const startingTaskExpression =\n configObject ?\n getObjectPropertyExpression(configObject, \"startingTask\")\n : undefined;\n const roots = collectTaskFunctions(\n startingTaskExpression,\n ctx,\n new Set<string>(),\n );\n workflowByName.set(workflowName, analyzeFunctionGraph(roots, ctx));\n });\n\n const webAppByName = new Map<string, DependencySignatures>();\n registry.webApps.forEach((webApp: any, webAppName: string) => {\n const handlerExpression = webAppEntries.get(webAppName);\n if (!handlerExpression) {\n webAppByName.set(webAppName, { pullsDataFrom: [], pushesDataTo: [] });\n return;\n }\n\n const roots = resolveWebAppRootFunctions(handlerExpression, ctx);\n webAppByName.set(webAppName, analyzeFunctionGraph(roots, ctx));\n });\n\n return { apiByKey, workflowByName, webAppByName };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n compilerLog(\n `Warning: lineage analysis failed; returning empty lineage results. ${message}`,\n );\n return createEmptyResult();\n }\n}\n","/**\n * @module internal\n * Internal implementation details for the Moose v2 data model (dmv2).\n *\n * This module manages the registration of user-defined dmv2 resources (Tables, Streams, APIs, etc.)\n * and provides functions to serialize these resources into a JSON format (`InfrastructureMap`)\n * expected by the Moose infrastructure management system. It also includes helper functions\n * to retrieve registered handler functions (for streams and APIs) and the base class\n * (`TypedBase`) used by dmv2 resource classes.\n *\n * @internal This module is intended for internal use by the Moose library and compiler plugin.\n * Its API might change without notice.\n */\nimport process from \"process\";\nimport * as path from \"path\";\nimport { Api, IngestApi, SqlResource, Task, Workflow } from \"./index\";\nimport type { IJsonSchemaCollection } from \"typia\";\nimport { Column } from \"../dataModels/dataModelTypes\";\nimport { ClickHouseEngines, ApiUtil } from \"../index\";\nimport {\n OlapTable,\n OlapConfig,\n ReplacingMergeTreeConfig,\n SummingMergeTreeConfig,\n ReplicatedMergeTreeConfig,\n ReplicatedReplacingMergeTreeConfig,\n ReplicatedAggregatingMergeTreeConfig,\n ReplicatedSummingMergeTreeConfig,\n ReplicatedCollapsingMergeTreeConfig,\n ReplicatedVersionedCollapsingMergeTreeConfig,\n S3QueueConfig,\n} from \"./sdk/olapTable\";\nimport type { TableProjection } from \"./sdk/olapTable\";\nimport {\n ConsumerConfig,\n KafkaSchemaConfig,\n Stream,\n TransformConfig,\n} from \"./sdk/stream\";\nimport { compilerLog } from \"../commons\";\nimport { WebApp } from \"./sdk/webApp\";\nimport { MaterializedView } from \"./sdk/materializedView\";\nimport { View } from \"./sdk/view\";\nimport {\n getSourceDir,\n getCompiledIndexPath,\n getOutDir,\n hasCompiledArtifacts,\n loadModule,\n} from \"../compiler-config\";\nimport {\n analyzeRegistryLineage,\n type DependencyAnalysisResult,\n type InfrastructureSignatureJson,\n} from \"./dependencyAnalysis\";\nimport { findSourceFiles } from \"./utils\";\n\n/**\n * Strips the file extension from a path, returning the \"stem\".\n * Handles compound extensions like .d.ts by stripping only the last extension.\n */\nfunction pathStem(filePath: string): string {\n const ext = path.extname(filePath);\n return ext ? filePath.slice(0, -ext.length) : filePath;\n}\n\n/**\n * Checks for source files that exist but weren't loaded.\n *\n * Since we now load pre-compiled JS from the outDir (e.g. .moose/compiled/app/),\n * require.cache contains compiled paths, not source paths. We compare using\n * path stems relative to each root: for source files relative to appDir, and\n * for loaded files relative to compiledAppDir. This maps e.g.\n * source: app/models.ts -> stem \"models\"\n * compiled: .moose/compiled/app/models.js -> stem \"models\"\n */\nfunction findUnloadedFiles(): string[] {\n const cwd = process.cwd();\n const sourceDir = getSourceDir();\n const appDir = path.resolve(cwd, sourceDir);\n\n // The compiled equivalent of appDir lives under outDir/sourceDir\n const compiledAppDir = path.resolve(cwd, getOutDir(), sourceDir);\n\n // Find all source files in the source directory\n const allSourceFiles = findSourceFiles(appDir, (directory, error) => {\n compilerLog(`Warning: Could not read directory ${directory}: ${error}`);\n });\n\n // Build a set of stems from require.cache entries under the compiled directory.\n // e.g. \".moose/compiled/app/models.js\" -> stem \"models\"\n const loadedStems = new Set(\n Object.keys(require.cache)\n .filter((key) => key.startsWith(compiledAppDir))\n .map((key) => pathStem(path.relative(compiledAppDir, key))),\n );\n\n // A source file is unloaded if its stem (relative to appDir) is not in loadedStems.\n // e.g. \"app/unloaded_table.ts\" -> stem \"unloaded_table\" -> not in loadedStems\n const unloadedFiles = allSourceFiles\n .filter((file) => {\n const stem = pathStem(path.relative(appDir, file));\n return !loadedStems.has(stem);\n })\n .map((file) => path.relative(cwd, file));\n\n return unloadedFiles;\n}\n\n/**\n * Client-only mode check. When true, resource registration is permissive\n * (duplicates overwrite silently instead of throwing).\n * Set via MOOSE_CLIENT_ONLY=true environment variable.\n *\n * This enables Next.js apps to import OlapTable definitions for type-safe\n * queries without the Moose runtime, avoiding \"already exists\" errors on HMR.\n *\n * @returns true if MOOSE_CLIENT_ONLY environment variable is set to \"true\"\n */\nexport const isClientOnlyMode = (): boolean =>\n process.env.MOOSE_CLIENT_ONLY === \"true\";\n\nclass MutationTrackingMap<K, V> extends Map<K, V> {\n private onMutate: (() => void) | undefined;\n\n constructor(entries?: Iterable<readonly [K, V]>, onMutate?: () => void) {\n super(entries);\n this.onMutate = onMutate;\n }\n\n setMutationListener(onMutate: () => void): void {\n this.onMutate = onMutate;\n }\n\n override set(key: K, value: V): this {\n super.set(key, value);\n this.onMutate?.();\n return this;\n }\n\n override delete(key: K): boolean {\n const deleted = super.delete(key);\n if (deleted) {\n this.onMutate?.();\n }\n return deleted;\n }\n\n override clear(): void {\n if (this.size === 0) {\n return;\n }\n super.clear();\n this.onMutate?.();\n }\n}\n\ntype MooseInternalRegistry = {\n tables: Map<string, OlapTable<any>>;\n streams: Map<string, Stream<any>>;\n ingestApis: Map<string, IngestApi<any>>;\n apis: Map<string, Api<any>>;\n sqlResources: Map<string, SqlResource>;\n workflows: Map<string, Workflow>;\n webApps: Map<string, WebApp>;\n materializedViews: Map<string, MaterializedView<any>>;\n views: Map<string, View>;\n};\n\nlet registryMutationVersion = 0;\nlet lineageCache:\n | {\n registry: MooseInternalRegistry;\n version: number;\n result: DependencyAnalysisResult;\n }\n | undefined;\n\nconst markRegistryMutated = () => {\n registryMutationVersion += 1;\n lineageCache = undefined;\n};\n\nfunction toTrackingMap<V>(\n map: Map<string, V> | undefined,\n): MutationTrackingMap<string, V> {\n if (map instanceof MutationTrackingMap) {\n map.setMutationListener(markRegistryMutated);\n return map;\n }\n return new MutationTrackingMap<string, V>(\n map?.entries(),\n markRegistryMutated,\n );\n}\n\nfunction createRegistryFrom(\n existing?: Partial<MooseInternalRegistry>,\n): MooseInternalRegistry {\n return {\n tables: toTrackingMap(existing?.tables),\n streams: toTrackingMap(existing?.streams),\n ingestApis: toTrackingMap(existing?.ingestApis),\n apis: toTrackingMap(existing?.apis),\n sqlResources: toTrackingMap(existing?.sqlResources),\n workflows: toTrackingMap(existing?.workflows),\n webApps: toTrackingMap(existing?.webApps),\n materializedViews: toTrackingMap(existing?.materializedViews),\n views: toTrackingMap(existing?.views),\n };\n}\n\n/**\n * Internal registry holding all defined Moose dmv2 resources.\n * Populated by the constructors of OlapTable, Stream, IngestApi, etc.\n * Accessed via `getMooseInternal()`.\n */\nconst moose_internal: MooseInternalRegistry = {\n tables: new MutationTrackingMap<string, OlapTable<any>>(\n undefined,\n markRegistryMutated,\n ),\n streams: new MutationTrackingMap<string, Stream<any>>(\n undefined,\n markRegistryMutated,\n ),\n ingestApis: new MutationTrackingMap<string, IngestApi<any>>(\n undefined,\n markRegistryMutated,\n ),\n apis: new MutationTrackingMap<string, Api<any>>(\n undefined,\n markRegistryMutated,\n ),\n sqlResources: new MutationTrackingMap<string, SqlResource>(\n undefined,\n markRegistryMutated,\n ),\n workflows: new MutationTrackingMap<string, Workflow>(\n undefined,\n markRegistryMutated,\n ),\n webApps: new MutationTrackingMap<string, WebApp>(\n undefined,\n markRegistryMutated,\n ),\n materializedViews: new MutationTrackingMap<string, MaterializedView<any>>(\n undefined,\n markRegistryMutated,\n ),\n views: new MutationTrackingMap<string, View>(undefined, markRegistryMutated),\n};\n\nfunction getCachedLineage(\n registry: MooseInternalRegistry,\n): DependencyAnalysisResult {\n if (\n lineageCache &&\n lineageCache.registry === registry &&\n lineageCache.version === registryMutationVersion\n ) {\n return lineageCache.result;\n }\n\n const result = analyzeRegistryLineage(registry);\n lineageCache = {\n registry,\n version: registryMutationVersion,\n result,\n };\n return result;\n}\n/**\n * Default retention period for streams if not specified (7 days in seconds).\n */\nconst defaultRetentionPeriod = 60 * 60 * 24 * 7;\n\n/**\n * Engine-specific configuration types using discriminated union pattern\n */\ninterface MergeTreeEngineConfig {\n engine: \"MergeTree\";\n}\n\ninterface ReplacingMergeTreeEngineConfig {\n engine: \"ReplacingMergeTree\";\n ver?: string;\n isDeleted?: string;\n}\n\ninterface AggregatingMergeTreeEngineConfig {\n engine: \"AggregatingMergeTree\";\n}\n\ninterface SummingMergeTreeEngineConfig {\n engine: \"SummingMergeTree\";\n columns?: string[];\n}\n\ninterface CollapsingMergeTreeEngineConfig {\n engine: \"CollapsingMergeTree\";\n sign: string;\n}\n\ninterface VersionedCollapsingMergeTreeEngineConfig {\n engine: \"VersionedCollapsingMergeTree\";\n sign: string;\n ver: string;\n}\n\ninterface ReplicatedMergeTreeEngineConfig {\n engine: \"ReplicatedMergeTree\";\n keeperPath?: string;\n replicaName?: string;\n}\n\ninterface ReplicatedReplacingMergeTreeEngineConfig {\n engine: \"ReplicatedReplacingMergeTree\";\n keeperPath?: string;\n replicaName?: string;\n ver?: string;\n isDeleted?: string;\n}\n\ninterface ReplicatedAggregatingMergeTreeEngineConfig {\n engine: \"ReplicatedAggregatingMergeTree\";\n keeperPath?: string;\n replicaName?: string;\n}\n\ninterface ReplicatedSummingMergeTreeEngineConfig {\n engine: \"ReplicatedSummingMergeTree\";\n keeperPath?: string;\n replicaName?: string;\n columns?: string[];\n}\n\ninterface ReplicatedCollapsingMergeTreeEngineConfig {\n engine: \"ReplicatedCollapsingMergeTree\";\n keeperPath?: string;\n replicaName?: string;\n sign: string;\n}\n\ninterface ReplicatedVersionedCollapsingMergeTreeEngineConfig {\n engine: \"ReplicatedVersionedCollapsingMergeTree\";\n keeperPath?: string;\n replicaName?: string;\n sign: string;\n ver: string;\n}\n\ninterface S3QueueEngineConfig {\n engine: \"S3Queue\";\n s3Path: string;\n format: string;\n awsAccessKeyId?: string;\n awsSecretAccessKey?: string;\n compression?: string;\n headers?: { [key: string]: string };\n}\n\ninterface S3EngineConfig {\n engine: \"S3\";\n path: string;\n format: string;\n awsAccessKeyId?: string;\n awsSecretAccessKey?: string;\n compression?: string;\n partitionStrategy?: string;\n partitionColumnsInDataFile?: string;\n}\n\ninterface BufferEngineConfig {\n engine: \"Buffer\";\n targetDatabase: string;\n targetTable: string;\n numLayers: number;\n minTime: number;\n maxTime: number;\n minRows: number;\n maxRows: number;\n minBytes: number;\n maxBytes: number;\n flushTime?: number;\n flushRows?: number;\n flushBytes?: number;\n}\n\ninterface DistributedEngineConfig {\n engine: \"Distributed\";\n cluster: string;\n targetDatabase: string;\n targetTable: string;\n shardingKey?: string;\n policyName?: string;\n}\n\ninterface IcebergS3EngineConfig {\n engine: \"IcebergS3\";\n path: string;\n format: string;\n awsAccessKeyId?: string;\n awsSecretAccessKey?: string;\n compression?: string;\n}\n\ninterface KafkaEngineConfig {\n engine: \"Kafka\";\n brokerList: string;\n topicList: string;\n groupName: string;\n format: string;\n}\n\ninterface MergeEngineConfig {\n engine: \"Merge\";\n sourceDatabase: string;\n tablesRegexp: string;\n}\n\n/**\n * Union type for all supported engine configurations\n */\ntype EngineConfig =\n | MergeTreeEngineConfig\n | ReplacingMergeTreeEngineConfig\n | AggregatingMergeTreeEngineConfig\n | SummingMergeTreeEngineConfig\n | CollapsingMergeTreeEngineConfig\n | VersionedCollapsingMergeTreeEngineConfig\n | ReplicatedMergeTreeEngineConfig\n | ReplicatedReplacingMergeTreeEngineConfig\n | ReplicatedAggregatingMergeTreeEngineConfig\n | ReplicatedSummingMergeTreeEngineConfig\n | ReplicatedCollapsingMergeTreeEngineConfig\n | ReplicatedVersionedCollapsingMergeTreeEngineConfig\n | S3QueueEngineConfig\n | S3EngineConfig\n | BufferEngineConfig\n | DistributedEngineConfig\n | IcebergS3EngineConfig\n | KafkaEngineConfig\n | MergeEngineConfig;\n\n/**\n * JSON representation of an OLAP table configuration.\n */\ninterface TableJson {\n /** The name of the table. */\n name: string;\n /** Array defining the table's columns and their types. */\n columns: Column[];\n /** ORDER BY clause: either array of column names or a single ClickHouse expression. */\n orderBy: string[] | string;\n /** The column name used for the PARTITION BY clause. */\n partitionBy?: string;\n /** SAMPLE BY expression for approximate query processing. */\n sampleByExpression?: string;\n /** PRIMARY KEY expression (overrides column-level primary_key flags when specified). */\n primaryKeyExpression?: string;\n /** Engine configuration with type-safe, engine-specific parameters */\n engineConfig?: EngineConfig;\n /** Optional version string for the table configuration. */\n version?: string;\n /** Optional metadata for the table (e.g., description). */\n metadata?: { description?: string };\n /** Lifecycle management setting for the table. */\n lifeCycle?: string;\n /** Optional table-level settings that can be modified with ALTER TABLE MODIFY SETTING. */\n tableSettings?: { [key: string]: string };\n /** Optional table indexes */\n indexes?: {\n name: string;\n expression: string;\n type: string;\n arguments: string[];\n granularity: number;\n }[];\n /** Optional table projections */\n projections?: TableProjection[];\n /** Optional table-level TTL expression (without leading 'TTL'). */\n ttl?: string;\n /** Optional database name for multi-database support. */\n database?: string;\n /** Optional cluster name for ON CLUSTER support. */\n cluster?: string;\n /** Optional seed filter for `moose seed clickhouse`. */\n seedFilter?: { limit?: number; where?: string };\n}\n/**\n * Represents a target destination for data flow, typically a stream.\n */\ninterface Target {\n /** The name of the target resource (e.g., stream name). */\n name: string;\n /** The kind of the target resource. */\n kind: \"stream\"; // may add `| \"table\"` in the future\n /** Optional version string of the target resource's configuration. */\n version?: string;\n /** Optional metadata for the target (e.g., description for function processes). */\n metadata?: { description?: string };\n /** Optional source file path where this transform was declared. */\n sourceFile?: string;\n}\n\n/**\n * Represents a consumer attached to a stream.\n */\ninterface Consumer {\n /** Optional version string for the consumer configuration. */\n version?: string;\n /** Optional source file path where this consumer was declared. */\n sourceFile?: string;\n}\n\n/**\n * JSON representation of a Stream/Topic configuration.\n */\ninterface StreamJson {\n /** The name of the stream/topic. */\n name: string;\n /** Array defining the message schema (columns/fields). */\n columns: Column[];\n /** Data retention period in seconds. */\n retentionPeriod: number;\n /** Number of partitions for the stream/topic. */\n partitionCount: number;\n /** Optional name of the OLAP table this stream automatically syncs to. */\n targetTable?: string;\n /** Optional version of the target OLAP table configuration. */\n targetTableVersion?: string;\n /** Optional version string for the stream configuration. */\n version?: string;\n /** List of target streams this stream transforms data into. */\n transformationTargets: Target[];\n /** Flag indicating if a multi-transform function (`_multipleTransformations`) is defined. */\n hasMultiTransform: boolean;\n /** List of consumers attached to this stream. */\n consumers: Consumer[];\n /** Optional description for the stream. */\n metadata?: { description?: string };\n /** Lifecycle management setting for the stream. */\n lifeCycle?: string;\n /** Optional Schema Registry config */\n schemaConfig?: KafkaSchemaConfig;\n}\n/**\n * JSON representation of an Ingest API configuration.\n */\ninterface IngestApiJson {\n /** The name of the Ingest API endpoint. */\n name: string;\n /** Array defining the expected input schema (columns/fields). */\n columns: Column[];\n\n /** The target stream where ingested data is written. */\n writeTo: Target;\n /** The DLQ if the data does not fit the schema. */\n deadLetterQueue?: string;\n /** Optional version string for the API configuration. */\n version?: string;\n /** Optional custom path for the ingestion endpoint. */\n path?: string;\n /** Optional description for the API. */\n metadata?: { description?: string };\n /** JSON schema */\n schema: IJsonSchemaCollection.IV3_1;\n /**\n * Whether this API allows extra fields beyond the defined columns.\n * When true, extra fields in payloads are passed through to streaming functions.\n */\n allowExtraFields?: boolean;\n}\n\n/**\n * JSON representation of an API configuration.\n */\ninterface ApiJson {\n /** The name of the API endpoint. */\n name: string;\n /** Array defining the expected query parameters schema. */\n queryParams: Column[];\n /** JSON schema definition of the API's response body. */\n responseSchema: IJsonSchemaCollection.IV3_1;\n /** Optional version string for the API configuration. */\n version?: string;\n /** Optional custom path for the API endpoint. */\n path?: string;\n /** Optional description for the API. */\n metadata?: { description?: string };\n /** Components that this API reads from. */\n pullsDataFrom: InfrastructureSignatureJson[];\n /** Components that this API writes to. */\n pushesDataTo: InfrastructureSignatureJson[];\n}\n\ninterface WorkflowJson {\n name: string;\n retries?: number;\n timeout?: string;\n schedule?: string;\n pullsDataFrom: InfrastructureSignatureJson[];\n pushesDataTo: InfrastructureSignatureJson[];\n}\n\ninterface WebAppJson {\n name: string;\n mountPath: string;\n metadata?: { description?: string };\n pullsDataFrom: InfrastructureSignatureJson[];\n pushesDataTo: InfrastructureSignatureJson[];\n}\n\ninterface SqlResourceJson {\n /** The name of the SQL resource. */\n name: string;\n /** Array of SQL DDL statements required to create the resource. */\n setup: readonly string[];\n /** Array of SQL DDL statements required to drop the resource. */\n teardown: readonly string[];\n\n /** List of infrastructure components (by signature) that this resource reads from. */\n pullsDataFrom: InfrastructureSignatureJson[];\n /** List of infrastructure components (by signature) that this resource writes to. */\n pushesDataTo: InfrastructureSignatureJson[];\n /** Optional source file path where this resource is defined. */\n sourceFile?: string;\n /** Optional source line number where this resource is defined. */\n sourceLine?: number;\n /** Optional source column number where this resource is defined. */\n sourceColumn?: number;\n}\n\n/**\n * JSON representation of a structured Materialized View.\n */\ninterface MaterializedViewJson {\n /** Name of the materialized view */\n name: string;\n /** Database where the MV is created (optional, uses default if not set) */\n database?: string;\n /** The SELECT SQL statement */\n selectSql: string;\n /** Source tables that the SELECT reads from */\n sourceTables: string[];\n /** Target table where transformed data is written */\n targetTable: string;\n /** Target table database (optional) */\n targetDatabase?: string;\n /** Optional metadata for the materialized view (e.g., description, source file) */\n metadata?: { [key: string]: any };\n /** Optional lifecycle management policy */\n lifeCycle?: string;\n}\n\n/**\n * JSON representation of a structured View.\n */\ninterface ViewJson {\n /** Name of the view */\n name: string;\n /** Database where the view is created (optional, uses default if not set) */\n database?: string;\n /** The SELECT SQL statement */\n selectSql: string;\n /** Source tables that the SELECT reads from */\n sourceTables: string[];\n /** Optional metadata for the view (e.g., description, source file) */\n metadata?: { [key: string]: any };\n}\n\n/**\n * Type guard: Check if config is S3QueueConfig\n */\nfunction isS3QueueConfig(\n config: OlapConfig<any>,\n): config is S3QueueConfig<any> {\n return \"engine\" in config && config.engine === ClickHouseEngines.S3Queue;\n}\n\n/**\n * Type guard: Check if config has a replicated engine\n * Checks if the engine value is one of the replicated engine types\n */\nfunction hasReplicatedEngine(\n config: OlapConfig<any>,\n): config is\n | ReplicatedMergeTreeConfig<any>\n | ReplicatedReplacingMergeTreeConfig<any>\n | ReplicatedAggregatingMergeTreeConfig<any>\n | ReplicatedSummingMergeTreeConfig<any>\n | ReplicatedCollapsingMergeTreeConfig<any>\n | ReplicatedVersionedCollapsingMergeTreeConfig<any> {\n if (!(\"engine\" in config)) {\n return false;\n }\n\n const engine = config.engine as ClickHouseEngines;\n // Check if engine is one of the replicated engine types\n return (\n engine === ClickHouseEngines.ReplicatedMergeTree ||\n engine === ClickHouseEngines.ReplicatedReplacingMergeTree ||\n engine === ClickHouseEngines.ReplicatedAggregatingMergeTree ||\n engine === ClickHouseEngines.ReplicatedSummingMergeTree ||\n engine === ClickHouseEngines.ReplicatedCollapsingMergeTree ||\n engine === ClickHouseEngines.ReplicatedVersionedCollapsingMergeTree\n );\n}\n\n/**\n * Extract engine value from table config, handling both legacy and new formats\n */\nfunction extractEngineValue(config: OlapConfig<any>): ClickHouseEngines {\n // Legacy config without engine property defaults to MergeTree\n if (!(\"engine\" in config)) {\n return ClickHouseEngines.MergeTree;\n }\n\n // All engines (replicated and non-replicated) have engine as direct value\n return config.engine as ClickHouseEngines;\n}\n\n/**\n * Convert engine config for basic MergeTree engines\n */\nfunction convertBasicEngineConfig(\n engine: ClickHouseEngines,\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n switch (engine) {\n case ClickHouseEngines.MergeTree:\n return { engine: \"MergeTree\" };\n\n case ClickHouseEngines.AggregatingMergeTree:\n return { engine: \"AggregatingMergeTree\" };\n\n case ClickHouseEngines.ReplacingMergeTree: {\n const replacingConfig = config as ReplacingMergeTreeConfig<any>;\n return {\n engine: \"ReplacingMergeTree\",\n ver: replacingConfig.ver,\n isDeleted: replacingConfig.isDeleted,\n };\n }\n\n case ClickHouseEngines.SummingMergeTree: {\n const summingConfig = config as SummingMergeTreeConfig<any>;\n return {\n engine: \"SummingMergeTree\",\n columns: summingConfig.columns,\n };\n }\n\n case ClickHouseEngines.CollapsingMergeTree: {\n const collapsingConfig = config as any; // CollapsingMergeTreeConfig<any>\n return {\n engine: \"CollapsingMergeTree\",\n sign: collapsingConfig.sign,\n };\n }\n\n case ClickHouseEngines.VersionedCollapsingMergeTree: {\n const versionedConfig = config as any; // VersionedCollapsingMergeTreeConfig<any>\n return {\n engine: \"VersionedCollapsingMergeTree\",\n sign: versionedConfig.sign,\n ver: versionedConfig.ver,\n };\n }\n\n default:\n return undefined;\n }\n}\n\n/**\n * Convert engine config for replicated MergeTree engines\n */\nfunction convertReplicatedEngineConfig(\n engine: ClickHouseEngines,\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n // First check if this is a replicated engine config\n if (!hasReplicatedEngine(config)) {\n return undefined;\n }\n\n switch (engine) {\n case ClickHouseEngines.ReplicatedMergeTree: {\n const replicatedConfig = config as ReplicatedMergeTreeConfig<any>;\n return {\n engine: \"ReplicatedMergeTree\",\n keeperPath: replicatedConfig.keeperPath,\n replicaName: replicatedConfig.replicaName,\n };\n }\n\n case ClickHouseEngines.ReplicatedReplacingMergeTree: {\n const replicatedConfig =\n config as ReplicatedReplacingMergeTreeConfig<any>;\n return {\n engine: \"ReplicatedReplacingMergeTree\",\n keeperPath: replicatedConfig.keeperPath,\n replicaName: replicatedConfig.replicaName,\n ver: replicatedConfig.ver,\n isDeleted: replicatedConfig.isDeleted,\n };\n }\n\n case ClickHouseEngines.ReplicatedAggregatingMergeTree: {\n const replicatedConfig =\n config as ReplicatedAggregatingMergeTreeConfig<any>;\n return {\n engine: \"ReplicatedAggregatingMergeTree\",\n keeperPath: replicatedConfig.keeperPath,\n replicaName: replicatedConfig.replicaName,\n };\n }\n\n case ClickHouseEngines.ReplicatedSummingMergeTree: {\n const replicatedConfig = config as ReplicatedSummingMergeTreeConfig<any>;\n return {\n engine: \"ReplicatedSummingMergeTree\",\n keeperPath: replicatedConfig.keeperPath,\n replicaName: replicatedConfig.replicaName,\n columns: replicatedConfig.columns,\n };\n }\n\n case ClickHouseEngines.ReplicatedCollapsingMergeTree: {\n const replicatedConfig = config as any; // ReplicatedCollapsingMergeTreeConfig<any>\n return {\n engine: \"ReplicatedCollapsingMergeTree\",\n keeperPath: replicatedConfig.keeperPath,\n replicaName: replicatedConfig.replicaName,\n sign: replicatedConfig.sign,\n };\n }\n\n case ClickHouseEngines.ReplicatedVersionedCollapsingMergeTree: {\n const replicatedConfig = config as any; // ReplicatedVersionedCollapsingMergeTreeConfig<any>\n return {\n engine: \"ReplicatedVersionedCollapsingMergeTree\",\n keeperPath: replicatedConfig.keeperPath,\n replicaName: replicatedConfig.replicaName,\n sign: replicatedConfig.sign,\n ver: replicatedConfig.ver,\n };\n }\n\n default:\n return undefined;\n }\n}\n\n/**\n * Convert S3Queue engine config\n * Uses type guard for fully type-safe property access\n */\nfunction convertS3QueueEngineConfig(\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n if (!isS3QueueConfig(config)) {\n return undefined;\n }\n\n return {\n engine: \"S3Queue\",\n s3Path: config.s3Path,\n format: config.format,\n awsAccessKeyId: config.awsAccessKeyId,\n awsSecretAccessKey: config.awsSecretAccessKey,\n compression: config.compression,\n headers: config.headers,\n };\n}\n\n/**\n * Convert S3 engine config\n */\nfunction convertS3EngineConfig(\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n if (!(\"engine\" in config) || config.engine !== ClickHouseEngines.S3) {\n return undefined;\n }\n\n return {\n engine: \"S3\",\n path: config.path,\n format: config.format,\n awsAccessKeyId: config.awsAccessKeyId,\n awsSecretAccessKey: config.awsSecretAccessKey,\n compression: config.compression,\n partitionStrategy: config.partitionStrategy,\n partitionColumnsInDataFile: config.partitionColumnsInDataFile,\n };\n}\n\n/**\n * Convert Buffer engine config\n */\nfunction convertBufferEngineConfig(\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n if (!(\"engine\" in config) || config.engine !== ClickHouseEngines.Buffer) {\n return undefined;\n }\n\n return {\n engine: \"Buffer\",\n targetDatabase: config.targetDatabase,\n targetTable: config.targetTable,\n numLayers: config.numLayers,\n minTime: config.minTime,\n maxTime: config.maxTime,\n minRows: config.minRows,\n maxRows: config.maxRows,\n minBytes: config.minBytes,\n maxBytes: config.maxBytes,\n flushTime: config.flushTime,\n flushRows: config.flushRows,\n flushBytes: config.flushBytes,\n };\n}\n\n/**\n * Convert Distributed engine config\n */\nfunction convertDistributedEngineConfig(\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n if (\n !(\"engine\" in config) ||\n config.engine !== ClickHouseEngines.Distributed\n ) {\n return undefined;\n }\n\n return {\n engine: \"Distributed\",\n cluster: config.cluster,\n targetDatabase: config.targetDatabase,\n targetTable: config.targetTable,\n shardingKey: config.shardingKey,\n policyName: config.policyName,\n };\n}\n\n/**\n * Convert IcebergS3 engine config\n */\nfunction convertIcebergS3EngineConfig(\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n if (!(\"engine\" in config) || config.engine !== ClickHouseEngines.IcebergS3) {\n return undefined;\n }\n\n return {\n engine: \"IcebergS3\",\n path: config.path,\n format: config.format,\n awsAccessKeyId: config.awsAccessKeyId,\n awsSecretAccessKey: config.awsSecretAccessKey,\n compression: config.compression,\n };\n}\n\n/**\n * Convert Kafka engine configuration\n */\nfunction convertKafkaEngineConfig(\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n if (!(\"engine\" in config) || config.engine !== ClickHouseEngines.Kafka) {\n return undefined;\n }\n\n return {\n engine: \"Kafka\",\n brokerList: config.brokerList,\n topicList: config.topicList,\n groupName: config.groupName,\n format: config.format,\n };\n}\n\n/**\n * Convert Merge engine config\n */\nfunction convertMergeEngineConfig(\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n if (!(\"engine\" in config) || config.engine !== ClickHouseEngines.Merge) {\n return undefined;\n }\n\n return {\n engine: \"Merge\",\n sourceDatabase: config.sourceDatabase,\n tablesRegexp: config.tablesRegexp,\n };\n}\n\n/**\n * Convert table configuration to engine config\n */\nfunction convertTableConfigToEngineConfig(\n config: OlapConfig<any>,\n): EngineConfig | undefined {\n const engine = extractEngineValue(config);\n\n // Try basic engines first\n const basicConfig = convertBasicEngineConfig(engine, config);\n if (basicConfig) {\n return basicConfig;\n }\n\n // Try replicated engines\n const replicatedConfig = convertReplicatedEngineConfig(engine, config);\n if (replicatedConfig) {\n return replicatedConfig;\n }\n\n // Handle S3Queue\n if (engine === ClickHouseEngines.S3Queue) {\n return convertS3QueueEngineConfig(config);\n }\n\n // Handle S3\n if (engine === ClickHouseEngines.S3) {\n return convertS3EngineConfig(config);\n }\n\n // Handle Buffer\n if (engine === ClickHouseEngines.Buffer) {\n return convertBufferEngineConfig(config);\n }\n\n // Handle Distributed\n if (engine === ClickHouseEngines.Distributed) {\n return convertDistributedEngineConfig(config);\n }\n\n // Handle IcebergS3\n if (engine === ClickHouseEngines.IcebergS3) {\n return convertIcebergS3EngineConfig(config);\n }\n\n // Handle Kafka\n if (engine === ClickHouseEngines.Kafka) {\n return convertKafkaEngineConfig(config);\n }\n\n // Handle Merge\n if (engine === ClickHouseEngines.Merge) {\n return convertMergeEngineConfig(config);\n }\n\n return undefined;\n}\n\nexport const toInfraMap = (registry: MooseInternalRegistry) => {\n const tables: { [key: string]: TableJson } = {};\n const topics: { [key: string]: StreamJson } = {};\n const ingestApis: { [key: string]: IngestApiJson } = {};\n const apis: { [key: string]: ApiJson } = {};\n const sqlResources: { [key: string]: SqlResourceJson } = {};\n const workflows: { [key: string]: WorkflowJson } = {};\n const webApps: { [key: string]: WebAppJson } = {};\n const materializedViews: { [key: string]: MaterializedViewJson } = {};\n const views: { [key: string]: ViewJson } = {};\n const lineage = getCachedLineage(registry);\n\n registry.tables.forEach((table) => {\n const id =\n table.config.version ?\n `${table.name}_${table.config.version}`\n : table.name;\n // If the table is part of an IngestPipeline, inherit metadata if not set\n let metadata = (table as any).metadata;\n if (!metadata && table.config && (table as any).pipelineParent) {\n metadata = (table as any).pipelineParent.metadata;\n }\n // Create type-safe engine configuration\n const engineConfig: EngineConfig | undefined =\n convertTableConfigToEngineConfig(table.config);\n\n // Get table settings, applying defaults for S3Queue\n let tableSettings: { [key: string]: string } | undefined = undefined;\n\n if (table.config.settings) {\n // Convert all settings to strings, filtering out undefined values\n tableSettings = Object.entries(table.config.settings).reduce(\n (acc, [key, value]) => {\n if (value !== undefined) {\n acc[key] = String(value);\n }\n return acc;\n },\n {} as { [key: string]: string },\n );\n }\n\n // Apply default settings for S3Queue if not already specified\n if (engineConfig?.engine === \"S3Queue\") {\n if (!tableSettings) {\n tableSettings = {};\n }\n // Set default mode to 'unordered' if not specified\n if (!tableSettings.mode) {\n tableSettings.mode = \"unordered\";\n }\n }\n\n // Determine ORDER BY from config\n // Note: engines like Buffer and Distributed don't support orderBy/partitionBy/sampleBy\n const hasOrderByFields =\n \"orderByFields\" in table.config &&\n Array.isArray(table.config.orderByFields) &&\n table.config.orderByFields.length > 0;\n const hasOrderByExpression =\n \"orderByExpression\" in table.config &&\n typeof table.config.orderByExpression === \"string\" &&\n table.config.orderByExpression.length > 0;\n if (hasOrderByFields && hasOrderByExpression) {\n throw new Error(\n `Table ${table.name}: Provide either orderByFields or orderByExpression, not both.`,\n );\n }\n const orderBy: string[] | string =\n hasOrderByExpression && \"orderByExpression\" in table.config ?\n (table.config.orderByExpression ?? \"\")\n : \"orderByFields\" in table.config ? (table.config.orderByFields ?? [])\n : [];\n\n tables[id] = {\n name: table.name,\n columns: table.columnArray,\n orderBy,\n partitionBy:\n \"partitionBy\" in table.config ? table.config.partitionBy : undefined,\n sampleByExpression:\n \"sampleByExpression\" in table.config ?\n table.config.sampleByExpression\n : undefined,\n primaryKeyExpression:\n \"primaryKeyExpression\" in table.config ?\n table.config.primaryKeyExpression\n : undefined,\n engineConfig,\n version: table.config.version,\n metadata,\n lifeCycle: table.config.lifeCycle,\n // Map 'settings' to 'tableSettings' for internal use\n tableSettings:\n tableSettings && Object.keys(tableSettings).length > 0 ?\n tableSettings\n : undefined,\n indexes:\n table.config.indexes?.map((i) => ({\n ...i,\n granularity: i.granularity === undefined ? 1 : i.granularity,\n arguments: i.arguments === undefined ? [] : i.arguments,\n })) || [],\n projections:\n (\"projections\" in table.config && table.config.projections) || [],\n ttl: table.config.ttl,\n database: table.config.database,\n cluster: table.config.cluster,\n seedFilter:\n \"seedFilter\" in table.config ? table.config.seedFilter : undefined,\n };\n });\n\n registry.streams.forEach((stream) => {\n // If the stream is part of an IngestPipeline, inherit metadata if not set\n let metadata = stream.metadata;\n if (!metadata && stream.config && (stream as any).pipelineParent) {\n metadata = (stream as any).pipelineParent.metadata;\n }\n const transformationTargets: Target[] = [];\n const consumers: Consumer[] = [];\n\n stream._transformations.forEach((transforms, destinationName) => {\n transforms.forEach(([destination, _, config]) => {\n transformationTargets.push({\n kind: \"stream\",\n name: destinationName,\n version: config.version,\n metadata: config.metadata,\n sourceFile: config.sourceFile,\n });\n });\n });\n\n stream._consumers.forEach((consumer) => {\n consumers.push({\n version: consumer.config.version,\n sourceFile: consumer.config.sourceFile,\n });\n });\n\n topics[stream.name] = {\n name: stream.name,\n columns: stream.columnArray,\n targetTable: stream.config.destination?.name,\n targetTableVersion: stream.config.destination?.config.version,\n retentionPeriod: stream.config.retentionPeriod ?? defaultRetentionPeriod,\n partitionCount: stream.config.parallelism ?? 1,\n version: stream.config.version,\n transformationTargets,\n hasMultiTransform: stream._multipleTransformations === undefined,\n consumers,\n metadata,\n lifeCycle: stream.config.lifeCycle,\n schemaConfig: stream.config.schemaConfig,\n };\n });\n\n registry.ingestApis.forEach((api) => {\n // If the ingestApi is part of an IngestPipeline, inherit metadata if not set\n let metadata = api.metadata;\n if (!metadata && api.config && (api as any).pipelineParent) {\n metadata = (api as any).pipelineParent.metadata;\n }\n ingestApis[api.name] = {\n name: api.name,\n columns: api.columnArray,\n version: api.config.version,\n path: api.config.path,\n writeTo: {\n kind: \"stream\",\n name: api.config.destination.name,\n },\n deadLetterQueue: api.config.deadLetterQueue?.name,\n metadata,\n schema: api.schema,\n allowExtraFields: api.allowExtraFields,\n };\n });\n\n registry.apis.forEach((api, key) => {\n const rustKey =\n api.config.version ? `${api.name}:${api.config.version}` : api.name;\n const apiLineage = lineage.apiByKey.get(rustKey);\n apis[rustKey] = {\n name: api.name,\n queryParams: api.columnArray,\n responseSchema: api.responseSchema,\n version: api.config.version,\n path: api.config.path,\n metadata: api.metadata,\n pullsDataFrom: apiLineage?.pullsDataFrom ?? [],\n pushesDataTo: apiLineage?.pushesDataTo ?? [],\n };\n });\n\n registry.sqlResources.forEach((sqlResource) => {\n sqlResources[sqlResource.name] = {\n name: sqlResource.name,\n setup: sqlResource.setup,\n teardown: sqlResource.teardown,\n sourceFile: sqlResource.sourceFile,\n sourceLine: sqlResource.sourceLine,\n sourceColumn: sqlResource.sourceColumn,\n\n pullsDataFrom: sqlResource.pullsDataFrom.map((r) => {\n if (r.kind === \"OlapTable\") {\n const table = r as OlapTable<any>;\n const id =\n table.config.version ?\n `${table.name}_${table.config.version}`\n : table.name;\n return {\n id,\n kind: \"Table\",\n };\n } else if (r.kind === \"SqlResource\") {\n const resource = r as SqlResource;\n return {\n id: resource.name,\n kind: \"SqlResource\",\n };\n } else if (r.kind === \"View\") {\n const view = r as View;\n return {\n id: view.name,\n kind: \"View\",\n };\n } else if (r.kind === \"MaterializedView\") {\n const mv = r as MaterializedView<any>;\n return {\n id: mv.name,\n kind: \"MaterializedView\",\n };\n } else {\n throw new Error(`Unknown sql resource dependency type: ${r}`);\n }\n }),\n pushesDataTo: sqlResource.pushesDataTo.map((r) => {\n if (r.kind === \"OlapTable\") {\n const table = r as OlapTable<any>;\n const id =\n table.config.version ?\n `${table.name}_${table.config.version}`\n : table.name;\n return {\n id,\n kind: \"Table\",\n };\n } else if (r.kind === \"SqlResource\") {\n const resource = r as SqlResource;\n return {\n id: resource.name,\n kind: \"SqlResource\",\n };\n } else if (r.kind === \"View\") {\n const view = r as View;\n return {\n id: view.name,\n kind: \"View\",\n };\n } else if (r.kind === \"MaterializedView\") {\n const mv = r as MaterializedView<any>;\n return {\n id: mv.name,\n kind: \"MaterializedView\",\n };\n } else {\n throw new Error(`Unknown sql resource dependency type: ${r}`);\n }\n }),\n };\n });\n\n registry.workflows.forEach((workflow) => {\n const workflowLineage = lineage.workflowByName.get(workflow.name);\n workflows[workflow.name] = {\n name: workflow.name,\n retries: workflow.config.retries,\n timeout: workflow.config.timeout,\n schedule: workflow.config.schedule,\n pullsDataFrom: workflowLineage?.pullsDataFrom ?? [],\n pushesDataTo: workflowLineage?.pushesDataTo ?? [],\n };\n });\n\n registry.webApps.forEach((webApp) => {\n const webAppLineage = lineage.webAppByName.get(webApp.name);\n webApps[webApp.name] = {\n name: webApp.name,\n mountPath: webApp.config.mountPath || \"/\",\n metadata: webApp.config.metadata,\n pullsDataFrom: webAppLineage?.pullsDataFrom ?? [],\n pushesDataTo: webAppLineage?.pushesDataTo ?? [],\n };\n });\n\n // Serialize materialized views with structured data\n registry.materializedViews.forEach((mv) => {\n materializedViews[mv.name] = {\n name: mv.name,\n selectSql: mv.selectSql,\n sourceTables: mv.sourceTables,\n targetTable: mv.targetTable.name,\n targetDatabase: mv.targetTable.config.database,\n metadata: mv.metadata,\n lifeCycle: mv.lifeCycle,\n };\n });\n\n // Serialize views with structured data\n registry.views.forEach((view) => {\n views[view.name] = {\n name: view.name,\n selectSql: view.selectSql,\n sourceTables: view.sourceTables,\n metadata: view.metadata,\n };\n });\n\n return {\n topics,\n tables,\n ingestApis,\n apis,\n sqlResources,\n workflows,\n webApps,\n materializedViews,\n views,\n unloadedFiles: [] as string[], // Will be populated by dumpMooseInternal\n };\n};\n\n/**\n * Retrieves the global internal Moose resource registry.\n * Uses `globalThis` to ensure a single registry instance.\n *\n * @returns The internal Moose resource registry.\n */\nconst initializeMooseInternalRegistry = () => {\n const existing = (globalThis as any).moose_internal as\n | Partial<MooseInternalRegistry>\n | undefined;\n\n if (existing === undefined) {\n (globalThis as any).moose_internal = moose_internal;\n return;\n }\n\n (globalThis as any).moose_internal = createRegistryFrom(existing);\n};\n\ninitializeMooseInternalRegistry();\n\nexport const getMooseInternal = (): MooseInternalRegistry =>\n (globalThis as any).moose_internal;\n\n/**\n * Loads the user's application entry point (`app/index.ts`) to register resources,\n * then generates and prints the infrastructure map as JSON.\n *\n * This function is the main entry point used by the Moose infrastructure system\n * to discover the defined resources.\n * It prints the JSON map surrounded by specific delimiters (`___MOOSE_STUFF___start`\n * and `end___MOOSE_STUFF___`) for easy extraction by the calling process.\n */\nexport const dumpMooseInternal = async () => {\n await loadIndex();\n\n const infraMap = toInfraMap(getMooseInternal());\n\n // Check for unloaded files\n const unloadedFiles = findUnloadedFiles();\n infraMap.unloadedFiles = unloadedFiles;\n\n console.log(\n \"___MOOSE_STUFF___start\",\n JSON.stringify(infraMap),\n \"end___MOOSE_STUFF___\",\n );\n};\n\nconst loadIndex = async () => {\n // Always use pre-compiled JavaScript - no ts-node fallback.\n // Compilation is handled by moose-tspc before this runs.\n\n // Check if compiled artifacts exist\n if (!hasCompiledArtifacts()) {\n const outDir = getOutDir();\n const sourceDir = getSourceDir();\n throw new Error(\n `Compiled artifacts not found at ${outDir}/${sourceDir}/index.js. ` +\n `Run 'npx moose-tspc' to compile your TypeScript first.`,\n );\n }\n\n // Clear registry and require.cache for hot reloading\n const registry = getMooseInternal();\n registry.tables.clear();\n registry.streams.clear();\n registry.ingestApis.clear();\n registry.apis.clear();\n registry.sqlResources.clear();\n registry.workflows.clear();\n registry.webApps.clear();\n registry.materializedViews.clear();\n registry.views.clear();\n\n // Clear require cache for compiled directory to pick up changes\n const outDir = getOutDir();\n const compiledDir =\n path.isAbsolute(outDir) ? outDir : path.join(process.cwd(), outDir);\n Object.keys(require.cache).forEach((key) => {\n if (key.startsWith(compiledDir)) {\n delete require.cache[key];\n }\n });\n\n try {\n // Load pre-compiled JavaScript from the configured outDir\n const indexPath = getCompiledIndexPath();\n await loadModule(indexPath);\n } catch (error) {\n let hint: string | undefined;\n let includeDetails = true;\n const details = error instanceof Error ? error.message : String(error);\n\n // Check for typia configuration errors\n if (\n details.includes(\"no transform has been configured\") ||\n details.includes(\"NoTransformConfigurationError\")\n ) {\n hint =\n \"🔴 Typia Transformation Error\\n\\n\" +\n \"This is likely a bug in Moose. The Typia type transformer failed to process your code.\\n\\n\" +\n \"Please report this issue:\\n\" +\n \" • Moose Slack: https://join.slack.com/t/moose-community/shared_invite/zt-2fjh5n3wz-cnOmM9Xe9DYAgQrNu8xKxg\\n\" +\n \" • Include the stack trace below and the file being processed\\n\\n\";\n includeDetails = false;\n } else if (\n details.includes(\"ERR_REQUIRE_ESM\") ||\n details.includes(\"ES Module\")\n ) {\n hint =\n \"The file or its dependencies are ESM-only. Switch to packages that dual-support CJS & ESM, or upgrade to Node 22.12+. \" +\n \"If you must use Node 20, you may try Node 20.19\\n\\n\";\n }\n\n if (hint === undefined) {\n throw error;\n } else {\n const errorMsg = includeDetails ? `${hint}${details}` : hint;\n const cause = error instanceof Error ? error : undefined;\n throw new Error(errorMsg, { cause });\n }\n }\n};\n\n/**\n * Loads the user's application entry point and extracts all registered stream\n * transformation and consumer functions.\n *\n * @returns A Map where keys are unique identifiers for transformations/consumers\n * (e.g., \"sourceStream_destStream_version\", \"sourceStream_<no-target>_version\")\n * and values are tuples containing: [handler function, config, source stream columns]\n */\nexport const getStreamingFunctions = async () => {\n await loadIndex();\n\n const registry = getMooseInternal();\n const transformFunctions = new Map<\n string,\n [\n (data: unknown) => unknown,\n TransformConfig<any> | ConsumerConfig<any>,\n Column[],\n ]\n >();\n\n registry.streams.forEach((stream) => {\n stream._transformations.forEach((transforms, destinationName) => {\n transforms.forEach(([_, transform, config]) => {\n const transformFunctionKey = `${stream.name}_${destinationName}${config.version ? `_${config.version}` : \"\"}`;\n compilerLog(`getStreamingFunctions: ${transformFunctionKey}`);\n transformFunctions.set(transformFunctionKey, [\n transform,\n config,\n stream.columnArray,\n ]);\n });\n });\n\n stream._consumers.forEach((consumer) => {\n const consumerFunctionKey = `${stream.name}_<no-target>${consumer.config.version ? `_${consumer.config.version}` : \"\"}`;\n transformFunctions.set(consumerFunctionKey, [\n consumer.consumer,\n consumer.config,\n stream.columnArray,\n ]);\n });\n });\n\n return transformFunctions;\n};\n\n/**\n * Loads the user's application entry point and extracts all registered\n * API handler functions.\n *\n * @returns A Map where keys are the names of the APIs and values\n * are their corresponding handler functions.\n */\nexport const getApis = async () => {\n await loadIndex();\n const apiFunctions = new Map<\n string,\n (params: unknown, utils: ApiUtil) => unknown\n >();\n\n const registry = getMooseInternal();\n // Single pass: store full keys, track aliasing decisions\n const versionCountByName = new Map<string, number>();\n const nameToSoleVersionHandler = new Map<\n string,\n (params: unknown, utils: ApiUtil) => unknown\n >();\n\n registry.apis.forEach((api, key) => {\n const handler = api.getHandler();\n apiFunctions.set(key, handler);\n\n if (!api.config.version) {\n // Explicit unversioned takes precedence for alias\n if (!apiFunctions.has(api.name)) {\n apiFunctions.set(api.name, handler);\n }\n nameToSoleVersionHandler.delete(api.name);\n versionCountByName.delete(api.name);\n } else if (!apiFunctions.has(api.name)) {\n // Only track versioned for alias if no explicit unversioned present\n const count = (versionCountByName.get(api.name) ?? 0) + 1;\n versionCountByName.set(api.name, count);\n if (count === 1) {\n nameToSoleVersionHandler.set(api.name, handler);\n } else {\n nameToSoleVersionHandler.delete(api.name);\n }\n }\n });\n\n // Finalize aliases for names that have exactly one versioned API and no unversioned\n nameToSoleVersionHandler.forEach((handler, name) => {\n if (!apiFunctions.has(name)) {\n apiFunctions.set(name, handler);\n }\n });\n\n return apiFunctions;\n};\n\nexport const dlqSchema: IJsonSchemaCollection.IV3_1 = {\n version: \"3.1\",\n components: {\n schemas: {\n DeadLetterModel: {\n type: \"object\",\n properties: {\n originalRecord: {\n $ref: \"#/components/schemas/Recordstringany\",\n },\n errorMessage: {\n type: \"string\",\n },\n errorType: {\n type: \"string\",\n },\n failedAt: {\n type: \"string\",\n format: \"date-time\",\n },\n source: {\n oneOf: [\n {\n const: \"api\",\n },\n {\n const: \"transform\",\n },\n {\n const: \"table\",\n },\n ],\n },\n },\n required: [\n \"originalRecord\",\n \"errorMessage\",\n \"errorType\",\n \"failedAt\",\n \"source\",\n ],\n },\n Recordstringany: {\n type: \"object\",\n properties: {},\n required: [],\n description: \"Construct a type with a set of properties K of type T\",\n additionalProperties: {},\n },\n },\n },\n schemas: [\n {\n $ref: \"#/components/schemas/DeadLetterModel\",\n },\n ],\n};\n\nexport const dlqColumns: Column[] = [\n {\n name: \"originalRecord\",\n data_type: \"Json\",\n primary_key: false,\n required: true,\n unique: false,\n default: null,\n annotations: [],\n ttl: null,\n codec: null,\n materialized: null,\n comment: null,\n },\n {\n name: \"errorMessage\",\n data_type: \"String\",\n primary_key: false,\n required: true,\n unique: false,\n default: null,\n annotations: [],\n ttl: null,\n codec: null,\n materialized: null,\n comment: null,\n },\n {\n name: \"errorType\",\n data_type: \"String\",\n primary_key: false,\n required: true,\n unique: false,\n default: null,\n annotations: [],\n ttl: null,\n codec: null,\n materialized: null,\n comment: null,\n },\n {\n name: \"failedAt\",\n data_type: \"DateTime\",\n primary_key: false,\n required: true,\n unique: false,\n default: null,\n annotations: [],\n ttl: null,\n codec: null,\n materialized: null,\n comment: null,\n },\n {\n name: \"source\",\n data_type: \"String\",\n primary_key: false,\n required: true,\n unique: false,\n default: null,\n annotations: [],\n ttl: null,\n codec: null,\n materialized: null,\n comment: null,\n },\n];\n\nexport const getWorkflows = async () => {\n await loadIndex();\n\n const registry = getMooseInternal();\n return registry.workflows;\n};\n\nfunction findTaskInTree(\n task: Task<any, any>,\n targetName: string,\n): Task<any, any> | undefined {\n if (task.name === targetName) {\n return task;\n }\n\n if (task.config.onComplete?.length) {\n for (const childTask of task.config.onComplete) {\n const found = findTaskInTree(childTask, targetName);\n if (found) {\n return found;\n }\n }\n }\n\n return undefined;\n}\n\nexport const getTaskForWorkflow = async (\n workflowName: string,\n taskName: string,\n): Promise<Task<any, any>> => {\n const workflows = await getWorkflows();\n const workflow = workflows.get(workflowName);\n if (!workflow) {\n throw new Error(`Workflow ${workflowName} not found`);\n }\n\n const task = findTaskInTree(\n workflow.config.startingTask as Task<any, any>,\n taskName,\n );\n if (!task) {\n throw new Error(`Task ${taskName} not found in workflow ${workflowName}`);\n }\n\n return task;\n};\n\nexport const getWebApps = async () => {\n await loadIndex();\n return getMooseInternal().webApps;\n};\n","#!/usr/bin/env node\n\n// This file is used to run the proper runners for moose based on the\n// arguments passed to the file.\n// It loads pre-compiled JavaScript - no ts-node required.\n\nimport { readFileSync } from \"fs\";\nimport { join } from \"path\";\n\nimport { dumpMooseInternal } from \"./dmv2/internal\";\nimport { runApis } from \"./consumption-apis/runner\";\nimport { runStreamingFunctions } from \"./streaming-functions/runner\";\nimport { runExportSerializer } from \"./moduleExportSerializer\";\nimport { runScripts } from \"./scripts/runner\";\n\nimport { Command } from \"commander\";\n\nimport type { StreamingFunctionArgs } from \"./streaming-functions/runner\";\n\nconst packageJson = JSON.parse(\n readFileSync(join(__dirname, \"..\", \"package.json\"), \"utf-8\"),\n);\n\nconst program = new Command();\n\nprogram\n .name(\"moose-runner\")\n .description(\"Moose runner for various operations\")\n .version(packageJson.version);\n\nprogram\n .command(\"print-version\")\n .description(\"Print the installed moose-lib version\")\n .action(() => {\n process.stdout.write(packageJson.version);\n });\n\nprogram\n .command(\"dmv2-serializer\")\n .description(\"Load DMv2 index\")\n .action(async () => {\n await dumpMooseInternal();\n });\n\nprogram\n .command(\"export-serializer\")\n .description(\"Run export serializer\")\n .argument(\"<target-model>\", \"Target model to serialize\")\n .action(async (targetModel) => {\n await runExportSerializer(targetModel);\n });\n\nprogram\n .command(\"consumption-apis\")\n .description(\"Run consumption APIs\")\n .argument(\"<clickhouse-db>\", \"Clickhouse database name\")\n .argument(\"<clickhouse-host>\", \"Clickhouse host\")\n .argument(\"<clickhouse-port>\", \"Clickhouse port\")\n .argument(\"<clickhouse-username>\", \"Clickhouse username\")\n .argument(\"<clickhouse-password>\", \"Clickhouse password\")\n .option(\"--clickhouse-use-ssl\", \"Use SSL for Clickhouse connection\", false)\n .option(\"--jwt-secret <secret>\", \"JWT public key for verification\")\n .option(\"--jwt-issuer <issuer>\", \"Expected JWT issuer\")\n .option(\"--jwt-audience <audience>\", \"Expected JWT audience\")\n .option(\n \"--enforce-auth\",\n \"Enforce authentication on all consumption APIs\",\n false,\n )\n .option(\"--temporal-url <url>\", \"Temporal server URL\")\n .option(\"--temporal-namespace <namespace>\", \"Temporal namespace\")\n .option(\"--client-cert <path>\", \"Path to client certificate\")\n .option(\"--client-key <path>\", \"Path to client key\")\n .option(\"--api-key <key>\", \"API key for authentication\")\n .option(\"--proxy-port <port>\", \"Port to run the proxy server on\", parseInt)\n .option(\n \"--worker-count <count>\",\n \"Number of worker processes for the consumption API cluster\",\n parseInt,\n )\n .action(\n (\n clickhouseDb,\n clickhouseHost,\n clickhousePort,\n clickhouseUsername,\n clickhousePassword,\n options,\n ) => {\n runApis({\n clickhouseConfig: {\n database: clickhouseDb,\n host: clickhouseHost,\n port: clickhousePort,\n username: clickhouseUsername,\n password: clickhousePassword,\n useSSL: options.clickhouseUseSsl,\n },\n jwtConfig: {\n secret: options.jwtSecret,\n issuer: options.jwtIssuer,\n audience: options.jwtAudience,\n },\n temporalConfig:\n options.temporalUrl ?\n {\n url: options.temporalUrl,\n namespace: options.temporalNamespace,\n clientCert: options.clientCert,\n clientKey: options.clientKey,\n apiKey: options.apiKey,\n }\n : undefined,\n enforceAuth: options.enforceAuth,\n proxyPort: options.proxyPort,\n workerCount: options.workerCount,\n });\n },\n );\n\nprogram\n .command(\"streaming-functions\")\n .description(\"Run streaming functions\")\n .argument(\"<source-topic>\", \"Source topic configuration as JSON\")\n .argument(\"<function-file-path>\", \"Path to the function file\")\n .argument(\n \"<broker>\",\n \"Kafka broker address(es) - comma-separated for multiple brokers (e.g., 'broker1:9092, broker2:9092'). Whitespace around commas is automatically trimmed.\",\n )\n .argument(\"<max-subscriber-count>\", \"Maximum number of subscribers\")\n .option(\"--target-topic <target-topic>\", \"Target topic configuration as JSON\")\n .option(\"--sasl-username <username>\", \"SASL username\")\n .option(\"--sasl-password <password>\", \"SASL password\")\n .option(\"--sasl-mechanism <mechanism>\", \"SASL mechanism\")\n .option(\"--security-protocol <protocol>\", \"Security protocol\")\n .option(\"--log-payloads\", \"Log payloads for debugging\", false)\n .action(\n (sourceTopic, functionFilePath, broker, maxSubscriberCount, options) => {\n const config: StreamingFunctionArgs = {\n sourceTopic: JSON.parse(sourceTopic),\n targetTopic:\n options.targetTopic ? JSON.parse(options.targetTopic) : undefined,\n functionFilePath,\n broker,\n maxSubscriberCount: parseInt(maxSubscriberCount),\n logPayloads: options.logPayloads,\n saslUsername: options.saslUsername,\n saslPassword: options.saslPassword,\n saslMechanism: options.saslMechanism,\n securityProtocol: options.securityProtocol,\n };\n runStreamingFunctions(config);\n },\n );\n\nprogram\n .command(\"scripts\")\n .description(\"Run scripts\")\n .option(\"--temporal-url <url>\", \"Temporal server URL\")\n .option(\"--temporal-namespace <namespace>\", \"Temporal namespace\")\n .option(\"--client-cert <path>\", \"Path to client certificate\")\n .option(\"--client-key <path>\", \"Path to client key\")\n .option(\"--api-key <key>\", \"API key for authentication\")\n .action((options) => {\n runScripts({\n temporalConfig:\n options.temporalUrl ?\n {\n url: options.temporalUrl,\n namespace: options.temporalNamespace,\n clientCert: options.clientCert,\n clientKey: options.clientKey,\n apiKey: options.apiKey,\n }\n : undefined,\n });\n });\n\nprogram.parse();\n","import http from \"http\";\nimport * as path from \"path\";\nimport { getClickhouseClient } from \"../commons\";\nimport { MooseClient, QueryClient, getTemporalClient } from \"./helpers\";\nimport * as jose from \"jose\";\nimport { ClickHouseClient } from \"@clickhouse/client\";\nimport { Cluster } from \"../cluster-utils\";\nimport { sql } from \"../sqlHelpers\";\nimport { Client as TemporalClient } from \"@temporalio/client\";\nimport { getApis, getWebApps } from \"../dmv2/internal\";\nimport { getSourceDir, getOutDir } from \"../compiler-config\";\nimport { setupStructuredConsole } from \"../utils/structured-logging\";\n\ninterface ClickhouseConfig {\n database: string;\n host: string;\n port: string;\n username: string;\n password: string;\n useSSL: boolean;\n}\n\ninterface JwtConfig {\n secret?: string;\n issuer: string;\n audience: string;\n}\n\ninterface TemporalConfig {\n url: string;\n namespace: string;\n clientCert: string;\n clientKey: string;\n apiKey: string;\n}\n\ninterface ApisConfig {\n clickhouseConfig: ClickhouseConfig;\n jwtConfig?: JwtConfig;\n temporalConfig?: TemporalConfig;\n enforceAuth: boolean;\n proxyPort?: number;\n workerCount?: number;\n}\n\n// Convert our config to Clickhouse client config\nconst toClientConfig = (config: ClickhouseConfig) => ({\n ...config,\n useSSL: config.useSSL ? \"true\" : \"false\",\n});\n\nconst createPath = (apisDir: string, path: string) => {\n // Always use compiled JavaScript\n return `${apisDir}${path}.js`;\n};\n\nconst httpLogger = (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n startMs: number,\n apiName?: string,\n) => {\n const logFn = () =>\n console.log(\n `${req.method} ${req.url} ${res.statusCode} ${Date.now() - startMs}ms`,\n );\n\n if (apiName) {\n apiContextStorage.run({ apiName }, logFn);\n } else {\n logFn();\n }\n};\n\n// Cache stores both the module and the matched API name for structured logging\ninterface CachedApiEntry {\n module: any;\n apiName: string;\n}\nconst modulesCache = new Map<string, CachedApiEntry>();\n\n// Set up structured console logging for API context\nconst apiContextStorage = setupStructuredConsole<{ apiName: string }>(\n (ctx) => ctx.apiName,\n \"api_name\",\n);\n\nconst apiHandler = async (\n publicKey: jose.KeyLike | undefined,\n clickhouseClient: ClickHouseClient,\n temporalClient: TemporalClient | undefined,\n enforceAuth: boolean,\n jwtConfig?: JwtConfig,\n) => {\n // Always use compiled JavaScript\n const sourceDir = getSourceDir();\n const outDir = getOutDir();\n const outRoot =\n path.isAbsolute(outDir) ? outDir : path.join(process.cwd(), outDir);\n const actualApisDir = path.join(outRoot, sourceDir, \"apis\");\n\n const apis = await getApis();\n return async (req: http.IncomingMessage, res: http.ServerResponse) => {\n const start = Date.now();\n // Track matched API name for structured logging - declared outside try\n // so it's accessible in catch block for error logging context\n let matchedApiName: string | undefined;\n\n try {\n const url = new URL(req.url || \"\", \"http://localhost\");\n const fileName = url.pathname;\n\n let jwtPayload: jose.JWTPayload | undefined;\n if (publicKey && jwtConfig) {\n const jwt = req.headers.authorization?.split(\" \")[1]; // Bearer <token>\n if (jwt) {\n try {\n const { payload } = await jose.jwtVerify(jwt, publicKey, {\n issuer: jwtConfig.issuer,\n audience: jwtConfig.audience,\n });\n jwtPayload = payload;\n } catch (error) {\n console.log(\"JWT verification failed\");\n if (enforceAuth) {\n res.writeHead(401, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Unauthorized\" }));\n httpLogger(req, res, start);\n return;\n }\n }\n } else if (enforceAuth) {\n res.writeHead(401, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Unauthorized\" }));\n httpLogger(req, res, start);\n return;\n }\n } else if (enforceAuth) {\n res.writeHead(401, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Unauthorized\" }));\n httpLogger(req, res, start);\n return;\n }\n\n const pathName = createPath(actualApisDir, fileName);\n const paramsObject = Array.from(url.searchParams.entries()).reduce(\n (obj: { [key: string]: string[] | string }, [key, value]) => {\n const existingValue = obj[key];\n if (existingValue) {\n if (Array.isArray(existingValue)) {\n existingValue.push(value);\n } else {\n obj[key] = [existingValue, value];\n }\n } else {\n obj[key] = value;\n }\n return obj;\n },\n {},\n );\n\n // Include version query param in cache key to avoid collisions\n // Path-based versions (/myapi/1) are already in pathName, but query versions (?version=1) are not\n const versionParam = url.searchParams.get(\"version\");\n const cacheKey = versionParam ? `${pathName}:${versionParam}` : pathName;\n\n let userFuncModule: any;\n\n const cachedEntry = modulesCache.get(cacheKey);\n if (cachedEntry !== undefined) {\n userFuncModule = cachedEntry.module;\n matchedApiName = cachedEntry.apiName;\n } else {\n let lookupName = fileName.replace(/^\\/+|\\/+$/g, \"\");\n let version: string | null = null;\n\n // First, try to find the API by the full path (for custom paths)\n userFuncModule = apis.get(lookupName);\n if (userFuncModule) {\n // For custom path lookup, the key IS the API name\n matchedApiName = lookupName;\n }\n\n if (!userFuncModule) {\n // Fall back to the old name:version parsing\n version = url.searchParams.get(\"version\");\n\n // Check if version is in the path (e.g., /bar/1)\n if (!version && lookupName.includes(\"/\")) {\n const pathParts = lookupName.split(\"/\");\n if (pathParts.length >= 2) {\n // Treat as name/version since full path lookup already failed at line 184\n lookupName = pathParts[0];\n version = pathParts.slice(1).join(\"/\");\n }\n }\n\n // Only do versioned lookup if we still haven't found it\n if (!userFuncModule && version) {\n const versionedKey = `${lookupName}:${version}`;\n userFuncModule = apis.get(versionedKey);\n if (userFuncModule) {\n // The API name is the base name without version\n matchedApiName = lookupName;\n }\n }\n\n // Try unversioned lookup if still not found\n if (!userFuncModule) {\n userFuncModule = apis.get(lookupName);\n if (userFuncModule) {\n matchedApiName = lookupName;\n }\n }\n }\n\n if (!userFuncModule || matchedApiName === undefined) {\n const availableApis = Array.from(apis.keys()).map((key) =>\n key.replace(\":\", \"/\"),\n );\n const errorMessage =\n version ?\n `API ${lookupName} with version ${version} not found. Available APIs: ${availableApis.join(\", \")}`\n : `API ${lookupName} not found. Available APIs: ${availableApis.join(\", \")}`;\n throw new Error(errorMessage);\n }\n\n // Cache both the module and API name for future requests\n modulesCache.set(cacheKey, {\n module: userFuncModule,\n apiName: matchedApiName,\n });\n apiContextStorage.run({ apiName: matchedApiName }, () => {\n console.log(`[API] | Executing API: ${matchedApiName}`);\n });\n }\n\n const queryClient = new QueryClient(clickhouseClient, fileName);\n\n // Use matched API name for structured logging context\n // This matches source_primitive.name in the infrastructure map\n // Note: matchedApiName is guaranteed to be defined here (either from cache or lookup)\n const apiName = matchedApiName!;\n\n // Use AsyncLocalStorage to set context for this API call.\n // This avoids race conditions from concurrent API requests - each async\n // execution chain has its own isolated context value\n const result = await apiContextStorage.run({ apiName }, async () => {\n return await userFuncModule(paramsObject, {\n client: new MooseClient(queryClient, temporalClient),\n sql: sql,\n jwt: jwtPayload,\n });\n });\n let body: string;\n let status: number | undefined;\n\n // TODO investigate why these prototypes are different\n if (Object.getPrototypeOf(result).constructor.name === \"ResultSet\") {\n body = JSON.stringify(await result.json());\n } else {\n if (\"body\" in result && \"status\" in result) {\n body = JSON.stringify(result.body);\n status = result.status;\n } else {\n body = JSON.stringify(result);\n }\n }\n\n if (status) {\n res.writeHead(status, { \"Content-Type\": \"application/json\" });\n httpLogger(req, res, start, apiName);\n } else {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n httpLogger(req, res, start, apiName);\n }\n\n res.end(body);\n } catch (error: any) {\n // Log error with API context if we know which API was being called\n const logError = () => console.log(\"error in path \", req.url, error);\n if (matchedApiName) {\n apiContextStorage.run({ apiName: matchedApiName }, logError);\n } else {\n logError();\n }\n\n // todo: same workaround as ResultSet\n if (Object.getPrototypeOf(error).constructor.name === \"TypeGuardError\") {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: error.message }));\n httpLogger(req, res, start, matchedApiName);\n } else if (error?.name === \"BadRequestError\") {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify(error.toJSON?.() ?? { error: error.message }));\n httpLogger(req, res, start, matchedApiName);\n } else if (error instanceof Error) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: error.message }));\n httpLogger(req, res, start, matchedApiName);\n } else {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end();\n httpLogger(req, res, start, matchedApiName);\n }\n }\n };\n};\n\nconst createMainRouter = async (\n publicKey: jose.KeyLike | undefined,\n clickhouseClient: ClickHouseClient,\n temporalClient: TemporalClient | undefined,\n enforceAuth: boolean,\n jwtConfig?: JwtConfig,\n) => {\n const apiRequestHandler = await apiHandler(\n publicKey,\n clickhouseClient,\n temporalClient,\n enforceAuth,\n jwtConfig,\n );\n\n const webApps = await getWebApps();\n\n const sortedWebApps = Array.from(webApps.values()).sort((a, b) => {\n const pathA = a.config.mountPath || \"/\";\n const pathB = b.config.mountPath || \"/\";\n return pathB.length - pathA.length;\n });\n\n return async (req: http.IncomingMessage, res: http.ServerResponse) => {\n const start = Date.now();\n\n const url = new URL(req.url || \"\", \"http://localhost\");\n const pathname = url.pathname;\n\n // Health check - checked before all other routes\n if (pathname === \"/_moose_internal/health\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(\n JSON.stringify({\n status: \"healthy\",\n timestamp: new Date().toISOString(),\n }),\n );\n return;\n }\n\n let jwtPayload: jose.JWTPayload | undefined;\n if (publicKey && jwtConfig) {\n const jwt = req.headers.authorization?.split(\" \")[1];\n if (jwt) {\n try {\n const { payload } = await jose.jwtVerify(jwt, publicKey, {\n issuer: jwtConfig.issuer,\n audience: jwtConfig.audience,\n });\n jwtPayload = payload;\n } catch (error) {\n console.log(\"JWT verification failed for WebApp route\");\n }\n }\n }\n\n for (const webApp of sortedWebApps) {\n const mountPath = webApp.config.mountPath || \"/\";\n const normalizedMount =\n mountPath.endsWith(\"/\") && mountPath !== \"/\" ?\n mountPath.slice(0, -1)\n : mountPath;\n\n const matches =\n pathname === normalizedMount ||\n pathname.startsWith(normalizedMount + \"/\");\n\n if (matches) {\n if (webApp.config.injectMooseUtils !== false) {\n // Import getMooseUtils dynamically to avoid circular deps\n const { getMooseUtils } = await import(\"./standalone\");\n (req as any).moose = await getMooseUtils();\n }\n\n let proxiedUrl = req.url;\n if (normalizedMount !== \"/\") {\n const pathWithoutMount =\n pathname.substring(normalizedMount.length) || \"/\";\n proxiedUrl = pathWithoutMount + url.search;\n }\n\n try {\n // Create a modified request preserving all properties including headers\n // A shallow clone (like { ...req }) generally will not work since headers and other\n // members are not cloned.\n const modifiedReq = Object.assign(\n Object.create(Object.getPrototypeOf(req)),\n req,\n {\n url: proxiedUrl,\n },\n );\n await webApp.handler(modifiedReq, res);\n return;\n } catch (error) {\n console.error(`Error in WebApp ${webApp.name}:`, error);\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Internal Server Error\" }));\n }\n return;\n }\n }\n }\n\n // If no WebApp matched, check if it's an Api request\n // Strip /api or /consumption prefix for Api routing\n let apiPath = pathname;\n if (pathname.startsWith(\"/api/\")) {\n apiPath = pathname.substring(4); // Remove \"/api\"\n } else if (pathname.startsWith(\"/consumption/\")) {\n apiPath = pathname.substring(13); // Remove \"/consumption\"\n }\n\n // If we stripped a prefix, it's an Api request\n if (apiPath !== pathname) {\n // Create a modified request with the rewritten URL for the apiHandler\n // Preserve all properties including headers by using Object.assign with prototype chain\n // A shallow clone (like { ...req }) generally will not work since headers and other\n // members are not cloned.\n const modifiedReq = Object.assign(\n Object.create(Object.getPrototypeOf(req)),\n req,\n {\n url: apiPath + url.search,\n },\n );\n await apiRequestHandler(modifiedReq as http.IncomingMessage, res);\n return;\n }\n\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Not Found\" }));\n httpLogger(req, res, start);\n };\n};\n\nexport const runApis = async (config: ApisConfig) => {\n const apisCluster = new Cluster({\n maxWorkerCount:\n (config.workerCount ?? 0) > 0 ? config.workerCount : undefined,\n workerStart: async () => {\n let temporalClient: TemporalClient | undefined;\n if (config.temporalConfig) {\n temporalClient = await getTemporalClient(\n config.temporalConfig.url,\n config.temporalConfig.namespace,\n config.temporalConfig.clientCert,\n config.temporalConfig.clientKey,\n config.temporalConfig.apiKey,\n );\n }\n const clickhouseClient = getClickhouseClient(\n toClientConfig(config.clickhouseConfig),\n );\n let publicKey: jose.KeyLike | undefined;\n if (config.jwtConfig?.secret) {\n console.log(\"Importing JWT public key...\");\n publicKey = await jose.importSPKI(config.jwtConfig.secret, \"RS256\");\n }\n\n // Set runtime context for getMooseUtils() to detect\n const runtimeQueryClient = new QueryClient(clickhouseClient, \"runtime\");\n (globalThis as any)._mooseRuntimeContext = {\n client: new MooseClient(runtimeQueryClient, temporalClient),\n };\n\n const server = http.createServer(\n await createMainRouter(\n publicKey,\n clickhouseClient,\n temporalClient,\n config.enforceAuth,\n config.jwtConfig,\n ),\n );\n // port is now passed via config.proxyPort or defaults to 4001\n const port = config.proxyPort !== undefined ? config.proxyPort : 4001;\n server.listen(port, \"localhost\", () => {\n console.log(`Server running on port ${port}`);\n });\n\n return server;\n },\n workerStop: async (server) => {\n return new Promise<void>((resolve) => {\n server.close(() => resolve());\n });\n },\n });\n\n apisCluster.start();\n};\n","import cluster from \"node:cluster\";\nimport { availableParallelism } from \"node:os\";\nimport { exit } from \"node:process\";\nimport { Worker } from \"node:cluster\";\n\nconst DEFAULT_MAX_CPU_USAGE_RATIO = 0.7;\n// Time to restart the worker when it exits unexpectedly\n// This value is not too high to avoid the worker to be stuck in a bad state\n// but also not too low to avoid restarting the worker too often\nconst RESTART_TIME_MS = 10000;\nconst SIGTERM = \"SIGTERM\";\nconst SIGINT = \"SIGINT\";\nconst SHUTDOWN_WORKERS_INTERVAL = 500;\n\n/**\n * Manages a cluster of worker processes, handling their lifecycle including startup,\n * shutdown, and error handling.\n *\n * @typeParam C - The type of output produced during worker startup\n */\nexport class Cluster<C> {\n // Tracks if shutdown is currently in progress\n private shutdownInProgress: boolean = false;\n // Tracks if workers exited cleanly during shutdown\n private hasCleanWorkerExit: boolean = true;\n\n // String identifying if this is primary or worker process\n private processStr = `${cluster.isPrimary ? \"primary\" : \"worker\"} process ${process.pid}`;\n\n // Functions for starting and stopping workers\n private workerStart: (w: Worker, paralelism: number) => Promise<C>;\n private workerStop: (c: C) => Promise<void>;\n\n // Result from starting worker, needed for cleanup\n private startOutput: C | undefined;\n private maxCpuUsageRatio: number;\n private usedCpuCount: number;\n\n /**\n * Creates a new cluster manager instance.\n *\n * @param options - Configuration options for the cluster\n * @param options.workerStart - Async function to execute when starting a worker\n * @param options.workerStop - Async function to execute when stopping a worker\n * @param options.maxCpuUsageRatio - Maximum ratio of CPU cores to utilize (0-1)\n * @param options.maxWorkerCount - Maximum number of workers to spawn\n * @throws {Error} If maxCpuUsageRatio is not between 0 and 1\n */\n constructor(options: {\n workerStart: (w: Worker, paralelism: number) => Promise<C>;\n workerStop: (c: C) => Promise<void>;\n maxCpuUsageRatio?: number;\n maxWorkerCount?: number;\n }) {\n this.workerStart = options.workerStart;\n this.workerStop = options.workerStop;\n if (\n options.maxCpuUsageRatio &&\n (options.maxCpuUsageRatio > 1 || options.maxCpuUsageRatio < 0)\n ) {\n throw new Error(\"maxCpuUsageRatio must be between 0 and 1\");\n }\n this.maxCpuUsageRatio =\n options.maxCpuUsageRatio || DEFAULT_MAX_CPU_USAGE_RATIO;\n this.usedCpuCount = this.computeCPUUsageCount(\n this.maxCpuUsageRatio,\n options.maxWorkerCount,\n );\n }\n\n /**\n * Calculates the number of CPU cores to utilize based on available parallelism and constraints.\n *\n * @param cpuUsageRatio - Ratio of CPU cores to use (0-1)\n * @param maxWorkerCount - Optional maximum number of workers\n * @returns The number of CPU cores to utilize\n */\n computeCPUUsageCount(cpuUsageRatio: number, maxWorkerCount?: number) {\n const cpuCount = availableParallelism();\n const maxWorkers = maxWorkerCount || cpuCount;\n return Math.min(\n maxWorkers,\n Math.max(1, Math.floor(cpuCount * cpuUsageRatio)),\n );\n }\n\n /**\n * Initializes the cluster by spawning worker processes and setting up signal handlers.\n * For the primary process, spawns workers and monitors parent process.\n * For worker processes, executes the worker startup function.\n *\n * @throws {Error} If worker is undefined in worker process\n */\n async start() {\n process.on(SIGTERM, this.gracefulClusterShutdown(SIGTERM));\n process.on(SIGINT, this.gracefulClusterShutdown(SIGINT));\n\n if (cluster.isPrimary) {\n const parentPid = process.ppid;\n\n setInterval(() => {\n try {\n process.kill(parentPid, 0);\n } catch (e) {\n console.log(\"Parent process has exited.\");\n this.gracefulClusterShutdown(SIGTERM)();\n }\n }, 1000);\n\n await this.bootWorkers(this.usedCpuCount);\n } else {\n if (!cluster.worker) {\n throw new Error(\n \"Worker is not defined, it should be defined in worker process\",\n );\n }\n\n this.startOutput = await this.workerStart(\n cluster.worker,\n this.usedCpuCount,\n );\n }\n }\n\n /**\n * Spawns worker processes and configures their lifecycle event handlers.\n * Handles worker online, exit and disconnect events.\n * Automatically restarts failed workers during normal operation.\n *\n * @param numWorkers - Number of worker processes to spawn\n */\n bootWorkers = async (numWorkers: number) => {\n console.info(`Setting ${numWorkers} workers...`);\n\n for (let i = 0; i < numWorkers; i++) {\n cluster.fork();\n }\n\n cluster.on(\"online\", (worker) => {\n console.info(`worker process ${worker.process.pid} is online`);\n });\n\n cluster.on(\"exit\", (worker, code, signal) => {\n console.info(\n `worker ${worker.process.pid} exited with code ${code} and signal ${signal}`,\n );\n\n if (!this.shutdownInProgress) {\n setTimeout(() => cluster.fork(), RESTART_TIME_MS);\n }\n\n if (this.shutdownInProgress && code != 0) {\n this.hasCleanWorkerExit = false;\n }\n });\n\n cluster.on(\"disconnect\", (worker) => {\n console.info(`worker process ${worker.process.pid} has disconnected`);\n });\n };\n\n /**\n * Creates a handler function for graceful shutdown on receipt of a signal.\n * Ensures only one shutdown can occur at a time.\n * Handles shutdown differently for primary and worker processes.\n *\n * @param signal - The signal triggering the shutdown (e.g. SIGTERM)\n * @returns An async function that performs the shutdown\n */\n gracefulClusterShutdown = (signal: NodeJS.Signals) => async () => {\n if (this.shutdownInProgress) {\n return;\n }\n\n this.shutdownInProgress = true;\n this.hasCleanWorkerExit = true;\n\n console.info(\n `Got ${signal} on ${this.processStr}. Graceful shutdown start at ${new Date().toISOString()}`,\n );\n\n try {\n if (cluster.isPrimary) {\n await this.shutdownWorkers(signal);\n console.info(`${this.processStr} - worker shutdown successful`);\n exit(0);\n } else {\n // Only attempt to stop if the worker has finished starting\n if (this.startOutput) {\n await this.workerStop(this.startOutput);\n } else {\n console.info(\n `${this.processStr} - shutdown before worker fully started`,\n );\n }\n console.info(`${this.processStr} shutdown successful`);\n this.hasCleanWorkerExit ? exit(0) : exit(1);\n }\n } catch (e) {\n console.error(`${this.processStr} - shutdown failed`, e);\n exit(1);\n }\n };\n\n /**\n * Gracefully terminates all worker processes.\n * Monitors workers until they all exit or timeout occurs.\n * Only relevant for the primary process.\n *\n * @param signal - The signal to send to worker processes\n * @returns A promise that resolves when all workers have terminated\n */\n shutdownWorkers = (signal: NodeJS.Signals) => {\n return new Promise<void>((resolve, reject) => {\n if (!cluster.isPrimary) {\n return resolve();\n }\n\n if (!cluster.workers) {\n return resolve();\n }\n\n const workerIds = Object.keys(cluster.workers);\n if (workerIds.length == 0) {\n return resolve();\n }\n\n let workersAlive = 0;\n let funcRun = 0;\n\n const cleanWorkers = () => {\n ++funcRun;\n workersAlive = 0;\n\n Object.values(cluster.workers || {})\n .filter((worker) => !!worker)\n .forEach((worker) => {\n if (worker && !worker.isDead()) {\n ++workersAlive;\n if (funcRun == 1) {\n worker.kill(signal);\n }\n }\n });\n\n console.info(workersAlive + \" workers alive\");\n if (workersAlive == 0) {\n clearInterval(interval);\n return resolve();\n }\n };\n\n const interval = setInterval(cleanWorkers, SHUTDOWN_WORKERS_INTERVAL);\n });\n };\n}\n","import * as util from \"util\";\nimport { AsyncLocalStorage } from \"async_hooks\";\n\n/**\n * Sets up structured console logging by wrapping all console methods.\n * Returns the AsyncLocalStorage for use with .run() during execution.\n *\n * @template TContext - The type of context stored in AsyncLocalStorage\n * @param getContextField - Function to extract the identifying field from context\n * @param contextFieldName - The JSON field name for the context (e.g., \"api_name\", \"task_name\")\n * @returns The AsyncLocalStorage instance to use with .run() for setting context\n *\n * @example\n * ```ts\n * const taskContextStorage = setupStructuredConsole<{ taskName: string }>(\n * (ctx) => ctx.taskName,\n * \"task_name\"\n * );\n *\n * // Use with .run() to set context for user code execution\n * await taskContextStorage.run({ taskName: \"myTask\" }, async () => {\n * console.log(\"Hello\"); // Emits structured JSON log\n * });\n * ```\n */\nexport function setupStructuredConsole<TContext>(\n getContextField: (context: TContext) => string,\n contextFieldName: string,\n): AsyncLocalStorage<TContext> {\n const contextStorage = new AsyncLocalStorage<TContext>();\n\n const originalConsole = {\n log: console.log,\n info: console.info,\n warn: console.warn,\n error: console.error,\n debug: console.debug,\n };\n\n console.log = createStructuredConsoleWrapper(\n contextStorage,\n getContextField,\n contextFieldName,\n originalConsole.log,\n \"info\",\n );\n console.info = createStructuredConsoleWrapper(\n contextStorage,\n getContextField,\n contextFieldName,\n originalConsole.info,\n \"info\",\n );\n console.warn = createStructuredConsoleWrapper(\n contextStorage,\n getContextField,\n contextFieldName,\n originalConsole.warn,\n \"warn\",\n );\n console.error = createStructuredConsoleWrapper(\n contextStorage,\n getContextField,\n contextFieldName,\n originalConsole.error,\n \"error\",\n );\n console.debug = createStructuredConsoleWrapper(\n contextStorage,\n getContextField,\n contextFieldName,\n originalConsole.debug,\n \"debug\",\n );\n\n return contextStorage;\n}\n\n/**\n * Directly emits a structured log if currently in a context, without formatting.\n * Returns true if structured log was emitted, false if not in context.\n *\n * This is useful for framework code that needs to emit structured logs\n * but doesn't go through console.log() wrapper (e.g., Temporal's logger).\n *\n * @template TContext - The type of context stored in AsyncLocalStorage\n * @param contextStorage - The AsyncLocalStorage instance containing execution context\n * @param getContextField - Function to extract the identifying field from context\n * @param contextFieldName - The JSON field name for the context (e.g., \"task_name\")\n * @param level - The log level (info, warn, error, debug)\n * @param message - The log message\n * @returns true if structured log was emitted, false otherwise\n */\nexport function emitStructuredLog<TContext>(\n contextStorage: AsyncLocalStorage<TContext>,\n getContextField: (context: TContext) => string,\n contextFieldName: string,\n level: string,\n message: string,\n): boolean {\n const context = contextStorage.getStore();\n if (!context) {\n return false;\n }\n\n let ctxValue: string;\n try {\n ctxValue = getContextField(context);\n } catch {\n ctxValue = \"unknown\";\n }\n\n try {\n process.stderr.write(\n JSON.stringify({\n __moose_structured_log__: true,\n level,\n message,\n [contextFieldName]: ctxValue,\n timestamp: new Date().toISOString(),\n }) + \"\\n\",\n );\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Safely serializes a value to a string, handling circular references, BigInt, Symbols, and Error objects.\n *\n * This function:\n * - Preserves Error objects (message and stack) via util.inspect\n * - Attempts JSON.stringify for plain objects, then falls back to util.inspect\n * - Uses util.inspect for non-object types (Symbols, functions, etc.)\n *\n * @param arg - The value to serialize (can be any type)\n * @returns A string representation of the value\n */\nfunction safeStringify(arg: unknown): string {\n if (typeof arg === \"object\" && arg !== null) {\n // Special-case Error objects: JSON.stringify(new Error(\"x\")) returns \"{}\"\n // Use util.inspect to preserve message and stack trace\n if (arg instanceof Error) {\n return util.inspect(arg, { depth: 2, breakLength: Infinity });\n }\n try {\n return JSON.stringify(arg);\n } catch (e) {\n // Fall back to util.inspect for circular references or BigInt\n return util.inspect(arg, { depth: 2, breakLength: Infinity });\n }\n }\n // Return strings directly without util.inspect to avoid unwanted quotes\n if (typeof arg === \"string\") {\n return arg;\n }\n // Use util.inspect for all other non-object types to handle Symbols, functions, etc.\n // String(Symbol()) throws TypeError, but util.inspect handles it correctly\n return util.inspect(arg);\n}\n\n/**\n * Creates a structured console wrapper that emits JSON logs when in a context.\n *\n * This factory function creates a console method wrapper that:\n * - Checks if the current execution is within a context (using AsyncLocalStorage)\n * - If in context: emits a structured JSON log to stderr\n * - If not in context: delegates to the original console method\n *\n * This pattern ensures structured logs are only emitted when running user code\n * (APIs, streaming functions, workflow tasks), while preserving normal console\n * behavior for framework code.\n *\n * @template TContext - The type of context stored in AsyncLocalStorage\n * @param contextStorage - The AsyncLocalStorage instance containing execution context\n * @param getContextField - Function to extract the identifying field from context\n * @param contextFieldName - The JSON field name for the context (e.g., \"api_name\")\n * @param originalMethod - The original console method to call when not in context\n * @param level - The log level (info, warn, error, debug)\n * @returns A wrapped console method that emits structured logs\n */\nexport function createStructuredConsoleWrapper<TContext>(\n contextStorage: AsyncLocalStorage<TContext>,\n getContextField: (context: TContext) => string,\n contextFieldName: string,\n originalMethod: (...args: unknown[]) => void,\n level: string,\n) {\n return (...args: unknown[]) => {\n const context = contextStorage.getStore();\n if (!context) {\n originalMethod(...args);\n return;\n }\n\n // Safely extract context field - never throws\n let ctxValue: string;\n try {\n ctxValue = getContextField(context);\n } catch {\n ctxValue = \"unknown\";\n }\n\n // Emit structured log, fall back to original on any failure\n try {\n const message = args.map((arg) => safeStringify(arg)).join(\" \");\n process.stderr.write(\n JSON.stringify({\n __moose_structured_log__: true,\n level,\n message,\n [contextFieldName]: ctxValue,\n timestamp: new Date().toISOString(),\n }) + \"\\n\",\n );\n } catch {\n originalMethod(...args);\n }\n };\n}\n","import { Readable } from \"node:stream\";\nimport { KafkaJS } from \"@514labs/kafka-javascript\";\nconst { Kafka } = KafkaJS;\n\ntype Consumer = KafkaJS.Consumer;\ntype Producer = KafkaJS.Producer;\n\ntype KafkaMessage = {\n value: Buffer | string | null;\n key?: Buffer | string | null;\n partition?: number;\n offset?: string;\n timestamp?: string;\n headers?: Record<string, Buffer | string | undefined>;\n};\n\ntype SASLOptions = {\n mechanism: \"plain\" | \"scram-sha-256\" | \"scram-sha-512\";\n username: string;\n password: string;\n};\nimport { Buffer } from \"node:buffer\";\nimport * as process from \"node:process\";\nimport * as http from \"node:http\";\nimport {\n cliLog,\n getKafkaClient,\n createProducerConfig,\n Logger,\n logError,\n} from \"../commons\";\nimport { Cluster } from \"../cluster-utils\";\nimport { getStreamingFunctions } from \"../dmv2/internal\";\nimport type { ConsumerConfig, TransformConfig, DeadLetterQueue } from \"../dmv2\";\nimport {\n buildFieldMutationsFromColumns,\n mutateParsedJson,\n type FieldMutations,\n} from \"../utilities/json\";\nimport type { Column } from \"../dataModels/dataModelTypes\";\nimport { setupStructuredConsole } from \"../utils/structured-logging\";\n\nconst HOSTNAME = process.env.HOSTNAME;\nconst AUTO_COMMIT_INTERVAL_MS = 5000;\nconst PARTITIONS_CONSUMED_CONCURRENTLY = 3;\nconst MAX_RETRIES_CONSUMER = 150;\nconst SESSION_TIMEOUT_CONSUMER = 30000;\nconst HEARTBEAT_INTERVAL_CONSUMER = 3000;\nconst DEFAULT_MAX_STREAMING_CONCURRENCY = 100;\n// Max messages per eachBatch call - Confluent client defaults to 32, increase for throughput\nconst CONSUMER_MAX_BATCH_SIZE = 1000;\n\n// Set up structured console logging for streaming function context\nconst functionContextStorage = setupStructuredConsole<{ functionName: string }>(\n (ctx) => ctx.functionName,\n \"function_name\",\n);\n\n/**\n * Data structure for metrics logging containing counts and metadata\n */\ntype MetricsData = {\n count_in: number;\n count_out: number;\n bytes: number;\n function_name: string;\n timestamp: Date;\n};\n\n/**\n * Interface for tracking message processing metrics\n */\ninterface Metrics {\n count_in: number;\n count_out: number;\n bytes: number;\n}\n\n/**\n * Type definition for streaming transformation function\n */\ntype StreamingFunction = (data: unknown) => unknown | Promise<unknown>;\n\n/**\n * Simplified Kafka message type containing only value\n */\ntype KafkaMessageWithLineage = {\n value: string;\n originalValue: object;\n originalMessage: KafkaMessage;\n dlq?: DeadLetterQueue<any>;\n};\n\n/**\n * Configuration interface for Kafka topics including namespace and version support\n */\nexport interface TopicConfig {\n name: string; // Full topic name including namespace if present\n partitions: number;\n retention_ms: number;\n max_message_bytes: number;\n namespace?: string;\n version?: string;\n}\n\n/**\n * Configuration interface for streaming function arguments\n */\nexport interface StreamingFunctionArgs {\n sourceTopic: TopicConfig;\n targetTopic?: TopicConfig;\n functionFilePath: string;\n broker: string; // Comma-separated list of Kafka broker addresses (e.g., \"broker1:9092, broker2:9092\"). Whitespace around commas is automatically trimmed.\n maxSubscriberCount: number;\n logPayloads?: boolean;\n saslUsername?: string;\n saslPassword?: string;\n saslMechanism?: string;\n securityProtocol?: string;\n}\n\n/**\n * Maximum number of concurrent streaming operations, configurable via environment\n */\nconst MAX_STREAMING_CONCURRENCY =\n process.env.MAX_STREAMING_CONCURRENCY ?\n parseInt(process.env.MAX_STREAMING_CONCURRENCY, 10)\n : DEFAULT_MAX_STREAMING_CONCURRENCY;\n\n/**\n * Logs metrics data to HTTP endpoint\n */\nexport const metricsLog: (log: MetricsData) => void = (log) => {\n const req = http.request({\n port: parseInt(process.env.MOOSE_MANAGEMENT_PORT ?? \"5001\", 10),\n method: \"POST\",\n path: \"/metrics-logs\",\n });\n\n req.on(\"error\", (err: Error) => {\n console.log(\n `Error ${err.name} sending metrics to management port.`,\n err.message,\n );\n });\n\n req.write(JSON.stringify({ ...log }));\n req.end();\n};\n\n/**\n * Initializes and connects Kafka producer\n */\nconst startProducer = async (\n logger: Logger,\n producer: Producer,\n): Promise<void> => {\n try {\n logger.log(\"Connecting producer...\");\n await producer.connect();\n logger.log(\"Producer is running...\");\n } catch (error) {\n logger.error(\"Failed to connect producer:\");\n if (error instanceof Error) {\n logError(logger, error);\n }\n throw error;\n }\n};\n\n/**\n * Disconnects a Kafka producer and logs the shutdown\n *\n * @param logger - Logger instance for outputting producer status\n * @param producer - KafkaJS Producer instance to disconnect\n * @returns Promise that resolves when producer is disconnected\n * @example\n * ```ts\n * await stopProducer(logger, producer); // Disconnects producer and logs shutdown\n * ```\n */\nconst stopProducer = async (\n logger: Logger,\n producer: Producer,\n): Promise<void> => {\n await producer.disconnect();\n logger.log(\"Producer is shutting down...\");\n};\n\n/**\n * Gracefully stops a Kafka consumer by pausing all partitions and then disconnecting\n *\n * @param logger - Logger instance for outputting consumer status\n * @param consumer - KafkaJS Consumer instance to disconnect\n * @param sourceTopic - Topic configuration containing name and partition count\n * @returns Promise that resolves when consumer is disconnected\n * @example\n * ```ts\n * await stopConsumer(logger, consumer, sourceTopic); // Pauses all partitions and disconnects consumer\n * ```\n */\nconst stopConsumer = async (\n logger: Logger,\n consumer: Consumer,\n sourceTopic: TopicConfig,\n): Promise<void> => {\n try {\n // Try to pause the consumer first if the method exists\n logger.log(\"Pausing consumer...\");\n\n // Generate partition numbers array based on the topic's partition count\n const partitionNumbers = Array.from(\n { length: sourceTopic.partitions },\n (_, i) => i,\n );\n\n await consumer.pause([\n {\n topic: sourceTopic.name,\n partitions: partitionNumbers,\n },\n ]);\n\n logger.log(\"Disconnecting consumer...\");\n await consumer.disconnect();\n logger.log(\"Consumer is shutting down...\");\n } catch (error) {\n logger.error(`Error during consumer shutdown: ${error}`);\n // Continue with disconnect even if pause fails\n try {\n await consumer.disconnect();\n logger.log(\"Consumer disconnected after error\");\n } catch (disconnectError) {\n logger.error(`Failed to disconnect consumer: ${disconnectError}`);\n }\n }\n};\n\n/**\n * Processes a single Kafka message through a streaming function and returns transformed message(s)\n *\n * @param logger - Logger instance for outputting message processing status and errors\n * @param streamingFunctionWithConfigList - functions (with their configs) that transforms input message data\n * @param message - Kafka message to be processed\n * @param producer - Kafka producer for sending dead letter\n * @param fieldMutations - Pre-built field mutations for data transformations\n * @returns Promise resolving to array of transformed messages or undefined if processing fails\n *\n * The function will:\n * 1. Check for null/undefined message values\n * 2. Parse the message value as JSON\n * 3. Apply field mutations (e.g., date parsing) using pre-built configuration\n * 4. Pass parsed data through the streaming function\n * 5. Convert transformed data back to string format\n * 6. Handle both single and array return values\n * 7. Log any processing errors\n */\nconst handleMessage = async (\n logger: Logger,\n // Note: TransformConfig<any> is intentionally generic here as it handles\n // various data model types that are determined at runtime\n streamingFunctionWithConfigList: [StreamingFunction, TransformConfig<any>][],\n message: KafkaMessage,\n producer: Producer,\n fieldMutations?: FieldMutations,\n logPayloads?: boolean,\n): Promise<KafkaMessageWithLineage[] | undefined> => {\n if (message.value === undefined || message.value === null) {\n logger.log(`Received message with no value, skipping...`);\n return undefined;\n }\n\n try {\n // Detect Schema Registry JSON envelope: 0x00 + 4-byte schema ID (big-endian) + JSON bytes\n let payloadBuffer = message.value as Buffer;\n if (\n payloadBuffer &&\n payloadBuffer.length >= 5 &&\n payloadBuffer[0] === 0x00\n ) {\n payloadBuffer = payloadBuffer.subarray(5);\n }\n // Parse JSON then apply field mutations using pre-built configuration\n const parsedData = JSON.parse(payloadBuffer.toString());\n mutateParsedJson(parsedData, fieldMutations);\n\n // Log payload before transformation if enabled\n if (logPayloads) {\n logger.log(`[PAYLOAD:STREAM_IN] ${JSON.stringify(parsedData)}`);\n }\n\n // Context is already set at batch level via functionContextStorage.run()\n const transformedData = await Promise.all(\n streamingFunctionWithConfigList.map(async ([fn, config]) => {\n try {\n return await fn(parsedData);\n } catch (e) {\n // Check if there's a deadLetterQueue configured\n const deadLetterQueue = config.deadLetterQueue;\n\n if (deadLetterQueue) {\n // Create a dead letter record\n const deadLetterRecord = {\n originalRecord: {\n ...parsedData,\n // Include original Kafka message metadata\n __sourcePartition: message.partition,\n __sourceOffset: message.offset,\n __sourceTimestamp: message.timestamp,\n },\n errorMessage: e instanceof Error ? e.message : String(e),\n errorType: e instanceof Error ? e.constructor.name : \"Unknown\",\n failedAt: new Date(),\n source: \"transform\",\n };\n\n cliLog({\n action: \"DeadLetter\",\n message: `Sending message to DLQ ${deadLetterQueue.name}: ${e instanceof Error ? e.message : String(e)}`,\n message_type: \"Error\",\n });\n // Send to the DLQ\n try {\n await producer.send({\n topic: deadLetterQueue.name,\n messages: [{ value: JSON.stringify(deadLetterRecord) }],\n });\n } catch (dlqError) {\n logger.error(`Failed to send to dead letter queue: ${dlqError}`);\n }\n } else {\n // No DLQ configured, just log the error\n cliLog({\n action: \"Function\",\n message: `Error processing message (no DLQ configured): ${e instanceof Error ? e.message : String(e)}`,\n message_type: \"Error\",\n });\n }\n\n // rethrow for the outside error handling\n throw e;\n }\n }),\n );\n\n const processedMessages = transformedData\n .map((userFunctionOutput, i) => {\n const [_, config] = streamingFunctionWithConfigList[i];\n if (userFunctionOutput) {\n if (Array.isArray(userFunctionOutput)) {\n // We Promise.all streamingFunctionWithConfigList above.\n // Promise.all always wraps results in an array, even for single transforms.\n // When a transform returns an array (e.g., [msg1, msg2] to emit multiple messages),\n // we get [[msg1, msg2]]. flat() unwraps one level so each item becomes its own message.\n // Without flat(), the entire array would be JSON.stringify'd as a single message.\n return userFunctionOutput\n .flat()\n .filter((item) => item !== undefined && item !== null)\n .map((item) => ({\n value: JSON.stringify(item),\n originalValue: parsedData,\n originalMessage: message,\n dlq: config.deadLetterQueue ?? undefined,\n }));\n } else {\n return [\n {\n value: JSON.stringify(userFunctionOutput),\n originalValue: parsedData,\n originalMessage: message,\n dlq: config.deadLetterQueue ?? undefined,\n },\n ];\n }\n }\n })\n .flat()\n .filter((item) => item !== undefined && item !== null);\n\n // Log payload after transformation if enabled (what we're actually sending to Kafka)\n if (logPayloads) {\n if (processedMessages.length > 0) {\n // msg.value is already JSON stringified, just construct array format\n const outgoingJsonStrings = processedMessages.map((msg) => msg.value);\n logger.log(`[PAYLOAD:STREAM_OUT] [${outgoingJsonStrings.join(\",\")}]`);\n } else {\n logger.log(`[PAYLOAD:STREAM_OUT] (no output from streaming function)`);\n }\n }\n\n return processedMessages;\n } catch (e) {\n // TODO: Track failure rate\n logger.error(`Failed to transform data`);\n if (e instanceof Error) {\n logError(logger, e);\n }\n }\n\n return undefined;\n};\n\n/**\n * Handles sending failed messages to their configured Dead Letter Queues\n *\n * @param logger - Logger instance for outputting DLQ status\n * @param producer - Kafka producer for sending to DLQ topics\n * @param messages - Array of failed messages with DLQ configuration\n * @param error - The error that caused the failure\n * @returns true if ALL messages were successfully sent to their DLQs, false otherwise\n */\nconst handleDLQForFailedMessages = async (\n logger: Logger,\n producer: Producer,\n messages: KafkaMessageWithLineage[],\n error: unknown,\n): Promise<boolean> => {\n let messagesHandledByDLQ = 0;\n let messagesWithoutDLQ = 0;\n let dlqErrors = 0;\n\n for (const msg of messages) {\n if (msg.dlq && msg.originalValue) {\n const deadLetterRecord = {\n originalRecord: {\n ...msg.originalValue,\n // Include original Kafka message metadata\n __sourcePartition: msg.originalMessage.partition,\n __sourceOffset: msg.originalMessage.offset,\n __sourceTimestamp: msg.originalMessage.timestamp,\n },\n errorMessage: error instanceof Error ? error.message : String(error),\n errorType: error instanceof Error ? error.constructor.name : \"Unknown\",\n failedAt: new Date(),\n source: \"transform\",\n };\n\n cliLog({\n action: \"DeadLetter\",\n message: `Sending failed message to DLQ ${msg.dlq.name}: ${error instanceof Error ? error.message : String(error)}`,\n message_type: \"Error\",\n });\n\n try {\n await producer.send({\n topic: msg.dlq.name,\n messages: [{ value: JSON.stringify(deadLetterRecord) }],\n });\n logger.log(`Sent failed message to DLQ ${msg.dlq.name}`);\n messagesHandledByDLQ++;\n } catch (dlqError) {\n logger.error(`Failed to send to DLQ: ${dlqError}`);\n dlqErrors++;\n }\n } else if (!msg.dlq) {\n messagesWithoutDLQ++;\n logger.warn(`Cannot send to DLQ: no DLQ configured for message`);\n } else {\n messagesWithoutDLQ++;\n logger.warn(`Cannot send to DLQ: original message value not available`);\n }\n }\n\n // Check if ALL messages were successfully handled by DLQ\n const allMessagesHandled =\n messagesHandledByDLQ === messages.length &&\n messagesWithoutDLQ === 0 &&\n dlqErrors === 0;\n\n if (allMessagesHandled) {\n logger.log(\n `All ${messagesHandledByDLQ} failed message(s) sent to DLQ, suppressing original error`,\n );\n } else if (messagesHandledByDLQ > 0) {\n // Log summary of partial DLQ handling\n logger.warn(\n `Partial DLQ success: ${messagesHandledByDLQ}/${messages.length} message(s) sent to DLQ`,\n );\n if (messagesWithoutDLQ > 0) {\n logger.error(\n `Cannot handle batch failure: ${messagesWithoutDLQ} message(s) have no DLQ configured or missing original value`,\n );\n }\n if (dlqErrors > 0) {\n logger.error(`${dlqErrors} message(s) failed to send to DLQ`);\n }\n }\n\n return allMessagesHandled;\n};\n\n/**\n * Sends processed messages to a target Kafka topic\n *\n * @param logger - Logger instance for outputting send status and errors\n * @param metrics - Metrics object for tracking message counts and bytes sent\n * @param targetTopic - Target topic configuration\n * @param producer - Kafka producer instance for sending messages\n * @param messages - Array of processed messages to send (messages carry their own DLQ config)\n * @returns Promise that resolves when all messages are sent\n *\n * The Confluent Kafka library handles batching internally via message.max.bytes\n * and retries transient failures automatically. This function simply sends all\n * messages and handles permanent failures by routing to DLQ.\n */\nconst sendMessages = async (\n logger: Logger,\n metrics: Metrics,\n targetTopic: TopicConfig,\n producer: Producer,\n messages: KafkaMessageWithLineage[],\n): Promise<void> => {\n if (messages.length === 0) return;\n\n try {\n // Library handles batching and retries internally\n await producer.send({\n topic: targetTopic.name,\n messages: messages,\n });\n\n // Track metrics only after successful send to target topic\n // Messages routed to DLQ should NOT be counted as successful sends\n for (const msg of messages) {\n metrics.bytes += Buffer.byteLength(msg.value, \"utf8\");\n }\n metrics.count_out += messages.length;\n\n logger.log(`Sent ${messages.length} messages to ${targetTopic.name}`);\n } catch (e) {\n // Library already retried - this is a permanent failure\n logger.error(`Failed to send transformed data`);\n if (e instanceof Error) {\n logError(logger, e);\n }\n\n // Handle DLQ for failed messages\n // Only throw if not all messages were successfully routed to DLQ\n const allHandledByDLQ = await handleDLQForFailedMessages(\n logger,\n producer,\n messages,\n e,\n );\n if (!allHandledByDLQ) {\n throw e;\n }\n }\n};\n\n/**\n * Periodically sends metrics about message processing to a metrics logging endpoint.\n * Resets metrics counters after each send. Runs every second via setTimeout.\n *\n * @param logger - Logger instance containing the function name prefix\n * @param metrics - Metrics object tracking message counts and bytes processed\n * @example\n * ```ts\n * const metrics = { count_in: 10, count_out: 8, bytes: 1024 };\n * sendMessageMetrics(logger, metrics); // Sends metrics and resets counters\n * ```\n */\nconst sendMessageMetrics = (logger: Logger, metrics: Metrics) => {\n if (metrics.count_in > 0 || metrics.count_out > 0 || metrics.bytes > 0) {\n metricsLog({\n count_in: metrics.count_in,\n count_out: metrics.count_out,\n function_name: logger.logPrefix,\n bytes: metrics.bytes,\n timestamp: new Date(),\n });\n }\n metrics.count_in = 0;\n metrics.bytes = 0;\n metrics.count_out = 0;\n setTimeout(() => sendMessageMetrics(logger, metrics), 1000);\n};\n\nasync function loadStreamingFunction(\n sourceTopic: TopicConfig,\n targetTopic?: TopicConfig,\n): Promise<{\n functions: [StreamingFunction, TransformConfig<any> | ConsumerConfig<any>][];\n fieldMutations: FieldMutations | undefined;\n}> {\n const transformFunctions = await getStreamingFunctions();\n const transformFunctionKey = `${topicNameToStreamName(sourceTopic)}_${targetTopic ? topicNameToStreamName(targetTopic) : \"<no-target>\"}`;\n\n const matchingEntries = Array.from(transformFunctions.entries()).filter(\n ([key]) => key.startsWith(transformFunctionKey),\n );\n\n if (matchingEntries.length === 0) {\n const message = `No functions found for ${transformFunctionKey}`;\n cliLog({\n action: \"Function\",\n message: `${message}`,\n message_type: \"Error\",\n });\n throw new Error(message);\n }\n\n // Extract functions and configs, and get columns from the first entry\n // (all functions for the same source topic will have the same columns)\n const functions = matchingEntries.map(([_, [fn, config]]) => [\n fn,\n config,\n ]) as [StreamingFunction, TransformConfig<any> | ConsumerConfig<any>][];\n const [_key, firstEntry] = matchingEntries[0];\n const sourceColumns = firstEntry[2];\n\n // Pre-build field mutations once for all messages\n const fieldMutations = buildFieldMutationsFromColumns(sourceColumns);\n\n return { functions, fieldMutations };\n}\n\n/**\n * Initializes and starts a Kafka consumer that processes messages using a streaming function\n *\n * @param logger - Logger instance for outputting consumer status and errors\n * @param metrics - Metrics object for tracking message counts and bytes processed\n * @param parallelism - Number of parallel workers processing messages\n * @param args - Configuration arguments for source/target topics and streaming function\n * @param consumer - KafkaJS Consumer instance\n * @param producer - KafkaJS Producer instance for sending processed messages\n * @param streamingFuncId - Unique identifier for this consumer group\n * @param maxMessageSize - Maximum message size in bytes allowed by Kafka broker\n * @returns Promise that resolves when consumer is started\n *\n * The consumer will:\n * 1. Connect to Kafka\n * 2. Subscribe to the source topic\n * 3. Process messages in batches using the streaming function\n * 4. Send processed messages to target topic (if configured)\n * 5. Commit offsets after successful processing\n */\nconst startConsumer = async (\n args: StreamingFunctionArgs,\n logger: Logger,\n metrics: Metrics,\n _parallelism: number,\n consumer: Consumer,\n producer: Producer,\n streamingFuncId: string,\n): Promise<void> => {\n // Validate topic configurations\n validateTopicConfig(args.sourceTopic);\n if (args.targetTopic) {\n validateTopicConfig(args.targetTopic);\n }\n\n try {\n logger.log(\"Connecting consumer...\");\n await consumer.connect();\n logger.log(\"Consumer connected successfully\");\n } catch (error) {\n logger.error(\"Failed to connect consumer:\");\n if (error instanceof Error) {\n logError(logger, error);\n }\n throw error;\n }\n\n logger.log(\n `Starting consumer group '${streamingFuncId}' with source topic: ${args.sourceTopic.name} and target topic: ${args.targetTopic?.name || \"none\"}`,\n );\n\n // We preload the function to not have to load it for each message\n // Note: Config types use 'any' as generics because they handle various\n // data model types determined at runtime, not compile time\n // Since dmv1 was removed, always use loadStreamingFunction which loads\n // transforms from the registry (populated via addTransform() calls).\n // The loadIndex() inside getStreamingFunctions() handles compiled vs\n // non-compiled mode internally.\n const result = await loadStreamingFunction(\n args.sourceTopic,\n args.targetTopic,\n );\n const streamingFunctions = result.functions;\n const fieldMutations = result.fieldMutations;\n\n await consumer.subscribe({\n topics: [args.sourceTopic.name], // Use full topic name for Kafka operations\n });\n\n await consumer.run({\n eachBatchAutoResolve: true,\n // Enable parallel processing of partitions\n partitionsConsumedConcurrently: PARTITIONS_CONSUMED_CONCURRENTLY, // To be adjusted\n eachBatch: async ({ batch, heartbeat, isRunning, isStale }) => {\n if (!isRunning() || isStale()) {\n return;\n }\n\n // Get function name for structured logging context\n const functionName = logger.logPrefix;\n\n // Wrap entire batch processing in context so all logs have resource_name\n await functionContextStorage.run({ functionName }, async () => {\n metrics.count_in += batch.messages.length;\n\n cliLog({\n action: \"Received\",\n message: `${logger.logPrefix.replace(\"__\", \" -> \")} ${batch.messages.length} message(s)`,\n });\n logger.log(`Received ${batch.messages.length} message(s)`);\n\n let index = 0;\n const readableStream = Readable.from(batch.messages);\n\n const processedMessages: (KafkaMessageWithLineage[] | undefined)[] =\n await readableStream\n .map(\n async (message) => {\n index++;\n if (\n (batch.messages.length > DEFAULT_MAX_STREAMING_CONCURRENCY &&\n index % DEFAULT_MAX_STREAMING_CONCURRENCY) ||\n index - 1 === batch.messages.length\n ) {\n await heartbeat();\n }\n return handleMessage(\n logger,\n streamingFunctions,\n message,\n producer,\n fieldMutations,\n args.logPayloads,\n );\n },\n {\n concurrency: MAX_STREAMING_CONCURRENCY,\n },\n )\n .toArray();\n\n const filteredMessages = processedMessages\n .flat()\n .filter((msg) => msg !== undefined && msg.value !== undefined);\n\n if (args.targetTopic === undefined || processedMessages.length === 0) {\n return;\n }\n\n await heartbeat();\n\n if (filteredMessages.length > 0) {\n // Messages now carry their own DLQ configuration in the lineage\n await sendMessages(\n logger,\n metrics,\n args.targetTopic,\n producer,\n filteredMessages as KafkaMessageWithLineage[],\n );\n }\n });\n },\n });\n\n logger.log(\"Consumer is running...\");\n};\n\n/**\n * Creates a Logger instance that prefixes all log messages with the source and target topic\n *\n * @param args - The streaming function arguments containing source and target topics\n * @returns A Logger instance with standard log, error and warn methods\n *\n * The logPrefix is set to match source_primitive.name in the infrastructure map:\n * - For transforms: `{source}__{target}` (double underscore)\n * - For consumers: just `{source}` (no suffix)\n *\n * This ensures structured logs can be correlated with the infrastructure map.\n *\n * @example\n * ```ts\n * const logger = buildLogger({sourceTopic: {..., name: 'events'}, targetTopic: {..., name: 'processed'}}, 0);\n * logger.log('message'); // Outputs: \"events__processed (worker 0): message\"\n * ```\n */\nconst buildLogger = (args: StreamingFunctionArgs, workerId: number): Logger => {\n // Get base stream names without namespace prefix or version suffix\n const sourceBaseName = topicNameToStreamName(args.sourceTopic);\n const targetBaseName =\n args.targetTopic ? topicNameToStreamName(args.targetTopic) : undefined;\n\n // Function name matches source_primitive.name in infrastructure map\n // Uses double underscore separator for transforms, plain name for consumers\n const functionName =\n targetBaseName ? `${sourceBaseName}__${targetBaseName}` : sourceBaseName;\n\n // Human-readable log prefix includes worker ID for debugging\n const logPrefix = `${functionName} (worker ${workerId})`;\n\n return {\n // logPrefix is used for structured logging (function_name field)\n // Must match source_primitive.name format for log correlation\n logPrefix: functionName,\n log: (message: string): void => {\n console.log(`${logPrefix}: ${message}`);\n },\n error: (message: string): void => {\n console.error(`${logPrefix}: ${message}`);\n },\n warn: (message: string): void => {\n console.warn(`${logPrefix}: ${message}`);\n },\n };\n};\n\n/**\n * Formats a version string into a topic suffix format by replacing dots with underscores\n * Example: \"1.2.3\" -> \"_1_2_3\"\n */\nexport function formatVersionSuffix(version: string): string {\n return `_${version.replace(/\\./g, \"_\")}`;\n}\n\n/**\n * Transforms a topic name by removing namespace prefix and version suffix\n * to get the base stream name for function mapping\n */\nexport function topicNameToStreamName(config: TopicConfig): string {\n let name = config.name;\n\n // Handle version suffix if present\n if (config.version) {\n const versionSuffix = formatVersionSuffix(config.version);\n if (name.endsWith(versionSuffix)) {\n name = name.slice(0, -versionSuffix.length);\n } else {\n throw new Error(\n `Version suffix ${versionSuffix} not found in topic name ${name}`,\n );\n }\n }\n\n // Handle namespace prefix if present\n if (config.namespace && config.namespace !== \"\") {\n const prefix = `${config.namespace}.`;\n if (name.startsWith(prefix)) {\n name = name.slice(prefix.length);\n } else {\n throw new Error(\n `Namespace prefix ${prefix} not found in topic name ${name}`,\n );\n }\n }\n\n return name;\n}\n\n/**\n * Validates a topic configuration for proper namespace and version formatting\n */\nexport function validateTopicConfig(config: TopicConfig): void {\n if (config.namespace && !config.name.startsWith(`${config.namespace}.`)) {\n throw new Error(\n `Topic name ${config.name} must start with namespace ${config.namespace}`,\n );\n }\n\n if (config.version) {\n const versionSuffix = formatVersionSuffix(config.version);\n if (!config.name.endsWith(versionSuffix)) {\n throw new Error(\n `Topic name ${config.name} must end with version ${config.version}`,\n );\n }\n }\n}\n\n/**\n * Initializes and runs a clustered streaming function system that processes messages from Kafka\n *\n * This function:\n * 1. Creates a cluster of workers to handle Kafka message processing\n * 2. Sets up Kafka producers and consumers for each worker\n * 3. Configures logging and metrics collection\n * 4. Handles graceful shutdown on termination\n *\n * The system supports:\n * - Multiple workers processing messages in parallel\n * - Dynamic CPU usage control via maxCpuUsageRatio\n * - SASL authentication for Kafka\n * - Metrics tracking for message counts and bytes processed\n * - Graceful shutdown of Kafka connections\n *\n * @returns Promise that resolves when the cluster is started\n * @throws Will log errors if Kafka connections fail\n *\n * @example\n * ```ts\n * await runStreamingFunctions({\n * sourceTopic: { name: 'source', partitions: 3, retentionPeriod: 86400, maxMessageBytes: 1048576 },\n * targetTopic: { name: 'target', partitions: 3, retentionPeriod: 86400, maxMessageBytes: 1048576 },\n * functionFilePath: './transform.js',\n * broker: 'localhost:9092',\n * maxSubscriberCount: 3\n * }); // Starts the streaming function cluster\n * ```\n */\nexport const runStreamingFunctions = async (\n args: StreamingFunctionArgs,\n): Promise<void> => {\n // Validate topic configurations at startup\n validateTopicConfig(args.sourceTopic);\n if (args.targetTopic) {\n validateTopicConfig(args.targetTopic);\n }\n\n // Use base stream names (without namespace/version) for function ID\n // We use flow- instead of function- because that's what the ACLs in boreal are linked with\n // When migrating - make sure the ACLs are updated to use the new prefix.\n const streamingFuncId = `flow-${args.sourceTopic.name}-${args.targetTopic?.name || \"\"}`;\n\n const cluster = new Cluster({\n maxCpuUsageRatio: 0.5,\n maxWorkerCount: args.maxSubscriberCount,\n workerStart: async (worker, parallelism) => {\n const logger = buildLogger(args, worker.id);\n const functionName = logger.logPrefix;\n\n // Wrap entire startup in context so all logs have function_name\n return await functionContextStorage.run({ functionName }, async () => {\n const metrics = {\n count_in: 0,\n count_out: 0,\n bytes: 0,\n };\n\n setTimeout(() => sendMessageMetrics(logger, metrics), 1000);\n\n const clientIdPrefix = HOSTNAME ? `${HOSTNAME}-` : \"\";\n const processId = `${clientIdPrefix}${streamingFuncId}-ts-${worker.id}`;\n\n const kafka = await getKafkaClient(\n {\n clientId: processId,\n broker: args.broker,\n securityProtocol: args.securityProtocol,\n saslUsername: args.saslUsername,\n saslPassword: args.saslPassword,\n saslMechanism: args.saslMechanism,\n },\n logger,\n );\n\n // Note: \"js.consumer.max.batch.size\" is a librdkafka native config not in TS types\n const consumer: Consumer = kafka.consumer({\n kafkaJS: {\n groupId: streamingFuncId,\n sessionTimeout: SESSION_TIMEOUT_CONSUMER,\n heartbeatInterval: HEARTBEAT_INTERVAL_CONSUMER,\n retry: {\n retries: MAX_RETRIES_CONSUMER,\n },\n autoCommit: true,\n autoCommitInterval: AUTO_COMMIT_INTERVAL_MS,\n fromBeginning: true,\n },\n \"js.consumer.max.batch.size\": CONSUMER_MAX_BATCH_SIZE,\n });\n\n // Sync producer message.max.bytes with topic config\n const maxMessageBytes =\n args.targetTopic?.max_message_bytes || 1024 * 1024;\n\n const producer: Producer = kafka.producer(\n createProducerConfig(maxMessageBytes),\n );\n\n try {\n logger.log(\"Starting producer...\");\n await startProducer(logger, producer);\n\n try {\n logger.log(\"Starting consumer...\");\n await startConsumer(\n args,\n logger,\n metrics,\n parallelism,\n consumer,\n producer,\n streamingFuncId,\n );\n } catch (e) {\n logger.error(\"Failed to start kafka consumer: \");\n if (e instanceof Error) {\n logError(logger, e);\n }\n // Re-throw to ensure proper error handling\n throw e;\n }\n } catch (e) {\n logger.error(\"Failed to start kafka producer: \");\n if (e instanceof Error) {\n logError(logger, e);\n }\n // Re-throw to ensure proper error handling\n throw e;\n }\n\n return [logger, producer, consumer] as [Logger, Producer, Consumer];\n });\n },\n workerStop: async ([logger, producer, consumer]) => {\n const functionName = logger.logPrefix;\n\n // Wrap entire shutdown in context so all logs have function_name\n await functionContextStorage.run({ functionName }, async () => {\n logger.log(`Received SIGTERM, shutting down gracefully...`);\n\n // First stop the consumer to prevent new messages\n logger.log(\"Stopping consumer first...\");\n await stopConsumer(logger, consumer, args.sourceTopic);\n\n // Wait a bit for in-flight messages to complete processing\n logger.log(\"Waiting for in-flight messages to complete...\");\n await new Promise((resolve) => setTimeout(resolve, 2000));\n\n // Then stop the producer\n logger.log(\"Stopping producer...\");\n await stopProducer(logger, producer);\n\n logger.log(\"Graceful shutdown completed\");\n });\n },\n });\n\n cluster.start();\n};\n","import { getSourceDir, getOutDir, loadModule } from \"./compiler-config\";\n\nexport async function runExportSerializer(targetModel: string) {\n const sourceDir = getSourceDir();\n const outDir = getOutDir();\n\n // Always use compiled paths - replace source directory with compiled path\n let modulePath = targetModel;\n\n // Replace source directory with compiled path and .ts with .js\n // Handle both absolute paths (starting with /) and relative paths\n // Use string replacement instead of RegExp to avoid ReDoS risk\n const sourcePattern = `/${sourceDir}/`;\n if (modulePath.includes(sourcePattern)) {\n modulePath = modulePath.replace(sourcePattern, `/${outDir}/${sourceDir}/`);\n }\n // Replace .ts extension with .js\n modulePath = modulePath.replace(/\\.ts$/, \".js\");\n\n // Use dynamic loader that handles both CJS and ESM\n const exports_list = await loadModule(modulePath);\n console.log(JSON.stringify(exports_list));\n}\n","import {\n DefaultLogger,\n NativeConnection,\n NativeConnectionOptions,\n Worker,\n bundleWorkflowCode,\n} from \"@temporalio/worker\";\nimport * as path from \"path\";\nimport * as fs from \"fs\";\nimport { Workflow } from \"../dmv2\";\nimport { getWorkflows } from \"../dmv2/internal\";\nimport { createActivityForScript } from \"./activity\";\nimport { activities } from \"./activity\";\nimport { initializeLogger } from \"./logger\";\n\ninterface TemporalConfig {\n url: string;\n namespace: string;\n clientCert?: string;\n clientKey?: string;\n apiKey?: string;\n}\n\ninterface ScriptsConfig {\n temporalConfig?: TemporalConfig;\n}\n\n// Maintain a global set of activity names we've already registered\nconst ALREADY_REGISTERED = new Set<string>();\n\nfunction collectActivities(\n logger: DefaultLogger,\n workflows: Map<string, Workflow>,\n) {\n logger.info(`Collecting tasks from workflows`);\n const scriptNames: string[] = [];\n for (const [name, workflow] of workflows.entries()) {\n logger.info(\n `Registering workflow: ${name} with starting task: ${workflow.config.startingTask.name}`,\n );\n scriptNames.push(`${name}/${workflow.config.startingTask.name}`);\n }\n return scriptNames;\n}\n\n/**\n * This looks similar to the client in apis.\n * Temporal SDK uses similar looking connection options & client,\n * but there are different libraries for a worker like this & a client\n * like in the apis.\n */\nasync function createTemporalConnection(\n logger: DefaultLogger,\n temporalConfig: TemporalConfig,\n): Promise<NativeConnection> {\n logger.info(\n `Using temporal_url: ${temporalConfig.url} and namespace: ${temporalConfig.namespace}`,\n );\n\n let connectionOptions: NativeConnectionOptions = {\n address: temporalConfig.url,\n };\n\n if (temporalConfig.clientCert && temporalConfig.clientKey) {\n logger.info(\"Using TLS for secure Temporal\");\n const cert = await fs.readFileSync(temporalConfig.clientCert);\n const key = await fs.readFileSync(temporalConfig.clientKey);\n\n connectionOptions.tls = {\n clientCertPair: {\n crt: cert,\n key: key,\n },\n };\n } else if (temporalConfig.apiKey) {\n logger.info(`Using API key for secure Temporal`);\n // URL with API key uses gRPC regional endpoint\n connectionOptions.address = \"us-west1.gcp.api.temporal.io:7233\";\n connectionOptions.apiKey = temporalConfig.apiKey;\n connectionOptions.tls = {};\n connectionOptions.metadata = {\n \"temporal-namespace\": temporalConfig.namespace,\n };\n }\n\n logger.info(`Connecting to Temporal at ${connectionOptions.address}`);\n\n const maxRetries = 5;\n const baseDelay = 1000;\n let attempt = 0;\n\n while (true) {\n try {\n const connection = await NativeConnection.connect(connectionOptions);\n logger.info(\"Connected to Temporal server\");\n return connection;\n } catch (err) {\n attempt++;\n logger.error(`Connection attempt ${attempt} failed: ${err}`);\n\n if (attempt >= maxRetries) {\n logger.error(`Failed to connect after ${attempt} attempts`);\n throw err;\n }\n\n const backoff = baseDelay * Math.pow(2, attempt - 1);\n logger.warn(`Retrying connection in ${backoff}ms...`);\n await new Promise((resolve) => setTimeout(resolve, backoff));\n }\n }\n}\n\nasync function registerWorkflows(\n logger: DefaultLogger,\n config: ScriptsConfig,\n): Promise<Worker | null> {\n logger.info(`Registering workflows`);\n\n // If temporalConfig is not provided, workflows are disabled\n if (!config.temporalConfig) {\n logger.info(`Temporal config not provided, skipping workflow registration`);\n return null;\n }\n\n // Collect all TypeScript activities from registered workflows\n const allScriptPaths: string[] = [];\n const dynamicActivities: any[] = [];\n\n try {\n const workflows = await getWorkflows();\n if (workflows.size > 0) {\n logger.info(`Found ${workflows.size} workflows`);\n allScriptPaths.push(...collectActivities(logger, workflows));\n\n if (allScriptPaths.length === 0) {\n logger.info(`No tasks found in workflows`);\n return null;\n }\n\n logger.info(`Found ${allScriptPaths.length} tasks in workflows`);\n\n for (const activityName of allScriptPaths) {\n if (!ALREADY_REGISTERED.has(activityName)) {\n const activity = await createActivityForScript(activityName);\n dynamicActivities.push(activity);\n ALREADY_REGISTERED.add(activityName);\n logger.info(`Registered task ${activityName}`);\n }\n }\n\n if (dynamicActivities.length === 0) {\n logger.info(`No dynamic activities found in workflows`);\n return null;\n }\n\n logger.info(\n `Found ${dynamicActivities.length} dynamic activities in workflows`,\n );\n }\n\n if (allScriptPaths.length === 0) {\n logger.info(`No workflows found`);\n return null;\n }\n\n logger.info(`Found ${allScriptPaths.length} workflows`);\n\n if (dynamicActivities.length === 0) {\n logger.info(`No tasks found`);\n return null;\n }\n\n logger.info(`Found ${dynamicActivities.length} task(s)`);\n\n const connection = await createTemporalConnection(\n logger,\n config.temporalConfig,\n );\n\n // Create a custom logger that suppresses webpack output\n const silentLogger = {\n info: () => {}, // Suppress info logs (webpack output)\n debug: () => {}, // Suppress debug logs\n warn: () => {}, // Suppress warnings if desired\n log: () => {}, // Suppress general logs\n trace: () => {}, // Suppress trace logs\n error: (message: string, meta?: any) => {\n // Keep error logs but forward to the main logger\n logger.error(message, meta);\n },\n };\n\n // Pre-bundle workflows with silent logger to suppress webpack output\n // https://github.com/temporalio/sdk-typescript/issues/1740\n const workflowBundle = await bundleWorkflowCode({\n workflowsPath: path.resolve(__dirname, \"scripts/workflow.js\"),\n logger: silentLogger,\n });\n\n const worker = await Worker.create({\n connection,\n namespace: config.temporalConfig.namespace,\n taskQueue: \"typescript-script-queue\",\n workflowBundle,\n activities: {\n ...activities,\n ...Object.fromEntries(\n dynamicActivities.map((activity) => [\n Object.keys(activity)[0],\n Object.values(activity)[0],\n ]),\n ),\n },\n });\n\n return worker;\n } catch (error) {\n logger.error(`Error registering workflows: ${error}`);\n throw error;\n }\n}\n\n/**\n * Start a Temporal worker that handles TypeScript script execution workflows.\n */\nexport async function runScripts(\n config: ScriptsConfig,\n): Promise<Worker | null> {\n const logger = initializeLogger();\n\n // Add process-level uncaught exception handler\n process.on(\"uncaughtException\", (error) => {\n console.error(`[PROCESS] Uncaught Exception: ${error}`);\n process.exit(1);\n });\n\n const worker = await registerWorkflows(logger, config);\n\n if (!worker) {\n logger.warn(\n `No workflows found. To disable workflow infrastructure, set workflows=false in moose.config.toml`,\n );\n process.exit(0);\n }\n\n let isShuttingDown = false;\n\n // Handle shutdown signals\n async function handleSignal(signal: string) {\n console.log(`[SHUTDOWN] Received ${signal}`);\n\n if (isShuttingDown) {\n return;\n }\n\n isShuttingDown = true;\n\n try {\n if (!worker) {\n process.exit(0);\n }\n await Promise.race([\n worker.shutdown(),\n new Promise((_, reject) =>\n setTimeout(() => reject(new Error(\"Shutdown timeout\")), 3000),\n ),\n ]);\n process.exit(0);\n } catch (error) {\n console.log(`[SHUTDOWN] Error: ${error}`);\n process.exit(1);\n }\n }\n\n // Register signal handlers immediately\n [\"SIGTERM\", \"SIGINT\", \"SIGHUP\", \"SIGQUIT\"].forEach((signal) => {\n process.on(signal, () => {\n handleSignal(signal).catch((error) => {\n console.log(`[SHUTDOWN] Error: ${error}`);\n process.exit(1);\n });\n });\n });\n\n logger.info(\"Starting TypeScript worker...\");\n try {\n await worker.run();\n } catch (error) {\n console.log(`[SHUTDOWN] Error: ${error}`);\n process.exit(1);\n }\n\n return worker;\n}\n","import { log as logger, Context } from \"@temporalio/activity\";\nimport { isCancellation } from \"@temporalio/workflow\";\nimport { Task, Workflow } from \"../dmv2\";\nimport { getWorkflows, getTaskForWorkflow } from \"../dmv2/internal\";\nimport { jsonDateReviver } from \"../utilities/json\";\nimport { taskContextStorage } from \"./task-context\";\n\nexport const activities = {\n async hasWorkflow(name: string): Promise<boolean> {\n try {\n const workflows = await getWorkflows();\n const hasWorkflow = workflows.has(name);\n logger.info(`Found workflow:: ${hasWorkflow}`);\n return hasWorkflow;\n } catch (error) {\n logger.error(`Failed to check if workflow ${name} exists: ${error}`);\n return false;\n }\n },\n\n async getWorkflowByName(name: string): Promise<Workflow> {\n try {\n logger.info(`Getting workflow ${name}`);\n\n const workflows = await getWorkflows();\n\n if (workflows.has(name)) {\n logger.info(`Workflow ${name} found`);\n return workflows.get(name)!;\n } else {\n const errorData = {\n error: \"Workflow not found\",\n details: `Workflow ${name} not found`,\n stack: undefined,\n };\n const errorMsg = JSON.stringify(errorData);\n logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n } catch (error) {\n const errorData = {\n error: \"Failed to get workflow\",\n details: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n const errorMsg = JSON.stringify(errorData);\n logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n },\n\n async getTaskForWorkflow(\n workflowName: string,\n taskName: string,\n ): Promise<Task<any, any>> {\n try {\n logger.info(`Getting task ${taskName} from workflow ${workflowName}`);\n const task = await getTaskForWorkflow(workflowName, taskName);\n logger.info(`Task ${taskName} found in workflow ${workflowName}`);\n return task;\n } catch (error) {\n const errorData = {\n error: \"Failed to get task\",\n details: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n const errorMsg = JSON.stringify(errorData);\n logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n },\n\n async executeTask(\n workflow: Workflow,\n task: Task<any, any>,\n inputData: any,\n ): Promise<any[]> {\n // Get context for heartbeat (required for cancellation detection)\n const context = Context.current();\n const taskState = {};\n const taskIdentifier = workflow.name;\n\n // Wrap entire executeTask in task context so ALL logs (framework + user) have task_name\n return await taskContextStorage.run(\n { taskName: taskIdentifier },\n async () => {\n // Periodic heartbeat is required for cancellation detection\n // https://docs.temporal.io/develop/typescript/cancellation#cancel-an-activity\n // - Temporal activities can only receive cancellation if they send heartbeats\n // - Heartbeats are the communication channel between activity and Temporal server\n // - Server sends cancellation signals back in heartbeat responses\n // - Without heartbeats, context.cancelled will never resolve and cancellation is impossible\n let heartbeatInterval: NodeJS.Timeout | null = null;\n const startPeriodicHeartbeat = () => {\n heartbeatInterval = setInterval(() => {\n context.heartbeat(`Task ${task.name} in progress`);\n }, 5000);\n };\n const stopPeriodicHeartbeat = () => {\n if (heartbeatInterval) {\n clearInterval(heartbeatInterval);\n heartbeatInterval = null;\n }\n };\n\n try {\n logger.info(\n `Task ${task.name} received input: ${JSON.stringify(inputData)}`,\n );\n\n // Send initial heartbeat to enable cancellation detection\n context.heartbeat(`Starting task: ${task.name}`);\n\n // Data between temporal workflow & activities are serialized so we\n // have to get it again to access the user's run function\n const fullTask = await getTaskForWorkflow(workflow.name, task.name);\n\n // Revive any JSON serialized dates in the input data\n const revivedInputData =\n inputData ?\n JSON.parse(JSON.stringify(inputData), jsonDateReviver)\n : inputData;\n\n try {\n startPeriodicHeartbeat();\n\n // Race user code against cancellation detection\n // - context.cancelled Promise rejects when server signals cancellation via heartbeat response\n // - This allows immediate cancellation detection rather than waiting for user code to finish\n // - If cancellation happens first, we catch it below and call onCancel cleanup\n const result = await Promise.race([\n fullTask.config.run({\n state: taskState,\n input: revivedInputData,\n }),\n context.cancelled,\n ]);\n return result;\n } catch (error) {\n if (isCancellation(error)) {\n logger.info(\n `Task ${task.name} cancelled, calling onCancel handler if it exists`,\n );\n if (fullTask.config.onCancel) {\n await fullTask.config.onCancel({\n state: taskState,\n input: revivedInputData,\n });\n }\n return [];\n } else {\n throw error;\n }\n } finally {\n stopPeriodicHeartbeat();\n }\n } catch (error) {\n const errorData = {\n error: \"Task execution failed\",\n details: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n };\n const errorMsg = JSON.stringify(errorData);\n logger.error(errorMsg);\n throw new Error(errorMsg);\n }\n },\n );\n },\n};\n\n// Helper function to create activity for a specific script\nexport function createActivityForScript(scriptName: string) {\n return {\n [scriptName]: activities.executeTask,\n };\n}\n","import { setupStructuredConsole } from \"../utils/structured-logging\";\n\n// Task context storage - shared across logger and activity modules\nexport const taskContextStorage = setupStructuredConsole<{ taskName: string }>(\n (ctx) => ctx.taskName,\n \"task_name\",\n);\n\n// Re-export constants for use in logger\nexport const TASK_CONTEXT_FIELD_NAME = \"task_name\";\nexport const getTaskContextField = (ctx: { taskName: string }) => ctx.taskName;\n","import {\n makeTelemetryFilterString,\n DefaultLogger,\n Runtime,\n} from \"@temporalio/worker\";\nimport { emitStructuredLog } from \"../utils/structured-logging\";\nimport {\n taskContextStorage,\n getTaskContextField,\n TASK_CONTEXT_FIELD_NAME,\n} from \"./task-context\";\n\nclass LoggerSingleton {\n private static instance: DefaultLogger | null = null;\n\n private constructor() {}\n\n public static initializeLogger(): DefaultLogger {\n if (!LoggerSingleton.instance) {\n LoggerSingleton.instance = new DefaultLogger(\n \"DEBUG\",\n ({ level, message }) => {\n const structuredLevel = level.toLowerCase();\n\n // Try to emit as structured log if in task context\n const emitted = emitStructuredLog(\n taskContextStorage,\n getTaskContextField,\n TASK_CONTEXT_FIELD_NAME,\n structuredLevel,\n message,\n );\n\n // If not in context, emit as regular log\n if (!emitted) {\n console.log(`${level} | ${message}`);\n }\n },\n );\n\n Runtime.install({\n logger: LoggerSingleton.instance,\n telemetryOptions: {\n logging: {\n filter: makeTelemetryFilterString({ core: \"INFO\", other: \"INFO\" }),\n forward: {},\n },\n },\n });\n }\n\n return LoggerSingleton.instance;\n }\n\n public static getInstance(): DefaultLogger {\n return LoggerSingleton.instance!;\n }\n}\n\nexport const initializeLogger = LoggerSingleton.initializeLogger;\n"],"mappings":"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","names":["sql","createHash","logger","createHash","apiKey","sql","getWorkflows","createClient","fs","isNestedType","parse","init_helpers","init_helpers","init_helpers","readFileSync","path","projectRoot","fs","path","fs","path","process","ids","identifier","program","process","path","getApis","getWorkflows","getWebApps","orderBy","outDir","count","readFileSync","join","path","resolve","toClientConfig","path","getApis","logError","getWebApps","getMooseUtils","resolve","Readable","KafkaJS","Buffer","process","http","Kafka","KafkaJS","logger","Buffer","Readable","cluster","resolve","path","fs","getWorkflows","logger","resolve","getWorkflows","readFileSync","join"]}