@proofkit/fmodata 0.1.0-alpha.19 → 0.1.0-alpha.20
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 +198 -0
- package/dist/esm/client/builders/default-select.d.ts +4 -1
- package/dist/esm/client/builders/default-select.js +3 -2
- package/dist/esm/client/builders/default-select.js.map +1 -1
- package/dist/esm/client/builders/expand-builder.js.map +1 -1
- package/dist/esm/client/builders/query-string-builder.d.ts +1 -0
- package/dist/esm/client/builders/query-string-builder.js.map +1 -1
- package/dist/esm/client/builders/response-processor.d.ts +2 -0
- package/dist/esm/client/builders/response-processor.js +8 -3
- package/dist/esm/client/builders/response-processor.js.map +1 -1
- package/dist/esm/client/database.d.ts +27 -4
- package/dist/esm/client/database.js +17 -4
- package/dist/esm/client/database.js.map +1 -1
- package/dist/esm/client/delete-builder.d.ts +2 -0
- package/dist/esm/client/delete-builder.js +2 -0
- package/dist/esm/client/delete-builder.js.map +1 -1
- package/dist/esm/client/entity-set.d.ts +8 -7
- package/dist/esm/client/entity-set.js +21 -26
- package/dist/esm/client/entity-set.js.map +1 -1
- package/dist/esm/client/error-parser.js.map +1 -1
- package/dist/esm/client/filemaker-odata.d.ts +15 -2
- package/dist/esm/client/filemaker-odata.js +25 -2
- package/dist/esm/client/filemaker-odata.js.map +1 -1
- package/dist/esm/client/insert-builder.d.ts +2 -0
- package/dist/esm/client/insert-builder.js +2 -0
- package/dist/esm/client/insert-builder.js.map +1 -1
- package/dist/esm/client/query/query-builder.d.ts +26 -15
- package/dist/esm/client/query/query-builder.js +43 -9
- package/dist/esm/client/query/query-builder.js.map +1 -1
- package/dist/esm/client/query/response-processor.d.ts +1 -0
- package/dist/esm/client/query/types.d.ts +24 -3
- package/dist/esm/client/record-builder.d.ts +24 -13
- package/dist/esm/client/record-builder.js +50 -17
- package/dist/esm/client/record-builder.js.map +1 -1
- package/dist/esm/client/update-builder.d.ts +2 -0
- package/dist/esm/client/update-builder.js +2 -0
- 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 +197 -0
- package/dist/esm/client/webhook-builder.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/orm/field-builders.d.ts +12 -2
- package/dist/esm/orm/field-builders.js +18 -2
- package/dist/esm/orm/field-builders.js.map +1 -1
- package/dist/esm/orm/table.d.ts +23 -10
- package/dist/esm/orm/table.js +17 -30
- package/dist/esm/orm/table.js.map +1 -1
- package/dist/esm/types.d.ts +32 -2
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/validation.d.ts +5 -5
- package/dist/esm/validation.js +44 -13
- package/dist/esm/validation.js.map +1 -1
- package/package.json +2 -2
- package/src/client/builders/default-select.ts +12 -1
- package/src/client/builders/expand-builder.ts +1 -1
- package/src/client/builders/query-string-builder.ts +6 -0
- package/src/client/builders/response-processor.ts +10 -0
- package/src/client/database.ts +54 -12
- package/src/client/delete-builder.ts +5 -1
- package/src/client/entity-set.ts +72 -44
- package/src/client/error-parser.ts +3 -0
- package/src/client/filemaker-odata.ts +39 -6
- package/src/client/insert-builder.ts +4 -0
- package/src/client/query/query-builder.ts +198 -35
- package/src/client/query/response-processor.ts +15 -25
- package/src/client/query/types.ts +35 -6
- package/src/client/record-builder.ts +159 -32
- package/src/client/update-builder.ts +4 -1
- package/src/client/webhook-builder.ts +285 -0
- package/src/index.ts +6 -0
- package/src/orm/field-builders.ts +24 -2
- package/src/orm/table.ts +40 -48
- package/src/types.ts +62 -6
- package/src/validation.ts +58 -13
package/README.md
CHANGED
|
@@ -778,6 +778,173 @@ console.log(result.result.recordId);
|
|
|
778
778
|
|
|
779
779
|
**Note:** OData doesn't support script names with special characters (e.g., `@`, `&`, `/`) or script names beginning with a number. TypeScript will catch these at compile time.
|
|
780
780
|
|
|
781
|
+
## Webhooks
|
|
782
|
+
|
|
783
|
+
Webhooks allow you to receive notifications when data changes in your FileMaker database. The library provides a type-safe API for managing webhooks through the `db.webhook` property.
|
|
784
|
+
|
|
785
|
+
### Adding a Webhook
|
|
786
|
+
|
|
787
|
+
Create a new webhook to monitor a table for changes:
|
|
788
|
+
|
|
789
|
+
```typescript
|
|
790
|
+
// Basic webhook
|
|
791
|
+
const result = await db.webhook.add({
|
|
792
|
+
webhook: "https://example.com/webhook",
|
|
793
|
+
tableName: contactsTable,
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
// Access the created webhook ID
|
|
797
|
+
console.log(result.webHookResult.webHookID);
|
|
798
|
+
```
|
|
799
|
+
|
|
800
|
+
### Webhook Configuration Options
|
|
801
|
+
|
|
802
|
+
Webhooks support various configuration options:
|
|
803
|
+
|
|
804
|
+
```typescript
|
|
805
|
+
// With custom headers
|
|
806
|
+
const result = await db.webhook.add({
|
|
807
|
+
webhook: "https://example.com/webhook",
|
|
808
|
+
tableName: contactsTable,
|
|
809
|
+
headers: {
|
|
810
|
+
"X-Custom-Header": "value",
|
|
811
|
+
Authorization: "Bearer token",
|
|
812
|
+
},
|
|
813
|
+
notifySchemaChanges: true, // Notify when schema changes
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
// With field selection (using column references)
|
|
817
|
+
const result = await db.webhook.add({
|
|
818
|
+
webhook: "https://example.com/webhook",
|
|
819
|
+
tableName: contacts,
|
|
820
|
+
select: [contacts.name, contacts.email, contacts.PrimaryKey],
|
|
821
|
+
});
|
|
822
|
+
|
|
823
|
+
// With filtering (using filter expressions)
|
|
824
|
+
import { eq, gt } from "@proofkit/fmodata";
|
|
825
|
+
|
|
826
|
+
const result = await db.webhook.add({
|
|
827
|
+
webhook: "https://example.com/webhook",
|
|
828
|
+
tableName: contacts,
|
|
829
|
+
filter: eq(contacts.active, true),
|
|
830
|
+
select: [contacts.name, contacts.email],
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
// Complex filter example
|
|
834
|
+
const result = await db.webhook.add({
|
|
835
|
+
webhook: "https://example.com/webhook",
|
|
836
|
+
tableName: users,
|
|
837
|
+
filter: and(eq(users.active, true), gt(users.age, 18)),
|
|
838
|
+
select: [users.username, users.email],
|
|
839
|
+
});
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
**Webhook Configuration Properties:**
|
|
843
|
+
|
|
844
|
+
- `webhook` (required) - The URL to call when the webhook is triggered
|
|
845
|
+
- `tableName` (required) - The `FMTable` instance for the table to monitor
|
|
846
|
+
- `headers` (optional) - Custom headers to include in webhook requests
|
|
847
|
+
- `notifySchemaChanges` (optional) - Whether to notify on schema changes
|
|
848
|
+
- `select` (optional) - Field selection as a string or array of `Column` references
|
|
849
|
+
- `filter` (optional) - Filter expression (string or `FilterExpression`) to limit which records trigger the webhook
|
|
850
|
+
|
|
851
|
+
### Listing Webhooks
|
|
852
|
+
|
|
853
|
+
Get all webhooks configured for the database:
|
|
854
|
+
|
|
855
|
+
```typescript
|
|
856
|
+
const result = await db.webhook.list();
|
|
857
|
+
|
|
858
|
+
console.log(result.Status); // Status of the operation
|
|
859
|
+
console.log(result.WebHook); // Array of webhook configurations
|
|
860
|
+
|
|
861
|
+
result.WebHook.forEach((webhook) => {
|
|
862
|
+
console.log(`Webhook ${webhook.webHookID}:`);
|
|
863
|
+
console.log(` Table: ${webhook.tableName}`);
|
|
864
|
+
console.log(` URL: ${webhook.url}`);
|
|
865
|
+
console.log(` Notify Schema Changes: ${webhook.notifySchemaChanges}`);
|
|
866
|
+
console.log(` Select: ${webhook.select}`);
|
|
867
|
+
console.log(` Filter: ${webhook.filter}`);
|
|
868
|
+
console.log(` Pending Operations: ${webhook.pendingOperations.length}`);
|
|
869
|
+
});
|
|
870
|
+
```
|
|
871
|
+
|
|
872
|
+
### Getting a Webhook
|
|
873
|
+
|
|
874
|
+
Retrieve a specific webhook by ID:
|
|
875
|
+
|
|
876
|
+
```typescript
|
|
877
|
+
const webhook = await db.webhook.get(1);
|
|
878
|
+
|
|
879
|
+
console.log(webhook.webHookID);
|
|
880
|
+
console.log(webhook.tableName);
|
|
881
|
+
console.log(webhook.url);
|
|
882
|
+
console.log(webhook.headers);
|
|
883
|
+
console.log(webhook.notifySchemaChanges);
|
|
884
|
+
console.log(webhook.select);
|
|
885
|
+
console.log(webhook.filter);
|
|
886
|
+
console.log(webhook.pendingOperations);
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
### Removing a Webhook
|
|
890
|
+
|
|
891
|
+
Delete a webhook by ID:
|
|
892
|
+
|
|
893
|
+
```typescript
|
|
894
|
+
await db.webhook.remove(1);
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
### Invoking a Webhook
|
|
898
|
+
|
|
899
|
+
Manually trigger a webhook. This is useful for testing or triggering webhooks on-demand:
|
|
900
|
+
|
|
901
|
+
```typescript
|
|
902
|
+
// Invoke for all rows matching the webhook's filter
|
|
903
|
+
await db.webhook.invoke(1);
|
|
904
|
+
|
|
905
|
+
// Invoke for specific row IDs
|
|
906
|
+
await db.webhook.invoke(1, { rowIDs: [63, 61] });
|
|
907
|
+
```
|
|
908
|
+
|
|
909
|
+
### Complete Example
|
|
910
|
+
|
|
911
|
+
Here's a complete example of setting up and managing webhooks:
|
|
912
|
+
|
|
913
|
+
```typescript
|
|
914
|
+
import { eq } from "@proofkit/fmodata";
|
|
915
|
+
|
|
916
|
+
// Add a webhook to monitor active contacts
|
|
917
|
+
const addResult = await db.webhook.add({
|
|
918
|
+
webhook: "https://api.example.com/webhooks/contacts",
|
|
919
|
+
tableName: contacts,
|
|
920
|
+
headers: {
|
|
921
|
+
"X-API-Key": "your-api-key",
|
|
922
|
+
},
|
|
923
|
+
filter: eq(contacts.active, true),
|
|
924
|
+
select: [contacts.name, contacts.email, contacts.PrimaryKey],
|
|
925
|
+
notifySchemaChanges: false,
|
|
926
|
+
});
|
|
927
|
+
|
|
928
|
+
const webhookId = addResult.webHookResult.webHookID;
|
|
929
|
+
console.log(`Created webhook with ID: ${webhookId}`);
|
|
930
|
+
|
|
931
|
+
// List all webhooks
|
|
932
|
+
const listResult = await db.webhook.list();
|
|
933
|
+
console.log(`Total webhooks: ${listResult.WebHook.length}`);
|
|
934
|
+
|
|
935
|
+
// Get the webhook we just created
|
|
936
|
+
const webhook = await db.webhook.get(webhookId);
|
|
937
|
+
console.log(`Webhook URL: ${webhook.url}`);
|
|
938
|
+
|
|
939
|
+
// Manually invoke the webhook for specific records
|
|
940
|
+
await db.webhook.invoke(webhookId, { rowIDs: [1, 2, 3] });
|
|
941
|
+
|
|
942
|
+
// Remove the webhook when done
|
|
943
|
+
await db.webhook.remove(webhookId);
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
**Note:** Webhooks are triggered automatically by FileMaker when records matching the webhook's filter are created, updated, or deleted. The `invoke()` method allows you to manually trigger webhooks for testing or on-demand processing.
|
|
947
|
+
|
|
781
948
|
## Batch Operations
|
|
782
949
|
|
|
783
950
|
Batch operations allow you to execute multiple queries and operations together in a single request. All operations in a batch are executed atomically - they all succeed or all fail together. This is both more efficient (fewer network round-trips) and ensures data consistency across related operations.
|
|
@@ -1275,6 +1442,37 @@ const users = fmTableOccurrence(
|
|
|
1275
1442
|
);
|
|
1276
1443
|
```
|
|
1277
1444
|
|
|
1445
|
+
### Special Columns (ROWID and ROWMODID)
|
|
1446
|
+
|
|
1447
|
+
FileMaker provides special columns `ROWID` and `ROWMODID` that uniquely identify records and track modifications. These can be included in query responses when enabled.
|
|
1448
|
+
|
|
1449
|
+
Enable special columns at the database level:
|
|
1450
|
+
|
|
1451
|
+
```typescript
|
|
1452
|
+
const db = connection.database("MyDatabase", {
|
|
1453
|
+
includeSpecialColumns: true,
|
|
1454
|
+
});
|
|
1455
|
+
|
|
1456
|
+
const result = await db.from(users).list().execute();
|
|
1457
|
+
// result.data[0] will have ROWID and ROWMODID properties
|
|
1458
|
+
```
|
|
1459
|
+
|
|
1460
|
+
Override at the request level:
|
|
1461
|
+
|
|
1462
|
+
```typescript
|
|
1463
|
+
// Enable for this request only
|
|
1464
|
+
const result = await db.from(users).list().execute({
|
|
1465
|
+
includeSpecialColumns: true,
|
|
1466
|
+
});
|
|
1467
|
+
|
|
1468
|
+
// Disable for this request
|
|
1469
|
+
const result = await db.from(users).list().execute({
|
|
1470
|
+
includeSpecialColumns: false,
|
|
1471
|
+
});
|
|
1472
|
+
```
|
|
1473
|
+
|
|
1474
|
+
**Important:** Special columns are only included when no `$select` query is applied (per OData specification). When using `.select()`, special columns are excluded even if `includeSpecialColumns` is enabled.
|
|
1475
|
+
|
|
1278
1476
|
### Error Handling
|
|
1279
1477
|
|
|
1280
1478
|
All operations return a `Result` type with either `data` or `error`. The library provides rich error types that help you handle different error scenarios appropriately.
|
|
@@ -3,5 +3,8 @@ import { FMTable } from '../../orm/table.js';
|
|
|
3
3
|
* Gets default select fields from a table definition.
|
|
4
4
|
* Returns undefined if defaultSelect is "all".
|
|
5
5
|
* Automatically filters out container fields since they cannot be selected via $select.
|
|
6
|
+
*
|
|
7
|
+
* @param table - The table occurrence
|
|
8
|
+
* @param includeSpecialColumns - If true, includes ROWID and ROWMODID when defaultSelect is "schema"
|
|
6
9
|
*/
|
|
7
|
-
export declare function getDefaultSelectFields(table: FMTable<any, any> | undefined): string[] | undefined;
|
|
10
|
+
export declare function getDefaultSelectFields(table: FMTable<any, any> | undefined, includeSpecialColumns?: boolean): string[] | undefined;
|
|
@@ -7,14 +7,15 @@ function getContainerFieldNames(table) {
|
|
|
7
7
|
}
|
|
8
8
|
return baseTableConfig.containerFields;
|
|
9
9
|
}
|
|
10
|
-
function getDefaultSelectFields(table) {
|
|
10
|
+
function getDefaultSelectFields(table, includeSpecialColumns) {
|
|
11
11
|
if (!table) return void 0;
|
|
12
12
|
const defaultSelect = table[FMTable.Symbol.DefaultSelect];
|
|
13
13
|
const containerFields = getContainerFieldNames(table);
|
|
14
14
|
if (defaultSelect === "schema") {
|
|
15
15
|
const baseTableConfig = getBaseTableConfig(table);
|
|
16
16
|
const allFields = Object.keys(baseTableConfig.schema);
|
|
17
|
-
|
|
17
|
+
const fields = [...new Set(allFields.filter((f) => !containerFields.includes(f)))];
|
|
18
|
+
return fields;
|
|
18
19
|
}
|
|
19
20
|
if (Array.isArray(defaultSelect)) {
|
|
20
21
|
return [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"default-select.js","sources":["../../../../src/client/builders/default-select.ts"],"sourcesContent":["import type { FMTable } from \"../../orm/table\";\nimport { FMTable as FMTableClass } from \"../../orm/table\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { getBaseTableConfig } from \"../../orm/table\";\nimport { isColumn } from \"../../orm/column\";\n\n/**\n * Helper function to get container field names from a table.\n * Container fields cannot be selected via $select in FileMaker OData API.\n */\nfunction getContainerFieldNames(table: FMTable<any, any>): string[] {\n const baseTableConfig = getBaseTableConfig(table);\n if (!baseTableConfig || !baseTableConfig.containerFields) {\n return [];\n }\n return baseTableConfig.containerFields as string[];\n}\n\n/**\n * Gets default select fields from a table definition.\n * Returns undefined if defaultSelect is \"all\".\n * Automatically filters out container fields since they cannot be selected via $select.\n */\nexport function getDefaultSelectFields(\n table: FMTable<any, any> | undefined,\n): string[] | undefined {\n if (!table) return undefined;\n\n const defaultSelect = (table as any)[FMTableClass.Symbol.DefaultSelect];\n const containerFields = getContainerFieldNames(table);\n\n if (defaultSelect === \"schema\") {\n const baseTableConfig = getBaseTableConfig(table);\n const allFields = Object.keys(baseTableConfig.schema);\n // Filter out container fields\n
|
|
1
|
+
{"version":3,"file":"default-select.js","sources":["../../../../src/client/builders/default-select.ts"],"sourcesContent":["import type { FMTable } from \"../../orm/table\";\nimport { FMTable as FMTableClass } from \"../../orm/table\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { getBaseTableConfig } from \"../../orm/table\";\nimport { isColumn } from \"../../orm/column\";\n\n/**\n * Helper function to get container field names from a table.\n * Container fields cannot be selected via $select in FileMaker OData API.\n */\nfunction getContainerFieldNames(table: FMTable<any, any>): string[] {\n const baseTableConfig = getBaseTableConfig(table);\n if (!baseTableConfig || !baseTableConfig.containerFields) {\n return [];\n }\n return baseTableConfig.containerFields as string[];\n}\n\n/**\n * Gets default select fields from a table definition.\n * Returns undefined if defaultSelect is \"all\".\n * Automatically filters out container fields since they cannot be selected via $select.\n * \n * @param table - The table occurrence\n * @param includeSpecialColumns - If true, includes ROWID and ROWMODID when defaultSelect is \"schema\"\n */\nexport function getDefaultSelectFields(\n table: FMTable<any, any> | undefined,\n includeSpecialColumns?: boolean,\n): string[] | undefined {\n if (!table) return undefined;\n\n const defaultSelect = (table as any)[FMTableClass.Symbol.DefaultSelect];\n const containerFields = getContainerFieldNames(table);\n\n if (defaultSelect === \"schema\") {\n const baseTableConfig = getBaseTableConfig(table);\n const allFields = Object.keys(baseTableConfig.schema);\n // Filter out container fields\n const fields = [...new Set(allFields.filter((f) => !containerFields.includes(f)))];\n \n // Add special columns if requested\n if (includeSpecialColumns) {\n fields.push(\"ROWID\", \"ROWMODID\");\n }\n \n return fields;\n }\n\n if (Array.isArray(defaultSelect)) {\n // Filter out container fields\n return [\n ...new Set(defaultSelect.filter((f) => !containerFields.includes(f))),\n ];\n }\n\n // Check if defaultSelect is a Record<string, Column> (resolved from function)\n if (\n typeof defaultSelect === \"object\" &&\n defaultSelect !== null &&\n !Array.isArray(defaultSelect)\n ) {\n // Extract field names from Column instances\n const fieldNames: string[] = [];\n for (const value of Object.values(defaultSelect)) {\n if (isColumn(value)) {\n fieldNames.push(value.fieldName);\n }\n }\n if (fieldNames.length > 0) {\n // Filter out container fields\n return [\n ...new Set(fieldNames.filter((f) => !containerFields.includes(f))),\n ];\n }\n }\n\n // defaultSelect is \"all\" or undefined\n return undefined;\n}\n"],"names":["FMTableClass"],"mappings":";;AAUA,SAAS,uBAAuB,OAAoC;AAC5D,QAAA,kBAAkB,mBAAmB,KAAK;AAChD,MAAI,CAAC,mBAAmB,CAAC,gBAAgB,iBAAiB;AACxD,WAAO,CAAC;AAAA,EAAA;AAEV,SAAO,gBAAgB;AACzB;AAUgB,SAAA,uBACd,OACA,uBACsB;AAClB,MAAA,CAAC,MAAc,QAAA;AAEnB,QAAM,gBAAiB,MAAcA,QAAa,OAAO,aAAa;AAChE,QAAA,kBAAkB,uBAAuB,KAAK;AAEpD,MAAI,kBAAkB,UAAU;AACxB,UAAA,kBAAkB,mBAAmB,KAAK;AAChD,UAAM,YAAY,OAAO,KAAK,gBAAgB,MAAM;AAEpD,UAAM,SAAS,CAAC,GAAG,IAAI,IAAI,UAAU,OAAO,CAAC,MAAM,CAAC,gBAAgB,SAAS,CAAC,CAAC,CAAC,CAAC;AAO1E,WAAA;AAAA,EAAA;AAGL,MAAA,MAAM,QAAQ,aAAa,GAAG;AAEzB,WAAA;AAAA,MACL,GAAG,IAAI,IAAI,cAAc,OAAO,CAAC,MAAM,CAAC,gBAAgB,SAAS,CAAC,CAAC,CAAC;AAAA,IACtE;AAAA,EAAA;AAKA,MAAA,OAAO,kBAAkB,YACzB,kBAAkB,QAClB,CAAC,MAAM,QAAQ,aAAa,GAC5B;AAEA,UAAM,aAAuB,CAAC;AAC9B,eAAW,SAAS,OAAO,OAAO,aAAa,GAAG;AAC5C,UAAA,SAAS,KAAK,GAAG;AACR,mBAAA,KAAK,MAAM,SAAS;AAAA,MAAA;AAAA,IACjC;AAEE,QAAA,WAAW,SAAS,GAAG;AAElB,aAAA;AAAA,QACL,GAAG,IAAI,IAAI,WAAW,OAAO,CAAC,MAAM,CAAC,gBAAgB,SAAS,CAAC,CAAC,CAAC;AAAA,MACnE;AAAA,IAAA;AAAA,EACF;AAIK,SAAA;AACT;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"expand-builder.js","sources":["../../../../src/client/builders/expand-builder.ts"],"sourcesContent":["import { QueryOptions } from \"odata-query\";\nimport buildQuery from \"odata-query\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { FMTable } from \"../../orm/table\";\nimport {\n getBaseTableConfig,\n getTableName,\n getNavigationPaths,\n} from \"../../orm/table\";\nimport type { ExpandValidationConfig } from \"../../validation\";\nimport type { ExpandConfig } from \"./shared-types\";\nimport { formatSelectFields } from \"./select-utils\";\nimport { getDefaultSelectFields } from \"./default-select\";\nimport { InternalLogger } from \"../../logger\";\n\n/**\n * Builds OData expand query strings and validation configs.\n * Handles nested expands recursively and transforms relation names to FMTIDs\n * when using entity IDs.\n */\nexport class ExpandBuilder {\n constructor(\n private useEntityIds: boolean,\n private logger: InternalLogger,\n ) {}\n\n /**\n * Builds OData $expand query string from expand configurations.\n */\n buildExpandString(configs: ExpandConfig[]): string {\n if (configs.length === 0) return \"\";\n\n return configs.map((config) => this.buildSingleExpand(config)).join(\",\");\n }\n\n /**\n * Builds validation configs for expanded navigation properties.\n */\n buildValidationConfigs(configs: ExpandConfig[]): ExpandValidationConfig[] {\n return configs.map((config) => {\n const targetTable = config.targetTable;\n\n let targetSchema: Record<string, StandardSchemaV1> | undefined;\n if (targetTable) {\n const baseTableConfig = getBaseTableConfig(targetTable);\n const containerFields = baseTableConfig.containerFields || [];\n\n // Filter out container fields from schema\n const schema = { ...baseTableConfig.schema };\n for (const containerField of containerFields) {\n delete schema[containerField as string];\n }\n\n targetSchema = schema;\n }\n\n const selectedFields = config.options?.select\n ? Array.isArray(config.options.select)\n ? config.options.select.map(String)\n : [String(config.options.select)]\n : undefined;\n\n // Recursively build validation configs for nested expands\n const nestedExpands = config.nestedExpandConfigs\n ? this.buildValidationConfigs(config.nestedExpandConfigs)\n : undefined;\n\n return {\n relation: config.relation,\n targetSchema,\n targetTable,\n table: targetTable,\n selectedFields,\n nestedExpands,\n };\n });\n }\n\n /**\n * Process an expand() call and return the expand config.\n * Used by both QueryBuilder and RecordBuilder to eliminate duplication.\n *\n * @param targetTable - The target table to expand to\n * @param sourceTable - The source table (for validation)\n * @param callback - Optional callback to configure the expand query\n * @param builderFactory - Function that creates a QueryBuilder for the target table\n * @returns ExpandConfig to add to the builder's expandConfigs array\n */\n processExpand<TargetTable extends FMTable<any, any>, Builder = any>(\n targetTable: TargetTable,\n sourceTable: FMTable<any, any> | undefined,\n callback?: (builder: Builder) => Builder,\n builderFactory?: () => Builder,\n ): ExpandConfig {\n // Extract name and validate\n const relationName = getTableName(targetTable);\n\n // Runtime validation: Check if relation name is in navigationPaths\n if (sourceTable) {\n const navigationPaths = getNavigationPaths(sourceTable);\n if (navigationPaths && !navigationPaths.includes(relationName)) {\n this.logger.warn(\n `Cannot expand to \"${relationName}\". Valid navigation paths: ${navigationPaths.length > 0 ? navigationPaths.join(\", \") : \"none\"}`,\n );\n }\n }\n\n if (callback && builderFactory) {\n // Create a new QueryBuilder for the target table\n const targetBuilder = builderFactory();\n\n // Pass to callback and get configured builder\n const configuredBuilder = callback(targetBuilder);\n\n // Extract the builder's query options\n const expandOptions: Partial<QueryOptions<any>> = {\n ...(configuredBuilder as any).queryOptions,\n };\n\n // If callback didn't provide select, apply defaultSelect from target table\n if (!expandOptions.select) {\n const defaultFields = getDefaultSelectFields(targetTable);\n if (defaultFields) {\n expandOptions.select = defaultFields;\n }\n }\n\n // If the configured builder has nested expands, we need to include them\n const nestedExpandConfigs = (configuredBuilder as any).expandConfigs;\n if (nestedExpandConfigs?.length > 0) {\n // Build nested expand string from the configured builder's expand configs\n const nestedExpandString = this.buildExpandString(nestedExpandConfigs);\n if (nestedExpandString) {\n // Add nested expand to options\n expandOptions.expand = nestedExpandString as any;\n }\n }\n\n return {\n relation: relationName,\n options: expandOptions,\n targetTable,\n nestedExpandConfigs: nestedExpandConfigs?.length > 0 ? nestedExpandConfigs : undefined,\n };\n } else {\n // Simple expand without callback - apply defaultSelect if available\n const defaultFields = getDefaultSelectFields(targetTable);\n if (defaultFields) {\n return {\n relation: relationName,\n options: { select: defaultFields },\n targetTable,\n };\n } else {\n return {\n relation: relationName,\n targetTable,\n };\n }\n }\n }\n\n /**\n * Builds a single expand string with its options.\n */\n private buildSingleExpand(config: ExpandConfig): string {\n const relationName = this.resolveRelationName(config);\n const parts = this.buildExpandParts(config);\n\n if (parts.length === 0) {\n return relationName;\n }\n\n return `${relationName}(${parts.join(\";\")})`;\n }\n\n /**\n * Resolves relation name, using FMTID if entity IDs are enabled.\n */\n private resolveRelationName(config: ExpandConfig): string {\n if (!this.useEntityIds) {\n return config.relation;\n }\n\n const targetTable = config.targetTable;\n if (targetTable && FMTable.Symbol.EntityId in targetTable) {\n const tableId = (targetTable as any)[FMTable.Symbol.EntityId] as\n | `FMTID:${string}`\n | undefined;\n if (tableId) {\n return tableId;\n }\n }\n\n return config.relation;\n }\n\n /**\n * Builds expand parts (select, filter, orderBy, etc.) for a single expand.\n */\n private buildExpandParts(config: ExpandConfig): string[] {\n if (!config.options || Object.keys(config.options).length === 0) {\n return [];\n }\n\n const parts: string[] = [];\n const opts = config.options;\n\n if (opts.select) {\n const selectArray = Array.isArray(opts.select)\n ? opts.select.map(String)\n : [String(opts.select)];\n const selectFields = formatSelectFields(\n selectArray,\n config.targetTable,\n this.useEntityIds,\n );\n parts.push(`$select=${selectFields}`);\n }\n\n if (opts.filter) {\n const filterQuery = buildQuery({ filter: opts.filter });\n const match = filterQuery.match(/\\$filter=([^&]+)/);\n if (match) parts.push(`$filter=${match[1]}`);\n }\n\n if (opts.orderBy) {\n const orderByValue = Array.isArray(opts.orderBy)\n ? opts.orderBy.join(\",\")\n : String(opts.orderBy);\n parts.push(`$orderby=${orderByValue}`);\n }\n\n if (opts.top !== undefined) parts.push(`$top=${opts.top}`);\n if (opts.skip !== undefined) parts.push(`$skip=${opts.skip}`);\n\n if (opts.expand) {\n if (typeof opts.expand === \"string\") {\n parts.push(`$expand=${opts.expand}`);\n }\n }\n\n return parts;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"expand-builder.js","sources":["../../../../src/client/builders/expand-builder.ts"],"sourcesContent":["import { QueryOptions } from \"odata-query\";\nimport buildQuery from \"odata-query\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport { FMTable } from \"../../orm/table\";\nimport {\n getBaseTableConfig,\n getTableName,\n getNavigationPaths,\n} from \"../../orm/table\";\nimport type { ExpandValidationConfig } from \"../../validation\";\nimport type { ExpandConfig } from \"./shared-types\";\nimport { formatSelectFields } from \"./select-utils\";\nimport { getDefaultSelectFields } from \"./default-select\";\nimport { InternalLogger } from \"../../logger\";\n\n/**\n * Builds OData expand query strings and validation configs.\n * Handles nested expands recursively and transforms relation names to FMTIDs\n * when using entity IDs.\n */\nexport class ExpandBuilder {\n constructor(\n private useEntityIds: boolean,\n private logger: InternalLogger,\n ) {}\n\n /**\n * Builds OData $expand query string from expand configurations.\n */\n buildExpandString(configs: ExpandConfig[]): string {\n if (configs.length === 0) return \"\";\n\n return configs.map((config) => this.buildSingleExpand(config)).join(\",\");\n }\n\n /**\n * Builds validation configs for expanded navigation properties.\n */\n buildValidationConfigs(configs: ExpandConfig[]): ExpandValidationConfig[] {\n return configs.map((config) => {\n const targetTable = config.targetTable;\n\n let targetSchema: Partial<Record<string, StandardSchemaV1>> | undefined;\n if (targetTable) {\n const baseTableConfig = getBaseTableConfig(targetTable);\n const containerFields = baseTableConfig.containerFields || [];\n\n // Filter out container fields from schema\n const schema = { ...baseTableConfig.schema };\n for (const containerField of containerFields) {\n delete schema[containerField as string];\n }\n\n targetSchema = schema;\n }\n\n const selectedFields = config.options?.select\n ? Array.isArray(config.options.select)\n ? config.options.select.map(String)\n : [String(config.options.select)]\n : undefined;\n\n // Recursively build validation configs for nested expands\n const nestedExpands = config.nestedExpandConfigs\n ? this.buildValidationConfigs(config.nestedExpandConfigs)\n : undefined;\n\n return {\n relation: config.relation,\n targetSchema,\n targetTable,\n table: targetTable,\n selectedFields,\n nestedExpands,\n };\n });\n }\n\n /**\n * Process an expand() call and return the expand config.\n * Used by both QueryBuilder and RecordBuilder to eliminate duplication.\n *\n * @param targetTable - The target table to expand to\n * @param sourceTable - The source table (for validation)\n * @param callback - Optional callback to configure the expand query\n * @param builderFactory - Function that creates a QueryBuilder for the target table\n * @returns ExpandConfig to add to the builder's expandConfigs array\n */\n processExpand<TargetTable extends FMTable<any, any>, Builder = any>(\n targetTable: TargetTable,\n sourceTable: FMTable<any, any> | undefined,\n callback?: (builder: Builder) => Builder,\n builderFactory?: () => Builder,\n ): ExpandConfig {\n // Extract name and validate\n const relationName = getTableName(targetTable);\n\n // Runtime validation: Check if relation name is in navigationPaths\n if (sourceTable) {\n const navigationPaths = getNavigationPaths(sourceTable);\n if (navigationPaths && !navigationPaths.includes(relationName)) {\n this.logger.warn(\n `Cannot expand to \"${relationName}\". Valid navigation paths: ${navigationPaths.length > 0 ? navigationPaths.join(\", \") : \"none\"}`,\n );\n }\n }\n\n if (callback && builderFactory) {\n // Create a new QueryBuilder for the target table\n const targetBuilder = builderFactory();\n\n // Pass to callback and get configured builder\n const configuredBuilder = callback(targetBuilder);\n\n // Extract the builder's query options\n const expandOptions: Partial<QueryOptions<any>> = {\n ...(configuredBuilder as any).queryOptions,\n };\n\n // If callback didn't provide select, apply defaultSelect from target table\n if (!expandOptions.select) {\n const defaultFields = getDefaultSelectFields(targetTable);\n if (defaultFields) {\n expandOptions.select = defaultFields;\n }\n }\n\n // If the configured builder has nested expands, we need to include them\n const nestedExpandConfigs = (configuredBuilder as any).expandConfigs;\n if (nestedExpandConfigs?.length > 0) {\n // Build nested expand string from the configured builder's expand configs\n const nestedExpandString = this.buildExpandString(nestedExpandConfigs);\n if (nestedExpandString) {\n // Add nested expand to options\n expandOptions.expand = nestedExpandString as any;\n }\n }\n\n return {\n relation: relationName,\n options: expandOptions,\n targetTable,\n nestedExpandConfigs: nestedExpandConfigs?.length > 0 ? nestedExpandConfigs : undefined,\n };\n } else {\n // Simple expand without callback - apply defaultSelect if available\n const defaultFields = getDefaultSelectFields(targetTable);\n if (defaultFields) {\n return {\n relation: relationName,\n options: { select: defaultFields },\n targetTable,\n };\n } else {\n return {\n relation: relationName,\n targetTable,\n };\n }\n }\n }\n\n /**\n * Builds a single expand string with its options.\n */\n private buildSingleExpand(config: ExpandConfig): string {\n const relationName = this.resolveRelationName(config);\n const parts = this.buildExpandParts(config);\n\n if (parts.length === 0) {\n return relationName;\n }\n\n return `${relationName}(${parts.join(\";\")})`;\n }\n\n /**\n * Resolves relation name, using FMTID if entity IDs are enabled.\n */\n private resolveRelationName(config: ExpandConfig): string {\n if (!this.useEntityIds) {\n return config.relation;\n }\n\n const targetTable = config.targetTable;\n if (targetTable && FMTable.Symbol.EntityId in targetTable) {\n const tableId = (targetTable as any)[FMTable.Symbol.EntityId] as\n | `FMTID:${string}`\n | undefined;\n if (tableId) {\n return tableId;\n }\n }\n\n return config.relation;\n }\n\n /**\n * Builds expand parts (select, filter, orderBy, etc.) for a single expand.\n */\n private buildExpandParts(config: ExpandConfig): string[] {\n if (!config.options || Object.keys(config.options).length === 0) {\n return [];\n }\n\n const parts: string[] = [];\n const opts = config.options;\n\n if (opts.select) {\n const selectArray = Array.isArray(opts.select)\n ? opts.select.map(String)\n : [String(opts.select)];\n const selectFields = formatSelectFields(\n selectArray,\n config.targetTable,\n this.useEntityIds,\n );\n parts.push(`$select=${selectFields}`);\n }\n\n if (opts.filter) {\n const filterQuery = buildQuery({ filter: opts.filter });\n const match = filterQuery.match(/\\$filter=([^&]+)/);\n if (match) parts.push(`$filter=${match[1]}`);\n }\n\n if (opts.orderBy) {\n const orderByValue = Array.isArray(opts.orderBy)\n ? opts.orderBy.join(\",\")\n : String(opts.orderBy);\n parts.push(`$orderby=${orderByValue}`);\n }\n\n if (opts.top !== undefined) parts.push(`$top=${opts.top}`);\n if (opts.skip !== undefined) parts.push(`$skip=${opts.skip}`);\n\n if (opts.expand) {\n if (typeof opts.expand === \"string\") {\n parts.push(`$expand=${opts.expand}`);\n }\n }\n\n return parts;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-string-builder.js","sources":["../../../../src/client/builders/query-string-builder.ts"],"sourcesContent":["import type { FMTable } from \"../../orm/table\";\nimport { ExpandBuilder } from \"./expand-builder\";\nimport type { ExpandConfig } from \"./shared-types\";\nimport { formatSelectFields } from \"./select-utils\";\nimport { InternalLogger } from \"../../logger\";\n\n/**\n * Builds OData query string for $select and $expand parameters.\n * Used by both QueryBuilder and RecordBuilder to eliminate duplication.\n *\n * @param config - Configuration object\n * @returns Query string starting with ? or empty string if no parameters\n */\nexport function buildSelectExpandQueryString(config: {\n selectedFields?: string[];\n expandConfigs: ExpandConfig[];\n table?: FMTable<any, any>;\n useEntityIds: boolean;\n logger: InternalLogger;\n}): string {\n const parts: string[] = [];\n const expandBuilder = new ExpandBuilder(config.useEntityIds, config.logger);\n\n // Build $select\n if (config.selectedFields && config.selectedFields.length > 0) {\n const selectString = formatSelectFields(\n config.selectedFields,\n config.table,\n config.useEntityIds,\n );\n if (selectString) {\n parts.push(`$select=${selectString}`);\n }\n }\n\n // Build $expand\n const expandString = expandBuilder.buildExpandString(config.expandConfigs);\n if (expandString) {\n parts.push(`$expand=${expandString}`);\n }\n\n return parts.length > 0 ? `?${parts.join(\"&\")}` : \"\";\n}\n"],"names":[],"mappings":";;AAaO,SAAS,6BAA6B,
|
|
1
|
+
{"version":3,"file":"query-string-builder.js","sources":["../../../../src/client/builders/query-string-builder.ts"],"sourcesContent":["import type { FMTable } from \"../../orm/table\";\nimport { ExpandBuilder } from \"./expand-builder\";\nimport type { ExpandConfig } from \"./shared-types\";\nimport { formatSelectFields } from \"./select-utils\";\nimport { InternalLogger } from \"../../logger\";\n\n/**\n * Builds OData query string for $select and $expand parameters.\n * Used by both QueryBuilder and RecordBuilder to eliminate duplication.\n *\n * @param config - Configuration object\n * @returns Query string starting with ? or empty string if no parameters\n */\nexport function buildSelectExpandQueryString(config: {\n selectedFields?: string[];\n expandConfigs: ExpandConfig[];\n table?: FMTable<any, any>;\n useEntityIds: boolean;\n logger: InternalLogger;\n includeSpecialColumns?: boolean;\n}): string {\n const parts: string[] = [];\n const expandBuilder = new ExpandBuilder(config.useEntityIds, config.logger);\n\n // Build $select\n if (config.selectedFields && config.selectedFields.length > 0) {\n // Important: do NOT implicitly add system columns (ROWID/ROWMODID) here.\n // - `includeSpecialColumns` controls the Prefer header + response parsing, but should not\n // mutate/expand an explicit `$select` (e.g. when the user calls `.select({ ... })`).\n // - If system columns are desired with `.select()`, they must be explicitly included via\n // the `systemColumns` argument, which will already have added them to `selectedFields`.\n const selectString = formatSelectFields(\n config.selectedFields,\n config.table,\n config.useEntityIds,\n );\n if (selectString) {\n parts.push(`$select=${selectString}`);\n }\n }\n\n // Build $expand\n const expandString = expandBuilder.buildExpandString(config.expandConfigs);\n if (expandString) {\n parts.push(`$expand=${expandString}`);\n }\n\n return parts.length > 0 ? `?${parts.join(\"&\")}` : \"\";\n}\n"],"names":[],"mappings":";;AAaO,SAAS,6BAA6B,QAOlC;AACT,QAAM,QAAkB,CAAC;AACzB,QAAM,gBAAgB,IAAI,cAAc,OAAO,cAAc,OAAO,MAAM;AAG1E,MAAI,OAAO,kBAAkB,OAAO,eAAe,SAAS,GAAG;AAM7D,UAAM,eAAe;AAAA,MACnB,OAAO;AAAA,MACP,OAAO;AAAA,MACP,OAAO;AAAA,IACT;AACA,QAAI,cAAc;AACV,YAAA,KAAK,WAAW,YAAY,EAAE;AAAA,IAAA;AAAA,EACtC;AAIF,QAAM,eAAe,cAAc,kBAAkB,OAAO,aAAa;AACzE,MAAI,cAAc;AACV,UAAA,KAAK,WAAW,YAAY,EAAE;AAAA,EAAA;AAG/B,SAAA,MAAM,SAAS,IAAI,IAAI,MAAM,KAAK,GAAG,CAAC,KAAK;AACpD;"}
|
|
@@ -11,6 +11,7 @@ export interface ProcessResponseConfig {
|
|
|
11
11
|
expandValidationConfigs?: ExpandValidationConfig[];
|
|
12
12
|
skipValidation?: boolean;
|
|
13
13
|
useEntityIds?: boolean;
|
|
14
|
+
includeSpecialColumns?: boolean;
|
|
14
15
|
fieldMapping?: Record<string, string>;
|
|
15
16
|
}
|
|
16
17
|
/**
|
|
@@ -36,6 +37,7 @@ export declare function processQueryResponse<T>(response: any, config: {
|
|
|
36
37
|
expandConfigs: ExpandConfig[];
|
|
37
38
|
skipValidation?: boolean;
|
|
38
39
|
useEntityIds?: boolean;
|
|
40
|
+
includeSpecialColumns?: boolean;
|
|
39
41
|
fieldMapping?: Record<string, string>;
|
|
40
42
|
logger: InternalLogger;
|
|
41
43
|
}): Promise<Result<any>>;
|
|
@@ -12,6 +12,7 @@ async function processODataResponse(rawResponse, config) {
|
|
|
12
12
|
expandValidationConfigs,
|
|
13
13
|
skipValidation,
|
|
14
14
|
useEntityIds,
|
|
15
|
+
includeSpecialColumns,
|
|
15
16
|
fieldMapping
|
|
16
17
|
} = config;
|
|
17
18
|
let response = rawResponse;
|
|
@@ -41,7 +42,8 @@ async function processODataResponse(rawResponse, config) {
|
|
|
41
42
|
schema,
|
|
42
43
|
selectedFields,
|
|
43
44
|
expandValidationConfigs,
|
|
44
|
-
singleMode
|
|
45
|
+
singleMode,
|
|
46
|
+
includeSpecialColumns
|
|
45
47
|
);
|
|
46
48
|
if (!validation2.valid) {
|
|
47
49
|
return { data: void 0, error: validation2.error };
|
|
@@ -58,7 +60,8 @@ async function processODataResponse(rawResponse, config) {
|
|
|
58
60
|
response,
|
|
59
61
|
schema,
|
|
60
62
|
selectedFields,
|
|
61
|
-
expandValidationConfigs
|
|
63
|
+
expandValidationConfigs,
|
|
64
|
+
includeSpecialColumns
|
|
62
65
|
);
|
|
63
66
|
if (!validation.valid) {
|
|
64
67
|
return { data: void 0, error: validation.error };
|
|
@@ -140,6 +143,7 @@ async function processQueryResponse(response, config) {
|
|
|
140
143
|
expandConfigs,
|
|
141
144
|
skipValidation,
|
|
142
145
|
useEntityIds,
|
|
146
|
+
includeSpecialColumns,
|
|
143
147
|
fieldMapping,
|
|
144
148
|
logger
|
|
145
149
|
} = config;
|
|
@@ -153,7 +157,8 @@ async function processQueryResponse(response, config) {
|
|
|
153
157
|
selectedFields,
|
|
154
158
|
expandValidationConfigs,
|
|
155
159
|
skipValidation,
|
|
156
|
-
useEntityIds
|
|
160
|
+
useEntityIds,
|
|
161
|
+
includeSpecialColumns
|
|
157
162
|
});
|
|
158
163
|
if (processedResponse.data && fieldMapping && Object.keys(fieldMapping).length > 0) {
|
|
159
164
|
processedResponse = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"response-processor.js","sources":["../../../../src/client/builders/response-processor.ts"],"sourcesContent":["import type { FMTable } from \"../../orm/table\";\nimport type { Result } from \"../../types\";\nimport type { ExpandValidationConfig } from \"../../validation\";\nimport { validateSingleResponse, validateListResponse } from \"../../validation\";\nimport { transformResponseFields } from \"../../transform\";\nimport { RecordCountMismatchError } from \"../../errors\";\nimport { getBaseTableConfig } from \"../../orm/table\";\nimport { ExpandBuilder } from \"./expand-builder\";\nimport type { ExpandConfig } from \"./shared-types\";\nimport { InternalLogger } from \"../../logger\";\n\nexport interface ProcessResponseConfig {\n table?: FMTable<any, any>;\n schema?: Record<string, any>;\n singleMode: \"exact\" | \"maybe\" | false;\n selectedFields?: string[];\n expandValidationConfigs?: ExpandValidationConfig[];\n skipValidation?: boolean;\n useEntityIds?: boolean;\n // Mapping from field names to output keys (for renamed fields in select)\n fieldMapping?: Record<string, string>;\n}\n\n/**\n * Processes OData response with transformation and validation.\n * Shared by QueryBuilder and RecordBuilder.\n */\nexport async function processODataResponse<T>(\n rawResponse: any,\n config: ProcessResponseConfig,\n): Promise<Result<T>> {\n const {\n table,\n schema,\n singleMode,\n selectedFields,\n expandValidationConfigs,\n skipValidation,\n useEntityIds,\n fieldMapping,\n } = config;\n\n // Transform field IDs back to names if using entity IDs\n let response = rawResponse;\n if (table && useEntityIds) {\n response = transformResponseFields(\n response,\n table,\n expandValidationConfigs,\n );\n }\n\n // Fast path: skip validation\n if (skipValidation) {\n const result = extractRecords(response, singleMode);\n // Rename fields AFTER extraction (but before returning)\n if (result.data && fieldMapping && Object.keys(fieldMapping).length > 0) {\n if (result.error) {\n return { data: undefined, error: result.error } as Result<T>;\n }\n return {\n data: renameFieldsInResponse(result.data, fieldMapping) as T,\n error: undefined,\n };\n }\n return result as Result<T>;\n }\n\n // Validation path\n if (singleMode !== false) {\n const validation = await validateSingleResponse<any>(\n response,\n schema,\n selectedFields as any,\n expandValidationConfigs,\n singleMode,\n );\n\n if (!validation.valid) {\n return { data: undefined, error: validation.error };\n }\n\n // Rename fields AFTER validation completes\n if (fieldMapping && Object.keys(fieldMapping).length > 0) {\n return {\n data: renameFieldsInResponse(validation.data, fieldMapping) as T,\n error: undefined,\n };\n }\n\n return { data: validation.data as T, error: undefined };\n }\n\n const validation = await validateListResponse<any>(\n response,\n schema,\n selectedFields as any,\n expandValidationConfigs,\n );\n\n if (!validation.valid) {\n return { data: undefined, error: validation.error };\n }\n\n // Rename fields AFTER validation completes\n if (fieldMapping && Object.keys(fieldMapping).length > 0) {\n return {\n data: renameFieldsInResponse(validation.data, fieldMapping) as T,\n error: undefined,\n };\n }\n\n return { data: validation.data as T, error: undefined };\n}\n\n/**\n * Extracts records from response without validation.\n */\nfunction extractRecords<T>(\n response: any,\n singleMode: \"exact\" | \"maybe\" | false,\n): Result<T> {\n if (singleMode === false) {\n const records = response.value ?? [];\n return { data: records as T, error: undefined };\n }\n\n const records = response.value ?? [response];\n const count = Array.isArray(records) ? records.length : 1;\n\n if (count > 1) {\n return {\n data: undefined,\n error: new RecordCountMismatchError(\n singleMode === \"exact\" ? \"one\" : \"at-most-one\",\n count,\n ),\n };\n }\n\n if (count === 0) {\n if (singleMode === \"exact\") {\n return { data: undefined, error: new RecordCountMismatchError(\"one\", 0) };\n }\n return { data: null as T, error: undefined };\n }\n\n const record = Array.isArray(records) ? records[0] : records;\n return { data: record as T, error: undefined };\n}\n\n/**\n * Gets schema from a table occurrence, excluding container fields.\n * Container fields are never returned in regular responses (only via getSingleField).\n */\nexport function getSchemaFromTable(\n table: FMTable<any, any> | undefined,\n): Record<string, any> | undefined {\n if (!table) return undefined;\n const baseTableConfig = getBaseTableConfig(table);\n const containerFields = baseTableConfig.containerFields || [];\n\n // Filter out container fields from schema\n const schema = { ...baseTableConfig.schema };\n for (const containerField of containerFields) {\n delete schema[containerField as string];\n }\n\n return schema;\n}\n\n/**\n * Renames fields in response data according to the field mapping.\n * Used when select() is called with renamed fields (e.g., { userEmail: users.email }).\n */\nfunction renameFieldsInResponse(\n data: any,\n fieldMapping: Record<string, string>,\n): any {\n if (!data || typeof data !== \"object\") {\n return data;\n }\n\n // Handle array responses\n if (Array.isArray(data)) {\n return data.map((item) => renameFieldsInResponse(item, fieldMapping));\n }\n\n // Handle OData list response structure\n if (\"value\" in data && Array.isArray(data.value)) {\n return {\n ...data,\n value: data.value.map((item: any) =>\n renameFieldsInResponse(item, fieldMapping),\n ),\n };\n }\n\n // Handle single record\n const renamed: Record<string, any> = {};\n for (const [key, value] of Object.entries(data)) {\n // Check if this field should be renamed\n const outputKey = fieldMapping[key];\n if (outputKey) {\n renamed[outputKey] = value;\n } else {\n renamed[key] = value;\n }\n }\n return renamed;\n}\n\n/**\n * Processes query response with expand configs.\n * This is a convenience wrapper that builds validation configs from expand configs.\n */\nexport async function processQueryResponse<T>(\n response: any,\n config: {\n occurrence?: FMTable<any, any>;\n singleMode: \"exact\" | \"maybe\" | false;\n queryOptions: { select?: (keyof T)[] | string[] };\n expandConfigs: ExpandConfig[];\n skipValidation?: boolean;\n useEntityIds?: boolean;\n // Mapping from field names to output keys (for renamed fields in select)\n fieldMapping?: Record<string, string>;\n logger: InternalLogger;\n },\n): Promise<Result<any>> {\n const {\n occurrence,\n singleMode,\n queryOptions,\n expandConfigs,\n skipValidation,\n useEntityIds,\n fieldMapping,\n logger,\n } = config;\n\n const expandBuilder = new ExpandBuilder(useEntityIds ?? false, logger);\n const expandValidationConfigs =\n expandBuilder.buildValidationConfigs(expandConfigs);\n\n const selectedFields = queryOptions.select\n ? Array.isArray(queryOptions.select)\n ? queryOptions.select.map(String)\n : [String(queryOptions.select)]\n : undefined;\n\n // Process the response first\n let processedResponse = await processODataResponse(response, {\n table: occurrence,\n schema: getSchemaFromTable(occurrence),\n singleMode,\n selectedFields,\n expandValidationConfigs,\n skipValidation,\n useEntityIds,\n });\n\n // Rename fields if field mapping is provided (for renamed fields in select)\n if (\n processedResponse.data &&\n fieldMapping &&\n Object.keys(fieldMapping).length > 0\n ) {\n processedResponse = {\n ...processedResponse,\n data: renameFieldsInResponse(processedResponse.data, fieldMapping),\n };\n }\n\n return processedResponse;\n}\n"],"names":["validation","records"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"response-processor.js","sources":["../../../../src/client/builders/response-processor.ts"],"sourcesContent":["import type { FMTable } from \"../../orm/table\";\nimport type { Result } from \"../../types\";\nimport type { ExpandValidationConfig } from \"../../validation\";\nimport { validateSingleResponse, validateListResponse } from \"../../validation\";\nimport { transformResponseFields } from \"../../transform\";\nimport { RecordCountMismatchError } from \"../../errors\";\nimport { getBaseTableConfig } from \"../../orm/table\";\nimport { ExpandBuilder } from \"./expand-builder\";\nimport type { ExpandConfig } from \"./shared-types\";\nimport { InternalLogger } from \"../../logger\";\n\nexport interface ProcessResponseConfig {\n table?: FMTable<any, any>;\n schema?: Record<string, any>;\n singleMode: \"exact\" | \"maybe\" | false;\n selectedFields?: string[];\n expandValidationConfigs?: ExpandValidationConfig[];\n skipValidation?: boolean;\n useEntityIds?: boolean;\n includeSpecialColumns?: boolean;\n // Mapping from field names to output keys (for renamed fields in select)\n fieldMapping?: Record<string, string>;\n}\n\n/**\n * Processes OData response with transformation and validation.\n * Shared by QueryBuilder and RecordBuilder.\n */\nexport async function processODataResponse<T>(\n rawResponse: any,\n config: ProcessResponseConfig,\n): Promise<Result<T>> {\n const {\n table,\n schema,\n singleMode,\n selectedFields,\n expandValidationConfigs,\n skipValidation,\n useEntityIds,\n includeSpecialColumns,\n fieldMapping,\n } = config;\n\n // Transform field IDs back to names if using entity IDs\n let response = rawResponse;\n if (table && useEntityIds) {\n response = transformResponseFields(\n response,\n table,\n expandValidationConfigs,\n );\n }\n\n // Fast path: skip validation\n if (skipValidation) {\n const result = extractRecords(response, singleMode);\n // Rename fields AFTER extraction (but before returning)\n if (result.data && fieldMapping && Object.keys(fieldMapping).length > 0) {\n if (result.error) {\n return { data: undefined, error: result.error } as Result<T>;\n }\n return {\n data: renameFieldsInResponse(result.data, fieldMapping) as T,\n error: undefined,\n };\n }\n return result as Result<T>;\n }\n\n // Validation path\n // Note: Special columns are excluded when using QueryBuilder.single() method,\n // but included for RecordBuilder.get() method (both use singleMode: \"exact\")\n // The exclusion is handled in QueryBuilder's processQueryResponse, not here\n if (singleMode !== false) {\n const validation = await validateSingleResponse<any>(\n response,\n schema,\n selectedFields as any,\n expandValidationConfigs,\n singleMode,\n includeSpecialColumns,\n );\n\n if (!validation.valid) {\n return { data: undefined, error: validation.error };\n }\n\n // Rename fields AFTER validation completes\n if (fieldMapping && Object.keys(fieldMapping).length > 0) {\n return {\n data: renameFieldsInResponse(validation.data, fieldMapping) as T,\n error: undefined,\n };\n }\n\n return { data: validation.data as T, error: undefined };\n }\n\n const validation = await validateListResponse<any>(\n response,\n schema,\n selectedFields as any,\n expandValidationConfigs,\n includeSpecialColumns,\n );\n\n if (!validation.valid) {\n return { data: undefined, error: validation.error };\n }\n\n // Rename fields AFTER validation completes\n if (fieldMapping && Object.keys(fieldMapping).length > 0) {\n return {\n data: renameFieldsInResponse(validation.data, fieldMapping) as T,\n error: undefined,\n };\n }\n\n return { data: validation.data as T, error: undefined };\n}\n\n/**\n * Extracts records from response without validation.\n */\nfunction extractRecords<T>(\n response: any,\n singleMode: \"exact\" | \"maybe\" | false,\n): Result<T> {\n if (singleMode === false) {\n const records = response.value ?? [];\n return { data: records as T, error: undefined };\n }\n\n const records = response.value ?? [response];\n const count = Array.isArray(records) ? records.length : 1;\n\n if (count > 1) {\n return {\n data: undefined,\n error: new RecordCountMismatchError(\n singleMode === \"exact\" ? \"one\" : \"at-most-one\",\n count,\n ),\n };\n }\n\n if (count === 0) {\n if (singleMode === \"exact\") {\n return { data: undefined, error: new RecordCountMismatchError(\"one\", 0) };\n }\n return { data: null as T, error: undefined };\n }\n\n const record = Array.isArray(records) ? records[0] : records;\n return { data: record as T, error: undefined };\n}\n\n/**\n * Gets schema from a table occurrence, excluding container fields.\n * Container fields are never returned in regular responses (only via getSingleField).\n */\nexport function getSchemaFromTable(\n table: FMTable<any, any> | undefined,\n): Record<string, any> | undefined {\n if (!table) return undefined;\n const baseTableConfig = getBaseTableConfig(table);\n const containerFields = baseTableConfig.containerFields || [];\n\n // Filter out container fields from schema\n const schema = { ...baseTableConfig.schema };\n for (const containerField of containerFields) {\n delete schema[containerField as string];\n }\n\n return schema;\n}\n\n/**\n * Renames fields in response data according to the field mapping.\n * Used when select() is called with renamed fields (e.g., { userEmail: users.email }).\n */\nfunction renameFieldsInResponse(\n data: any,\n fieldMapping: Record<string, string>,\n): any {\n if (!data || typeof data !== \"object\") {\n return data;\n }\n\n // Handle array responses\n if (Array.isArray(data)) {\n return data.map((item) => renameFieldsInResponse(item, fieldMapping));\n }\n\n // Handle OData list response structure\n if (\"value\" in data && Array.isArray(data.value)) {\n return {\n ...data,\n value: data.value.map((item: any) =>\n renameFieldsInResponse(item, fieldMapping),\n ),\n };\n }\n\n // Handle single record\n const renamed: Record<string, any> = {};\n for (const [key, value] of Object.entries(data)) {\n // Check if this field should be renamed\n const outputKey = fieldMapping[key];\n if (outputKey) {\n renamed[outputKey] = value;\n } else {\n renamed[key] = value;\n }\n }\n return renamed;\n}\n\n/**\n * Processes query response with expand configs.\n * This is a convenience wrapper that builds validation configs from expand configs.\n */\nexport async function processQueryResponse<T>(\n response: any,\n config: {\n occurrence?: FMTable<any, any>;\n singleMode: \"exact\" | \"maybe\" | false;\n queryOptions: { select?: (keyof T)[] | string[] };\n expandConfigs: ExpandConfig[];\n skipValidation?: boolean;\n useEntityIds?: boolean;\n includeSpecialColumns?: boolean;\n // Mapping from field names to output keys (for renamed fields in select)\n fieldMapping?: Record<string, string>;\n logger: InternalLogger;\n },\n): Promise<Result<any>> {\n const {\n occurrence,\n singleMode,\n queryOptions,\n expandConfigs,\n skipValidation,\n useEntityIds,\n includeSpecialColumns,\n fieldMapping,\n logger,\n } = config;\n\n const expandBuilder = new ExpandBuilder(useEntityIds ?? false, logger);\n const expandValidationConfigs =\n expandBuilder.buildValidationConfigs(expandConfigs);\n\n const selectedFields = queryOptions.select\n ? Array.isArray(queryOptions.select)\n ? queryOptions.select.map(String)\n : [String(queryOptions.select)]\n : undefined;\n\n // Process the response first\n let processedResponse = await processODataResponse(response, {\n table: occurrence,\n schema: getSchemaFromTable(occurrence),\n singleMode,\n selectedFields,\n expandValidationConfigs,\n skipValidation,\n useEntityIds,\n includeSpecialColumns,\n });\n\n // Rename fields if field mapping is provided (for renamed fields in select)\n if (\n processedResponse.data &&\n fieldMapping &&\n Object.keys(fieldMapping).length > 0\n ) {\n processedResponse = {\n ...processedResponse,\n data: renameFieldsInResponse(processedResponse.data, fieldMapping),\n };\n }\n\n return processedResponse;\n}\n"],"names":["validation","records"],"mappings":"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"}
|
|
@@ -4,11 +4,26 @@ import { EntitySet } from './entity-set.js';
|
|
|
4
4
|
import { BatchBuilder } from './batch-builder.js';
|
|
5
5
|
import { SchemaManager } from './schema-manager.js';
|
|
6
6
|
import { FMTable } from '../orm/table.js';
|
|
7
|
-
|
|
7
|
+
import { WebhookManager } from './webhook-builder.js';
|
|
8
|
+
type MetadataArgs = {
|
|
9
|
+
format?: "xml" | "json";
|
|
10
|
+
/**
|
|
11
|
+
* If provided, only the metadata for the specified table will be returned.
|
|
12
|
+
* Requires FileMaker Server 22.0.4 or later.
|
|
13
|
+
*/
|
|
14
|
+
tableName?: string;
|
|
15
|
+
/**
|
|
16
|
+
* If true, a reduced payload size will be returned by omitting certain annotations.
|
|
17
|
+
*/
|
|
18
|
+
reduceAnnotations?: boolean;
|
|
19
|
+
};
|
|
20
|
+
export declare class Database<IncludeSpecialColumns extends boolean = false> {
|
|
8
21
|
private readonly databaseName;
|
|
9
22
|
private readonly context;
|
|
10
23
|
private _useEntityIds;
|
|
24
|
+
private _includeSpecialColumns;
|
|
11
25
|
readonly schema: SchemaManager;
|
|
26
|
+
readonly webhook: WebhookManager;
|
|
12
27
|
constructor(databaseName: string, context: ExecutionContext, config?: {
|
|
13
28
|
/**
|
|
14
29
|
* Whether to use entity IDs instead of field names in the actual requests to the server
|
|
@@ -16,20 +31,27 @@ export declare class Database {
|
|
|
16
31
|
* If set to false but some occurrences do not use entity IDs, an error will be thrown
|
|
17
32
|
*/
|
|
18
33
|
useEntityIds?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Whether to include special columns (ROWID and ROWMODID) in responses.
|
|
36
|
+
* Note: Special columns are only included when there is no $select query.
|
|
37
|
+
*/
|
|
38
|
+
includeSpecialColumns?: IncludeSpecialColumns;
|
|
19
39
|
});
|
|
20
|
-
from<T extends FMTable<any, any>>(table: T): EntitySet<T>;
|
|
40
|
+
from<T extends FMTable<any, any>>(table: T): EntitySet<T, IncludeSpecialColumns>;
|
|
21
41
|
/**
|
|
22
42
|
* Retrieves the OData metadata for this database.
|
|
23
43
|
* @param args Optional configuration object
|
|
24
44
|
* @param args.format The format to retrieve metadata in. Defaults to "json".
|
|
45
|
+
* @param args.tableName If provided, only the metadata for the specified table will be returned. Requires FileMaker Server 22.0.4 or later.
|
|
46
|
+
* @param args.reduceAnnotations If true, a reduced payload size will be returned by omitting certain annotations.
|
|
25
47
|
* @returns The metadata in the specified format
|
|
26
48
|
*/
|
|
27
49
|
getMetadata(args: {
|
|
28
50
|
format: "xml";
|
|
29
|
-
}): Promise<string>;
|
|
51
|
+
} & MetadataArgs): Promise<string>;
|
|
30
52
|
getMetadata(args?: {
|
|
31
53
|
format?: "json";
|
|
32
|
-
}): Promise<Metadata>;
|
|
54
|
+
} & MetadataArgs): Promise<Metadata>;
|
|
33
55
|
/**
|
|
34
56
|
* Lists all available tables (entity sets) in this database.
|
|
35
57
|
* @returns Promise resolving to an array of table names
|
|
@@ -77,3 +99,4 @@ export declare class Database {
|
|
|
77
99
|
*/
|
|
78
100
|
batch<const Builders extends readonly ExecutableBuilder<any>[]>(builders: Builders): BatchBuilder<Builders>;
|
|
79
101
|
}
|
|
102
|
+
export {};
|
|
@@ -5,14 +5,19 @@ import { EntitySet } from "./entity-set.js";
|
|
|
5
5
|
import { BatchBuilder } from "./batch-builder.js";
|
|
6
6
|
import { SchemaManager } from "./schema-manager.js";
|
|
7
7
|
import { FMTable } from "../orm/table.js";
|
|
8
|
+
import { WebhookManager } from "./webhook-builder.js";
|
|
8
9
|
class Database {
|
|
9
10
|
constructor(databaseName, context, config) {
|
|
10
11
|
__publicField(this, "_useEntityIds", false);
|
|
12
|
+
__publicField(this, "_includeSpecialColumns");
|
|
11
13
|
__publicField(this, "schema");
|
|
14
|
+
__publicField(this, "webhook");
|
|
12
15
|
this.databaseName = databaseName;
|
|
13
16
|
this.context = context;
|
|
14
17
|
this.schema = new SchemaManager(this.databaseName, this.context);
|
|
18
|
+
this.webhook = new WebhookManager(this.databaseName, this.context);
|
|
15
19
|
this._useEntityIds = (config == null ? void 0 : config.useEntityIds) ?? false;
|
|
20
|
+
this._includeSpecialColumns = (config == null ? void 0 : config.includeSpecialColumns) ?? false;
|
|
16
21
|
}
|
|
17
22
|
from(table) {
|
|
18
23
|
if (Object.prototype.hasOwnProperty.call(table, FMTable.Symbol.UseEntityIds)) {
|
|
@@ -29,10 +34,18 @@ class Database {
|
|
|
29
34
|
});
|
|
30
35
|
}
|
|
31
36
|
async getMetadata(args) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
let url = `/${this.databaseName}/$metadata`;
|
|
38
|
+
if (args == null ? void 0 : args.tableName) {
|
|
39
|
+
url = `/${this.databaseName}/$metadata%23${args.tableName}`;
|
|
40
|
+
}
|
|
41
|
+
const headers = {
|
|
42
|
+
Accept: (args == null ? void 0 : args.format) === "xml" ? "application/xml" : "application/json"
|
|
43
|
+
};
|
|
44
|
+
if (args == null ? void 0 : args.reduceAnnotations) {
|
|
45
|
+
headers["Prefer"] = 'include-annotations="-*"';
|
|
46
|
+
}
|
|
47
|
+
const result = await this.context._makeRequest(url, {
|
|
48
|
+
headers
|
|
36
49
|
});
|
|
37
50
|
if (result.error) {
|
|
38
51
|
throw result.error;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"database.js","sources":["../../../src/client/database.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { ExecutionContext, ExecutableBuilder, Metadata } from \"../types\";\nimport { EntitySet } from \"./entity-set\";\nimport { BatchBuilder } from \"./batch-builder\";\nimport { SchemaManager } from \"./schema-manager\";\nimport { FMTable } from \"../orm/table\";\n\nexport class Database {\n private _useEntityIds: boolean = false;\n public readonly schema: SchemaManager;\n\n constructor(\n private readonly databaseName: string,\n private readonly context: ExecutionContext,\n config?: {\n /**\n * Whether to use entity IDs instead of field names in the actual requests to the server\n * Defaults to true if all occurrences use entity IDs, false otherwise\n * If set to false but some occurrences do not use entity IDs, an error will be thrown\n */\n useEntityIds?: boolean;\n },\n ) {\n // Initialize schema manager\n this.schema = new SchemaManager(this.databaseName, this.context);\n this._useEntityIds = config?.useEntityIds ?? false;\n }\n\n from<T extends FMTable<any, any>>(table: T): EntitySet<T> {\n // Only override database-level useEntityIds if table explicitly sets it\n // (not if it's undefined, which would override the database setting)\n if (\n Object.prototype.hasOwnProperty.call(table, FMTable.Symbol.UseEntityIds)\n ) {\n const tableUseEntityIds = (table as any)[FMTable.Symbol.UseEntityIds];\n if (typeof tableUseEntityIds === \"boolean\") {\n this._useEntityIds = tableUseEntityIds;\n }\n }\n return new EntitySet<T>({\n occurrence: table as T,\n databaseName: this.databaseName,\n context: this.context,\n database: this,\n });\n }\n\n /**\n * Retrieves the OData metadata for this database.\n * @param args Optional configuration object\n * @param args.format The format to retrieve metadata in. Defaults to \"json\".\n * @returns The metadata in the specified format\n */\n async getMetadata(args: { format: \"xml\" }): Promise<string>;\n async getMetadata(args?: { format?: \"json\" }): Promise<Metadata>;\n async getMetadata(args?: {\n format?: \"xml\" | \"json\";\n }): Promise<string | Metadata> {\n const result = await this.context._makeRequest<\n Record<string, Metadata> | string\n >(`/${this.databaseName}/$metadata`, {\n headers: {\n Accept: args?.format === \"xml\" ? \"application/xml\" : \"application/json\",\n },\n });\n if (result.error) {\n throw result.error;\n }\n\n if (args?.format === \"json\") {\n const data = result.data as Record<string, Metadata>;\n const metadata = data[this.databaseName];\n if (!metadata) {\n throw new Error(\n `Metadata for database \"${this.databaseName}\" not found in response`,\n );\n }\n return metadata;\n }\n return result.data as string;\n }\n\n /**\n * Lists all available tables (entity sets) in this database.\n * @returns Promise resolving to an array of table names\n */\n async listTableNames(): Promise<string[]> {\n const result = await this.context._makeRequest<{\n value?: Array<{ name: string }>;\n }>(`/${this.databaseName}`);\n if (result.error) {\n throw result.error;\n }\n if (result.data.value && Array.isArray(result.data.value)) {\n return result.data.value.map((item) => item.name);\n }\n return [];\n }\n\n /**\n * Executes a FileMaker script.\n * @param scriptName - The name of the script to execute (must be valid according to OData rules)\n * @param options - Optional script parameter and result schema\n * @returns Promise resolving to script execution result\n */\n async runScript<ResultSchema extends StandardSchemaV1<string, any> = never>(\n scriptName: string,\n options?: {\n scriptParam?: string | number | Record<string, any>;\n resultSchema?: ResultSchema;\n },\n ): Promise<\n [ResultSchema] extends [never]\n ? { resultCode: number; result?: string }\n : ResultSchema extends StandardSchemaV1<string, infer Output>\n ? { resultCode: number; result: Output }\n : { resultCode: number; result?: string }\n > {\n const body: { scriptParameterValue?: unknown } = {};\n if (options?.scriptParam !== undefined) {\n body.scriptParameterValue = options.scriptParam;\n }\n\n const result = await this.context._makeRequest<{\n scriptResult: {\n code: number;\n resultParameter?: string;\n };\n }>(`/${this.databaseName}/Script.${scriptName}`, {\n method: \"POST\",\n body: Object.keys(body).length > 0 ? JSON.stringify(body) : undefined,\n });\n\n if (result.error) {\n throw result.error;\n }\n\n const response = result.data;\n\n // If resultSchema is provided, validate the result through it\n if (options?.resultSchema && response.scriptResult !== undefined) {\n const validationResult = options.resultSchema[\"~standard\"].validate(\n response.scriptResult.resultParameter,\n );\n // Handle both sync and async validation\n const result =\n validationResult instanceof Promise\n ? await validationResult\n : validationResult;\n\n if (result.issues) {\n throw new Error(\n `Script result validation failed: ${JSON.stringify(result.issues)}`,\n );\n }\n\n return {\n resultCode: response.scriptResult.code,\n result: result.value,\n } as any;\n }\n\n return {\n resultCode: response.scriptResult.code,\n result: response.scriptResult.resultParameter,\n } as any;\n }\n\n /**\n * Create a batch operation builder that allows multiple queries to be executed together\n * in a single atomic request. All operations succeed or fail together (transactional).\n *\n * @param builders - Array of executable query builders to batch\n * @returns A BatchBuilder that can be executed\n * @example\n * ```ts\n * const result = await db.batch([\n * db.from('contacts').list().top(5),\n * db.from('users').list().top(5),\n * db.from('contacts').insert({ name: 'John' })\n * ]).execute();\n *\n * if (result.data) {\n * const [contacts, users, insertResult] = result.data;\n * }\n * ```\n */\n batch<const Builders extends readonly ExecutableBuilder<any>[]>(\n builders: Builders,\n ): BatchBuilder<Builders> {\n return new BatchBuilder(builders, this.databaseName, this.context);\n }\n}\n"],"names":["result"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"database.js","sources":["../../../src/client/database.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { ExecutionContext, ExecutableBuilder, Metadata } from \"../types\";\nimport { EntitySet } from \"./entity-set\";\nimport { BatchBuilder } from \"./batch-builder\";\nimport { SchemaManager } from \"./schema-manager\";\nimport { FMTable } from \"../orm/table\";\nimport { WebhookManager } from \"./webhook-builder\";\n\ntype MetadataArgs = {\n format?: \"xml\" | \"json\";\n /**\n * If provided, only the metadata for the specified table will be returned.\n * Requires FileMaker Server 22.0.4 or later.\n */\n tableName?: string;\n /**\n * If true, a reduced payload size will be returned by omitting certain annotations.\n */\n reduceAnnotations?: boolean;\n};\n\nexport class Database<IncludeSpecialColumns extends boolean = false> {\n private _useEntityIds: boolean = false;\n private _includeSpecialColumns: IncludeSpecialColumns;\n public readonly schema: SchemaManager;\n public readonly webhook: WebhookManager;\n\n constructor(\n private readonly databaseName: string,\n private readonly context: ExecutionContext,\n config?: {\n /**\n * Whether to use entity IDs instead of field names in the actual requests to the server\n * Defaults to true if all occurrences use entity IDs, false otherwise\n * If set to false but some occurrences do not use entity IDs, an error will be thrown\n */\n useEntityIds?: boolean;\n /**\n * Whether to include special columns (ROWID and ROWMODID) in responses.\n * Note: Special columns are only included when there is no $select query.\n */\n includeSpecialColumns?: IncludeSpecialColumns;\n },\n ) {\n // Initialize schema manager\n this.schema = new SchemaManager(this.databaseName, this.context);\n this.webhook = new WebhookManager(this.databaseName, this.context);\n this._useEntityIds = config?.useEntityIds ?? false;\n this._includeSpecialColumns = (config?.includeSpecialColumns ??\n false) as IncludeSpecialColumns;\n }\n\n from<T extends FMTable<any, any>>(\n table: T,\n ): EntitySet<T, IncludeSpecialColumns> {\n // Only override database-level useEntityIds if table explicitly sets it\n // (not if it's undefined, which would override the database setting)\n if (\n Object.prototype.hasOwnProperty.call(table, FMTable.Symbol.UseEntityIds)\n ) {\n const tableUseEntityIds = (table as any)[FMTable.Symbol.UseEntityIds];\n if (typeof tableUseEntityIds === \"boolean\") {\n this._useEntityIds = tableUseEntityIds;\n }\n }\n return new EntitySet<T, IncludeSpecialColumns>({\n occurrence: table as T,\n databaseName: this.databaseName,\n context: this.context,\n database: this,\n });\n }\n\n /**\n * Retrieves the OData metadata for this database.\n * @param args Optional configuration object\n * @param args.format The format to retrieve metadata in. Defaults to \"json\".\n * @param args.tableName If provided, only the metadata for the specified table will be returned. Requires FileMaker Server 22.0.4 or later.\n * @param args.reduceAnnotations If true, a reduced payload size will be returned by omitting certain annotations.\n * @returns The metadata in the specified format\n */\n async getMetadata(args: { format: \"xml\" } & MetadataArgs): Promise<string>;\n async getMetadata(\n args?: { format?: \"json\" } & MetadataArgs,\n ): Promise<Metadata>;\n async getMetadata(args?: MetadataArgs): Promise<string | Metadata> {\n // Build the URL - if tableName is provided, append %23{tableName} to the path\n let url = `/${this.databaseName}/$metadata`;\n if (args?.tableName) {\n url = `/${this.databaseName}/$metadata%23${args.tableName}`;\n }\n\n // Build headers\n const headers: Record<string, string> = {\n Accept: args?.format === \"xml\" ? \"application/xml\" : \"application/json\",\n };\n\n // Add Prefer header if reduceAnnotations is true\n if (args?.reduceAnnotations) {\n headers[\"Prefer\"] = 'include-annotations=\"-*\"';\n }\n\n const result = await this.context._makeRequest<\n Record<string, Metadata> | string\n >(url, {\n headers,\n });\n if (result.error) {\n throw result.error;\n }\n\n if (args?.format === \"json\") {\n const data = result.data as Record<string, Metadata>;\n const metadata = data[this.databaseName];\n if (!metadata) {\n throw new Error(\n `Metadata for database \"${this.databaseName}\" not found in response`,\n );\n }\n return metadata;\n }\n return result.data as string;\n }\n\n /**\n * Lists all available tables (entity sets) in this database.\n * @returns Promise resolving to an array of table names\n */\n async listTableNames(): Promise<string[]> {\n const result = await this.context._makeRequest<{\n value?: Array<{ name: string }>;\n }>(`/${this.databaseName}`);\n if (result.error) {\n throw result.error;\n }\n if (result.data.value && Array.isArray(result.data.value)) {\n return result.data.value.map((item) => item.name);\n }\n return [];\n }\n\n /**\n * Executes a FileMaker script.\n * @param scriptName - The name of the script to execute (must be valid according to OData rules)\n * @param options - Optional script parameter and result schema\n * @returns Promise resolving to script execution result\n */\n async runScript<ResultSchema extends StandardSchemaV1<string, any> = never>(\n scriptName: string,\n options?: {\n scriptParam?: string | number | Record<string, any>;\n resultSchema?: ResultSchema;\n },\n ): Promise<\n [ResultSchema] extends [never]\n ? { resultCode: number; result?: string }\n : ResultSchema extends StandardSchemaV1<string, infer Output>\n ? { resultCode: number; result: Output }\n : { resultCode: number; result?: string }\n > {\n const body: { scriptParameterValue?: unknown } = {};\n if (options?.scriptParam !== undefined) {\n body.scriptParameterValue = options.scriptParam;\n }\n\n const result = await this.context._makeRequest<{\n scriptResult: {\n code: number;\n resultParameter?: string;\n };\n }>(`/${this.databaseName}/Script.${scriptName}`, {\n method: \"POST\",\n body: Object.keys(body).length > 0 ? JSON.stringify(body) : undefined,\n });\n\n if (result.error) {\n throw result.error;\n }\n\n const response = result.data;\n\n // If resultSchema is provided, validate the result through it\n if (options?.resultSchema && response.scriptResult !== undefined) {\n const validationResult = options.resultSchema[\"~standard\"].validate(\n response.scriptResult.resultParameter,\n );\n // Handle both sync and async validation\n const result =\n validationResult instanceof Promise\n ? await validationResult\n : validationResult;\n\n if (result.issues) {\n throw new Error(\n `Script result validation failed: ${JSON.stringify(result.issues)}`,\n );\n }\n\n return {\n resultCode: response.scriptResult.code,\n result: result.value,\n } as any;\n }\n\n return {\n resultCode: response.scriptResult.code,\n result: response.scriptResult.resultParameter,\n } as any;\n }\n\n /**\n * Create a batch operation builder that allows multiple queries to be executed together\n * in a single atomic request. All operations succeed or fail together (transactional).\n *\n * @param builders - Array of executable query builders to batch\n * @returns A BatchBuilder that can be executed\n * @example\n * ```ts\n * const result = await db.batch([\n * db.from('contacts').list().top(5),\n * db.from('users').list().top(5),\n * db.from('contacts').insert({ name: 'John' })\n * ]).execute();\n *\n * if (result.data) {\n * const [contacts, users, insertResult] = result.data;\n * }\n * ```\n */\n batch<const Builders extends readonly ExecutableBuilder<any>[]>(\n builders: Builders,\n ): BatchBuilder<Builders> {\n return new BatchBuilder(builders, this.databaseName, this.context);\n }\n}\n"],"names":["result"],"mappings":"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"}
|
|
@@ -10,11 +10,13 @@ export declare class DeleteBuilder<Occ extends FMTable<any, any>> {
|
|
|
10
10
|
private context;
|
|
11
11
|
private table;
|
|
12
12
|
private databaseUseEntityIds;
|
|
13
|
+
private databaseIncludeSpecialColumns;
|
|
13
14
|
constructor(config: {
|
|
14
15
|
occurrence: Occ;
|
|
15
16
|
databaseName: string;
|
|
16
17
|
context: ExecutionContext;
|
|
17
18
|
databaseUseEntityIds?: boolean;
|
|
19
|
+
databaseIncludeSpecialColumns?: boolean;
|
|
18
20
|
});
|
|
19
21
|
/**
|
|
20
22
|
* Delete a single record by ID
|