@proofkit/fmodata 0.1.0-alpha.8 → 0.1.0-beta.23
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/LICENSE.md +21 -0
- package/README.md +651 -449
- package/dist/esm/client/batch-builder.d.ts +10 -9
- package/dist/esm/client/batch-builder.js +119 -56
- package/dist/esm/client/batch-builder.js.map +1 -1
- package/dist/esm/client/batch-request.js +16 -21
- package/dist/esm/client/batch-request.js.map +1 -1
- package/dist/esm/client/builders/default-select.d.ts +10 -0
- package/dist/esm/client/builders/default-select.js +41 -0
- package/dist/esm/client/builders/default-select.js.map +1 -0
- package/dist/esm/client/builders/expand-builder.d.ts +45 -0
- package/dist/esm/client/builders/expand-builder.js +185 -0
- package/dist/esm/client/builders/expand-builder.js.map +1 -0
- package/dist/esm/client/builders/index.d.ts +9 -0
- package/dist/esm/client/builders/query-string-builder.d.ts +18 -0
- package/dist/esm/client/builders/query-string-builder.js +21 -0
- package/dist/esm/client/builders/query-string-builder.js.map +1 -0
- package/dist/esm/client/builders/response-processor.d.ts +43 -0
- package/dist/esm/client/builders/response-processor.js +175 -0
- package/dist/esm/client/builders/response-processor.js.map +1 -0
- package/dist/esm/client/builders/select-mixin.d.ts +25 -0
- package/dist/esm/client/builders/select-mixin.js +28 -0
- package/dist/esm/client/builders/select-mixin.js.map +1 -0
- package/dist/esm/client/builders/select-utils.d.ts +18 -0
- package/dist/esm/client/builders/select-utils.js +30 -0
- package/dist/esm/client/builders/select-utils.js.map +1 -0
- package/dist/esm/client/builders/shared-types.d.ts +40 -0
- package/dist/esm/client/builders/table-utils.d.ts +35 -0
- package/dist/esm/client/builders/table-utils.js +44 -0
- package/dist/esm/client/builders/table-utils.js.map +1 -0
- package/dist/esm/client/database.d.ts +34 -22
- package/dist/esm/client/database.js +48 -84
- package/dist/esm/client/database.js.map +1 -1
- package/dist/esm/client/delete-builder.d.ts +25 -30
- package/dist/esm/client/delete-builder.js +45 -30
- package/dist/esm/client/delete-builder.js.map +1 -1
- package/dist/esm/client/entity-set.d.ts +35 -43
- package/dist/esm/client/entity-set.js +110 -52
- 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 +25 -0
- package/dist/esm/client/error-parser.js.map +1 -0
- package/dist/esm/client/filemaker-odata.d.ts +26 -7
- package/dist/esm/client/filemaker-odata.js +65 -42
- package/dist/esm/client/filemaker-odata.js.map +1 -1
- package/dist/esm/client/insert-builder.d.ts +19 -24
- package/dist/esm/client/insert-builder.js +94 -58
- 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 +4 -0
- package/dist/esm/client/query/query-builder.d.ts +132 -0
- package/dist/esm/client/query/query-builder.js +456 -0
- package/dist/esm/client/query/query-builder.js.map +1 -0
- package/dist/esm/client/query/response-processor.d.ts +25 -0
- package/dist/esm/client/query/types.d.ts +77 -0
- package/dist/esm/client/query/url-builder.d.ts +71 -0
- package/dist/esm/client/query/url-builder.js +100 -0
- package/dist/esm/client/query/url-builder.js.map +1 -0
- package/dist/esm/client/query-builder.d.ts +2 -115
- package/dist/esm/client/record-builder.d.ts +108 -36
- package/dist/esm/client/record-builder.js +284 -119
- package/dist/esm/client/record-builder.js.map +1 -1
- package/dist/esm/client/response-processor.d.ts +4 -9
- package/dist/esm/client/sanitize-json.d.ts +35 -0
- package/dist/esm/client/sanitize-json.js +27 -0
- package/dist/esm/client/sanitize-json.js.map +1 -0
- package/dist/esm/client/schema-manager.d.ts +5 -5
- package/dist/esm/client/schema-manager.js +45 -31
- package/dist/esm/client/schema-manager.js.map +1 -1
- package/dist/esm/client/update-builder.d.ts +34 -40
- package/dist/esm/client/update-builder.js +99 -58
- package/dist/esm/client/update-builder.js.map +1 -1
- package/dist/esm/client/webhook-builder.d.ts +126 -0
- package/dist/esm/client/webhook-builder.js +189 -0
- package/dist/esm/client/webhook-builder.js.map +1 -0
- package/dist/esm/errors.d.ts +19 -2
- package/dist/esm/errors.js +39 -4
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/index.d.ts +10 -8
- package/dist/esm/index.js +40 -10
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/logger.d.ts +47 -0
- package/dist/esm/logger.js +69 -0
- package/dist/esm/logger.js.map +1 -0
- package/dist/esm/logger.test.d.ts +1 -0
- package/dist/esm/orm/column.d.ts +62 -0
- package/dist/esm/orm/column.js +63 -0
- package/dist/esm/orm/column.js.map +1 -0
- package/dist/esm/orm/field-builders.d.ts +164 -0
- package/dist/esm/orm/field-builders.js +158 -0
- package/dist/esm/orm/field-builders.js.map +1 -0
- package/dist/esm/orm/index.d.ts +5 -0
- package/dist/esm/orm/operators.d.ts +173 -0
- package/dist/esm/orm/operators.js +260 -0
- package/dist/esm/orm/operators.js.map +1 -0
- package/dist/esm/orm/table.d.ts +355 -0
- package/dist/esm/orm/table.js +202 -0
- package/dist/esm/orm/table.js.map +1 -0
- package/dist/esm/transform.d.ts +20 -21
- package/dist/esm/transform.js +44 -45
- package/dist/esm/transform.js.map +1 -1
- package/dist/esm/types.d.ts +96 -30
- package/dist/esm/types.js +7 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/validation.d.ts +22 -12
- package/dist/esm/validation.js +132 -85
- package/dist/esm/validation.js.map +1 -1
- package/package.json +28 -20
- package/src/client/batch-builder.ts +153 -89
- package/src/client/batch-request.ts +25 -41
- package/src/client/builders/default-select.ts +75 -0
- package/src/client/builders/expand-builder.ts +246 -0
- package/src/client/builders/index.ts +11 -0
- package/src/client/builders/query-string-builder.ts +46 -0
- package/src/client/builders/response-processor.ts +279 -0
- package/src/client/builders/select-mixin.ts +65 -0
- package/src/client/builders/select-utils.ts +59 -0
- package/src/client/builders/shared-types.ts +45 -0
- package/src/client/builders/table-utils.ts +83 -0
- package/src/client/database.ts +89 -183
- package/src/client/delete-builder.ts +74 -84
- package/src/client/entity-set.ts +266 -293
- package/src/client/error-parser.ts +41 -0
- package/src/client/filemaker-odata.ts +98 -66
- package/src/client/insert-builder.ts +157 -118
- package/src/client/query/expand-builder.ts +160 -0
- package/src/client/query/index.ts +14 -0
- package/src/client/query/query-builder.ts +729 -0
- package/src/client/query/response-processor.ts +226 -0
- package/src/client/query/types.ts +126 -0
- package/src/client/query/url-builder.ts +151 -0
- package/src/client/query-builder.ts +10 -1455
- package/src/client/record-builder.ts +575 -240
- package/src/client/response-processor.ts +15 -42
- package/src/client/sanitize-json.ts +64 -0
- package/src/client/schema-manager.ts +61 -76
- package/src/client/update-builder.ts +161 -143
- package/src/client/webhook-builder.ts +265 -0
- package/src/errors.ts +49 -16
- package/src/index.ts +99 -54
- package/src/logger.test.ts +34 -0
- package/src/logger.ts +116 -0
- package/src/orm/column.ts +106 -0
- package/src/orm/field-builders.ts +250 -0
- package/src/orm/index.ts +61 -0
- package/src/orm/operators.ts +473 -0
- package/src/orm/table.ts +741 -0
- package/src/transform.ts +90 -70
- package/src/types.ts +154 -113
- package/src/validation.ts +200 -115
- package/dist/esm/client/base-table.d.ts +0 -125
- package/dist/esm/client/base-table.js +0 -57
- package/dist/esm/client/base-table.js.map +0 -1
- package/dist/esm/client/query-builder.js +0 -896
- package/dist/esm/client/query-builder.js.map +0 -1
- package/dist/esm/client/table-occurrence.d.ts +0 -72
- package/dist/esm/client/table-occurrence.js +0 -74
- package/dist/esm/client/table-occurrence.js.map +0 -1
- package/dist/esm/filter-types.d.ts +0 -76
- package/src/client/base-table.ts +0 -166
- package/src/client/query-builder.ts.bak +0 -1457
- package/src/client/table-occurrence.ts +0 -175
- package/src/filter-types.ts +0 -97
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { type FMTable, getTableName } from "../orm";
|
|
2
|
+
import { type Column, isColumn } from "../orm/column";
|
|
3
|
+
import { FilterExpression } from "../orm/operators";
|
|
4
|
+
import type { ExecuteMethodOptions, ExecutionContext } from "../types";
|
|
5
|
+
import { formatSelectFields } from "./builders/select-utils";
|
|
6
|
+
|
|
7
|
+
export interface Webhook<TableName = string> {
|
|
8
|
+
webhook: string;
|
|
9
|
+
headers?: Record<string, string>;
|
|
10
|
+
tableName: TableName;
|
|
11
|
+
notifySchemaChanges?: boolean;
|
|
12
|
+
// biome-ignore lint/suspicious/noExplicitAny: Generic constraint accepting any Column configuration
|
|
13
|
+
select?: string | Column<any, any, any>[];
|
|
14
|
+
filter?: string | FilterExpression;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Webhook information returned by the API
|
|
19
|
+
*/
|
|
20
|
+
export interface WebhookInfo {
|
|
21
|
+
webHookID: number;
|
|
22
|
+
tableName: string;
|
|
23
|
+
url: string;
|
|
24
|
+
headers?: Record<string, string>;
|
|
25
|
+
notifySchemaChanges: boolean;
|
|
26
|
+
select: string;
|
|
27
|
+
filter: string;
|
|
28
|
+
pendingOperations: unknown[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Response from listing all webhooks
|
|
33
|
+
*/
|
|
34
|
+
export interface WebhookListResponse {
|
|
35
|
+
Status: string;
|
|
36
|
+
WebHook: WebhookInfo[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Response from adding a webhook
|
|
41
|
+
*/
|
|
42
|
+
export interface WebhookAddResponse {
|
|
43
|
+
webHookResult: {
|
|
44
|
+
webHookID: number;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export class WebhookManager {
|
|
49
|
+
private readonly databaseName: string;
|
|
50
|
+
private readonly context: ExecutionContext;
|
|
51
|
+
|
|
52
|
+
constructor(databaseName: string, context: ExecutionContext) {
|
|
53
|
+
this.databaseName = databaseName;
|
|
54
|
+
this.context = context;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Adds a new webhook to the database.
|
|
59
|
+
* @param webhook - The webhook configuration object
|
|
60
|
+
* @param webhook.webhook - The webhook URL to call
|
|
61
|
+
* @param webhook.tableName - The FMTable instance for the table to monitor
|
|
62
|
+
* @param webhook.headers - Optional custom headers to include in webhook requests
|
|
63
|
+
* @param webhook.notifySchemaChanges - Whether to notify on schema changes
|
|
64
|
+
* @param webhook.select - Optional field selection (string or array of Column references)
|
|
65
|
+
* @param webhook.filter - Optional filter (string or FilterExpression)
|
|
66
|
+
* @returns Promise resolving to the created webhook data with ID
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* const result = await db.webhook.add({
|
|
70
|
+
* webhook: "https://example.com/webhook",
|
|
71
|
+
* tableName: contactsTable,
|
|
72
|
+
* headers: { "X-Custom-Header": "value" },
|
|
73
|
+
* });
|
|
74
|
+
* // result.webHookResult.webHookID contains the new webhook ID
|
|
75
|
+
* ```
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* // Using filter expressions and column arrays (same DX as query builder)
|
|
79
|
+
* const result = await db.webhook.add({
|
|
80
|
+
* webhook: "https://example.com/webhook",
|
|
81
|
+
* tableName: contacts,
|
|
82
|
+
* filter: eq(contacts.name, "John"),
|
|
83
|
+
* select: [contacts.name, contacts.PrimaryKey],
|
|
84
|
+
* });
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
async add(webhook: Webhook<FMTable>, options?: ExecuteMethodOptions): Promise<WebhookAddResponse> {
|
|
88
|
+
// Extract the string table name from the FMTable instance
|
|
89
|
+
const tableName = getTableName(webhook.tableName);
|
|
90
|
+
|
|
91
|
+
// Get useEntityIds setting (check options first, then context, default to false)
|
|
92
|
+
const useEntityIds = options?.useEntityIds ?? this.context._getUseEntityIds?.() ?? false;
|
|
93
|
+
|
|
94
|
+
// Transform filter if it's a FilterExpression
|
|
95
|
+
let filter: string | undefined;
|
|
96
|
+
if (webhook.filter !== undefined) {
|
|
97
|
+
if (webhook.filter instanceof FilterExpression) {
|
|
98
|
+
filter = webhook.filter.toODataFilter(useEntityIds);
|
|
99
|
+
} else {
|
|
100
|
+
filter = webhook.filter;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Transform select if it's an array of Columns
|
|
105
|
+
let select: string | undefined;
|
|
106
|
+
if (webhook.select !== undefined) {
|
|
107
|
+
if (Array.isArray(webhook.select)) {
|
|
108
|
+
// Extract field identifiers from columns or use strings as-is
|
|
109
|
+
const fieldNames = webhook.select.map((item) => {
|
|
110
|
+
if (isColumn(item)) {
|
|
111
|
+
return item.getFieldIdentifier(useEntityIds);
|
|
112
|
+
}
|
|
113
|
+
return String(item);
|
|
114
|
+
});
|
|
115
|
+
// Use formatSelectFields to properly format the select string
|
|
116
|
+
select = formatSelectFields(fieldNames, webhook.tableName, useEntityIds);
|
|
117
|
+
} else {
|
|
118
|
+
// Already a string, use as-is
|
|
119
|
+
select = webhook.select;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Create request body with string table name and transformed filter/select
|
|
124
|
+
const requestBody: {
|
|
125
|
+
webhook: string;
|
|
126
|
+
headers?: Record<string, string>;
|
|
127
|
+
tableName: string;
|
|
128
|
+
notifySchemaChanges?: boolean;
|
|
129
|
+
select?: string;
|
|
130
|
+
filter?: string;
|
|
131
|
+
} = {
|
|
132
|
+
webhook: webhook.webhook,
|
|
133
|
+
tableName,
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
if (webhook.headers !== undefined) {
|
|
137
|
+
requestBody.headers = webhook.headers;
|
|
138
|
+
}
|
|
139
|
+
if (webhook.notifySchemaChanges !== undefined) {
|
|
140
|
+
requestBody.notifySchemaChanges = webhook.notifySchemaChanges;
|
|
141
|
+
}
|
|
142
|
+
if (select !== undefined) {
|
|
143
|
+
requestBody.select = select;
|
|
144
|
+
}
|
|
145
|
+
if (filter !== undefined) {
|
|
146
|
+
requestBody.filter = filter;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const result = await this.context._makeRequest<WebhookAddResponse>(`/${this.databaseName}/Webhook.Add`, {
|
|
150
|
+
method: "POST",
|
|
151
|
+
body: JSON.stringify(requestBody),
|
|
152
|
+
...options,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
if (result.error) {
|
|
156
|
+
throw result.error;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return result.data;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Deletes a webhook by ID.
|
|
164
|
+
* @param webhookId - The ID of the webhook to delete
|
|
165
|
+
* @returns Promise that resolves when the webhook is deleted
|
|
166
|
+
* @example
|
|
167
|
+
* ```ts
|
|
168
|
+
* await db.webhook.remove(1);
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
async remove(webhookId: number, options?: ExecuteMethodOptions): Promise<void> {
|
|
172
|
+
const result = await this.context._makeRequest(`/${this.databaseName}/Webhook.Delete(${webhookId})`, {
|
|
173
|
+
method: "POST",
|
|
174
|
+
...options,
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
if (result.error) {
|
|
178
|
+
throw result.error;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Gets a webhook by ID.
|
|
184
|
+
* @param webhookId - The ID of the webhook to retrieve
|
|
185
|
+
* @returns Promise resolving to the webhook data
|
|
186
|
+
* @example
|
|
187
|
+
* ```ts
|
|
188
|
+
* const webhook = await db.webhook.get(1);
|
|
189
|
+
* // webhook.webHookID, webhook.tableName, webhook.url, etc.
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
async get(webhookId: number, options?: ExecuteMethodOptions): Promise<WebhookInfo> {
|
|
193
|
+
const result = await this.context._makeRequest<WebhookInfo>(
|
|
194
|
+
`/${this.databaseName}/Webhook.Get(${webhookId})`,
|
|
195
|
+
options,
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
if (result.error) {
|
|
199
|
+
throw result.error;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return result.data;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Lists all webhooks.
|
|
207
|
+
* @returns Promise resolving to webhook list response with status and webhooks array
|
|
208
|
+
* @example
|
|
209
|
+
* ```ts
|
|
210
|
+
* const result = await db.webhook.list();
|
|
211
|
+
* // result.Status contains the status
|
|
212
|
+
* // result.WebHook contains the array of webhooks
|
|
213
|
+
* ```
|
|
214
|
+
*/
|
|
215
|
+
async list(options?: ExecuteMethodOptions): Promise<WebhookListResponse> {
|
|
216
|
+
const result = await this.context._makeRequest<WebhookListResponse>(
|
|
217
|
+
`/${this.databaseName}/Webhook.GetAll`,
|
|
218
|
+
options,
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
if (result.error) {
|
|
222
|
+
throw result.error;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return result.data;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Invokes a webhook by ID, optionally for specific row IDs.
|
|
230
|
+
* @param webhookId - The ID of the webhook to invoke
|
|
231
|
+
* @param options - Optional configuration
|
|
232
|
+
* @param options.rowIDs - Array of row IDs to trigger the webhook for
|
|
233
|
+
* @returns Promise resolving to the invocation result (type unknown until API behavior is confirmed)
|
|
234
|
+
* @example
|
|
235
|
+
* ```ts
|
|
236
|
+
* // Invoke for all rows
|
|
237
|
+
* await db.webhook.invoke(1);
|
|
238
|
+
*
|
|
239
|
+
* // Invoke for specific rows
|
|
240
|
+
* await db.webhook.invoke(1, { rowIDs: [63, 61] });
|
|
241
|
+
* ```
|
|
242
|
+
*/
|
|
243
|
+
async invoke(
|
|
244
|
+
webhookId: number,
|
|
245
|
+
options?: { rowIDs?: number[] },
|
|
246
|
+
executeOptions?: ExecuteMethodOptions,
|
|
247
|
+
): Promise<unknown> {
|
|
248
|
+
const body: { rowIDs?: number[] } = {};
|
|
249
|
+
if (options?.rowIDs !== undefined) {
|
|
250
|
+
body.rowIDs = options.rowIDs;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const result = await this.context._makeRequest<unknown>(`/${this.databaseName}/Webhook.Invoke(${webhookId})`, {
|
|
254
|
+
method: "POST",
|
|
255
|
+
body: Object.keys(body).length > 0 ? JSON.stringify(body) : undefined,
|
|
256
|
+
...executeOptions,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
if (result.error) {
|
|
260
|
+
throw result.error;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return result.data;
|
|
264
|
+
}
|
|
265
|
+
}
|
package/src/errors.ts
CHANGED
|
@@ -23,8 +23,10 @@ export class HTTPError extends FMODataError {
|
|
|
23
23
|
readonly url: string;
|
|
24
24
|
readonly status: number;
|
|
25
25
|
readonly statusText: string;
|
|
26
|
+
// biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API
|
|
26
27
|
readonly response?: any;
|
|
27
28
|
|
|
29
|
+
// biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API
|
|
28
30
|
constructor(url: string, status: number, statusText: string, response?: any) {
|
|
29
31
|
super(`HTTP ${status} ${statusText} for ${url}`);
|
|
30
32
|
this.url = url;
|
|
@@ -63,8 +65,10 @@ export class ODataError extends FMODataError {
|
|
|
63
65
|
readonly kind = "ODataError" as const;
|
|
64
66
|
readonly url: string;
|
|
65
67
|
readonly code?: string;
|
|
68
|
+
// biome-ignore lint/suspicious/noExplicitAny: Dynamic error details from OData API
|
|
66
69
|
readonly details?: any;
|
|
67
70
|
|
|
71
|
+
// biome-ignore lint/suspicious/noExplicitAny: Dynamic error details from OData API
|
|
68
72
|
constructor(url: string, message: string, code?: string, details?: any) {
|
|
69
73
|
super(`OData error: ${message}`);
|
|
70
74
|
this.url = url;
|
|
@@ -77,8 +81,10 @@ export class SchemaLockedError extends FMODataError {
|
|
|
77
81
|
readonly kind = "SchemaLockedError" as const;
|
|
78
82
|
readonly url: string;
|
|
79
83
|
readonly code: string;
|
|
84
|
+
// biome-ignore lint/suspicious/noExplicitAny: Dynamic error details from OData API
|
|
80
85
|
readonly details?: any;
|
|
81
86
|
|
|
87
|
+
// biome-ignore lint/suspicious/noExplicitAny: Dynamic error details from OData API
|
|
82
88
|
constructor(url: string, message: string, details?: any) {
|
|
83
89
|
super(`OData error: ${message}`);
|
|
84
90
|
this.url = url;
|
|
@@ -106,10 +112,7 @@ export class ValidationError extends FMODataError {
|
|
|
106
112
|
cause?: Error["cause"];
|
|
107
113
|
},
|
|
108
114
|
) {
|
|
109
|
-
super(
|
|
110
|
-
message,
|
|
111
|
-
options?.cause !== undefined ? { cause: options.cause } : undefined,
|
|
112
|
-
);
|
|
115
|
+
super(message, options?.cause !== undefined ? { cause: options.cause } : undefined);
|
|
113
116
|
this.field = options?.field;
|
|
114
117
|
this.issues = issues;
|
|
115
118
|
this.value = options?.value;
|
|
@@ -119,8 +122,10 @@ export class ValidationError extends FMODataError {
|
|
|
119
122
|
export class ResponseStructureError extends FMODataError {
|
|
120
123
|
readonly kind = "ResponseStructureError" as const;
|
|
121
124
|
readonly expected: string;
|
|
125
|
+
// biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API
|
|
122
126
|
readonly received: any;
|
|
123
127
|
|
|
128
|
+
// biome-ignore lint/suspicious/noExplicitAny: Dynamic response type from OData API
|
|
124
129
|
constructor(expected: string, received: any) {
|
|
125
130
|
super(`Invalid response structure: expected ${expected}`);
|
|
126
131
|
this.expected = expected;
|
|
@@ -151,6 +156,30 @@ export class InvalidLocationHeaderError extends FMODataError {
|
|
|
151
156
|
}
|
|
152
157
|
}
|
|
153
158
|
|
|
159
|
+
export class ResponseParseError extends FMODataError {
|
|
160
|
+
readonly kind = "ResponseParseError" as const;
|
|
161
|
+
readonly url: string;
|
|
162
|
+
readonly rawText?: string;
|
|
163
|
+
|
|
164
|
+
constructor(url: string, message: string, options?: { rawText?: string; cause?: Error }) {
|
|
165
|
+
super(message, options?.cause ? { cause: options.cause } : undefined);
|
|
166
|
+
this.url = url;
|
|
167
|
+
this.rawText = options?.rawText;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export class BatchTruncatedError extends FMODataError {
|
|
172
|
+
readonly kind = "BatchTruncatedError" as const;
|
|
173
|
+
readonly operationIndex: number;
|
|
174
|
+
readonly failedAtIndex: number;
|
|
175
|
+
|
|
176
|
+
constructor(operationIndex: number, failedAtIndex: number) {
|
|
177
|
+
super(`Operation ${operationIndex} was not executed because operation ${failedAtIndex} failed`);
|
|
178
|
+
this.operationIndex = operationIndex;
|
|
179
|
+
this.failedAtIndex = failedAtIndex;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
154
183
|
// ============================================
|
|
155
184
|
// Type Guards
|
|
156
185
|
// ============================================
|
|
@@ -167,24 +196,26 @@ export function isODataError(error: unknown): error is ODataError {
|
|
|
167
196
|
return error instanceof ODataError;
|
|
168
197
|
}
|
|
169
198
|
|
|
170
|
-
export function isSchemaLockedError(
|
|
171
|
-
error: unknown,
|
|
172
|
-
): error is SchemaLockedError {
|
|
199
|
+
export function isSchemaLockedError(error: unknown): error is SchemaLockedError {
|
|
173
200
|
return error instanceof SchemaLockedError;
|
|
174
201
|
}
|
|
175
202
|
|
|
176
|
-
export function isResponseStructureError(
|
|
177
|
-
error: unknown,
|
|
178
|
-
): error is ResponseStructureError {
|
|
203
|
+
export function isResponseStructureError(error: unknown): error is ResponseStructureError {
|
|
179
204
|
return error instanceof ResponseStructureError;
|
|
180
205
|
}
|
|
181
206
|
|
|
182
|
-
export function isRecordCountMismatchError(
|
|
183
|
-
error: unknown,
|
|
184
|
-
): error is RecordCountMismatchError {
|
|
207
|
+
export function isRecordCountMismatchError(error: unknown): error is RecordCountMismatchError {
|
|
185
208
|
return error instanceof RecordCountMismatchError;
|
|
186
209
|
}
|
|
187
210
|
|
|
211
|
+
export function isResponseParseError(error: unknown): error is ResponseParseError {
|
|
212
|
+
return error instanceof ResponseParseError;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function isBatchTruncatedError(error: unknown): error is BatchTruncatedError {
|
|
216
|
+
return error instanceof BatchTruncatedError;
|
|
217
|
+
}
|
|
218
|
+
|
|
188
219
|
export function isFMODataError(error: unknown): error is FMODataError {
|
|
189
220
|
return error instanceof FMODataError;
|
|
190
221
|
}
|
|
@@ -195,11 +226,11 @@ export function isFMODataError(error: unknown): error is FMODataError {
|
|
|
195
226
|
|
|
196
227
|
// Re-export ffetch errors (they'll be imported from @fetchkit/ffetch)
|
|
197
228
|
export type {
|
|
198
|
-
TimeoutError,
|
|
199
229
|
AbortError,
|
|
230
|
+
CircuitOpenError,
|
|
200
231
|
NetworkError,
|
|
201
232
|
RetryLimitError,
|
|
202
|
-
|
|
233
|
+
TimeoutError,
|
|
203
234
|
} from "@fetchkit/ffetch";
|
|
204
235
|
|
|
205
236
|
export type FMODataErrorType =
|
|
@@ -214,4 +245,6 @@ export type FMODataErrorType =
|
|
|
214
245
|
| ValidationError
|
|
215
246
|
| ResponseStructureError
|
|
216
247
|
| RecordCountMismatchError
|
|
217
|
-
| InvalidLocationHeaderError
|
|
248
|
+
| InvalidLocationHeaderError
|
|
249
|
+
| ResponseParseError
|
|
250
|
+
| BatchTruncatedError;
|
package/src/index.ts
CHANGED
|
@@ -1,74 +1,119 @@
|
|
|
1
1
|
// Barrel file - exports all public API from the client folder
|
|
2
|
-
|
|
2
|
+
/** biome-ignore-all lint/performance/noBarrelFile: Re-exporting all public API from the client folder */
|
|
3
|
+
|
|
4
|
+
export type { FFetchOptions } from "@fetchkit/ffetch";
|
|
5
|
+
// Re-export ffetch errors and types
|
|
3
6
|
export {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
AbortError,
|
|
8
|
+
CircuitOpenError,
|
|
9
|
+
NetworkError,
|
|
10
|
+
RetryLimitError,
|
|
11
|
+
TimeoutError,
|
|
12
|
+
} from "@fetchkit/ffetch";
|
|
9
13
|
|
|
10
|
-
// Type-only exports for
|
|
11
|
-
// Users get these instances from the builder pattern, not by direct instantiation
|
|
14
|
+
// Type-only exports - for type annotations only, not direct instantiation
|
|
12
15
|
export type { Database } from "./client/database";
|
|
13
16
|
export type { EntitySet } from "./client/entity-set";
|
|
17
|
+
// Main API - use these functions to create tables and occurrences
|
|
18
|
+
export { FMServerConnection } from "./client/filemaker-odata";
|
|
14
19
|
export type {
|
|
15
|
-
|
|
20
|
+
ContainerField,
|
|
21
|
+
DateField,
|
|
16
22
|
Field,
|
|
17
|
-
StringField,
|
|
18
23
|
NumericField,
|
|
19
|
-
|
|
24
|
+
SchemaManager,
|
|
25
|
+
StringField,
|
|
20
26
|
TimeField,
|
|
21
27
|
TimestampField,
|
|
22
|
-
ContainerField,
|
|
23
28
|
} from "./client/schema-manager";
|
|
24
|
-
|
|
25
|
-
// Utility types for type annotations
|
|
26
|
-
export type {
|
|
27
|
-
Result,
|
|
28
|
-
InferSchemaType,
|
|
29
|
-
InsertData,
|
|
30
|
-
UpdateData,
|
|
31
|
-
ODataRecordMetadata,
|
|
32
|
-
Metadata,
|
|
33
|
-
} from "./types";
|
|
34
|
-
|
|
35
|
-
// Filter types
|
|
36
29
|
export type {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
DateOperators,
|
|
44
|
-
LogicalFilter,
|
|
45
|
-
} from "./filter-types";
|
|
46
|
-
|
|
47
|
-
// Re-export ffetch errors
|
|
48
|
-
export {
|
|
49
|
-
TimeoutError,
|
|
50
|
-
AbortError,
|
|
51
|
-
NetworkError,
|
|
52
|
-
RetryLimitError,
|
|
53
|
-
CircuitOpenError,
|
|
54
|
-
} from "@fetchkit/ffetch";
|
|
55
|
-
|
|
30
|
+
Webhook,
|
|
31
|
+
WebhookAddResponse,
|
|
32
|
+
WebhookInfo,
|
|
33
|
+
WebhookListResponse,
|
|
34
|
+
} from "./client/webhook-builder";
|
|
35
|
+
export type { FMODataErrorType } from "./errors";
|
|
56
36
|
// Export our errors
|
|
57
37
|
export {
|
|
38
|
+
BatchTruncatedError,
|
|
58
39
|
FMODataError,
|
|
59
40
|
HTTPError,
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
ValidationError,
|
|
63
|
-
ResponseStructureError,
|
|
64
|
-
RecordCountMismatchError,
|
|
41
|
+
isBatchTruncatedError,
|
|
42
|
+
isFMODataError,
|
|
65
43
|
isHTTPError,
|
|
66
|
-
isValidationError,
|
|
67
44
|
isODataError,
|
|
68
|
-
isSchemaLockedError,
|
|
69
|
-
isResponseStructureError,
|
|
70
45
|
isRecordCountMismatchError,
|
|
71
|
-
|
|
46
|
+
isResponseParseError,
|
|
47
|
+
isResponseStructureError,
|
|
48
|
+
isSchemaLockedError,
|
|
49
|
+
isValidationError,
|
|
50
|
+
ODataError,
|
|
51
|
+
RecordCountMismatchError,
|
|
52
|
+
ResponseParseError,
|
|
53
|
+
ResponseStructureError,
|
|
54
|
+
SchemaLockedError,
|
|
55
|
+
ValidationError,
|
|
72
56
|
} from "./errors";
|
|
73
|
-
|
|
74
|
-
|
|
57
|
+
export type { Logger } from "./logger";
|
|
58
|
+
// NEW ORM API - Drizzle-inspired field builders and operators
|
|
59
|
+
export {
|
|
60
|
+
and,
|
|
61
|
+
asc,
|
|
62
|
+
// Column references
|
|
63
|
+
type Column,
|
|
64
|
+
calcField,
|
|
65
|
+
containerField,
|
|
66
|
+
contains,
|
|
67
|
+
dateField,
|
|
68
|
+
desc,
|
|
69
|
+
endsWith,
|
|
70
|
+
eq,
|
|
71
|
+
type FieldBuilder,
|
|
72
|
+
// Filter operators
|
|
73
|
+
type FilterExpression,
|
|
74
|
+
FMTable,
|
|
75
|
+
type FMTableWithColumns as TableOccurrenceResult,
|
|
76
|
+
// Table definition
|
|
77
|
+
fmTableOccurrence,
|
|
78
|
+
// Table helper functions
|
|
79
|
+
// getTableFields,
|
|
80
|
+
// getDefaultSelect,
|
|
81
|
+
// getBaseTableConfig,
|
|
82
|
+
// getFieldId,
|
|
83
|
+
// getFieldName,
|
|
84
|
+
// getTableId,
|
|
85
|
+
getTableColumns,
|
|
86
|
+
gt,
|
|
87
|
+
gte,
|
|
88
|
+
type InferTableSchema,
|
|
89
|
+
inArray,
|
|
90
|
+
isColumn,
|
|
91
|
+
isNotNull,
|
|
92
|
+
isNull,
|
|
93
|
+
lt,
|
|
94
|
+
lte,
|
|
95
|
+
ne,
|
|
96
|
+
not,
|
|
97
|
+
notInArray,
|
|
98
|
+
numberField,
|
|
99
|
+
// OrderBy operators
|
|
100
|
+
type OrderByExpression,
|
|
101
|
+
or,
|
|
102
|
+
startsWith,
|
|
103
|
+
// Field builders
|
|
104
|
+
textField,
|
|
105
|
+
timeField,
|
|
106
|
+
timestampField,
|
|
107
|
+
} from "./orm/index";
|
|
108
|
+
// Utility types for type annotations
|
|
109
|
+
export type {
|
|
110
|
+
BatchItemResult,
|
|
111
|
+
BatchResult,
|
|
112
|
+
ExecuteMethodOptions,
|
|
113
|
+
ExecuteOptions,
|
|
114
|
+
FetchHandler,
|
|
115
|
+
InferSchemaType,
|
|
116
|
+
Metadata,
|
|
117
|
+
ODataRecordMetadata,
|
|
118
|
+
Result,
|
|
119
|
+
} from "./types";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import type { LogLevel } from "./logger";
|
|
3
|
+
import { shouldPublishLog } from "./logger";
|
|
4
|
+
|
|
5
|
+
describe("shouldPublishLog", () => {
|
|
6
|
+
const testCases: {
|
|
7
|
+
currentLogLevel: LogLevel;
|
|
8
|
+
logLevel: LogLevel;
|
|
9
|
+
expected: boolean;
|
|
10
|
+
}[] = [
|
|
11
|
+
{ currentLogLevel: "debug", logLevel: "debug", expected: true },
|
|
12
|
+
{ currentLogLevel: "debug", logLevel: "info", expected: true },
|
|
13
|
+
{ currentLogLevel: "debug", logLevel: "warn", expected: true },
|
|
14
|
+
{ currentLogLevel: "debug", logLevel: "error", expected: true },
|
|
15
|
+
{ currentLogLevel: "info", logLevel: "debug", expected: false },
|
|
16
|
+
{ currentLogLevel: "info", logLevel: "info", expected: true },
|
|
17
|
+
{ currentLogLevel: "info", logLevel: "warn", expected: true },
|
|
18
|
+
{ currentLogLevel: "info", logLevel: "error", expected: true },
|
|
19
|
+
{ currentLogLevel: "warn", logLevel: "debug", expected: false },
|
|
20
|
+
{ currentLogLevel: "warn", logLevel: "info", expected: false },
|
|
21
|
+
{ currentLogLevel: "warn", logLevel: "warn", expected: true },
|
|
22
|
+
{ currentLogLevel: "warn", logLevel: "error", expected: true },
|
|
23
|
+
{ currentLogLevel: "error", logLevel: "debug", expected: false },
|
|
24
|
+
{ currentLogLevel: "error", logLevel: "info", expected: false },
|
|
25
|
+
{ currentLogLevel: "error", logLevel: "warn", expected: false },
|
|
26
|
+
{ currentLogLevel: "error", logLevel: "error", expected: true },
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
for (const { currentLogLevel, logLevel, expected } of testCases) {
|
|
30
|
+
it(`should return "${expected}" when currentLogLevel is "${currentLogLevel}" and logLevel is "${logLevel}"`, () => {
|
|
31
|
+
expect(shouldPublishLog(currentLogLevel, logLevel)).toBe(expected);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
});
|