@proofkit/fmodata 0.1.0-alpha.13 → 0.1.0-alpha.15
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 +489 -334
- package/dist/esm/client/batch-builder.d.ts +7 -5
- package/dist/esm/client/batch-builder.js +84 -25
- package/dist/esm/client/batch-builder.js.map +1 -1
- package/dist/esm/client/builders/default-select.d.ts +7 -0
- package/dist/esm/client/builders/default-select.js +42 -0
- package/dist/esm/client/builders/default-select.js.map +1 -0
- package/dist/esm/client/builders/expand-builder.d.ts +43 -0
- package/dist/esm/client/builders/expand-builder.js +173 -0
- package/dist/esm/client/builders/expand-builder.js.map +1 -0
- package/dist/esm/client/builders/index.d.ts +8 -0
- package/dist/esm/client/builders/query-string-builder.d.ts +15 -0
- package/dist/esm/client/builders/query-string-builder.js +25 -0
- package/dist/esm/client/builders/query-string-builder.js.map +1 -0
- package/dist/esm/client/builders/response-processor.d.ts +39 -0
- package/dist/esm/client/builders/response-processor.js +170 -0
- package/dist/esm/client/builders/response-processor.js.map +1 -0
- package/dist/esm/client/builders/select-mixin.d.ts +31 -0
- package/dist/esm/client/builders/select-mixin.js +30 -0
- package/dist/esm/client/builders/select-mixin.js.map +1 -0
- package/dist/esm/client/builders/select-utils.d.ts +8 -0
- package/dist/esm/client/builders/select-utils.js +15 -0
- package/dist/esm/client/builders/select-utils.js.map +1 -0
- package/dist/esm/client/builders/shared-types.d.ts +39 -0
- package/dist/esm/client/builders/table-utils.d.ts +35 -0
- package/dist/esm/client/builders/table-utils.js +45 -0
- package/dist/esm/client/builders/table-utils.js.map +1 -0
- package/dist/esm/client/database.d.ts +3 -22
- package/dist/esm/client/database.js +14 -76
- package/dist/esm/client/database.js.map +1 -1
- package/dist/esm/client/delete-builder.d.ts +12 -19
- package/dist/esm/client/delete-builder.js +26 -26
- package/dist/esm/client/delete-builder.js.map +1 -1
- package/dist/esm/client/entity-set.d.ts +32 -32
- package/dist/esm/client/entity-set.js +92 -69
- package/dist/esm/client/entity-set.js.map +1 -1
- package/dist/esm/client/error-parser.d.ts +12 -0
- package/dist/esm/client/error-parser.js +30 -0
- package/dist/esm/client/error-parser.js.map +1 -0
- package/dist/esm/client/filemaker-odata.d.ts +2 -4
- package/dist/esm/client/filemaker-odata.js +1 -5
- package/dist/esm/client/filemaker-odata.js.map +1 -1
- package/dist/esm/client/insert-builder.d.ts +9 -12
- package/dist/esm/client/insert-builder.js +70 -24
- package/dist/esm/client/insert-builder.js.map +1 -1
- package/dist/esm/client/query/expand-builder.d.ts +35 -0
- package/dist/esm/client/query/index.d.ts +3 -0
- package/dist/esm/client/query/query-builder.d.ts +133 -0
- package/dist/esm/client/query/query-builder.js +505 -0
- package/dist/esm/client/query/query-builder.js.map +1 -0
- package/dist/esm/client/query/response-processor.d.ts +22 -0
- package/dist/esm/client/query/types.d.ts +52 -0
- package/dist/esm/client/query/url-builder.d.ts +71 -0
- package/dist/esm/client/query/url-builder.js +107 -0
- package/dist/esm/client/query/url-builder.js.map +1 -0
- package/dist/esm/client/query-builder.d.ts +1 -111
- package/dist/esm/client/record-builder.d.ts +56 -64
- package/dist/esm/client/record-builder.js +158 -297
- package/dist/esm/client/record-builder.js.map +1 -1
- package/dist/esm/client/response-processor.d.ts +3 -3
- package/dist/esm/client/update-builder.d.ts +17 -25
- package/dist/esm/client/update-builder.js +56 -30
- package/dist/esm/client/update-builder.js.map +1 -1
- package/dist/esm/errors.d.ts +8 -1
- package/dist/esm/errors.js +17 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +3 -7
- package/dist/esm/index.js +37 -8
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/orm/column.d.ts +45 -0
- package/dist/esm/orm/column.js +59 -0
- package/dist/esm/orm/column.js.map +1 -0
- package/dist/esm/orm/field-builders.d.ts +154 -0
- package/dist/esm/orm/field-builders.js +152 -0
- package/dist/esm/orm/field-builders.js.map +1 -0
- package/dist/esm/orm/index.d.ts +4 -0
- package/dist/esm/orm/operators.d.ts +175 -0
- package/dist/esm/orm/operators.js +221 -0
- package/dist/esm/orm/operators.js.map +1 -0
- package/dist/esm/orm/table.d.ts +341 -0
- package/dist/esm/orm/table.js +211 -0
- package/dist/esm/orm/table.js.map +1 -0
- package/dist/esm/transform.d.ts +20 -21
- package/dist/esm/transform.js +34 -34
- package/dist/esm/transform.js.map +1 -1
- package/dist/esm/types.d.ts +73 -12
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/validation.d.ts +14 -4
- package/dist/esm/validation.js +45 -1
- package/dist/esm/validation.js.map +1 -1
- package/package.json +22 -17
- package/src/client/batch-builder.ts +102 -33
- package/src/client/builders/default-select.ts +69 -0
- package/src/client/builders/expand-builder.ts +236 -0
- package/src/client/builders/index.ts +11 -0
- package/src/client/builders/query-string-builder.ts +41 -0
- package/src/client/builders/response-processor.ts +273 -0
- package/src/client/builders/select-mixin.ts +74 -0
- package/src/client/builders/select-utils.ts +34 -0
- package/src/client/builders/shared-types.ts +41 -0
- package/src/client/builders/table-utils.ts +87 -0
- package/src/client/database.ts +19 -160
- package/src/client/delete-builder.ts +48 -52
- package/src/client/entity-set.ts +227 -302
- package/src/client/error-parser.ts +59 -0
- package/src/client/filemaker-odata.ts +3 -14
- package/src/client/insert-builder.ts +126 -44
- package/src/client/query/expand-builder.ts +164 -0
- package/src/client/query/index.ts +13 -0
- package/src/client/query/query-builder.ts +826 -0
- package/src/client/query/response-processor.ts +244 -0
- package/src/client/query/types.ts +102 -0
- package/src/client/query/url-builder.ts +179 -0
- package/src/client/query-builder.ts +8 -1454
- package/src/client/record-builder.ts +336 -586
- package/src/client/response-processor.ts +4 -5
- package/src/client/update-builder.ts +113 -75
- package/src/errors.ts +22 -1
- package/src/index.ts +58 -5
- package/src/orm/column.ts +78 -0
- package/src/orm/field-builders.ts +296 -0
- package/src/orm/index.ts +60 -0
- package/src/orm/operators.ts +428 -0
- package/src/orm/table.ts +759 -0
- package/src/transform.ts +62 -48
- package/src/types.ts +88 -63
- package/src/validation.ts +76 -4
- package/LICENSE.md +0 -21
- package/dist/esm/client/base-table.d.ts +0 -128
- package/dist/esm/client/base-table.js +0 -57
- package/dist/esm/client/base-table.js.map +0 -1
- package/dist/esm/client/build-occurrences.d.ts +0 -74
- package/dist/esm/client/build-occurrences.js +0 -31
- package/dist/esm/client/build-occurrences.js.map +0 -1
- package/dist/esm/client/query-builder.js +0 -900
- package/dist/esm/client/query-builder.js.map +0 -1
- package/dist/esm/client/table-occurrence.d.ts +0 -86
- package/dist/esm/client/table-occurrence.js +0 -58
- package/dist/esm/client/table-occurrence.js.map +0 -1
- package/src/client/base-table.ts +0 -178
- package/src/client/build-occurrences.ts +0 -155
- package/src/client/query-builder.ts.bak +0 -1457
- package/src/client/table-occurrence.ts +0 -156
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { FMTable } from '../../orm/table.js';
|
|
2
|
+
import { Result } from '../../types.js';
|
|
3
|
+
import { ExpandValidationConfig } from '../../validation.js';
|
|
4
|
+
import { ExpandConfig } from './shared-types.js';
|
|
5
|
+
export interface ProcessResponseConfig {
|
|
6
|
+
table?: FMTable<any, any>;
|
|
7
|
+
schema?: Record<string, any>;
|
|
8
|
+
singleMode: "exact" | "maybe" | false;
|
|
9
|
+
selectedFields?: string[];
|
|
10
|
+
expandValidationConfigs?: ExpandValidationConfig[];
|
|
11
|
+
skipValidation?: boolean;
|
|
12
|
+
useEntityIds?: boolean;
|
|
13
|
+
fieldMapping?: Record<string, string>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Processes OData response with transformation and validation.
|
|
17
|
+
* Shared by QueryBuilder and RecordBuilder.
|
|
18
|
+
*/
|
|
19
|
+
export declare function processODataResponse<T>(rawResponse: any, config: ProcessResponseConfig): Promise<Result<T>>;
|
|
20
|
+
/**
|
|
21
|
+
* Gets schema from a table occurrence, excluding container fields.
|
|
22
|
+
* Container fields are never returned in regular responses (only via getSingleField).
|
|
23
|
+
*/
|
|
24
|
+
export declare function getSchemaFromTable(table: FMTable<any, any> | undefined): Record<string, any> | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Processes query response with expand configs.
|
|
27
|
+
* This is a convenience wrapper that builds validation configs from expand configs.
|
|
28
|
+
*/
|
|
29
|
+
export declare function processQueryResponse<T>(response: any, config: {
|
|
30
|
+
occurrence?: FMTable<any, any>;
|
|
31
|
+
singleMode: "exact" | "maybe" | false;
|
|
32
|
+
queryOptions: {
|
|
33
|
+
select?: (keyof T)[] | string[];
|
|
34
|
+
};
|
|
35
|
+
expandConfigs: ExpandConfig[];
|
|
36
|
+
skipValidation?: boolean;
|
|
37
|
+
useEntityIds?: boolean;
|
|
38
|
+
fieldMapping?: Record<string, string>;
|
|
39
|
+
}): Promise<Result<any>>;
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { validateSingleResponse, validateListResponse } from "../../validation.js";
|
|
2
|
+
import { transformResponseFields } from "../../transform.js";
|
|
3
|
+
import { RecordCountMismatchError } from "../../errors.js";
|
|
4
|
+
import { getBaseTableConfig } from "../../orm/table.js";
|
|
5
|
+
import { ExpandBuilder } from "./expand-builder.js";
|
|
6
|
+
async function processODataResponse(rawResponse, config) {
|
|
7
|
+
const {
|
|
8
|
+
table,
|
|
9
|
+
schema,
|
|
10
|
+
singleMode,
|
|
11
|
+
selectedFields,
|
|
12
|
+
expandValidationConfigs,
|
|
13
|
+
skipValidation,
|
|
14
|
+
useEntityIds,
|
|
15
|
+
fieldMapping
|
|
16
|
+
} = config;
|
|
17
|
+
let response = rawResponse;
|
|
18
|
+
if (table && useEntityIds) {
|
|
19
|
+
response = transformResponseFields(
|
|
20
|
+
response,
|
|
21
|
+
table,
|
|
22
|
+
expandValidationConfigs
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
if (skipValidation) {
|
|
26
|
+
const result = extractRecords(response, singleMode);
|
|
27
|
+
if (result.data && fieldMapping && Object.keys(fieldMapping).length > 0) {
|
|
28
|
+
if (result.error) {
|
|
29
|
+
return { data: void 0, error: result.error };
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
data: renameFieldsInResponse(result.data, fieldMapping),
|
|
33
|
+
error: void 0
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return result;
|
|
37
|
+
}
|
|
38
|
+
if (singleMode !== false) {
|
|
39
|
+
const validation2 = await validateSingleResponse(
|
|
40
|
+
response,
|
|
41
|
+
schema,
|
|
42
|
+
selectedFields,
|
|
43
|
+
expandValidationConfigs,
|
|
44
|
+
singleMode
|
|
45
|
+
);
|
|
46
|
+
if (!validation2.valid) {
|
|
47
|
+
return { data: void 0, error: validation2.error };
|
|
48
|
+
}
|
|
49
|
+
if (fieldMapping && Object.keys(fieldMapping).length > 0) {
|
|
50
|
+
return {
|
|
51
|
+
data: renameFieldsInResponse(validation2.data, fieldMapping),
|
|
52
|
+
error: void 0
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return { data: validation2.data, error: void 0 };
|
|
56
|
+
}
|
|
57
|
+
const validation = await validateListResponse(
|
|
58
|
+
response,
|
|
59
|
+
schema,
|
|
60
|
+
selectedFields,
|
|
61
|
+
expandValidationConfigs
|
|
62
|
+
);
|
|
63
|
+
if (!validation.valid) {
|
|
64
|
+
return { data: void 0, error: validation.error };
|
|
65
|
+
}
|
|
66
|
+
if (fieldMapping && Object.keys(fieldMapping).length > 0) {
|
|
67
|
+
return {
|
|
68
|
+
data: renameFieldsInResponse(validation.data, fieldMapping),
|
|
69
|
+
error: void 0
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return { data: validation.data, error: void 0 };
|
|
73
|
+
}
|
|
74
|
+
function extractRecords(response, singleMode) {
|
|
75
|
+
if (singleMode === false) {
|
|
76
|
+
const records2 = response.value ?? [];
|
|
77
|
+
return { data: records2, error: void 0 };
|
|
78
|
+
}
|
|
79
|
+
const records = response.value ?? [response];
|
|
80
|
+
const count = Array.isArray(records) ? records.length : 1;
|
|
81
|
+
if (count > 1) {
|
|
82
|
+
return {
|
|
83
|
+
data: void 0,
|
|
84
|
+
error: new RecordCountMismatchError(
|
|
85
|
+
singleMode === "exact" ? "one" : "at-most-one",
|
|
86
|
+
count
|
|
87
|
+
)
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (count === 0) {
|
|
91
|
+
if (singleMode === "exact") {
|
|
92
|
+
return { data: void 0, error: new RecordCountMismatchError("one", 0) };
|
|
93
|
+
}
|
|
94
|
+
return { data: null, error: void 0 };
|
|
95
|
+
}
|
|
96
|
+
const record = Array.isArray(records) ? records[0] : records;
|
|
97
|
+
return { data: record, error: void 0 };
|
|
98
|
+
}
|
|
99
|
+
function getSchemaFromTable(table) {
|
|
100
|
+
if (!table) return void 0;
|
|
101
|
+
const baseTableConfig = getBaseTableConfig(table);
|
|
102
|
+
const containerFields = baseTableConfig.containerFields || [];
|
|
103
|
+
const schema = { ...baseTableConfig.schema };
|
|
104
|
+
for (const containerField of containerFields) {
|
|
105
|
+
delete schema[containerField];
|
|
106
|
+
}
|
|
107
|
+
return schema;
|
|
108
|
+
}
|
|
109
|
+
function renameFieldsInResponse(data, fieldMapping) {
|
|
110
|
+
if (!data || typeof data !== "object") {
|
|
111
|
+
return data;
|
|
112
|
+
}
|
|
113
|
+
if (Array.isArray(data)) {
|
|
114
|
+
return data.map((item) => renameFieldsInResponse(item, fieldMapping));
|
|
115
|
+
}
|
|
116
|
+
if ("value" in data && Array.isArray(data.value)) {
|
|
117
|
+
return {
|
|
118
|
+
...data,
|
|
119
|
+
value: data.value.map(
|
|
120
|
+
(item) => renameFieldsInResponse(item, fieldMapping)
|
|
121
|
+
)
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
const renamed = {};
|
|
125
|
+
for (const [key, value] of Object.entries(data)) {
|
|
126
|
+
const outputKey = fieldMapping[key];
|
|
127
|
+
if (outputKey) {
|
|
128
|
+
renamed[outputKey] = value;
|
|
129
|
+
} else {
|
|
130
|
+
renamed[key] = value;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return renamed;
|
|
134
|
+
}
|
|
135
|
+
async function processQueryResponse(response, config) {
|
|
136
|
+
const {
|
|
137
|
+
occurrence,
|
|
138
|
+
singleMode,
|
|
139
|
+
queryOptions,
|
|
140
|
+
expandConfigs,
|
|
141
|
+
skipValidation,
|
|
142
|
+
useEntityIds,
|
|
143
|
+
fieldMapping
|
|
144
|
+
} = config;
|
|
145
|
+
const expandBuilder = new ExpandBuilder(useEntityIds ?? false);
|
|
146
|
+
const expandValidationConfigs = expandBuilder.buildValidationConfigs(expandConfigs);
|
|
147
|
+
const selectedFields = queryOptions.select ? Array.isArray(queryOptions.select) ? queryOptions.select.map(String) : [String(queryOptions.select)] : void 0;
|
|
148
|
+
let processedResponse = await processODataResponse(response, {
|
|
149
|
+
table: occurrence,
|
|
150
|
+
schema: getSchemaFromTable(occurrence),
|
|
151
|
+
singleMode,
|
|
152
|
+
selectedFields,
|
|
153
|
+
expandValidationConfigs,
|
|
154
|
+
skipValidation,
|
|
155
|
+
useEntityIds
|
|
156
|
+
});
|
|
157
|
+
if (processedResponse.data && fieldMapping && Object.keys(fieldMapping).length > 0) {
|
|
158
|
+
processedResponse = {
|
|
159
|
+
...processedResponse,
|
|
160
|
+
data: renameFieldsInResponse(processedResponse.data, fieldMapping)
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return processedResponse;
|
|
164
|
+
}
|
|
165
|
+
export {
|
|
166
|
+
getSchemaFromTable,
|
|
167
|
+
processODataResponse,
|
|
168
|
+
processQueryResponse
|
|
169
|
+
};
|
|
170
|
+
//# sourceMappingURL=response-processor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response-processor.js","sources":["../../../../src/client/builders/response-processor.ts"],"sourcesContent":["import type { FMTable } from \"../../orm/table\";\nimport type { Result } from \"../../types\";\nimport type { ExpandValidationConfig } from \"../../validation\";\nimport { validateSingleResponse, validateListResponse } from \"../../validation\";\nimport { transformResponseFields } from \"../../transform\";\nimport { RecordCountMismatchError } from \"../../errors\";\nimport { getBaseTableConfig } from \"../../orm/table\";\nimport { ExpandBuilder } from \"./expand-builder\";\nimport type { ExpandConfig } from \"./shared-types\";\n\nexport interface ProcessResponseConfig {\n table?: FMTable<any, any>;\n schema?: Record<string, any>;\n singleMode: \"exact\" | \"maybe\" | false;\n selectedFields?: string[];\n expandValidationConfigs?: ExpandValidationConfig[];\n skipValidation?: boolean;\n useEntityIds?: boolean;\n // Mapping from field names to output keys (for renamed fields in select)\n fieldMapping?: Record<string, string>;\n}\n\n/**\n * Processes OData response with transformation and validation.\n * Shared by QueryBuilder and RecordBuilder.\n */\nexport async function processODataResponse<T>(\n rawResponse: any,\n config: ProcessResponseConfig,\n): Promise<Result<T>> {\n const {\n table,\n schema,\n singleMode,\n selectedFields,\n expandValidationConfigs,\n skipValidation,\n useEntityIds,\n fieldMapping,\n } = config;\n\n // Transform field IDs back to names if using entity IDs\n let response = rawResponse;\n if (table && useEntityIds) {\n response = transformResponseFields(\n response,\n table,\n expandValidationConfigs,\n );\n }\n\n // Fast path: skip validation\n if (skipValidation) {\n const result = extractRecords(response, singleMode);\n // Rename fields AFTER extraction (but before returning)\n if (result.data && fieldMapping && Object.keys(fieldMapping).length > 0) {\n if (result.error) {\n return { data: undefined, error: result.error } as Result<T>;\n }\n return {\n data: renameFieldsInResponse(result.data, fieldMapping) as T,\n error: undefined,\n };\n }\n return result as Result<T>;\n }\n\n // Validation path\n if (singleMode !== false) {\n const validation = await validateSingleResponse<any>(\n response,\n schema,\n selectedFields as any,\n expandValidationConfigs,\n singleMode,\n );\n\n if (!validation.valid) {\n return { data: undefined, error: validation.error };\n }\n\n // Rename fields AFTER validation completes\n if (fieldMapping && Object.keys(fieldMapping).length > 0) {\n return {\n data: renameFieldsInResponse(validation.data, fieldMapping) as T,\n error: undefined,\n };\n }\n\n return { data: validation.data as T, error: undefined };\n }\n\n const validation = await validateListResponse<any>(\n response,\n schema,\n selectedFields as any,\n expandValidationConfigs,\n );\n\n if (!validation.valid) {\n return { data: undefined, error: validation.error };\n }\n\n // Rename fields AFTER validation completes\n if (fieldMapping && Object.keys(fieldMapping).length > 0) {\n return {\n data: renameFieldsInResponse(validation.data, fieldMapping) as T,\n error: undefined,\n };\n }\n\n return { data: validation.data as T, error: undefined };\n}\n\n/**\n * Extracts records from response without validation.\n */\nfunction extractRecords<T>(\n response: any,\n singleMode: \"exact\" | \"maybe\" | false,\n): Result<T> {\n if (singleMode === false) {\n const records = response.value ?? [];\n return { data: records as T, error: undefined };\n }\n\n const records = response.value ?? [response];\n const count = Array.isArray(records) ? records.length : 1;\n\n if (count > 1) {\n return {\n data: undefined,\n error: new RecordCountMismatchError(\n singleMode === \"exact\" ? \"one\" : \"at-most-one\",\n count,\n ),\n };\n }\n\n if (count === 0) {\n if (singleMode === \"exact\") {\n return { data: undefined, error: new RecordCountMismatchError(\"one\", 0) };\n }\n return { data: null as T, error: undefined };\n }\n\n const record = Array.isArray(records) ? records[0] : records;\n return { data: record as T, error: undefined };\n}\n\n/**\n * Gets schema from a table occurrence, excluding container fields.\n * Container fields are never returned in regular responses (only via getSingleField).\n */\nexport function getSchemaFromTable(\n table: FMTable<any, any> | undefined,\n): Record<string, any> | undefined {\n if (!table) return undefined;\n const baseTableConfig = getBaseTableConfig(table);\n const containerFields = baseTableConfig.containerFields || [];\n\n // Filter out container fields from schema\n const schema = { ...baseTableConfig.schema };\n for (const containerField of containerFields) {\n delete schema[containerField as string];\n }\n\n return schema;\n}\n\n/**\n * Renames fields in response data according to the field mapping.\n * Used when select() is called with renamed fields (e.g., { userEmail: users.email }).\n */\nfunction renameFieldsInResponse(\n data: any,\n fieldMapping: Record<string, string>,\n): any {\n if (!data || typeof data !== \"object\") {\n return data;\n }\n\n // Handle array responses\n if (Array.isArray(data)) {\n return data.map((item) => renameFieldsInResponse(item, fieldMapping));\n }\n\n // Handle OData list response structure\n if (\"value\" in data && Array.isArray(data.value)) {\n return {\n ...data,\n value: data.value.map((item: any) =>\n renameFieldsInResponse(item, fieldMapping),\n ),\n };\n }\n\n // Handle single record\n const renamed: Record<string, any> = {};\n for (const [key, value] of Object.entries(data)) {\n // Check if this field should be renamed\n const outputKey = fieldMapping[key];\n if (outputKey) {\n renamed[outputKey] = value;\n } else {\n renamed[key] = value;\n }\n }\n return renamed;\n}\n\n/**\n * Processes query response with expand configs.\n * This is a convenience wrapper that builds validation configs from expand configs.\n */\nexport async function processQueryResponse<T>(\n response: any,\n config: {\n occurrence?: FMTable<any, any>;\n singleMode: \"exact\" | \"maybe\" | false;\n queryOptions: { select?: (keyof T)[] | string[] };\n expandConfigs: ExpandConfig[];\n skipValidation?: boolean;\n useEntityIds?: boolean;\n // Mapping from field names to output keys (for renamed fields in select)\n fieldMapping?: Record<string, string>;\n },\n): Promise<Result<any>> {\n const {\n occurrence,\n singleMode,\n queryOptions,\n expandConfigs,\n skipValidation,\n useEntityIds,\n fieldMapping,\n } = config;\n\n const expandBuilder = new ExpandBuilder(useEntityIds ?? false);\n const expandValidationConfigs =\n expandBuilder.buildValidationConfigs(expandConfigs);\n\n const selectedFields = queryOptions.select\n ? Array.isArray(queryOptions.select)\n ? queryOptions.select.map(String)\n : [String(queryOptions.select)]\n : undefined;\n\n // Process the response first\n let processedResponse = await processODataResponse(response, {\n table: occurrence,\n schema: getSchemaFromTable(occurrence),\n singleMode,\n selectedFields,\n expandValidationConfigs,\n skipValidation,\n useEntityIds,\n });\n\n // Rename fields if field mapping is provided (for renamed fields in select)\n if (\n processedResponse.data &&\n fieldMapping &&\n Object.keys(fieldMapping).length > 0\n ) {\n processedResponse = {\n ...processedResponse,\n data: renameFieldsInResponse(processedResponse.data, fieldMapping),\n };\n }\n\n return processedResponse;\n}\n"],"names":["validation","records"],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Column } from '../../orm/column.js';
|
|
2
|
+
/**
|
|
3
|
+
* Utility function for processing select() calls.
|
|
4
|
+
* Used by both QueryBuilder and RecordBuilder to eliminate duplication.
|
|
5
|
+
*
|
|
6
|
+
* @param fields - Field names or Column references
|
|
7
|
+
* @returns Object with selectedFields array
|
|
8
|
+
*/
|
|
9
|
+
export declare function processSelectFields(...fields: (string | Column<any, string>)[]): {
|
|
10
|
+
selectedFields: string[];
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Processes select() calls with field renaming support.
|
|
14
|
+
* Validates columns belong to the correct table and builds field mapping for renamed fields.
|
|
15
|
+
* Used by both QueryBuilder and RecordBuilder to eliminate duplication.
|
|
16
|
+
*
|
|
17
|
+
* @param fields - Object mapping output keys to column references
|
|
18
|
+
* @param tableName - Expected table name for validation
|
|
19
|
+
* @returns Object with selectedFields array and fieldMapping for renamed fields
|
|
20
|
+
*/
|
|
21
|
+
export declare function processSelectWithRenames<TTableName extends string>(fields: Record<string, Column<any, TTableName>>, tableName: string): {
|
|
22
|
+
selectedFields: string[];
|
|
23
|
+
fieldMapping: Record<string, string>;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Legacy class name for backward compatibility.
|
|
27
|
+
* @deprecated Use processSelectFields function instead
|
|
28
|
+
*/
|
|
29
|
+
export declare class SelectMixin {
|
|
30
|
+
static processSelect: typeof processSelectFields;
|
|
31
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { isColumn } from "../../orm/column.js";
|
|
2
|
+
function processSelectWithRenames(fields, tableName) {
|
|
3
|
+
const selectedFields = [];
|
|
4
|
+
const fieldMapping = {};
|
|
5
|
+
for (const [outputKey, column] of Object.entries(fields)) {
|
|
6
|
+
if (!isColumn(column)) {
|
|
7
|
+
throw new Error(
|
|
8
|
+
`select() expects column references, but got: ${typeof column}`
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
if (column.tableName !== tableName) {
|
|
12
|
+
console.warn(
|
|
13
|
+
`Column ${column.toString()} is from table "${column.tableName}", but query is for table "${tableName}"`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
const fieldName = column.fieldName;
|
|
17
|
+
selectedFields.push(fieldName);
|
|
18
|
+
if (fieldName !== outputKey) {
|
|
19
|
+
fieldMapping[fieldName] = outputKey;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
selectedFields,
|
|
24
|
+
fieldMapping: Object.keys(fieldMapping).length > 0 ? fieldMapping : {}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export {
|
|
28
|
+
processSelectWithRenames
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=select-mixin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select-mixin.js","sources":["../../../../src/client/builders/select-mixin.ts"],"sourcesContent":["import { isColumn, type Column } from \"../../orm/column\";\n\n/**\n * Utility function for processing select() calls.\n * Used by both QueryBuilder and RecordBuilder to eliminate duplication.\n *\n * @param fields - Field names or Column references\n * @returns Object with selectedFields array\n */\nexport function processSelectFields(\n ...fields: (string | Column<any, string>)[]\n): { selectedFields: string[] } {\n const fieldNames = fields.map((field) => {\n if (isColumn(field)) {\n return field.fieldName as string;\n }\n return String(field);\n });\n return { selectedFields: [...new Set(fieldNames)] };\n}\n\n/**\n * Processes select() calls with field renaming support.\n * Validates columns belong to the correct table and builds field mapping for renamed fields.\n * Used by both QueryBuilder and RecordBuilder to eliminate duplication.\n *\n * @param fields - Object mapping output keys to column references\n * @param tableName - Expected table name for validation\n * @returns Object with selectedFields array and fieldMapping for renamed fields\n */\nexport function processSelectWithRenames<TTableName extends string>(\n fields: Record<string, Column<any, TTableName>>,\n tableName: string,\n): { selectedFields: string[]; fieldMapping: Record<string, string> } {\n const selectedFields: string[] = [];\n const fieldMapping: Record<string, string> = {};\n\n for (const [outputKey, column] of Object.entries(fields)) {\n if (!isColumn(column)) {\n throw new Error(\n `select() expects column references, but got: ${typeof column}`,\n );\n }\n\n // Warn (not throw) on table mismatch for consistency\n if (column.tableName !== tableName) {\n console.warn(\n `Column ${column.toString()} is from table \"${column.tableName}\", but query is for table \"${tableName}\"`,\n );\n }\n\n const fieldName = column.fieldName;\n selectedFields.push(fieldName);\n\n // Build mapping from field name to output key (only if renamed)\n if (fieldName !== outputKey) {\n fieldMapping[fieldName] = outputKey;\n }\n }\n\n return {\n selectedFields,\n fieldMapping: Object.keys(fieldMapping).length > 0 ? fieldMapping : {},\n };\n}\n\n/**\n * Legacy class name for backward compatibility.\n * @deprecated Use processSelectFields function instead\n */\nexport class SelectMixin {\n static processSelect = processSelectFields;\n}\n\n"],"names":[],"mappings":";AA8BgB,SAAA,yBACd,QACA,WACoE;AACpE,QAAM,iBAA2B,CAAC;AAClC,QAAM,eAAuC,CAAC;AAE9C,aAAW,CAAC,WAAW,MAAM,KAAK,OAAO,QAAQ,MAAM,GAAG;AACpD,QAAA,CAAC,SAAS,MAAM,GAAG;AACrB,YAAM,IAAI;AAAA,QACR,gDAAgD,OAAO,MAAM;AAAA,MAC/D;AAAA,IAAA;AAIE,QAAA,OAAO,cAAc,WAAW;AAC1B,cAAA;AAAA,QACN,UAAU,OAAO,UAAU,mBAAmB,OAAO,SAAS,8BAA8B,SAAS;AAAA,MACvG;AAAA,IAAA;AAGF,UAAM,YAAY,OAAO;AACzB,mBAAe,KAAK,SAAS;AAG7B,QAAI,cAAc,WAAW;AAC3B,mBAAa,SAAS,IAAI;AAAA,IAAA;AAAA,EAC5B;AAGK,SAAA;AAAA,IACL;AAAA,IACA,cAAc,OAAO,KAAK,YAAY,EAAE,SAAS,IAAI,eAAe,CAAA;AAAA,EACtE;AACF;"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { FMTable } from '../../orm/table.js';
|
|
2
|
+
/**
|
|
3
|
+
* Formats select fields for use in OData query strings.
|
|
4
|
+
* - Transforms field names to FMFIDs if using entity IDs
|
|
5
|
+
* - Wraps "id" fields in double quotes (OData reserved)
|
|
6
|
+
* - URL-encodes special characters but preserves spaces
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatSelectFields(select: string[] | readonly string[] | undefined, table?: FMTable<any, any>, useEntityIds?: boolean): string;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { transformFieldNamesArray } from "../../transform.js";
|
|
2
|
+
function formatSelectFields(select, table, useEntityIds) {
|
|
3
|
+
if (!select || select.length === 0) return "";
|
|
4
|
+
const selectArray = Array.isArray(select) ? select : [select];
|
|
5
|
+
const transformedFields = table && useEntityIds ? transformFieldNamesArray(selectArray.map(String), table) : selectArray.map(String);
|
|
6
|
+
return transformedFields.map((field) => {
|
|
7
|
+
if (field === "id") return `"id"`;
|
|
8
|
+
const encoded = encodeURIComponent(field);
|
|
9
|
+
return encoded.replace(/%20/g, " ");
|
|
10
|
+
}).join(",");
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
formatSelectFields
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=select-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"select-utils.js","sources":["../../../../src/client/builders/select-utils.ts"],"sourcesContent":["import type { FMTable } from \"../../orm/table\";\nimport { transformFieldNamesArray } from \"../../transform\";\n\n/**\n * Formats select fields for use in OData query strings.\n * - Transforms field names to FMFIDs if using entity IDs\n * - Wraps \"id\" fields in double quotes (OData reserved)\n * - URL-encodes special characters but preserves spaces\n */\nexport function formatSelectFields(\n select: string[] | readonly string[] | undefined,\n table?: FMTable<any, any>,\n useEntityIds?: boolean,\n): string {\n if (!select || select.length === 0) return \"\";\n\n const selectArray = Array.isArray(select) ? select : [select];\n\n // Transform to field IDs if using entity IDs\n const transformedFields =\n table && useEntityIds\n ? transformFieldNamesArray(selectArray.map(String), table)\n : selectArray.map(String);\n\n return transformedFields\n .map((field) => {\n if (field === \"id\") return `\"id\"`;\n const encoded = encodeURIComponent(field);\n return encoded.replace(/%20/g, \" \");\n })\n .join(\",\");\n}\n\n\n"],"names":[],"mappings":";AASgB,SAAA,mBACd,QACA,OACA,cACQ;AACR,MAAI,CAAC,UAAU,OAAO,WAAW,EAAU,QAAA;AAE3C,QAAM,cAAc,MAAM,QAAQ,MAAM,IAAI,SAAS,CAAC,MAAM;AAG5D,QAAM,oBACJ,SAAS,eACL,yBAAyB,YAAY,IAAI,MAAM,GAAG,KAAK,IACvD,YAAY,IAAI,MAAM;AAErB,SAAA,kBACJ,IAAI,CAAC,UAAU;AACV,QAAA,UAAU,KAAa,QAAA;AACrB,UAAA,UAAU,mBAAmB,KAAK;AACjC,WAAA,QAAQ,QAAQ,QAAQ,GAAG;AAAA,EAAA,CACnC,EACA,KAAK,GAAG;AACb;"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { QueryOptions } from 'odata-query';
|
|
2
|
+
import { ExecutionContext } from '../../types.js';
|
|
3
|
+
import { FMTable } from '../../orm/table.js';
|
|
4
|
+
/**
|
|
5
|
+
* Expand configuration used by both QueryBuilder and RecordBuilder
|
|
6
|
+
*/
|
|
7
|
+
export type ExpandConfig = {
|
|
8
|
+
relation: string;
|
|
9
|
+
options?: Partial<QueryOptions<any>>;
|
|
10
|
+
targetTable?: FMTable<any, any>;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Type to represent expanded relations in return types
|
|
14
|
+
*/
|
|
15
|
+
export type ExpandedRelations = Record<string, {
|
|
16
|
+
schema: any;
|
|
17
|
+
selected: any;
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Navigation context shared between builders
|
|
21
|
+
*/
|
|
22
|
+
export interface NavigationContext {
|
|
23
|
+
isNavigate?: boolean;
|
|
24
|
+
navigateRecordId?: string | number;
|
|
25
|
+
navigateRelation?: string;
|
|
26
|
+
navigateSourceTableName?: string;
|
|
27
|
+
navigateBaseRelation?: string;
|
|
28
|
+
navigateBasePath?: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Common builder configuration
|
|
32
|
+
*/
|
|
33
|
+
export interface BuilderConfig<Occ extends FMTable<any, any> | undefined> {
|
|
34
|
+
occurrence?: Occ;
|
|
35
|
+
tableName: string;
|
|
36
|
+
databaseName: string;
|
|
37
|
+
context: ExecutionContext;
|
|
38
|
+
databaseUseEntityIds?: boolean;
|
|
39
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { ExecutionContext, ExecuteOptions } from '../../types.js';
|
|
2
|
+
import { FMTable } from '../../orm/table.js';
|
|
3
|
+
import { FFetchOptions } from '@fetchkit/ffetch';
|
|
4
|
+
/**
|
|
5
|
+
* Resolves table identifier based on entity ID settings.
|
|
6
|
+
* Used by both QueryBuilder and RecordBuilder.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveTableId(table: FMTable<any, any> | undefined, fallbackTableName: string, context: ExecutionContext, useEntityIdsOverride?: boolean): string;
|
|
9
|
+
/**
|
|
10
|
+
* Merges database-level useEntityIds with per-request options.
|
|
11
|
+
*/
|
|
12
|
+
export declare function mergeEntityIdOptions<T extends Record<string, any>>(options: T | undefined, databaseDefault: boolean): T & {
|
|
13
|
+
useEntityIds?: boolean;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Type-safe helper for merging execute options with entity ID settings
|
|
17
|
+
*/
|
|
18
|
+
export declare function mergeExecuteOptions(options: (RequestInit & FFetchOptions & ExecuteOptions) | undefined, databaseUseEntityIds: boolean): RequestInit & FFetchOptions & {
|
|
19
|
+
useEntityIds?: boolean;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Creates an OData Request object with proper headers.
|
|
23
|
+
* Used by both QueryBuilder and RecordBuilder to eliminate duplication.
|
|
24
|
+
*
|
|
25
|
+
* @param baseUrl - Base URL for the request
|
|
26
|
+
* @param config - Request configuration with method and url
|
|
27
|
+
* @param options - Optional execution options
|
|
28
|
+
* @returns Request object ready to use
|
|
29
|
+
*/
|
|
30
|
+
export declare function createODataRequest(baseUrl: string, config: {
|
|
31
|
+
method: string;
|
|
32
|
+
url: string;
|
|
33
|
+
}, options?: {
|
|
34
|
+
includeODataAnnotations?: boolean;
|
|
35
|
+
}): Request;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { getAcceptHeader } from "../../types.js";
|
|
2
|
+
import { isUsingEntityIds, getTableName, getTableId } from "../../orm/table.js";
|
|
3
|
+
function resolveTableId(table, fallbackTableName, context, useEntityIdsOverride) {
|
|
4
|
+
var _a;
|
|
5
|
+
if (!table) {
|
|
6
|
+
return fallbackTableName;
|
|
7
|
+
}
|
|
8
|
+
const contextDefault = ((_a = context._getUseEntityIds) == null ? void 0 : _a.call(context)) ?? false;
|
|
9
|
+
const shouldUseIds = useEntityIdsOverride ?? contextDefault;
|
|
10
|
+
if (shouldUseIds) {
|
|
11
|
+
if (!isUsingEntityIds(table)) {
|
|
12
|
+
throw new Error(
|
|
13
|
+
`useEntityIds is true but table "${getTableName(table)}" does not have entity IDs configured`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
return getTableId(table);
|
|
17
|
+
}
|
|
18
|
+
return getTableName(table);
|
|
19
|
+
}
|
|
20
|
+
function mergeEntityIdOptions(options, databaseDefault) {
|
|
21
|
+
return {
|
|
22
|
+
...options,
|
|
23
|
+
useEntityIds: (options == null ? void 0 : options.useEntityIds) ?? databaseDefault
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function mergeExecuteOptions(options, databaseUseEntityIds) {
|
|
27
|
+
return mergeEntityIdOptions(options, databaseUseEntityIds);
|
|
28
|
+
}
|
|
29
|
+
function createODataRequest(baseUrl, config, options) {
|
|
30
|
+
const fullUrl = `${baseUrl}${config.url}`;
|
|
31
|
+
return new Request(fullUrl, {
|
|
32
|
+
method: config.method,
|
|
33
|
+
headers: {
|
|
34
|
+
"Content-Type": "application/json",
|
|
35
|
+
Accept: getAcceptHeader(options == null ? void 0 : options.includeODataAnnotations)
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
createODataRequest,
|
|
41
|
+
mergeEntityIdOptions,
|
|
42
|
+
mergeExecuteOptions,
|
|
43
|
+
resolveTableId
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=table-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table-utils.js","sources":["../../../../src/client/builders/table-utils.ts"],"sourcesContent":["import type { ExecutionContext } from \"../../types\";\nimport { getAcceptHeader } from \"../../types\";\nimport type { FMTable } from \"../../orm/table\";\nimport {\n getTableName,\n getTableId as getTableIdHelper,\n isUsingEntityIds,\n} from \"../../orm/table\";\nimport type { FFetchOptions } from \"@fetchkit/ffetch\";\nimport type { ExecuteOptions } from \"../../types\";\n\n/**\n * Resolves table identifier based on entity ID settings.\n * Used by both QueryBuilder and RecordBuilder.\n */\nexport function resolveTableId(\n table: FMTable<any, any> | undefined,\n fallbackTableName: string,\n context: ExecutionContext,\n useEntityIdsOverride?: boolean,\n): string {\n if (!table) {\n return fallbackTableName;\n }\n\n const contextDefault = context._getUseEntityIds?.() ?? false;\n const shouldUseIds = useEntityIdsOverride ?? contextDefault;\n\n if (shouldUseIds) {\n if (!isUsingEntityIds(table)) {\n throw new Error(\n `useEntityIds is true but table \"${getTableName(table)}\" does not have entity IDs configured`,\n );\n }\n return getTableIdHelper(table);\n }\n\n return getTableName(table);\n}\n\n/**\n * Merges database-level useEntityIds with per-request options.\n */\nexport function mergeEntityIdOptions<T extends Record<string, any>>(\n options: T | undefined,\n databaseDefault: boolean,\n): T & { useEntityIds?: boolean } {\n return {\n ...options,\n useEntityIds: (options as any)?.useEntityIds ?? databaseDefault,\n } as T & { useEntityIds?: boolean };\n}\n\n/**\n * Type-safe helper for merging execute options with entity ID settings\n */\nexport function mergeExecuteOptions(\n options: (RequestInit & FFetchOptions & ExecuteOptions) | undefined,\n databaseUseEntityIds: boolean,\n): RequestInit & FFetchOptions & { useEntityIds?: boolean } {\n return mergeEntityIdOptions(options, databaseUseEntityIds);\n}\n\n/**\n * Creates an OData Request object with proper headers.\n * Used by both QueryBuilder and RecordBuilder to eliminate duplication.\n *\n * @param baseUrl - Base URL for the request\n * @param config - Request configuration with method and url\n * @param options - Optional execution options\n * @returns Request object ready to use\n */\nexport function createODataRequest(\n baseUrl: string,\n config: { method: string; url: string },\n options?: { includeODataAnnotations?: boolean },\n): Request {\n const fullUrl = `${baseUrl}${config.url}`;\n\n return new Request(fullUrl, {\n method: config.method,\n headers: {\n \"Content-Type\": \"application/json\",\n Accept: getAcceptHeader(options?.includeODataAnnotations),\n },\n });\n}\n"],"names":["getTableIdHelper"],"mappings":";;AAeO,SAAS,eACd,OACA,mBACA,SACA,sBACQ;;AACR,MAAI,CAAC,OAAO;AACH,WAAA;AAAA,EAAA;AAGH,QAAA,mBAAiB,aAAQ,qBAAR,qCAAgC;AACvD,QAAM,eAAe,wBAAwB;AAE7C,MAAI,cAAc;AACZ,QAAA,CAAC,iBAAiB,KAAK,GAAG;AAC5B,YAAM,IAAI;AAAA,QACR,mCAAmC,aAAa,KAAK,CAAC;AAAA,MACxD;AAAA,IAAA;AAEF,WAAOA,WAAiB,KAAK;AAAA,EAAA;AAG/B,SAAO,aAAa,KAAK;AAC3B;AAKgB,SAAA,qBACd,SACA,iBACgC;AACzB,SAAA;AAAA,IACL,GAAG;AAAA,IACH,eAAe,mCAAiB,iBAAgB;AAAA,EAClD;AACF;AAKgB,SAAA,oBACd,SACA,sBAC0D;AACnD,SAAA,qBAAqB,SAAS,oBAAoB;AAC3D;AAWgB,SAAA,mBACd,SACA,QACA,SACS;AACT,QAAM,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG;AAEhC,SAAA,IAAI,QAAQ,SAAS;AAAA,IAC1B,QAAQ,OAAO;AAAA,IACf,SAAS;AAAA,MACP,gBAAgB;AAAA,MAChB,QAAQ,gBAAgB,mCAAS,uBAAuB;AAAA,IAAA;AAAA,EAC1D,CACD;AACH;"}
|
|
@@ -1,24 +1,15 @@
|
|
|
1
1
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
2
|
import { ExecutionContext, ExecutableBuilder, Metadata } from '../types.js';
|
|
3
|
-
import { BaseTable } from './base-table.js';
|
|
4
|
-
import { TableOccurrence } from './table-occurrence.js';
|
|
5
3
|
import { EntitySet } from './entity-set.js';
|
|
6
4
|
import { BatchBuilder } from './batch-builder.js';
|
|
7
5
|
import { SchemaManager } from './schema-manager.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
infer First,
|
|
11
|
-
...infer Rest extends readonly TableOccurrence<any, any, any, any>[]
|
|
12
|
-
] ? First extends TableOccurrence<any, any, any, any> ? First["name"] extends Name ? First : FindOccurrenceByName<Rest, Name> : never : never;
|
|
13
|
-
type ExtractOccurrenceNames<Occurrences extends readonly TableOccurrence<any, any, any, any>[]> = Occurrences extends readonly [] ? string : Occurrences[number]["name"];
|
|
14
|
-
export declare class Database<Occurrences extends readonly TableOccurrence<any, any, any, any>[] = readonly []> {
|
|
6
|
+
import { FMTable } from '../orm/table.js';
|
|
7
|
+
export declare class Database {
|
|
15
8
|
private readonly databaseName;
|
|
16
9
|
private readonly context;
|
|
17
|
-
private occurrenceMap;
|
|
18
10
|
private _useEntityIds;
|
|
19
11
|
readonly schema: SchemaManager;
|
|
20
12
|
constructor(databaseName: string, context: ExecutionContext, config?: {
|
|
21
|
-
occurrences?: Occurrences | undefined;
|
|
22
13
|
/**
|
|
23
14
|
* Whether to use entity IDs instead of field names in the actual requests to the server
|
|
24
15
|
* Defaults to true if all occurrences use entity IDs, false otherwise
|
|
@@ -26,16 +17,7 @@ export declare class Database<Occurrences extends readonly TableOccurrence<any,
|
|
|
26
17
|
*/
|
|
27
18
|
useEntityIds?: boolean;
|
|
28
19
|
});
|
|
29
|
-
|
|
30
|
-
* Returns true if any table occurrence in this database is using entity IDs.
|
|
31
|
-
*/
|
|
32
|
-
isUsingEntityIds(): boolean;
|
|
33
|
-
/**
|
|
34
|
-
* Gets a table occurrence by name.
|
|
35
|
-
* @internal
|
|
36
|
-
*/
|
|
37
|
-
getOccurrence(name: string): TableOccurrence<any, any, any, any> | undefined;
|
|
38
|
-
from<Name extends ExtractOccurrenceNames<Occurrences> | (string & {})>(name: Name): Occurrences extends readonly [] ? EntitySet<Record<string, StandardSchemaV1>, undefined> : Name extends ExtractOccurrenceNames<Occurrences> ? EntitySet<ExtractSchemaFromOccurrence<FindOccurrenceByName<Occurrences, Name>>, FindOccurrenceByName<Occurrences, Name>> : EntitySet<Record<string, StandardSchemaV1>, undefined>;
|
|
20
|
+
from<T extends FMTable<any, any>>(table: T): EntitySet<T>;
|
|
39
21
|
/**
|
|
40
22
|
* Retrieves the OData metadata for this database.
|
|
41
23
|
* @param args Optional configuration object
|
|
@@ -95,4 +77,3 @@ export declare class Database<Occurrences extends readonly TableOccurrence<any,
|
|
|
95
77
|
*/
|
|
96
78
|
batch<const Builders extends readonly ExecutableBuilder<any>[]>(builders: Builders): BatchBuilder<Builders>;
|
|
97
79
|
}
|
|
98
|
-
export {};
|
|
@@ -4,91 +4,29 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
|
|
|
4
4
|
import { EntitySet } from "./entity-set.js";
|
|
5
5
|
import { BatchBuilder } from "./batch-builder.js";
|
|
6
6
|
import { SchemaManager } from "./schema-manager.js";
|
|
7
|
+
import { FMTable } from "../orm/table.js";
|
|
7
8
|
class Database {
|
|
8
9
|
constructor(databaseName, context, config) {
|
|
9
|
-
__publicField(this, "occurrenceMap");
|
|
10
10
|
__publicField(this, "_useEntityIds", false);
|
|
11
11
|
__publicField(this, "schema");
|
|
12
12
|
this.databaseName = databaseName;
|
|
13
13
|
this.context = context;
|
|
14
|
-
this.occurrenceMap = /* @__PURE__ */ new Map();
|
|
15
|
-
if (config == null ? void 0 : config.occurrences) {
|
|
16
|
-
const occurrencesWithIds = [];
|
|
17
|
-
const occurrencesWithoutIds = [];
|
|
18
|
-
for (const occ of config.occurrences) {
|
|
19
|
-
this.occurrenceMap.set(occ.name, occ);
|
|
20
|
-
const hasTableId = occ.isUsingTableId();
|
|
21
|
-
const hasFieldIds = occ.baseTable.isUsingFieldIds();
|
|
22
|
-
if (hasTableId && hasFieldIds) {
|
|
23
|
-
occurrencesWithIds.push(occ.name);
|
|
24
|
-
} else if (!hasTableId && !hasFieldIds) {
|
|
25
|
-
occurrencesWithoutIds.push(occ.name);
|
|
26
|
-
} else {
|
|
27
|
-
throw new Error(
|
|
28
|
-
`TableOccurrence "${occ.name}" has inconsistent entity ID configuration. Both fmtId (${hasTableId ? "present" : "missing"}) and fmfIds (${hasFieldIds ? "present" : "missing"}) must be defined together.`
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
const allOccurrencesUseEntityIds = occurrencesWithIds.length > 0 && occurrencesWithoutIds.length === 0;
|
|
33
|
-
const hasMixedUsage = occurrencesWithIds.length > 0 && occurrencesWithoutIds.length > 0;
|
|
34
|
-
if (config.useEntityIds !== void 0) {
|
|
35
|
-
if (config.useEntityIds === false) {
|
|
36
|
-
this._useEntityIds = false;
|
|
37
|
-
} else if (config.useEntityIds === true) {
|
|
38
|
-
if (hasMixedUsage || occurrencesWithoutIds.length > 0) {
|
|
39
|
-
throw new Error(
|
|
40
|
-
`useEntityIds is set to true but some occurrences do not use entity IDs. Occurrences without entity IDs: [${occurrencesWithoutIds.join(", ")}]. Either set useEntityIds to false or configure all occurrences with entity IDs.`
|
|
41
|
-
);
|
|
42
|
-
}
|
|
43
|
-
this._useEntityIds = true;
|
|
44
|
-
}
|
|
45
|
-
} else {
|
|
46
|
-
if (hasMixedUsage) {
|
|
47
|
-
throw new Error(
|
|
48
|
-
`Cannot mix TableOccurrence instances with and without entity IDs in the same database. Occurrences with entity IDs: [${occurrencesWithIds.join(", ")}]. Occurrences without entity IDs: [${occurrencesWithoutIds.join(", ")}]. Either all table occurrences must use entity IDs (fmtId + fmfIds), none should, or explicitly set useEntityIds to false.`
|
|
49
|
-
);
|
|
50
|
-
}
|
|
51
|
-
this._useEntityIds = allOccurrencesUseEntityIds;
|
|
52
|
-
}
|
|
53
|
-
} else {
|
|
54
|
-
this._useEntityIds = (config == null ? void 0 : config.useEntityIds) ?? false;
|
|
55
|
-
}
|
|
56
|
-
if (this.context._setUseEntityIds) {
|
|
57
|
-
this.context._setUseEntityIds(this._useEntityIds);
|
|
58
|
-
}
|
|
59
14
|
this.schema = new SchemaManager(this.databaseName, this.context);
|
|
15
|
+
this._useEntityIds = (config == null ? void 0 : config.useEntityIds) ?? false;
|
|
60
16
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Gets a table occurrence by name.
|
|
69
|
-
* @internal
|
|
70
|
-
*/
|
|
71
|
-
getOccurrence(name) {
|
|
72
|
-
return this.occurrenceMap.get(name);
|
|
73
|
-
}
|
|
74
|
-
from(name) {
|
|
75
|
-
const occurrence = this.occurrenceMap.get(name);
|
|
76
|
-
if (occurrence) {
|
|
77
|
-
return EntitySet.create({
|
|
78
|
-
occurrence,
|
|
79
|
-
tableName: name,
|
|
80
|
-
databaseName: this.databaseName,
|
|
81
|
-
context: this.context,
|
|
82
|
-
database: this
|
|
83
|
-
});
|
|
84
|
-
} else {
|
|
85
|
-
return new EntitySet({
|
|
86
|
-
tableName: name,
|
|
87
|
-
databaseName: this.databaseName,
|
|
88
|
-
context: this.context,
|
|
89
|
-
database: this
|
|
90
|
-
});
|
|
17
|
+
from(table) {
|
|
18
|
+
if (Object.prototype.hasOwnProperty.call(table, FMTable.Symbol.UseEntityIds)) {
|
|
19
|
+
const tableUseEntityIds = table[FMTable.Symbol.UseEntityIds];
|
|
20
|
+
if (typeof tableUseEntityIds === "boolean") {
|
|
21
|
+
this._useEntityIds = tableUseEntityIds;
|
|
22
|
+
}
|
|
91
23
|
}
|
|
24
|
+
return new EntitySet({
|
|
25
|
+
occurrence: table,
|
|
26
|
+
databaseName: this.databaseName,
|
|
27
|
+
context: this.context,
|
|
28
|
+
database: this
|
|
29
|
+
});
|
|
92
30
|
}
|
|
93
31
|
async getMetadata(args) {
|
|
94
32
|
const result = await this.context._makeRequest(`/${this.databaseName}/$metadata`, {
|