@openhi/constructs 0.0.177 → 0.0.179

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 (54) hide show
  1. package/lib/{chunk-Z4PZSLYY.mjs → chunk-3M4QTQH6.mjs} +2 -2
  2. package/lib/{chunk-JUSVETWK.mjs → chunk-4LQR32D2.mjs} +38 -40
  3. package/lib/{chunk-JUSVETWK.mjs.map → chunk-4LQR32D2.mjs.map} +1 -1
  4. package/lib/{chunk-XNUCKVSE.mjs → chunk-7GMTHOYF.mjs} +2 -2
  5. package/lib/{chunk-E2OWEBBH.mjs → chunk-DIVYB6GD.mjs} +18 -4
  6. package/lib/chunk-DIVYB6GD.mjs.map +1 -0
  7. package/lib/chunk-F2LY4TEI.mjs +272 -0
  8. package/lib/chunk-F2LY4TEI.mjs.map +1 -0
  9. package/lib/{chunk-GG2WD6TA.mjs → chunk-JJ3AQ6G5.mjs} +9 -3
  10. package/lib/{chunk-GG2WD6TA.mjs.map → chunk-JJ3AQ6G5.mjs.map} +1 -1
  11. package/lib/{chunk-EBB4RNUG.mjs → chunk-PIQISEGW.mjs} +2 -2
  12. package/lib/{chunk-FDBBTNCI.mjs → chunk-Q4KQD2NB.mjs} +117 -5
  13. package/lib/chunk-Q4KQD2NB.mjs.map +1 -0
  14. package/lib/{chunk-Y4RGUAM2.mjs → chunk-V6KLFEHC.mjs} +105 -34
  15. package/lib/chunk-V6KLFEHC.mjs.map +1 -0
  16. package/lib/chunk-VQY57NOV.mjs +60 -0
  17. package/lib/chunk-VQY57NOV.mjs.map +1 -0
  18. package/lib/counter-maintenance.handler.mjs +4 -4
  19. package/lib/counter-reconciliation.handler.js +2 -2
  20. package/lib/counter-reconciliation.handler.js.map +1 -1
  21. package/lib/counter-reconciliation.handler.mjs +9 -267
  22. package/lib/counter-reconciliation.handler.mjs.map +1 -1
  23. package/lib/index.d.mts +117 -2
  24. package/lib/index.d.ts +117 -2
  25. package/lib/index.js +6454 -6243
  26. package/lib/index.js.map +1 -1
  27. package/lib/index.mjs +106 -4
  28. package/lib/index.mjs.map +1 -1
  29. package/lib/pre-token-generation.handler.js +28 -19
  30. package/lib/pre-token-generation.handler.js.map +1 -1
  31. package/lib/pre-token-generation.handler.mjs +4 -5
  32. package/lib/pre-token-generation.handler.mjs.map +1 -1
  33. package/lib/provision-default-workspace.handler.js +22 -19
  34. package/lib/provision-default-workspace.handler.js.map +1 -1
  35. package/lib/provision-default-workspace.handler.mjs +3 -4
  36. package/lib/provision-default-workspace.handler.mjs.map +1 -1
  37. package/lib/rest-api-lambda.handler.js +400 -214
  38. package/lib/rest-api-lambda.handler.js.map +1 -1
  39. package/lib/rest-api-lambda.handler.mjs +243 -171
  40. package/lib/rest-api-lambda.handler.mjs.map +1 -1
  41. package/lib/seed-demo-data.handler.d.mts +19 -0
  42. package/lib/seed-demo-data.handler.d.ts +19 -0
  43. package/lib/seed-demo-data.handler.js +805 -159
  44. package/lib/seed-demo-data.handler.js.map +1 -1
  45. package/lib/seed-demo-data.handler.mjs +8 -4
  46. package/package.json +3 -3
  47. package/lib/chunk-6HGSR3TG.mjs +0 -123
  48. package/lib/chunk-6HGSR3TG.mjs.map +0 -1
  49. package/lib/chunk-E2OWEBBH.mjs.map +0 -1
  50. package/lib/chunk-FDBBTNCI.mjs.map +0 -1
  51. package/lib/chunk-Y4RGUAM2.mjs.map +0 -1
  52. /package/lib/{chunk-Z4PZSLYY.mjs.map → chunk-3M4QTQH6.mjs.map} +0 -0
  53. /package/lib/{chunk-XNUCKVSE.mjs.map → chunk-7GMTHOYF.mjs.map} +0 -0
  54. /package/lib/{chunk-EBB4RNUG.mjs.map → chunk-PIQISEGW.mjs.map} +0 -0
