@openhi/constructs 0.0.100 → 0.0.101

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.
@@ -5703,55 +5703,6 @@ function getDefaultPostgresQueryRunner() {
5703
5703
  return cached;
5704
5704
  }
5705
5705
 
5706
- // src/data/operations/data/appointment/appointment-search-by-date-operation.ts
5707
- var DEFAULT_LIMIT = 100;
5708
- function buildSearchAppointmentsByDateSql(opts) {
5709
- const datePredicates = buildAppointmentDateSearchPredicateSql(
5710
- opts.dateConstraints
5711
- );
5712
- const lines = [
5713
- "SELECT resource_id AS id, resource",
5714
- "FROM resources",
5715
- "WHERE tenant_id = :tenantId",
5716
- " AND workspace_id = :workspaceId",
5717
- " AND resource_type = 'Appointment'",
5718
- " AND deleted_at IS NULL"
5719
- ];
5720
- for (const fragment of datePredicates) {
5721
- lines.push(` AND ${fragment}`);
5722
- }
5723
- lines.push("ORDER BY last_updated DESC");
5724
- lines.push("LIMIT :limit;");
5725
- return lines.join("\n");
5726
- }
5727
- async function searchAppointmentsByDateOperation(params) {
5728
- const { context, dateConstraints } = params;
5729
- if (dateConstraints.length === 0) {
5730
- throw new Error(
5731
- "searchAppointmentsByDateOperation requires at least one dateConstraint"
5732
- );
5733
- }
5734
- const { tenantId, workspaceId } = context;
5735
- const runner = params.runner ?? getDefaultPostgresQueryRunner();
5736
- const limit = params.limit ?? DEFAULT_LIMIT;
5737
- const sql = buildSearchAppointmentsByDateSql({ dateConstraints });
5738
- const queryParams = [
5739
- { name: "tenantId", value: tenantId },
5740
- { name: "workspaceId", value: workspaceId },
5741
- { name: "limit", value: limit },
5742
- ...buildAppointmentDateSearchPredicateParams(dateConstraints)
5743
- ];
5744
- const rows = await runner.query(sql, queryParams);
5745
- const entries = rows.map((row) => ({
5746
- id: row.id,
5747
- resource: {
5748
- ...row.resource,
5749
- id: row.id
5750
- }
5751
- }));
5752
- return { entries, total: entries.length };
5753
- }
5754
-
5755
5706
  // src/data/operations/data/encounter/encounter-period-search-predicate.ts
