UNPKG

@tanstack/optimistic

Version:

Core optimistic updates library

1 lines 11.3 kB
{"version":3,"file":"functions.cjs","sources":["../../../src/query/functions.ts"],"sourcesContent":["import type { AllowedFunctionName } from \"./schema.js\"\n\n/**\n * Type for function implementations\n */\ntype FunctionImplementation = (arg: unknown) => unknown\n\n/**\n * Converts a string to uppercase\n */\nfunction upperFunction(arg: unknown): string {\n if (typeof arg !== `string`) {\n throw new Error(`UPPER function expects a string argument`)\n }\n return arg.toUpperCase()\n}\n\n/**\n * Converts a string to lowercase\n */\nfunction lowerFunction(arg: unknown): string {\n if (typeof arg !== `string`) {\n throw new Error(`LOWER function expects a string argument`)\n }\n return arg.toLowerCase()\n}\n\n/**\n * Returns the length of a string or array\n */\nfunction lengthFunction(arg: unknown): number {\n if (typeof arg === `string` || Array.isArray(arg)) {\n return arg.length\n }\n\n throw new Error(`LENGTH function expects a string or array argument`)\n}\n\n/**\n * Concatenates multiple strings\n */\nfunction concatFunction(arg: unknown): string {\n if (!Array.isArray(arg)) {\n throw new Error(`CONCAT function expects an array of string arguments`)\n }\n\n if (arg.length === 0) {\n return ``\n }\n\n // Check that all arguments are strings\n for (let i = 0; i < arg.length; i++) {\n if (arg[i] !== null && arg[i] !== undefined && typeof arg[i] !== `string`) {\n throw new Error(\n `CONCAT function expects all arguments to be strings, but argument at position ${i} is ${typeof arg[i]}`\n )\n }\n }\n\n // Concatenate strings, treating null and undefined as empty strings\n return arg\n .map((str) => (str === null || str === undefined ? `` : str))\n .join(``)\n}\n\n/**\n * Returns the first non-null, non-undefined value from an array\n */\nfunction coalesceFunction(arg: unknown): unknown {\n if (!Array.isArray(arg)) {\n throw new Error(`COALESCE function expects an array of arguments`)\n }\n\n if (arg.length === 0) {\n return null\n }\n\n // Return the first non-null, non-undefined value\n for (const value of arg) {\n if (value !== null && value !== undefined) {\n return value\n }\n }\n\n // If all values were null or undefined, return null\n return null\n}\n\n/**\n * Creates or converts a value to a Date object\n */\nfunction dateFunction(arg: unknown): Date | null {\n // If the argument is already a Date, return it\n if (arg instanceof Date) {\n return arg\n }\n\n // If the argument is null or undefined, return null\n if (arg === null || arg === undefined) {\n return null\n }\n\n // Handle string and number conversions\n if (typeof arg === `string` || typeof arg === `number`) {\n const date = new Date(arg)\n\n // Check if the date is valid\n if (isNaN(date.getTime())) {\n throw new Error(`DATE function could not parse \"${arg}\" as a valid date`)\n }\n\n return date\n }\n\n throw new Error(`DATE function expects a string, number, or Date argument`)\n}\n\n/**\n * Extracts a value from a JSON string or object using a path.\n * Similar to PostgreSQL's json_extract_path function.\n *\n * Usage: JSON_EXTRACT([jsonInput, 'path', 'to', 'property'])\n * Example: JSON_EXTRACT(['{\"user\": {\"name\": \"John\"}}', 'user', 'name']) returns \"John\"\n */\nfunction jsonExtractFunction(arg: unknown): unknown {\n if (!Array.isArray(arg) || arg.length < 1) {\n throw new Error(\n `JSON_EXTRACT function expects an array with at least one element [jsonInput, ...pathElements]`\n )\n }\n\n const [jsonInput, ...pathElements] = arg\n\n // Handle null or undefined input\n if (jsonInput === null || jsonInput === undefined) {\n return null\n }\n\n // Parse JSON if it's a string\n let jsonData: any\n\n if (typeof jsonInput === `string`) {\n try {\n jsonData = JSON.parse(jsonInput)\n } catch (error) {\n throw new Error(\n `JSON_EXTRACT function could not parse JSON string: ${error instanceof Error ? error.message : String(error)}`\n )\n }\n } else if (typeof jsonInput === `object`) {\n // If already an object, use it directly\n jsonData = jsonInput\n } else {\n throw new Error(\n `JSON_EXTRACT function expects a JSON string or object as the first argument`\n )\n }\n\n // If no path elements, return the parsed JSON\n if (pathElements.length === 0) {\n return jsonData\n }\n\n // Navigate through the path elements\n let current = jsonData\n\n for (let i = 0; i < pathElements.length; i++) {\n const pathElement = pathElements[i]\n\n // Path elements should be strings\n if (typeof pathElement !== `string`) {\n throw new Error(\n `JSON_EXTRACT function expects path elements to be strings, but element at position ${i + 1} is ${typeof pathElement}`\n )\n }\n\n // If current node is null or undefined, or not an object, we can't navigate further\n if (\n current === null ||\n current === undefined ||\n typeof current !== `object`\n ) {\n return null\n }\n\n // Access property\n current = current[pathElement]\n }\n\n // Return null instead of undefined for consistency\n return current === undefined ? null : current\n}\n\n/**\n * Placeholder function for ORDER_INDEX\n * This function doesn't do anything when called directly, as the actual index\n * is provided by the orderBy operator during query execution.\n * The argument can be 'numeric', 'fractional', or any truthy value (defaults to 'numeric')\n */\nfunction orderIndexFunction(arg: unknown): null {\n // This is just a placeholder - the actual index is provided by the orderBy operator\n // The function validates that the argument is one of the expected values\n if (\n arg !== `numeric` &&\n arg !== `fractional` &&\n arg !== true &&\n arg !== `default`\n ) {\n throw new Error(\n `ORDER_INDEX function expects \"numeric\", \"fractional\", \"default\", or true as argument`\n )\n }\n return null\n}\n\n/**\n * Map of function names to their implementations\n */\nconst functionImplementations: Record<\n AllowedFunctionName,\n FunctionImplementation\n> = {\n // Map function names to their implementation functions\n DATE: dateFunction,\n JSON_EXTRACT: jsonExtractFunction,\n JSON_EXTRACT_PATH: jsonExtractFunction, // Alias for JSON_EXTRACT\n UPPER: upperFunction,\n LOWER: lowerFunction,\n COALESCE: coalesceFunction,\n CONCAT: concatFunction,\n LENGTH: lengthFunction,\n ORDER_INDEX: orderIndexFunction,\n}\n\n/**\n * Evaluates a function call with the given name and arguments\n * @param functionName The name of the function to evaluate\n * @param arg The arguments to pass to the function\n * @returns The result of the function call\n */\nexport function evaluateFunction(\n functionName: AllowedFunctionName,\n arg: unknown\n): unknown {\n const implementation = functionImplementations[functionName] as\n | FunctionImplementation\n | undefined // Double check that the implementation is defined\n\n if (!implementation) {\n throw new Error(`Unknown function: ${functionName}`)\n }\n return implementation(arg)\n}\n\n/**\n * Determines if an object is a function call\n * @param obj The object to check\n * @returns True if the object is a function call, false otherwise\n */\nexport function isFunctionCall(obj: unknown): boolean {\n if (!obj || typeof obj !== `object`) {\n return false\n }\n\n const keys = Object.keys(obj)\n if (keys.length !== 1) {\n return false\n }\n\n const functionName = keys[0] as string\n\n // Check if the key is one of the allowed function names\n return Object.keys(functionImplementations).includes(functionName)\n}\n\n/**\n * Extracts the function name and argument from a function call object.\n */\nexport function extractFunctionCall(obj: Record<string, unknown>): {\n functionName: AllowedFunctionName\n argument: unknown\n} {\n const keys = Object.keys(obj)\n if (keys.length !== 1) {\n throw new Error(`Invalid function call: object must have exactly one key`)\n }\n\n const functionName = keys[0] as AllowedFunctionName\n if (!Object.keys(functionImplementations).includes(functionName)) {\n throw new Error(`Invalid function name: ${functionName}`)\n }\n\n return {\n functionName,\n argument: obj[functionName],\n }\n}\n"],"names":[],"mappings":"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"}