@@ -5,12 +5,10 @@ import {
5
5
  createRoleOperation
6
6
  } from "./chunk-SD7J3N3C.mjs";
7
7
  import {
8
- countMembershipsByUserOperation,
9
- listTenantsOperation,
10
- listWorkspacesOperation,
11
- membershipListByWorkspaceOperation,
12
- roleAssignmentListByWorkspaceOperation
13
- } from "./chunk-JUSVETWK.mjs";
8
+ deleteOrganizationOperation,
9
+ deleteTenantOperation,
10
+ deleteWorkspaceOperation
11
+ } from "./chunk-VQY57NOV.mjs";
14
12
  import {
15
13
  createAccountOperation,
16
14
  createAppointmentOperation,
@@ -26,26 +24,32 @@ import {
26
24
  createPractitionerOperation,
27
25
  createProcedureOperation,
28
26
  getRoleByIdOperation
29
- } from "./chunk-XNUCKVSE.mjs";
27
+ } from "./chunk-7GMTHOYF.mjs";
28
+ import {
29
+ countMembershipsByUserOperation,
30
+ listTenantsOperation,
31
+ listWorkspacesOperation,
32
+ membershipListByWorkspaceOperation,
33
+ roleAssignmentListByWorkspaceOperation
34
+ } from "./chunk-4LQR32D2.mjs";
30
35
  import {
31
36
  listPractitionerRolesOperation,
32
37
  listRoleAssignmentsOperation
33
- } from "./chunk-GG2WD6TA.mjs";
38
+ } from "./chunk-JJ3AQ6G5.mjs";
34
39
  import {
35
40
  listMembershipsOperation
36
- } from "./chunk-EBB4RNUG.mjs";
41
+ } from "./chunk-PIQISEGW.mjs";
37
42
  import {
38
43
  createMembershipOperation,
39
44
  createRoleAssignmentOperation,
40
45
  createTenantOperation,
41
46
  createWorkspaceOperation,
42
47
  extractDenormalizedReferenceDisplay
43
- } from "./chunk-Z4PZSLYY.mjs";
48
+ } from "./chunk-3M4QTQH6.mjs";
44
49
  import {
45
50
  extractRoleLevel,
46
51
  publishMembershipDeleted,
47
- publishRoleAssignmentDeleted,
48
- publishWorkspaceDeleted
52
+ publishRoleAssignmentDeleted
49
53
  } from "./chunk-BUAYVN3C.mjs";
50
54
  import {
51
55
  buildMembershipWorkspaceProjectionItem,
@@ -61,17 +65,14 @@ import {
61
65
  executeMultiWrite
62
66
  } from "./chunk-QJDHVMKT.mjs";
63
67
  import {
68
+ adminSetUserContextOperation,
64
69
  createUserOperation,
65
70
  deleteUserOperation,
66
71
  findUserBySubOperation,
67
72
  getUserByIdOperation,
68
73
  switchUserTenantWorkspaceOperation,
69
74
  updateUserOperation
70
- } from "./chunk-Y4RGUAM2.mjs";
71
- import {
72
- listUsersOperation,
73
- membershipListByUserOperation
74
- } from "./chunk-6HGSR3TG.mjs";
75
+ } from "./chunk-V6KLFEHC.mjs";
75
76
  import {
76
77
  getDynamoDataService
77
78
  } from "./chunk-6BB4CRSS.mjs";
