UNPKG

@proofkit/fmodata

Version:

FileMaker OData API client

294 lines (293 loc) 9.57 kB
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); const FILEMAKER_LIST_DELIMITER = "\r"; const FILEMAKER_NEWLINE_REGEX = /\r\n|\n/g; function normalizeFileMakerNewlines(value) { return value.replace(FILEMAKER_NEWLINE_REGEX, FILEMAKER_LIST_DELIMITER); } function splitFileMakerList(value) { const normalized = normalizeFileMakerNewlines(value); if (normalized === "") { return []; } return normalized.split(FILEMAKER_LIST_DELIMITER); } function issue(message) { return { message }; } function validateItemsWithSchema(items, itemValidator) { const validations = items.map((item) => itemValidator["~standard"].validate(item)); const hasAsyncValidation = validations.some((result) => result instanceof Promise); const finalize = (results) => { const transformed = []; const issues = []; results.forEach((result, index) => { if ("issues" in result && result.issues) { for (const validationIssue of result.issues) { issues.push({ ...validationIssue, path: validationIssue.path ? [index, ...validationIssue.path] : [index] }); } return; } if ("value" in result) { transformed.push(result.value); } }); if (issues.length > 0) { return { issues }; } return { value: transformed }; }; if (hasAsyncValidation) { return Promise.all(validations).then((results) => finalize(results)); } return finalize(validations); } class FieldBuilder { constructor(fieldType) { __publicField(this, "_primaryKey", false); __publicField(this, "_notNull", false); __publicField(this, "_readOnly", false); __publicField(this, "_entityId"); // biome-ignore lint/suspicious/noExplicitAny: Required for type inference with infer __publicField(this, "_outputValidator"); // biome-ignore lint/suspicious/noExplicitAny: Required for type inference with infer __publicField(this, "_inputValidator"); __publicField(this, "_fieldType"); __publicField(this, "_comment"); this._fieldType = fieldType; } /** * Mark this field as the primary key for the table. * Primary keys are automatically read-only and non-nullable. */ primaryKey() { const builder = this._clone(); builder._primaryKey = true; builder._notNull = true; builder._readOnly = true; return builder; } /** * Mark this field as non-nullable. * Updates the type to exclude null/undefined. */ notNull() { const builder = this._clone(); builder._notNull = true; return builder; } /** * Mark this field as read-only. * Read-only fields are excluded from insert and update operations. */ readOnly() { const builder = this._clone(); builder._readOnly = true; return builder; } /** * Assign a FileMaker field ID (FMFID) to this field. * When useEntityIds is enabled, this ID will be used in API requests instead of the field name. */ entityId(id) { const builder = this._clone(); builder._entityId = id; return builder; } /** * Set a validator for the output (reading from database). * The output validator transforms/validates data coming FROM the database in list or get operations. * * @example * numberField().readValidator(z.coerce.boolean()) * // FileMaker returns 0/1, you get true/false */ readValidator(validator) { const builder = this._clone(); builder._outputValidator = validator; return builder; } /** * Set a validator for the input (writing to database). * The input validator transforms/validates data going TO the database in insert, update, and filter operations. * * @example * numberField().writeValidator(z.boolean().transform(v => v ? 1 : 0)) * // You pass true/false, FileMaker gets 1/0 */ writeValidator(validator) { const builder = this._clone(); builder._inputValidator = validator; return builder; } /** * Add a comment to this field for metadata purposes. * This helps future developers understand the purpose of the field. * * @example * textField().comment("Account name of the user who last modified each record") */ comment(comment) { const builder = this._clone(); builder._comment = comment; return builder; } /** * Get the metadata configuration for this field. * @internal Used by fmTableOccurrence to extract field configuration */ _getConfig() { return { fieldType: this._fieldType, primaryKey: this._primaryKey, notNull: this._notNull, readOnly: this._readOnly, entityId: this._entityId, outputValidator: this._outputValidator, inputValidator: this._inputValidator, comment: this._comment }; } /** * Clone this builder to allow immutable chaining. * @private */ _clone() { const builder = new FieldBuilder(this._fieldType); builder._primaryKey = this._primaryKey; builder._notNull = this._notNull; builder._readOnly = this._readOnly; builder._entityId = this._entityId; builder._outputValidator = this._outputValidator; builder._inputValidator = this._inputValidator; builder._comment = this._comment; return builder; } } function textField() { return new FieldBuilder("text"); } function listField(options) { const allowNull = (options == null ? void 0 : options.allowNull) ?? false; const itemValidator = options == null ? void 0 : options.itemValidator; const readListSchema = { "~standard": { version: 1, vendor: "proofkit", validate(input) { if (input === null || input === void 0 || input === "") { return { value: allowNull ? null : [] }; } if (typeof input !== "string") { return { issues: [issue("Expected a FileMaker list string or null")] }; } const items = splitFileMakerList(input); if (!itemValidator) { return { value: items }; } const validatedItems = validateItemsWithSchema(items, itemValidator); if (validatedItems instanceof Promise) { return validatedItems.then((result) => { if ("issues" in result) { return result; } return { value: result.value }; }); } if ("issues" in validatedItems) { return validatedItems; } return { value: validatedItems.value }; } } }; const writeListSchema = { "~standard": { version: 1, vendor: "proofkit", validate(input) { if (input === null || input === void 0) { return { value: allowNull ? null : "" }; } if (!Array.isArray(input)) { return { issues: [issue("Expected an array for FileMaker list field input")] }; } if (!itemValidator) { const hasNonStringItem = input.some((item) => typeof item !== "string"); if (hasNonStringItem) { return { issues: [issue("Expected all list items to be strings without an itemValidator")] }; } const serialized = input.map((item) => normalizeFileMakerNewlines(item)).join(FILEMAKER_LIST_DELIMITER); return { value: serialized }; } const validateInputItems = input.map((item) => itemValidator["~standard"].validate(item)); const hasAsyncValidation = validateInputItems.some((result) => result instanceof Promise); const serializeValidated = (results) => { const validatedItems = []; const issues = []; results.forEach((result, index) => { if ("issues" in result && result.issues) { for (const validationIssue of result.issues) { issues.push({ ...validationIssue, path: validationIssue.path ? [index, ...validationIssue.path] : [index] }); } return; } if ("value" in result) { validatedItems.push(result.value); } }); if (issues.length > 0) { return { issues }; } const serialized = validatedItems.map((item) => normalizeFileMakerNewlines(typeof item === "string" ? item : String(item))).join(FILEMAKER_LIST_DELIMITER); return { value: serialized }; }; if (hasAsyncValidation) { return Promise.all(validateInputItems).then((results) => serializeValidated(results)); } return serializeValidated( validateInputItems ); } } }; return textField().readValidator(readListSchema).writeValidator(writeListSchema); } function numberField() { return new FieldBuilder("number"); } function dateField() { return new FieldBuilder("date"); } function timeField() { return new FieldBuilder("time"); } function timestampField() { return new FieldBuilder("timestamp"); } function containerField() { return new FieldBuilder("container"); } function calcField() { const builder = new FieldBuilder("calculated"); return builder.readOnly(); } export { FieldBuilder, calcField, containerField, dateField, listField, numberField, textField, timeField, timestampField }; //# sourceMappingURL=field-builders.js.map