@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.
Files changed (74) hide show
  1. package/README.md +198 -0
  2. package/dist/esm/client/builders/default-select.d.ts +4 -1
  3. package/dist/esm/client/builders/default-select.js +3 -2
  4. package/dist/esm/client/builders/default-select.js.map +1 -1
  5. package/dist/esm/client/builders/expand-builder.js.map +1 -1
  6. package/dist/esm/client/builders/query-string-builder.d.ts +1 -0
  7. package/dist/esm/client/builders/query-string-builder.js.map +1 -1
  8. package/dist/esm/client/builders/response-processor.d.ts +2 -0
  9. package/dist/esm/client/builders/response-processor.js +8 -3
  10. package/dist/esm/client/builders/response-processor.js.map +1 -1
  11. package/dist/esm/client/database.d.ts +27 -4
  12. package/dist/esm/client/database.js +17 -4
  13. package/dist/esm/client/database.js.map +1 -1
  14. package/dist/esm/client/delete-builder.d.ts +2 -0
  15. package/dist/esm/client/delete-builder.js +2 -0
  16. package/dist/esm/client/delete-builder.js.map +1 -1
  17. package/dist/esm/client/entity-set.d.ts +8 -7
  18. package/dist/esm/client/entity-set.js +21 -26
  19. package/dist/esm/client/entity-set.js.map +1 -1
  20. package/dist/esm/client/error-parser.js.map +1 -1
  21. package/dist/esm/client/filemaker-odata.d.ts +15 -2
  22. package/dist/esm/client/filemaker-odata.js +25 -2
  23. package/dist/esm/client/filemaker-odata.js.map +1 -1
  24. package/dist/esm/client/insert-builder.d.ts +2 -0
  25. package/dist/esm/client/insert-builder.js +2 -0
  26. package/dist/esm/client/insert-builder.js.map +1 -1
  27. package/dist/esm/client/query/query-builder.d.ts +26 -15
  28. package/dist/esm/client/query/query-builder.js +43 -9
  29. package/dist/esm/client/query/query-builder.js.map +1 -1
  30. package/dist/esm/client/query/response-processor.d.ts +1 -0
  31. package/dist/esm/client/query/types.d.ts +24 -3
  32. package/dist/esm/client/record-builder.d.ts +24 -13
  33. package/dist/esm/client/record-builder.js +50 -17
  34. package/dist/esm/client/record-builder.js.map +1 -1
  35. package/dist/esm/client/update-builder.d.ts +2 -0
  36. package/dist/esm/client/update-builder.js +2 -0
  37. package/dist/esm/client/update-builder.js.map +1 -1
  38. package/dist/esm/client/webhook-builder.d.ts +126 -0
  39. package/dist/esm/client/webhook-builder.js +197 -0
  40. package/dist/esm/client/webhook-builder.js.map +1 -0
  41. package/dist/esm/index.d.ts +1 -0
  42. package/dist/esm/orm/field-builders.d.ts +12 -2
  43. package/dist/esm/orm/field-builders.js +18 -2
  44. package/dist/esm/orm/field-builders.js.map +1 -1
  45. package/dist/esm/orm/table.d.ts +23 -10
  46. package/dist/esm/orm/table.js +17 -30
  47. package/dist/esm/orm/table.js.map +1 -1
  48. package/dist/esm/types.d.ts +32 -2
  49. package/dist/esm/types.js.map +1 -1
  50. package/dist/esm/validation.d.ts +5 -5
  51. package/dist/esm/validation.js +44 -13
  52. package/dist/esm/validation.js.map +1 -1
  53. package/package.json +2 -2
  54. package/src/client/builders/default-select.ts +12 -1
  55. package/src/client/builders/expand-builder.ts +1 -1
  56. package/src/client/builders/query-string-builder.ts +6 -0
  57. package/src/client/builders/response-processor.ts +10 -0
  58. package/src/client/database.ts +54 -12
  59. package/src/client/delete-builder.ts +5 -1
  60. package/src/client/entity-set.ts +72 -44
  61. package/src/client/error-parser.ts +3 -0
  62. package/src/client/filemaker-odata.ts +39 -6
  63. package/src/client/insert-builder.ts +4 -0
  64. package/src/client/query/query-builder.ts +198 -35
  65. package/src/client/query/response-processor.ts +15 -25
  66. package/src/client/query/types.ts +35 -6
  67. package/src/client/record-builder.ts +159 -32
  68. package/src/client/update-builder.ts +4 -1
  69. package/src/client/webhook-builder.ts +285 -0
  70. package/src/index.ts +6 -0
  71. package/src/orm/field-builders.ts +24 -2
  72. package/src/orm/table.ts +40 -48
  73. package/src/types.ts +62 -6
  74. 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
