UNPKG

@proofkit/fmodata

Version:

FileMaker OData API client

198 lines (197 loc) 7.93 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); import { requestFromService, runLayerResult, runLayerOrThrow } from "../effect.js"; import { BuilderInvariantError, MetadataNotFoundError, SchemaValidationFailedError } from "../errors.js"; import { FMTable } from "../orm/table.js"; import { createDatabaseLayer } from "../services.js"; import { BatchBuilder } from "./batch-builder.js"; import { stripFmp12Extension } from "./database-name.js"; import { EntitySet } from "./entity-set.js"; import { SchemaManager } from "./schema-manager.js"; import { WebhookManager } from "./webhook-builder.js"; class Database { constructor(databaseName, context, config) { __publicField(this, "schema"); __publicField(this, "webhook"); __publicField(this, "databaseName"); __publicField(this, "_normalizeDatabaseName"); __publicField(this, "_useEntityIds"); __publicField(this, "_includeSpecialColumns"); /** @internal Database-scoped Effect Layer for dependency injection */ __publicField(this, "_layer"); var _a; this.databaseName = databaseName; this._normalizeDatabaseName = (config == null ? void 0 : config.normalizeDatabaseName) ?? true; this._useEntityIds = (config == null ? void 0 : config.useEntityIds) ?? false; this._includeSpecialColumns = (config == null ? void 0 : config.includeSpecialColumns) ?? false; const baseLayer = (_a = context._getLayer) == null ? void 0 : _a.call(context); if (baseLayer) { this._layer = createDatabaseLayer(baseLayer, { databaseName: this.databaseName, normalizeDatabaseName: this._normalizeDatabaseName, useEntityIds: this._useEntityIds, includeSpecialColumns: this._includeSpecialColumns }); } else { throw new BuilderInvariantError( "Database", "ExecutionContext must implement _getLayer() for dependency injection" ); } this.schema = new SchemaManager(this._layer); this.webhook = new WebhookManager(this._layer); } /** * @internal Used by adapter packages to access the database filename. */ get _getDatabaseName() { return this.databaseName; } /** * @internal Used by EntitySet to access database configuration */ get _getUseEntityIds() { return this._useEntityIds; } /** * @internal Used by EntitySet to access database configuration */ get _getNormalizeDatabaseName() { return this._normalizeDatabaseName; } /** * @internal Used by EntitySet to access database configuration */ get _getIncludeSpecialColumns() { return this._includeSpecialColumns; } /** * @internal Used by adapter packages for raw OData requests. * Makes requests through the Effect DI layer. */ _makeRequest(path, options) { const pipeline = requestFromService(`/${this.databaseName}${path}`, options); return runLayerResult(this._layer, pipeline); } // biome-ignore lint/suspicious/noExplicitAny: Accepts any FMTable configuration from(table) { let useEntityIds = this._useEntityIds; if (Object.hasOwn(table, FMTable.Symbol.UseEntityIds)) { const tableUseEntityIds = table[FMTable.Symbol.UseEntityIds]; if (typeof tableUseEntityIds === "boolean") { useEntityIds = tableUseEntityIds; } } const layer = useEntityIds !== this._useEntityIds ? createDatabaseLayer(this._layer, { databaseName: this.databaseName, normalizeDatabaseName: this._normalizeDatabaseName, useEntityIds, includeSpecialColumns: this._includeSpecialColumns }) : this._layer; return new EntitySet({ occurrence: table, layer, database: this }); } async getMetadata(args) { let url = `/${this.databaseName}/$metadata`; if (args == null ? void 0 : args.tableName) { url = `/${this.databaseName}/$metadata%23${args.tableName}`; } const headers = { Accept: (args == null ? void 0 : args.format) === "xml" ? "application/xml" : "application/json" }; if (args == null ? void 0 : args.reduceAnnotations) { headers.Prefer = 'include-annotations="-*"'; } const pipeline = requestFromService(url, { headers }); const data = await runLayerOrThrow(this._layer, pipeline, "fmodata.metadata"); if ((args == null ? void 0 : args.format) === "xml") { return data; } const metadataMap = data; const metadata = metadataMap[this.databaseName] ?? metadataMap[stripFmp12Extension(this.databaseName)]; if (!metadata) { throw new MetadataNotFoundError(this.databaseName); } return metadata; } /** * Lists all available tables (entity sets) in this database. * @returns Promise resolving to an array of table names */ async listTableNames() { const pipeline = requestFromService(`/${this.databaseName}`); const data = await runLayerOrThrow(this._layer, pipeline, "fmodata.listTableNames"); if (data.value && Array.isArray(data.value)) { return data.value.map((item) => item.name); } return []; } /** * Executes a FileMaker script. * @param scriptName - The name of the script to execute (must be valid according to OData rules) * @param options - Optional script parameter and result schema * @returns Promise resolving to script execution result */ // biome-ignore lint/suspicious/noExplicitAny: Required for type inference with infer async runScript(scriptName, options) { const body = {}; if ((options == null ? void 0 : options.scriptParam) !== void 0) { body.scriptParameterValue = options.scriptParam; } const pipeline = requestFromService(`/${this.databaseName}/Script.${scriptName}`, { method: "POST", body: Object.keys(body).length > 0 ? JSON.stringify(body) : void 0 }); const response = await runLayerOrThrow(this._layer, pipeline, "fmodata.runScript"); if ((options == null ? void 0 : options.resultSchema) && response.scriptResult !== void 0) { const validationResult = options.resultSchema["~standard"].validate(response.scriptResult.resultParameter); const validated = validationResult instanceof Promise ? await validationResult : validationResult; if (validated.issues) { throw new SchemaValidationFailedError("Database.runScript", JSON.stringify(validated.issues), { issues: validated.issues }); } return { resultCode: response.scriptResult.code, result: validated.value // biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic return type }; } return { resultCode: response.scriptResult.code, result: response.scriptResult.resultParameter // biome-ignore lint/suspicious/noExplicitAny: Type assertion for generic return type }; } /** * Create a batch operation builder that allows multiple queries to be executed together * in a single atomic request. All operations succeed or fail together (transactional). * * @param builders - Array of executable query builders to batch * @returns A BatchBuilder that can be executed * @example * ```ts * const result = await db.batch([ * db.from('contacts').list().top(5), * db.from('users').list().top(5), * db.from('contacts').insert({ name: 'John' }) * ]).execute(); * * if (result.data) { * const [contacts, users, insertResult] = result.data; * } * ``` */ // biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any ExecutableBuilder result type batch(builders) { return new BatchBuilder(builders, this._layer); } } export { Database }; //# sourceMappingURL=database.js.map