@proofkit/fmodata 0.1.0-alpha.15 → 0.1.0-alpha.17

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.
Files changed (71) hide show
  1. package/README.md +6 -167
  2. package/dist/esm/client/builders/expand-builder.d.ts +3 -1
  3. package/dist/esm/client/builders/expand-builder.js +3 -2
  4. package/dist/esm/client/builders/expand-builder.js.map +1 -1
  5. package/dist/esm/client/builders/query-string-builder.d.ts +2 -0
  6. package/dist/esm/client/builders/query-string-builder.js +1 -1
  7. package/dist/esm/client/builders/query-string-builder.js.map +1 -1
  8. package/dist/esm/client/builders/response-processor.d.ts +2 -0
  9. package/dist/esm/client/builders/response-processor.js +3 -2
  10. package/dist/esm/client/builders/response-processor.js.map +1 -1
  11. package/dist/esm/client/builders/select-mixin.d.ts +3 -2
  12. package/dist/esm/client/builders/select-mixin.js +2 -2
  13. package/dist/esm/client/builders/select-mixin.js.map +1 -1
  14. package/dist/esm/client/entity-set.d.ts +4 -13
  15. package/dist/esm/client/entity-set.js +5 -2
  16. package/dist/esm/client/entity-set.js.map +1 -1
  17. package/dist/esm/client/error-parser.js.map +1 -1
  18. package/dist/esm/client/filemaker-odata.d.ts +8 -0
  19. package/dist/esm/client/filemaker-odata.js +14 -0
  20. package/dist/esm/client/filemaker-odata.js.map +1 -1
  21. package/dist/esm/client/query/query-builder.d.ts +10 -16
  22. package/dist/esm/client/query/query-builder.js +27 -85
  23. package/dist/esm/client/query/query-builder.js.map +1 -1
  24. package/dist/esm/client/query/response-processor.d.ts +2 -0
  25. package/dist/esm/client/query/types.d.ts +5 -5
  26. package/dist/esm/client/record-builder.d.ts +11 -9
  27. package/dist/esm/client/record-builder.js +41 -10
  28. package/dist/esm/client/record-builder.js.map +1 -1
  29. package/dist/esm/index.d.ts +3 -2
  30. package/dist/esm/logger.d.ts +47 -0
  31. package/dist/esm/logger.js +72 -0
  32. package/dist/esm/logger.js.map +1 -0
  33. package/dist/esm/logger.test.d.ts +1 -0
  34. package/dist/esm/orm/column.d.ts +21 -4
  35. package/dist/esm/orm/column.js +5 -2
  36. package/dist/esm/orm/column.js.map +1 -1
  37. package/dist/esm/orm/field-builders.d.ts +1 -1
  38. package/dist/esm/orm/field-builders.js +1 -1
  39. package/dist/esm/orm/field-builders.js.map +1 -1
  40. package/dist/esm/orm/operators.d.ts +19 -19
  41. package/dist/esm/orm/operators.js +31 -12
  42. package/dist/esm/orm/operators.js.map +1 -1
  43. package/dist/esm/orm/table.d.ts +10 -9
  44. package/dist/esm/orm/table.js +5 -3
  45. package/dist/esm/orm/table.js.map +1 -1
  46. package/dist/esm/transform.js +1 -5
  47. package/dist/esm/transform.js.map +1 -1
  48. package/dist/esm/types.d.ts +11 -43
  49. package/dist/esm/types.js.map +1 -1
  50. package/package.json +1 -1
  51. package/src/client/builders/expand-builder.ts +6 -2
  52. package/src/client/builders/query-string-builder.ts +3 -1
  53. package/src/client/builders/response-processor.ts +4 -1
  54. package/src/client/builders/select-mixin.ts +5 -4
  55. package/src/client/entity-set.ts +30 -9
  56. package/src/client/error-parser.ts +3 -0
  57. package/src/client/filemaker-odata.ts +18 -0
  58. package/src/client/query/query-builder.ts +32 -128
  59. package/src/client/query/response-processor.ts +2 -0
  60. package/src/client/query/types.ts +6 -5
  61. package/src/client/record-builder.ts +77 -34
  62. package/src/index.ts +5 -15
  63. package/src/logger.test.ts +34 -0
  64. package/src/logger.ts +140 -0
  65. package/src/orm/column.ts +33 -5
  66. package/src/orm/field-builders.ts +1 -1
  67. package/src/orm/operators.ts +105 -51
  68. package/src/orm/table.ts +21 -13
  69. package/src/types.ts +12 -51
  70. package/dist/esm/filter-types.d.ts +0 -76
  71. package/src/filter-types.ts +0 -97
@@ -0,0 +1,47 @@
1
+ export declare const TTY_COLORS: {
2
+ readonly reset: "\u001B[0m";
3
+ readonly bright: "\u001B[1m";
4
+ readonly dim: "\u001B[2m";
5
+ readonly undim: "\u001B[22m";
6
+ readonly underscore: "\u001B[4m";
7
+ readonly blink: "\u001B[5m";
8
+ readonly reverse: "\u001B[7m";
9
+ readonly hidden: "\u001B[8m";
10
+ readonly fg: {
11
+ readonly black: "\u001B[30m";
12
+ readonly red: "\u001B[31m";
13
+ readonly green: "\u001B[32m";
14
+ readonly yellow: "\u001B[33m";
15
+ readonly blue: "\u001B[34m";
16
+ readonly magenta: "\u001B[35m";
17
+ readonly cyan: "\u001B[36m";
18
+ readonly white: "\u001B[37m";
19
+ };
20
+ readonly bg: {
21
+ readonly black: "\u001B[40m";
22
+ readonly red: "\u001B[41m";
23
+ readonly green: "\u001B[42m";
24
+ readonly yellow: "\u001B[43m";
25
+ readonly blue: "\u001B[44m";
26
+ readonly magenta: "\u001B[45m";
27
+ readonly cyan: "\u001B[46m";
28
+ readonly white: "\u001B[47m";
29
+ };
30
+ };
31
+ export type LogLevel = "debug" | "info" | "success" | "warn" | "error";
32
+ export declare const levels: readonly ["debug", "info", "success", "warn", "error"];
33
+ export declare function shouldPublishLog(currentLogLevel: LogLevel, logLevel: LogLevel): boolean;
34
+ export interface Logger {
35
+ disabled?: boolean | undefined;
36
+ disableColors?: boolean | undefined;
37
+ level?: Exclude<LogLevel, "success"> | undefined;
38
+ log?: ((level: Exclude<LogLevel, "success">, message: string, ...args: any[]) => void) | undefined;
39
+ }
40
+ export type LogHandlerParams = Parameters<NonNullable<Logger["log"]>> extends [LogLevel, ...infer Rest] ? Rest : never;
41
+ export type InternalLogger = {
42
+ [K in LogLevel]: (...params: LogHandlerParams) => void;
43
+ } & {
44
+ get level(): LogLevel;
45
+ };
46
+ export declare const createLogger: (options?: Logger | undefined) => InternalLogger;
47
+ export declare const logger: InternalLogger;
@@ -0,0 +1,72 @@
1
+ const TTY_COLORS = {
2
+ reset: "\x1B[0m",
3
+ bright: "\x1B[1m",
4
+ dim: "\x1B[2m",
5
+ fg: {
6
+ red: "\x1B[31m",
7
+ green: "\x1B[32m",
8
+ yellow: "\x1B[33m",
9
+ blue: "\x1B[34m",
10
+ magenta: "\x1B[35m"
11
+ }
12
+ };
13
+ const levels = ["debug", "info", "success", "warn", "error"];
14
+ function shouldPublishLog(currentLogLevel, logLevel) {
15
+ return levels.indexOf(logLevel) >= levels.indexOf(currentLogLevel);
16
+ }
17
+ const levelColors = {
18
+ info: TTY_COLORS.fg.blue,
19
+ success: TTY_COLORS.fg.green,
20
+ warn: TTY_COLORS.fg.yellow,
21
+ error: TTY_COLORS.fg.red,
22
+ debug: TTY_COLORS.fg.magenta
23
+ };
24
+ const formatMessage = (level, message, colorsEnabled) => {
25
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
26
+ if (colorsEnabled) {
27
+ return `${TTY_COLORS.dim}${timestamp}${TTY_COLORS.reset} ${levelColors[level]}${level.toUpperCase()}${TTY_COLORS.reset} ${TTY_COLORS.bright}[FMODATA]:${TTY_COLORS.reset} ${message}`;
28
+ }
29
+ return `${timestamp} ${level.toUpperCase()} [FMODATA]: ${message}`;
30
+ };
31
+ const createLogger = (options) => {
32
+ const enabled = (options == null ? void 0 : options.disabled) !== true;
33
+ const logLevel = (options == null ? void 0 : options.level) ?? "error";
34
+ const colorsEnabled = (options == null ? void 0 : options.disableColors) !== true;
35
+ const LogFunc = (level, message, args = []) => {
36
+ if (!enabled || !shouldPublishLog(logLevel, level)) {
37
+ return;
38
+ }
39
+ const formattedMessage = formatMessage(level, message, colorsEnabled);
40
+ if (!options || typeof options.log !== "function") {
41
+ if (level === "error") {
42
+ console.error(formattedMessage, ...args);
43
+ } else if (level === "warn") {
44
+ console.warn(formattedMessage, ...args);
45
+ } else {
46
+ console.log(formattedMessage, ...args);
47
+ }
48
+ return;
49
+ }
50
+ options.log(level === "success" ? "info" : level, message, ...args);
51
+ };
52
+ const logger2 = Object.fromEntries(
53
+ levels.map((level) => [
54
+ level,
55
+ (...[message, ...args]) => LogFunc(level, message, args)
56
+ ])
57
+ );
58
+ return {
59
+ ...logger2,
60
+ get level() {
61
+ return logLevel;
62
+ }
63
+ };
64
+ };
65
+ createLogger();
66
+ export {
67
+ TTY_COLORS,
68
+ createLogger,
69
+ levels,
70
+ shouldPublishLog
71
+ };
72
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sources":["../../src/logger.ts"],"sourcesContent":["export const TTY_COLORS = {\n reset: \"\\x1b[0m\",\n bright: \"\\x1b[1m\",\n dim: \"\\x1b[2m\",\n undim: \"\\x1b[22m\",\n underscore: \"\\x1b[4m\",\n blink: \"\\x1b[5m\",\n reverse: \"\\x1b[7m\",\n hidden: \"\\x1b[8m\",\n fg: {\n black: \"\\x1b[30m\",\n red: \"\\x1b[31m\",\n green: \"\\x1b[32m\",\n yellow: \"\\x1b[33m\",\n blue: \"\\x1b[34m\",\n magenta: \"\\x1b[35m\",\n cyan: \"\\x1b[36m\",\n white: \"\\x1b[37m\",\n },\n bg: {\n black: \"\\x1b[40m\",\n red: \"\\x1b[41m\",\n green: \"\\x1b[42m\",\n yellow: \"\\x1b[43m\",\n blue: \"\\x1b[44m\",\n magenta: \"\\x1b[45m\",\n cyan: \"\\x1b[46m\",\n white: \"\\x1b[47m\",\n },\n} as const;\n\nexport type LogLevel = \"debug\" | \"info\" | \"success\" | \"warn\" | \"error\";\n\nexport const levels = [\"debug\", \"info\", \"success\", \"warn\", \"error\"] as const;\n\nexport function shouldPublishLog(\n currentLogLevel: LogLevel,\n logLevel: LogLevel,\n): boolean {\n return levels.indexOf(logLevel) >= levels.indexOf(currentLogLevel);\n}\n\nexport interface Logger {\n disabled?: boolean | undefined;\n disableColors?: boolean | undefined;\n level?: Exclude<LogLevel, \"success\"> | undefined;\n log?:\n | ((\n level: Exclude<LogLevel, \"success\">,\n message: string,\n ...args: any[]\n ) => void)\n | undefined;\n}\n\nexport type LogHandlerParams =\n Parameters<NonNullable<Logger[\"log\"]>> extends [LogLevel, ...infer Rest]\n ? Rest\n : never;\n\nconst levelColors: Record<LogLevel, string> = {\n info: TTY_COLORS.fg.blue,\n success: TTY_COLORS.fg.green,\n warn: TTY_COLORS.fg.yellow,\n error: TTY_COLORS.fg.red,\n debug: TTY_COLORS.fg.magenta,\n};\n\nconst formatMessage = (\n level: LogLevel,\n message: string,\n colorsEnabled: boolean,\n): string => {\n const timestamp = new Date().toISOString();\n\n if (colorsEnabled) {\n return `${TTY_COLORS.dim}${timestamp}${TTY_COLORS.reset} ${\n levelColors[level]\n }${level.toUpperCase()}${TTY_COLORS.reset} ${TTY_COLORS.bright}[FMODATA]:${\n TTY_COLORS.reset\n } ${message}`;\n }\n\n return `${timestamp} ${level.toUpperCase()} [FMODATA]: ${message}`;\n};\n\nexport type InternalLogger = {\n [K in LogLevel]: (...params: LogHandlerParams) => void;\n} & {\n get level(): LogLevel;\n};\n\nexport const createLogger = (options?: Logger | undefined): InternalLogger => {\n const enabled = options?.disabled !== true;\n const logLevel = options?.level ?? \"error\";\n\n const colorsEnabled = options?.disableColors !== true;\n\n const LogFunc = (\n level: LogLevel,\n message: string,\n args: any[] = [],\n ): void => {\n if (!enabled || !shouldPublishLog(logLevel, level)) {\n return;\n }\n\n const formattedMessage = formatMessage(level, message, colorsEnabled);\n\n if (!options || typeof options.log !== \"function\") {\n if (level === \"error\") {\n console.error(formattedMessage, ...args);\n } else if (level === \"warn\") {\n console.warn(formattedMessage, ...args);\n } else {\n console.log(formattedMessage, ...args);\n }\n return;\n }\n\n options.log(level === \"success\" ? \"info\" : level, message, ...args);\n };\n\n const logger = Object.fromEntries(\n levels.map((level) => [\n level,\n (...[message, ...args]: LogHandlerParams) =>\n LogFunc(level, message, args),\n ]),\n ) as Record<LogLevel, (...params: LogHandlerParams) => void>;\n\n return {\n ...logger,\n get level() {\n return logLevel;\n },\n };\n};\n\nexport const logger = createLogger();\n"],"names":["logger"],"mappings":"AAAO,MAAM,aAAa;AAAA,EACxB,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,KAAK;AAAA,EAML,IAAI;AAAA,IAEF,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,MAAM;AAAA,IACN,SAAS;AAAA,EAGX;AAWF;AAIO,MAAM,SAAS,CAAC,SAAS,QAAQ,WAAW,QAAQ,OAAO;AAElD,SAAA,iBACd,iBACA,UACS;AACT,SAAO,OAAO,QAAQ,QAAQ,KAAK,OAAO,QAAQ,eAAe;AACnE;AAoBA,MAAM,cAAwC;AAAA,EAC5C,MAAM,WAAW,GAAG;AAAA,EACpB,SAAS,WAAW,GAAG;AAAA,EACvB,MAAM,WAAW,GAAG;AAAA,EACpB,OAAO,WAAW,GAAG;AAAA,EACrB,OAAO,WAAW,GAAG;AACvB;AAEA,MAAM,gBAAgB,CACpB,OACA,SACA,kBACW;AACX,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AAEzC,MAAI,eAAe;AACV,WAAA,GAAG,WAAW,GAAG,GAAG,SAAS,GAAG,WAAW,KAAK,IACrD,YAAY,KAAK,CACnB,GAAG,MAAM,YAAY,CAAC,GAAG,WAAW,KAAK,IAAI,WAAW,MAAM,aAC5D,WAAW,KACb,IAAI,OAAO;AAAA,EAAA;AAGb,SAAO,GAAG,SAAS,IAAI,MAAM,aAAa,eAAe,OAAO;AAClE;AAQa,MAAA,eAAe,CAAC,YAAiD;AACtE,QAAA,WAAU,mCAAS,cAAa;AAChC,QAAA,YAAW,mCAAS,UAAS;AAE7B,QAAA,iBAAgB,mCAAS,mBAAkB;AAEjD,QAAM,UAAU,CACd,OACA,SACA,OAAc,CAAA,MACL;AACT,QAAI,CAAC,WAAW,CAAC,iBAAiB,UAAU,KAAK,GAAG;AAClD;AAAA,IAAA;AAGF,UAAM,mBAAmB,cAAc,OAAO,SAAS,aAAa;AAEpE,QAAI,CAAC,WAAW,OAAO,QAAQ,QAAQ,YAAY;AACjD,UAAI,UAAU,SAAS;AACb,gBAAA,MAAM,kBAAkB,GAAG,IAAI;AAAA,MAAA,WAC9B,UAAU,QAAQ;AACnB,gBAAA,KAAK,kBAAkB,GAAG,IAAI;AAAA,MAAA,OACjC;AACG,gBAAA,IAAI,kBAAkB,GAAG,IAAI;AAAA,MAAA;AAEvC;AAAA,IAAA;AAGF,YAAQ,IAAI,UAAU,YAAY,SAAS,OAAO,SAAS,GAAG,IAAI;AAAA,EACpE;AAEA,QAAMA,UAAS,OAAO;AAAA,IACpB,OAAO,IAAI,CAAC,UAAU;AAAA,MACpB;AAAA,MACA,IAAI,CAAC,SAAY,OAAI,MACnB,QAAQ,OAAO,SAAS,IAAI;AAAA,IAC/B,CAAA;AAAA,EACH;AAEO,SAAA;AAAA,IACL,GAAGA;AAAAA,IACH,IAAI,QAAQ;AACH,aAAA;AAAA,IAAA;AAAA,EAEX;AACF;AAEsB,aAAa;"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1,23 +1,28 @@
1
+ import { StandardSchemaV1 } from '@standard-schema/spec';
1
2
  /**
2
3
  * Column represents a type-safe reference to a table field.
3
4
  * Used in queries, filters, and operators to provide autocomplete and type checking.
4
5
  *
5
- * @template T - The TypeScript type of the column's value
6
+ * @template TOutput - The TypeScript type when reading from the database (output type)
7
+ * @template TInput - The TypeScript type when writing to the database (input type, for filters)
6
8
  * @template TableName - The table name as a string literal type (for validation)
7
9
  * @template IsContainer - Whether this column represents a container field (cannot be selected)
8
10
  */
