@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,76 @@
|
|
|
1
|
+
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
2
|
+
export type StringOperators = {
|
|
3
|
+
eq: string | null;
|
|
4
|
+
} | {
|
|
5
|
+
ne: string | null;
|
|
6
|
+
} | {
|
|
7
|
+
gt: string;
|
|
8
|
+
} | {
|
|
9
|
+
ge: string;
|
|
10
|
+
} | {
|
|
11
|
+
lt: string;
|
|
12
|
+
} | {
|
|
13
|
+
le: string;
|
|
14
|
+
} | {
|
|
15
|
+
contains: string;
|
|
16
|
+
} | {
|
|
17
|
+
startswith: string;
|
|
18
|
+
} | {
|
|
19
|
+
endswith: string;
|
|
20
|
+
} | {
|
|
21
|
+
in: string[];
|
|
22
|
+
};
|
|
23
|
+
export type NumberOperators = {
|
|
24
|
+
eq: number | null;
|
|
25
|
+
} | {
|
|
26
|
+
ne: number | null;
|
|
27
|
+
} | {
|
|
28
|
+
gt: number;
|
|
29
|
+
} | {
|
|
30
|
+
ge: number;
|
|
31
|
+
} | {
|
|
32
|
+
lt: number;
|
|
33
|
+
} | {
|
|
34
|
+
le: number;
|
|
35
|
+
} | {
|
|
36
|
+
in: number[];
|
|
37
|
+
};
|
|
38
|
+
export type BooleanOperators = {
|
|
39
|
+
eq: boolean | null;
|
|
40
|
+
} | {
|
|
41
|
+
ne: boolean | null;
|
|
42
|
+
};
|
|
43
|
+
export type DateOperators = {
|
|
44
|
+
eq: Date | null;
|
|
45
|
+
} | {
|
|
46
|
+
ne: Date | null;
|
|
47
|
+
} | {
|
|
48
|
+
gt: Date;
|
|
49
|
+
} | {
|
|
50
|
+
ge: Date;
|
|
51
|
+
} | {
|
|
52
|
+
lt: Date;
|
|
53
|
+
} | {
|
|
54
|
+
le: Date;
|
|
55
|
+
} | {
|
|
56
|
+
in: Date[];
|
|
57
|
+
};
|
|
58
|
+
export type InferOutput<S> = S extends StandardSchemaV1<any, infer Output> ? Output : never;
|
|
59
|
+
export type OperatorsForType<T> = T extends string | null | undefined ? StringOperators : T extends number | null | undefined ? NumberOperators : T extends boolean | null | undefined ? BooleanOperators : T extends Date | null | undefined ? DateOperators : never;
|
|
60
|
+
export type OperatorsForSchemaField<S extends StandardSchemaV1> = OperatorsForType<InferOutput<S>>;
|
|
61
|
+
export type FieldFilter<S extends StandardSchemaV1> = InferOutput<S> | OperatorsForSchemaField<S> | Array<OperatorsForSchemaField<S>>;
|
|
62
|
+
export type LogicalFilter<Schema extends Record<string, StandardSchemaV1>> = {
|
|
63
|
+
and?: Array<TypedFilter<Schema>>;
|
|
64
|
+
or?: Array<TypedFilter<Schema>>;
|
|
65
|
+
not?: TypedFilter<Schema>;
|
|
66
|
+
};
|
|
67
|
+
type IsUntypedSchema<Schema> = [
|
|
68
|
+
Record<string, StandardSchemaV1>
|
|
69
|
+
] extends [Schema] ? [Schema] extends [Record<string, StandardSchemaV1>] ? true : false : false;
|
|
70
|
+
export type TypedFilter<Schema extends Record<string, StandardSchemaV1>> = LogicalFilter<Schema> | (IsUntypedSchema<Schema> extends true ? {
|
|
71
|
+
[key: string]: FieldFilter<any> | any;
|
|
72
|
+
} & {} : {
|
|
73
|
+
[K in keyof Schema]?: FieldFilter<Schema[K]>;
|
|
74
|
+
});
|
|
75
|
+
export type Filter<Schema extends Record<string, StandardSchemaV1>> = TypedFilter<Schema> | Array<TypedFilter<Schema>> | string;
|
|
76
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { BaseTable } from './client/base-table.js';
|
|
2
|
+
export { TableOccurrence, createTableOccurrence, } from './client/table-occurrence.js';
|
|
3
|
+
export { FileMakerOData } from './client/filemaker-odata.js';
|
|
4
|
+
export type { Filter, TypedFilter, FieldFilter, StringOperators, NumberOperators, BooleanOperators, DateOperators, LogicalFilter, } from './filter-types.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BaseTable } from "./client/base-table.js";
|
|
2
|
+
import { TableOccurrence, createTableOccurrence } from "./client/table-occurrence.js";
|
|
3
|
+
import { FileMakerOData } from "./client/filemaker-odata.js";
|
|
4
|
+
export {
|
|
5
|
+
BaseTable,
|
|
6
|
+
FileMakerOData,
|
|
7
|
+
TableOccurrence,
|
|
8
|
+
createTableOccurrence
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { FFetchOptions } from '@fetchkit/ffetch';
|
|
2
|
+
import { z } from 'zod/v4';
|
|
3
|
+
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
4
|
+
export type Auth = {
|
|
5
|
+
username: string;
|
|
6
|
+
password: string;
|
|
7
|
+
} | {
|
|
8
|
+
apiKey: string;
|
|
9
|
+
};
|
|
10
|
+
export interface ExecutableBuilder<T> {
|
|
11
|
+
execute(): Promise<Result<T>>;
|
|
12
|
+
getRequestConfig(): {
|
|
13
|
+
method: string;
|
|
14
|
+
url: string;
|
|
15
|
+
body?: any;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export interface ExecutionContext {
|
|
19
|
+
_makeRequest<T>(url: string, options?: RequestInit & FFetchOptions): Promise<T>;
|
|
20
|
+
}
|
|
21
|
+
export type InferSchemaType<Schema extends Record<string, StandardSchemaV1>> = {
|
|
22
|
+
[K in keyof Schema]: Schema[K] extends StandardSchemaV1<any, infer Output> ? Output : never;
|
|
23
|
+
};
|
|
24
|
+
export type WithSystemFields<T> = T extends Record<string, any> ? T & {
|
|
25
|
+
ROWID: number;
|
|
26
|
+
ROWMODID: number;
|
|
27
|
+
} : never;
|
|
28
|
+
export type ExcludeSystemFields<T extends keyof any> = Exclude<T, "ROWID" | "ROWMODID">;
|
|
29
|
+
export type OmitSystemFields<T> = Omit<T, "ROWID" | "ROWMODID">;
|
|
30
|
+
export type ODataRecordMetadata = {
|
|
31
|
+
"@id": string;
|
|
32
|
+
"@editLink": string;
|
|
33
|
+
};
|
|
34
|
+
export type ODataListResponse<T> = {
|
|
35
|
+
"@context": string;
|
|
36
|
+
value: (T & ODataRecordMetadata)[];
|
|
37
|
+
};
|
|
38
|
+
export type ODataSingleResponse<T> = T & ODataRecordMetadata & {
|
|
39
|
+
"@context": string;
|
|
40
|
+
};
|
|
41
|
+
export type ODataFieldResponse<T> = {
|
|
42
|
+
"@context": string;
|
|
43
|
+
value: T;
|
|
44
|
+
};
|
|
45
|
+
export type Result<T, E = Error> = {
|
|
46
|
+
data: T;
|
|
47
|
+
error: undefined;
|
|
48
|
+
} | {
|
|
49
|
+
data: undefined;
|
|
50
|
+
error: E;
|
|
51
|
+
};
|
|
52
|
+
export type MakeFieldsRequired<T, Keys extends keyof T> = Partial<T> & Required<Pick<T, Keys>>;
|
|
53
|
+
export type InsertData<BT> = BT extends import('./client/base-table.js').BaseTable<infer Schema extends Record<string, z.ZodType>, any, infer InsertRequired extends readonly any[], any> ? [InsertRequired[number]] extends [keyof InferSchemaType<Schema>] ? InsertRequired extends readonly (keyof InferSchemaType<Schema>)[] ? MakeFieldsRequired<InferSchemaType<Schema>, InsertRequired[number]> : Partial<InferSchemaType<Schema>> : Partial<InferSchemaType<Schema>> : Partial<Record<string, any>>;
|
|
54
|
+
export type UpdateData<BT> = BT extends import('./client/base-table.js').BaseTable<infer Schema extends Record<string, z.ZodType>, any, any, infer UpdateRequired extends readonly any[]> ? [UpdateRequired[number]] extends [keyof InferSchemaType<Schema>] ? UpdateRequired extends readonly (keyof InferSchemaType<Schema>)[] ? MakeFieldsRequired<InferSchemaType<Schema>, UpdateRequired[number]> : Partial<InferSchemaType<Schema>> : Partial<InferSchemaType<Schema>> : Partial<Record<string, any>>;
|
|
55
|
+
export type ExecuteOptions = {
|
|
56
|
+
includeODataAnnotations?: boolean;
|
|
57
|
+
skipValidation?: boolean;
|
|
58
|
+
};
|
|
59
|
+
export type ConditionallyWithODataAnnotations<T, IncludeODataAnnotations extends boolean> = IncludeODataAnnotations extends true ? T & {
|
|
60
|
+
"@id": string;
|
|
61
|
+
"@editLink": string;
|
|
62
|
+
} : T;
|
|
63
|
+
export type ExtractSchemaFromOccurrence<Occ> = Occ extends {
|
|
64
|
+
baseTable: {
|
|
65
|
+
schema: infer S;
|
|
66
|
+
};
|
|
67
|
+
} ? S extends Record<string, StandardSchemaV1> ? S : Record<string, StandardSchemaV1> : Record<string, StandardSchemaV1>;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ODataRecordMetadata } from './types.js';
|
|
2
|
+
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
3
|
+
import { TableOccurrence } from './client/table-occurrence.js';
|
|
4
|
+
export type ExpandValidationConfig = {
|
|
5
|
+
relation: string;
|
|
6
|
+
targetSchema?: Record<string, StandardSchemaV1>;
|
|
7
|
+
targetOccurrence?: TableOccurrence<any, any, any, any>;
|
|
8
|
+
selectedFields?: string[];
|
|
9
|
+
nestedExpands?: ExpandValidationConfig[];
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Validates a single record against a schema, only validating selected fields.
|
|
13
|
+
* Also validates expanded relations if expandConfigs are provided.
|
|
14
|
+
*/
|
|
15
|
+
export declare function validateRecord<T extends Record<string, any>>(record: any, schema: Record<string, StandardSchemaV1> | undefined, selectedFields?: (keyof T)[], expandConfigs?: ExpandValidationConfig[]): Promise<{
|
|
16
|
+
valid: true;
|
|
17
|
+
data: T & ODataRecordMetadata;
|
|
18
|
+
} | {
|
|
19
|
+
valid: false;
|
|
20
|
+
error: Error;
|
|
21
|
+
}>;
|
|
22
|
+
/**
|
|
23
|
+
* Validates a list response against a schema.
|
|
24
|
+
*/
|
|
25
|
+
export declare function validateListResponse<T extends Record<string, any>>(response: any, schema: Record<string, StandardSchemaV1> | undefined, selectedFields?: (keyof T)[], expandConfigs?: ExpandValidationConfig[]): Promise<{
|
|
26
|
+
valid: true;
|
|
27
|
+
data: (T & ODataRecordMetadata)[];
|
|
28
|
+
} | {
|
|
29
|
+
valid: false;
|
|
30
|
+
error: Error;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Validates a single record response against a schema.
|
|
34
|
+
*/
|
|
35
|
+
export declare function validateSingleResponse<T extends Record<string, any>>(response: any, schema: Record<string, StandardSchemaV1> | undefined, selectedFields?: (keyof T)[], expandConfigs?: ExpandValidationConfig[], mode?: "exact" | "maybe"): Promise<{
|
|
36
|
+
valid: true;
|
|
37
|
+
data: (T & ODataRecordMetadata) | null;
|
|
38
|
+
} | {
|
|
39
|
+
valid: false;
|
|
40
|
+
error: Error;
|
|
41
|
+
}>;
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
async function validateRecord(record, schema, selectedFields, expandConfigs) {
|
|
2
|
+
var _a, _b;
|
|
3
|
+
const { "@id": id, "@editLink": editLink, ...rest } = record;
|
|
4
|
+
const metadata = {
|
|
5
|
+
"@id": id || "",
|
|
6
|
+
"@editLink": editLink || ""
|
|
7
|
+
};
|
|
8
|
+
if (!schema) {
|
|
9
|
+
return {
|
|
10
|
+
valid: true,
|
|
11
|
+
data: { ...rest, ...metadata }
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
const { ROWID, ROWMODID, ...restWithoutSystemFields } = rest;
|
|
15
|
+
if (selectedFields && selectedFields.length > 0) {
|
|
16
|
+
const validatedRecord2 = {};
|
|
17
|
+
for (const field of selectedFields) {
|
|
18
|
+
const fieldName = String(field);
|
|
19
|
+
const fieldSchema = schema[fieldName];
|
|
20
|
+
if (fieldSchema) {
|
|
21
|
+
const input = rest[fieldName];
|
|
22
|
+
let result = fieldSchema["~standard"].validate(input);
|
|
23
|
+
if (result instanceof Promise) result = await result;
|
|
24
|
+
if (result.issues) {
|
|
25
|
+
return {
|
|
26
|
+
valid: false,
|
|
27
|
+
error: new Error(
|
|
28
|
+
`Validation failed for field '${fieldName}': ${JSON.stringify(result.issues, null, 2)}`
|
|
29
|
+
)
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
validatedRecord2[fieldName] = result.value;
|
|
33
|
+
} else {
|
|
34
|
+
validatedRecord2[fieldName] = rest[fieldName];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (expandConfigs && expandConfigs.length > 0) {
|
|
38
|
+
for (const expandConfig of expandConfigs) {
|
|
39
|
+
const expandValue = rest[expandConfig.relation];
|
|
40
|
+
if (expandValue === void 0) {
|
|
41
|
+
if (Array.isArray(rest.error) && rest.error.length > 0) {
|
|
42
|
+
const errorDetail = (_a = rest.error[0]) == null ? void 0 : _a.error;
|
|
43
|
+
if (errorDetail == null ? void 0 : errorDetail.message) {
|
|
44
|
+
const errorMessage = errorDetail.message;
|
|
45
|
+
const isRelatedToExpand = errorMessage.toLowerCase().includes(expandConfig.relation.toLowerCase()) || expandConfig.selectedFields && expandConfig.selectedFields.some(
|
|
46
|
+
(field) => errorMessage.toLowerCase().includes(field.toLowerCase())
|
|
47
|
+
);
|
|
48
|
+
if (isRelatedToExpand) {
|
|
49
|
+
return {
|
|
50
|
+
valid: false,
|
|
51
|
+
error: new Error(
|
|
52
|
+
`Validation failed for expanded relation '${expandConfig.relation}': ${errorMessage}`
|
|
53
|
+
)
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
if (Array.isArray(expandValue)) {
|
|
60
|
+
const validatedExpandedItems = [];
|
|
61
|
+
for (let i = 0; i < expandValue.length; i++) {
|
|
62
|
+
const item = expandValue[i];
|
|
63
|
+
const itemValidation = await validateRecord(
|
|
64
|
+
item,
|
|
65
|
+
expandConfig.targetSchema,
|
|
66
|
+
expandConfig.selectedFields,
|
|
67
|
+
expandConfig.nestedExpands
|
|
68
|
+
);
|
|
69
|
+
if (!itemValidation.valid) {
|
|
70
|
+
return {
|
|
71
|
+
valid: false,
|
|
72
|
+
error: new Error(
|
|
73
|
+
`Validation failed for expanded relation '${expandConfig.relation}' at index ${i}: ${itemValidation.error.message}`
|
|
74
|
+
)
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
validatedExpandedItems.push(itemValidation.data);
|
|
78
|
+
}
|
|
79
|
+
validatedRecord2[expandConfig.relation] = validatedExpandedItems;
|
|
80
|
+
} else {
|
|
81
|
+
const itemValidation = await validateRecord(
|
|
82
|
+
expandValue,
|
|
83
|
+
expandConfig.targetSchema,
|
|
84
|
+
expandConfig.selectedFields,
|
|
85
|
+
expandConfig.nestedExpands
|
|
86
|
+
);
|
|
87
|
+
if (!itemValidation.valid) {
|
|
88
|
+
return {
|
|
89
|
+
valid: false,
|
|
90
|
+
error: new Error(
|
|
91
|
+
`Validation failed for expanded relation '${expandConfig.relation}': ${itemValidation.error.message}`
|
|
92
|
+
)
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
validatedRecord2[expandConfig.relation] = itemValidation.data;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
valid: true,
|
|
102
|
+
data: { ...validatedRecord2, ...metadata }
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const validatedRecord = { ...restWithoutSystemFields };
|
|
106
|
+
for (const [fieldName, fieldSchema] of Object.entries(schema)) {
|
|
107
|
+
const input = rest[fieldName];
|
|
108
|
+
let result = fieldSchema["~standard"].validate(input);
|
|
109
|
+
if (result instanceof Promise) result = await result;
|
|
110
|
+
if (result.issues) {
|
|
111
|
+
return {
|
|
112
|
+
valid: false,
|
|
113
|
+
error: new Error(
|
|
114
|
+
`Validation failed for field '${fieldName}': ${JSON.stringify(result.issues, null, 2)}`
|
|
115
|
+
)
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
validatedRecord[fieldName] = result.value;
|
|
119
|
+
}
|
|
120
|
+
if (expandConfigs && expandConfigs.length > 0) {
|
|
121
|
+
for (const expandConfig of expandConfigs) {
|
|
122
|
+
const expandValue = rest[expandConfig.relation];
|
|
123
|
+
if (expandValue === void 0) {
|
|
124
|
+
if (Array.isArray(rest.error) && rest.error.length > 0) {
|
|
125
|
+
const errorDetail = (_b = rest.error[0]) == null ? void 0 : _b.error;
|
|
126
|
+
if (errorDetail == null ? void 0 : errorDetail.message) {
|
|
127
|
+
const errorMessage = errorDetail.message;
|
|
128
|
+
const isRelatedToExpand = errorMessage.toLowerCase().includes(expandConfig.relation.toLowerCase()) || expandConfig.selectedFields && expandConfig.selectedFields.some(
|
|
129
|
+
(field) => errorMessage.toLowerCase().includes(field.toLowerCase())
|
|
130
|
+
);
|
|
131
|
+
if (isRelatedToExpand) {
|
|
132
|
+
return {
|
|
133
|
+
valid: false,
|
|
134
|
+
error: new Error(
|
|
135
|
+
`Validation failed for expanded relation '${expandConfig.relation}': ${errorMessage}`
|
|
136
|
+
)
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} else {
|
|
142
|
+
if (Array.isArray(expandValue)) {
|
|
143
|
+
const validatedExpandedItems = [];
|
|
144
|
+
for (let i = 0; i < expandValue.length; i++) {
|
|
145
|
+
const item = expandValue[i];
|
|
146
|
+
const itemValidation = await validateRecord(
|
|
147
|
+
item,
|
|
148
|
+
expandConfig.targetSchema,
|
|
149
|
+
expandConfig.selectedFields,
|
|
150
|
+
expandConfig.nestedExpands
|
|
151
|
+
);
|
|
152
|
+
if (!itemValidation.valid) {
|
|
153
|
+
return {
|
|
154
|
+
valid: false,
|
|
155
|
+
error: new Error(
|
|
156
|
+
`Validation failed for expanded relation '${expandConfig.relation}' at index ${i}: ${itemValidation.error.message}`
|
|
157
|
+
)
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
validatedExpandedItems.push(itemValidation.data);
|
|
161
|
+
}
|
|
162
|
+
validatedRecord[expandConfig.relation] = validatedExpandedItems;
|
|
163
|
+
} else {
|
|
164
|
+
const itemValidation = await validateRecord(
|
|
165
|
+
expandValue,
|
|
166
|
+
expandConfig.targetSchema,
|
|
167
|
+
expandConfig.selectedFields,
|
|
168
|
+
expandConfig.nestedExpands
|
|
169
|
+
);
|
|
170
|
+
if (!itemValidation.valid) {
|
|
171
|
+
return {
|
|
172
|
+
valid: false,
|
|
173
|
+
error: new Error(
|
|
174
|
+
`Validation failed for expanded relation '${expandConfig.relation}': ${itemValidation.error.message}`
|
|
175
|
+
)
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
validatedRecord[expandConfig.relation] = itemValidation.data;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
valid: true,
|
|
185
|
+
data: { ...validatedRecord, ...metadata }
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
async function validateListResponse(response, schema, selectedFields, expandConfigs) {
|
|
189
|
+
if (!response || typeof response !== "object") {
|
|
190
|
+
return {
|
|
191
|
+
valid: false,
|
|
192
|
+
error: new Error("Invalid response: expected an object")
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
const { "@context": context, value, ...rest } = response;
|
|
196
|
+
if (!Array.isArray(value)) {
|
|
197
|
+
return {
|
|
198
|
+
valid: false,
|
|
199
|
+
error: new Error(
|
|
200
|
+
"Invalid response: expected 'value' property to be an array"
|
|
201
|
+
)
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const validatedRecords = [];
|
|
205
|
+
for (let i = 0; i < value.length; i++) {
|
|
206
|
+
const record = value[i];
|
|
207
|
+
const validation = await validateRecord(
|
|
208
|
+
record,
|
|
209
|
+
schema,
|
|
210
|
+
selectedFields,
|
|
211
|
+
expandConfigs
|
|
212
|
+
);
|
|
213
|
+
if (!validation.valid) {
|
|
214
|
+
return {
|
|
215
|
+
valid: false,
|
|
216
|
+
error: new Error(
|
|
217
|
+
`Validation failed for record at index ${i}: ${validation.error.message}`
|
|
218
|
+
)
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
validatedRecords.push(validation.data);
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
valid: true,
|
|
225
|
+
data: validatedRecords
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
async function validateSingleResponse(response, schema, selectedFields, expandConfigs, mode = "maybe") {
|
|
229
|
+
var _a;
|
|
230
|
+
if (response.value && Array.isArray(response.value) && response.value.length > 1) {
|
|
231
|
+
return {
|
|
232
|
+
valid: false,
|
|
233
|
+
error: new Error(
|
|
234
|
+
`Expected ${mode === "exact" ? "exactly one" : "at most one"} record, but received ${response.value.length}`
|
|
235
|
+
)
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
if (!response || response.value && response.value.length === 0) {
|
|
239
|
+
if (mode === "exact") {
|
|
240
|
+
return {
|
|
241
|
+
valid: false,
|
|
242
|
+
error: new Error("Expected exactly one record, but received none")
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
return {
|
|
246
|
+
valid: true,
|
|
247
|
+
data: null
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
const record = ((_a = response.value) == null ? void 0 : _a[0]) ?? response;
|
|
251
|
+
const validation = await validateRecord(
|
|
252
|
+
record,
|
|
253
|
+
schema,
|
|
254
|
+
selectedFields,
|
|
255
|
+
expandConfigs
|
|
256
|
+
);
|
|
257
|
+
if (!validation.valid) {
|
|
258
|
+
return validation;
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
valid: true,
|
|
262
|
+
data: validation.data
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
export {
|
|
266
|
+
validateListResponse,
|
|
267
|
+
validateRecord,
|
|
268
|
+
validateSingleResponse
|
|
269
|
+
};
|
|
270
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sources":["../../src/validation.ts"],"sourcesContent":["import type { ODataRecordMetadata } from \"./types\";\nimport { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { TableOccurrence } from \"./client/table-occurrence\";\n\n// Type for expand validation configuration\nexport type ExpandValidationConfig = {\n relation: string;\n targetSchema?: Record<string, StandardSchemaV1>;\n targetOccurrence?: TableOccurrence<any, any, any, any>;\n selectedFields?: string[];\n nestedExpands?: ExpandValidationConfig[];\n};\n\n/**\n * Validates a single record against a schema, only validating selected fields.\n * Also validates expanded relations if expandConfigs are provided.\n */\nexport async function validateRecord<T extends Record<string, any>>(\n record: any,\n schema: Record<string, StandardSchemaV1> | undefined,\n selectedFields?: (keyof T)[],\n expandConfigs?: ExpandValidationConfig[],\n): Promise<\n | { valid: true; data: T & ODataRecordMetadata }\n | { valid: false; error: Error }\n> {\n // Extract OData metadata fields (don't validate them - include if present)\n const { \"@id\": id, \"@editLink\": editLink, ...rest } = record;\n\n // Include metadata fields if present (don't validate they exist)\n const metadata: ODataRecordMetadata = {\n \"@id\": id || \"\",\n \"@editLink\": editLink || \"\",\n };\n\n // If no schema, just return the data with metadata\n if (!schema) {\n return {\n valid: true,\n data: { ...rest, ...metadata } as T & ODataRecordMetadata,\n };\n }\n\n // Filter out FileMaker system fields that shouldn't be in responses by default\n const { ROWID, ROWMODID, ...restWithoutSystemFields } = rest;\n\n // If selected fields are specified, validate only those fields\n if (selectedFields && selectedFields.length > 0) {\n const validatedRecord: Record<string, any> = {};\n\n for (const field of selectedFields) {\n const fieldName = String(field);\n const fieldSchema = schema[fieldName];\n\n if (fieldSchema) {\n const input = rest[fieldName];\n let result = fieldSchema[\"~standard\"].validate(input);\n if (result instanceof Promise) result = await result;\n\n // if the `issues` field exists, the validation failed\n if (result.issues) {\n return {\n valid: false,\n error: new Error(\n `Validation failed for field '${fieldName}': ${JSON.stringify(result.issues, null, 2)}`,\n ),\n };\n }\n\n validatedRecord[fieldName] = result.value;\n } else {\n // For fields not in schema (like when explicitly selecting ROWID/ROWMODID)\n // include them from the original response\n validatedRecord[fieldName] = rest[fieldName];\n }\n }\n\n // Validate expanded relations\n if (expandConfigs && expandConfigs.length > 0) {\n for (const expandConfig of expandConfigs) {\n const expandValue = rest[expandConfig.relation];\n\n // Check if expand field is missing\n if (expandValue === undefined) {\n // Check for inline error array (FileMaker returns errors inline when expand fails)\n if (Array.isArray(rest.error) && rest.error.length > 0) {\n // Extract error message from inline error\n const errorDetail = rest.error[0]?.error;\n if (errorDetail?.message) {\n const errorMessage = errorDetail.message;\n // Check if the error is related to this expand by checking if:\n // 1. The error mentions the relation name, OR\n // 2. The error mentions any of the selected fields\n const isRelatedToExpand =\n errorMessage\n .toLowerCase()\n .includes(expandConfig.relation.toLowerCase()) ||\n (expandConfig.selectedFields &&\n expandConfig.selectedFields.some((field) =>\n errorMessage.toLowerCase().includes(field.toLowerCase()),\n ));\n\n if (isRelatedToExpand) {\n return {\n valid: false,\n error: new Error(\n `Validation failed for expanded relation '${expandConfig.relation}': ${errorMessage}`,\n ),\n };\n }\n }\n }\n // If no inline error but expand was expected, that's also an issue\n // However, this might be a legitimate case (e.g., no related records)\n // So we'll only fail if there's an explicit error array\n } else {\n // Original validation logic for when expand exists\n if (Array.isArray(expandValue)) {\n // Validate each item in the expanded array\n const validatedExpandedItems: any[] = [];\n for (let i = 0; i < expandValue.length; i++) {\n const item = expandValue[i];\n const itemValidation = await validateRecord(\n item,\n expandConfig.targetSchema,\n expandConfig.selectedFields as string[] | undefined,\n expandConfig.nestedExpands,\n );\n if (!itemValidation.valid) {\n return {\n valid: false,\n error: new Error(\n `Validation failed for expanded relation '${expandConfig.relation}' at index ${i}: ${itemValidation.error.message}`,\n ),\n };\n }\n validatedExpandedItems.push(itemValidation.data);\n }\n validatedRecord[expandConfig.relation] = validatedExpandedItems;\n } else {\n // Single expanded item (shouldn't happen in OData, but handle it)\n const itemValidation = await validateRecord(\n expandValue,\n expandConfig.targetSchema,\n expandConfig.selectedFields as string[] | undefined,\n expandConfig.nestedExpands,\n );\n if (!itemValidation.valid) {\n return {\n valid: false,\n error: new Error(\n `Validation failed for expanded relation '${expandConfig.relation}': ${itemValidation.error.message}`,\n ),\n };\n }\n validatedRecord[expandConfig.relation] = itemValidation.data;\n }\n }\n }\n }\n\n // Merge validated data with metadata\n return {\n valid: true,\n data: { ...validatedRecord, ...metadata } as T & ODataRecordMetadata,\n };\n }\n\n // Validate all fields in schema, but exclude ROWID/ROWMODID by default\n const validatedRecord: Record<string, any> = { ...restWithoutSystemFields };\n\n for (const [fieldName, fieldSchema] of Object.entries(schema)) {\n const input = rest[fieldName];\n let result = fieldSchema[\"~standard\"].validate(input);\n if (result instanceof Promise) result = await result;\n\n // if the `issues` field exists, the validation failed\n if (result.issues) {\n return {\n valid: false,\n error: new Error(\n `Validation failed for field '${fieldName}': ${JSON.stringify(result.issues, null, 2)}`,\n ),\n };\n }\n\n validatedRecord[fieldName] = result.value;\n }\n\n // Validate expanded relations even when not using selected fields\n if (expandConfigs && expandConfigs.length > 0) {\n for (const expandConfig of expandConfigs) {\n const expandValue = rest[expandConfig.relation];\n\n // Check if expand field is missing\n if (expandValue === undefined) {\n // Check for inline error array (FileMaker returns errors inline when expand fails)\n if (Array.isArray(rest.error) && rest.error.length > 0) {\n // Extract error message from inline error\n const errorDetail = rest.error[0]?.error;\n if (errorDetail?.message) {\n const errorMessage = errorDetail.message;\n // Check if the error is related to this expand by checking if:\n // 1. The error mentions the relation name, OR\n // 2. The error mentions any of the selected fields\n const isRelatedToExpand =\n errorMessage\n .toLowerCase()\n .includes(expandConfig.relation.toLowerCase()) ||\n (expandConfig.selectedFields &&\n expandConfig.selectedFields.some((field) =>\n errorMessage.toLowerCase().includes(field.toLowerCase()),\n ));\n\n if (isRelatedToExpand) {\n return {\n valid: false,\n error: new Error(\n `Validation failed for expanded relation '${expandConfig.relation}': ${errorMessage}`,\n ),\n };\n }\n }\n }\n // If no inline error but expand was expected, that's also an issue\n // However, this might be a legitimate case (e.g., no related records)\n // So we'll only fail if there's an explicit error array\n } else {\n // Original validation logic for when expand exists\n if (Array.isArray(expandValue)) {\n // Validate each item in the expanded array\n const validatedExpandedItems: any[] = [];\n for (let i = 0; i < expandValue.length; i++) {\n const item = expandValue[i];\n const itemValidation = await validateRecord(\n item,\n expandConfig.targetSchema,\n expandConfig.selectedFields as string[] | undefined,\n expandConfig.nestedExpands,\n );\n if (!itemValidation.valid) {\n return {\n valid: false,\n error: new Error(\n `Validation failed for expanded relation '${expandConfig.relation}' at index ${i}: ${itemValidation.error.message}`,\n ),\n };\n }\n validatedExpandedItems.push(itemValidation.data);\n }\n validatedRecord[expandConfig.relation] = validatedExpandedItems;\n } else {\n // Single expanded item (shouldn't happen in OData, but handle it)\n const itemValidation = await validateRecord(\n expandValue,\n expandConfig.targetSchema,\n expandConfig.selectedFields as string[] | undefined,\n expandConfig.nestedExpands,\n );\n if (!itemValidation.valid) {\n return {\n valid: false,\n error: new Error(\n `Validation failed for expanded relation '${expandConfig.relation}': ${itemValidation.error.message}`,\n ),\n };\n }\n validatedRecord[expandConfig.relation] = itemValidation.data;\n }\n }\n }\n }\n\n return {\n valid: true,\n data: { ...validatedRecord, ...metadata } as T & ODataRecordMetadata,\n };\n}\n\n/**\n * Validates a list response against a schema.\n */\nexport async function validateListResponse<T extends Record<string, any>>(\n response: any,\n schema: Record<string, StandardSchemaV1> | undefined,\n selectedFields?: (keyof T)[],\n expandConfigs?: ExpandValidationConfig[],\n): Promise<\n | { valid: true; data: (T & ODataRecordMetadata)[] }\n | { valid: false; error: Error }\n> {\n // Check if response has the expected structure\n if (!response || typeof response !== \"object\") {\n return {\n valid: false,\n error: new Error(\"Invalid response: expected an object\"),\n };\n }\n\n // Extract @context (for internal validation, but we won't return it)\n const { \"@context\": context, value, ...rest } = response;\n\n if (!Array.isArray(value)) {\n return {\n valid: false,\n error: new Error(\n \"Invalid response: expected 'value' property to be an array\",\n ),\n };\n }\n\n // Validate each record in the array\n const validatedRecords: (T & ODataRecordMetadata)[] = [];\n\n for (let i = 0; i < value.length; i++) {\n const record = value[i];\n const validation = await validateRecord<T>(\n record,\n schema,\n selectedFields,\n expandConfigs,\n );\n\n if (!validation.valid) {\n return {\n valid: false,\n error: new Error(\n `Validation failed for record at index ${i}: ${validation.error.message}`,\n ),\n };\n }\n\n validatedRecords.push(validation.data);\n }\n\n return {\n valid: true,\n data: validatedRecords,\n };\n}\n\n/**\n * Validates a single record response against a schema.\n */\nexport async function validateSingleResponse<T extends Record<string, any>>(\n response: any,\n schema: Record<string, StandardSchemaV1> | undefined,\n selectedFields?: (keyof T)[],\n expandConfigs?: ExpandValidationConfig[],\n mode: \"exact\" | \"maybe\" = \"maybe\",\n): Promise<\n | { valid: true; data: (T & ODataRecordMetadata) | null }\n | { valid: false; error: Error }\n> {\n // Check for multiple records (error in both modes)\n if (response.value && Array.isArray(response.value) && response.value.length > 1) {\n return {\n valid: false,\n error: new Error(\n `Expected ${mode === \"exact\" ? \"exactly one\" : \"at most one\"} record, but received ${response.value.length}`\n ),\n };\n }\n\n // Handle empty responses\n if (!response || (response.value && response.value.length === 0)) {\n if (mode === \"exact\") {\n return {\n valid: false,\n error: new Error(\"Expected exactly one record, but received none\"),\n };\n }\n // mode === \"maybe\" - return null for empty\n return {\n valid: true,\n data: null,\n };\n }\n\n // Single record validation\n const record = response.value?.[0] ?? response;\n const validation = await validateRecord<T>(\n record,\n schema,\n selectedFields,\n expandConfigs,\n );\n\n if (!validation.valid) {\n return validation as { valid: false; error: Error };\n }\n\n return {\n valid: true,\n data: validation.data,\n };\n}\n"],"names":["validatedRecord"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@proofkit/fmodata",
|
|
3
|
+
"version": "0.1.0-alpha.0",
|
|
4
|
+
"description": "FileMaker OData API client",
|
|
5
|
+
"repository": "git@github.com:proofgeist/proofkit.git",
|
|
6
|
+
"author": "Eric <37158449+eluce2@users.noreply.github.com>",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"private": false,
|
|
9
|
+
"type": "module",
|
|
10
|
+
"main": "./dist/esm/index.js",
|
|
11
|
+
"types": "./dist/esm/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": {
|
|
15
|
+
"types": "./dist/esm/index.d.ts",
|
|
16
|
+
"default": "./dist/esm/index.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"./package.json": "./package.json"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc && vite build && publint --strict",
|
|
23
|
+
"build:watch": "tsc && vite build --watch",
|
|
24
|
+
"check-format": "prettier --check .",
|
|
25
|
+
"format": "prettier --write .",
|
|
26
|
+
"dev": "tsc --watch",
|
|
27
|
+
"test": "vitest run --typecheck",
|
|
28
|
+
"test:typecheck": "vitest run --typecheck",
|
|
29
|
+
"test:watch": "vitest --typecheck",
|
|
30
|
+
"test:e2e": "op inject -i op.env -o .env.local -f && vitest run tests/e2e.test.ts",
|
|
31
|
+
"capture": "op inject -i op.env -o .env.local -f && tsx scripts/capture-responses.ts",
|
|
32
|
+
"knip": "knip",
|
|
33
|
+
"pub:alpha": "bun run scripts/publish-alpha.ts"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@fetchkit/ffetch": "^4.1.0",
|
|
37
|
+
"dotenv": "^16.5.0",
|
|
38
|
+
"neverthrow": "^8.2.0",
|
|
39
|
+
"odata-query": "^8.0.4"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@standard-schema/spec": "^1.0.0",
|
|
43
|
+
"@tanstack/vite-config": "^0.2.0",
|
|
44
|
+
"@types/node": "^22.17.1",
|
|
45
|
+
"prettier": "^3.5.3",
|
|
46
|
+
"publint": "^0.3.12",
|
|
47
|
+
"tsx": "^4.19.2",
|
|
48
|
+
"typescript": "^5.9.3",
|
|
49
|
+
"vite": "^6.3.4",
|
|
50
|
+
"vitest": "^4.0.7",
|
|
51
|
+
"zod": "4.1.12"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18.0.0"
|
|
55
|
+
},
|
|
56
|
+
"files": [
|
|
57
|
+
"src",
|
|
58
|
+
"dist"
|
|
59
|
+
],
|
|
60
|
+
"keywords": [
|
|
61
|
+
"filemaker",
|
|
62
|
+
"fms",
|
|
63
|
+
"fm",
|
|
64
|
+
"odata",
|
|
65
|
+
"proofgeist",
|
|
66
|
+
"proofkit"
|
|
67
|
+
]
|
|
68
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
|
|
3
|
+
export class BaseTable<
|
|
4
|
+
Schema extends Record<string, StandardSchemaV1> = any,
|
|
5
|
+
IdField extends keyof Schema | undefined = undefined,
|
|
6
|
+
InsertRequired extends readonly (keyof Schema)[] = readonly [],
|
|
7
|
+
UpdateRequired extends readonly (keyof Schema)[] = readonly [],
|
|
8
|
+
> {
|
|
9
|
+
public readonly schema: Schema;
|
|
10
|
+
public readonly idField?: IdField;
|
|
11
|
+
public readonly insertRequired?: InsertRequired;
|
|
12
|
+
public readonly updateRequired?: UpdateRequired;
|
|
13
|
+
|
|
14
|
+
constructor(config: {
|
|
15
|
+
schema: Schema;
|
|
16
|
+
idField?: IdField;
|
|
17
|
+
insertRequired?: InsertRequired;
|
|
18
|
+
updateRequired?: UpdateRequired;
|
|
19
|
+
}) {
|
|
20
|
+
this.schema = config.schema;
|
|
21
|
+
this.idField = config.idField;
|
|
22
|
+
this.insertRequired = config.insertRequired;
|
|
23
|
+
this.updateRequired = config.updateRequired;
|
|
24
|
+
}
|
|
25
|
+
}
|