5756
5707
  var PERIOD_SEARCH_PREFIXES = [
5757
5708
  "gt",
@@ -5804,7 +5755,7 @@ function buildPeriodSearchPredicateParams(constraints) {
5804
5755
  }
5805
5756
 
5806
5757
  // src/data/operations/data/encounter/encounter-search-by-patient-operation.ts
5807
- var DEFAULT_LIMIT2 = 100;
5758
+ var DEFAULT_LIMIT = 100;
5808
5759
  function buildOpenHiResourceUrn(opts) {
5809
5760
  return `urn:ohi:${opts.tenantId}:${opts.workspaceId}:${opts.resourceType}:${opts.resourceId}`;
5810
5761
  }
@@ -5834,7 +5785,7 @@ async function searchEncountersByPatientOperation(params) {
5834
5785
  const periodConstraints = params.periodConstraints ?? [];
5835
5786
  const { tenantId, workspaceId } = context;
5836
5787
  const runner = params.runner ?? getDefaultPostgresQueryRunner();
5837
- const limit = params.limit ?? DEFAULT_LIMIT2;
5788
+ const limit = params.limit ?? DEFAULT_LIMIT;
5838
5789
  const containmentRelative = JSON.stringify({
5839
5790
  subject: { reference: `Patient/${patientId}` }
5840
5791
  });
@@ -5868,8 +5819,155 @@ async function searchEncountersByPatientOperation(params) {
5868
5819
  return { entries, total: entries.length };
5869
5820
  }
5870
5821
 
5871
- // src/data/operations/data/appointment/appointment-search-by-patient-operation.ts
5822
+ // src/data/operations/data/reference-containment-predicate.ts
5823
+ var REFERENCE_CONTAINMENT_SQL_FRAGMENT = "(resource @> :containmentRelative::jsonb OR resource @> :containmentUrn::jsonb)";
5824
+ function parseTypedReference(s) {
5825
+ const match = /^([A-Za-z][A-Za-z0-9_]*)\/([^\s/]+)$/.exec(s);
5826
+ if (!match) {
5827
+ return void 0;
5828
+ }
5829
+ return { resourceType: match[1], resourceId: match[2] };
5830
+ }
5831
+ function wrapReferenceInShape(shape, reference) {
5832
+ switch (shape.kind) {
5833
+ case "scalar":
5834
+ return { [shape.field]: { reference } };
5835
+ case "array-of-references":
5836
+ return { [shape.field]: [{ reference }] };
5837
+ case "array-of-objects":
5838
+ return { [shape.field]: [{ [shape.subfield]: { reference } }] };
5839
+ }
5840
+ }
5841
+ function buildReferenceContainmentPayload(params) {
5842
+ const parsed = parseTypedReference(params.reference);
5843
+ if (!parsed) {
5844
+ throw new Error(
5845
+ `Reference "${params.reference}" is not a valid typed reference (<ResourceType>/<id>).`
5846
+ );
5847
+ }
5848
+ const urn = buildOpenHiResourceUrn({
5849
+ tenantId: params.tenantId,
5850
+ workspaceId: params.workspaceId,
5851
+ resourceType: parsed.resourceType,
5852
+ resourceId: parsed.resourceId
5853
+ });
5854
+ return {
5855
+ containmentRelative: JSON.stringify(
5856
+ wrapReferenceInShape(params.shape, params.reference)
5857
+ ),
5858
+ containmentUrn: JSON.stringify(wrapReferenceInShape(params.shape, urn))
5859
+ };
5860
+ }
5861
+
5862
+ // src/data/operations/data/appointment/appointment-search-by-actor-operation.ts
5863
+ var DEFAULT_LIMIT2 = 100;
5864
+ function buildSearchAppointmentsByActorSql(opts) {
5865
+ const datePredicates = buildAppointmentDateSearchPredicateSql(
5866
+ opts?.dateConstraints ?? []
5867
+ );
5868
+ const lines = [
5869
+ "SELECT resource_id AS id, resource",
5870
+ "FROM resources",
5871
+ "WHERE tenant_id = :tenantId",
5872
+ " AND workspace_id = :workspaceId",
5873
+ " AND resource_type = 'Appointment'",
5874
+ " AND deleted_at IS NULL",
5875
+ ` AND ${REFERENCE_CONTAINMENT_SQL_FRAGMENT}`
5876
+ ];
5877
+ for (const fragment of datePredicates) {
5878
+ lines.push(` AND ${fragment}`);
5879
+ }
5880
+ lines.push("ORDER BY last_updated DESC");
5881
+ lines.push("LIMIT :limit;");
5882
+ return lines.join("\n");
5883
+ }
5884
+ async function searchAppointmentsByActorOperation(params) {
5885
+ const { context, actorReference } = params;
5886
+ const dateConstraints = params.dateConstraints ?? [];
5887
+ const { tenantId, workspaceId } = context;
5888
+ const runner = params.runner ?? getDefaultPostgresQueryRunner();
5889
+ const limit = params.limit ?? DEFAULT_LIMIT2;
5890
+ const { containmentRelative, containmentUrn } = buildReferenceContainmentPayload({
5891
+ shape: {
5892
+ kind: "array-of-objects",
5893
+ field: "participant",
5894
+ subfield: "actor"
5895
+ },
5896
+ reference: actorReference,
5897
+ tenantId,
5898
+ workspaceId
5899
+ });
5900
+ const sql = buildSearchAppointmentsByActorSql({ dateConstraints });
5901
+ const queryParams = [
5902
+ { name: "tenantId", value: tenantId },
5903
+ { name: "workspaceId", value: workspaceId },
5904
+ { name: "containmentRelative", value: containmentRelative },
5905
+ { name: "containmentUrn", value: containmentUrn },
5906
+ { name: "limit", value: limit },
5907
+ ...buildAppointmentDateSearchPredicateParams(dateConstraints)
5908
+ ];
5909
+ const rows = await runner.query(sql, queryParams);
5910
+ const entries = rows.map((row) => ({
5911
+ id: row.id,
5912
+ resource: {
5913
+ ...row.resource,
5914
+ id: row.id
5915
+ }
5916
+ }));
5917
+ return { entries, total: entries.length };
5918
+ }
5919
+
5920
+ // src/data/operations/data/appointment/appointment-search-by-date-operation.ts
5872
5921
  var DEFAULT_LIMIT3 = 100;
5922
+ function buildSearchAppointmentsByDateSql(opts) {
5923
+ const datePredicates = buildAppointmentDateSearchPredicateSql(
5924
+ opts.dateConstraints
5925
+ );
5926
+ const lines = [
5927
+ "SELECT resource_id AS id, resource",
5928
+ "FROM resources",
5929
+ "WHERE tenant_id = :tenantId",
5930
+ " AND workspace_id = :workspaceId",
5931
+ " AND resource_type = 'Appointment'",
5932
+ " AND deleted_at IS NULL"
5933
+ ];
5934
+ for (const fragment of datePredicates) {
5935
+ lines.push(` AND ${fragment}`);
5936
+ }
5937
+ lines.push("ORDER BY last_updated DESC");
5938
+ lines.push("LIMIT :limit;");
5939
+ return lines.join("\n");
5940
+ }
5941
+ async function searchAppointmentsByDateOperation(params) {
5942
+ const { context, dateConstraints } = params;
5943
+ if (dateConstraints.length === 0) {
5944
+ throw new Error(
5945
+ "searchAppointmentsByDateOperation requires at least one dateConstraint"
5946
+ );
5947
+ }
5948
+ const { tenantId, workspaceId } = context;
5949
+ const runner = params.runner ?? getDefaultPostgresQueryRunner();
5950
+ const limit = params.limit ?? DEFAULT_LIMIT3;
5951
+ const sql = buildSearchAppointmentsByDateSql({ dateConstraints });
5952
+ const queryParams = [
5953
+ { name: "tenantId", value: tenantId },
5954
+ { name: "workspaceId", value: workspaceId },
5955
+ { name: "limit", value: limit },
5956
+ ...buildAppointmentDateSearchPredicateParams(dateConstraints)
5957
+ ];
5958
+ const rows = await runner.query(sql, queryParams);
5959
+ const entries = rows.map((row) => ({
5960
+ id: row.id,
5961
+ resource: {
5962
+ ...row.resource,
5963
+ id: row.id
5964
+ }
5965
+ }));
5966
+ return { entries, total: entries.length };
5967
+ }
5968
+
5969
+ // src/data/operations/data/appointment/appointment-search-by-patient-operation.ts
5970
+ var DEFAULT_LIMIT4 = 100;
5873
5971
  function buildSearchAppointmentsByPatientSql(opts) {
5874
5972
  const datePredicates = buildAppointmentDateSearchPredicateSql(
5875
5973
  opts?.dateConstraints ?? []
@@ -5896,7 +5994,7 @@ async function searchAppointmentsByPatientOperation(params) {
5896
5994
  const dateConstraints = params.dateConstraints ?? [];
5897
5995
  const { tenantId, workspaceId } = context;
5898
5996
  const runner = params.runner ?? getDefaultPostgresQueryRunner();
5899
- const limit = params.limit ?? DEFAULT_LIMIT3;
5997
+ const limit = params.limit ?? DEFAULT_LIMIT4;
5900
5998
  const containmentRelative = JSON.stringify({
5901
5999
  participant: [{ actor: { reference: `Patient/${patientId}` } }]
5902
6000
  });
@@ -5983,11 +6081,45 @@ function sendInvalidSearch400(res, diagnostics) {
5983
6081
  }
5984
6082
  async function listAppointmentsRoute(req, res) {
5985
6083
  const patientId = singleStringQueryParam(req, "patient");
6084
+ const actorRef = singleStringQueryParam(req, "actor");
5986
6085
  const parsed = parseAppointmentDateConstraints(req);
5987
6086
  if (isError(parsed)) {
5988
6087
  return sendInvalidSearch400(res, parsed.error);
5989
6088
  }
5990
6089
  const dateConstraints = parsed;
6090
+ if (patientId !== void 0 && actorRef !== void 0) {
6091
+ return sendInvalidSearch400(
6092
+ res,
6093
+ "?patient= and ?actor= cannot be combined on the same request."
6094
+ );
6095
+ }
6096
+ if (actorRef !== void 0) {
6097
+ if (parseTypedReference(actorRef) === void 0) {
6098
+ return sendInvalidSearch400(
6099
+ res,
6100
+ `?actor must be a typed reference like "Practitioner/<id>"; got "${actorRef}".`
6101
+ );
6102
+ }
6103
+ const ctx = req.openhiContext;
6104
+ try {
6105
+ const result = await searchAppointmentsByActorOperation({
6106
+ context: ctx,
6107
+ actorReference: actorRef,
6108
+ dateConstraints
6109
+ });
6110
+ const bundle = buildSearchsetBundle(
6111
+ BASE_PATH.APPOINTMENT,
6112
+ result.entries
6113
+ );
6114
+ return res.json(bundle);
6115
+ } catch (err) {
6116
+ return sendOperationOutcome500(
6117
+ res,
6118
+ err,
6119
+ "GET /Appointment?actor= search error:"
6120
+ );
6121
+ }
6122
+ }
5991
6123
  if (patientId !== void 0) {
5992
6124
  const ctx = req.openhiContext;
5993
6125
  try {
@@ -13579,7 +13711,7 @@ async function listEncountersOperation(params) {
13579
13711
  }
13580
13712
 
13581
13713
  // src/data/operations/data/encounter/encounter-search-by-date-operation.ts
13582
- var DEFAULT_LIMIT4 = 100;
13714
+ var DEFAULT_LIMIT5 = 100;
13583
13715
  function buildSearchEncountersByDateSql(opts) {
13584
13716
  const periodPredicates = buildPeriodSearchPredicateSql(
13585
13717
  opts.periodConstraints
@@ -13608,7 +13740,7 @@ async function searchEncountersByDateOperation(params) {
13608
13740
  }
13609
13741
  const { tenantId, workspaceId } = context;
13610
13742
  const runner = params.runner ?? getDefaultPostgresQueryRunner();
13611
- const limit = params.limit ?? DEFAULT_LIMIT4;
13743
+ const limit = params.limit ?? DEFAULT_LIMIT5;
13612
13744
  const sql = buildSearchEncountersByDateSql({ periodConstraints });
13613
13745
  const queryParams = [
13614
13746
  { name: "tenantId", value: tenantId },
@@ -13627,6 +13759,64 @@ async function searchEncountersByDateOperation(params) {
13627
13759
  return { entries, total: entries.length };
13628
13760
  }
13629
13761
 
13762
+ // src/data/operations/data/encounter/encounter-search-by-participant-operation.ts
13763
+ var DEFAULT_LIMIT6 = 100;
13764
+ function buildSearchEncountersByParticipantSql(opts) {
13765
+ const periodPredicates = buildPeriodSearchPredicateSql(
13766
+ opts?.periodConstraints ?? []
13767
+ );
13768
+ const lines = [
13769
+ "SELECT resource_id AS id, resource",
13770
+ "FROM resources",
13771
+ "WHERE tenant_id = :tenantId",
13772
+ " AND workspace_id = :workspaceId",
13773
+ " AND resource_type = 'Encounter'",
13774
+ " AND deleted_at IS NULL",
13775
+ ` AND ${REFERENCE_CONTAINMENT_SQL_FRAGMENT}`
13776
+ ];
13777
+ for (const fragment of periodPredicates) {
13778
+ lines.push(` AND ${fragment}`);
13779
+ }
13780
+ lines.push("ORDER BY last_updated DESC");
13781
+ lines.push("LIMIT :limit;");
13782
+ return lines.join("\n");
13783
+ }
13784
+ async function searchEncountersByParticipantOperation(params) {
13785
+ const { context, participantReference } = params;
13786
+ const periodConstraints = params.periodConstraints ?? [];
13787
+ const { tenantId, workspaceId } = context;
13788
+ const runner = params.runner ?? getDefaultPostgresQueryRunner();
13789
+ const limit = params.limit ?? DEFAULT_LIMIT6;
13790
+ const { containmentRelative, containmentUrn } = buildReferenceContainmentPayload({
13791
+ shape: {
13792
+ kind: "array-of-objects",
13793
+ field: "participant",
13794
+ subfield: "individual"
13795
+ },
13796
+ reference: participantReference,
13797
+ tenantId,
13798
+ workspaceId
13799
+ });
13800
+ const sql = buildSearchEncountersByParticipantSql({ periodConstraints });
13801
+ const queryParams = [
13802
+ { name: "tenantId", value: tenantId },
13803
+ { name: "workspaceId", value: workspaceId },
13804
+ { name: "containmentRelative", value: containmentRelative },
13805
+ { name: "containmentUrn", value: containmentUrn },
13806
+ { name: "limit", value: limit },
13807
+ ...buildPeriodSearchPredicateParams(periodConstraints)
13808
+ ];
13809
+ const rows = await runner.query(sql, queryParams);
13810
+ const entries = rows.map((row) => ({
13811
+ id: row.id,
13812
+ resource: {
13813
+ ...row.resource,
13814
+ id: row.id
13815
+ }
13816
+ }));
13817
+ return { entries, total: entries.length };
13818
+ }
13819
+
13630
13820
  // src/data/rest-api/routes/data/encounter/encounter-list-route.ts
13631
13821
  function singleStringQueryParam2(req, name) {
13632
13822
  const v = req.query[name];
@@ -13676,11 +13866,42 @@ function sendInvalidSearch4002(res, diagnostics) {
13676
13866
  }
13677
13867
  async function listEncountersRoute(req, res) {
13678
13868
  const patientId = singleStringQueryParam2(req, "patient");
13869
+ const participantRef = singleStringQueryParam2(req, "participant");
13679
13870
  const parsed = parseEncounterDateConstraints(req);
13680
13871
  if (isError2(parsed)) {
13681
13872
  return sendInvalidSearch4002(res, parsed.error);
13682
13873
  }
13683
13874
  const periodConstraints = parsed;
13875
+ if (patientId !== void 0 && participantRef !== void 0) {
13876
+ return sendInvalidSearch4002(
13877
+ res,
13878
+ "?patient= and ?participant= cannot be combined on the same request."
13879
+ );
13880
+ }
13881
+ if (participantRef !== void 0) {
13882
+ if (parseTypedReference(participantRef) === void 0) {
13883
+ return sendInvalidSearch4002(
13884
+ res,
13885
+ `?participant must be a typed reference like "Practitioner/<id>"; got "${participantRef}".`
13886
+ );
13887
+ }
13888
+ const ctx = req.openhiContext;
13889
+ try {
13890
+ const result = await searchEncountersByParticipantOperation({
13891
+ context: ctx,
13892
+ participantReference: participantRef,
13893
+ periodConstraints
13894
+ });
13895
+ const bundle = buildSearchsetBundle(BASE_PATH.ENCOUNTER, result.entries);
13896
+ return res.json(bundle);
13897
+ } catch (err) {
13898
+ return sendOperationOutcome500(
13899
+ res,
13900
+ err,
13901
+ "GET /Encounter?participant= search error:"
13902
+ );
13903
+ }
13904
+ }
13684
13905
  if (patientId !== void 0) {
13685
13906
  const ctx = req.openhiContext;
13686
13907
  try {
@@ -25481,8 +25702,91 @@ async function listPatientsOperation(params) {
25481
25702
  );
25482
25703
  }
25483
25704
 
25705
+ // src/data/operations/data/patient/patient-search-by-general-practitioner-operation.ts
25706
+ var DEFAULT_LIMIT7 = 100;
25707
+ function buildSearchPatientsByGeneralPractitionerSql() {
25708
+ return [
25709
+ "SELECT resource_id AS id, resource",
25710
+ "FROM resources",
25711
+ "WHERE tenant_id = :tenantId",
25712
+ " AND workspace_id = :workspaceId",
25713
+ " AND resource_type = 'Patient'",
25714
+ " AND deleted_at IS NULL",
25715
+ ` AND ${REFERENCE_CONTAINMENT_SQL_FRAGMENT}`,
25716
+ "ORDER BY last_updated DESC",
25717
+ "LIMIT :limit;"
25718
+ ].join("\n");
25719
+ }
25720
+ async function searchPatientsByGeneralPractitionerOperation(params) {
25721
+ const { context, generalPractitionerReference } = params;
25722
+ const { tenantId, workspaceId } = context;
25723
+ const runner = params.runner ?? getDefaultPostgresQueryRunner();
25724
+ const limit = params.limit ?? DEFAULT_LIMIT7;
25725
+ const { containmentRelative, containmentUrn } = buildReferenceContainmentPayload({
25726
+ shape: { kind: "array-of-references", field: "generalPractitioner" },
25727
+ reference: generalPractitionerReference,
25728
+ tenantId,
25729
+ workspaceId
25730
+ });
25731
+ const sql = buildSearchPatientsByGeneralPractitionerSql();
25732
+ const queryParams = [
25733
+ { name: "tenantId", value: tenantId },
25734
+ { name: "workspaceId", value: workspaceId },
25735
+ { name: "containmentRelative", value: containmentRelative },
25736
+ { name: "containmentUrn", value: containmentUrn },
25737
+ { name: "limit", value: limit }
25738
+ ];
25739
+ const rows = await runner.query(sql, queryParams);
25740
+ const entries = rows.map((row) => ({
25741
+ id: row.id,
25742
+ resource: { ...row.resource, id: row.id }
25743
+ }));
25744
+ return { entries, total: entries.length };
25745
+ }
25746
+
25484
25747
  // src/data/rest-api/routes/data/patient/patient-list-route.ts
25748
+ function singleStringQueryParam3(req, name) {
25749
+ const v = req.query[name];
25750
+ if (typeof v !== "string") {
25751
+ return void 0;
25752
+ }
25753
+ const trimmed = v.trim();
25754
+ return trimmed === "" ? void 0 : trimmed;
25755
+ }
25756
+ function sendInvalidSearch4003(res, diagnostics) {
25757
+ return res.status(400).json({
25758
+ resourceType: "OperationOutcome",
25759
+ issue: [{ severity: "error", code: "invalid", diagnostics }]
25760
+ });
25761
+ }
25485
25762
  async function listPatientsRoute(req, res) {
25763
+ const generalPractitionerRef = singleStringQueryParam3(
25764
+ req,
25765
+ "general-practitioner"
25766
+ );
25767
+ if (generalPractitionerRef !== void 0) {
25768
+ if (parseTypedReference(generalPractitionerRef) === void 0) {
25769
+ return sendInvalidSearch4003(
25770
+ res,
25771
+ `?general-practitioner must be a typed reference like "Practitioner/<id>"; got "${generalPractitionerRef}".`
25772
+ );
25773
+ }
25774
+ const ctx = req.openhiContext;
25775
+ try {
25776
+ const result = await searchPatientsByGeneralPractitionerOperation({
25777
+ context: ctx,
25778
+ generalPractitionerReference: generalPractitionerRef
25779
+ });
25780
+ const bundle = buildSearchsetBundle(BASE_PATH.PATIENT, result.entries);
25781
+ return res.json(bundle);
25782
+ } catch (err) {
25783
+ return sendOperationOutcome500(
25784
+ res,
25785
+ err,
25786
+ "GET /Patient?general-practitioner= search error:"
25787
+ );
25788
+ }
25789
+ }
25486
25790
  return handleListRoute({
25487
25791
  req,
25488
25792
  res,
@@ -29369,8 +29673,90 @@ async function listSchedulesOperation(params) {
29369
29673
  );
29370
29674
  }
29371
29675
 
29676
+ // src/data/operations/data/schedule/schedule-search-by-actor-operation.ts
29677
+ var DEFAULT_LIMIT8 = 100;
29678
+ function buildSearchSchedulesByActorSql() {
29679
+ return [
29680
+ "SELECT resource_id AS id, resource",
29681
+ "FROM resources",
29682
+ "WHERE tenant_id = :tenantId",
29683
+ " AND workspace_id = :workspaceId",
29684
+ " AND resource_type = 'Schedule'",
29685
+ " AND deleted_at IS NULL",
29686
+ ` AND ${REFERENCE_CONTAINMENT_SQL_FRAGMENT}`,
29687
+ "ORDER BY last_updated DESC",
29688
+ "LIMIT :limit;"
29689
+ ].join("\n");
29690
+ }
29691
+ async function searchSchedulesByActorOperation(params) {
29692
+ const { context, actorReference } = params;
29693
+ const { tenantId, workspaceId } = context;
29694
+ const runner = params.runner ?? getDefaultPostgresQueryRunner();
29695
+ const limit = params.limit ?? DEFAULT_LIMIT8;
29696
+ const { containmentRelative, containmentUrn } = buildReferenceContainmentPayload({
29697
+ shape: { kind: "array-of-references", field: "actor" },
29698
+ reference: actorReference,
29699
+ tenantId,
29700
+ workspaceId
29701
+ });
29702
+ const queryParams = [
29703
+ { name: "tenantId", value: tenantId },
29704
+ { name: "workspaceId", value: workspaceId },
29705
+ { name: "containmentRelative", value: containmentRelative },
29706
+ { name: "containmentUrn", value: containmentUrn },
29707
+ { name: "limit", value: limit }
29708
+ ];
29709
+ const rows = await runner.query(
29710
+ buildSearchSchedulesByActorSql(),
29711
+ queryParams
29712
+ );
29713
+ const entries = rows.map((row) => ({
29714
+ id: row.id,
29715
+ resource: { ...row.resource, id: row.id }
29716
+ }));
29717
+ return { entries, total: entries.length };
29718
+ }
29719
+
29372
29720
  // src/data/rest-api/routes/data/schedule/schedule-list-route.ts
29721
+ function singleStringQueryParam4(req, name) {
29722
+ const v = req.query[name];
29723
+ if (typeof v !== "string") {
29724
+ return void 0;
29725
+ }
29726
+ const trimmed = v.trim();
29727
+ return trimmed === "" ? void 0 : trimmed;
29728
+ }
29729
+ function sendInvalidSearch4004(res, diagnostics) {
29730
+ return res.status(400).json({
29731
+ resourceType: "OperationOutcome",
29732
+ issue: [{ severity: "error", code: "invalid", diagnostics }]
29733
+ });
29734
+ }
29373
29735
  async function listSchedulesRoute(req, res) {
29736
+ const actorRef = singleStringQueryParam4(req, "actor");
29737
+ if (actorRef !== void 0) {
29738
+ if (parseTypedReference(actorRef) === void 0) {
29739
+ return sendInvalidSearch4004(
29740
+ res,
29741
+ `?actor must be a typed reference like "Practitioner/<id>"; got "${actorRef}".`
29742
+ );
29743
+ }
29744
+ const ctx = req.openhiContext;
29745
+ try {
29746
+ const result = await searchSchedulesByActorOperation({
29747
+ context: ctx,
29748
+ actorReference: actorRef
29749
+ });
29750
+ const bundle = buildSearchsetBundle(BASE_PATH.SCHEDULE, result.entries);
29751
+ return res.json(bundle);
29752
+ } catch (err) {
29753
+ return sendOperationOutcome500(
29754
+ res,
29755
+ err,
29756
+ "GET /Schedule?actor= search error:"
29757
+ );
29758
+ }
29759
+ }
29374
29760
  return handleListRoute({
29375
29761
  req,
29376
29762
  res,
@@ -33051,8 +33437,164 @@ async function listTasksOperation(params) {
33051
33437
  );
33052
33438
  }
33053
33439
 
33440
+ // src/data/operations/data/task/task-search-by-owner-operation.ts
33441
+ var DEFAULT_LIMIT9 = 100;
33442
+ function buildSearchTasksByOwnerSql() {
33443
+ return [
33444
+ "SELECT resource_id AS id, resource",
33445
+ "FROM resources",
33446
+ "WHERE tenant_id = :tenantId",
33447
+ " AND workspace_id = :workspaceId",
33448
+ " AND resource_type = 'Task'",
33449
+ " AND deleted_at IS NULL",
33450
+ ` AND ${REFERENCE_CONTAINMENT_SQL_FRAGMENT}`,
33451
+ "ORDER BY last_updated DESC",
33452
+ "LIMIT :limit;"
33453
+ ].join("\n");
33454
+ }
33455
+ async function searchTasksByOwnerOperation(params) {
33456
+ const { context, ownerReference } = params;
33457
+ const { tenantId, workspaceId } = context;
33458
+ const runner = params.runner ?? getDefaultPostgresQueryRunner();
33459
+ const limit = params.limit ?? DEFAULT_LIMIT9;
33460
+ const { containmentRelative, containmentUrn } = buildReferenceContainmentPayload({
33461
+ shape: { kind: "scalar", field: "owner" },
33462
+ reference: ownerReference,
33463
+ tenantId,
33464
+ workspaceId
33465
+ });
33466
+ const queryParams = [
33467
+ { name: "tenantId", value: tenantId },
33468
+ { name: "workspaceId", value: workspaceId },
33469
+ { name: "containmentRelative", value: containmentRelative },
33470
+ { name: "containmentUrn", value: containmentUrn },
33471
+ { name: "limit", value: limit }
33472
+ ];
33473
+ const rows = await runner.query(
33474
+ buildSearchTasksByOwnerSql(),
33475
+ queryParams
33476
+ );
33477
+ const entries = rows.map((row) => ({
33478
+ id: row.id,
33479
+ resource: { ...row.resource, id: row.id }
33480
+ }));
33481
+ return { entries, total: entries.length };
33482
+ }
33483
+
33484
+ // src/data/operations/data/task/task-search-by-requester-operation.ts
33485
+ var DEFAULT_LIMIT10 = 100;
33486
+ function buildSearchTasksByRequesterSql() {
33487
+ return [
33488
+ "SELECT resource_id AS id, resource",
33489
+ "FROM resources",
33490
+ "WHERE tenant_id = :tenantId",
33491
+ " AND workspace_id = :workspaceId",
33492
+ " AND resource_type = 'Task'",
33493
+ " AND deleted_at IS NULL",
33494
+ ` AND ${REFERENCE_CONTAINMENT_SQL_FRAGMENT}`,
33495
+ "ORDER BY last_updated DESC",
33496
+ "LIMIT :limit;"
33497
+ ].join("\n");
33498
+ }
33499
+ async function searchTasksByRequesterOperation(params) {
33500
+ const { context, requesterReference } = params;
33501
+ const { tenantId, workspaceId } = context;
33502
+ const runner = params.runner ?? getDefaultPostgresQueryRunner();
33503
+ const limit = params.limit ?? DEFAULT_LIMIT10;
33504
+ const { containmentRelative, containmentUrn } = buildReferenceContainmentPayload({
33505
+ shape: { kind: "scalar", field: "requester" },
33506
+ reference: requesterReference,
33507
+ tenantId,
33508
+ workspaceId
33509
+ });
33510
+ const queryParams = [
33511
+ { name: "tenantId", value: tenantId },
33512
+ { name: "workspaceId", value: workspaceId },
33513
+ { name: "containmentRelative", value: containmentRelative },
33514
+ { name: "containmentUrn", value: containmentUrn },
33515
+ { name: "limit", value: limit }
33516
+ ];
33517
+ const rows = await runner.query(
33518
+ buildSearchTasksByRequesterSql(),
33519
+ queryParams
33520
+ );
33521
+ const entries = rows.map((row) => ({
33522
+ id: row.id,
33523
+ resource: { ...row.resource, id: row.id }
33524
+ }));
33525
+ return { entries, total: entries.length };
33526
+ }
33527
+
33054
33528
  // src/data/rest-api/routes/data/task/task-list-route.ts
33529
+ function singleStringQueryParam5(req, name) {
33530
+ const v = req.query[name];
33531
+ if (typeof v !== "string") {
33532
+ return void 0;
33533
+ }
33534
+ const trimmed = v.trim();
33535
+ return trimmed === "" ? void 0 : trimmed;
33536
+ }
33537
+ function sendInvalidSearch4005(res, diagnostics) {
33538
+ return res.status(400).json({
33539
+ resourceType: "OperationOutcome",
33540
+ issue: [{ severity: "error", code: "invalid", diagnostics }]
33541
+ });
33542
+ }
33055
33543
  async function listTasksRoute(req, res) {
33544
+ const ownerRef = singleStringQueryParam5(req, "owner");
33545
+ const requesterRef = singleStringQueryParam5(req, "requester");
33546
+ if (ownerRef !== void 0 && requesterRef !== void 0) {
33547
+ return sendInvalidSearch4005(
33548
+ res,
33549
+ "?owner= and ?requester= cannot be combined on the same request."
33550
+ );
33551
+ }
33552
+ if (ownerRef !== void 0) {
33553
+ if (parseTypedReference(ownerRef) === void 0) {
33554
+ return sendInvalidSearch4005(
33555
+ res,
33556
+ `?owner must be a typed reference like "Practitioner/<id>"; got "${ownerRef}".`
33557
+ );
33558
+ }
33559
+ const ctx = req.openhiContext;
33560
+ try {
33561
+ const result = await searchTasksByOwnerOperation({
33562
+ context: ctx,
33563
+ ownerReference: ownerRef
33564
+ });
33565
+ const bundle = buildSearchsetBundle(BASE_PATH.TASK, result.entries);
33566
+ return res.json(bundle);
33567
+ } catch (err) {
33568
+ return sendOperationOutcome500(
33569
+ res,
33570
+ err,
33571
+ "GET /Task?owner= search error:"
33572
+ );
33573
+ }
33574
+ }
33575
+ if (requesterRef !== void 0) {
33576
+ if (parseTypedReference(requesterRef) === void 0) {
33577
+ return sendInvalidSearch4005(
33578
+ res,
33579
+ `?requester must be a typed reference like "Practitioner/<id>"; got "${requesterRef}".`
33580
+ );
33581
+ }
33582
+ const ctx = req.openhiContext;
33583
+ try {
33584
+ const result = await searchTasksByRequesterOperation({
33585
+ context: ctx,
33586
+ requesterReference: requesterRef
33587
+ });
33588
+ const bundle = buildSearchsetBundle(BASE_PATH.TASK, result.entries);
33589
+ return res.json(bundle);
33590
+ } catch (err) {
33591
+ return sendOperationOutcome500(
33592
+ res,
33593
+ err,
33594
+ "GET /Task?requester= search error:"
33595
+ );
33596
+ }
33597
+ }
33056
33598
  return handleListRoute({
33057
33599
  req,
33058
33600
  res,