- return [...new Set(allFields.filter((f) => !containerFields.includes(f)))];
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 return [...new Set(allFields.filter((f) => !containerFields.includes(f)))];\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;AAOO,SAAS,uBACd,OACsB;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,WAAO,CAAC,GAAG,IAAI,IAAI,UAAU,OAAO,CAAC,MAAM,CAAC,gBAAgB,SAAS,CAAC,CAAC,CAAC,CAAC;AAAA,EAAA;AAGvE,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
+ {"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":";;;;AAoBO,MAAM,cAAc;AAAA,EACzB,YACU,cACA,QACR;AAFQ,SAAA,eAAA;AACA,SAAA,SAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMV,kBAAkB,SAAiC;AAC7C,QAAA,QAAQ,WAAW,EAAU,QAAA;AAE1B,WAAA,QAAQ,IAAI,CAAC,WAAW,KAAK,kBAAkB,MAAM,CAAC,EAAE,KAAK,GAAG;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMzE,uBAAuB,SAAmD;AACjE,WAAA,QAAQ,IAAI,CAAC,WAAW;;AAC7B,YAAM,cAAc,OAAO;AAEvB,UAAA;AACJ,UAAI,aAAa;AACT,cAAA,kBAAkB,mBAAmB,WAAW;AAChD,cAAA,kBAAkB,gBAAgB,mBAAmB,CAAC;AAG5D,cAAM,SAAS,EAAE,GAAG,gBAAgB,OAAO;AAC3C,mBAAW,kBAAkB,iBAAiB;AAC5C,iBAAO,OAAO,cAAwB;AAAA,QAAA;AAGzB,uBAAA;AAAA,MAAA;AAGX,YAAA,mBAAiB,YAAO,YAAP,mBAAgB,UACnC,MAAM,QAAQ,OAAO,QAAQ,MAAM,IACjC,OAAO,QAAQ,OAAO,IAAI,MAAM,IAChC,CAAC,OAAO,OAAO,QAAQ,MAAM,CAAC,IAChC;AAGJ,YAAM,gBAAgB,OAAO,sBACzB,KAAK,uBAAuB,OAAO,mBAAmB,IACtD;AAEG,aAAA;AAAA,QACL,UAAU,OAAO;AAAA,QACjB;AAAA,QACA;AAAA,QACA,OAAO;AAAA,QACP;AAAA,QACA;AAAA,MACF;AAAA,IAAA,CACD;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaH,cACE,aACA,aACA,UACA,gBACc;AAER,UAAA,eAAe,aAAa,WAAW;AAG7C,QAAI,aAAa;AACT,YAAA,kBAAkB,mBAAmB,WAAW;AACtD,UAAI,mBAAmB,CAAC,gBAAgB,SAAS,YAAY,GAAG;AAC9D,aAAK,OAAO;AAAA,UACV,qBAAqB,YAAY,8BAA8B,gBAAgB,SAAS,IAAI,gBAAgB,KAAK,IAAI,IAAI,MAAM;AAAA,QACjI;AAAA,MAAA;AAAA,IACF;AAGF,QAAI,YAAY,gBAAgB;AAE9B,YAAM,gBAAgB,eAAe;AAG/B,YAAA,oBAAoB,SAAS,aAAa;AAGhD,YAAM,gBAA4C;AAAA,QAChD,GAAI,kBAA0B;AAAA,MAChC;AAGI,UAAA,CAAC,cAAc,QAAQ;AACnB,cAAA,gBAAgB,uBAAuB,WAAW;AACxD,YAAI,eAAe;AACjB,wBAAc,SAAS;AAAA,QAAA;AAAA,MACzB;AAIF,YAAM,sBAAuB,kBAA0B;AACnD,WAAA,2DAAqB,UAAS,GAAG;AAE7B,cAAA,qBAAqB,KAAK,kBAAkB,mBAAmB;AACrE,YAAI,oBAAoB;AAEtB,wBAAc,SAAS;AAAA,QAAA;AAAA,MACzB;AAGK,aAAA;AAAA,QACL,UAAU;AAAA,QACV,SAAS;AAAA,QACT;AAAA,QACA,sBAAqB,2DAAqB,UAAS,IAAI,sBAAsB;AAAA,MAC/E;AAAA,IAAA,OACK;AAEC,YAAA,gBAAgB,uBAAuB,WAAW;AACxD,UAAI,eAAe;AACV,eAAA;AAAA,UACL,UAAU;AAAA,UACV,SAAS,EAAE,QAAQ,cAAc;AAAA,UACjC;AAAA,QACF;AAAA,MAAA,OACK;AACE,eAAA;AAAA,UACL,UAAU;AAAA,UACV;AAAA,QACF;AAAA,MAAA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMM,kBAAkB,QAA8B;AAChD,UAAA,eAAe,KAAK,oBAAoB,MAAM;AAC9C,UAAA,QAAQ,KAAK,iBAAiB,MAAM;AAEtC,QAAA,MAAM,WAAW,GAAG;AACf,aAAA;AAAA,IAAA;AAGT,WAAO,GAAG,YAAY,IAAI,MAAM,KAAK,GAAG,CAAC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMnC,oBAAoB,QAA8B;AACpD,QAAA,CAAC,KAAK,cAAc;AACtB,aAAO,OAAO;AAAA,IAAA;AAGhB,UAAM,cAAc,OAAO;AAC3B,QAAI,eAAe,QAAQ,OAAO,YAAY,aAAa;AACzD,YAAM,UAAW,YAAoB,QAAQ,OAAO,QAAQ;AAG5D,UAAI,SAAS;AACJ,eAAA;AAAA,MAAA;AAAA,IACT;AAGF,WAAO,OAAO;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMR,iBAAiB,QAAgC;AACnD,QAAA,CAAC,OAAO,WAAW,OAAO,KAAK,OAAO,OAAO,EAAE,WAAW,GAAG;AAC/D,aAAO,CAAC;AAAA,IAAA;AAGV,UAAM,QAAkB,CAAC;AACzB,UAAM,OAAO,OAAO;AAEpB,QAAI,KAAK,QAAQ;AACf,YAAM,cAAc,MAAM,QAAQ,KAAK,MAAM,IACzC,KAAK,OAAO,IAAI,MAAM,IACtB,CAAC,OAAO,KAAK,MAAM,CAAC;AACxB,YAAM,eAAe;AAAA,QACnB;AAAA,QACA,OAAO;AAAA,QACP,KAAK;AAAA,MACP;AACM,YAAA,KAAK,WAAW,YAAY,EAAE;AAAA,IAAA;AAGtC,QAAI,KAAK,QAAQ;AACf,YAAM,cAAc,WAAW,EAAE,QAAQ,KAAK,QAAQ;AAChD,YAAA,QAAQ,YAAY,MAAM,kBAAkB;AAClD,UAAI,MAAa,OAAA,KAAK,WAAW,MAAM,CAAC,CAAC,EAAE;AAAA,IAAA;AAG7C,QAAI,KAAK,SAAS;AAChB,YAAM,eAAe,MAAM,QAAQ,KAAK,OAAO,IAC3C,KAAK,QAAQ,KAAK,GAAG,IACrB,OAAO,KAAK,OAAO;AACjB,YAAA,KAAK,YAAY,YAAY,EAAE;AAAA,IAAA;AAGnC,QAAA,KAAK,QAAQ,OAAW,OAAM,KAAK,QAAQ,KAAK,GAAG,EAAE;AACrD,QAAA,KAAK,SAAS,OAAW,OAAM,KAAK,SAAS,KAAK,IAAI,EAAE;AAE5D,QAAI,KAAK,QAAQ;AACX,UAAA,OAAO,KAAK,WAAW,UAAU;AACnC,cAAM,KAAK,WAAW,KAAK,MAAM,EAAE;AAAA,MAAA;AAAA,IACrC;AAGK,WAAA;AAAA,EAAA;AAEX;"}
@@ -14,4 +14,5 @@ export declare function buildSelectExpandQueryString(config: {
14
14
  table?: FMTable<any, any>;
15
15
  useEntityIds: boolean;
16
16
  logger: InternalLogger;
17
+ includeSpecialColumns?: boolean;
17
18
  }): string;
@@ -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,QAMlC;AACT,QAAM,QAAkB,CAAC;AACzB,QAAM,gBAAgB,IAAI,cAAc,OAAO,cAAc,OAAO,MAAM;AAG1E,MAAI,OAAO,kBAAkB,OAAO,eAAe,SAAS,GAAG;AAC7D,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;"}
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
- export declare class Database {
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
- const result = await this.context._makeRequest(`/${this.databaseName}/$metadata`, {
33
- headers: {
34
- Accept: (args == null ? void 0 : args.format) === "xml" ? "application/xml" : "application/json"
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