@@ -83,9 +84,11 @@ import {
83
84
  dispatchListMode,
84
85
  getDataEntityById,
85
86
  listDataEntitiesByWorkspace,
87
+ listUsersOperation,
88
+ membershipListByUserOperation,
86
89
  mergeAuditIntoMeta,
87
90
  updateDataEntityById
88
- } from "./chunk-FDBBTNCI.mjs";
91
+ } from "./chunk-Q4KQD2NB.mjs";
89
92
  import {
90
93
  compressResource,
91
94
  decompressResource
@@ -2744,13 +2747,6 @@ async function createTenantRoute(req, res) {
2744
2747
  }
2745
2748
  }
2746
2749
 
2747
- // src/data/operations/control/tenant/tenant-delete-operation.ts
2748
- async function deleteTenantOperation(params) {
2749
- const { id, tableName } = params;
2750
- const service = getDynamoControlService(tableName);
2751
- await service.entities.tenant.delete({ tenantId: id, sk: "CURRENT" }).go();
2752
- }
2753
-
2754
2750
  // src/data/rest-api/routes/control/tenant/tenant-delete-route.ts
2755
2751
  async function deleteTenantRoute(req, res) {
2756
2752
  const id = String(req.params.id);
@@ -2826,6 +2822,76 @@ async function listTenantsRoute(req, res) {
2826
2822
  });
2827
2823
  }
2828
2824
 
2825
+ // src/data/operations/control/roleassignment/roleassignment-list-by-user-operation.ts
2826
+ function buildSkPrefix(mode) {
2827
+ switch (mode) {
2828
+ case "tenant":
2829
+ return "ROLEASSIGNMENT#TENANT#";
2830
+ case "workspace":
2831
+ case "workspaceInTenant":
2832
+ return "ROLEASSIGNMENT#WORKSPACE#";
2833
+ case "all":
2834
+ default:
2835
+ return "ROLEASSIGNMENT#";
2836
+ }
2837
+ }
2838
+ async function roleAssignmentListByUserOperation(params) {
2839
+ const {
2840
+ userId,
2841
+ mode = "all",
2842
+ tenantId,
2843
+ workspaceId,
2844
+ cursor = null,
2845
+ limit,
2846
+ order,
2847
+ consistent,
2848
+ tableName
2849
+ } = params;
2850
+ if (mode === "workspaceInTenant" && !tenantId) {
2851
+ throw new Error(
2852
+ 'roleAssignmentListByUserOperation: tenantId is required when mode === "workspaceInTenant"'
2853
+ );
2854
+ }
2855
+ const service = getDynamoControlService(tableName);
2856
+ const skPrefix = buildSkPrefix(mode);
2857
+ const goOptions = {
2858
+ cursor
2859
+ };
2860
+ if (limit !== void 0) {
2861
+ goOptions.limit = limit;
2862
+ }
2863
+ if (order !== void 0) {
2864
+ goOptions.order = order;
2865
+ }
2866
+ if (consistent) {
2867
+ goOptions.consistent = true;
2868
+ }
2869
+ const baseQuery = service.entities.roleAssignmentUserProjection.query.record({ userId }).begins({ sk: skPrefix });
2870
+ const filteredQuery = mode === "workspaceInTenant" ? baseQuery.where((attr, op) => {
2871
+ const tenantClause = op.eq(attr.tenantId, tenantId);
2872
+ if (workspaceId === void 0 || workspaceId.length === 0) {
2873
+ return tenantClause;
2874
+ }
2875
+ return `${tenantClause} AND ${op.eq(attr.workspaceId, workspaceId)}`;
2876
+ }) : baseQuery;
2877
+ const result = await filteredQuery.go(goOptions);
2878
+ const items = (result.data ?? []).map((row) => ({
2879
+ userId: row.userId,
2880
+ sk: row.sk,
2881
+ tenantId: row.tenantId,
2882
+ workspaceId: row.workspaceId,
2883
+ roleId: row.roleId,
2884
+ roleAssignmentId: row.roleAssignmentId,
2885
+ summary: row.summary,
2886
+ vid: row.vid,
2887
+ lastUpdated: row.lastUpdated,
2888
+ denormalizedTenantName: row.denormalizedTenantName,
2889
+ denormalizedUserName: row.denormalizedUserName,
2890
+ denormalizedRoleName: row.denormalizedRoleName
2891
+ }));
2892
+ return { items, cursor: result.cursor ?? null };
2893
+ }
2894
+
2829
2895
  // src/data/operations/control/tenant/tenant-users-operation.ts
