@proofkit/fmodata
Version:
FileMaker OData API client
305 lines (304 loc) • 10.1 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
import { needsFieldQuoting } from "../client/builders/select-utils.js";
import { ColumnFunction, isColumnFunction, isColumn } from "./column.js";
class FilterExpression {
// biome-ignore lint/suspicious/noExplicitAny: Operands can be Column, FilterExpression, or any value type
constructor(operator, operands) {
__publicField(this, "operator");
// biome-ignore lint/suspicious/noExplicitAny: Operands can be Column, FilterExpression, or any value type
__publicField(this, "operands");
this.operator = operator;
this.operands = operands;
}
/**
* Convert this expression to OData filter syntax.
* @internal Used by QueryBuilder
*/
toODataFilter(useEntityIds) {
switch (this.operator) {
// Comparison operators
case "eq":
return this._binaryOp("eq", useEntityIds);
case "ne":
return this._binaryOp("ne", useEntityIds);
case "gt":
return this._binaryOp("gt", useEntityIds);
case "gte":
return this._binaryOp("ge", useEntityIds);
case "lt":
return this._binaryOp("lt", useEntityIds);
case "lte":
return this._binaryOp("le", useEntityIds);
case "in":
return this._inOp(useEntityIds);
case "notIn":
return this._notInOp(useEntityIds);
// String operators
case "contains":
return this._functionOp("contains", useEntityIds);
case "startsWith":
return this._functionOp("startswith", useEntityIds);
case "endsWith":
return this._functionOp("endswith", useEntityIds);
case "matchesPattern":
return this._functionOp("matchesPattern", useEntityIds);
// Null checks
case "isNull":
return this._isNullOp(useEntityIds);
case "isNotNull":
return this._isNotNullOp(useEntityIds);
// Logical operators
case "and":
return this._logicalOp("and", useEntityIds);
case "or":
return this._logicalOp("or", useEntityIds);
case "not":
return this._notOp(useEntityIds);
default:
throw new Error(`Unknown operator: ${this.operator}`);
}
}
_binaryOp(op, useEntityIds) {
const [left, right] = this.operands;
let columnForValue;
if (isColumn(left) && !isColumn(right)) {
columnForValue = left;
} else if (isColumn(right) && !isColumn(left)) {
columnForValue = right;
} else {
columnForValue = void 0;
}
const leftStr = this._operandToString(left, useEntityIds, columnForValue);
const rightStr = this._operandToString(right, useEntityIds, columnForValue);
return `${leftStr} ${op} ${rightStr}`;
}
_functionOp(fnName, useEntityIds) {
const [column, value] = this.operands;
const columnInstance = isColumn(column) ? column : void 0;
const columnStr = this._operandToString(column, useEntityIds);
const valueStr = this._operandToString(value, useEntityIds, columnInstance);
return `${fnName}(${columnStr}, ${valueStr})`;
}
_inOp(useEntityIds) {
const [column, values] = this.operands;
const columnInstance = isColumn(column) ? column : void 0;
const columnStr = this._operandToString(column, useEntityIds);
const valuesStr = values.map((v) => this._operandToString(v, useEntityIds, columnInstance)).join(", ");
return `${columnStr} in (${valuesStr})`;
}
_notInOp(useEntityIds) {
const [column, values] = this.operands;
const columnInstance = isColumn(column) ? column : void 0;
const columnStr = this._operandToString(column, useEntityIds);
const valuesStr = values.map((v) => this._operandToString(v, useEntityIds, columnInstance)).join(", ");
return `not (${columnStr} in (${valuesStr}))`;
}
_isNullOp(useEntityIds) {
const [column] = this.operands;
const columnStr = this._operandToString(column, useEntityIds);
return `${columnStr} eq null`;
}
_isNotNullOp(useEntityIds) {
const [column] = this.operands;
const columnStr = this._operandToString(column, useEntityIds);
return `${columnStr} ne null`;
}
_logicalOp(op, useEntityIds) {
const expressions = this.operands.map((expr) => {
if (expr instanceof FilterExpression) {
const innerExpr = expr.toODataFilter(useEntityIds);
if (expr.operator === "and" || expr.operator === "or") {
return `(${innerExpr})`;
}
return innerExpr;
}
throw new Error("Logical operators require FilterExpression operands");
});
return expressions.join(` ${op} `);
}
_notOp(useEntityIds) {
const [expr] = this.operands;
if (expr instanceof FilterExpression) {
return `not (${expr.toODataFilter(useEntityIds)})`;
}
throw new Error("NOT operator requires a FilterExpression operand");
}
_formatTemporalValue(value, fieldType) {
if (!(value instanceof Date)) {
return String(value);
}
if (fieldType === "date") {
return value.toISOString().slice(0, 10);
}
if (fieldType === "time") {
return value.toISOString().slice(11, 19);
}
return value.toISOString();
}
_operandToString(operand, useEntityIds, column) {
var _a;
if (isColumnFunction(operand)) {
return operand.toFilterString(useEntityIds);
}
if (isColumn(operand)) {
const fieldIdentifier = operand.getFieldIdentifier(useEntityIds);
return needsFieldQuoting(fieldIdentifier) ? `"${fieldIdentifier}"` : fieldIdentifier;
}
let value = operand;
if (column == null ? void 0 : column.inputValidator) {
try {
const result = column.inputValidator["~standard"].validate(value);
if (result instanceof Promise) {
value = operand;
} else if ("issues" in result && result.issues) {
value = operand;
} else if ("value" in result) {
value = result.value;
}
} catch (_error) {
value = operand;
}
}
const ft = column == null ? void 0 : column.fieldType;
if (ft === "date" || ft === "time" || ft === "timestamp") {
return this._formatTemporalValue(value, ft);
}
if (typeof value === "string") {
return `'${value.replace(/'/g, "''")}'`;
}
if (value === null || value === void 0) {
return "null";
}
if (value instanceof Date) {
return value.toISOString();
}
if (typeof value === "object") {
const valueType = ((_a = value == null ? void 0 : value.constructor) == null ? void 0 : _a.name) ?? "Object";
throw new Error(
`Unsupported filter operand: received ${valueType}. Pass a table column or a primitive filter value.`
);
}
return String(value);
}
}
function eq(column, value) {
return new FilterExpression("eq", [column, value]);
}
function ne(column, value) {
return new FilterExpression("ne", [column, value]);
}
function gt(column, value) {
return new FilterExpression("gt", [column, value]);
}
function gte(column, value) {
return new FilterExpression("gte", [column, value]);
}
function lt(column, value) {
return new FilterExpression("lt", [column, value]);
}
function lte(column, value) {
return new FilterExpression("lte", [column, value]);
}
function contains(column, value) {
return new FilterExpression("contains", [column, value]);
}
function startsWith(column, value) {
return new FilterExpression("startsWith", [column, value]);
}
function endsWith(column, value) {
return new FilterExpression("endsWith", [column, value]);
}
function matchesPattern(column, pattern) {
return new FilterExpression("matchesPattern", [column, pattern]);
}
function tolower(column) {
return new ColumnFunction("tolower", column);
}
function toupper(column) {
return new ColumnFunction("toupper", column);
}
function trim(column) {
return new ColumnFunction("trim", column);
}
function inArray(column, values) {
return new FilterExpression("in", [column, values]);
}
function notInArray(column, values) {
return new FilterExpression("notIn", [column, values]);
}
function isNull(column) {
return new FilterExpression("isNull", [column]);
}
function isNotNull(column) {
return new FilterExpression("isNotNull", [column]);
}
function and(...expressions) {
if (expressions.length === 0) {
throw new Error("AND operator requires at least one expression");
}
if (expressions.length === 1 && expressions[0] !== void 0) {
return expressions[0];
}
return new FilterExpression("and", expressions);
}
function or(...expressions) {
if (expressions.length === 0) {
throw new Error("OR operator requires at least one expression");
}
if (expressions.length === 1 && expressions[0] !== void 0) {
return expressions[0];
}
return new FilterExpression("or", expressions);
}
function not(expression) {
return new FilterExpression("not", [expression]);
}
class OrderByExpression {
// biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration
constructor(column, direction) {
// biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration
__publicField(this, "column");
__publicField(this, "direction");
this.column = column;
this.direction = direction;
}
}
function isOrderByExpression(value) {
return value instanceof OrderByExpression;
}
function asc(column) {
return new OrderByExpression(column, "asc");
}
function desc(column) {
return new OrderByExpression(column, "desc");
}
export {
FilterExpression,
OrderByExpression,
and,
asc,
contains,
desc,
endsWith,
eq,
gt,
gte,
inArray,
isNotNull,
isNull,
isOrderByExpression,
lt,
lte,
matchesPattern,
ne,
not,
notInArray,
or,
startsWith,
tolower,
toupper,
trim
};
//# sourceMappingURL=operators.js.map