@proofkit/fmodata
Version:
FileMaker OData API client
198 lines (197 loc) • 7.93 kB
JavaScript
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
import { 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