2830
2896
  var USER_SK = "CURRENT";
2831
2897
  function extractEmail(user) {
@@ -2854,18 +2920,6 @@ function extractDisplayName(user) {
2854
2920
  const composed = `${given} ${family}`.trim();
2855
2921
  return composed.length > 0 ? composed : null;
2856
2922
  }
2857
- function extractReferenceDisplay(resource, fieldName) {
2858
- const field = resource[fieldName];
2859
- if (!field || typeof field !== "object") {
2860
- return null;
2861
- }
2862
- const display = field.display;
2863
- if (typeof display !== "string") {
2864
- return null;
2865
- }
2866
- const trimmed = display.trim();
2867
- return trimmed.length > 0 ? trimmed : null;
2868
- }
2869
2923
  function ensureAccumulator(byUser, userId) {
2870
2924
  let acc = byUser.get(userId);
2871
2925
  if (!acc) {
@@ -2877,9 +2931,8 @@ function ensureAccumulator(byUser, userId) {
2877
2931
  async function tenantUsersOperation(params) {
2878
2932
  const { context, tableName } = params;
2879
2933
  const service = getDynamoControlService(tableName);
2880
- const [memberships, roleAssignments, workspaces] = await Promise.all([
2934
+ const [memberships, workspaces] = await Promise.all([
2881
2935
  listMembershipsOperation({ context, tableName }),
2882
- listRoleAssignmentsOperation({ context, tableName }),
2883
2936
  listWorkspacesOperation({ context, tableName })
2884
2937
  ]);
2885
2938
  const workspaceNames = /* @__PURE__ */ new Map();
@@ -2901,34 +2954,46 @@ async function tenantUsersOperation(params) {
2901
2954
  acc.workspaces.set(workspaceId, { workspaceId, role: null });
2902
2955
  }
2903
2956
  }
2904
- for (const entry of roleAssignments.entries) {
2905
- const resource = entry.resource;
2906
- const userId = extractReferenceSlug(resource, "user");
2907
- const roleId = extractReferenceSlug(resource, "role");
2908
- if (userId === void 0 || roleId === void 0) {
2909
- continue;
2910
- }
2957
+ const membershipUserIds = Array.from(byUser.keys());
2958
+ const projectionPages = await Promise.all(
2959
+ membershipUserIds.map(
2960
+ (userId) => roleAssignmentListByUserOperation({
2961
+ userId,
2962
+ mode: "all",
2963
+ consistent: true,
2964
+ tableName
2965
+ })
2966
+ )
2967
+ );
2968
+ projectionPages.forEach((page, index) => {
2969
+ const userId = membershipUserIds[index];
2911
2970
  const acc = ensureAccumulator(byUser, userId);
2912
- const role = {
2913
- roleId,
2914
- roleName: extractReferenceDisplay(resource, "role")
2915
- };
2916
- const workspaceId = extractReferenceSlug(resource, "workspace");
2917
- if (workspaceId === void 0) {
2918
- acc.tenantRole = role;
2919
- } else {
2920
- const existing = acc.workspaces.get(workspaceId);
2921
- if (existing) {
2922
- existing.role = role;
2971
+ for (const row of page.items) {
2972
+ if (row.tenantId !== context.tenantId) {
2973
+ continue;
2974
+ }
2975
+ const role = {
2976
+ roleId: row.roleId,
2977
+ roleName: row.denormalizedRoleName ?? null
2978
+ };
2979
+ const workspaceId = row.workspaceId;
2980
+ if (workspaceId === void 0 || workspaceId.length === 0) {
2981
+ acc.tenantRole = role;
2923
2982
  } else {
2924
- acc.workspaces.set(workspaceId, { workspaceId, role });
2983
+ const existing = acc.workspaces.get(workspaceId);
2984
+ if (existing) {
2985
+ existing.role = role;
2986
+ } else {
2987
+ acc.workspaces.set(workspaceId, { workspaceId, role });
2988
+ }
2925
2989
  }
2926
2990
  }
2927
- }
2991
+ });
2928
2992
  const userIds = Array.from(byUser.keys());
2929
2993
  const userRows = userIds.length === 0 ? [] : await batchGetWithRetry(
2930
2994
  service.entities.user,
2931
- userIds.map((id) => ({ id, sk: USER_SK }))
2995
+ userIds.map((id) => ({ id, sk: USER_SK })),
2996
+ { consistent: true }
2932
2997
  );
2933
2998
  const usersById = /* @__PURE__ */ new Map();
2934
2999
  for (const row of userRows) {
@@ -3075,6 +3140,84 @@ router6.delete("/:id", deleteTenantRoute);
3075
3140
  // src/data/rest-api/routes/control/user/user.ts
3076
3141
  import express7 from "express";
3077
3142
 
3143
+ // src/data/rest-api/routes/control/user/user-admin-set-context-route.ts
3144
+ async function userAdminSetContextRoute(req, res) {
3145
+ const ctx = req.openhiContext;
3146
+ if (!ctx) {
3147
+ return res.status(403).json({
3148
+ resourceType: "OperationOutcome",
3149
+ issue: [
3150
+ {
3151
+ severity: "error",
3152
+ code: "forbidden",
3153
+ diagnostics: "Missing or invalid OpenHI JWT claims (tenant, workspace, or audit context)."
3154
+ }
3155
+ ]
3156
+ });
3157
+ }
3158
+ const targetUserId = String(req.params.id);
3159
+ const bodyResult = requireJsonBody(req, res);
3160
+ if ("errorResponse" in bodyResult) {
3161
+ return bodyResult.errorResponse;
3162
+ }
3163
+ const body = bodyResult.body;
3164
+ const tenantReference = body.tenant?.reference;
3165
+ const workspaceReference = body.workspace?.reference;
3166
+ if (typeof tenantReference !== "string" || tenantReference === "") {
3167
+ return sendInvalid(
3168
+ res,
3169
+ "Body must include `tenant.reference` (e.g. 'Tenant/<id>')."
3170
+ );
3171
+ }
3172
+ if (typeof workspaceReference !== "string" || workspaceReference === "") {
3173
+ return sendInvalid(
3174
+ res,
3175
+ "Body must include `workspace.reference` (e.g. 'Workspace/<id>')."
3176
+ );
3177
+ }
3178
+ try {
3179
+ const result = await adminSetUserContextOperation({
3180
+ context: ctx,
3181
+ targetUserId,
3182
+ tenantReference,
3183
+ workspaceReference
3184
+ });
3185
+ res.setHeader("Cache-Control", "no-store");
3186
+ return res.status(200).json(result.resource);
3187
+ } catch (err) {
3188
+ if (err instanceof ValidationError) {
3189
+ return sendInvalid(res, err.message);
3190
+ }
3191
+ if (err instanceof ForbiddenError) {
3192
+ return res.status(403).json({
3193
+ resourceType: "OperationOutcome",
3194
+ issue: [
3195
+ { severity: "error", code: "forbidden", diagnostics: err.message }
3196
+ ]
3197
+ });
3198
+ }
3199
+ if (err instanceof NotFoundError) {
3200
+ return res.status(404).json({
3201
+ resourceType: "OperationOutcome",
3202
+ issue: [
3203
+ { severity: "error", code: "not-found", diagnostics: err.message }
3204
+ ]
3205
+ });
3206
+ }
3207
+ return sendOperationOutcome500(
3208
+ res,
3209
+ err,
3210
+ "POST /User/:id/$set-context error:"
3211
+ );
3212
+ }
3213
+ }
3214
+ function sendInvalid(res, diagnostics) {
3215
+ return res.status(400).json({
3216
+ resourceType: "OperationOutcome",
3217
+ issue: [{ severity: "error", code: "invalid", diagnostics }]
3218
+ });
3219
+ }
3220
+
3078
3221
  // src/data/rest-api/routes/control/user/user-create-route.ts
3079
3222
  async function createUserRoute(req, res) {
3080
3223
  const bodyResult = requireJsonBody(req, res);
@@ -3583,72 +3726,6 @@ async function listUserMembershipsRoute(req, res) {
3583
3726
  }
3584
3727
  }
3585
3728
 
3586
- // src/data/operations/control/roleassignment/roleassignment-list-by-user-operation.ts
3587
- function buildSkPrefix(mode) {
3588
- switch (mode) {
3589
- case "tenant":
3590
- return "ROLEASSIGNMENT#TENANT#";
3591
- case "workspace":
3592
- case "workspaceInTenant":
3593
- return "ROLEASSIGNMENT#WORKSPACE#";
3594
- case "all":
3595
- default:
3596
- return "ROLEASSIGNMENT#";
3597
- }
3598
- }
3599
- async function roleAssignmentListByUserOperation(params) {
3600
- const {
3601
- userId,
3602
- mode = "all",
3603
- tenantId,
3604
- workspaceId,
3605
- cursor = null,
3606
- limit,
3607
- order,
3608
- tableName
3609
- } = params;
3610
- if (mode === "workspaceInTenant" && !tenantId) {
3611
- throw new Error(
3612
- 'roleAssignmentListByUserOperation: tenantId is required when mode === "workspaceInTenant"'
3613
- );
3614
- }
3615
- const service = getDynamoControlService(tableName);
3616
- const skPrefix = buildSkPrefix(mode);
3617
- const goOptions = {
3618
- cursor
3619
- };
3620
- if (limit !== void 0) {
3621
- goOptions.limit = limit;
3622
- }
3623
- if (order !== void 0) {
3624
- goOptions.order = order;
3625
- }
3626
- const baseQuery = service.entities.roleAssignmentUserProjection.query.record({ userId }).begins({ sk: skPrefix });
3627
- const filteredQuery = mode === "workspaceInTenant" ? baseQuery.where((attr, op) => {
3628
- const tenantClause = op.eq(attr.tenantId, tenantId);
3629
- if (workspaceId === void 0 || workspaceId.length === 0) {
3630
- return tenantClause;
3631
- }
3632
- return `${tenantClause} AND ${op.eq(attr.workspaceId, workspaceId)}`;
3633
- }) : baseQuery;
3634
- const result = await filteredQuery.go(goOptions);
3635
- const items = (result.data ?? []).map((row) => ({
3636
- userId: row.userId,
3637
- sk: row.sk,
3638
- tenantId: row.tenantId,
3639
- workspaceId: row.workspaceId,
3640
- roleId: row.roleId,
3641
- roleAssignmentId: row.roleAssignmentId,
3642
- summary: row.summary,
3643
- vid: row.vid,
3644
- lastUpdated: row.lastUpdated,
3645
- denormalizedTenantName: row.denormalizedTenantName,
3646
- denormalizedUserName: row.denormalizedUserName,
3647
- denormalizedRoleName: row.denormalizedRoleName
3648
- }));
3649
- return { items, cursor: result.cursor ?? null };
3650
- }
3651
-
3652
3729
  // src/data/rest-api/routes/control/user/user-list-role-assignments-route.ts
3653
3730
  var ALLOWED_MODES2 = [
3654
3731
  "all",
@@ -3797,6 +3874,7 @@ router7.get("/:id", getUserByIdRoute);
3797
3874
  router7.post("/", createUserRoute);
3798
3875
  router7.put("/:id", updateUserRoute);
3799
3876
  router7.delete("/:id", deleteUserRoute);
3877
+ router7.post("/:id/$set-context", userAdminSetContextRoute);
3800
3878
  router7.get("/:id/Membership", listUserMembershipsRoute);
3801
3879
  router7.get("/:id/RoleAssignment", listUserRoleAssignmentsRoute);
3802
3880
  router7.get("/:id/Configuration", listUserConfigurationsRoute);
@@ -4159,13 +4237,13 @@ async function userSwitchRoute(req, res) {
4159
4237
  const tenantReference = body.tenant?.reference;
4160
4238
  const workspaceReference = body.workspace?.reference;
4161
4239
  if (typeof tenantReference !== "string" || tenantReference === "") {
4162
- return sendInvalid(
4240
+ return sendInvalid2(
4163
4241
  res,
4164
4242
  "Body must include `tenant.reference` (e.g. 'Tenant/<id>')."
4165
4243
  );
4166
4244
  }
4167
4245
  if (typeof workspaceReference !== "string" || workspaceReference === "") {
4168
- return sendInvalid(
4246
+ return sendInvalid2(
4169
4247
  res,
4170
4248
  "Body must include `workspace.reference` (e.g. 'Workspace/<id>')."
4171
4249
  );
@@ -4180,7 +4258,7 @@ async function userSwitchRoute(req, res) {
4180
4258
  return res.status(200).json(result.resource);
4181
4259
  } catch (err) {
4182
4260
  if (err instanceof ValidationError) {
4183
- return sendInvalid(res, err.message);
4261
+ return sendInvalid2(res, err.message);
4184
4262
  }
4185
4263
  if (err instanceof ForbiddenError) {
4186
4264
  return res.status(403).json({
@@ -4201,7 +4279,7 @@ async function userSwitchRoute(req, res) {
4201
4279
  return sendOperationOutcome500(res, err, "POST /User/$switch error:");
4202
4280
  }
4203
4281
  }
4204
- function sendInvalid(res, diagnostics) {
4282
+ function sendInvalid2(res, diagnostics) {
4205
4283
  return res.status(400).json({
4206
4284
  resourceType: "OperationOutcome",
4207
4285
  issue: [{ severity: "error", code: "invalid", diagnostics }]
@@ -4243,40 +4321,6 @@ async function createWorkspaceRoute(req, res) {
4243
4321
  }
4244
4322
  }
4245
4323
 
4246
- // src/data/operations/data/organization/organization-delete-operation.ts
4247
- async function deleteOrganizationOperation(params) {
4248
- const { context, id, tableName } = params;
4249
- const { tenantId, workspaceId } = context;
4250
- const service = getDynamoDataService(tableName);
4251
- await deleteDataEntityById(
4252
- service.entities.organization,
4253
- tenantId,
4254
- workspaceId,
4255
- id
4256
- );
4257
- }
4258
-
4259
- // src/data/operations/control/workspace/workspace-delete-operation.ts
4260
- async function deleteWorkspaceOperation(params) {
4261
- const { context, id, tableName } = params;
4262
- const { tenantId } = context;
4263
- const service = getDynamoControlService(tableName);
4264
- const existing = await service.entities.workspace.get({ tenantId, id, sk: "CURRENT" }).go();
4265
- const workspaceExisted = existing.data !== null;
4266
- await service.entities.workspace.delete({ tenantId, id, sk: "CURRENT" }).go();
4267
- await deleteOrganizationOperation({
4268
- context: { ...context, workspaceId: id },
4269
- id,
4270
- tableName
4271
- });
4272
- if (workspaceExisted) {
4273
- await publishWorkspaceDeleted(context, {
4274
- workspaceId: id,
4275
- tenantId
4276
- });
4277
- }
4278
- }
4279
-
4280
4324
  // src/data/rest-api/routes/control/workspace/workspace-delete-route.ts
4281
4325
  async function deleteWorkspaceRoute(req, res) {
4282
4326
  const id = String(req.params.id);
@@ -5646,6 +5690,16 @@ function jsonbPathToStringShape(jsonbPath) {
5646
5690
  if (arrayOfScalars) {
5647
5691
  return { kind: "array-of-scalars", field: arrayOfScalars[1] };
5648
5692
  }
5693
+ const arrayOfArrays = /^\$\.([A-Za-z_][A-Za-z0-9_]*)\[\*\]\.([A-Za-z_][A-Za-z0-9_]*)\[\*\]$/.exec(
5694
+ jsonbPath
5695
+ );
5696
+ if (arrayOfArrays) {
5697
+ return {
5698
+ kind: "array-of-arrays",
5699
+ field: arrayOfArrays[1],
5700
+ subfield: arrayOfArrays[2]
5701
+ };
5702
+ }
5649
5703
  const arrayOfObjs = /^\$\.([A-Za-z_][A-Za-z0-9_]*)\[\*\]\.([A-Za-z_][A-Za-z0-9_]*)$/.exec(
5650
5704
  jsonbPath
5651
5705
  );
@@ -5657,7 +5711,7 @@ function jsonbPathToStringShape(jsonbPath) {
5657
5711
  };
5658
5712
  }
5659
5713
  throw new Error(
5660
- `String predicate cannot translate JSONPath "${jsonbPath}". Supported shapes: "$.field", "$.field[*]", "$.field[*].subfield".`
5714
+ `String predicate cannot translate JSONPath "${jsonbPath}". Supported shapes: "$.field", "$.field[*]", "$.field[*].subfield", "$.field[*].subfield[*]".`
5661
5715
  );
5662
5716
  }
5663
5717
  function escapeLikePattern(value) {
@@ -5690,6 +5744,13 @@ function buildIlikeExtractSql(shape, paramName) {
5690
5744
  `jsonb_array_elements(resource->'${shape.field}') AS s_obj(obj)`,
5691
5745
  `WHERE s_obj.obj->>'${shape.subfield}' ILIKE :${paramName})`
5692
5746
  ].join(" ");
5747
+ case "array-of-arrays":
5748
+ return [
5749
+ "EXISTS (SELECT 1 FROM",
5750
+ `jsonb_array_elements(resource->'${shape.field}') AS s_obj(obj),`,
5751
+ `jsonb_array_elements_text(s_obj.obj->'${shape.subfield}') AS s_elem(text_val)`,
5752
+ `WHERE s_elem.text_val ILIKE :${paramName})`
5753
+ ].join(" ");
5693
5754
  }
5694
5755
  }
5695
5756
  function emitStringPredicate(opts) {
@@ -5962,12 +6023,23 @@ async function genericSearchOperation(params) {
5962
6023
  ...combined.params
5963
6024
  ];
5964
6025
  const rows = await runner.query(sql, queryParams);
5965
- const entries = rows.map((row) => ({
5966
- id: row.id,
5967
- resource: { ...row.resource, id: row.id }
5968
- }));
6026
+ const entries = rows.map((row) => {
6027
+ const parsed = parseResourceColumn(row.resource);
6028
+ const bodyId = typeof parsed.id === "string" ? parsed.id : void 0;
6029
+ const id = bodyId ?? row.id;
6030
+ return {
6031
+ id,
6032
+ resource: { ...parsed, id }
6033
+ };
6034
+ });
5969
6035
  return { entries, total: entries.length };
5970
6036
  }
6037
+ function parseResourceColumn(raw) {
6038
+ if (typeof raw === "string") {
6039
+ return JSON.parse(raw);
6040
+ }
6041
+ return raw;
6042
+ }
5971
6043
 
5972
6044
  // src/data/search/registry/allergyintolerance-search-parameters.ts
5973
6045
  var ALLERGYINTOLERANCE_SEARCH_PARAMETERS = [
@@ -6568,7 +6640,7 @@ var PATIENT_SEARCH_PARAMETERS = [
6568
6640
  {
6569
6641
  code: "given",
6570
6642
  type: "string",
6571
- jsonbPath: "$.name[*].given",
6643
+ jsonbPath: "$.name[*].given[*]",
6572
6644
  modifiers: ["exact", "contains", "missing", "not"]
6573
6645
  },
6574
6646
  { code: "active", type: "token", jsonbPath: "$.active" },
@@ -6618,7 +6690,7 @@ var PRACTITIONER_SEARCH_PARAMETERS = [
6618
6690
  {
6619
6691
  code: "given",
6620
6692
  type: "string",
6621
- jsonbPath: "$.name[*].given",
6693
+ jsonbPath: "$.name[*].given[*]",
6622
6694
  modifiers: ["exact", "contains", "missing", "not"]
6623
6695
  },
6624
6696
  { code: "active", type: "token", jsonbPath: "$.active" },