9
- export declare class Column<T = any, TableName extends string = string, IsContainer extends boolean = false> {
11
+ export declare class Column<TOutput = any, TInput = TOutput, TableName extends string = string, IsContainer extends boolean = false> {
10
12
  readonly fieldName: string;
11
13
  readonly entityId?: `FMFID:${string}`;
12
14
  readonly tableName: TableName;
13
15
  readonly tableEntityId?: `FMTID:${string}`;
14
- readonly _phantom: T;
16
+ readonly inputValidator?: StandardSchemaV1<TInput, any>;
17
+ readonly _phantomOutput: TOutput;
18
+ readonly _phantomInput: TInput;
15
19
  readonly _isContainer: IsContainer;
16
20
  constructor(config: {
17
21
  fieldName: string;
18
22
  entityId?: `FMFID:${string}`;
19
23
  tableName: TableName;
20
24
  tableEntityId?: `FMTID:${string}`;
25
+ inputValidator?: StandardSchemaV1<TInput, any>;
21
26
  });
22
27
  /**
23
28
  * Get the field identifier (entity ID if available, otherwise field name).
@@ -42,4 +47,16 @@ export declare class Column<T = any, TableName extends string = string, IsContai
42
47
  /**
43
48
  * Type guard to check if a value is a Column instance.
44
49
  */
45
- export declare function isColumn(value: any): value is Column<any, any, any>;
50
+ export declare function isColumn(value: any): value is Column<any, any, any, any>;
51
+ /**
52
+ * Create a Column with proper type inference from the inputValidator.
53
+ * This helper ensures TypeScript can infer TInput from the validator's input type.
54
+ * @internal
55
+ */
56
+ export declare function createColumn<TOutput, TInput, TName extends string, IsContainer extends boolean = false>(config: {
57
+ fieldName: string;
58
+ entityId?: `FMFID:${string}`;
59
+ tableName: TName;
60
+ tableEntityId?: `FMTID:${string}`;
61
+ inputValidator?: StandardSchemaV1<TInput, any>;
62
+ }): Column<TOutput, TInput, TName, IsContainer>;
@@ -7,13 +7,16 @@ class Column {
7
7
  __publicField(this, "entityId");
8
8
  __publicField(this, "tableName");
9
9
  __publicField(this, "tableEntityId");
10
- // Phantom type for TypeScript inference - never actually holds a value
11
- __publicField(this, "_phantom");
10
+ __publicField(this, "inputValidator");
11
+ // Phantom types for TypeScript inference - never actually hold values
12
+ __publicField(this, "_phantomOutput");
13
+ __publicField(this, "_phantomInput");
12
14
  __publicField(this, "_isContainer");
13
15
  this.fieldName = config.fieldName;
14
16
  this.entityId = config.entityId;
15
17
  this.tableName = config.tableName;
16
18
  this.tableEntityId = config.tableEntityId;
19
+ this.inputValidator = config.inputValidator;
17
20
  }
18
21
  /**
19
22
  * Get the field identifier (entity ID if available, otherwise field name).
@@ -1 +1 @@
1
- {"version":3,"file":"column.js","sources":["../../../src/orm/column.ts"],"sourcesContent":["/**\n * Column represents a type-safe reference to a table field.\n * Used in queries, filters, and operators to provide autocomplete and type checking.\n *\n * @template T - The TypeScript type of the column's value\n * @template TableName - The table name as a string literal type (for validation)\n * @template IsContainer - Whether this column represents a container field (cannot be selected)\n */\nexport class Column<\n T = any,\n TableName extends string = string,\n IsContainer extends boolean = false,\n> {\n readonly fieldName: string;\n readonly entityId?: `FMFID:${string}`;\n readonly tableName: TableName;\n readonly tableEntityId?: `FMTID:${string}`;\n\n // Phantom type for TypeScript inference - never actually holds a value\n readonly _phantom!: T;\n readonly _isContainer!: IsContainer;\n\n constructor(config: {\n fieldName: string;\n entityId?: `FMFID:${string}`;\n tableName: TableName;\n tableEntityId?: `FMTID:${string}`;\n }) {\n this.fieldName = config.fieldName;\n this.entityId = config.entityId;\n this.tableName = config.tableName;\n this.tableEntityId = config.tableEntityId;\n }\n\n /**\n * Get the field identifier (entity ID if available, otherwise field name).\n * Used when building OData queries.\n */\n getFieldIdentifier(useEntityIds?: boolean): string {\n if (useEntityIds && this.entityId) {\n return this.entityId;\n }\n return this.fieldName;\n }\n\n /**\n * Get the table identifier (entity ID if available, otherwise table name).\n * Used when building OData queries.\n */\n getTableIdentifier(useEntityIds?: boolean): string {\n if (useEntityIds && this.tableEntityId) {\n return this.tableEntityId;\n }\n return this.tableName;\n }\n\n /**\n * Check if this column is from a specific table.\n * Useful for validation in cross-table operations.\n */\n isFromTable(tableName: string): boolean {\n return this.tableName === tableName;\n }\n\n /**\n * Create a string representation for debugging.\n */\n toString(): string {\n return `${this.tableName}.${this.fieldName}`;\n }\n}\n\n/**\n * Type guard to check if a value is a Column instance.\n */\nexport function isColumn(value: any): value is Column<any, any, any> {\n return value instanceof Column;\n}\n"],"names":[],"mappings":";;;AAQO,MAAM,OAIX;AAAA,EAUA,YAAY,QAKT;AAdM;AACA;AACA;AACA;AAGA;AAAA;AACA;AAQP,SAAK,YAAY,OAAO;AACxB,SAAK,WAAW,OAAO;AACvB,SAAK,YAAY,OAAO;AACxB,SAAK,gBAAgB,OAAO;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO9B,mBAAmB,cAAgC;AAC7C,QAAA,gBAAgB,KAAK,UAAU;AACjC,aAAO,KAAK;AAAA,IAAA;AAEd,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd,mBAAmB,cAAgC;AAC7C,QAAA,gBAAgB,KAAK,eAAe;AACtC,aAAO,KAAK;AAAA,IAAA;AAEd,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd,YAAY,WAA4B;AACtC,WAAO,KAAK,cAAc;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAM5B,WAAmB;AACjB,WAAO,GAAG,KAAK,SAAS,IAAI,KAAK,SAAS;AAAA,EAAA;AAE9C;AAKO,SAAS,SAAS,OAA4C;AACnE,SAAO,iBAAiB;AAC1B;"}
1
+ {"version":3,"file":"column.js","sources":["../../../src/orm/column.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Column represents a type-safe reference to a table field.\n * Used in queries, filters, and operators to provide autocomplete and type checking.\n *\n * @template TOutput - The TypeScript type when reading from the database (output type)\n * @template TInput - The TypeScript type when writing to the database (input type, for filters)\n * @template TableName - The table name as a string literal type (for validation)\n * @template IsContainer - Whether this column represents a container field (cannot be selected)\n */\nexport class Column<\n TOutput = any,\n TInput = TOutput,\n TableName extends string = string,\n IsContainer extends boolean = false,\n> {\n readonly fieldName: string;\n readonly entityId?: `FMFID:${string}`;\n readonly tableName: TableName;\n readonly tableEntityId?: `FMTID:${string}`;\n readonly inputValidator?: StandardSchemaV1<TInput, any>;\n\n // Phantom types for TypeScript inference - never actually hold values\n readonly _phantomOutput!: TOutput;\n readonly _phantomInput!: TInput;\n readonly _isContainer!: IsContainer;\n\n constructor(config: {\n fieldName: string;\n entityId?: `FMFID:${string}`;\n tableName: TableName;\n tableEntityId?: `FMTID:${string}`;\n inputValidator?: StandardSchemaV1<TInput, any>;\n }) {\n this.fieldName = config.fieldName;\n this.entityId = config.entityId;\n this.tableName = config.tableName;\n this.tableEntityId = config.tableEntityId;\n this.inputValidator = config.inputValidator;\n }\n\n /**\n * Get the field identifier (entity ID if available, otherwise field name).\n * Used when building OData queries.\n */\n getFieldIdentifier(useEntityIds?: boolean): string {\n if (useEntityIds && this.entityId) {\n return this.entityId;\n }\n return this.fieldName;\n }\n\n /**\n * Get the table identifier (entity ID if available, otherwise table name).\n * Used when building OData queries.\n */\n getTableIdentifier(useEntityIds?: boolean): string {\n if (useEntityIds && this.tableEntityId) {\n return this.tableEntityId;\n }\n return this.tableName;\n }\n\n /**\n * Check if this column is from a specific table.\n * Useful for validation in cross-table operations.\n */\n isFromTable(tableName: string): boolean {\n return this.tableName === tableName;\n }\n\n /**\n * Create a string representation for debugging.\n */\n toString(): string {\n return `${this.tableName}.${this.fieldName}`;\n }\n}\n\n/**\n * Type guard to check if a value is a Column instance.\n */\nexport function isColumn(value: any): value is Column<any, any, any, any> {\n return value instanceof Column;\n}\n\n/**\n * Create a Column with proper type inference from the inputValidator.\n * This helper ensures TypeScript can infer TInput from the validator's input type.\n * @internal\n */\nexport function createColumn<\n TOutput,\n TInput,\n TName extends string,\n IsContainer extends boolean = false,\n>(config: {\n fieldName: string;\n entityId?: `FMFID:${string}`;\n tableName: TName;\n tableEntityId?: `FMTID:${string}`;\n inputValidator?: StandardSchemaV1<TInput, any>;\n}): Column<TOutput, TInput, TName, IsContainer> {\n return new Column(config) as Column<TOutput, TInput, TName, IsContainer>;\n}\n"],"names":[],"mappings":";;;AAWO,MAAM,OAKX;AAAA,EAYA,YAAY,QAMT;AAjBM;AACA;AACA;AACA;AACA;AAGA;AAAA;AACA;AACA;AASP,SAAK,YAAY,OAAO;AACxB,SAAK,WAAW,OAAO;AACvB,SAAK,YAAY,OAAO;AACxB,SAAK,gBAAgB,OAAO;AAC5B,SAAK,iBAAiB,OAAO;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAO/B,mBAAmB,cAAgC;AAC7C,QAAA,gBAAgB,KAAK,UAAU;AACjC,aAAO,KAAK;AAAA,IAAA;AAEd,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd,mBAAmB,cAAgC;AAC7C,QAAA,gBAAgB,KAAK,eAAe;AACtC,aAAO,KAAK;AAAA,IAAA;AAEd,WAAO,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOd,YAAY,WAA4B;AACtC,WAAO,KAAK,cAAc;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAM5B,WAAmB;AACjB,WAAO,GAAG,KAAK,SAAS,IAAI,KAAK,SAAS;AAAA,EAAA;AAE9C;AAKO,SAAS,SAAS,OAAiD;AACxE,SAAO,iBAAiB;AAC1B;"}
@@ -56,7 +56,7 @@ export declare class FieldBuilder<TOutput = any, TInput = TOutput, TDbType = TOu
56
56
  readValidator<O, VInput = TDbType>(validator: StandardSchemaV1<VInput, O>): FieldBuilder<O, TInput, TDbType, TReadOnly>;
57
57
  /**
58
58
  * Set a validator for the input (writing to database).
59
- * The input validator transforms/validates data going TO the database in insert and update operations.
59
+ * The input validator transforms/validates data going TO the database in insert, update, and filter operations.
60
60
  *
61
61
  * @example
62
62
  * numberField().writeValidator(z.boolean().transform(v => v ? 1 : 0))
@@ -64,7 +64,7 @@ class FieldBuilder {
64
64
  }
65
65
  /**
66
66
  * Set a validator for the input (writing to database).
67
- * The input validator transforms/validates data going TO the database in insert and update operations.
67
+ * The input validator transforms/validates data going TO the database in insert, update, and filter operations.
68
68
  *
69
69
  * @example
70
70
  * numberField().writeValidator(z.boolean().transform(v => v ? 1 : 0))
@@ -1 +1 @@
1
- {"version":3,"file":"field-builders.js","sources":["../../../src/orm/field-builders.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Branded type for container field's database type.\n * This allows TypeScript to distinguish container fields from regular string fields\n * at the type level, enabling compile-time exclusion from select operations.\n */\nexport type ContainerDbType = string & { readonly __container: true };\n\n/**\n * FieldBuilder provides a fluent API for defining table fields with type-safe metadata.\n * Supports chaining methods to configure primary keys, nullability, read-only status, entity IDs, and validators.\n *\n * @template TOutput - The output type after applying outputValidator (what you get when reading)\n * @template TInput - The input type after applying inputValidator (what you pass when writing)\n * @template TDbType - The database type (what FileMaker stores/expects)\n * @template TReadOnly - Whether this field is read-only (for type-level exclusion from insert/update)\n */\nexport class FieldBuilder<\n TOutput = any,\n TInput = TOutput,\n TDbType = TOutput,\n TReadOnly extends boolean = false,\n> {\n private _primaryKey = false;\n private _notNull = false;\n private _readOnly = false;\n private _entityId?: `FMFID:${string}`;\n private _outputValidator?: StandardSchemaV1<any, TOutput>;\n private _inputValidator?: StandardSchemaV1<TInput, any>;\n private _fieldType: string;\n\n constructor(fieldType: string) {\n this._fieldType = fieldType;\n }\n\n /**\n * Mark this field as the primary key for the table.\n * Primary keys are automatically read-only.\n */\n primaryKey(): FieldBuilder<TOutput, TInput, TDbType, true> {\n const builder = this._clone() as any;\n builder._primaryKey = true;\n builder._readOnly = true; // Primary keys are automatically read-only\n return builder;\n }\n\n /**\n * Mark this field as non-nullable.\n * Updates the type to exclude null/undefined.\n */\n notNull(): FieldBuilder<\n NonNullable<TOutput>,\n NonNullable<TInput>,\n NonNullable<TDbType>,\n TReadOnly\n > {\n const builder = this._clone() as any;\n builder._notNull = true;\n return builder;\n }\n\n /**\n * Mark this field as read-only.\n * Read-only fields are excluded from insert and update operations.\n */\n readOnly(): FieldBuilder<TOutput, TInput, TDbType, true> {\n const builder = this._clone() as any;\n builder._readOnly = true;\n return builder;\n }\n\n /**\n * Assign a FileMaker field ID (FMFID) to this field.\n * When useEntityIds is enabled, this ID will be used in API requests instead of the field name.\n */\n entityId(\n id: `FMFID:${string}`,\n ): FieldBuilder<TOutput, TInput, TDbType, TReadOnly> {\n const builder = this._clone();\n builder._entityId = id;\n return builder;\n }\n\n /**\n * Set a validator for the output (reading from database).\n * The output validator transforms/validates data coming FROM the database in list or get operations.\n *\n * @example\n * numberField().readValidator(z.coerce.boolean())\n * // FileMaker returns 0/1, you get true/false\n */\n readValidator<O, VInput = TDbType>(\n validator: StandardSchemaV1<VInput, O>,\n ): FieldBuilder<O, TInput, TDbType, TReadOnly> {\n const builder = this._clone() as any;\n builder._outputValidator = validator;\n return builder;\n }\n\n /**\n * Set a validator for the input (writing to database).\n * The input validator transforms/validates data going TO the database in insert and update operations.\n *\n * @example\n * numberField().writeValidator(z.boolean().transform(v => v ? 1 : 0))\n * // You pass true/false, FileMaker gets 1/0\n */\n writeValidator<I>(\n validator: StandardSchemaV1<I, TDbType>,\n ): FieldBuilder<TOutput, I, TDbType, TReadOnly> {\n const builder = this._clone() as any;\n builder._inputValidator = validator;\n return builder;\n }\n\n /**\n * Get the metadata configuration for this field.\n * @internal Used by fmTableOccurrence to extract field configuration\n */\n _getConfig() {\n return {\n fieldType: this._fieldType,\n primaryKey: this._primaryKey,\n notNull: this._notNull,\n readOnly: this._readOnly,\n entityId: this._entityId,\n outputValidator: this._outputValidator,\n inputValidator: this._inputValidator,\n };\n }\n\n /**\n * Clone this builder to allow immutable chaining.\n * @private\n */\n private _clone(): FieldBuilder<TOutput, TInput, TDbType, TReadOnly> {\n const builder = new FieldBuilder<TOutput, TInput, TDbType, TReadOnly>(\n this._fieldType,\n );\n builder._primaryKey = this._primaryKey;\n builder._notNull = this._notNull;\n builder._readOnly = this._readOnly;\n builder._entityId = this._entityId;\n builder._outputValidator = this._outputValidator;\n builder._inputValidator = this._inputValidator;\n return builder;\n }\n}\n\n/**\n * Create a text field (Edm.String in FileMaker OData).\n * By default, text fields are nullable.\n *\n * @example\n * textField() // string | null\n * textField().notNull() // string\n * textField().entityId(\"FMFID:1\") // with entity ID\n */\nexport function textField(): FieldBuilder<\n string | null,\n string | null,\n string | null,\n false\n> {\n return new FieldBuilder<string | null, string | null, string | null, false>(\n \"text\",\n );\n}\n\n/**\n * Create a number field (Edm.Decimal in FileMaker OData).\n * By default, number fields are nullable.\n *\n * @example\n * numberField() // number | null\n * numberField().notNull() // number\n * numberField().outputValidator(z.coerce.boolean()) // transform to boolean on read\n */\nexport function numberField(): FieldBuilder<\n number | null,\n number | null,\n number | null,\n false\n> {\n return new FieldBuilder<number | null, number | null, number | null, false>(\n \"number\",\n );\n}\n\n/**\n * Create a date field (Edm.Date in FileMaker OData).\n * By default, date fields are nullable and represented as ISO date strings (YYYY-MM-DD).\n *\n * @example\n * dateField() // string | null (ISO date format)\n * dateField().notNull() // string\n */\nexport function dateField(): FieldBuilder<\n string | null,\n string | null,\n string | null,\n false\n> {\n return new FieldBuilder<string | null, string | null, string | null, false>(\n \"date\",\n );\n}\n\n/**\n * Create a time field (Edm.TimeOfDay in FileMaker OData).\n * By default, time fields are nullable and represented as ISO time strings (HH:mm:ss).\n *\n * @example\n * timeField() // string | null (ISO time format)\n * timeField().notNull() // string\n */\nexport function timeField(): FieldBuilder<\n string | null,\n string | null,\n string | null,\n false\n> {\n return new FieldBuilder<string | null, string | null, string | null, false>(\n \"time\",\n );\n}\n\n/**\n * Create a timestamp field (Edm.DateTimeOffset in FileMaker OData).\n * By default, timestamp fields are nullable and represented as ISO 8601 strings.\n *\n * @example\n * timestampField() // string | null (ISO 8601 format)\n * timestampField().notNull() // string\n * timestampField().readOnly() // typical for CreationTimestamp\n */\nexport function timestampField(): FieldBuilder<\n string | null,\n string | null,\n string | null,\n false\n> {\n return new FieldBuilder<string | null, string | null, string | null, false>(\n \"timestamp\",\n );\n}\n\n/**\n * Create a container field (Edm.Stream in FileMaker OData).\n * Container fields store binary data and are represented as base64 strings in the API.\n * By default, container fields are nullable.\n *\n * Note: Container fields cannot be selected via .select() - they can only be accessed\n * via .getSingleField() due to FileMaker OData API limitations.\n *\n * @example\n * containerField() // string | null (base64 encoded)\n * containerField().notNull() // string\n */\nexport function containerField(): FieldBuilder<\n string | null,\n string | null,\n ContainerDbType | null,\n false\n> {\n return new FieldBuilder<\n string | null,\n string | null,\n ContainerDbType | null,\n false\n >(\"container\");\n}\n\n/**\n * Create a calculated field (read-only field computed by FileMaker).\n * Calculated fields are automatically marked as read-only.\n *\n * @example\n * calcField() // string | null\n * calcField().notNull() // string\n */\nexport function calcField(): FieldBuilder<\n string | null,\n string | null,\n string | null,\n true\n> {\n const builder = new FieldBuilder<\n string | null,\n string | null,\n string | null,\n false\n >(\"calculated\");\n return builder.readOnly();\n}\n"],"names":[],"mappings":";;;AAkBO,MAAM,aAKX;AAAA,EASA,YAAY,WAAmB;AARvB,uCAAc;AACd,oCAAW;AACX,qCAAY;AACZ;AACA;AACA;AACA;AAGN,SAAK,aAAa;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpB,aAA2D;AACnD,UAAA,UAAU,KAAK,OAAO;AAC5B,YAAQ,cAAc;AACtB,YAAQ,YAAY;AACb,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOT,UAKE;AACM,UAAA,UAAU,KAAK,OAAO;AAC5B,YAAQ,WAAW;AACZ,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOT,WAAyD;AACjD,UAAA,UAAU,KAAK,OAAO;AAC5B,YAAQ,YAAY;AACb,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOT,SACE,IACmD;AAC7C,UAAA,UAAU,KAAK,OAAO;AAC5B,YAAQ,YAAY;AACb,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWT,cACE,WAC6C;AACvC,UAAA,UAAU,KAAK,OAAO;AAC5B,YAAQ,mBAAmB;AACpB,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWT,eACE,WAC8C;AACxC,UAAA,UAAU,KAAK,OAAO;AAC5B,YAAQ,kBAAkB;AACnB,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOT,aAAa;AACJ,WAAA;AAAA,MACL,WAAW,KAAK;AAAA,MAChB,YAAY,KAAK;AAAA,MACjB,SAAS,KAAK;AAAA,MACd,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,iBAAiB,KAAK;AAAA,MACtB,gBAAgB,KAAK;AAAA,IACvB;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOM,SAA4D;AAClE,UAAM,UAAU,IAAI;AAAA,MAClB,KAAK;AAAA,IACP;AACA,YAAQ,cAAc,KAAK;AAC3B,YAAQ,WAAW,KAAK;AACxB,YAAQ,YAAY,KAAK;AACzB,YAAQ,YAAY,KAAK;AACzB,YAAQ,mBAAmB,KAAK;AAChC,YAAQ,kBAAkB,KAAK;AACxB,WAAA;AAAA,EAAA;AAEX;AAWO,SAAS,YAKd;AACA,SAAO,IAAI;AAAA,IACT;AAAA,EACF;AACF;AAWO,SAAS,cAKd;AACA,SAAO,IAAI;AAAA,IACT;AAAA,EACF;AACF;AAUO,SAAS,YAKd;AACA,SAAO,IAAI;AAAA,IACT;AAAA,EACF;AACF;AAUO,SAAS,YAKd;AACA,SAAO,IAAI;AAAA,IACT;AAAA,EACF;AACF;AAWO,SAAS,iBAKd;AACA,SAAO,IAAI;AAAA,IACT;AAAA,EACF;AACF;AAcO,SAAS,iBAKd;AACO,SAAA,IAAI,aAKT,WAAW;AACf;AAUO,SAAS,YAKd;AACM,QAAA,UAAU,IAAI,aAKlB,YAAY;AACd,SAAO,QAAQ,SAAS;AAC1B;"}
1
+ {"version":3,"file":"field-builders.js","sources":["../../../src/orm/field-builders.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/**\n * Branded type for container field's database type.\n * This allows TypeScript to distinguish container fields from regular string fields\n * at the type level, enabling compile-time exclusion from select operations.\n */\nexport type ContainerDbType = string & { readonly __container: true };\n\n/**\n * FieldBuilder provides a fluent API for defining table fields with type-safe metadata.\n * Supports chaining methods to configure primary keys, nullability, read-only status, entity IDs, and validators.\n *\n * @template TOutput - The output type after applying outputValidator (what you get when reading)\n * @template TInput - The input type after applying inputValidator (what you pass when writing)\n * @template TDbType - The database type (what FileMaker stores/expects)\n * @template TReadOnly - Whether this field is read-only (for type-level exclusion from insert/update)\n */\nexport class FieldBuilder<\n TOutput = any,\n TInput = TOutput,\n TDbType = TOutput,\n TReadOnly extends boolean = false,\n> {\n private _primaryKey = false;\n private _notNull = false;\n private _readOnly = false;\n private _entityId?: `FMFID:${string}`;\n private _outputValidator?: StandardSchemaV1<any, TOutput>;\n private _inputValidator?: StandardSchemaV1<TInput, any>;\n private _fieldType: string;\n\n constructor(fieldType: string) {\n this._fieldType = fieldType;\n }\n\n /**\n * Mark this field as the primary key for the table.\n * Primary keys are automatically read-only.\n */\n primaryKey(): FieldBuilder<TOutput, TInput, TDbType, true> {\n const builder = this._clone() as any;\n builder._primaryKey = true;\n builder._readOnly = true; // Primary keys are automatically read-only\n return builder;\n }\n\n /**\n * Mark this field as non-nullable.\n * Updates the type to exclude null/undefined.\n */\n notNull(): FieldBuilder<\n NonNullable<TOutput>,\n NonNullable<TInput>,\n NonNullable<TDbType>,\n TReadOnly\n > {\n const builder = this._clone() as any;\n builder._notNull = true;\n return builder;\n }\n\n /**\n * Mark this field as read-only.\n * Read-only fields are excluded from insert and update operations.\n */\n readOnly(): FieldBuilder<TOutput, TInput, TDbType, true> {\n const builder = this._clone() as any;\n builder._readOnly = true;\n return builder;\n }\n\n /**\n * Assign a FileMaker field ID (FMFID) to this field.\n * When useEntityIds is enabled, this ID will be used in API requests instead of the field name.\n */\n entityId(\n id: `FMFID:${string}`,\n ): FieldBuilder<TOutput, TInput, TDbType, TReadOnly> {\n const builder = this._clone();\n builder._entityId = id;\n return builder;\n }\n\n /**\n * Set a validator for the output (reading from database).\n * The output validator transforms/validates data coming FROM the database in list or get operations.\n *\n * @example\n * numberField().readValidator(z.coerce.boolean())\n * // FileMaker returns 0/1, you get true/false\n */\n readValidator<O, VInput = TDbType>(\n validator: StandardSchemaV1<VInput, O>,\n ): FieldBuilder<O, TInput, TDbType, TReadOnly> {\n const builder = this._clone() as any;\n builder._outputValidator = validator;\n return builder;\n }\n\n /**\n * Set a validator for the input (writing to database).\n * The input validator transforms/validates data going TO the database in insert, update, and filter operations.\n *\n * @example\n * numberField().writeValidator(z.boolean().transform(v => v ? 1 : 0))\n * // You pass true/false, FileMaker gets 1/0\n */\n writeValidator<I>(\n validator: StandardSchemaV1<I, TDbType>,\n ): FieldBuilder<TOutput, I, TDbType, TReadOnly> {\n const builder = this._clone() as any;\n builder._inputValidator = validator;\n return builder;\n }\n\n /**\n * Get the metadata configuration for this field.\n * @internal Used by fmTableOccurrence to extract field configuration\n */\n _getConfig() {\n return {\n fieldType: this._fieldType,\n primaryKey: this._primaryKey,\n notNull: this._notNull,\n readOnly: this._readOnly,\n entityId: this._entityId,\n outputValidator: this._outputValidator,\n inputValidator: this._inputValidator,\n };\n }\n\n /**\n * Clone this builder to allow immutable chaining.\n * @private\n */\n private _clone(): FieldBuilder<TOutput, TInput, TDbType, TReadOnly> {\n const builder = new FieldBuilder<TOutput, TInput, TDbType, TReadOnly>(\n this._fieldType,\n );\n builder._primaryKey = this._primaryKey;\n builder._notNull = this._notNull;\n builder._readOnly = this._readOnly;\n builder._entityId = this._entityId;\n builder._outputValidator = this._outputValidator;\n builder._inputValidator = this._inputValidator;\n return builder;\n }\n}\n\n/**\n * Create a text field (Edm.String in FileMaker OData).\n * By default, text fields are nullable.\n *\n * @example\n * textField() // string | null\n * textField().notNull() // string\n * textField().entityId(\"FMFID:1\") // with entity ID\n */\nexport function textField(): FieldBuilder<\n string | null,\n string | null,\n string | null,\n false\n> {\n return new FieldBuilder<string | null, string | null, string | null, false>(\n \"text\",\n );\n}\n\n/**\n * Create a number field (Edm.Decimal in FileMaker OData).\n * By default, number fields are nullable.\n *\n * @example\n * numberField() // number | null\n * numberField().notNull() // number\n * numberField().outputValidator(z.coerce.boolean()) // transform to boolean on read\n */\nexport function numberField(): FieldBuilder<\n number | null,\n number | null,\n number | null,\n false\n> {\n return new FieldBuilder<number | null, number | null, number | null, false>(\n \"number\",\n );\n}\n\n/**\n * Create a date field (Edm.Date in FileMaker OData).\n * By default, date fields are nullable and represented as ISO date strings (YYYY-MM-DD).\n *\n * @example\n * dateField() // string | null (ISO date format)\n * dateField().notNull() // string\n */\nexport function dateField(): FieldBuilder<\n string | null,\n string | null,\n string | null,\n false\n> {\n return new FieldBuilder<string | null, string | null, string | null, false>(\n \"date\",\n );\n}\n\n/**\n * Create a time field (Edm.TimeOfDay in FileMaker OData).\n * By default, time fields are nullable and represented as ISO time strings (HH:mm:ss).\n *\n * @example\n * timeField() // string | null (ISO time format)\n * timeField().notNull() // string\n */\nexport function timeField(): FieldBuilder<\n string | null,\n string | null,\n string | null,\n false\n> {\n return new FieldBuilder<string | null, string | null, string | null, false>(\n \"time\",\n );\n}\n\n/**\n * Create a timestamp field (Edm.DateTimeOffset in FileMaker OData).\n * By default, timestamp fields are nullable and represented as ISO 8601 strings.\n *\n * @example\n * timestampField() // string | null (ISO 8601 format)\n * timestampField().notNull() // string\n * timestampField().readOnly() // typical for CreationTimestamp\n */\nexport function timestampField(): FieldBuilder<\n string | null,\n string | null,\n string | null,\n false\n> {\n return new FieldBuilder<string | null, string | null, string | null, false>(\n \"timestamp\",\n );\n}\n\n/**\n * Create a container field (Edm.Stream in FileMaker OData).\n * Container fields store binary data and are represented as base64 strings in the API.\n * By default, container fields are nullable.\n *\n * Note: Container fields cannot be selected via .select() - they can only be accessed\n * via .getSingleField() due to FileMaker OData API limitations.\n *\n * @example\n * containerField() // string | null (base64 encoded)\n * containerField().notNull() // string\n */\nexport function containerField(): FieldBuilder<\n string | null,\n string | null,\n ContainerDbType | null,\n false\n> {\n return new FieldBuilder<\n string | null,\n string | null,\n ContainerDbType | null,\n false\n >(\"container\");\n}\n\n/**\n * Create a calculated field (read-only field computed by FileMaker).\n * Calculated fields are automatically marked as read-only.\n *\n * @example\n * calcField() // string | null\n * calcField().notNull() // string\n */\nexport function calcField(): FieldBuilder<\n string | null,\n string | null,\n string | null,\n true\n> {\n const builder = new FieldBuilder<\n string | null,\n string | null,\n string | null,\n false\n >(\"calculated\");\n return builder.readOnly();\n}\n"],"names":[],"mappings":";;;AAkBO,MAAM,aAKX;AAAA,EASA,YAAY,WAAmB;AARvB,uCAAc;AACd,oCAAW;AACX,qCAAY;AACZ;AACA;AACA;AACA;AAGN,SAAK,aAAa;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOpB,aAA2D;AACnD,UAAA,UAAU,KAAK,OAAO;AAC5B,YAAQ,cAAc;AACtB,YAAQ,YAAY;AACb,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOT,UAKE;AACM,UAAA,UAAU,KAAK,OAAO;AAC5B,YAAQ,WAAW;AACZ,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOT,WAAyD;AACjD,UAAA,UAAU,KAAK,OAAO;AAC5B,YAAQ,YAAY;AACb,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOT,SACE,IACmD;AAC7C,UAAA,UAAU,KAAK,OAAO;AAC5B,YAAQ,YAAY;AACb,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWT,cACE,WAC6C;AACvC,UAAA,UAAU,KAAK,OAAO;AAC5B,YAAQ,mBAAmB;AACpB,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWT,eACE,WAC8C;AACxC,UAAA,UAAU,KAAK,OAAO;AAC5B,YAAQ,kBAAkB;AACnB,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOT,aAAa;AACJ,WAAA;AAAA,MACL,WAAW,KAAK;AAAA,MAChB,YAAY,KAAK;AAAA,MACjB,SAAS,KAAK;AAAA,MACd,UAAU,KAAK;AAAA,MACf,UAAU,KAAK;AAAA,MACf,iBAAiB,KAAK;AAAA,MACtB,gBAAgB,KAAK;AAAA,IACvB;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOM,SAA4D;AAClE,UAAM,UAAU,IAAI;AAAA,MAClB,KAAK;AAAA,IACP;AACA,YAAQ,cAAc,KAAK;AAC3B,YAAQ,WAAW,KAAK;AACxB,YAAQ,YAAY,KAAK;AACzB,YAAQ,YAAY,KAAK;AACzB,YAAQ,mBAAmB,KAAK;AAChC,YAAQ,kBAAkB,KAAK;AACxB,WAAA;AAAA,EAAA;AAEX;AAWO,SAAS,YAKd;AACA,SAAO,IAAI;AAAA,IACT;AAAA,EACF;AACF;AAWO,SAAS,cAKd;AACA,SAAO,IAAI;AAAA,IACT;AAAA,EACF;AACF;AAUO,SAAS,YAKd;AACA,SAAO,IAAI;AAAA,IACT;AAAA,EACF;AACF;AAUO,SAAS,YAKd;AACA,SAAO,IAAI;AAAA,IACT;AAAA,EACF;AACF;AAWO,SAAS,iBAKd;AACA,SAAO,IAAI;AAAA,IACT;AAAA,EACF;AACF;AAcO,SAAS,iBAKd;AACO,SAAA,IAAI,aAKT,WAAW;AACf;AAUO,SAAS,YAKd;AACM,QAAA,UAAU,IAAI,aAKlB,YAAY;AACd,SAAO,QAAQ,SAAS;AAC1B;"}
@@ -29,8 +29,8 @@ export declare class FilterExpression {
29
29
  * eq(users.name, "John") // name equals "John"
30
30
  * eq(users.id, contacts.id_user) // cross-table comparison
31
31
  */
32
- export declare function eq<T>(column: Column<T>, value: T): FilterExpression;
33
- export declare function eq<T>(column1: Column<T>, column2: Column<T>): FilterExpression;
32
+ export declare function eq<TOutput, TInput>(column: Column<TOutput, TInput>, value: NoInfer<TInput>): FilterExpression;
33
+ export declare function eq<TOutput, TInput>(column1: Column<TOutput, TInput>, column2: Column<TOutput, TInput>): FilterExpression;
34
34
  /**
35
35
  * Not equal operator - checks if column does not equal a value or another column.
36
36
  *
@@ -38,85 +38,85 @@ export declare function eq<T>(column1: Column<T>, column2: Column<T>): FilterExp
38
38
  * ne(users.status, "inactive") // status not equal to "inactive"
39
39
  * ne(users.id, contacts.id_user) // cross-table comparison
40
40
  */
41
- export declare function ne<T>(column: Column<T>, value: T): FilterExpression;
42
- export declare function ne<T>(column1: Column<T>, column2: Column<T>): FilterExpression;
41
+ export declare function ne<TOutput, TInput>(column: Column<TOutput, TInput>, value: NoInfer<TInput>): FilterExpression;
42
+ export declare function ne<TOutput, TInput>(column1: Column<TOutput, TInput>, column2: Column<TOutput, TInput>): FilterExpression;
43
43
  /**
44
44
  * Greater than operator - checks if column is greater than a value.
45
45
  *
46
46
  * @example
47
47
  * gt(users.age, 18) // age greater than 18
48
48
  */
49
- export declare function gt<T extends number | string | Date | null>(column: Column<T>, value: T): FilterExpression;
49
+ export declare function gt<TOutput extends number | string | Date | null, TInput>(column: Column<TOutput, TInput>, value: NoInfer<TInput>): FilterExpression;
50
50
  /**
51
51
  * Greater than or equal operator - checks if column is >= a value.
52
52
  *
53
53
  * @example
54
54
  * gte(users.age, 18) // age >= 18
55
55
  */
56
- export declare function gte<T extends number | string | Date | null>(column: Column<T>, value: T): FilterExpression;
56
+ export declare function gte<TOutput extends number | string | Date | null, TInput>(column: Column<TOutput, TInput>, value: NoInfer<TInput>): FilterExpression;
57
57
  /**
58
58
  * Less than operator - checks if column is less than a value.
59
59
  *
60
60
  * @example
61
61
  * lt(users.age, 65) // age less than 65
62
62
  */
63
- export declare function lt<T extends number | string | Date | null>(column: Column<T>, value: T): FilterExpression;
63
+ export declare function lt<TOutput extends number | string | Date | null, TInput>(column: Column<TOutput, TInput>, value: NoInfer<TInput>): FilterExpression;
64
64
  /**
65
65
  * Less than or equal operator - checks if column is <= a value.
66
66
  *
67
67
  * @example
68
68
  * lte(users.age, 65) // age <= 65
69
69
  */
70
- export declare function lte<T extends number | string | Date | null>(column: Column<T>, value: T): FilterExpression;
70
+ export declare function lte<TOutput extends number | string | Date | null, TInput>(column: Column<TOutput, TInput>, value: NoInfer<TInput>): FilterExpression;
71
71
  /**
72
72
  * Contains operator - checks if a string column contains a substring.
73
73
  *
74
74
  * @example
75
75
  * contains(users.name, "John") // name contains "John"
76
76
  */
77
- export declare function contains(column: Column<string | null>, value: string): FilterExpression;
77
+ export declare function contains<TOutput, TInput>(column: Column<TOutput, TInput>, value: NoInfer<TInput>): FilterExpression;
78
78
  /**
79
79
  * Starts with operator - checks if a string column starts with a prefix.
80
80
  *
81
81
  * @example
82
82
  * startsWith(users.email, "admin") // email starts with "admin"
83
83
  */
84
- export declare function startsWith(column: Column<string | null>, value: string): FilterExpression;
84
+ export declare function startsWith<TOutput, TInput>(column: Column<TOutput, TInput>, value: NoInfer<TInput>): FilterExpression;
85
85
  /**
86
86
  * Ends with operator - checks if a string column ends with a suffix.
87
87
  *
88
88
  * @example
89
89
  * endsWith(users.email, "@example.com") // email ends with "@example.com"
90
90
  */
91
- export declare function endsWith(column: Column<string | null>, value: string): FilterExpression;
91
+ export declare function endsWith<TOutput, TInput>(column: Column<TOutput, TInput>, value: NoInfer<TInput>): FilterExpression;
92
92
  /**
93
93
  * In array operator - checks if column value is in an array of values.
94
94
  *
95
95
  * @example
96
96
  * inArray(users.status, ["active", "pending"]) // status is "active" or "pending"
97
97
  */
98
- export declare function inArray<T>(column: Column<T | null>, values: T[]): FilterExpression;
98
+ export declare function inArray<TOutput, TInput>(column: Column<TOutput, TInput>, values: NoInfer<TInput>[]): FilterExpression;
99
99
  /**
100
100
  * Not in array operator - checks if column value is not in an array of values.
101
101
  *
102
102
  * @example
103
103
  * notInArray(users.status, ["deleted", "banned"]) // status is neither "deleted" nor "banned"
104
104
  */
105
- export declare function notInArray<T>(column: Column<T>, values: T[]): FilterExpression;
105
+ export declare function notInArray<TOutput, TInput>(column: Column<TOutput, TInput>, values: NoInfer<TInput>[]): FilterExpression;
106
106
  /**
107
107
  * Is null operator - checks if column value is null.
108
108
  *
109
109
  * @example
110
110
  * isNull(users.deletedAt) // deletedAt is null
111
111
  */
112
- export declare function isNull<T>(column: Column<T>): FilterExpression;
112
+ export declare function isNull<TOutput, TInput>(column: Column<TOutput, TInput>): FilterExpression;
113
113
  /**
114
114
  * Is not null operator - checks if column value is not null.
115
115
  *
116
116
  * @example
117
117
  * isNotNull(users.email) // email is not null
118
118
  */
119
- export declare function isNotNull<T>(column: Column<T>): FilterExpression;
119
+ export declare function isNotNull<TOutput, TInput>(column: Column<TOutput, TInput>): FilterExpression;
120
120
  /**
121
121
  * AND operator - combines multiple filter expressions with logical AND.
122
122
  * All expressions must be true for the record to match.
@@ -151,9 +151,9 @@ export declare function not(expression: FilterExpression): FilterExpression;
151
151
  * Used in orderBy() clauses to provide type-safe sorting with direction.
152
152
  */
153
153
  export declare class OrderByExpression<TableName extends string = string> {
154
- readonly column: Column<any, TableName>;
154
+ readonly column: Column<any, any, TableName>;
155
155
  readonly direction: "asc" | "desc";
156
- constructor(column: Column<any, TableName>, direction: "asc" | "desc");
156
+ constructor(column: Column<any, any, TableName>, direction: "asc" | "desc");
157
157
  }
158
158
  /**
159
159
  * Type guard to check if a value is an OrderByExpression instance.
@@ -165,11 +165,11 @@ export declare function isOrderByExpression(value: any): value is OrderByExpress
165
165
  * @example
166
166
  * asc(users.name) // Sort by name ascending
167
167
  */
168
- export declare function asc<TableName extends string>(column: Column<any, TableName>): OrderByExpression<TableName>;
168
+ export declare function asc<TableName extends string>(column: Column<any, any, TableName>): OrderByExpression<TableName>;
169
169
  /**
170
170
  * Descending order operator - sorts a column in descending order.
171
171
  *
172
172
  * @example
173
173
  * desc(users.age) // Sort by age descending
174
174
  */
175
- export declare function desc<TableName extends string>(column: Column<any, TableName>): OrderByExpression<TableName>;
175
+ export declare function desc<TableName extends string>(column: Column<any, any, TableName>): OrderByExpression<TableName>;
@@ -52,26 +52,30 @@ class FilterExpression {
52
52
  }
53
53
  _binaryOp(op, useEntityIds) {
54
54
  const [left, right] = this.operands;
55
- const leftStr = this._operandToString(left, useEntityIds);
56
- const rightStr = this._operandToString(right, useEntityIds);
55
+ const columnForValue = isColumn(left) && !isColumn(right) ? left : isColumn(right) && !isColumn(left) ? right : void 0;
56
+ const leftStr = this._operandToString(left, useEntityIds, columnForValue);
57
+ const rightStr = this._operandToString(right, useEntityIds, columnForValue);
57
58
  return `${leftStr} ${op} ${rightStr}`;
58
59
  }
59
60
  _functionOp(fnName, useEntityIds) {
60
61
  const [column, value] = this.operands;
62
+ const columnInstance = isColumn(column) ? column : void 0;
61
63
  const columnStr = this._operandToString(column, useEntityIds);
62
- const valueStr = this._operandToString(value, useEntityIds);
64
+ const valueStr = this._operandToString(value, useEntityIds, columnInstance);
63
65
  return `${fnName}(${columnStr}, ${valueStr})`;
64
66
  }
65
67
  _inOp(useEntityIds) {
66
68
  const [column, values] = this.operands;
69
+ const columnInstance = isColumn(column) ? column : void 0;
67
70
  const columnStr = this._operandToString(column, useEntityIds);
68
- const valuesStr = values.map((v) => this._operandToString(v, useEntityIds)).join(", ");
71
+ const valuesStr = values.map((v) => this._operandToString(v, useEntityIds, columnInstance)).join(", ");
69
72
  return `${columnStr} in (${valuesStr})`;
70
73
  }
71
74
  _notInOp(useEntityIds) {
72
75
  const [column, values] = this.operands;
76
+ const columnInstance = isColumn(column) ? column : void 0;
73
77
  const columnStr = this._operandToString(column, useEntityIds);
74
- const valuesStr = values.map((v) => this._operandToString(v, useEntityIds)).join(", ");
78
+ const valuesStr = values.map((v) => this._operandToString(v, useEntityIds, columnInstance)).join(", ");
75
79
  return `not (${columnStr} in (${valuesStr}))`;
76
80
  }
77
81
  _isNullOp(useEntityIds) {
@@ -104,20 +108,35 @@ class FilterExpression {
104
108
  }
105
109
  throw new Error("NOT operator requires a FilterExpression operand");
106
110
  }
107
- _operandToString(operand, useEntityIds) {
111
+ _operandToString(operand, useEntityIds, column) {
108
112
  if (isColumn(operand)) {
109
113
  return operand.getFieldIdentifier(useEntityIds);
110
114
  }
111
- if (typeof operand === "string") {
112
- return `'${operand.replace(/'/g, "''")}'`;
115
+ let value = operand;
116
+ if (column == null ? void 0 : column.inputValidator) {
117
+ try {
118
+ const result = column.inputValidator["~standard"].validate(value);
119
+ if (result instanceof Promise) {
120
+ value = operand;
121
+ } else if ("issues" in result && result.issues) {
122
+ value = operand;
123
+ } else if ("value" in result) {
124
+ value = result.value;
125
+ }
126
+ } catch (error) {
127
+ value = operand;
128
+ }
129
+ }
130
+ if (typeof value === "string") {
131
+ return `'${value.replace(/'/g, "''")}'`;
113
132
  }
114
- if (operand === null || operand === void 0) {
133
+ if (value === null || value === void 0) {
115
134
  return "null";
116
135
  }
117
- if (operand instanceof Date) {
118
- return operand.toISOString();
136
+ if (value instanceof Date) {
137
+ return value.toISOString();
119
138
  }
120
- return String(operand);
139
+ return String(value);
121
140
  }
122
141
  }
123
142
  function eq(column, value) {
@@ -1 +1 @@
1
- {"version":3,"file":"operators.js","sources":["../../../src/orm/operators.ts"],"sourcesContent":["import type { Column } from \"./column\";\nimport { isColumn } from \"./column\";\n\n/**\n * FilterExpression represents a filter condition that can be used in where() clauses.\n * Internal representation of operator expressions that get converted to OData filter syntax.\n */\nexport class FilterExpression {\n constructor(\n public readonly operator: string,\n public readonly operands: (Column | any | FilterExpression)[],\n ) {}\n\n /**\n * Convert this expression to OData filter syntax.\n * @internal Used by QueryBuilder\n */\n toODataFilter(useEntityIds?: boolean): string {\n switch (this.operator) {\n // Comparison operators\n case \"eq\":\n return this._binaryOp(\"eq\", useEntityIds);\n case \"ne\":\n return this._binaryOp(\"ne\", useEntityIds);\n case \"gt\":\n return this._binaryOp(\"gt\", useEntityIds);\n case \"gte\":\n return this._binaryOp(\"ge\", useEntityIds);\n case \"lt\":\n return this._binaryOp(\"lt\", useEntityIds);\n case \"lte\":\n return this._binaryOp(\"le\", useEntityIds);\n case \"in\":\n return this._inOp(useEntityIds);\n case \"notIn\":\n return this._notInOp(useEntityIds);\n\n // String operators\n case \"contains\":\n return this._functionOp(\"contains\", useEntityIds);\n case \"startsWith\":\n return this._functionOp(\"startswith\", useEntityIds);\n case \"endsWith\":\n return this._functionOp(\"endswith\", useEntityIds);\n\n // Null checks\n case \"isNull\":\n return this._isNullOp(useEntityIds);\n case \"isNotNull\":\n return this._isNotNullOp(useEntityIds);\n\n // Logical operators\n case \"and\":\n return this._logicalOp(\"and\", useEntityIds);\n case \"or\":\n return this._logicalOp(\"or\", useEntityIds);\n case \"not\":\n return this._notOp(useEntityIds);\n\n default:\n throw new Error(`Unknown operator: ${this.operator}`);\n }\n }\n\n private _binaryOp(op: string, useEntityIds?: boolean): string {\n const [left, right] = this.operands;\n const leftStr = this._operandToString(left, useEntityIds);\n const rightStr = this._operandToString(right, useEntityIds);\n return `${leftStr} ${op} ${rightStr}`;\n }\n\n private _functionOp(fnName: string, useEntityIds?: boolean): string {\n const [column, value] = this.operands;\n const columnStr = this._operandToString(column, useEntityIds);\n const valueStr = this._operandToString(value, useEntityIds);\n return `${fnName}(${columnStr}, ${valueStr})`;\n }\n\n private _inOp(useEntityIds?: boolean): string {\n const [column, values] = this.operands;\n const columnStr = this._operandToString(column, useEntityIds);\n const valuesStr = (values as any[])\n .map((v) => this._operandToString(v, useEntityIds))\n .join(\", \");\n return `${columnStr} in (${valuesStr})`;\n }\n\n private _notInOp(useEntityIds?: boolean): string {\n const [column, values] = this.operands;\n const columnStr = this._operandToString(column, useEntityIds);\n const valuesStr = (values as any[])\n .map((v) => this._operandToString(v, useEntityIds))\n .join(\", \");\n return `not (${columnStr} in (${valuesStr}))`;\n }\n\n private _isNullOp(useEntityIds?: boolean): string {\n const [column] = this.operands;\n const columnStr = this._operandToString(column, useEntityIds);\n return `${columnStr} eq null`;\n }\n\n private _isNotNullOp(useEntityIds?: boolean): string {\n const [column] = this.operands;\n const columnStr = this._operandToString(column, useEntityIds);\n return `${columnStr} ne null`;\n }\n\n private _logicalOp(op: string, useEntityIds?: boolean): string {\n const expressions = this.operands.map((expr) => {\n if (expr instanceof FilterExpression) {\n const innerExpr = expr.toODataFilter(useEntityIds);\n // Wrap in parens if it's a logical expression to ensure precedence\n if (expr.operator === \"and\" || expr.operator === \"or\") {\n return `(${innerExpr})`;\n }\n return innerExpr;\n }\n throw new Error(\"Logical operators require FilterExpression operands\");\n });\n return expressions.join(` ${op} `);\n }\n\n private _notOp(useEntityIds?: boolean): string {\n const [expr] = this.operands;\n if (expr instanceof FilterExpression) {\n return `not (${expr.toODataFilter(useEntityIds)})`;\n }\n throw new Error(\"NOT operator requires a FilterExpression operand\");\n }\n\n private _operandToString(operand: any, useEntityIds?: boolean): string {\n if (isColumn(operand)) {\n return operand.getFieldIdentifier(useEntityIds);\n }\n if (typeof operand === \"string\") {\n return `'${operand.replace(/'/g, \"''\")}'`; // Escape single quotes\n }\n if (operand === null || operand === undefined) {\n return \"null\";\n }\n if (operand instanceof Date) {\n return operand.toISOString();\n }\n return String(operand);\n }\n}\n\n// ============================================================================\n// Comparison Operators\n// ============================================================================\n\n/**\n * Equal operator - checks if column equals a value or another column.\n *\n * @example\n * eq(users.name, \"John\") // name equals \"John\"\n * eq(users.id, contacts.id_user) // cross-table comparison\n */\nexport function eq<T>(column: Column<T>, value: T): FilterExpression;\nexport function eq<T>(column1: Column<T>, column2: Column<T>): FilterExpression;\nexport function eq(column: Column, value: any): FilterExpression {\n return new FilterExpression(\"eq\", [column, value]);\n}\n\n/**\n * Not equal operator - checks if column does not equal a value or another column.\n *\n * @example\n * ne(users.status, \"inactive\") // status not equal to \"inactive\"\n * ne(users.id, contacts.id_user) // cross-table comparison\n */\nexport function ne<T>(column: Column<T>, value: T): FilterExpression;\nexport function ne<T>(column1: Column<T>, column2: Column<T>): FilterExpression;\nexport function ne(column: Column, value: any): FilterExpression {\n return new FilterExpression(\"ne\", [column, value]);\n}\n\n/**\n * Greater than operator - checks if column is greater than a value.\n *\n * @example\n * gt(users.age, 18) // age greater than 18\n */\nexport function gt<T extends number | string | Date | null>(\n column: Column<T>,\n value: T,\n): FilterExpression {\n return new FilterExpression(\"gt\", [column, value]);\n}\n\n/**\n * Greater than or equal operator - checks if column is >= a value.\n *\n * @example\n * gte(users.age, 18) // age >= 18\n */\nexport function gte<T extends number | string | Date | null>(\n column: Column<T>,\n value: T,\n): FilterExpression {\n return new FilterExpression(\"gte\", [column, value]);\n}\n\n/**\n * Less than operator - checks if column is less than a value.\n *\n * @example\n * lt(users.age, 65) // age less than 65\n */\nexport function lt<T extends number | string | Date | null>(\n column: Column<T>,\n value: T,\n): FilterExpression {\n return new FilterExpression(\"lt\", [column, value]);\n}\n\n/**\n * Less than or equal operator - checks if column is <= a value.\n *\n * @example\n * lte(users.age, 65) // age <= 65\n */\nexport function lte<T extends number | string | Date | null>(\n column: Column<T>,\n value: T,\n): FilterExpression {\n return new FilterExpression(\"lte\", [column, value]);\n}\n\n// ============================================================================\n// String Operators\n// ============================================================================\n\n/**\n * Contains operator - checks if a string column contains a substring.\n *\n * @example\n * contains(users.name, \"John\") // name contains \"John\"\n */\nexport function contains(\n column: Column<string | null>,\n value: string,\n): FilterExpression {\n return new FilterExpression(\"contains\", [column, value]);\n}\n\n/**\n * Starts with operator - checks if a string column starts with a prefix.\n *\n * @example\n * startsWith(users.email, \"admin\") // email starts with \"admin\"\n */\nexport function startsWith(\n column: Column<string | null>,\n value: string,\n): FilterExpression {\n return new FilterExpression(\"startsWith\", [column, value]);\n}\n\n/**\n * Ends with operator - checks if a string column ends with a suffix.\n *\n * @example\n * endsWith(users.email, \"@example.com\") // email ends with \"@example.com\"\n */\nexport function endsWith(\n column: Column<string | null>,\n value: string,\n): FilterExpression {\n return new FilterExpression(\"endsWith\", [column, value]);\n}\n\n// ============================================================================\n// Array Operators\n// ============================================================================\n\n/**\n * In array operator - checks if column value is in an array of values.\n *\n * @example\n * inArray(users.status, [\"active\", \"pending\"]) // status is \"active\" or \"pending\"\n */\nexport function inArray<T>(\n column: Column<T | null>,\n values: T[],\n): FilterExpression {\n return new FilterExpression(\"in\", [column, values]);\n}\n\n/**\n * Not in array operator - checks if column value is not in an array of values.\n *\n * @example\n * notInArray(users.status, [\"deleted\", \"banned\"]) // status is neither \"deleted\" nor \"banned\"\n */\nexport function notInArray<T>(\n column: Column<T>,\n values: T[],\n): FilterExpression {\n return new FilterExpression(\"notIn\", [column, values]);\n}\n\n// ============================================================================\n// Null Check Operators\n// ============================================================================\n\n/**\n * Is null operator - checks if column value is null.\n *\n * @example\n * isNull(users.deletedAt) // deletedAt is null\n */\nexport function isNull<T>(column: Column<T>): FilterExpression {\n return new FilterExpression(\"isNull\", [column]);\n}\n\n/**\n * Is not null operator - checks if column value is not null.\n *\n * @example\n * isNotNull(users.email) // email is not null\n */\nexport function isNotNull<T>(column: Column<T>): FilterExpression {\n return new FilterExpression(\"isNotNull\", [column]);\n}\n\n// ============================================================================\n// Logical Operators\n// ============================================================================\n\n/**\n * AND operator - combines multiple filter expressions with logical AND.\n * All expressions must be true for the record to match.\n *\n * @example\n * and(\n * eq(users.active, true),\n * gt(users.age, 18)\n * ) // active is true AND age > 18\n */\nexport function and(...expressions: FilterExpression[]): FilterExpression {\n if (expressions.length === 0) {\n throw new Error(\"AND operator requires at least one expression\");\n }\n if (expressions.length === 1 && expressions[0] !== undefined) {\n return expressions[0];\n }\n return new FilterExpression(\"and\", expressions);\n}\n\n/**\n * OR operator - combines multiple filter expressions with logical OR.\n * At least one expression must be true for the record to match.\n *\n * @example\n * or(\n * eq(users.role, \"admin\"),\n * eq(users.role, \"moderator\")\n * ) // role is \"admin\" OR \"moderator\"\n */\nexport function or(...expressions: FilterExpression[]): FilterExpression {\n if (expressions.length === 0) {\n throw new Error(\"OR operator requires at least one expression\");\n }\n if (expressions.length === 1 && expressions[0] !== undefined) {\n return expressions[0];\n }\n return new FilterExpression(\"or\", expressions);\n}\n\n/**\n * NOT operator - negates a filter expression.\n *\n * @example\n * not(eq(users.status, \"deleted\")) // status is NOT \"deleted\"\n */\nexport function not(expression: FilterExpression): FilterExpression {\n return new FilterExpression(\"not\", [expression]);\n}\n\n// ============================================================================\n// OrderBy Operators\n// ============================================================================\n\n/**\n * OrderByExpression represents a sort order specification for a column.\n * Used in orderBy() clauses to provide type-safe sorting with direction.\n */\nexport class OrderByExpression<TableName extends string = string> {\n constructor(\n public readonly column: Column<any, TableName>,\n public readonly direction: \"asc\" | \"desc\",\n ) {}\n}\n\n/**\n * Type guard to check if a value is an OrderByExpression instance.\n */\nexport function isOrderByExpression(\n value: any,\n): value is OrderByExpression {\n return value instanceof OrderByExpression;\n}\n\n/**\n * Ascending order operator - sorts a column in ascending order.\n *\n * @example\n * asc(users.name) // Sort by name ascending\n */\nexport function asc<TableName extends string>(\n column: Column<any, TableName>,\n): OrderByExpression<TableName> {\n return new OrderByExpression(column, \"asc\");\n}\n\n/**\n * Descending order operator - sorts a column in descending order.\n *\n * @example\n * desc(users.age) // Sort by age descending\n */\nexport function desc<TableName extends string>(\n column: Column<any, TableName>,\n): OrderByExpression<TableName> {\n return new OrderByExpression(column, \"desc\");\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"operators.js","sources":["../../../src/orm/operators.ts"],"sourcesContent":["import type { Column } from \"./column\";\nimport { isColumn } from \"./column\";\n\n/**\n * FilterExpression represents a filter condition that can be used in where() clauses.\n * Internal representation of operator expressions that get converted to OData filter syntax.\n */\nexport class FilterExpression {\n constructor(\n public readonly operator: string,\n public readonly operands: (Column | any | FilterExpression)[],\n ) {}\n\n /**\n * Convert this expression to OData filter syntax.\n * @internal Used by QueryBuilder\n */\n toODataFilter(useEntityIds?: boolean): string {\n switch (this.operator) {\n // Comparison operators\n case \"eq\":\n return this._binaryOp(\"eq\", useEntityIds);\n case \"ne\":\n return this._binaryOp(\"ne\", useEntityIds);\n case \"gt\":\n return this._binaryOp(\"gt\", useEntityIds);\n case \"gte\":\n return this._binaryOp(\"ge\", useEntityIds);\n case \"lt\":\n return this._binaryOp(\"lt\", useEntityIds);\n case \"lte\":\n return this._binaryOp(\"le\", useEntityIds);\n case \"in\":\n return this._inOp(useEntityIds);\n case \"notIn\":\n return this._notInOp(useEntityIds);\n\n // String operators\n case \"contains\":\n return this._functionOp(\"contains\", useEntityIds);\n case \"startsWith\":\n return this._functionOp(\"startswith\", useEntityIds);\n case \"endsWith\":\n return this._functionOp(\"endswith\", useEntityIds);\n\n // Null checks\n case \"isNull\":\n return this._isNullOp(useEntityIds);\n case \"isNotNull\":\n return this._isNotNullOp(useEntityIds);\n\n // Logical operators\n case \"and\":\n return this._logicalOp(\"and\", useEntityIds);\n case \"or\":\n return this._logicalOp(\"or\", useEntityIds);\n case \"not\":\n return this._notOp(useEntityIds);\n\n default:\n throw new Error(`Unknown operator: ${this.operator}`);\n }\n }\n\n private _binaryOp(op: string, useEntityIds?: boolean): string {\n const [left, right] = this.operands;\n // For binary ops, the column is typically the first operand and value is the second\n // But we also support column-to-column comparisons, so check both\n const columnForValue =\n isColumn(left) && !isColumn(right)\n ? left\n : isColumn(right) && !isColumn(left)\n ? right\n : undefined;\n const leftStr = this._operandToString(left, useEntityIds, columnForValue);\n const rightStr = this._operandToString(right, useEntityIds, columnForValue);\n return `${leftStr} ${op} ${rightStr}`;\n }\n\n private _functionOp(fnName: string, useEntityIds?: boolean): string {\n const [column, value] = this.operands;\n const columnInstance = isColumn(column) ? column : undefined;\n const columnStr = this._operandToString(column, useEntityIds);\n const valueStr = this._operandToString(value, useEntityIds, columnInstance);\n return `${fnName}(${columnStr}, ${valueStr})`;\n }\n\n private _inOp(useEntityIds?: boolean): string {\n const [column, values] = this.operands;\n const columnInstance = isColumn(column) ? column : undefined;\n const columnStr = this._operandToString(column, useEntityIds);\n const valuesStr = (values as any[])\n .map((v) => this._operandToString(v, useEntityIds, columnInstance))\n .join(\", \");\n return `${columnStr} in (${valuesStr})`;\n }\n\n private _notInOp(useEntityIds?: boolean): string {\n const [column, values] = this.operands;\n const columnInstance = isColumn(column) ? column : undefined;\n const columnStr = this._operandToString(column, useEntityIds);\n const valuesStr = (values as any[])\n .map((v) => this._operandToString(v, useEntityIds, columnInstance))\n .join(\", \");\n return `not (${columnStr} in (${valuesStr}))`;\n }\n\n private _isNullOp(useEntityIds?: boolean): string {\n const [column] = this.operands;\n const columnStr = this._operandToString(column, useEntityIds);\n return `${columnStr} eq null`;\n }\n\n private _isNotNullOp(useEntityIds?: boolean): string {\n const [column] = this.operands;\n const columnStr = this._operandToString(column, useEntityIds);\n return `${columnStr} ne null`;\n }\n\n private _logicalOp(op: string, useEntityIds?: boolean): string {\n const expressions = this.operands.map((expr) => {\n if (expr instanceof FilterExpression) {\n const innerExpr = expr.toODataFilter(useEntityIds);\n // Wrap in parens if it's a logical expression to ensure precedence\n if (expr.operator === \"and\" || expr.operator === \"or\") {\n return `(${innerExpr})`;\n }\n return innerExpr;\n }\n throw new Error(\"Logical operators require FilterExpression operands\");\n });\n return expressions.join(` ${op} `);\n }\n\n private _notOp(useEntityIds?: boolean): string {\n const [expr] = this.operands;\n if (expr instanceof FilterExpression) {\n return `not (${expr.toODataFilter(useEntityIds)})`;\n }\n throw new Error(\"NOT operator requires a FilterExpression operand\");\n }\n\n private _operandToString(\n operand: any,\n useEntityIds?: boolean,\n column?: Column<any, any, any, any>,\n ): string {\n if (isColumn(operand)) {\n return operand.getFieldIdentifier(useEntityIds);\n }\n\n // If we have a column with an input validator, apply it to transform the value\n let value = operand;\n if (column?.inputValidator) {\n try {\n const result = column.inputValidator[\"~standard\"].validate(value);\n // Handle async validators (though they shouldn't be async for filters)\n if (result instanceof Promise) {\n // For filters, we can't use async validators, so skip transformation\n // This is a limitation - async validators won't work in filters\n value = operand;\n } else if (\"issues\" in result && result.issues) {\n // Validation failed, use original value\n value = operand;\n } else if (\"value\" in result) {\n // Validation succeeded, use transformed value\n value = result.value;\n }\n } catch (error) {\n // If validation throws, use the original value (will likely cause a query error)\n // This maintains backward compatibility and allows the server to handle validation\n value = operand;\n }\n }\n\n if (typeof value === \"string\") {\n return `'${value.replace(/'/g, \"''\")}'`; // Escape single quotes\n }\n if (value === null || value === undefined) {\n return \"null\";\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n return String(value);\n }\n}\n\n// ============================================================================\n// Comparison Operators\n// ============================================================================\n\n/**\n * Equal operator - checks if column equals a value or another column.\n *\n * @example\n * eq(users.name, \"John\") // name equals \"John\"\n * eq(users.id, contacts.id_user) // cross-table comparison\n */\nexport function eq<TOutput, TInput>(\n column: Column<TOutput, TInput>,\n value: NoInfer<TInput>,\n): FilterExpression;\nexport function eq<TOutput, TInput>(\n column1: Column<TOutput, TInput>,\n column2: Column<TOutput, TInput>,\n): FilterExpression;\nexport function eq(column: Column, value: any): FilterExpression {\n return new FilterExpression(\"eq\", [column, value]);\n}\n\n/**\n * Not equal operator - checks if column does not equal a value or another column.\n *\n * @example\n * ne(users.status, \"inactive\") // status not equal to \"inactive\"\n * ne(users.id, contacts.id_user) // cross-table comparison\n */\nexport function ne<TOutput, TInput>(\n column: Column<TOutput, TInput>,\n value: NoInfer<TInput>,\n): FilterExpression;\nexport function ne<TOutput, TInput>(\n column1: Column<TOutput, TInput>,\n column2: Column<TOutput, TInput>,\n): FilterExpression;\nexport function ne(column: Column, value: any): FilterExpression {\n return new FilterExpression(\"ne\", [column, value]);\n}\n\n/**\n * Greater than operator - checks if column is greater than a value.\n *\n * @example\n * gt(users.age, 18) // age greater than 18\n */\nexport function gt<TOutput extends number | string | Date | null, TInput>(\n column: Column<TOutput, TInput>,\n value: NoInfer<TInput>,\n): FilterExpression {\n return new FilterExpression(\"gt\", [column, value]);\n}\n\n/**\n * Greater than or equal operator - checks if column is >= a value.\n *\n * @example\n * gte(users.age, 18) // age >= 18\n */\nexport function gte<TOutput extends number | string | Date | null, TInput>(\n column: Column<TOutput, TInput>,\n value: NoInfer<TInput>,\n): FilterExpression {\n return new FilterExpression(\"gte\", [column, value]);\n}\n\n/**\n * Less than operator - checks if column is less than a value.\n *\n * @example\n * lt(users.age, 65) // age less than 65\n */\nexport function lt<TOutput extends number | string | Date | null, TInput>(\n column: Column<TOutput, TInput>,\n value: NoInfer<TInput>,\n): FilterExpression {\n return new FilterExpression(\"lt\", [column, value]);\n}\n\n/**\n * Less than or equal operator - checks if column is <= a value.\n *\n * @example\n * lte(users.age, 65) // age <= 65\n */\nexport function lte<TOutput extends number | string | Date | null, TInput>(\n column: Column<TOutput, TInput>,\n value: NoInfer<TInput>,\n): FilterExpression {\n return new FilterExpression(\"lte\", [column, value]);\n}\n\n// ============================================================================\n// String Operators\n// ============================================================================\n\n/**\n * Contains operator - checks if a string column contains a substring.\n *\n * @example\n * contains(users.name, \"John\") // name contains \"John\"\n */\nexport function contains<TOutput, TInput>(\n column: Column<TOutput, TInput>,\n value: NoInfer<TInput>,\n): FilterExpression {\n return new FilterExpression(\"contains\", [column, value]);\n}\n\n/**\n * Starts with operator - checks if a string column starts with a prefix.\n *\n * @example\n * startsWith(users.email, \"admin\") // email starts with \"admin\"\n */\nexport function startsWith<TOutput, TInput>(\n column: Column<TOutput, TInput>,\n value: NoInfer<TInput>,\n): FilterExpression {\n return new FilterExpression(\"startsWith\", [column, value]);\n}\n\n/**\n * Ends with operator - checks if a string column ends with a suffix.\n *\n * @example\n * endsWith(users.email, \"@example.com\") // email ends with \"@example.com\"\n */\nexport function endsWith<TOutput, TInput>(\n column: Column<TOutput, TInput>,\n value: NoInfer<TInput>,\n): FilterExpression {\n return new FilterExpression(\"endsWith\", [column, value]);\n}\n\n// ============================================================================\n// Array Operators\n// ============================================================================\n\n/**\n * In array operator - checks if column value is in an array of values.\n *\n * @example\n * inArray(users.status, [\"active\", \"pending\"]) // status is \"active\" or \"pending\"\n */\nexport function inArray<TOutput, TInput>(\n column: Column<TOutput, TInput>,\n values: NoInfer<TInput>[],\n): FilterExpression {\n return new FilterExpression(\"in\", [column, values]);\n}\n\n/**\n * Not in array operator - checks if column value is not in an array of values.\n *\n * @example\n * notInArray(users.status, [\"deleted\", \"banned\"]) // status is neither \"deleted\" nor \"banned\"\n */\nexport function notInArray<TOutput, TInput>(\n column: Column<TOutput, TInput>,\n values: NoInfer<TInput>[],\n): FilterExpression {\n return new FilterExpression(\"notIn\", [column, values]);\n}\n\n// ============================================================================\n// Null Check Operators\n// ============================================================================\n\n/**\n * Is null operator - checks if column value is null.\n *\n * @example\n * isNull(users.deletedAt) // deletedAt is null\n */\nexport function isNull<TOutput, TInput>(\n column: Column<TOutput, TInput>,\n): FilterExpression {\n return new FilterExpression(\"isNull\", [column]);\n}\n\n/**\n * Is not null operator - checks if column value is not null.\n *\n * @example\n * isNotNull(users.email) // email is not null\n */\nexport function isNotNull<TOutput, TInput>(\n column: Column<TOutput, TInput>,\n): FilterExpression {\n return new FilterExpression(\"isNotNull\", [column]);\n}\n\n// ============================================================================\n// Logical Operators\n// ============================================================================\n\n/**\n * AND operator - combines multiple filter expressions with logical AND.\n * All expressions must be true for the record to match.\n *\n * @example\n * and(\n * eq(users.active, true),\n * gt(users.age, 18)\n * ) // active is true AND age > 18\n */\nexport function and(...expressions: FilterExpression[]): FilterExpression {\n if (expressions.length === 0) {\n throw new Error(\"AND operator requires at least one expression\");\n }\n if (expressions.length === 1 && expressions[0] !== undefined) {\n return expressions[0];\n }\n return new FilterExpression(\"and\", expressions);\n}\n\n/**\n * OR operator - combines multiple filter expressions with logical OR.\n * At least one expression must be true for the record to match.\n *\n * @example\n * or(\n * eq(users.role, \"admin\"),\n * eq(users.role, \"moderator\")\n * ) // role is \"admin\" OR \"moderator\"\n */\nexport function or(...expressions: FilterExpression[]): FilterExpression {\n if (expressions.length === 0) {\n throw new Error(\"OR operator requires at least one expression\");\n }\n if (expressions.length === 1 && expressions[0] !== undefined) {\n return expressions[0];\n }\n return new FilterExpression(\"or\", expressions);\n}\n\n/**\n * NOT operator - negates a filter expression.\n *\n * @example\n * not(eq(users.status, \"deleted\")) // status is NOT \"deleted\"\n */\nexport function not(expression: FilterExpression): FilterExpression {\n return new FilterExpression(\"not\", [expression]);\n}\n\n// ============================================================================\n// OrderBy Operators\n// ============================================================================\n\n/**\n * OrderByExpression represents a sort order specification for a column.\n * Used in orderBy() clauses to provide type-safe sorting with direction.\n */\nexport class OrderByExpression<TableName extends string = string> {\n constructor(\n public readonly column: Column<any, any, TableName>,\n public readonly direction: \"asc\" | \"desc\",\n ) {}\n}\n\n/**\n * Type guard to check if a value is an OrderByExpression instance.\n */\nexport function isOrderByExpression(value: any): value is OrderByExpression {\n return value instanceof OrderByExpression;\n}\n\n/**\n * Ascending order operator - sorts a column in ascending order.\n *\n * @example\n * asc(users.name) // Sort by name ascending\n */\nexport function asc<TableName extends string>(\n column: Column<any, any, TableName>,\n): OrderByExpression<TableName> {\n return new OrderByExpression(column, \"asc\");\n}\n\n/**\n * Descending order operator - sorts a column in descending order.\n *\n * @example\n * desc(users.age) // Sort by age descending\n */\nexport function desc<TableName extends string>(\n column: Column<any, any, TableName>,\n): OrderByExpression<TableName> {\n return new OrderByExpression(column, \"desc\");\n}\n"],"names":[],"mappings":";AAOO,MAAM,iBAAiB;AAAA,EAC5B,YACkB,UACA,UAChB;AAFgB,SAAA,WAAA;AACA,SAAA,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOlB,cAAc,cAAgC;AAC5C,YAAQ,KAAK,UAAU;AAAA;AAAA,MAErB,KAAK;AACI,eAAA,KAAK,UAAU,MAAM,YAAY;AAAA,MAC1C,KAAK;AACI,eAAA,KAAK,UAAU,MAAM,YAAY;AAAA,MAC1C,KAAK;AACI,eAAA,KAAK,UAAU,MAAM,YAAY;AAAA,MAC1C,KAAK;AACI,eAAA,KAAK,UAAU,MAAM,YAAY;AAAA,MAC1C,KAAK;AACI,eAAA,KAAK,UAAU,MAAM,YAAY;AAAA,MAC1C,KAAK;AACI,eAAA,KAAK,UAAU,MAAM,YAAY;AAAA,MAC1C,KAAK;AACI,eAAA,KAAK,MAAM,YAAY;AAAA,MAChC,KAAK;AACI,eAAA,KAAK,SAAS,YAAY;AAAA;AAAA,MAGnC,KAAK;AACI,eAAA,KAAK,YAAY,YAAY,YAAY;AAAA,MAClD,KAAK;AACI,eAAA,KAAK,YAAY,cAAc,YAAY;AAAA,MACpD,KAAK;AACI,eAAA,KAAK,YAAY,YAAY,YAAY;AAAA;AAAA,MAGlD,KAAK;AACI,eAAA,KAAK,UAAU,YAAY;AAAA,MACpC,KAAK;AACI,eAAA,KAAK,aAAa,YAAY;AAAA;AAAA,MAGvC,KAAK;AACI,eAAA,KAAK,WAAW,OAAO,YAAY;AAAA,MAC5C,KAAK;AACI,eAAA,KAAK,WAAW,MAAM,YAAY;AAAA,MAC3C,KAAK;AACI,eAAA,KAAK,OAAO,YAAY;AAAA,MAEjC;AACE,cAAM,IAAI,MAAM,qBAAqB,KAAK,QAAQ,EAAE;AAAA,IAAA;AAAA,EACxD;AAAA,EAGM,UAAU,IAAY,cAAgC;AAC5D,UAAM,CAAC,MAAM,KAAK,IAAI,KAAK;AAG3B,UAAM,iBACJ,SAAS,IAAI,KAAK,CAAC,SAAS,KAAK,IAC7B,OACA,SAAS,KAAK,KAAK,CAAC,SAAS,IAAI,IAC/B,QACA;AACR,UAAM,UAAU,KAAK,iBAAiB,MAAM,cAAc,cAAc;AACxE,UAAM,WAAW,KAAK,iBAAiB,OAAO,cAAc,cAAc;AAC1E,WAAO,GAAG,OAAO,IAAI,EAAE,IAAI,QAAQ;AAAA,EAAA;AAAA,EAG7B,YAAY,QAAgB,cAAgC;AAClE,UAAM,CAAC,QAAQ,KAAK,IAAI,KAAK;AAC7B,UAAM,iBAAiB,SAAS,MAAM,IAAI,SAAS;AACnD,UAAM,YAAY,KAAK,iBAAiB,QAAQ,YAAY;AAC5D,UAAM,WAAW,KAAK,iBAAiB,OAAO,cAAc,cAAc;AAC1E,WAAO,GAAG,MAAM,IAAI,SAAS,KAAK,QAAQ;AAAA,EAAA;AAAA,EAGpC,MAAM,cAAgC;AAC5C,UAAM,CAAC,QAAQ,MAAM,IAAI,KAAK;AAC9B,UAAM,iBAAiB,SAAS,MAAM,IAAI,SAAS;AACnD,UAAM,YAAY,KAAK,iBAAiB,QAAQ,YAAY;AAC5D,UAAM,YAAa,OAChB,IAAI,CAAC,MAAM,KAAK,iBAAiB,GAAG,cAAc,cAAc,CAAC,EACjE,KAAK,IAAI;AACL,WAAA,GAAG,SAAS,QAAQ,SAAS;AAAA,EAAA;AAAA,EAG9B,SAAS,cAAgC;AAC/C,UAAM,CAAC,QAAQ,MAAM,IAAI,KAAK;AAC9B,UAAM,iBAAiB,SAAS,MAAM,IAAI,SAAS;AACnD,UAAM,YAAY,KAAK,iBAAiB,QAAQ,YAAY;AAC5D,UAAM,YAAa,OAChB,IAAI,CAAC,MAAM,KAAK,iBAAiB,GAAG,cAAc,cAAc,CAAC,EACjE,KAAK,IAAI;AACL,WAAA,QAAQ,SAAS,QAAQ,SAAS;AAAA,EAAA;AAAA,EAGnC,UAAU,cAAgC;AAC1C,UAAA,CAAC,MAAM,IAAI,KAAK;AACtB,UAAM,YAAY,KAAK,iBAAiB,QAAQ,YAAY;AAC5D,WAAO,GAAG,SAAS;AAAA,EAAA;AAAA,EAGb,aAAa,cAAgC;AAC7C,UAAA,CAAC,MAAM,IAAI,KAAK;AACtB,UAAM,YAAY,KAAK,iBAAiB,QAAQ,YAAY;AAC5D,WAAO,GAAG,SAAS;AAAA,EAAA;AAAA,EAGb,WAAW,IAAY,cAAgC;AAC7D,UAAM,cAAc,KAAK,SAAS,IAAI,CAAC,SAAS;AAC9C,UAAI,gBAAgB,kBAAkB;AAC9B,cAAA,YAAY,KAAK,cAAc,YAAY;AAEjD,YAAI,KAAK,aAAa,SAAS,KAAK,aAAa,MAAM;AACrD,iBAAO,IAAI,SAAS;AAAA,QAAA;AAEf,eAAA;AAAA,MAAA;AAEH,YAAA,IAAI,MAAM,qDAAqD;AAAA,IAAA,CACtE;AACD,WAAO,YAAY,KAAK,IAAI,EAAE,GAAG;AAAA,EAAA;AAAA,EAG3B,OAAO,cAAgC;AACvC,UAAA,CAAC,IAAI,IAAI,KAAK;AACpB,QAAI,gBAAgB,kBAAkB;AACpC,aAAO,QAAQ,KAAK,cAAc,YAAY,CAAC;AAAA,IAAA;AAE3C,UAAA,IAAI,MAAM,kDAAkD;AAAA,EAAA;AAAA,EAG5D,iBACN,SACA,cACA,QACQ;AACJ,QAAA,SAAS,OAAO,GAAG;AACd,aAAA,QAAQ,mBAAmB,YAAY;AAAA,IAAA;AAIhD,QAAI,QAAQ;AACZ,QAAI,iCAAQ,gBAAgB;AACtB,UAAA;AACF,cAAM,SAAS,OAAO,eAAe,WAAW,EAAE,SAAS,KAAK;AAEhE,YAAI,kBAAkB,SAAS;AAGrB,kBAAA;AAAA,QACC,WAAA,YAAY,UAAU,OAAO,QAAQ;AAEtC,kBAAA;AAAA,QAAA,WACC,WAAW,QAAQ;AAE5B,kBAAQ,OAAO;AAAA,QAAA;AAAA,eAEV,OAAO;AAGN,gBAAA;AAAA,MAAA;AAAA,IACV;AAGE,QAAA,OAAO,UAAU,UAAU;AAC7B,aAAO,IAAI,MAAM,QAAQ,MAAM,IAAI,CAAC;AAAA,IAAA;AAElC,QAAA,UAAU,QAAQ,UAAU,QAAW;AAClC,aAAA;AAAA,IAAA;AAET,QAAI,iBAAiB,MAAM;AACzB,aAAO,MAAM,YAAY;AAAA,IAAA;AAE3B,WAAO,OAAO,KAAK;AAAA,EAAA;AAEvB;AAqBgB,SAAA,GAAG,QAAgB,OAA8B;AAC/D,SAAO,IAAI,iBAAiB,MAAM,CAAC,QAAQ,KAAK,CAAC;AACnD;AAiBgB,SAAA,GAAG,QAAgB,OAA8B;AAC/D,SAAO,IAAI,iBAAiB,MAAM,CAAC,QAAQ,KAAK,CAAC;AACnD;AAQgB,SAAA,GACd,QACA,OACkB;AAClB,SAAO,IAAI,iBAAiB,MAAM,CAAC,QAAQ,KAAK,CAAC;AACnD;AAQgB,SAAA,IACd,QACA,OACkB;AAClB,SAAO,IAAI,iBAAiB,OAAO,CAAC,QAAQ,KAAK,CAAC;AACpD;AAQgB,SAAA,GACd,QACA,OACkB;AAClB,SAAO,IAAI,iBAAiB,MAAM,CAAC,QAAQ,KAAK,CAAC;AACnD;AAQgB,SAAA,IACd,QACA,OACkB;AAClB,SAAO,IAAI,iBAAiB,OAAO,CAAC,QAAQ,KAAK,CAAC;AACpD;AAYgB,SAAA,SACd,QACA,OACkB;AAClB,SAAO,IAAI,iBAAiB,YAAY,CAAC,QAAQ,KAAK,CAAC;AACzD;AAQgB,SAAA,WACd,QACA,OACkB;AAClB,SAAO,IAAI,iBAAiB,cAAc,CAAC,QAAQ,KAAK,CAAC;AAC3D;AAQgB,SAAA,SACd,QACA,OACkB;AAClB,SAAO,IAAI,iBAAiB,YAAY,CAAC,QAAQ,KAAK,CAAC;AACzD;AAYgB,SAAA,QACd,QACA,QACkB;AAClB,SAAO,IAAI,iBAAiB,MAAM,CAAC,QAAQ,MAAM,CAAC;AACpD;AAQgB,SAAA,WACd,QACA,QACkB;AAClB,SAAO,IAAI,iBAAiB,SAAS,CAAC,QAAQ,MAAM,CAAC;AACvD;AAYO,SAAS,OACd,QACkB;AAClB,SAAO,IAAI,iBAAiB,UAAU,CAAC,MAAM,CAAC;AAChD;AAQO,SAAS,UACd,QACkB;AAClB,SAAO,IAAI,iBAAiB,aAAa,CAAC,MAAM,CAAC;AACnD;AAgBO,SAAS,OAAO,aAAmD;AACpE,MAAA,YAAY,WAAW,GAAG;AACtB,UAAA,IAAI,MAAM,+CAA+C;AAAA,EAAA;AAEjE,MAAI,YAAY,WAAW,KAAK,YAAY,CAAC,MAAM,QAAW;AAC5D,WAAO,YAAY,CAAC;AAAA,EAAA;AAEf,SAAA,IAAI,iBAAiB,OAAO,WAAW;AAChD;AAYO,SAAS,MAAM,aAAmD;AACnE,MAAA,YAAY,WAAW,GAAG;AACtB,UAAA,IAAI,MAAM,8CAA8C;AAAA,EAAA;AAEhE,MAAI,YAAY,WAAW,KAAK,YAAY,CAAC,MAAM,QAAW;AAC5D,WAAO,YAAY,CAAC;AAAA,EAAA;AAEf,SAAA,IAAI,iBAAiB,MAAM,WAAW;AAC/C;AAQO,SAAS,IAAI,YAAgD;AAClE,SAAO,IAAI,iBAAiB,OAAO,CAAC,UAAU,CAAC;AACjD;AAUO,MAAM,kBAAqD;AAAA,EAChE,YACkB,QACA,WAChB;AAFgB,SAAA,SAAA;AACA,SAAA,YAAA;AAAA,EAAA;AAEpB;AAKO,SAAS,oBAAoB,OAAwC;AAC1E,SAAO,iBAAiB;AAC1B;AAQO,SAAS,IACd,QAC8B;AACvB,SAAA,IAAI,kBAAkB,QAAQ,KAAK;AAC5C;AAQO,SAAS,KACd,QAC8B;AACvB,SAAA,IAAI,kBAAkB,QAAQ,MAAM;AAC7C;"}