@medplum/fhir-router 5.1.35 → 5.1.37

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.
@@ -19,7 +19,14 @@ import type { WithId } from '@medplum/core';
19
19
  export declare interface BatchEvent extends Event_2 {
20
20
  bundleType: Bundle['type'];
21
21
  count?: number;
22
+ /** Error messages from the entries processed by this processor. */
22
23
  errors?: string[];
24
+ /**
25
+ * Total failed entries, when that is known more accurately than `errors`. A re-entrant (async)
26
+ * batch resumed after a restart only holds the errors from its own run, so its caller supplies
27
+ * the count from the fully assembled response bundle instead.
28
+ */
29
+ errorCount?: number;
23
30
  size?: number;
24
31
  }
25
32
 
@@ -111,6 +118,11 @@ export declare class BatchProcessor {
111
118
  * For the re-entrant flow, the returned {@link BatchInitialState} should be persisted to
112
119
  * durable storage before processing entries so that the processor can be rehydrated via
113
120
  * {@link BatchProcessor.fromState} after a restart.
121
+ *
122
+ * Dispatches the pre-batch telemetry event once the bundle is known to be well formed. This is
123
+ * the one step both flows perform exactly once — a transaction retry reuses the existing
124
+ * preprocessing, and a resumed async job enters through {@link BatchProcessor.fromState} — so
125
+ * the event describes the bundle once however it is processed.
114
126
  * @returns The durable initial state for the batch.
115
127
  */
116
128
  preprocess(): Promise<BatchInitialState>;
@@ -133,8 +145,20 @@ export declare class BatchProcessor {
133
145
  * if no new entries were produced.
134
146
  */
135
147
  takePendingResults(): Record<number, BundleEntry> | undefined;
148
+ /**
149
+ * Processes remaining entries. Re-run per attempt for transactions, so emit no telemetry.
150
+ * @returns The response bundle.
151
+ */
136
152
  private processEntriesAndBuild;
137
153
  private dispatchPreEvent;
154
+ /**
155
+ * Dispatches the post-batch telemetry event, closing out the pre-event sent by
156
+ * {@link BatchProcessor.preprocess}.
157
+ *
158
+ * Only the one-shot {@link BatchProcessor.run} flow uses this. The re-entrant flow spans multiple
159
+ * processors, so no single instance knows when the batch is finished or what every run before it
160
+ * produced; its driver emits the terminal event itself once the job reaches a terminal state.
161
+ */
138
162
  private dispatchPostEvent;
139
163
  /**
140
164
  * Assembles the response bundle from the result entries recorded on this instance. Used by the
@@ -19,7 +19,14 @@ import type { WithId } from '@medplum/core';
19
19
  export declare interface BatchEvent extends Event_2 {
20
20
  bundleType: Bundle['type'];
21
21
  count?: number;
22
+ /** Error messages from the entries processed by this processor. */
22
23
  errors?: string[];
24
+ /**
25
+ * Total failed entries, when that is known more accurately than `errors`. A re-entrant (async)
26
+ * batch resumed after a restart only holds the errors from its own run, so its caller supplies
27
+ * the count from the fully assembled response bundle instead.
28
+ */
29
+ errorCount?: number;
23
30
  size?: number;
24
31
  }
25
32
 
@@ -111,6 +118,11 @@ export declare class BatchProcessor {
111
118
  * For the re-entrant flow, the returned {@link BatchInitialState} should be persisted to
112
119
  * durable storage before processing entries so that the processor can be rehydrated via
113
120
  * {@link BatchProcessor.fromState} after a restart.
121
+ *
122
+ * Dispatches the pre-batch telemetry event once the bundle is known to be well formed. This is
123
+ * the one step both flows perform exactly once — a transaction retry reuses the existing
124
+ * preprocessing, and a resumed async job enters through {@link BatchProcessor.fromState} — so
125
+ * the event describes the bundle once however it is processed.
114
126
  * @returns The durable initial state for the batch.
115
127
  */
116
128
  preprocess(): Promise<BatchInitialState>;
@@ -133,8 +145,20 @@ export declare class BatchProcessor {
133
145
  * if no new entries were produced.
134
146
  */
135
147
  takePendingResults(): Record<number, BundleEntry> | undefined;
148
+ /**
149
+ * Processes remaining entries. Re-run per attempt for transactions, so emit no telemetry.
150
+ * @returns The response bundle.
151
+ */
136
152
  private processEntriesAndBuild;
137
153
  private dispatchPreEvent;
154
+ /**
155
+ * Dispatches the post-batch telemetry event, closing out the pre-event sent by
156
+ * {@link BatchProcessor.preprocess}.
157
+ *
158
+ * Only the one-shot {@link BatchProcessor.run} flow uses this. The re-entrant flow spans multiple
159
+ * processors, so no single instance knows when the batch is finished or what every run before it
160
+ * produced; its driver emits the terminal event itself once the job reaches a terminal state.
161
+ */
138
162
  private dispatchPostEvent;
139
163
  /**
140
164
  * Assembles the response bundle from the result entries recorded on this instance. Used by the
@@ -1,4 +1,4 @@
1
- var xo=Object.defineProperty;var Ma=(t,e,r)=>e in t?xo(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var Va=(t,e)=>{for(var r in e)xo(t,r,{get:e[r],enumerable:!0})};var C=(t,e,r)=>Ma(t,typeof e!="symbol"?e+"":e,r);import{badRequest as Z,getReferenceString as Ut,getStatus as Do,isOk as Lo,normalizeOperationOutcome as Ua,notFound as Oo,OperationOutcomeError as q,parseSearchRequest as wo}from"@medplum/core";var $a=50,Ga=8,Ba=/urn(:|%3A)uuid(:|%3A)[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/;async function bn(t,e,r,n){return new gr(r,e,n,t).run()}var gr=class t{constructor(e,r,n,i){C(this,"router");C(this,"repo");C(this,"bundle");C(this,"req");C(this,"resolvedIdentities");C(this,"bundleInfo");C(this,"state");this.router=e,this.repo=r,this.bundle=n,this.req=i,this.resolvedIdentities=Object.create(null),this.state={position:0,resultEntries:new Array(n.entry?.length??0),pendingIndices:[],errors:[]}}static fromState(e,r,n,i,o){let s=new t(e,r,i.bundle,n);return s.resolvedIdentities=i.resolvedIdentities,s.bundleInfo={...i.bundleInfo},s.state.position=o,s}async run(){await this.preprocess();let e=this.bundleInfo;if(!this.isTransaction())return this.processEntriesAndBuild();let r=_o(this.state);return this.repo.withTransaction(n=>this.withRepo(n,()=>(this.state=_o(r),this.processEntriesAndBuild())),{serializable:e.requiresStrongTransaction,resourceTypes:e.resourceTypes})}async preprocess(){let e=this.bundle.type;if(e!=="batch"&&e!=="transaction")throw new q(Z("Unrecognized bundle type: "+e));if(this.bundleInfo=await this.preprocessBundle(),this.isTransaction()){if(this.bundleInfo.updates>$a)throw new q(Z("Transaction contains more update operations than allowed"));if(this.bundleInfo.requiresStrongTransaction&&this.state.resultEntries.length>Ga)throw new q(Z("Transaction requires strict isolation but has too many entries"))}let r=Object.create(null);for(let n=0;n<this.state.resultEntries.length;n++)this.state.resultEntries[n]&&(r[n]=this.state.resultEntries[n]);return{bundle:this.bundle,bundleInfo:{ordering:this.bundleInfo.ordering,resourceTypes:this.bundleInfo.resourceTypes,requiresStrongTransaction:this.bundleInfo.requiresStrongTransaction,updates:this.bundleInfo.updates},resolvedIdentities:this.resolvedIdentities,preprocessResults:r}}hasMoreEntries(){return this.bundleInfo!==void 0&&this.state.position<this.bundleInfo.ordering.length}getPosition(){return this.state.position}takePendingResults(){if(this.state.pendingIndices.length===0)return;let e=Object.create(null);for(let r of this.state.pendingIndices)e[r]=this.state.resultEntries[r];return this.state.pendingIndices=[],e}async processEntriesAndBuild(){for(this.dispatchPreEvent();this.hasMoreEntries();)await this.processNextEntryImpl();return this.dispatchPostEvent(),this.buildResultBundle()}dispatchPreEvent(){let e={type:"batch",bundleType:this.bundle.type,count:this.bundle.entry?.length,size:JSON.stringify(this.bundle).length};this.router.dispatchEvent(e)}dispatchPostEvent(){let e={type:"batch",bundleType:this.bundle.type,errors:this.state.errors.length?this.state.errors:void 0};this.router.dispatchEvent(e)}buildResultBundle(){return{resourceType:"Bundle",type:`${this.bundle.type}-response`,entry:this.state.resultEntries}}async withRepo(e,r){let n=this.repo;this.repo=e;try{return await r()}finally{this.repo=n}}async preprocessBundle(){let e=this.bundle.entry;if(!e?.length)throw new q(Z("Missing bundle entries"));let r={transaction:[],batch:[],delete:[],create:[],update:[],patch:[],operation:[],"search-system":[],"search-type":[],read:[],vread:[],"history-system":[],"history-type":[],"history-instance":[]},n=new Set,i=new Set,o=!1,s=0;for(let c=0;c<e.length;c++){let u=e[c];if(!u.request?.method){this.state.resultEntries[c]=Vt(Z("Missing Bundle entry request method",`Bundle.entry[${c}].request.method`));continue}let f=await this.preprocessEntry(u,c,n);if(f){if(!this.isTransaction()){this.state.resultEntries[c]=Vt(f);continue}throw new q(f)}let m=this.getRouteForEntry(u),T=m?.data?.interaction;if(!T||!r[T])throw new q(Z(`Invalid REST interaction in batch: ${u.request?.method} ${u.request?.url}`));let y=m?.params?.resourceType;y&&i.add(y),T==="create"&&u.request?.ifNoneExist?o=!0:T==="update"?(u.request?.url.includes("?")&&(o=!0),s++):T==="delete"&&u.request?.url.includes("?")&&(o=!0),r[T].push(c)}let a=[];for(let c of Object.values(r))a.push(...c);return{ordering:a,requiresStrongTransaction:o,updates:s,resourceTypes:i}}async preprocessEntry(e,r,n){if(!e.request?.url)return Z("Missing Bundle entry request URL",`Bundle.entry[${r}].request.url`);let i;try{i=await this.resolveIdentity(e,`Bundle.entry[${r}]`)}catch(o){if(o instanceof q)return o.outcome;throw o}if(i&&(this.resolvedIdentities[i.placeholder]=i.reference,this.isTransaction())){if(n.has(i.reference))throw new q(Z("Duplicate resource identity found in Bundle"));n.add(i.reference)}}async resolveIdentity(e,r){let i=this.getRouteForEntry(e)?.data?.interaction;if(!i)throw new q(Oo);switch(i){case"create":return this.resolveCreateIdentity(e);case"delete":case"update":case"patch":return this.resolveModificationIdentity(e,r);default:return}}getRouteForEntry(e){return this.router.find(e.request?.method,e.request?.url??"")}async resolveCreateIdentity(e){let r=e.fullUrl??"";if(e.request?.ifNoneExist){let n=await this.repo.searchResources(wo(e.request.url+"?"+e.request.ifNoneExist));if(n.length===1)return{placeholder:r,reference:Ut(n[0])}}if(e.resource)return e.resource.id=this.repo.generateId(),{placeholder:r,reference:Ut(e.resource)}}async resolveModificationIdentity(e,r){let n=e.fullUrl??"";if(e.request?.url?.includes("?")){let i=e.request.method,o=wo(e.request.url);o.count=2,o.offset=0,o.sortRules=void 0;let[s,a]=await this.repo.searchResources(o);if(!s)switch(i){case"DELETE":return;case"PUT":if(e.resource){if(e.resource.id)throw new q(Z("Cannot provide ID for create by update"));return e.resource.id=this.repo.generateId(),{placeholder:n,reference:Ut(e.resource)}}return;default:throw new q(Z(`Conditional ${e.request.method} did not match any resources`,r+".request.url"))}if(a)throw new q(Z(`Conditional ${e.request.method} matched multiple resources`,r+".request.url"));let c=Ut(s);return e.request.url=c,e.resource&&(e.resource.id=s.id),{placeholder:n,reference:c}}if(e.request?.url.includes("/"))return{placeholder:n,reference:e.request.url}}async processNextEntry(){if(this.isTransaction())throw new Error("Re-entrant batch processing does not support transaction semantics");await this.processNextEntryImpl()}async processNextEntryImpl(){let e=this.bundleInfo;if(!e)throw new Error("processNextEntry called before preprocess()/fromState()");let r=this.bundle.entry;if(!r)throw new q(Z("Missing bundle entry"));if(this.state.position>=e.ordering.length)return;let n=this.state.position,i=e.ordering[n],o=r[i],s=this.rewriteIdsInObject(o);try{this.setResult(i,await this.processBatchEntry(s)),this.state.position=n+1}catch(a){if(this.isTransaction())throw a;if(this.state.errors.push(a.message),a instanceof q&&Do(a.outcome)===429){for(let c=n;c<e.ordering.length;c++)this.setResult(e.ordering[c],Vt(a.outcome));this.state.position=e.ordering.length;return}this.setResult(i,Vt(Ua(a))),this.state.position=n+1}}setResult(e,r){this.state.resultEntries[e]=r,this.state.pendingIndices.push(e)}async processBatchEntry(e){let r=this.getRouteForEntry(e);if(!r)throw new q(Oo);let n=this.parseBatchRequest(e,r),[i,o]=await r.handler(n,this.repo,this.router,{batch:!0});if(!Lo(i)&&this.isTransaction())throw new q(i);return Vt(i,o)}parseBatchRequest(e,r){let n=e.request,i=Object.create(null);n.ifNoneExist&&(i["if-none-exist"]=n.ifNoneExist),n.ifMatch&&(i["if-match"]=n.ifMatch),n.ifNoneMatch&&(i["if-none-match"]=n.ifNoneMatch),n.ifModifiedSince&&(i["if-modified-since"]=n.ifModifiedSince);let o;return n.method==="PATCH"?o=this.parsePatchBody(e):o=e.resource,{method:n.method,url:r?.query?n.url.slice(0,n.url.indexOf("?")):n.url,pathname:"",params:r?.params??Object.create(null),query:r?.query??Object.create(null),body:o,headers:i,config:this.req.config}}parsePatchBody(e){let r=e.resource,n;if(r?.resourceType==="Binary"){if(!r.data)throw new q(Z("Missing entry.resource.data"));n=JSON.parse(Buffer.from(r.data,"base64").toString("utf8"))}else if(r?.resourceType==="Parameters"){if(Qa(r))return this.rewriteIds(r);if(r.parameter){n=[];for(let i of r.parameter)if(i.name==="operation"){let o=this.parsePatchParameter(i);n.push(o)}}}else throw new q(Z("Patch entry must include a Binary or Parameters resource"));if(!Array.isArray(n))throw new q(Z("Decoded PATCH body must be an array"));return this.rewriteIdsInArray(n)}parsePatchParameter(e){let r=e.part?.find(i=>i.name==="op")?.valueCode;if(!r)throw new q(Z("PATCH Parameters missing op"));let n={op:r};switch(r){case"add":case"replace":case"test":for(let i of e.part)i.name==="path"?n.path=i.valueString:i.name==="value"&&(n.value=JSON.parse(i.valueString??""));break;case"copy":case"move":for(let i of e.part)i.name==="path"?n.path=i.valueString:i.name==="from"&&(n.from=i.valueString);break;case"remove":n.path=e.part?.find(i=>i.name==="path")?.valueString;break}return n}rewriteIds(e){return Array.isArray(e)?this.rewriteIdsInArray(e):typeof e=="string"?this.rewriteIdsInString(e):typeof e=="object"&&e!==null?this.rewriteIdsInObject(e):e}rewriteIdsInArray(e){return e.map(r=>this.rewriteIds(r))}rewriteIdsInObject(e){return Object.fromEntries(Object.entries(e).map(([r,n])=>[r,this.rewriteIds(n)]))}rewriteIdsInString(e){let r=Ba.exec(e)?.[0];if(!r)return e;let n=r.replaceAll("%3A",":"),i=this.resolvedIdentities[n];return i?e.replaceAll(r,i):e}isTransaction(){return this.bundle.type==="transaction"&&!!this.req.config?.transactions}};function Qa(t){return!!t.parameter?.some(e=>e.name==="operation"&&e.part?.some(r=>r.name==="type"))}function Vt(t,e){return{response:{outcome:t,status:Do(t).toString(),location:Lo(t)&&e?.id?Ut(e):void 0},resource:e}}function ja(t,e,r){let n=new Array(e);for(let i=0;i<e;i++)n[i]=r[i];return{resourceType:"Bundle",type:`${t}-response`,entry:n}}function _o(t){return{position:t.position,resultEntries:t.resultEntries.slice(),pendingIndices:t.pendingIndices.slice(),errors:t.errors.slice()}}import{EventTarget as kf,OperationOutcomeError as Pf,allOk as Se,badRequest as Pe,created as Mf,isResource as Vf,normalizeOperationOutcome as Aa,notFound as ka,parseSearchRequest as gt,singularize as Uf}from"@medplum/core";import{allOk as tf,badRequest as Mt,ContentType as rf,deepClone as _a,DEFAULT_SEARCH_COUNT as nf,forbidden as of,getResourceTypes as wa,LRUCache as sf,normalizeOperationOutcome as af,OperationOutcomeError as yr}from"@medplum/core";import cf from"dataloader";function g(t){return Er(t,[])}function Er(t,e){switch(typeof t){case"string":return JSON.stringify(t);case"function":return t.name?`[function ${t.name}]`:"[function]";case"object":return qa(t,e);default:return String(t)}}function qa(t,e){if(t===null)return"null";if(e.includes(t))return"[Circular]";let r=[...e,t];if(Ka(t)){let n=t.toJSON();if(n!==t)return typeof n=="string"?n:Er(n,r)}else if(Array.isArray(t))return Ha(t,r);return Ya(t,r)}function Ka(t){return typeof t.toJSON=="function"}function Ya(t,e){let r=Object.entries(t);return r.length===0?"{}":e.length>2?"["+Wa(t)+"]":"{ "+r.map(([i,o])=>i+": "+Er(o,e)).join(", ")+" }"}function Ha(t,e){if(t.length===0)return"[]";if(e.length>2)return"[Array]";let r=Math.min(10,t.length),n=t.length-r,i=[];for(let o=0;o<r;++o)i.push(Er(t[o],e));return n===1?i.push("... 1 more item"):n>1&&i.push(`... ${n} more items`),"["+i.join(", ")+"]"}function Wa(t){let e=Object.prototype.toString.call(t).replace(/^\[object /,"").replace(/]$/,"");if(e==="Object"&&typeof t.constructor=="function"){let r=t.constructor.name;if(typeof r=="string"&&r!=="")return r}return e}function Ja(t,e){return t?.__kind===e}var ee=Ja;function xe(t){return t instanceof Promise}function pe(t){return typeof t?.then=="function"}function Ir(t){return t instanceof Error?t:new vn(t)}var vn=class extends Error{constructor(e){super("Unexpected error value: "+g(e)),this.name="NonErrorThrown",this.thrownValue=e}};function Q(t){return typeof t=="object"&&t!==null}function O(t,e){if(!t)throw new Error(e??"Unexpected invariant triggered.")}var Xa=/\r\n|[\n\r]/g;function It(t,e){let r=0,n=1;for(let i of t.body.matchAll(Xa)){if(typeof i.index!="number"&&O(!1),i.index>=e)break;r=i.index+i[0].length,n+=1}return{line:n,column:e+1-r}}function Rn(t){return Nr(t.source,It(t.source,t.start))}function Nr(t,e){let r=t.locationOffset.column-1,n="".padStart(r)+t.body,i=e.line-1,o=t.locationOffset.line-1,s=e.line+o,a=e.line===1?r:0,c=e.column+a,u=`${t.name}:${s}:${c}
1
+ var xo=Object.defineProperty;var Ma=(t,e,r)=>e in t?xo(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var Va=(t,e)=>{for(var r in e)xo(t,r,{get:e[r],enumerable:!0})};var C=(t,e,r)=>Ma(t,typeof e!="symbol"?e+"":e,r);import{badRequest as Z,getReferenceString as Ut,getStatus as Do,isOk as Lo,normalizeOperationOutcome as Ua,notFound as Oo,OperationOutcomeError as q,parseSearchRequest as wo}from"@medplum/core";var $a=50,Ga=8,Ba=/urn(:|%3A)uuid(:|%3A)[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/;async function bn(t,e,r,n){return new gr(r,e,n,t).run()}var gr=class t{constructor(e,r,n,i){C(this,"router");C(this,"repo");C(this,"bundle");C(this,"req");C(this,"resolvedIdentities");C(this,"bundleInfo");C(this,"state");this.router=e,this.repo=r,this.bundle=n,this.req=i,this.resolvedIdentities=Object.create(null),this.state={position:0,resultEntries:new Array(n.entry?.length??0),pendingIndices:[],errors:[]}}static fromState(e,r,n,i,o){let s=new t(e,r,i.bundle,n);return s.resolvedIdentities=i.resolvedIdentities,s.bundleInfo={...i.bundleInfo},s.state.position=o,s}async run(){await this.preprocess();let e=this.bundleInfo;if(!this.isTransaction()){let i=await this.processEntriesAndBuild();return this.dispatchPostEvent(),i}let r=_o(this.state),n=await this.repo.withTransaction(i=>this.withRepo(i,()=>(this.state=_o(r),this.processEntriesAndBuild())),{serializable:e.requiresStrongTransaction,resourceTypes:e.resourceTypes});return this.dispatchPostEvent(),n}async preprocess(){let e=this.bundle.type;if(e!=="batch"&&e!=="transaction")throw new q(Z("Unrecognized bundle type: "+e));if(this.bundleInfo=await this.preprocessBundle(),this.isTransaction()){if(this.bundleInfo.updates>$a)throw new q(Z("Transaction contains more update operations than allowed"));if(this.bundleInfo.requiresStrongTransaction&&this.state.resultEntries.length>Ga)throw new q(Z("Transaction requires strict isolation but has too many entries"))}this.dispatchPreEvent();let r=Object.create(null);for(let n=0;n<this.state.resultEntries.length;n++)this.state.resultEntries[n]&&(r[n]=this.state.resultEntries[n]);return{bundle:this.bundle,bundleInfo:{ordering:this.bundleInfo.ordering,resourceTypes:this.bundleInfo.resourceTypes,requiresStrongTransaction:this.bundleInfo.requiresStrongTransaction,updates:this.bundleInfo.updates},resolvedIdentities:this.resolvedIdentities,preprocessResults:r}}hasMoreEntries(){return this.bundleInfo!==void 0&&this.state.position<this.bundleInfo.ordering.length}getPosition(){return this.state.position}takePendingResults(){if(this.state.pendingIndices.length===0)return;let e=Object.create(null);for(let r of this.state.pendingIndices)e[r]=this.state.resultEntries[r];return this.state.pendingIndices=[],e}async processEntriesAndBuild(){for(;this.hasMoreEntries();)await this.processNextEntryImpl();return this.buildResultBundle()}dispatchPreEvent(){let e={type:"batch",bundleType:this.bundle.type,count:this.bundle.entry?.length,size:JSON.stringify(this.bundle).length};this.router.dispatchEvent(e)}dispatchPostEvent(){let e={type:"batch",bundleType:this.bundle.type,errors:this.state.errors.length?this.state.errors:void 0};this.router.dispatchEvent(e)}buildResultBundle(){return{resourceType:"Bundle",type:`${this.bundle.type}-response`,entry:this.state.resultEntries}}async withRepo(e,r){let n=this.repo;this.repo=e;try{return await r()}finally{this.repo=n}}async preprocessBundle(){let e=this.bundle.entry;if(!e?.length)throw new q(Z("Missing bundle entries"));let r={transaction:[],batch:[],delete:[],create:[],update:[],patch:[],operation:[],"search-system":[],"search-type":[],read:[],vread:[],"history-system":[],"history-type":[],"history-instance":[]},n=new Set,i=new Set,o=!1,s=0;for(let c=0;c<e.length;c++){let u=e[c];if(!u.request?.method){this.state.resultEntries[c]=Vt(Z("Missing Bundle entry request method",`Bundle.entry[${c}].request.method`));continue}let f=await this.preprocessEntry(u,c,n);if(f){if(!this.isTransaction()){this.state.resultEntries[c]=Vt(f);continue}throw new q(f)}let m=this.getRouteForEntry(u),T=m?.data?.interaction;if(!T||!r[T])throw new q(Z(`Invalid REST interaction in batch: ${u.request?.method} ${u.request?.url}`));let y=m?.params?.resourceType;y&&i.add(y),T==="create"&&u.request?.ifNoneExist?o=!0:T==="update"?(u.request?.url.includes("?")&&(o=!0),s++):T==="delete"&&u.request?.url.includes("?")&&(o=!0),r[T].push(c)}let a=[];for(let c of Object.values(r))a.push(...c);return{ordering:a,requiresStrongTransaction:o,updates:s,resourceTypes:i}}async preprocessEntry(e,r,n){if(!e.request?.url)return Z("Missing Bundle entry request URL",`Bundle.entry[${r}].request.url`);let i;try{i=await this.resolveIdentity(e,`Bundle.entry[${r}]`)}catch(o){if(o instanceof q)return o.outcome;throw o}if(i&&(this.resolvedIdentities[i.placeholder]=i.reference,this.isTransaction())){if(n.has(i.reference))throw new q(Z("Duplicate resource identity found in Bundle"));n.add(i.reference)}}async resolveIdentity(e,r){let i=this.getRouteForEntry(e)?.data?.interaction;if(!i)throw new q(Oo);switch(i){case"create":return this.resolveCreateIdentity(e);case"delete":case"update":case"patch":return this.resolveModificationIdentity(e,r);default:return}}getRouteForEntry(e){return this.router.find(e.request?.method,e.request?.url??"")}async resolveCreateIdentity(e){let r=e.fullUrl??"";if(e.request?.ifNoneExist){let n=await this.repo.searchResources(wo(e.request.url+"?"+e.request.ifNoneExist));if(n.length===1)return{placeholder:r,reference:Ut(n[0])}}if(e.resource)return e.resource.id=this.repo.generateId(),{placeholder:r,reference:Ut(e.resource)}}async resolveModificationIdentity(e,r){let n=e.fullUrl??"";if(e.request?.url?.includes("?")){let i=e.request.method,o=wo(e.request.url);o.count=2,o.offset=0,o.sortRules=void 0;let[s,a]=await this.repo.searchResources(o);if(!s)switch(i){case"DELETE":return;case"PUT":if(e.resource){if(e.resource.id)throw new q(Z("Cannot provide ID for create by update"));return e.resource.id=this.repo.generateId(),{placeholder:n,reference:Ut(e.resource)}}return;default:throw new q(Z(`Conditional ${e.request.method} did not match any resources`,r+".request.url"))}if(a)throw new q(Z(`Conditional ${e.request.method} matched multiple resources`,r+".request.url"));let c=Ut(s);return e.request.url=c,e.resource&&(e.resource.id=s.id),{placeholder:n,reference:c}}if(e.request?.url.includes("/"))return{placeholder:n,reference:e.request.url}}async processNextEntry(){if(this.isTransaction())throw new Error("Re-entrant batch processing does not support transaction semantics");await this.processNextEntryImpl()}async processNextEntryImpl(){let e=this.bundleInfo;if(!e)throw new Error("processNextEntry called before preprocess()/fromState()");let r=this.bundle.entry;if(!r)throw new q(Z("Missing bundle entry"));if(this.state.position>=e.ordering.length)return;let n=this.state.position,i=e.ordering[n],o=r[i],s=this.rewriteIdsInObject(o);try{this.setResult(i,await this.processBatchEntry(s)),this.state.position=n+1}catch(a){if(this.isTransaction())throw a;if(this.state.errors.push(a.message),a instanceof q&&Do(a.outcome)===429){for(let c=n;c<e.ordering.length;c++)this.setResult(e.ordering[c],Vt(a.outcome));this.state.position=e.ordering.length;return}this.setResult(i,Vt(Ua(a))),this.state.position=n+1}}setResult(e,r){this.state.resultEntries[e]=r,this.state.pendingIndices.push(e)}async processBatchEntry(e){let r=this.getRouteForEntry(e);if(!r)throw new q(Oo);let n=this.parseBatchRequest(e,r),[i,o]=await r.handler(n,this.repo,this.router,{batch:!0});if(!Lo(i)&&this.isTransaction())throw new q(i);return Vt(i,o)}parseBatchRequest(e,r){let n=e.request,i=Object.create(null);n.ifNoneExist&&(i["if-none-exist"]=n.ifNoneExist),n.ifMatch&&(i["if-match"]=n.ifMatch),n.ifNoneMatch&&(i["if-none-match"]=n.ifNoneMatch),n.ifModifiedSince&&(i["if-modified-since"]=n.ifModifiedSince);let o;return n.method==="PATCH"?o=this.parsePatchBody(e):o=e.resource,{method:n.method,url:r?.query?n.url.slice(0,n.url.indexOf("?")):n.url,pathname:"",params:r?.params??Object.create(null),query:r?.query??Object.create(null),body:o,headers:i,config:this.req.config}}parsePatchBody(e){let r=e.resource,n;if(r?.resourceType==="Binary"){if(!r.data)throw new q(Z("Missing entry.resource.data"));n=JSON.parse(Buffer.from(r.data,"base64").toString("utf8"))}else if(r?.resourceType==="Parameters"){if(Qa(r))return this.rewriteIds(r);if(r.parameter){n=[];for(let i of r.parameter)if(i.name==="operation"){let o=this.parsePatchParameter(i);n.push(o)}}}else throw new q(Z("Patch entry must include a Binary or Parameters resource"));if(!Array.isArray(n))throw new q(Z("Decoded PATCH body must be an array"));return this.rewriteIdsInArray(n)}parsePatchParameter(e){let r=e.part?.find(i=>i.name==="op")?.valueCode;if(!r)throw new q(Z("PATCH Parameters missing op"));let n={op:r};switch(r){case"add":case"replace":case"test":for(let i of e.part)i.name==="path"?n.path=i.valueString:i.name==="value"&&(n.value=JSON.parse(i.valueString??""));break;case"copy":case"move":for(let i of e.part)i.name==="path"?n.path=i.valueString:i.name==="from"&&(n.from=i.valueString);break;case"remove":n.path=e.part?.find(i=>i.name==="path")?.valueString;break}return n}rewriteIds(e){return Array.isArray(e)?this.rewriteIdsInArray(e):typeof e=="string"?this.rewriteIdsInString(e):typeof e=="object"&&e!==null?this.rewriteIdsInObject(e):e}rewriteIdsInArray(e){return e.map(r=>this.rewriteIds(r))}rewriteIdsInObject(e){return Object.fromEntries(Object.entries(e).map(([r,n])=>[r,this.rewriteIds(n)]))}rewriteIdsInString(e){let r=Ba.exec(e)?.[0];if(!r)return e;let n=r.replaceAll("%3A",":"),i=this.resolvedIdentities[n];return i?e.replaceAll(r,i):e}isTransaction(){return this.bundle.type==="transaction"&&!!this.req.config?.transactions}};function Qa(t){return!!t.parameter?.some(e=>e.name==="operation"&&e.part?.some(r=>r.name==="type"))}function Vt(t,e){return{response:{outcome:t,status:Do(t).toString(),location:Lo(t)&&e?.id?Ut(e):void 0},resource:e}}function ja(t,e,r){let n=new Array(e);for(let i=0;i<e;i++)n[i]=r[i];return{resourceType:"Bundle",type:`${t}-response`,entry:n}}function _o(t){return{position:t.position,resultEntries:t.resultEntries.slice(),pendingIndices:t.pendingIndices.slice(),errors:t.errors.slice()}}import{EventTarget as kf,OperationOutcomeError as Pf,allOk as Se,badRequest as Pe,created as Mf,isResource as Vf,normalizeOperationOutcome as Aa,notFound as ka,parseSearchRequest as gt,singularize as Uf}from"@medplum/core";import{allOk as tf,badRequest as Mt,ContentType as rf,deepClone as _a,DEFAULT_SEARCH_COUNT as nf,forbidden as of,getResourceTypes as wa,LRUCache as sf,normalizeOperationOutcome as af,OperationOutcomeError as yr}from"@medplum/core";import cf from"dataloader";function g(t){return Er(t,[])}function Er(t,e){switch(typeof t){case"string":return JSON.stringify(t);case"function":return t.name?`[function ${t.name}]`:"[function]";case"object":return qa(t,e);default:return String(t)}}function qa(t,e){if(t===null)return"null";if(e.includes(t))return"[Circular]";let r=[...e,t];if(Ka(t)){let n=t.toJSON();if(n!==t)return typeof n=="string"?n:Er(n,r)}else if(Array.isArray(t))return Ha(t,r);return Ya(t,r)}function Ka(t){return typeof t.toJSON=="function"}function Ya(t,e){let r=Object.entries(t);return r.length===0?"{}":e.length>2?"["+Wa(t)+"]":"{ "+r.map(([i,o])=>i+": "+Er(o,e)).join(", ")+" }"}function Ha(t,e){if(t.length===0)return"[]";if(e.length>2)return"[Array]";let r=Math.min(10,t.length),n=t.length-r,i=[];for(let o=0;o<r;++o)i.push(Er(t[o],e));return n===1?i.push("... 1 more item"):n>1&&i.push(`... ${n} more items`),"["+i.join(", ")+"]"}function Wa(t){let e=Object.prototype.toString.call(t).replace(/^\[object /,"").replace(/]$/,"");if(e==="Object"&&typeof t.constructor=="function"){let r=t.constructor.name;if(typeof r=="string"&&r!=="")return r}return e}function Ja(t,e){return t?.__kind===e}var ee=Ja;function xe(t){return t instanceof Promise}function pe(t){return typeof t?.then=="function"}function Ir(t){return t instanceof Error?t:new vn(t)}var vn=class extends Error{constructor(e){super("Unexpected error value: "+g(e)),this.name="NonErrorThrown",this.thrownValue=e}};function Q(t){return typeof t=="object"&&t!==null}function O(t,e){if(!t)throw new Error(e??"Unexpected invariant triggered.")}var Xa=/\r\n|[\n\r]/g;function It(t,e){let r=0,n=1;for(let i of t.body.matchAll(Xa)){if(typeof i.index!="number"&&O(!1),i.index>=e)break;r=i.index+i[0].length,n+=1}return{line:n,column:e+1-r}}function Rn(t){return Nr(t.source,It(t.source,t.start))}function Nr(t,e){let r=t.locationOffset.column-1,n="".padStart(r)+t.body,i=e.line-1,o=t.locationOffset.line-1,s=e.line+o,a=e.line===1?r:0,c=e.column+a,u=`${t.name}:${s}:${c}
2
2
  `,l=n.split(/\r\n|[\n\r]/g),f=l[i];if(f.length>120){let m=Math.floor(c/80),T=c%80,y=[];for(let R=0;R<f.length;R+=80)y.push(f.slice(R,R+80));return u+Ao([[`${s} |`,y[0]],...y.slice(1,m+1).map(R=>["|",R]),["|","^".padStart(T)],["|",y[m+1]]])}return u+Ao([[`${s-1} |`,l[i-1]],[`${s} |`,f],["|","^".padStart(c)],[`${s+1} |`,l[i+1]]])}function Ao(t){let e=t.filter(([n,i])=>i!==void 0),r=Math.max(...e.map(([n])=>n.length));return e.map(([n,i])=>n.padStart(r)+(i?" "+i:"")).join(`
3
3
  `)}var d=class t extends Error{constructor(e,r={}){let{nodes:n,source:i,positions:o,path:s,originalError:a,cause:c,extensions:u}=r,l="cause"in r,f=l?c:a,m=l||a!=null?{cause:f}:void 0;super(e,m),this.name="GraphQLError",this.path=s??void 0;let T=a??(c instanceof Error?c:void 0);this.originalError=T,this.nodes=Fo(Array.isArray(n)?n:n?[n]:void 0);let y=Fo(this.nodes?.map(v=>v.loc).filter(v=>v!=null));this.source=i??y?.[0]?.source,this.positions=o??y?.map(v=>v.start),this.locations=o&&i?o.map(v=>It(i,v)):y?.map(v=>It(v.source,v.start));let R=Q(T?.extensions)?T.extensions:void 0;this.extensions=u??R??Object.create(null),Object.defineProperties(this,{message:{writable:!0,enumerable:!0},name:{enumerable:!1},nodes:{enumerable:!1},source:{enumerable:!1},positions:{enumerable:!1},originalError:{enumerable:!1}}),a?.stack!=null?Object.defineProperty(this,"stack",{value:a.stack,writable:!0,configurable:!0}):Error.captureStackTrace!=null?Error.captureStackTrace(this,t):Object.defineProperty(this,"stack",{value:Error().stack,writable:!0,configurable:!0})}get[Symbol.toStringTag](){return"GraphQLError"}toString(){let e=this.message;if(this.nodes)for(let r of this.nodes)r.loc&&(e+=`
4
4