@oystehr/sdk 4.3.7 → 4.3.9

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 (53) hide show
  1. package/README.md +105 -41
  2. package/dist/cjs/client/client.cjs +137 -5
  3. package/dist/cjs/client/client.cjs.map +1 -1
  4. package/dist/cjs/client/client.d.ts +28 -3
  5. package/dist/cjs/index.min.cjs +1 -1
  6. package/dist/cjs/index.min.cjs.map +1 -1
  7. package/dist/cjs/resources/classes/fhir-ext.cjs +108 -17
  8. package/dist/cjs/resources/classes/fhir-ext.cjs.map +1 -1
  9. package/dist/cjs/resources/classes/fhir-ext.d.ts +67 -12
  10. package/dist/cjs/resources/classes/fhir.cjs +3 -0
  11. package/dist/cjs/resources/classes/fhir.cjs.map +1 -1
  12. package/dist/cjs/resources/classes/fhir.d.ts +3 -0
  13. package/dist/cjs/resources/classes/zambda-ext.cjs +23 -3
  14. package/dist/cjs/resources/classes/zambda-ext.cjs.map +1 -1
  15. package/dist/cjs/resources/classes/zambda-ext.d.ts +3 -0
  16. package/dist/cjs/resources/classes/zambda.cjs +13 -0
  17. package/dist/cjs/resources/classes/zambda.cjs.map +1 -1
  18. package/dist/cjs/resources/classes/zambda.d.ts +12 -1
  19. package/dist/cjs/resources/types/ZambdaGetPresignedUrlParams.d.ts +11 -0
  20. package/dist/cjs/resources/types/ZambdaGetPresignedUrlResponse.d.ts +9 -0
  21. package/dist/cjs/resources/types/fhir.d.ts +67 -0
  22. package/dist/cjs/resources/types/index.d.ts +2 -0
  23. package/dist/esm/client/client.d.ts +28 -3
  24. package/dist/esm/client/client.js +137 -5
  25. package/dist/esm/client/client.js.map +1 -1
  26. package/dist/esm/index.min.js +1 -1
  27. package/dist/esm/index.min.js.map +1 -1
  28. package/dist/esm/resources/classes/fhir-ext.d.ts +67 -12
  29. package/dist/esm/resources/classes/fhir-ext.js +107 -19
  30. package/dist/esm/resources/classes/fhir-ext.js.map +1 -1
  31. package/dist/esm/resources/classes/fhir.d.ts +3 -0
  32. package/dist/esm/resources/classes/fhir.js +4 -1
  33. package/dist/esm/resources/classes/fhir.js.map +1 -1
  34. package/dist/esm/resources/classes/zambda-ext.d.ts +3 -0
  35. package/dist/esm/resources/classes/zambda-ext.js +23 -4
  36. package/dist/esm/resources/classes/zambda-ext.js.map +1 -1
  37. package/dist/esm/resources/classes/zambda.d.ts +12 -1
  38. package/dist/esm/resources/classes/zambda.js +14 -1
  39. package/dist/esm/resources/classes/zambda.js.map +1 -1
  40. package/dist/esm/resources/types/ZambdaGetPresignedUrlParams.d.ts +11 -0
  41. package/dist/esm/resources/types/ZambdaGetPresignedUrlResponse.d.ts +9 -0
  42. package/dist/esm/resources/types/fhir.d.ts +67 -0
  43. package/dist/esm/resources/types/index.d.ts +2 -0
  44. package/package.json +1 -1
  45. package/src/client/client.ts +214 -7
  46. package/src/resources/classes/fhir-ext.ts +278 -38
  47. package/src/resources/classes/fhir.ts +3 -0
  48. package/src/resources/classes/zambda-ext.ts +30 -3
  49. package/src/resources/classes/zambda.ts +18 -0
  50. package/src/resources/types/ZambdaGetPresignedUrlParams.ts +13 -0
  51. package/src/resources/types/ZambdaGetPresignedUrlResponse.ts +11 -0
  52. package/src/resources/types/fhir.ts +88 -0
  53. package/src/resources/types/index.ts +2 -0
@@ -1,13 +1,32 @@
1
+ import { OystehrSdkError } from '../../errors/index.js';
2
+
1
3
  function baseUrlThunk() {
2
- return this.config.services?.['projectApiUrl'] ?? 'https://project-api.zapehr.com/v1';
4
+ return this.config.services?.['zambdaApiUrl'] ?? 'https://zambda-api.zapehr.com/v1';
3
5
  }
