UNPKG

@proofkit/fmdapi

Version:

FileMaker Data API client

367 lines (366 loc) • 11.6 kB
import { FileMakerError } from "./client-types.js"; function asNumber(input) { return typeof input === "string" ? Number.parseInt(input, 10) : input; } function DataApi(options) { if ("zodValidators" in options) { throw new Error("zodValidators is no longer supported. Use schema instead, or re-run the typegen command"); } const schema = options.schema; const layout = options.layout; const { create, delete: _adapterDelete, find, findAll: adapterFindAll, get, list, listAll: adapterListAll, update, layoutMetadata, containerUpload, executeScript, ...otherMethods } = options.adapter; function normalizeListRequest(args) { const { batch, fetch, timeout, ...params } = args ?? {}; if ("limit" in params && params.limit !== void 0) { Object.assign(params, { _limit: params.limit }).limit = void 0; } if ("offset" in params && params.offset !== void 0) { if (params.offset <= 1) { params.offset = void 0; } else { Object.assign(params, { _offset: params.offset }).offset = void 0; } } if ("sort" in params && params.sort !== void 0) { Object.assign(params, { _sort: Array.isArray(params.sort) ? params.sort : [params.sort] }).sort = void 0; } return { batch, fetch, params, timeout }; } function normalizeFindRequest(args) { const { batch, query: queryInput, ignoreEmptyResult = false, timeout, fetch, ...params } = args; const query = Array.isArray(queryInput) ? queryInput : [queryInput]; if ("offset" in params && params.offset !== void 0 && params.offset <= 1) { params.offset = void 0; } if ("sort" in params && params.sort !== void 0 && !Array.isArray(params.sort)) { params.sort = [params.sort]; } if ("dateformats" in params && params.dateformats !== void 0) { let dateFormatValue; if (params.dateformats === "US") { dateFormatValue = 0; } else if (params.dateformats === "file_locale") { dateFormatValue = 1; } else if (params.dateformats === "ISO8601") { dateFormatValue = 2; } else { dateFormatValue = 0; } params.dateformats = dateFormatValue.toString(); } return { batch, fetch, ignoreEmptyResult, params: { ...params, query }, timeout }; } async function _list(args) { const { batch, fetch, params, timeout } = normalizeListRequest(args); const result = await list({ batch, layout, data: params, fetch, timeout }); if (result.dataInfo.foundCount > result.dataInfo.returnedCount && (args == null ? void 0 : args.limit) === void 0 && (args == null ? void 0 : args.offset) === void 0) { console.warn( `🚨 @proofkit/fmdapi: Loaded only ${result.dataInfo.returnedCount} of the ${result.dataInfo.foundCount} records from your "${layout}" layout. Use the "listAll" method to automatically paginate through all records, or specify a "limit" and "offset" to handle pagination yourself.` ); } return await runSchemaValidationAndTransform(schema, result); } async function listAll(args) { let runningData = []; const limit = (args == null ? void 0 : args.limit) ?? 100; let offset = (args == null ? void 0 : args.offset) ?? 1; if (adapterListAll) { const { batch, fetch, params, timeout } = normalizeListRequest({ ...args, limit, offset }); const result = await adapterListAll({ batch, data: params, fetch, layout, timeout }); return (await runSchemaValidationAndTransform(schema, result)).data; } while (true) { const data = await _list({ ...args, offset }); runningData = [...runningData, ...data.data]; if (runningData.length >= data.dataInfo.foundCount) { break; } offset += limit; } return runningData; } async function _create(args) { const { batch, fetch, timeout, ...params } = args ?? {}; return await create({ batch, layout, data: params, fetch, timeout }); } async function _get(args) { args.recordId = asNumber(args.recordId); const { batch, recordId, fetch, timeout, ...params } = args; const result = await get({ batch, layout, data: { ...params, recordId }, fetch, timeout }); return await runSchemaValidationAndTransform(schema, result); } async function _update(args) { args.recordId = asNumber(args.recordId); const { batch, recordId, fetch, timeout, ...params } = args; return await update({ batch, layout, data: { ...params, recordId }, fetch, timeout }); } function deleteRecord(args) { args.recordId = asNumber(args.recordId); const { batch, recordId, fetch, timeout, ...params } = args; return _adapterDelete({ batch, layout, data: { ...params, recordId }, fetch, timeout }); } async function _find(args) { const { batch, fetch, ignoreEmptyResult, params, timeout } = normalizeFindRequest(args); const result = await find({ batch, data: params, layout, fetch, timeout }).catch((e) => { if (ignoreEmptyResult && e instanceof FileMakerError && e.code === "401") { return { data: [], dataInfo: { foundCount: 0, returnedCount: 0 } }; } throw e; }); if (result.dataInfo.foundCount > result.dataInfo.returnedCount && (args == null ? void 0 : args.limit) === void 0 && (args == null ? void 0 : args.offset) === void 0) { console.warn( `🚨 @proofkit/fmdapi: Loaded only ${result.dataInfo.returnedCount} of the ${result.dataInfo.foundCount} records from your "${layout}" layout. Use the "findAll" method to automatically paginate through all records, or specify a "limit" and "offset" to handle pagination yourself.` ); } return await runSchemaValidationAndTransform(schema, result); } async function findOne(args) { const result = await _find(args); if (result.data.length !== 1) { throw new Error(`${result.data.length} records found; expecting exactly 1`); } const transformedResult = await runSchemaValidationAndTransform(schema, result); if (!transformedResult.data[0]) { throw new Error("No data found"); } return { ...transformedResult, data: transformedResult.data[0] }; } async function findFirst(args) { const result = await _find(args); const transformedResult = await runSchemaValidationAndTransform(schema, result); if (!transformedResult.data[0]) { throw new Error("No data found"); } return { ...transformedResult, data: transformedResult.data[0] }; } async function maybeFindFirst(args) { const result = await _find({ ...args, ignoreEmptyResult: true }); const transformedResult = await runSchemaValidationAndTransform(schema, result); if (!transformedResult.data[0]) { return null; } return { ...transformedResult, data: transformedResult.data[0] }; } async function findAll(args) { let runningData = []; const limit = args.limit ?? 100; let offset = args.offset ?? 1; if (adapterFindAll) { const { batch, fetch, params, timeout } = normalizeFindRequest({ ...args, ignoreEmptyResult: true, limit, offset }); const result = await adapterFindAll({ batch, data: params, fetch, layout, timeout }).catch((e) => { if (e instanceof FileMakerError && e.code === "401") { return { data: [], dataInfo: { foundCount: 0, returnedCount: 0 } }; } throw e; }); return (await runSchemaValidationAndTransform(schema, result)).data; } while (true) { const data = await _find({ ...args, offset, ignoreEmptyResult: true }); runningData = [...runningData, ...data.data]; if (runningData.length === 0 || runningData.length >= data.dataInfo.foundCount) { break; } offset += limit; } return runningData; } async function _layoutMetadata(args) { const { ...restArgs } = args ?? {}; const params = restArgs; return await layoutMetadata({ batch: params.batch, layout, fetch: params.fetch, // Now should correctly resolve to undefined if not present timeout: params.timeout // Now should correctly resolve to undefined if not present }); } async function _containerUpload(args) { const { fetch, timeout, ...params } = args; return await containerUpload({ layout, data: { ...params, containerFieldName: params.containerFieldName, repetition: params.containerFieldRepetition }, fetch, timeout }); } async function runSchemaValidationAndTransform(schema2, result) { var _a; const fieldDataIssues = []; const portalDataIssues = []; if (!schema2) { return result; } const transformedData = []; for (const record of result.data) { let fieldResult = schema2.fieldData["~standard"].validate(record.fieldData); if (fieldResult instanceof Promise) { fieldResult = await fieldResult; } if ("value" in fieldResult) { record.fieldData = fieldResult.value; } else { fieldDataIssues.push(...fieldResult.issues); } if (schema2.portalData) { for (const [portalName, portalRecords] of Object.entries(record.portalData)) { const validatedPortalRecords = []; for (const portalRecord of portalRecords) { let portalResult = (_a = schema2.portalData[portalName]) == null ? void 0 : _a["~standard"].validate(portalRecord); if (portalResult instanceof Promise) { portalResult = await portalResult; } if (portalResult && "value" in portalResult) { validatedPortalRecords.push({ ...portalResult.value, recordId: portalRecord.recordId, modId: portalRecord.modId }); } else { portalDataIssues.push(...(portalResult == null ? void 0 : portalResult.issues) ?? []); } } record.portalData[portalName] = validatedPortalRecords; } } transformedData.push(record); } result.data = transformedData; if (fieldDataIssues.length > 0 || portalDataIssues.length > 0) { console.error( `🚨 @proofkit/fmdapi: Validation issues for layout "${layout}". Run the typegen command again to generate the latest field definitions from your layout.`, { fieldDataIssues, portalDataIssues } ); throw new Error("Schema validation issues"); } return result; } async function _executeScript(args) { return await executeScript({ ...args, layout }); } return { ...otherMethods, layout: options.layout, list: _list, listAll, create: _create, get: _get, update: _update, delete: deleteRecord, find: _find, findOne, findFirst, maybeFindFirst, findAll, layoutMetadata: _layoutMetadata, containerUpload: _containerUpload, executeScript: _executeScript }; } export { DataApi, DataApi as default }; //# sourceMappingURL=client.js.map