@proofkit/fmodata 0.1.0-alpha.0
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.
- package/README.md +37 -0
- package/dist/esm/client/base-table.d.ts +13 -0
- package/dist/esm/client/base-table.js +19 -0
- package/dist/esm/client/base-table.js.map +1 -0
- package/dist/esm/client/database.d.ts +49 -0
- package/dist/esm/client/database.js +90 -0
- package/dist/esm/client/database.js.map +1 -0
- package/dist/esm/client/delete-builder.d.ts +61 -0
- package/dist/esm/client/delete-builder.js +121 -0
- package/dist/esm/client/delete-builder.js.map +1 -0
- package/dist/esm/client/entity-set.d.ts +43 -0
- package/dist/esm/client/entity-set.js +120 -0
- package/dist/esm/client/entity-set.js.map +1 -0
- package/dist/esm/client/filemaker-odata.d.ts +26 -0
- package/dist/esm/client/filemaker-odata.js +85 -0
- package/dist/esm/client/filemaker-odata.js.map +1 -0
- package/dist/esm/client/insert-builder.d.ts +23 -0
- package/dist/esm/client/insert-builder.js +69 -0
- package/dist/esm/client/insert-builder.js.map +1 -0
- package/dist/esm/client/query-builder.d.ts +94 -0
- package/dist/esm/client/query-builder.js +649 -0
- package/dist/esm/client/query-builder.js.map +1 -0
- package/dist/esm/client/record-builder.d.ts +43 -0
- package/dist/esm/client/record-builder.js +121 -0
- package/dist/esm/client/record-builder.js.map +1 -0
- package/dist/esm/client/table-occurrence.d.ts +25 -0
- package/dist/esm/client/table-occurrence.js +47 -0
- package/dist/esm/client/table-occurrence.js.map +1 -0
- package/dist/esm/client/update-builder.d.ts +69 -0
- package/dist/esm/client/update-builder.js +134 -0
- package/dist/esm/client/update-builder.js.map +1 -0
- package/dist/esm/filter-types.d.ts +76 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +10 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/types.d.ts +67 -0
- package/dist/esm/validation.d.ts +41 -0
- package/dist/esm/validation.js +270 -0
- package/dist/esm/validation.js.map +1 -0
- package/package.json +68 -0
- package/src/client/base-table.ts +25 -0
- package/src/client/database.ts +177 -0
- package/src/client/delete-builder.ts +193 -0
- package/src/client/entity-set.ts +310 -0
- package/src/client/filemaker-odata.ts +119 -0
- package/src/client/insert-builder.ts +93 -0
- package/src/client/query-builder.ts +1076 -0
- package/src/client/record-builder.ts +240 -0
- package/src/client/table-occurrence.ts +100 -0
- package/src/client/update-builder.ts +212 -0
- package/src/filter-types.ts +97 -0
- package/src/index.ts +17 -0
- package/src/types.ts +123 -0
- package/src/validation.ts +397 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { FFetchOptions } from '@fetchkit/ffetch';
|
|
2
|
+
import { Auth, ExecutionContext } from '../types.js';
|
|
3
|
+
import { Database } from './database.js';
|
|
4
|
+
import { TableOccurrence } from './table-occurrence.js';
|
|
5
|
+
export declare class FileMakerOData implements ExecutionContext {
|
|
6
|
+
private fetchClient;
|
|
7
|
+
private serverUrl;
|
|
8
|
+
private auth;
|
|
9
|
+
constructor(config: {
|
|
10
|
+
serverUrl: string;
|
|
11
|
+
auth: Auth;
|
|
12
|
+
fetchClientOptions?: FFetchOptions;
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
_makeRequest<T>(url: string, options?: RequestInit & FFetchOptions): Promise<T>;
|
|
18
|
+
database<const Occurrences extends readonly TableOccurrence<any, any, any, any>[]>(name: string, config?: {
|
|
19
|
+
occurrences?: Occurrences;
|
|
20
|
+
}): Database<Occurrences>;
|
|
21
|
+
/**
|
|
22
|
+
* Lists all available databases from the FileMaker OData service.
|
|
23
|
+
* @returns Promise resolving to an array of database names
|
|
24
|
+
*/
|
|
25
|
+
listDatabaseNames(): Promise<string[]>;
|
|
26
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
import createClient from "@fetchkit/ffetch";
|
|
5
|
+
import { Database } from "./database.js";
|
|
6
|
+
class FileMakerOData {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
__publicField(this, "fetchClient");
|
|
9
|
+
__publicField(this, "serverUrl");
|
|
10
|
+
__publicField(this, "auth");
|
|
11
|
+
this.fetchClient = createClient({
|
|
12
|
+
retries: 0,
|
|
13
|
+
...config.fetchClientOptions
|
|
14
|
+
});
|
|
15
|
+
const url = new URL(config.serverUrl);
|
|
16
|
+
if (url.protocol !== "https:") {
|
|
17
|
+
url.protocol = "https:";
|
|
18
|
+
}
|
|
19
|
+
url.pathname = url.pathname.replace(/\/+$/, "");
|
|
20
|
+
this.serverUrl = url.toString().replace(/\/+$/, "");
|
|
21
|
+
this.auth = config.auth;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* @internal
|
|
25
|
+
*/
|
|
26
|
+
async _makeRequest(url, options) {
|
|
27
|
+
var _a;
|
|
28
|
+
const baseUrl = `${this.serverUrl}${"apiKey" in this.auth ? `/otto` : ""}/fmi/odata/v4`;
|
|
29
|
+
const headers = {
|
|
30
|
+
Authorization: "apiKey" in this.auth ? `Bearer ${this.auth.apiKey}` : `Basic ${btoa(`${this.auth.username}:${this.auth.password}`)}`,
|
|
31
|
+
"Content-Type": "application/json",
|
|
32
|
+
Accept: "application/json",
|
|
33
|
+
...(options == null ? void 0 : options.headers) || {}
|
|
34
|
+
};
|
|
35
|
+
const fetchHandler = options == null ? void 0 : options.fetchHandler;
|
|
36
|
+
const {
|
|
37
|
+
headers: _headers,
|
|
38
|
+
fetchHandler: _fetchHandler,
|
|
39
|
+
...restOptions
|
|
40
|
+
} = options || {};
|
|
41
|
+
const clientToUse = fetchHandler ? createClient({ retries: 0, fetchHandler }) : this.fetchClient;
|
|
42
|
+
const resp = await clientToUse(baseUrl + url, {
|
|
43
|
+
...restOptions,
|
|
44
|
+
headers
|
|
45
|
+
});
|
|
46
|
+
if (!resp.ok) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`Failed to make request to ${baseUrl + url}: ${resp.statusText}`
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
const affectedRows = resp.headers.get("fmodata.affected_rows");
|
|
52
|
+
if (affectedRows !== null) {
|
|
53
|
+
return parseInt(affectedRows, 10);
|
|
54
|
+
}
|
|
55
|
+
if (resp.status === 204) {
|
|
56
|
+
return 0;
|
|
57
|
+
}
|
|
58
|
+
if ((_a = resp.headers.get("content-type")) == null ? void 0 : _a.includes("application/json")) {
|
|
59
|
+
let data = await resp.json();
|
|
60
|
+
if (data.error) {
|
|
61
|
+
throw new Error(data.error);
|
|
62
|
+
}
|
|
63
|
+
return data;
|
|
64
|
+
}
|
|
65
|
+
return await resp.text();
|
|
66
|
+
}
|
|
67
|
+
database(name, config) {
|
|
68
|
+
return new Database(name, this, config);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Lists all available databases from the FileMaker OData service.
|
|
72
|
+
* @returns Promise resolving to an array of database names
|
|
73
|
+
*/
|
|
74
|
+
async listDatabaseNames() {
|
|
75
|
+
const response = await this._makeRequest("/");
|
|
76
|
+
if (response.value && Array.isArray(response.value)) {
|
|
77
|
+
return response.value.map((item) => item.name);
|
|
78
|
+
}
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export {
|
|
83
|
+
FileMakerOData
|
|
84
|
+
};
|
|
85
|
+
//# sourceMappingURL=filemaker-odata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filemaker-odata.js","sources":["../../../src/client/filemaker-odata.ts"],"sourcesContent":["import createClient, { FFetchOptions } from \"@fetchkit/ffetch\";\nimport type { Auth, ExecutionContext } from \"../types\";\nimport { Database } from \"./database\";\nimport { TableOccurrence } from \"./table-occurrence\";\n\nexport class FileMakerOData implements ExecutionContext {\n private fetchClient: ReturnType<typeof createClient>;\n private serverUrl: string;\n private auth: Auth;\n constructor(config: {\n serverUrl: string;\n auth: Auth;\n fetchClientOptions?: FFetchOptions;\n }) {\n this.fetchClient = createClient({\n retries: 0,\n ...config.fetchClientOptions,\n });\n // Ensure the URL uses https://, is valid, and has no trailing slash\n const url = new URL(config.serverUrl);\n if (url.protocol !== \"https:\") {\n url.protocol = \"https:\";\n }\n // Remove any trailing slash from pathname\n url.pathname = url.pathname.replace(/\\/+$/, \"\");\n this.serverUrl = url.toString().replace(/\\/+$/, \"\");\n this.auth = config.auth;\n }\n\n /**\n * @internal\n */\n async _makeRequest<T>(\n url: string,\n options?: RequestInit & FFetchOptions,\n ): Promise<T> {\n const baseUrl = `${this.serverUrl}${\"apiKey\" in this.auth ? `/otto` : \"\"}/fmi/odata/v4`;\n\n const headers = {\n Authorization:\n \"apiKey\" in this.auth\n ? `Bearer ${this.auth.apiKey}`\n : `Basic ${btoa(`${this.auth.username}:${this.auth.password}`)}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n ...(options?.headers || {}),\n };\n\n // TEMPORARY WORKAROUND: Hopefully this feature will be fixed in the ffetch library\n // Extract fetchHandler and headers separately, only for tests where we're overriding the fetch handler per-request\n const fetchHandler = options?.fetchHandler;\n const {\n headers: _headers,\n fetchHandler: _fetchHandler,\n ...restOptions\n } = options || {};\n\n // If fetchHandler is provided, create a temporary client with it\n // Otherwise use the existing client\n const clientToUse = fetchHandler\n ? createClient({ retries: 0, fetchHandler })\n : this.fetchClient;\n\n const resp = await clientToUse(baseUrl + url, {\n ...restOptions,\n headers,\n });\n\n if (!resp.ok) {\n throw new Error(\n `Failed to make request to ${baseUrl + url}: ${resp.statusText}`,\n );\n }\n\n // Check for affected rows header (for DELETE and bulk PATCH operations)\n // FileMaker may return this with 204 No Content or 200 OK\n const affectedRows = resp.headers.get(\"fmodata.affected_rows\");\n if (affectedRows !== null) {\n return parseInt(affectedRows, 10) as T;\n }\n\n // Handle 204 No Content with no body\n if (resp.status === 204) {\n return 0 as T;\n }\n\n if (resp.headers.get(\"content-type\")?.includes(\"application/json\")) {\n let data = await resp.json();\n if (data.error) {\n throw new Error(data.error);\n }\n return data as T;\n }\n return (await resp.text()) as T;\n }\n\n database<\n const Occurrences extends readonly TableOccurrence<any, any, any, any>[],\n >(\n name: string,\n config?: { occurrences?: Occurrences },\n ): Database<Occurrences> {\n return new Database(name, this, config);\n }\n\n /**\n * Lists all available databases from the FileMaker OData service.\n * @returns Promise resolving to an array of database names\n */\n async listDatabaseNames(): Promise<string[]> {\n const response = (await this._makeRequest(\"/\")) as {\n value?: Array<{ name: string }>;\n };\n if (response.value && Array.isArray(response.value)) {\n return response.value.map((item) => item.name);\n }\n return [];\n }\n}\n"],"names":[],"mappings":";;;;;AAKO,MAAM,eAA2C;AAAA,EAItD,YAAY,QAIT;AAPK;AACA;AACA;AAMN,SAAK,cAAc,aAAa;AAAA,MAC9B,SAAS;AAAA,MACT,GAAG,OAAO;AAAA,IAAA,CACX;AAED,UAAM,MAAM,IAAI,IAAI,OAAO,SAAS;AAChC,QAAA,IAAI,aAAa,UAAU;AAC7B,UAAI,WAAW;AAAA,IAAA;AAGjB,QAAI,WAAW,IAAI,SAAS,QAAQ,QAAQ,EAAE;AAC9C,SAAK,YAAY,IAAI,SAAW,EAAA,QAAQ,QAAQ,EAAE;AAClD,SAAK,OAAO,OAAO;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMrB,MAAM,aACJ,KACA,SACY;;AACN,UAAA,UAAU,GAAG,KAAK,SAAS,GAAG,YAAY,KAAK,OAAO,UAAU,EAAE;AAExE,UAAM,UAAU;AAAA,MACd,eACE,YAAY,KAAK,OACb,UAAU,KAAK,KAAK,MAAM,KAC1B,SAAS,KAAK,GAAG,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,QAAQ,EAAE,CAAC;AAAA,MAClE,gBAAgB;AAAA,MAChB,QAAQ;AAAA,MACR,IAAI,mCAAS,YAAW,CAAA;AAAA,IAC1B;AAIA,UAAM,eAAe,mCAAS;AACxB,UAAA;AAAA,MACJ,SAAS;AAAA,MACT,cAAc;AAAA,MACd,GAAG;AAAA,IACL,IAAI,WAAW,CAAC;AAIV,UAAA,cAAc,eAChB,aAAa,EAAE,SAAS,GAAG,aAAA,CAAc,IACzC,KAAK;AAET,UAAM,OAAO,MAAM,YAAY,UAAU,KAAK;AAAA,MAC5C,GAAG;AAAA,MACH;AAAA,IAAA,CACD;AAEG,QAAA,CAAC,KAAK,IAAI;AACZ,YAAM,IAAI;AAAA,QACR,6BAA6B,UAAU,GAAG,KAAK,KAAK,UAAU;AAAA,MAChE;AAAA,IAAA;AAKF,UAAM,eAAe,KAAK,QAAQ,IAAI,uBAAuB;AAC7D,QAAI,iBAAiB,MAAM;AAClB,aAAA,SAAS,cAAc,EAAE;AAAA,IAAA;AAI9B,QAAA,KAAK,WAAW,KAAK;AAChB,aAAA;AAAA,IAAA;AAGT,SAAI,UAAK,QAAQ,IAAI,cAAc,MAA/B,mBAAkC,SAAS,qBAAqB;AAC9D,UAAA,OAAO,MAAM,KAAK,KAAK;AAC3B,UAAI,KAAK,OAAO;AACR,cAAA,IAAI,MAAM,KAAK,KAAK;AAAA,MAAA;AAErB,aAAA;AAAA,IAAA;AAED,WAAA,MAAM,KAAK,KAAK;AAAA,EAAA;AAAA,EAG1B,SAGE,MACA,QACuB;AACvB,WAAO,IAAI,SAAS,MAAM,MAAM,MAAM;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOxC,MAAM,oBAAuC;AAC3C,UAAM,WAAY,MAAM,KAAK,aAAa,GAAG;AAG7C,QAAI,SAAS,SAAS,MAAM,QAAQ,SAAS,KAAK,GAAG;AACnD,aAAO,SAAS,MAAM,IAAI,CAAC,SAAS,KAAK,IAAI;AAAA,IAAA;AAE/C,WAAO,CAAC;AAAA,EAAA;AAEZ;"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ExecutionContext, ExecutableBuilder, Result, ODataRecordMetadata } from '../types.js';
|
|
2
|
+
import { TableOccurrence } from './table-occurrence.js';
|
|
3
|
+
import { FFetchOptions } from '@fetchkit/ffetch';
|
|
4
|
+
export declare class InsertBuilder<T extends Record<string, any>, Occ extends TableOccurrence<any, any, any, any> | undefined = undefined> implements ExecutableBuilder<T & ODataRecordMetadata> {
|
|
5
|
+
private occurrence?;
|
|
6
|
+
private tableName;
|
|
7
|
+
private databaseName;
|
|
8
|
+
private context;
|
|
9
|
+
private data;
|
|
10
|
+
constructor(config: {
|
|
11
|
+
occurrence?: Occ;
|
|
12
|
+
tableName: string;
|
|
13
|
+
databaseName: string;
|
|
14
|
+
context: ExecutionContext;
|
|
15
|
+
data: Partial<T>;
|
|
16
|
+
});
|
|
17
|
+
execute(options?: RequestInit & FFetchOptions): Promise<Result<T & ODataRecordMetadata>>;
|
|
18
|
+
getRequestConfig(): {
|
|
19
|
+
method: string;
|
|
20
|
+
url: string;
|
|
21
|
+
body?: any;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
import { validateSingleResponse } from "../validation.js";
|
|
5
|
+
class InsertBuilder {
|
|
6
|
+
constructor(config) {
|
|
7
|
+
__publicField(this, "occurrence");
|
|
8
|
+
__publicField(this, "tableName");
|
|
9
|
+
__publicField(this, "databaseName");
|
|
10
|
+
__publicField(this, "context");
|
|
11
|
+
__publicField(this, "data");
|
|
12
|
+
this.occurrence = config.occurrence;
|
|
13
|
+
this.tableName = config.tableName;
|
|
14
|
+
this.databaseName = config.databaseName;
|
|
15
|
+
this.context = config.context;
|
|
16
|
+
this.data = config.data;
|
|
17
|
+
}
|
|
18
|
+
async execute(options) {
|
|
19
|
+
var _a, _b;
|
|
20
|
+
try {
|
|
21
|
+
const url = `/${this.databaseName}/${this.tableName}`;
|
|
22
|
+
const response = await this.context._makeRequest(url, {
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: {
|
|
25
|
+
"Content-Type": "application/json"
|
|
26
|
+
},
|
|
27
|
+
body: JSON.stringify(this.data),
|
|
28
|
+
...options
|
|
29
|
+
});
|
|
30
|
+
const schema = (_b = (_a = this.occurrence) == null ? void 0 : _a.baseTable) == null ? void 0 : _b.schema;
|
|
31
|
+
const validation = await validateSingleResponse(
|
|
32
|
+
response,
|
|
33
|
+
schema,
|
|
34
|
+
void 0,
|
|
35
|
+
// No selected fields for insert
|
|
36
|
+
void 0,
|
|
37
|
+
// No expand configs
|
|
38
|
+
"exact"
|
|
39
|
+
// Expect exactly one record
|
|
40
|
+
);
|
|
41
|
+
if (!validation.valid) {
|
|
42
|
+
return { data: void 0, error: validation.error };
|
|
43
|
+
}
|
|
44
|
+
if (validation.data === null) {
|
|
45
|
+
return {
|
|
46
|
+
data: void 0,
|
|
47
|
+
error: new Error("Insert operation returned null response")
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return { data: validation.data, error: void 0 };
|
|
51
|
+
} catch (error) {
|
|
52
|
+
return {
|
|
53
|
+
data: void 0,
|
|
54
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
getRequestConfig() {
|
|
59
|
+
return {
|
|
60
|
+
method: "POST",
|
|
61
|
+
url: `/${this.databaseName}/${this.tableName}`,
|
|
62
|
+
body: JSON.stringify(this.data)
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export {
|
|
67
|
+
InsertBuilder
|
|
68
|
+
};
|
|
69
|
+
//# sourceMappingURL=insert-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"insert-builder.js","sources":["../../../src/client/insert-builder.ts"],"sourcesContent":["import type {\n ExecutionContext,\n ExecutableBuilder,\n Result,\n ODataRecordMetadata,\n InferSchemaType,\n} from \"../types\";\nimport type { TableOccurrence } from \"./table-occurrence\";\nimport { validateSingleResponse } from \"../validation\";\nimport { type FFetchOptions } from \"@fetchkit/ffetch\";\n\nexport class InsertBuilder<\n T extends Record<string, any>,\n Occ extends TableOccurrence<any, any, any, any> | undefined = undefined,\n> implements ExecutableBuilder<T & ODataRecordMetadata>\n{\n private occurrence?: Occ;\n private tableName: string;\n private databaseName: string;\n private context: ExecutionContext;\n private data: Partial<T>;\n\n constructor(config: {\n occurrence?: Occ;\n tableName: string;\n databaseName: string;\n context: ExecutionContext;\n data: Partial<T>;\n }) {\n this.occurrence = config.occurrence;\n this.tableName = config.tableName;\n this.databaseName = config.databaseName;\n this.context = config.context;\n this.data = config.data;\n }\n\n async execute(\n options?: RequestInit & FFetchOptions,\n ): Promise<Result<T & ODataRecordMetadata>> {\n try {\n const url = `/${this.databaseName}/${this.tableName}`;\n\n // Make POST request with JSON body\n const response = await this.context._makeRequest(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(this.data),\n ...options,\n });\n\n // Get schema from occurrence if available\n const schema = this.occurrence?.baseTable?.schema;\n\n // Validate the response (FileMaker returns the created record)\n const validation = await validateSingleResponse<T>(\n response,\n schema,\n undefined, // No selected fields for insert\n undefined, // No expand configs\n \"exact\", // Expect exactly one record\n );\n\n if (!validation.valid) {\n return { data: undefined, error: validation.error };\n }\n\n // Handle null response (shouldn't happen for insert, but handle it)\n if (validation.data === null) {\n return {\n data: undefined,\n error: new Error(\"Insert operation returned null response\"),\n };\n }\n\n return { data: validation.data, error: undefined };\n } catch (error) {\n return {\n data: undefined,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n }\n\n getRequestConfig(): { method: string; url: string; body?: any } {\n return {\n method: \"POST\",\n url: `/${this.databaseName}/${this.tableName}`,\n body: JSON.stringify(this.data),\n };\n }\n}\n"],"names":[],"mappings":";;;;AAWO,MAAM,cAIb;AAAA,EAOE,YAAY,QAMT;AAZK;AACA;AACA;AACA;AACA;AASN,SAAK,aAAa,OAAO;AACzB,SAAK,YAAY,OAAO;AACxB,SAAK,eAAe,OAAO;AAC3B,SAAK,UAAU,OAAO;AACtB,SAAK,OAAO,OAAO;AAAA,EAAA;AAAA,EAGrB,MAAM,QACJ,SAC0C;;AACtC,QAAA;AACF,YAAM,MAAM,IAAI,KAAK,YAAY,IAAI,KAAK,SAAS;AAGnD,YAAM,WAAW,MAAM,KAAK,QAAQ,aAAa,KAAK;AAAA,QACpD,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU,KAAK,IAAI;AAAA,QAC9B,GAAG;AAAA,MAAA,CACJ;AAGK,YAAA,UAAS,gBAAK,eAAL,mBAAiB,cAAjB,mBAA4B;AAG3C,YAAM,aAAa,MAAM;AAAA,QACvB;AAAA,QACA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,QACA;AAAA;AAAA,MACF;AAEI,UAAA,CAAC,WAAW,OAAO;AACrB,eAAO,EAAE,MAAM,QAAW,OAAO,WAAW,MAAM;AAAA,MAAA;AAIhD,UAAA,WAAW,SAAS,MAAM;AACrB,eAAA;AAAA,UACL,MAAM;AAAA,UACN,OAAO,IAAI,MAAM,yCAAyC;AAAA,QAC5D;AAAA,MAAA;AAGF,aAAO,EAAE,MAAM,WAAW,MAAM,OAAO,OAAU;AAAA,aAC1C,OAAO;AACP,aAAA;AAAA,QACL,MAAM;AAAA,QACN,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,MACjE;AAAA,IAAA;AAAA,EACF;AAAA,EAGF,mBAAgE;AACvD,WAAA;AAAA,MACL,QAAQ;AAAA,MACR,KAAK,IAAI,KAAK,YAAY,IAAI,KAAK,SAAS;AAAA,MAC5C,MAAM,KAAK,UAAU,KAAK,IAAI;AAAA,IAChC;AAAA,EAAA;AAEJ;"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { QueryOptions } from 'odata-query';
|
|
2
|
+
import { ExecutionContext, WithSystemFields, Result, InferSchemaType, ExecuteOptions, ConditionallyWithODataAnnotations, ExtractSchemaFromOccurrence } from '../types.js';
|
|
3
|
+
import { Filter } from '../filter-types.js';
|
|
4
|
+
import { TableOccurrence } from './table-occurrence.js';
|
|
5
|
+
import { BaseTable } from './base-table.js';
|
|
6
|
+
import { FFetchOptions } from '@fetchkit/ffetch';
|
|
7
|
+
import { z } from 'zod/v4';
|
|
8
|
+
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
9
|
+
type ExtractNavigationNames<O extends TableOccurrence<any, any, any, any> | undefined> = O extends TableOccurrence<any, any, infer Nav, any> ? Nav extends Record<string, any> ? keyof Nav & string : never : never;
|
|
10
|
+
type ResolveNavigationItem<T> = T extends () => infer R ? R : T;
|
|
11
|
+
type FindNavigationTarget<O extends TableOccurrence<any, any, any, any> | undefined, Name extends string> = O extends TableOccurrence<any, any, infer Nav, any> ? Nav extends Record<string, any> ? Name extends keyof Nav ? ResolveNavigationItem<Nav[Name]> : TableOccurrence<BaseTable<Record<string, z.ZodTypeAny>, any>, any, any, any> : TableOccurrence<BaseTable<Record<string, z.ZodTypeAny>, any>, any, any, any> : TableOccurrence<BaseTable<Record<string, z.ZodTypeAny>, any>, any, any, any>;
|
|
12
|
+
type GetTargetSchemaType<O extends TableOccurrence<any, any, any, any> | undefined, Rel extends string> = [FindNavigationTarget<O, Rel>] extends [
|
|
13
|
+
TableOccurrence<infer BT, any, any, any>
|
|
14
|
+
] ? [BT] extends [BaseTable<infer S, any>] ? [S] extends [Record<string, StandardSchemaV1>] ? InferSchemaType<S> : Record<string, any> : Record<string, any> : Record<string, any>;
|
|
15
|
+
type ExpandedRelations = Record<string, {
|
|
16
|
+
schema: any;
|
|
17
|
+
selected: any;
|
|
18
|
+
}>;
|
|
19
|
+
export declare class QueryBuilder<T extends Record<string, any>, Selected extends keyof T = keyof T, SingleMode extends "exact" | "maybe" | false = false, IsCount extends boolean = false, Occ extends TableOccurrence<any, any, any, any> | undefined = undefined, Expands extends ExpandedRelations = {}> {
|
|
20
|
+
private queryOptions;
|
|
21
|
+
private expandConfigs;
|
|
22
|
+
private singleMode;
|
|
23
|
+
private isCountMode;
|
|
24
|
+
private occurrence?;
|
|
25
|
+
private tableName;
|
|
26
|
+
private databaseName;
|
|
27
|
+
private context;
|
|
28
|
+
private isNavigate?;
|
|
29
|
+
private navigateRecordId?;
|
|
30
|
+
private navigateRelation?;
|
|
31
|
+
private navigateSourceTableName?;
|
|
32
|
+
private navigateBaseRelation?;
|
|
33
|
+
constructor(config: {
|
|
34
|
+
occurrence?: Occ;
|
|
35
|
+
tableName: string;
|
|
36
|
+
databaseName: string;
|
|
37
|
+
context: ExecutionContext;
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* Helper to conditionally strip OData annotations based on options
|
|
41
|
+
*/
|
|
42
|
+
private stripODataAnnotationsIfNeeded;
|
|
43
|
+
select<K extends keyof T>(...fields: K[]): QueryBuilder<T, K, SingleMode, IsCount, Occ, Expands>;
|
|
44
|
+
/**
|
|
45
|
+
* Transforms our filter format to odata-query's expected format
|
|
46
|
+
* - Arrays of operators are converted to AND conditions
|
|
47
|
+
* - Single operator objects pass through as-is
|
|
48
|
+
* - Shorthand values are handled by odata-query
|
|
49
|
+
*/
|
|
50
|
+
private transformFilter;
|
|
51
|
+
filter(filter: Filter<ExtractSchemaFromOccurrence<Occ>>): QueryBuilder<T, Selected, SingleMode, IsCount, Occ, Expands>;
|
|
52
|
+
orderBy(orderBy: QueryOptions<T>["orderBy"]): QueryBuilder<T, Selected, SingleMode, IsCount, Occ, Expands>;
|
|
53
|
+
top(count: number): QueryBuilder<T, Selected, SingleMode, IsCount, Occ, Expands>;
|
|
54
|
+
skip(count: number): QueryBuilder<T, Selected, SingleMode, IsCount, Occ, Expands>;
|
|
55
|
+
/**
|
|
56
|
+
* Formats select fields for use in query strings.
|
|
57
|
+
* - Wraps "id" fields in double quotes
|
|
58
|
+
* - URL-encodes special characters but preserves spaces
|
|
59
|
+
*/
|
|
60
|
+
private formatSelectFields;
|
|
61
|
+
/**
|
|
62
|
+
* Builds expand validation configs from internal expand configurations.
|
|
63
|
+
* These are used to validate expanded navigation properties.
|
|
64
|
+
*/
|
|
65
|
+
private buildExpandValidationConfigs;
|
|
66
|
+
/**
|
|
67
|
+
* Builds OData expand query string from expand configurations.
|
|
68
|
+
* Handles nested expands recursively.
|
|
69
|
+
*/
|
|
70
|
+
private buildExpandString;
|
|
71
|
+
expand<Rel extends ExtractNavigationNames<Occ> | (string & {}), TargetOcc extends FindNavigationTarget<Occ, Rel> = FindNavigationTarget<Occ, Rel>, TargetSchema extends GetTargetSchemaType<Occ, Rel> = GetTargetSchemaType<Occ, Rel>, TargetSelected extends keyof TargetSchema = keyof TargetSchema>(relation: Rel, callback?: (builder: QueryBuilder<TargetSchema, keyof TargetSchema, false, false, TargetOcc extends TableOccurrence<any, any, any, any> ? TargetOcc : undefined>) => QueryBuilder<WithSystemFields<TargetSchema>, TargetSelected, any, any, any>): QueryBuilder<T, Selected, SingleMode, IsCount, Occ, Expands & {
|
|
72
|
+
[K in Rel]: {
|
|
73
|
+
schema: TargetSchema;
|
|
74
|
+
selected: TargetSelected;
|
|
75
|
+
};
|
|
76
|
+
}>;
|
|
77
|
+
single(): QueryBuilder<T, Selected, "exact", IsCount, Occ, Expands>;
|
|
78
|
+
maybeSingle(): QueryBuilder<T, Selected, "maybe", IsCount, Occ, Expands>;
|
|
79
|
+
count(): QueryBuilder<T, Selected, SingleMode, true, Occ, Expands>;
|
|
80
|
+
execute<EO extends ExecuteOptions>(options?: RequestInit & FFetchOptions & EO): Promise<Result<IsCount extends true ? number : SingleMode extends "exact" ? ConditionallyWithODataAnnotations<Pick<T, Selected> & {
|
|
81
|
+
[K in keyof Expands]: Pick<Expands[K]["schema"], Expands[K]["selected"]>[];
|
|
82
|
+
}, EO["includeODataAnnotations"] extends true ? true : false> : SingleMode extends "maybe" ? ConditionallyWithODataAnnotations<Pick<T, Selected> & {
|
|
83
|
+
[K in keyof Expands]: Pick<Expands[K]["schema"], Expands[K]["selected"]>[];
|
|
84
|
+
}, EO["includeODataAnnotations"] extends true ? true : false> | null : ConditionallyWithODataAnnotations<Pick<T, Selected> & {
|
|
85
|
+
[K in keyof Expands]: Pick<Expands[K]["schema"], Expands[K]["selected"]>[];
|
|
86
|
+
}, EO["includeODataAnnotations"] extends true ? true : false>[]>>;
|
|
87
|
+
getQueryString(): string;
|
|
88
|
+
getRequestConfig(): {
|
|
89
|
+
method: string;
|
|
90
|
+
url: string;
|
|
91
|
+
body?: any;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export {};
|