@proofkit/fmdapi 5.0.3-beta.0 → 5.1.0-beta.2
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/bin/intent.js +20 -0
- package/dist/esm/adapters/core.d.ts +4 -4
- package/dist/esm/adapters/fetch-base-types.d.ts +4 -4
- package/dist/esm/adapters/fetch-base.d.ts +2 -2
- package/dist/esm/adapters/fetch-base.js +36 -49
- package/dist/esm/adapters/fetch-base.js.map +1 -1
- package/dist/esm/adapters/fetch.d.ts +5 -5
- package/dist/esm/adapters/fetch.js +11 -10
- package/dist/esm/adapters/fetch.js.map +1 -1
- package/dist/esm/adapters/fm-http.d.ts +32 -0
- package/dist/esm/adapters/fm-http.js +170 -0
- package/dist/esm/adapters/fm-http.js.map +1 -0
- package/dist/esm/adapters/otto.d.ts +2 -2
- package/dist/esm/adapters/otto.js +3 -5
- package/dist/esm/adapters/otto.js.map +1 -1
- package/dist/esm/client-types.d.ts +41 -41
- package/dist/esm/client-types.js +1 -6
- package/dist/esm/client-types.js.map +1 -1
- package/dist/esm/client.d.ts +28 -44
- package/dist/esm/client.js +75 -80
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/index.d.ts +5 -6
- package/dist/esm/index.js +7 -5
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/tokenStore/index.d.ts +1 -1
- package/dist/esm/tokenStore/memory.js.map +1 -1
- package/dist/esm/tokenStore/types.d.ts +2 -2
- package/dist/esm/tokenStore/upstash.d.ts +1 -1
- package/dist/esm/utils.d.ts +7 -7
- package/dist/esm/utils.js +6 -4
- package/dist/esm/utils.js.map +1 -1
- package/package.json +37 -26
- package/skills/fmdapi-client/SKILL.md +490 -0
- package/src/adapters/core.ts +6 -9
- package/src/adapters/fetch-base-types.ts +5 -3
- package/src/adapters/fetch-base.ts +53 -78
- package/src/adapters/fetch.ts +19 -24
- package/src/adapters/fm-http.ts +224 -0
- package/src/adapters/otto.ts +8 -8
- package/src/client-types.ts +59 -83
- package/src/client.ts +131 -167
- package/src/index.ts +5 -9
- package/src/tokenStore/file.ts +2 -4
- package/src/tokenStore/index.ts +1 -1
- package/src/tokenStore/types.ts +2 -2
- package/src/tokenStore/upstash.ts +2 -5
- package/src/utils.ts +16 -23
package/src/client.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
1
2
|
import type { Adapter, ExecuteScriptOptions } from "./adapters/core.js";
|
|
2
3
|
import type {
|
|
3
4
|
CreateParams,
|
|
4
5
|
CreateResponse,
|
|
5
6
|
DeleteParams,
|
|
6
7
|
DeleteResponse,
|
|
7
|
-
FMRecord,
|
|
8
8
|
FieldData,
|
|
9
|
+
FMRecord,
|
|
9
10
|
GenericPortalData,
|
|
10
11
|
GetParams,
|
|
11
12
|
GetResponse,
|
|
@@ -17,13 +18,12 @@ import type {
|
|
|
17
18
|
UpdateResponse,
|
|
18
19
|
} from "./client-types.js";
|
|
19
20
|
import { FileMakerError } from "./index.js";
|
|
20
|
-
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
21
21
|
|
|
22
22
|
function asNumber(input: string | number): number {
|
|
23
|
-
return typeof input === "string" ? parseInt(input) : input;
|
|
23
|
+
return typeof input === "string" ? Number.parseInt(input, 10) : input;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export
|
|
26
|
+
export interface ClientObjectProps {
|
|
27
27
|
/**
|
|
28
28
|
* The layout to use by default for all requests. Can be overrridden on each request.
|
|
29
29
|
*/
|
|
@@ -38,11 +38,28 @@ export type ClientObjectProps = {
|
|
|
38
38
|
*/
|
|
39
39
|
portalData?: Record<string, StandardSchemaV1<FieldData>>;
|
|
40
40
|
};
|
|
41
|
-
}
|
|
41
|
+
}
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
interface FetchOptions {
|
|
44
44
|
fetch?: RequestInit;
|
|
45
|
-
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface IgnoreEmptyResult {
|
|
48
|
+
/**
|
|
49
|
+
* If true, a find that returns no results will retun an empty array instead of throwing an error.
|
|
50
|
+
* @default false
|
|
51
|
+
*/
|
|
52
|
+
ignoreEmptyResult?: boolean;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface ContainerUploadArgs<T extends FieldData = FieldData> {
|
|
56
|
+
containerFieldName: keyof T;
|
|
57
|
+
containerFieldRepetition?: string | number;
|
|
58
|
+
file: Blob;
|
|
59
|
+
recordId: number | string;
|
|
60
|
+
modId?: number;
|
|
61
|
+
timeout?: number;
|
|
62
|
+
}
|
|
46
63
|
|
|
47
64
|
function DataApi<
|
|
48
65
|
Fd extends FieldData = FieldData,
|
|
@@ -56,17 +73,13 @@ function DataApi<
|
|
|
56
73
|
type InferredPortalData = Opts["schema"] extends object
|
|
57
74
|
? Opts["schema"]["portalData"] extends object
|
|
58
75
|
? {
|
|
59
|
-
[K in keyof Opts["schema"]["portalData"]]: StandardSchemaV1.InferOutput<
|
|
60
|
-
Opts["schema"]["portalData"][K]
|
|
61
|
-
>;
|
|
76
|
+
[K in keyof Opts["schema"]["portalData"]]: StandardSchemaV1.InferOutput<Opts["schema"]["portalData"][K]>;
|
|
62
77
|
}
|
|
63
78
|
: Pd
|
|
64
79
|
: Pd;
|
|
65
80
|
|
|
66
81
|
if ("zodValidators" in options) {
|
|
67
|
-
throw new Error(
|
|
68
|
-
"zodValidators is no longer supported. Use schema instead, or re-run the typegen command",
|
|
69
|
-
);
|
|
82
|
+
throw new Error("zodValidators is no longer supported. Use schema instead, or re-run the typegen command");
|
|
70
83
|
}
|
|
71
84
|
|
|
72
85
|
const schema = options.schema;
|
|
@@ -90,10 +103,9 @@ function DataApi<
|
|
|
90
103
|
> = CreateParams<U> & {
|
|
91
104
|
fieldData: Partial<T>;
|
|
92
105
|
};
|
|
93
|
-
type GetArgs<U extends InferredPortalData = InferredPortalData> =
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
};
|
|
106
|
+
type GetArgs<U extends InferredPortalData = InferredPortalData> = GetParams<U> & {
|
|
107
|
+
recordId: number | string;
|
|
108
|
+
};
|
|
97
109
|
type UpdateArgs<
|
|
98
110
|
T extends InferredFieldData = InferredFieldData,
|
|
99
111
|
U extends InferredPortalData = InferredPortalData,
|
|
@@ -101,29 +113,14 @@ function DataApi<
|
|
|
101
113
|
fieldData: Partial<T>;
|
|
102
114
|
recordId: number | string;
|
|
103
115
|
};
|
|
104
|
-
type ContainerUploadArgs<T extends InferredFieldData = InferredFieldData> = {
|
|
105
|
-
containerFieldName: keyof T;
|
|
106
|
-
containerFieldRepetition?: string | number;
|
|
107
|
-
file: Blob;
|
|
108
|
-
recordId: number | string;
|
|
109
|
-
modId?: number;
|
|
110
|
-
timeout?: number;
|
|
111
|
-
};
|
|
112
116
|
type DeleteArgs = DeleteParams & {
|
|
113
117
|
recordId: number | string;
|
|
114
118
|
};
|
|
115
|
-
type IgnoreEmptyResult = {
|
|
116
|
-
/**
|
|
117
|
-
* If true, a find that returns no results will retun an empty array instead of throwing an error.
|
|
118
|
-
* @default false
|
|
119
|
-
*/
|
|
120
|
-
ignoreEmptyResult?: boolean;
|
|
121
|
-
};
|
|
122
119
|
type FindArgs<
|
|
123
120
|
T extends FieldData = InferredFieldData,
|
|
124
121
|
U extends InferredPortalData = InferredPortalData,
|
|
125
122
|
> = ListParams<T, U> & {
|
|
126
|
-
query: Query<T> |
|
|
123
|
+
query: Query<T> | Query<T>[];
|
|
127
124
|
timeout?: number;
|
|
128
125
|
};
|
|
129
126
|
|
|
@@ -132,11 +129,8 @@ function DataApi<
|
|
|
132
129
|
/**
|
|
133
130
|
* List all records from a given layout, no find criteria applied.
|
|
134
131
|
*/
|
|
135
|
-
async function _list(): Promise<
|
|
136
|
-
GetResponse<InferredFieldData, InferredPortalData>
|
|
137
|
-
>;
|
|
138
132
|
async function _list(
|
|
139
|
-
args
|
|
133
|
+
args?: ListParams<InferredFieldData, InferredPortalData> & FetchOptions,
|
|
140
134
|
): Promise<GetResponse<InferredFieldData, InferredPortalData>>;
|
|
141
135
|
async function _list(
|
|
142
136
|
args?: ListParams<InferredFieldData, InferredPortalData> & FetchOptions,
|
|
@@ -144,16 +138,21 @@ function DataApi<
|
|
|
144
138
|
const { fetch, timeout, ...params } = args ?? {};
|
|
145
139
|
|
|
146
140
|
// rename and refactor limit, offset, and sort keys for this request
|
|
147
|
-
if ("limit" in params && params.limit !== undefined)
|
|
148
|
-
|
|
141
|
+
if ("limit" in params && params.limit !== undefined) {
|
|
142
|
+
Object.assign(params, { _limit: params.limit }).limit = undefined;
|
|
143
|
+
}
|
|
149
144
|
if ("offset" in params && params.offset !== undefined) {
|
|
150
|
-
if (params.offset <= 1)
|
|
151
|
-
|
|
145
|
+
if (params.offset <= 1) {
|
|
146
|
+
params.offset = undefined;
|
|
147
|
+
} else {
|
|
148
|
+
Object.assign(params, { _offset: params.offset }).offset = undefined;
|
|
149
|
+
}
|
|
152
150
|
}
|
|
153
|
-
if ("sort" in params && params.sort !== undefined)
|
|
154
|
-
|
|
151
|
+
if ("sort" in params && params.sort !== undefined) {
|
|
152
|
+
Object.assign(params, {
|
|
155
153
|
_sort: Array.isArray(params.sort) ? params.sort : [params.sort],
|
|
156
|
-
})
|
|
154
|
+
}).sort = undefined;
|
|
155
|
+
}
|
|
157
156
|
|
|
158
157
|
const result = await list({
|
|
159
158
|
layout,
|
|
@@ -162,52 +161,44 @@ function DataApi<
|
|
|
162
161
|
timeout,
|
|
163
162
|
});
|
|
164
163
|
|
|
165
|
-
if (
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
164
|
+
if (
|
|
165
|
+
result.dataInfo.foundCount > result.dataInfo.returnedCount &&
|
|
166
|
+
args?.limit === undefined &&
|
|
167
|
+
args?.offset === undefined
|
|
168
|
+
) {
|
|
169
|
+
// more records found than returned and the user didn't specify a limit or offset, so we should warn them
|
|
170
|
+
console.warn(
|
|
171
|
+
`🚨 @proofkit/fmdapi: Loaded only ${result.dataInfo.returnedCount} of the ${result.dataInfo.foundCount} records from your "${layout}" layout. Use the "listAll" method to automatically paginate through all records, or specify a "limit" and "offset" to handle pagination yourself.`,
|
|
172
|
+
);
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
return await runSchemaValidationAndTransform(
|
|
176
|
-
schema,
|
|
177
|
-
result as GetResponse<InferredFieldData, InferredPortalData>,
|
|
178
|
-
);
|
|
175
|
+
return await runSchemaValidationAndTransform(schema, result as GetResponse<InferredFieldData, InferredPortalData>);
|
|
179
176
|
}
|
|
180
177
|
|
|
181
178
|
/**
|
|
182
179
|
* Paginate through all records from a given layout, no find criteria applied.
|
|
183
180
|
* ⚠️ WARNING: Use this method with caution, as it can be slow with large datasets
|
|
184
181
|
*/
|
|
185
|
-
async function listAll
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
>(args: ListParams<T, U> & FetchOptions): Promise<FMRecord<T, U>[]>;
|
|
193
|
-
async function listAll<
|
|
194
|
-
T extends FieldData = InferredFieldData,
|
|
195
|
-
U extends InferredPortalData = InferredPortalData,
|
|
196
|
-
>(args?: ListParams<T, U> & FetchOptions): Promise<FMRecord<T, U>[]> {
|
|
197
|
-
let runningData: GetResponse<T, U>["data"] = [];
|
|
182
|
+
async function listAll(
|
|
183
|
+
args?: ListParams<InferredFieldData, InferredPortalData> & FetchOptions,
|
|
184
|
+
): Promise<FMRecord<InferredFieldData, InferredPortalData>[]>;
|
|
185
|
+
async function listAll(
|
|
186
|
+
args?: ListParams<InferredFieldData, InferredPortalData> & FetchOptions,
|
|
187
|
+
): Promise<FMRecord<InferredFieldData, InferredPortalData>[]> {
|
|
188
|
+
let runningData: GetResponse<InferredFieldData, InferredPortalData>["data"] = [];
|
|
198
189
|
const limit = args?.limit ?? 100;
|
|
199
190
|
let offset = args?.offset ?? 1;
|
|
200
191
|
|
|
201
|
-
// eslint-disable-next-line no-constant-condition
|
|
202
192
|
while (true) {
|
|
203
|
-
const data =
|
|
193
|
+
const data = await _list({
|
|
204
194
|
...args,
|
|
205
195
|
offset,
|
|
206
|
-
|
|
207
|
-
} as any)) as unknown as GetResponse<T, U>;
|
|
196
|
+
});
|
|
208
197
|
runningData = [...runningData, ...data.data];
|
|
209
|
-
if (runningData.length >= data.dataInfo.foundCount)
|
|
210
|
-
|
|
198
|
+
if (runningData.length >= data.dataInfo.foundCount) {
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
offset += limit;
|
|
211
202
|
}
|
|
212
203
|
return runningData;
|
|
213
204
|
}
|
|
@@ -243,10 +234,7 @@ function DataApi<
|
|
|
243
234
|
fetch,
|
|
244
235
|
timeout,
|
|
245
236
|
});
|
|
246
|
-
return await runSchemaValidationAndTransform(
|
|
247
|
-
schema,
|
|
248
|
-
result as GetResponse<InferredFieldData, InferredPortalData>,
|
|
249
|
-
);
|
|
237
|
+
return await runSchemaValidationAndTransform(schema, result as GetResponse<InferredFieldData, InferredPortalData>);
|
|
250
238
|
}
|
|
251
239
|
|
|
252
240
|
/**
|
|
@@ -268,9 +256,7 @@ function DataApi<
|
|
|
268
256
|
/**
|
|
269
257
|
* Delete a single record by internal RecordId
|
|
270
258
|
*/
|
|
271
|
-
|
|
272
|
-
args: DeleteArgs & FetchOptions,
|
|
273
|
-
): Promise<DeleteResponse> {
|
|
259
|
+
function deleteRecord(args: DeleteArgs & FetchOptions): Promise<DeleteResponse> {
|
|
274
260
|
args.recordId = asNumber(args.recordId);
|
|
275
261
|
const { recordId, fetch, timeout, ...params } = args;
|
|
276
262
|
|
|
@@ -286,35 +272,29 @@ function DataApi<
|
|
|
286
272
|
* Find records in a given layout
|
|
287
273
|
*/
|
|
288
274
|
async function _find(
|
|
289
|
-
args: FindArgs<InferredFieldData, InferredPortalData> &
|
|
290
|
-
IgnoreEmptyResult &
|
|
291
|
-
FetchOptions,
|
|
275
|
+
args: FindArgs<InferredFieldData, InferredPortalData> & IgnoreEmptyResult & FetchOptions,
|
|
292
276
|
): Promise<GetResponse<InferredFieldData, InferredPortalData>> {
|
|
293
|
-
const {
|
|
294
|
-
|
|
295
|
-
ignoreEmptyResult = false,
|
|
296
|
-
timeout,
|
|
297
|
-
fetch,
|
|
298
|
-
...params
|
|
299
|
-
} = args;
|
|
300
|
-
const query = !Array.isArray(queryInput) ? [queryInput] : queryInput;
|
|
277
|
+
const { query: queryInput, ignoreEmptyResult = false, timeout, fetch, ...params } = args;
|
|
278
|
+
const query = Array.isArray(queryInput) ? queryInput : [queryInput];
|
|
301
279
|
|
|
302
280
|
// rename and refactor limit, offset, and sort keys for this request
|
|
303
|
-
if ("offset" in params && params.offset !== undefined) {
|
|
304
|
-
|
|
281
|
+
if ("offset" in params && params.offset !== undefined && params.offset <= 1) {
|
|
282
|
+
params.offset = undefined;
|
|
305
283
|
}
|
|
306
284
|
if ("dateformats" in params && params.dateformats !== undefined) {
|
|
307
285
|
// reassign dateformats to match FileMaker's expected values
|
|
286
|
+
let dateFormatValue: number;
|
|
287
|
+
if (params.dateformats === "US") {
|
|
288
|
+
dateFormatValue = 0;
|
|
289
|
+
} else if (params.dateformats === "file_locale") {
|
|
290
|
+
dateFormatValue = 1;
|
|
291
|
+
} else if (params.dateformats === "ISO8601") {
|
|
292
|
+
dateFormatValue = 2;
|
|
293
|
+
} else {
|
|
294
|
+
dateFormatValue = 0;
|
|
295
|
+
}
|
|
308
296
|
// @ts-expect-error FM wants a string, so this is fine
|
|
309
|
-
params.dateformats = (
|
|
310
|
-
params.dateformats === "US"
|
|
311
|
-
? 0
|
|
312
|
-
: params.dateformats === "file_locale"
|
|
313
|
-
? 1
|
|
314
|
-
: params.dateformats === "ISO8601"
|
|
315
|
-
? 2
|
|
316
|
-
: 0
|
|
317
|
-
).toString();
|
|
297
|
+
params.dateformats = dateFormatValue.toString();
|
|
318
298
|
}
|
|
319
299
|
const result = (await find({
|
|
320
300
|
data: { ...params, query },
|
|
@@ -322,18 +302,21 @@ function DataApi<
|
|
|
322
302
|
fetch,
|
|
323
303
|
timeout,
|
|
324
304
|
}).catch((e: unknown) => {
|
|
325
|
-
if (ignoreEmptyResult && e instanceof FileMakerError && e.code === "401")
|
|
305
|
+
if (ignoreEmptyResult && e instanceof FileMakerError && e.code === "401") {
|
|
326
306
|
return { data: [], dataInfo: { foundCount: 0, returnedCount: 0 } };
|
|
307
|
+
}
|
|
327
308
|
throw e;
|
|
328
309
|
})) as GetResponse<InferredFieldData, InferredPortalData>;
|
|
329
310
|
|
|
330
|
-
if (
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
311
|
+
if (
|
|
312
|
+
result.dataInfo.foundCount > result.dataInfo.returnedCount &&
|
|
313
|
+
args?.limit === undefined &&
|
|
314
|
+
args?.offset === undefined
|
|
315
|
+
) {
|
|
316
|
+
// more records found than returned and the user didn't specify a limit or offset
|
|
317
|
+
console.warn(
|
|
318
|
+
`🚨 @proofkit/fmdapi: Loaded only ${result.dataInfo.returnedCount} of the ${result.dataInfo.foundCount} records from your "${layout}" layout. Use the "findAll" method to automatically paginate through all records, or specify a "limit" and "offset" to handle pagination yourself.`,
|
|
319
|
+
);
|
|
337
320
|
}
|
|
338
321
|
|
|
339
322
|
return await runSchemaValidationAndTransform(schema, result);
|
|
@@ -346,15 +329,13 @@ function DataApi<
|
|
|
346
329
|
args: FindArgs<InferredFieldData, InferredPortalData> & FetchOptions,
|
|
347
330
|
): Promise<GetResponseOne<InferredFieldData, InferredPortalData>> {
|
|
348
331
|
const result = await _find(args);
|
|
349
|
-
if (result.data.length !== 1)
|
|
350
|
-
throw new Error(
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
);
|
|
357
|
-
if (!transformedResult.data[0]) throw new Error("No data found");
|
|
332
|
+
if (result.data.length !== 1) {
|
|
333
|
+
throw new Error(`${result.data.length} records found; expecting exactly 1`);
|
|
334
|
+
}
|
|
335
|
+
const transformedResult = await runSchemaValidationAndTransform(schema, result);
|
|
336
|
+
if (!transformedResult.data[0]) {
|
|
337
|
+
throw new Error("No data found");
|
|
338
|
+
}
|
|
358
339
|
return { ...transformedResult, data: transformedResult.data[0] };
|
|
359
340
|
}
|
|
360
341
|
|
|
@@ -362,17 +343,14 @@ function DataApi<
|
|
|
362
343
|
* Helper method for `find`. Will only return the first result instead of an array.
|
|
363
344
|
*/
|
|
364
345
|
async function findFirst(
|
|
365
|
-
args: FindArgs<InferredFieldData, InferredPortalData> &
|
|
366
|
-
IgnoreEmptyResult &
|
|
367
|
-
FetchOptions,
|
|
346
|
+
args: FindArgs<InferredFieldData, InferredPortalData> & IgnoreEmptyResult & FetchOptions,
|
|
368
347
|
): Promise<GetResponseOne<InferredFieldData, InferredPortalData>> {
|
|
369
348
|
const result = await _find(args);
|
|
370
|
-
const transformedResult = await runSchemaValidationAndTransform(
|
|
371
|
-
schema,
|
|
372
|
-
result,
|
|
373
|
-
);
|
|
349
|
+
const transformedResult = await runSchemaValidationAndTransform(schema, result);
|
|
374
350
|
|
|
375
|
-
if (!transformedResult.data[0])
|
|
351
|
+
if (!transformedResult.data[0]) {
|
|
352
|
+
throw new Error("No data found");
|
|
353
|
+
}
|
|
376
354
|
return { ...transformedResult, data: transformedResult.data[0] };
|
|
377
355
|
}
|
|
378
356
|
|
|
@@ -380,16 +358,13 @@ function DataApi<
|
|
|
380
358
|
* Helper method for `find`. Will return the first result or null if no results are found.
|
|
381
359
|
*/
|
|
382
360
|
async function maybeFindFirst(
|
|
383
|
-
args: FindArgs<InferredFieldData, InferredPortalData> &
|
|
384
|
-
IgnoreEmptyResult &
|
|
385
|
-
FetchOptions,
|
|
361
|
+
args: FindArgs<InferredFieldData, InferredPortalData> & IgnoreEmptyResult & FetchOptions,
|
|
386
362
|
): Promise<GetResponseOne<InferredFieldData, InferredPortalData> | null> {
|
|
387
363
|
const result = await _find({ ...args, ignoreEmptyResult: true });
|
|
388
|
-
const transformedResult = await runSchemaValidationAndTransform(
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
if (!transformedResult.data[0]) return null;
|
|
364
|
+
const transformedResult = await runSchemaValidationAndTransform(schema, result);
|
|
365
|
+
if (!transformedResult.data[0]) {
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
393
368
|
return { ...transformedResult, data: transformedResult.data[0] };
|
|
394
369
|
}
|
|
395
370
|
|
|
@@ -400,13 +375,10 @@ function DataApi<
|
|
|
400
375
|
async function findAll(
|
|
401
376
|
args: FindArgs<InferredFieldData, InferredPortalData> & FetchOptions,
|
|
402
377
|
): Promise<FMRecord<InferredFieldData, InferredPortalData>[]> {
|
|
403
|
-
let runningData: GetResponse<
|
|
404
|
-
InferredFieldData,
|
|
405
|
-
InferredPortalData
|
|
406
|
-
>["data"] = [];
|
|
378
|
+
let runningData: GetResponse<InferredFieldData, InferredPortalData>["data"] = [];
|
|
407
379
|
const limit = args.limit ?? 100;
|
|
408
380
|
let offset = args.offset ?? 1;
|
|
409
|
-
|
|
381
|
+
|
|
410
382
|
while (true) {
|
|
411
383
|
const data = await _find({
|
|
412
384
|
...args,
|
|
@@ -414,12 +386,10 @@ function DataApi<
|
|
|
414
386
|
ignoreEmptyResult: true,
|
|
415
387
|
});
|
|
416
388
|
runningData = [...runningData, ...data.data];
|
|
417
|
-
if (
|
|
418
|
-
runningData.length === 0 ||
|
|
419
|
-
runningData.length >= data.dataInfo.foundCount
|
|
420
|
-
)
|
|
389
|
+
if (runningData.length === 0 || runningData.length >= data.dataInfo.foundCount) {
|
|
421
390
|
break;
|
|
422
|
-
|
|
391
|
+
}
|
|
392
|
+
offset += limit;
|
|
423
393
|
}
|
|
424
394
|
return runningData;
|
|
425
395
|
}
|
|
@@ -436,9 +406,7 @@ function DataApi<
|
|
|
436
406
|
});
|
|
437
407
|
}
|
|
438
408
|
|
|
439
|
-
async function _containerUpload<
|
|
440
|
-
T extends InferredFieldData = InferredFieldData,
|
|
441
|
-
>(args: ContainerUploadArgs<T> & FetchOptions) {
|
|
409
|
+
async function _containerUpload(args: ContainerUploadArgs<InferredFieldData> & FetchOptions) {
|
|
442
410
|
const { ...params } = args;
|
|
443
411
|
return await containerUpload({
|
|
444
412
|
layout,
|
|
@@ -459,14 +427,15 @@ function DataApi<
|
|
|
459
427
|
const fieldDataIssues: StandardSchemaV1.Issue[] = [];
|
|
460
428
|
const portalDataIssues: StandardSchemaV1.Issue[] = [];
|
|
461
429
|
|
|
462
|
-
if (!schema)
|
|
463
|
-
|
|
464
|
-
|
|
430
|
+
if (!schema) {
|
|
431
|
+
return result;
|
|
432
|
+
}
|
|
433
|
+
const transformedData: FMRecord<InferredFieldData, InferredPortalData>[] = [];
|
|
465
434
|
for (const record of result.data) {
|
|
466
|
-
let fieldResult = schema.fieldData["~standard"].validate(
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
435
|
+
let fieldResult = schema.fieldData["~standard"].validate(record.fieldData);
|
|
436
|
+
if (fieldResult instanceof Promise) {
|
|
437
|
+
fieldResult = await fieldResult;
|
|
438
|
+
}
|
|
470
439
|
if ("value" in fieldResult) {
|
|
471
440
|
record.fieldData = fieldResult.value as InferredFieldData;
|
|
472
441
|
} else {
|
|
@@ -474,18 +443,13 @@ function DataApi<
|
|
|
474
443
|
}
|
|
475
444
|
|
|
476
445
|
if (schema.portalData) {
|
|
477
|
-
for (const [portalName, portalRecords] of Object.entries(
|
|
478
|
-
|
|
479
|
-
)) {
|
|
480
|
-
const validatedPortalRecords: PortalsWithIds<GenericPortalData>[] =
|
|
481
|
-
[];
|
|
446
|
+
for (const [portalName, portalRecords] of Object.entries(record.portalData)) {
|
|
447
|
+
const validatedPortalRecords: PortalsWithIds<GenericPortalData>[] = [];
|
|
482
448
|
for (const portalRecord of portalRecords) {
|
|
483
|
-
let portalResult =
|
|
484
|
-
|
|
485
|
-
portalRecord,
|
|
486
|
-
);
|
|
487
|
-
if (portalResult instanceof Promise)
|
|
449
|
+
let portalResult = schema.portalData[portalName]?.["~standard"].validate(portalRecord);
|
|
450
|
+
if (portalResult instanceof Promise) {
|
|
488
451
|
portalResult = await portalResult;
|
|
452
|
+
}
|
|
489
453
|
if (portalResult && "value" in portalResult) {
|
|
490
454
|
validatedPortalRecords.push({
|
|
491
455
|
...portalResult.value,
|
package/src/index.ts
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import { FileMakerError } from "./client-types.js";
|
|
2
|
-
import { DataApi } from "./client.js";
|
|
3
|
-
|
|
4
|
-
export { DataApi, FileMakerError };
|
|
5
|
-
export * from "./utils.js";
|
|
6
|
-
export * as clientTypes from "./client-types.js";
|
|
7
|
-
|
|
8
1
|
export { FetchAdapter } from "./adapters/fetch.js";
|
|
2
|
+
export { FmHttpAdapter, type FmHttpAdapterOptions } from "./adapters/fm-http.js";
|
|
9
3
|
export { OttoAdapter, type OttoAPIKey } from "./adapters/otto.js";
|
|
10
|
-
|
|
11
|
-
export
|
|
4
|
+
export { DataApi, DataApi as default } from "./client.js";
|
|
5
|
+
export * as clientTypes from "./client-types.js";
|
|
6
|
+
export { FileMakerError } from "./client-types.js";
|
|
7
|
+
export * from "./utils.js";
|
package/src/tokenStore/file.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { TokenStoreDefinitions } from "./types.js";
|
|
2
1
|
import fs from "fs-extra";
|
|
2
|
+
import type { TokenStoreDefinitions } from "./types.js";
|
|
3
3
|
|
|
4
4
|
function getDataFromFile(devFileName: string): Record<string, string> {
|
|
5
5
|
const data: Record<string, string> = {};
|
|
@@ -21,9 +21,7 @@ const getSharedData = (key: string, devFileName: string): string | null => {
|
|
|
21
21
|
const data = getDataFromFile(devFileName);
|
|
22
22
|
return data[key] ?? null;
|
|
23
23
|
};
|
|
24
|
-
export const fileTokenStore = (
|
|
25
|
-
fileName = "shared.json",
|
|
26
|
-
): TokenStoreDefinitions => {
|
|
24
|
+
export const fileTokenStore = (fileName = "shared.json"): TokenStoreDefinitions => {
|
|
27
25
|
return {
|
|
28
26
|
setToken: (key, value) => setSharedData(key, value, fileName),
|
|
29
27
|
getToken: (key) => getSharedData(key, fileName),
|
package/src/tokenStore/index.ts
CHANGED
package/src/tokenStore/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
type MaybePromise<T> = Promise<T> | T;
|
|
2
|
-
export
|
|
2
|
+
export interface TokenStoreDefinitions {
|
|
3
3
|
getKey?: () => string;
|
|
4
4
|
getToken: (key: string) => MaybePromise<string | null>;
|
|
5
5
|
setToken: (key: string, value: string) => void;
|
|
6
6
|
clearToken: (key: string) => void;
|
|
7
|
-
}
|
|
7
|
+
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import type { TokenStoreDefinitions } from "./types.js";
|
|
2
1
|
import type { RedisConfigNodejs } from "@upstash/redis";
|
|
2
|
+
import type { TokenStoreDefinitions } from "./types.js";
|
|
3
3
|
|
|
4
|
-
export function upstashTokenStore(
|
|
5
|
-
config: RedisConfigNodejs,
|
|
6
|
-
options: { prefix?: string } = {},
|
|
7
|
-
): TokenStoreDefinitions {
|
|
4
|
+
export function upstashTokenStore(config: RedisConfigNodejs, options: { prefix?: string } = {}): TokenStoreDefinitions {
|
|
8
5
|
const { prefix = "" } = options;
|
|
9
6
|
|
|
10
7
|
const getRedis = async () => {
|
package/src/utils.ts
CHANGED
|
@@ -1,39 +1,32 @@
|
|
|
1
|
-
|
|
2
|
-
import type * as z4 from "zod/v4/core";
|
|
3
|
-
|
|
4
|
-
type StripFMTableName<K extends PropertyKey> = K extends `${string}::${infer R}`
|
|
5
|
-
? R
|
|
6
|
-
: K;
|
|
1
|
+
type StripFMTableName<K extends PropertyKey> = K extends `${string}::${infer R}` ? R : K;
|
|
7
2
|
|
|
8
3
|
type TransformedFields<T extends object> = {
|
|
9
4
|
[K in keyof T as K extends string ? StripFMTableName<K> : K]: T[K];
|
|
10
5
|
};
|
|
11
6
|
|
|
12
|
-
export function removeFMTableNames<T extends object>(
|
|
13
|
-
obj: T,
|
|
14
|
-
): TransformedFields<T> {
|
|
7
|
+
export function removeFMTableNames<T extends object>(obj: T): TransformedFields<T> {
|
|
15
8
|
const newObj = {} as TransformedFields<T>;
|
|
16
9
|
for (const key in obj) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
10
|
+
if (Object.hasOwn(obj, key)) {
|
|
11
|
+
const originalKey = key as keyof T;
|
|
12
|
+
const value = obj[originalKey];
|
|
13
|
+
const mappedKey = (
|
|
14
|
+
typeof key === "string" && key.includes("::") ? key.split("::")[1] : key
|
|
15
|
+
) as keyof TransformedFields<T>;
|
|
22
16
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
] = value as unknown;
|
|
17
|
+
// Use a temporary index signature cast to assign without any
|
|
18
|
+
(newObj as unknown as Record<PropertyKey, unknown>)[mappedKey as unknown as PropertyKey] = value as unknown;
|
|
19
|
+
}
|
|
27
20
|
}
|
|
28
21
|
return newObj;
|
|
29
22
|
}
|
|
30
23
|
|
|
31
|
-
export type InferZodPortals<T extends Record<string,
|
|
32
|
-
[K in keyof T]: T[K] extends { _def:
|
|
24
|
+
export type InferZodPortals<T extends Record<string, unknown>> = {
|
|
25
|
+
[K in keyof T]: T[K] extends { _def: unknown; parse: (...args: unknown[]) => unknown }
|
|
33
26
|
? ReturnType<T[K]["parse"]>
|
|
34
|
-
: T[K] extends { _def:
|
|
35
|
-
? T[K] extends { parse: (...args:
|
|
27
|
+
: T[K] extends { _def: unknown; safeParse: (...args: unknown[]) => unknown }
|
|
28
|
+
? T[K] extends { parse: (...args: unknown[]) => unknown }
|
|
36
29
|
? ReturnType<T[K]["parse"]>
|
|
37
|
-
:
|
|
30
|
+
: unknown
|
|
38
31
|
: never;
|
|
39
32
|
};
|