4
6
  async function uploadFile({ id, file, filename, }) {
5
- const uploadUrl = await this.request('/zambda/{id}/s3-upload', 'post', baseUrlThunk.bind(this))({ id, filename });
6
- await fetch(uploadUrl.signedUrl, {
7
+ const uploadUrl = await this.request('/zambda/{id}/presigned-url', 'post', baseUrlThunk.bind(this))({ id, action: 'upload', filename });
8
+ const response = await fetch(uploadUrl.signedUrl, {
7
9
  method: 'PUT',
8
10
  body: file,
9
11
  });
12
+ if (!response.ok) {
13
+ throw new OystehrSdkError({ message: 'Failed to upload file', code: response.status, cause: response.statusText });
14
+ }
15
+ }
16
+ async function downloadFile({ id }) {
17
+ const downloadUrl = await this.request('/zambda/{id}/presigned-url', 'post', baseUrlThunk.bind(this))({ id, action: 'download' });
18
+ const response = await fetch(downloadUrl.signedUrl, {
19
+ method: 'GET',
20
+ });
21
+ if (!response.ok) {
22
+ throw new OystehrSdkError({
23
+ message: 'Failed to download file',
24
+ code: response.status,
25
+ cause: response.statusText,
26
+ });
27
+ }
28
+ return response.arrayBuffer();
10
29
  }
11
30
 
12
- export { uploadFile };
31
+ export { downloadFile, uploadFile };
13
32
  //# sourceMappingURL=zambda-ext.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"zambda-ext.js","sources":["../../../../src/resources/classes/zambda-ext.ts"],"sourcesContent":["import { SDKResource } from '../../client/client';\n\nfunction baseUrlThunk(this: SDKResource): string {\n return this.config.services?.['projectApiUrl'] ?? 'https://project-api.zapehr.com/v1';\n}\n\nexport async function uploadFile(\n this: SDKResource,\n {\n id,\n file,\n filename,\n }: {\n id: string;\n file: Blob;\n filename?: string | undefined;\n }\n): Promise<void> {\n const uploadUrl = await this.request('/zambda/{id}/s3-upload', 'post', baseUrlThunk.bind(this))({ id, filename });\n await fetch(uploadUrl.signedUrl, {\n method: 'PUT',\n body: file,\n });\n}\n"],"names":[],"mappings":"AAEA,SAAS,YAAY,GAAA;IACnB,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,eAAe,CAAC,IAAI,mCAAmC;AACvF;AAEO,eAAe,UAAU,CAE9B,EACE,EAAE,EACF,IAAI,EACJ,QAAQ,GAKT,EAAA;IAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,wBAAwB,EAAE,MAAM,EAAE,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC;AACjH,IAAA,MAAM,KAAK,CAAC,SAAS,CAAC,SAAS,EAAE;AAC/B,QAAA,MAAM,EAAE,KAAK;AACb,QAAA,IAAI,EAAE,IAAI;AACX,KAAA,CAAC;AACJ;;;;"}
1
+ {"version":3,"file":"zambda-ext.js","sources":["../../../../src/resources/classes/zambda-ext.ts"],"sourcesContent":["import { SDKResource } from '../../client/client';\nimport { OystehrSdkError } from '../../errors';\n\nfunction baseUrlThunk(this: SDKResource): string {\n return this.config.services?.['zambdaApiUrl'] ?? 'https://zambda-api.zapehr.com/v1';\n}\n\nexport async function uploadFile(\n this: SDKResource,\n {\n id,\n file,\n filename,\n }: {\n id: string;\n file: Blob;\n filename?: string | undefined;\n }\n): Promise<void> {\n const uploadUrl = await this.request(\n '/zambda/{id}/presigned-url',\n 'post',\n baseUrlThunk.bind(this)\n )({ id, action: 'upload', filename });\n const response = await fetch(uploadUrl.signedUrl, {\n method: 'PUT',\n body: file,\n });\n if (!response.ok) {\n throw new OystehrSdkError({ message: 'Failed to upload file', code: response.status, cause: response.statusText });\n }\n}\n\nexport async function downloadFile(this: SDKResource, { id }: { id: string }): Promise<ArrayBuffer> {\n const downloadUrl = await this.request(\n '/zambda/{id}/presigned-url',\n 'post',\n baseUrlThunk.bind(this)\n )({ id, action: 'download' });\n const response = await fetch(downloadUrl.signedUrl, {\n method: 'GET',\n });\n if (!response.ok) {\n throw new OystehrSdkError({\n message: 'Failed to download file',\n code: response.status,\n cause: response.statusText,\n });\n }\n return response.arrayBuffer();\n}\n"],"names":[],"mappings":";;AAGA,SAAS,YAAY,GAAA;IACnB,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,cAAc,CAAC,IAAI,kCAAkC;AACrF;AAEO,eAAe,UAAU,CAE9B,EACE,EAAE,EACF,IAAI,EACJ,QAAQ,GAKT,EAAA;AAED,IAAA,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,OAAO,CAClC,4BAA4B,EAC5B,MAAM,EACN,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CACxB,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;IACrC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,CAAC,SAAS,EAAE;AAChD,QAAA,MAAM,EAAE,KAAK;AACb,QAAA,IAAI,EAAE,IAAI;AACX,KAAA,CAAC;AACF,IAAA,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE;QAChB,MAAM,IAAI,eAAe,CAAC,EAAE,OAAO,EAAE,uBAAuB,EAAE,IAAI,EAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC;IACpH;AACF;AAEO,eAAe,YAAY,CAAoB,EAAE,EAAE,EAAkB,EAAA;IAC1E,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,OAAO,CACpC,4BAA4B,EAC5B,MAAM,EACN,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CACxB,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,SAAS,EAAE;AAClD,QAAA,MAAM,EAAE,KAAK;AACd,KAAA,CAAC;AACF,IAAA,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE;QAChB,MAAM,IAAI,eAAe,CAAC;AACxB,YAAA,OAAO,EAAE,yBAAyB;YAClC,IAAI,EAAE,QAAQ,CAAC,MAAM;YACrB,KAAK,EAAE,QAAQ,CAAC,UAAU;AAC3B,SAAA,CAAC;IACJ;AACA,IAAA,OAAO,QAAQ,CAAC,WAAW,EAAE;AAC/B;;;;"}
@@ -1,4 +1,4 @@
1
- import { OystehrClientRequest, ZambdaCreateParams, ZambdaCreateResponse, ZambdaDeleteParams, ZambdaExecuteParams, ZambdaExecutePublicParams, ZambdaExecutePublicResponse, ZambdaExecuteResponse, ZambdaGetParams, ZambdaGetResponse, ZambdaListResponse, ZambdaS3UploadParams, ZambdaS3UploadResponse, ZambdaUpdateParams, ZambdaUpdateResponse } from '../..';
1
+ import { OystehrClientRequest, ZambdaCreateParams, ZambdaCreateResponse, ZambdaDeleteParams, ZambdaExecuteParams, ZambdaExecutePublicParams, ZambdaExecutePublicResponse, ZambdaExecuteResponse, ZambdaGetParams, ZambdaGetPresignedUrlParams, ZambdaGetPresignedUrlResponse, ZambdaGetResponse, ZambdaListResponse, ZambdaS3UploadParams, ZambdaS3UploadResponse, ZambdaUpdateParams, ZambdaUpdateResponse } from '../..';
2
2
  import { SDKResource } from '../../client/client';
3
3
  import { OystehrConfig } from '../../config';
4
4
  import * as ext from './zambda-ext';
@@ -6,6 +6,7 @@ export declare class Zambda extends SDKResource {
6
6
  #private;
7
7
  constructor(config: OystehrConfig);
8
8
  uploadFile: typeof ext.uploadFile;
9
+ downloadFile: typeof ext.downloadFile;
9
10
  /**
10
11
  * Get a list of all Zambda Functions in the Project. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.
11
12
  *
@@ -55,10 +56,20 @@ export declare class Zambda extends SDKResource {
55
56
  */
56
57
  executePublic(params: ZambdaExecutePublicParams, request?: OystehrClientRequest): Promise<ZambdaExecutePublicResponse>;
57
58
  /**
59
+ * **Deprecated.** Use `POST /zambda/{id}/presigned-url` with `action: "upload"` instead. This endpoint will be removed in future releases.
60
+ *
58
61
  * Returns a URL that is used to deploy code to the Zambda Function with the provided ID. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.
59
62
  *
60
63
  * Access Policy Action: `Zambda:UpdateFunction`
61
64
  * Access Policy Resource: `Zambda:Function`
62
65
  */
63
66
  s3Upload(params: ZambdaS3UploadParams, request?: OystehrClientRequest): Promise<ZambdaS3UploadResponse>;
67
+ /**
68
+ * Returns a presigned URL to upload or download code for the Zambda Function with the provided ID. Pass `action: "upload"` to get a URL for deploying a new code zip, or `action: "download"` to get a URL for retrieving the currently deployed zip. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.
69
+ *
70
+ * Upload: Access Policy Action: `Zambda:UpdateFunction`
71
+ * Download: Access Policy Action: `Zambda:GetFunctionSource`
72
+ * Access Policy Resource: `Zambda:Function`
73
+ */
74
+ getPresignedUrl(params: ZambdaGetPresignedUrlParams, request?: OystehrClientRequest): Promise<ZambdaGetPresignedUrlResponse>;
64
75
  }
@@ -1,5 +1,5 @@
1
1
  import { SDKResource } from '../../client/client.js';
2
- import { uploadFile } from './zambda-ext.js';
2
+ import { uploadFile, downloadFile } from './zambda-ext.js';
3
3
 
4
4
  // AUTOGENERATED -- DO NOT EDIT
5
5
  class Zambda extends SDKResource {
@@ -10,6 +10,7 @@ class Zambda extends SDKResource {
10
10
  return this.config.services?.['zambdaApiUrl'] ?? 'https://zambda-api.zapehr.com/v1';
11
11
  }
12
12
  uploadFile = uploadFile;
13
+ downloadFile = downloadFile;
13
14
  /**
14
15
  * Get a list of all Zambda Functions in the Project. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.
15
16
  *
@@ -73,6 +74,8 @@ class Zambda extends SDKResource {
73
74
  return this.request('/zambda/{id}/execute-public', 'post', this.#baseUrlThunk.bind(this))(params, request);
74
75
  }
75
76
  /**
77
+ * **Deprecated.** Use `POST /zambda/{id}/presigned-url` with `action: "upload"` instead. This endpoint will be removed in future releases.
78
+ *
76
79
  * Returns a URL that is used to deploy code to the Zambda Function with the provided ID. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.
77
80
  *
78
81
  * Access Policy Action: `Zambda:UpdateFunction`
@@ -81,6 +84,16 @@ class Zambda extends SDKResource {
81
84
  s3Upload(params, request) {
82
85
  return this.request('/zambda/{id}/s3-upload', 'post', this.#baseUrlThunk.bind(this))(params, request);
83
86
  }
87
+ /**
88
+ * Returns a presigned URL to upload or download code for the Zambda Function with the provided ID. Pass `action: "upload"` to get a URL for deploying a new code zip, or `action: "download"` to get a URL for retrieving the currently deployed zip. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.
89
+ *
90
+ * Upload: Access Policy Action: `Zambda:UpdateFunction`
91
+ * Download: Access Policy Action: `Zambda:GetFunctionSource`
92
+ * Access Policy Resource: `Zambda:Function`
93
+ */
94
+ getPresignedUrl(params, request) {
95
+ return this.request('/zambda/{id}/presigned-url', 'post', this.#baseUrlThunk.bind(this))(params, request);
96
+ }
84
97
  }
85
98
 
86
99
  export { Zambda };
@@ -1 +1 @@
1
- {"version":3,"file":"zambda.js","sources":["../../../../src/resources/classes/zambda.ts"],"sourcesContent":["// AUTOGENERATED -- DO NOT EDIT\n\nimport {\n OystehrClientRequest,\n ZambdaCreateParams,\n ZambdaCreateResponse,\n ZambdaDeleteParams,\n ZambdaExecuteParams,\n ZambdaExecutePublicParams,\n ZambdaExecutePublicResponse,\n ZambdaExecuteResponse,\n ZambdaGetParams,\n ZambdaGetResponse,\n ZambdaListResponse,\n ZambdaS3UploadParams,\n ZambdaS3UploadResponse,\n ZambdaUpdateParams,\n ZambdaUpdateResponse,\n} from '../..';\nimport { SDKResource } from '../../client/client';\nimport { OystehrConfig } from '../../config';\nimport * as ext from './zambda-ext';\n\nexport class Zambda extends SDKResource {\n constructor(config: OystehrConfig) {\n super(config);\n }\n #baseUrlThunk(): string {\n return this.config.services?.['zambdaApiUrl'] ?? 'https://zambda-api.zapehr.com/v1';\n }\n uploadFile = ext.uploadFile;\n /**\n * Get a list of all Zambda Functions in the Project. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Access Policy Action: `Zambda:ListAllFunctions`\n * Access Policy Resource: `Zambda:Function`\n */\n list(request?: OystehrClientRequest): Promise<ZambdaListResponse> {\n return this.request('/zambda', 'get', this.#baseUrlThunk.bind(this))(request);\n }\n /**\n * Create a new Zambda Function. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Access Policy Action: `Zambda:CreateFunction`\n * Access Policy Resource: `Zambda:Function`\n */\n create(params: ZambdaCreateParams, request?: OystehrClientRequest): Promise<ZambdaCreateResponse> {\n return this.request('/zambda', 'post', this.#baseUrlThunk.bind(this))(params, request);\n }\n /**\n * Get the Zambda Function with the provided ID or name. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Access Policy Action: `Zambda:GetFunction`\n * Access Policy Resource: `Zambda:Function`\n */\n get(params: ZambdaGetParams, request?: OystehrClientRequest): Promise<ZambdaGetResponse> {\n return this.request('/zambda/{id}', 'get', this.#baseUrlThunk.bind(this))(params, request);\n }\n /**\n * Update the Zambda Function with the provided ID or name. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Access Policy Action: `Zambda:UpdateFunction`\n * Access Policy Resource: `Zambda:Function`\n */\n update(params: ZambdaUpdateParams, request?: OystehrClientRequest): Promise<ZambdaUpdateResponse> {\n return this.request('/zambda/{id}', 'patch', this.#baseUrlThunk.bind(this))(params, request);\n }\n /**\n * Delete the Zambda Function with the provided ID or name. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Access Policy Action: `Zambda:DeleteFunction`\n * Access Policy Resource: `Zambda:Function`\n */\n delete(params: ZambdaDeleteParams, request?: OystehrClientRequest): Promise<void> {\n return this.request('/zambda/{id}', 'delete', this.#baseUrlThunk.bind(this))(params, request);\n }\n /**\n * Execute the [Authenticated Zambda Function](https://docs.oystehr.com/oystehr/services/zambda/types/authenticated/) with the provided ID. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Access Policy Action: `Zambda:InvokeFunction`\n * Access Policy Resource: `Zambda:Function`\n */\n execute(params: ZambdaExecuteParams, request?: OystehrClientRequest): Promise<ZambdaExecuteResponse> {\n return this.request('/zambda/{id}/execute', 'post', this.#baseUrlThunk.bind(this))(params, request);\n }\n /**\n * Execute the [Public Zambda Function](https://docs.oystehr.com/oystehr/services/zambda/types/public/) with the provided ID. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Execute a zambda that has method http_open. This endpoint is public so there are no access policy requirements.\n */\n executePublic(\n params: ZambdaExecutePublicParams,\n request?: OystehrClientRequest\n ): Promise<ZambdaExecutePublicResponse> {\n return this.request('/zambda/{id}/execute-public', 'post', this.#baseUrlThunk.bind(this))(params, request);\n }\n /**\n * Returns a URL that is used to deploy code to the Zambda Function with the provided ID. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Access Policy Action: `Zambda:UpdateFunction`\n * Access Policy Resource: `Zambda:Function`\n */\n s3Upload(params: ZambdaS3UploadParams, request?: OystehrClientRequest): Promise<ZambdaS3UploadResponse> {\n return this.request('/zambda/{id}/s3-upload', 'post', this.#baseUrlThunk.bind(this))(params, request);\n }\n}\n"],"names":["ext.uploadFile"],"mappings":";;;AAAA;AAuBM,MAAO,MAAO,SAAQ,WAAW,CAAA;AACrC,IAAA,WAAA,CAAY,MAAqB,EAAA;QAC/B,KAAK,CAAC,MAAM,CAAC;IACf;IACA,aAAa,GAAA;QACX,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,cAAc,CAAC,IAAI,kCAAkC;IACrF;AACA,IAAA,UAAU,GAAGA,UAAc;AAC3B;;;;;AAKG;AACH,IAAA,IAAI,CAAC,OAA8B,EAAA;QACjC,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;IAC/E;AACA;;;;;AAKG;IACH,MAAM,CAAC,MAA0B,EAAE,OAA8B,EAAA;QAC/D,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC;IACxF;AACA;;;;;AAKG;IACH,GAAG,CAAC,MAAuB,EAAE,OAA8B,EAAA;QACzD,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC;IAC5F;AACA;;;;;AAKG;IACH,MAAM,CAAC,MAA0B,EAAE,OAA8B,EAAA;QAC/D,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC;IAC9F;AACA;;;;;AAKG;IACH,MAAM,CAAC,MAA0B,EAAE,OAA8B,EAAA;QAC/D,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,QAAQ,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/F;AACA;;;;;AAKG;IACH,OAAO,CAAC,MAA2B,EAAE,OAA8B,EAAA;QACjE,OAAO,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC;IACrG;AACA;;;;AAIG;IACH,aAAa,CACX,MAAiC,EACjC,OAA8B,EAAA;QAE9B,OAAO,IAAI,CAAC,OAAO,CAAC,6BAA6B,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC;IAC5G;AACA;;;;;AAKG;IACH,QAAQ,CAAC,MAA4B,EAAE,OAA8B,EAAA;QACnE,OAAO,IAAI,CAAC,OAAO,CAAC,wBAAwB,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC;IACvG;AACD;;;;"}
1
+ {"version":3,"file":"zambda.js","sources":["../../../../src/resources/classes/zambda.ts"],"sourcesContent":["// AUTOGENERATED -- DO NOT EDIT\n\nimport {\n OystehrClientRequest,\n ZambdaCreateParams,\n ZambdaCreateResponse,\n ZambdaDeleteParams,\n ZambdaExecuteParams,\n ZambdaExecutePublicParams,\n ZambdaExecutePublicResponse,\n ZambdaExecuteResponse,\n ZambdaGetParams,\n ZambdaGetPresignedUrlParams,\n ZambdaGetPresignedUrlResponse,\n ZambdaGetResponse,\n ZambdaListResponse,\n ZambdaS3UploadParams,\n ZambdaS3UploadResponse,\n ZambdaUpdateParams,\n ZambdaUpdateResponse,\n} from '../..';\nimport { SDKResource } from '../../client/client';\nimport { OystehrConfig } from '../../config';\nimport * as ext from './zambda-ext';\n\nexport class Zambda extends SDKResource {\n constructor(config: OystehrConfig) {\n super(config);\n }\n #baseUrlThunk(): string {\n return this.config.services?.['zambdaApiUrl'] ?? 'https://zambda-api.zapehr.com/v1';\n }\n uploadFile = ext.uploadFile;\n downloadFile = ext.downloadFile;\n /**\n * Get a list of all Zambda Functions in the Project. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Access Policy Action: `Zambda:ListAllFunctions`\n * Access Policy Resource: `Zambda:Function`\n */\n list(request?: OystehrClientRequest): Promise<ZambdaListResponse> {\n return this.request('/zambda', 'get', this.#baseUrlThunk.bind(this))(request);\n }\n /**\n * Create a new Zambda Function. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Access Policy Action: `Zambda:CreateFunction`\n * Access Policy Resource: `Zambda:Function`\n */\n create(params: ZambdaCreateParams, request?: OystehrClientRequest): Promise<ZambdaCreateResponse> {\n return this.request('/zambda', 'post', this.#baseUrlThunk.bind(this))(params, request);\n }\n /**\n * Get the Zambda Function with the provided ID or name. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Access Policy Action: `Zambda:GetFunction`\n * Access Policy Resource: `Zambda:Function`\n */\n get(params: ZambdaGetParams, request?: OystehrClientRequest): Promise<ZambdaGetResponse> {\n return this.request('/zambda/{id}', 'get', this.#baseUrlThunk.bind(this))(params, request);\n }\n /**\n * Update the Zambda Function with the provided ID or name. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Access Policy Action: `Zambda:UpdateFunction`\n * Access Policy Resource: `Zambda:Function`\n */\n update(params: ZambdaUpdateParams, request?: OystehrClientRequest): Promise<ZambdaUpdateResponse> {\n return this.request('/zambda/{id}', 'patch', this.#baseUrlThunk.bind(this))(params, request);\n }\n /**\n * Delete the Zambda Function with the provided ID or name. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Access Policy Action: `Zambda:DeleteFunction`\n * Access Policy Resource: `Zambda:Function`\n */\n delete(params: ZambdaDeleteParams, request?: OystehrClientRequest): Promise<void> {\n return this.request('/zambda/{id}', 'delete', this.#baseUrlThunk.bind(this))(params, request);\n }\n /**\n * Execute the [Authenticated Zambda Function](https://docs.oystehr.com/oystehr/services/zambda/types/authenticated/) with the provided ID. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Access Policy Action: `Zambda:InvokeFunction`\n * Access Policy Resource: `Zambda:Function`\n */\n execute(params: ZambdaExecuteParams, request?: OystehrClientRequest): Promise<ZambdaExecuteResponse> {\n return this.request('/zambda/{id}/execute', 'post', this.#baseUrlThunk.bind(this))(params, request);\n }\n /**\n * Execute the [Public Zambda Function](https://docs.oystehr.com/oystehr/services/zambda/types/public/) with the provided ID. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Execute a zambda that has method http_open. This endpoint is public so there are no access policy requirements.\n */\n executePublic(\n params: ZambdaExecutePublicParams,\n request?: OystehrClientRequest\n ): Promise<ZambdaExecutePublicResponse> {\n return this.request('/zambda/{id}/execute-public', 'post', this.#baseUrlThunk.bind(this))(params, request);\n }\n /**\n * **Deprecated.** Use `POST /zambda/{id}/presigned-url` with `action: \"upload\"` instead. This endpoint will be removed in future releases.\n *\n * Returns a URL that is used to deploy code to the Zambda Function with the provided ID. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Access Policy Action: `Zambda:UpdateFunction`\n * Access Policy Resource: `Zambda:Function`\n */\n s3Upload(params: ZambdaS3UploadParams, request?: OystehrClientRequest): Promise<ZambdaS3UploadResponse> {\n return this.request('/zambda/{id}/s3-upload', 'post', this.#baseUrlThunk.bind(this))(params, request);\n }\n /**\n * Returns a presigned URL to upload or download code for the Zambda Function with the provided ID. Pass `action: \"upload\"` to get a URL for deploying a new code zip, or `action: \"download\"` to get a URL for retrieving the currently deployed zip. [Zambdas](https://docs.oystehr.com/oystehr/services/zambda/) are functions that can be used to execute your code. They can be used to process data received from Oystehr's APIs or to perform operations on third-party services.\n *\n * Upload: Access Policy Action: `Zambda:UpdateFunction`\n * Download: Access Policy Action: `Zambda:GetFunctionSource`\n * Access Policy Resource: `Zambda:Function`\n */\n getPresignedUrl(\n params: ZambdaGetPresignedUrlParams,\n request?: OystehrClientRequest\n ): Promise<ZambdaGetPresignedUrlResponse> {\n return this.request('/zambda/{id}/presigned-url', 'post', this.#baseUrlThunk.bind(this))(params, request);\n }\n}\n"],"names":["ext.uploadFile","ext.downloadFile"],"mappings":";;;AAAA;AAyBM,MAAO,MAAO,SAAQ,WAAW,CAAA;AACrC,IAAA,WAAA,CAAY,MAAqB,EAAA;QAC/B,KAAK,CAAC,MAAM,CAAC;IACf;IACA,aAAa,GAAA;QACX,OAAO,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,cAAc,CAAC,IAAI,kCAAkC;IACrF;AACA,IAAA,UAAU,GAAGA,UAAc;AAC3B,IAAA,YAAY,GAAGC,YAAgB;AAC/B;;;;;AAKG;AACH,IAAA,IAAI,CAAC,OAA8B,EAAA;QACjC,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC;IAC/E;AACA;;;;;AAKG;IACH,MAAM,CAAC,MAA0B,EAAE,OAA8B,EAAA;QAC/D,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC;IACxF;AACA;;;;;AAKG;IACH,GAAG,CAAC,MAAuB,EAAE,OAA8B,EAAA;QACzD,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC;IAC5F;AACA;;;;;AAKG;IACH,MAAM,CAAC,MAA0B,EAAE,OAA8B,EAAA;QAC/D,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC;IAC9F;AACA;;;;;AAKG;IACH,MAAM,CAAC,MAA0B,EAAE,OAA8B,EAAA;QAC/D,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,QAAQ,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC;IAC/F;AACA;;;;;AAKG;IACH,OAAO,CAAC,MAA2B,EAAE,OAA8B,EAAA;QACjE,OAAO,IAAI,CAAC,OAAO,CAAC,sBAAsB,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC;IACrG;AACA;;;;AAIG;IACH,aAAa,CACX,MAAiC,EACjC,OAA8B,EAAA;QAE9B,OAAO,IAAI,CAAC,OAAO,CAAC,6BAA6B,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC;IAC5G;AACA;;;;;;;AAOG;IACH,QAAQ,CAAC,MAA4B,EAAE,OAA8B,EAAA;QACnE,OAAO,IAAI,CAAC,OAAO,CAAC,wBAAwB,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC;IACvG;AACA;;;;;;AAMG;IACH,eAAe,CACb,MAAmC,EACnC,OAA8B,EAAA;QAE9B,OAAO,IAAI,CAAC,OAAO,CAAC,4BAA4B,EAAE,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC;IAC3G;AACD;;;;"}
@@ -0,0 +1,11 @@
1
+ export interface ZambdaGetPresignedUrlParams {
2
+ /**
3
+ * Whether to get a presigned URL for uploading or downloading the Zambda Function code zip.
4
+ */
5
+ action: 'upload' | 'download';
6
+ /**
7
+ * Optional filename for the Zambda Function code zip (upload only). If not provided, the filename will be randomized.
8
+ */
9
+ filename?: string;
10
+ id: string;
11
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The presigned URL for the requested action.
3
+ */
4
+ export interface ZambdaGetPresignedUrlResponse {
5
+ /**
6
+ * The presigned URL to upload or download the Zambda Function code zip.
7
+ */
8
+ signedUrl: string;
9
+ }
@@ -117,4 +117,71 @@ export type BatchInputRequest<F extends FhirResource> = BatchInputGetRequest | B
117
117
  export interface BatchInput<F extends FhirResource> {
118
118
  requests: BatchInputRequest<F>[];
119
119
  }
120
+ export interface FhirAsyncJobHandle {
121
+ jobId: string;
122
+ contentLocation: string;
123
+ mode: FhirAsyncResponseMode;
124
+ }
125
+ export type FhirResponseMode = 'sync' | 'async-bundle' | 'async-bulk';
126
+ export type FhirAsyncResponseMode = 'bundle' | 'bulk';
127
+ export interface FhirAsyncCompletionBundleEntry<T extends FhirResource> {
128
+ response?: {
129
+ status?: string;
130
+ outcome?: OperationOutcome;
131
+ };
132
+ resource?: T | OperationOutcome;
133
+ }
134
+ export type FhirAsyncCompletionBundle<T extends FhirResource> = EntrylessFhirBundle<T | OperationOutcome> & {
135
+ resourceType: 'Bundle';
136
+ type: 'batch-response';
137
+ entry?: Array<FhirAsyncCompletionBundleEntry<T>>;
138
+ };
139
+ export interface FhirAsyncJobInProgress {
140
+ status: 202;
141
+ xProgress?: string;
142
+ retryAfter?: string;
143
+ }
144
+ export interface FhirAsyncJobCompletedBundle<T extends FhirResource = FhirResource> {
145
+ status: 200;
146
+ mode: 'bundle';
147
+ bundle: FhirAsyncCompletionBundle<T>;
148
+ interactionStatus?: string;
149
+ resource?: T | OperationOutcome;
150
+ outcome?: OperationOutcome;
151
+ }
152
+ export interface FhirAsyncBulkOutputFile {
153
+ type: string;
154
+ url: string;
155
+ }
156
+ export interface FhirAsyncBulkManifest {
157
+ transactionTime: string;
158
+ request: string;
159
+ requiresAccessToken: boolean;
160
+ output: FhirAsyncBulkOutputFile[];
161
+ error: FhirAsyncBulkOutputFile[];
162
+ deleted?: FhirAsyncBulkOutputFile[];
163
+ extension?: Record<string, unknown>;
164
+ }
165
+ export interface FhirAsyncJobCompletedBulk {
166
+ status: 200;
167
+ mode: 'bulk';
168
+ manifest: FhirAsyncBulkManifest;
169
+ }
170
+ export interface FhirAsyncJobExpired {
171
+ status: 410;
172
+ }
173
+ export interface FhirAsyncJobNotFound {
174
+ status: 404;
175
+ }
176
+ export interface FhirAsyncJobUnexpected {
177
+ status: Exclude<number, 200 | 202 | 404 | 410>;
178
+ body: unknown;
179
+ }
180
+ export type FhirAsyncJobStatus<T extends FhirResource = FhirResource> = FhirAsyncJobInProgress | FhirAsyncJobCompletedBundle<T> | FhirAsyncJobCompletedBulk | FhirAsyncJobExpired | FhirAsyncJobNotFound | FhirAsyncJobUnexpected;
181
+ export interface FhirAsyncWaitOptions {
182
+ /** Poll interval in milliseconds. Defaults to 1000 (1 second). */
183
+ pollIntervalMs: number;
184
+ /** Maximum wait time in milliseconds before timing out. Defaults to 900000 (15 minutes). */
185
+ timeoutMs: number;
186
+ }
120
187
  export {};
@@ -192,6 +192,8 @@ export * from './ZambdaLogStreamListParams';
192
192
  export * from './ZambdaLogStreamListResponse';
193
193
  export * from './ZambdaS3UploadParams';
194
194
  export * from './ZambdaS3UploadResponse';
195
+ export * from './ZambdaGetPresignedUrlParams';
196
+ export * from './ZambdaGetPresignedUrlResponse';
195
197
  export * from './ZambdaLogStreamSearchParams';
196
198
  export * from './ZambdaLogStreamSearchResponse';
197
199
  export * from './ZambdaLogStreamGetParams';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oystehr/sdk",
3
- "version": "4.3.7",
3
+ "version": "4.3.9",
4
4
  "description": "Oystehr SDK",
5
5
  "scripts": {
6
6
  "lint": "eslint .",
@@ -2,7 +2,16 @@ import { v4 as uuidv4, validate as uuidValidate } from 'uuid';
2
2
  import { OystehrConfig } from '../config';
3
3
  import { OystehrFHIRError, OystehrSdkError } from '../errors';
4
4
  import { Logger } from '../logger';
5
- import { FhirBundle, FhirResource, OperationOutcome } from '../resources/types';
5
+ import {
6
+ FhirAsyncBulkManifest,
7
+ FhirAsyncCompletionBundle,
8
+ FhirAsyncJobHandle,
9
+ FhirAsyncJobStatus,
10
+ FhirBundle,
11
+ FhirResource,
12
+ FhirResponseMode,
13
+ OperationOutcome,
14
+ } from '../resources/types';
6
15
 
7
16
  type HttpMethod = 'get' | 'put' | 'post' | 'delete' | 'options' | 'head' | 'patch' | 'trace';
8
17
  export const defaultProjectApiUrl = 'https://project-api.zapehr.com/v1';
@@ -41,10 +50,19 @@ export interface OystehrClientRequest {
41
50
  * Unique identifier for this request.
42
51
  */
43
52
  requestId?: string;
53
+ /**
54
+ * Optional execution mode for FHIR requests that support async behavior.
55
+ * Defaults to `sync` when omitted.
56
+ */
57
+ mode?: FhirResponseMode;
44
58
  }
45
59
 
46
60
  interface InternalClientRequest extends OystehrClientRequest {
47
61
  ifMatch?: string;
62
+ /**
63
+ * Internal-only: returns raw response metadata ({ status, headers, body }) instead of throwing on non-2xx statuses.
64
+ */
65
+ rawResponse?: boolean;
48
66
  }
49
67
 
50
68
  type FhirData<T extends FhirResource> = T | T[] | FhirBundle<T>;
@@ -75,14 +93,22 @@ export class SDKResource {
75
93
  };
76
94
  }
77
95
 
78
- protected fhirRequest<T extends FhirResource = any>(path: string, method: string) {
79
- return async (params: any, request?: InternalClientRequest): Promise<FhirFetcherResponse<T>> => {
96
+ protected fhirRequest<T = FhirResource>(
97
+ path: string,
98
+ method: string
99
+ ): {
100
+ (params: any, request: InternalClientRequest & { rawResponse: true }, requestMode?: FhirResponseMode): Promise<
101
+ RawFetcherResponse<T>
102
+ >;
103
+ (params: any, request?: InternalClientRequest, requestMode?: FhirResponseMode): Promise<T>;
104
+ } {
105
+ return async (params: any, request?: InternalClientRequest, requestMode?: FhirResponseMode): Promise<any> => {
80
106
  try {
81
107
  const baseUrlThunk = (): string => this.config.services?.fhirApiUrl ?? defaultFhirApiUrl;
82
108
  const configThunk = (): OystehrConfig => this.config;
83
109
  const loggerThunk = (): Logger => this.logger;
84
110
  // must await here to catch
85
- return await fetcher(baseUrlThunk, configThunk, loggerThunk, path, method)(params, request);
111
+ return await fetcher(baseUrlThunk, configThunk, loggerThunk, path, method)(params, request, requestMode);
86
112
  } catch (err: unknown) {
87
113
  // FHIR API error messages are JSON strings
88
114
  const fullError = err as { message: string | Record<string, any>; code: number; cause?: unknown };
@@ -100,13 +126,170 @@ export class SDKResource {
100
126
  }
101
127
  };
102
128
  }
129
+
130
+ protected async startAsyncJob(
131
+ path: string,
132
+ method: string,
133
+ params: Record<string, unknown>,
134
+ requestMode: FhirResponseMode,
135
+ request?: InternalClientRequest
136
+ ): Promise<FhirAsyncJobHandle> {
137
+ const mode = requestMode === 'async-bulk' ? 'bulk' : 'bundle';
138
+ const asyncPath = requestMode === 'async-bulk' ? this.appendBulkOutputFormat(path) : path;
139
+
140
+ const raw = await this.fhirRequest(asyncPath, method)(
141
+ params,
142
+ {
143
+ ...request,
144
+ rawResponse: true,
145
+ },
146
+ requestMode
147
+ );
148
+
149
+ if (raw.status !== 202) {
150
+ throw new OystehrSdkError({
151
+ message: `Expected start async job to return 202 Accepted, received ${raw.status}`,
152
+ code: raw.status,
153
+ });
154
+ }
155
+
156
+ const contentLocation = this.readHeader(raw.headers, 'content-location');
157
+ if (!contentLocation) {
158
+ throw new OystehrSdkError({
159
+ message: 'Start Async job response missing Content-Location header',
160
+ code: 500,
161
+ });
162
+ }
163
+
164
+ const jobId = this.parseAsyncJobId(contentLocation);
165
+ if (!jobId) {
166
+ throw new OystehrSdkError({
167
+ message: `Could not parse async job id from Content-Location: ${contentLocation}`,
168
+ code: 500,
169
+ });
170
+ }
171
+
172
+ return { jobId, contentLocation, mode };
173
+ }
174
+
175
+ protected async fetchAsyncJobStatus<T extends FhirResource>(
176
+ jobId: string,
177
+ request?: OystehrClientRequest
178
+ ): Promise<FhirAsyncJobStatus<T>> {
179
+ const raw = await this.fhirRequest(`/async-job/${jobId}`, 'GET')(
180
+ {},
181
+ {
182
+ ...request,
183
+ rawResponse: true,
184
+ }
185
+ );
186
+
187
+ if (raw.status === 202) {
188
+ return {
189
+ status: 202,
190
+ xProgress: this.readHeader(raw.headers, 'x-progress'),
191
+ retryAfter: this.readHeader(raw.headers, 'retry-after'),
192
+ };
193
+ }
194
+
195
+ if (raw.status === 410) {
196
+ return { status: 410 };
197
+ }
198
+
199
+ if (raw.status === 404) {
200
+ return { status: 404 };
201
+ }
202
+
203
+ if (raw.status === 200) {
204
+ if (this.isBulkManifest(raw.body)) {
205
+ return {
206
+ status: 200,
207
+ mode: 'bulk',
208
+ manifest: raw.body,
209
+ };
210
+ }
211
+
212
+ const bundle = raw.body as FhirAsyncCompletionBundle<T>;
213
+ if (bundle?.resourceType === 'Bundle' && bundle?.type === 'batch-response') {
214
+ const entry0 = bundle.entry?.[0];
215
+ const interactionStatus = entry0?.response?.status;
216
+ const resource = entry0?.resource;
217
+ const outcome = entry0?.response?.outcome;
218
+ return {
219
+ status: 200,
220
+ mode: 'bundle',
221
+ bundle,
222
+ interactionStatus,
223
+ resource,
224
+ outcome,
225
+ };
226
+ }
227
+
228
+ return {
229
+ status: 200,
230
+ body: raw.body,
231
+ };
232
+ }
233
+
234
+ return {
235
+ status: raw.status,
236
+ body: raw.body,
237
+ };
238
+ }
239
+
240
+ private readHeader(headers: Record<string, string>, name: string): string | undefined {
241
+ const direct = headers[name];
242
+ if (direct != null) {
243
+ return direct;
244
+ }
245
+
246
+ const lower = name.toLowerCase();
247
+ const key = Object.keys(headers).find((h) => h.toLowerCase() === lower);
248
+ return key ? headers[key] : undefined;
249
+ }
250
+
251
+ private parseAsyncJobId(contentLocation: string): string | undefined {
252
+ const segments = contentLocation.split('/').filter(Boolean);
253
+ const asyncJobIndex = segments.lastIndexOf('async-job');
254
+ if (asyncJobIndex < 0 || asyncJobIndex + 1 >= segments.length) {
255
+ return undefined;
256
+ }
257
+
258
+ return segments[asyncJobIndex + 1];
259
+ }
260
+
261
+ private appendBulkOutputFormat(path: string): string {
262
+ const separator = path.includes('?') ? '&' : '?';
263
+ return `${path}${separator}_outputFormat=${encodeURIComponent('application/fhir+ndjson')}`;
264
+ }
265
+
266
+ private isBulkManifest(body: unknown): body is FhirAsyncBulkManifest {
267
+ if (body == null || typeof body !== 'object') {
268
+ return false;
269
+ }
270
+
271
+ const maybe = body as Record<string, unknown>;
272
+ return (
273
+ typeof maybe.transactionTime === 'string' &&
274
+ typeof maybe.request === 'string' &&
275
+ typeof maybe.requiresAccessToken === 'boolean' &&
276
+ Array.isArray(maybe.output) &&
277
+ Array.isArray(maybe.error)
278
+ );
279
+ }
103
280
  }
104
281
 
105
282
  export type FetcherError = { message: string; code: number };
106
283
  export type FetcherResponse = any;
284
+ export type RawFetcherResponse<T = unknown> = {
285
+ status: number;
286
+ headers: Record<string, string>;
287
+ body: T | null;
288
+ };
107
289
  export type FetcherFunction = (
108
290
  params?: Record<string, any> | [any] | InternalClientRequest,
109
- request?: InternalClientRequest
291
+ request?: InternalClientRequest,
292
+ requestMode?: FhirResponseMode
110
293
  ) => Promise<FetcherResponse>;
111
294
 
112
295
  function isInternalClientRequest(request: Record<string, any>): request is InternalClientRequest {
@@ -115,10 +298,19 @@ function isInternalClientRequest(request: Record<string, any>): request is Inter
115
298
  ('projectId' in request && uuidValidate(request.projectId)) ||
116
299
  ('contentType' in request && request.contentType?.split('/').length === 2) ||
117
300
  'requestId' in request ||
118
- ('ifMatch' in request && request.ifMatch.startsWith('W/"'))
301
+ ('ifMatch' in request && request.ifMatch.startsWith('W/"')) ||
302
+ 'mode' in request ||
303
+ 'rawResponse' in request
119
304
  );
120
305
  }
121
306
 
307
+ function getPreferHeaderFromMode(mode: FhirResponseMode | undefined): string | undefined {
308
+ if (mode === 'async-bundle' || mode === 'async-bulk') {
309
+ return 'respond-async';
310
+ }
311
+ return undefined;
312
+ }
313
+
122
314
  /**
123
315
  * Parse XML response in format <response><status>...</status><output>...</output></response>
124
316
  */
@@ -151,7 +343,8 @@ function fetcher(
151
343
  ): FetcherFunction {
152
344
  return async (
153
345
  params?: Record<string, unknown> | [any] | InternalClientRequest,
154
- request?: InternalClientRequest
346
+ request?: InternalClientRequest,
347
+ requestMode?: FhirResponseMode
155
348
  ): Promise<FetcherResponse> => {
156
349
  // this function supports multiple signatures. fetcher(baseUrl, path, method)(params, request) or fetcher(baseUrl, path, method)(request)
157
350
  // or fetcher(baseUrl, path, method)(params) or fetcher(baseUrl, path, method)(). the types for this are handled by Client<Path, Methods>
@@ -214,6 +407,8 @@ function fetcher(
214
407
  requestId: requestCtx?.requestId,
215
408
  });
216
409
 
410
+ const preferHeader = getPreferHeaderFromMode(requestMode);
411
+
217
412
  const headers: Record<string, string> = Object.assign(
218
413
  projectId
219
414
  ? {
@@ -224,6 +419,7 @@ function fetcher(
224
419
  {
225
420
  'content-type': requestCtx?.contentType ?? 'application/json',
226
421
  },
422
+ preferHeader ? { Prefer: preferHeader } : {},
227
423
  accessToken ? { Authorization: `Bearer ${accessToken}` } : {},
228
424
  requestCtx?.ifMatch ? { 'If-Match': requestCtx.ifMatch } : {},
229
425
  { 'x-oystehr-request-id': requestCtx?.requestId }
@@ -301,6 +497,17 @@ function fetcher(
301
497
  url,
302
498
  requestId: requestCtx?.requestId,
303
499
  });
500
+ if (requestCtx?.rawResponse) {
501
+ const headersRecord: Record<string, string> = {};
502
+ response.headers.forEach((value, key) => {
503
+ headersRecord[key] = value;
504
+ });
505
+ return {
506
+ status: response.status,
507
+ headers: headersRecord,
508
+ body: responseJson ?? responseBody,
509
+ } as RawFetcherResponse;
510
+ }
304
511
  const isError = !response.ok || response.status >= 400;
305
512
  if (isError) {
306
513
  const errObj = {