@openhi/constructs 0.0.88 → 0.0.89

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.
@@ -945,7 +945,13 @@ async function createConfigurationOperation(params) {
945
945
  const roleId = body.roleId ?? context.roleId ?? "-";
946
946
  const lastUpdated = body.lastUpdated ?? date;
947
947
  const vid = body.vid ?? (date.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36));
948
- const summary = JSON.stringify({ resourceType: "Configuration", id, key });
948
+ const summary = JSON.stringify({
949
+ resourceType: "Configuration",
950
+ id,
951
+ key,
952
+ userId,
953
+ roleId
954
+ });
949
955
  const service = getDynamoControlService(tableName);
950
956
  await service.entities.configuration.put({
951
957
  tenantId,
@@ -1357,7 +1363,208 @@ async function getConfigurationByIdRoute(req, res) {
1357
1363
  }
1358
1364
  }
1359
1365
 
1366
+ // src/data/operations/data-operations-common.ts
1367
+ import { extractSortKey, extractSummary } from "@openhi/types";
1368
+
1369
+ // src/data/audit-meta.ts
1370
+ var OPENHI_EXT = "http://openhi.org/fhir/StructureDefinition";
1371
+ function mergeAuditIntoMeta(meta, audit) {
1372
+ const existing = meta ?? {};
1373
+ const ext = [
1374
+ ...Array.isArray(existing.extension) ? existing.extension : []
1375
+ ];
1376
+ const byUrl = new Map(ext.map((e) => [e.url, e]));
1377
+ function set(url, value, type) {
1378
+ if (value == null) return;
1379
+ byUrl.set(url, { url, [type]: value });
1380
+ }
1381
+ set(`${OPENHI_EXT}/created-date`, audit.createdDate, "valueDateTime");
1382
+ set(`${OPENHI_EXT}/created-by-id`, audit.createdById, "valueString");
1383
+ set(`${OPENHI_EXT}/created-by-name`, audit.createdByName, "valueString");
1384
+ set(`${OPENHI_EXT}/modified-date`, audit.modifiedDate, "valueDateTime");
1385
+ set(`${OPENHI_EXT}/modified-by-id`, audit.modifiedById, "valueString");
1386
+ set(`${OPENHI_EXT}/modified-by-name`, audit.modifiedByName, "valueString");
1387
+ set(`${OPENHI_EXT}/deleted-date`, audit.deletedDate, "valueDateTime");
1388
+ set(`${OPENHI_EXT}/deleted-by-id`, audit.deletedById, "valueString");
1389
+ set(`${OPENHI_EXT}/deleted-by-name`, audit.deletedByName, "valueString");
1390
+ return { ...existing, extension: Array.from(byUrl.values()) };
1391
+ }
1392
+
1393
+ // src/data/operations/data-operations-common.ts
1394
+ var DATA_ENTITY_SK = "CURRENT";
1395
+ async function getDataEntityById(entity, tenantId, workspaceId, id, resourceLabel) {
1396
+ const result = await entity.get({
1397
+ tenantId,
1398
+ workspaceId,
1399
+ id,
1400
+ sk: DATA_ENTITY_SK
1401
+ }).go();
1402
+ if (!result.data) {
1403
+ throw new NotFoundError(`${resourceLabel} ${id} not found`, {
1404
+ details: { id }
1405
+ });
1406
+ }
1407
+ const parsed = JSON.parse(decompressResource(result.data.resource));
1408
+ return {
1409
+ id: result.data.id,
1410
+ resource: { ...parsed, id: result.data.id }
1411
+ };
1412
+ }
1413
+ async function deleteDataEntityById(entity, tenantId, workspaceId, id) {
1414
+ await entity.delete({
1415
+ tenantId,
1416
+ workspaceId,
1417
+ id,
1418
+ sk: DATA_ENTITY_SK
1419
+ }).go();
1420
+ }
1421
+ var BATCH_GET_MAX_ATTEMPTS = 3;
1422
+ var BATCH_GET_BASE_BACKOFF_MS = 50;
1423
+ async function batchGetWithRetry(entity, keys) {
1424
+ if (keys.length === 0) return [];
1425
+ const collected = [];
1426
+ let pending = keys;
1427
+ let attempt = 0;
1428
+ while (pending.length > 0) {
1429
+ if (attempt > 0) {
1430
+ await new Promise(
1431
+ (resolve2) => setTimeout(resolve2, BATCH_GET_BASE_BACKOFF_MS * 2 ** (attempt - 1))
1432
+ );
1433
+ }
1434
+ attempt++;
1435
+ const result = await entity.get(pending).go();
1436
+ collected.push(...result.data);
1437
+ const unprocessed = result.unprocessed ?? [];
1438
+ if (unprocessed.length === 0) break;
1439
+ if (attempt >= BATCH_GET_MAX_ATTEMPTS) {
1440
+ throw new Error(
1441
+ `BatchGet exhausted retries: ${unprocessed.length} key(s) still unprocessed after ${BATCH_GET_MAX_ATTEMPTS} attempt(s)`
1442
+ );
1443
+ }
1444
+ pending = unprocessed;
1445
+ }
1446
+ return collected;
1447
+ }
1448
+ async function listDataEntitiesByWorkspace(entity, tenantId, workspaceId) {
1449
+ const shardResults = await Promise.all(
1450
+ Array.from(
1451
+ { length: SHARD_COUNT },
1452
+ (_, shard) => entity.query.gsi1({ tenantId, workspaceId, gsi1Shard: String(shard) }).go()
1453
+ )
1454
+ );
1455
+ const orderedIds = [];
1456
+ for (const shardResult of shardResults) {
1457
+ for (const item of shardResult.data ?? []) {
1458
+ orderedIds.push(item.id);
1459
+ }
1460
+ }
1461
+ if (orderedIds.length === 0) return { entries: [] };
1462
+ const items = await batchGetWithRetry(
1463
+ entity,
1464
+ orderedIds.map((id) => ({
1465
+ tenantId,
1466
+ workspaceId,
1467
+ id,
1468
+ sk: DATA_ENTITY_SK
1469
+ }))
1470
+ );
1471
+ const byId = new Map(items.map((item) => [item.id, item]));
1472
+ const entries = [];
1473
+ for (const id of orderedIds) {
1474
+ const item = byId.get(id);
1475
+ if (!item) continue;
1476
+ const parsed = JSON.parse(decompressResource(item.resource));
1477
+ entries.push({
1478
+ id,
1479
+ resource: { ...parsed, id }
1480
+ });
1481
+ }
1482
+ return { entries };
1483
+ }
1484
+ async function createDataEntityRecord(entity, tenantId, workspaceId, id, resourceWithAudit, fallbackDate) {
1485
+ const lastUpdated = resourceWithAudit.meta?.lastUpdated ?? fallbackDate ?? (/* @__PURE__ */ new Date()).toISOString();
1486
+ const vid = lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36);
1487
+ const resourceLike = resourceWithAudit;
1488
+ const summary = JSON.stringify(extractSummary(resourceLike));
1489
+ const gsi1sk = extractSortKey(resourceLike);
1490
+ await entity.put({
1491
+ sk: DATA_ENTITY_SK,
1492
+ tenantId,
1493
+ workspaceId,
1494
+ id,
1495
+ resource: compressResource(JSON.stringify(resourceWithAudit)),
1496
+ summary,
1497
+ vid,
1498
+ lastUpdated,
1499
+ gsi1sk
1500
+ }).go();
1501
+ return {
1502
+ id,
1503
+ resource: resourceWithAudit
1504
+ };
1505
+ }
1506
+ function buildUpdatedResourceWithAudit(body, id, date, actorId, actorName, existingResourceStr, resourceType) {
1507
+ const existingMeta = JSON.parse(existingResourceStr).meta;
1508
+ const bodyWithMeta = body;
1509
+ const resourceWithVersion = {
1510
+ ...body,
1511
+ resourceType,
1512
+ id,
1513
+ meta: {
1514
+ ...bodyWithMeta.meta ?? {},
1515
+ lastUpdated: date,
1516
+ versionId: "2"
1517
+ }
1518
+ };
1519
+ const resourceWithAudit = {
1520
+ ...resourceWithVersion,
1521
+ meta: mergeAuditIntoMeta(resourceWithVersion.meta ?? existingMeta, {
1522
+ modifiedDate: date,
1523
+ modifiedById: actorId,
1524
+ modifiedByName: actorName
1525
+ })
1526
+ };
1527
+ return {
1528
+ resource: resourceWithAudit,
1529
+ lastUpdated: date
1530
+ };
1531
+ }
1532
+ async function updateDataEntityById(entity, tenantId, workspaceId, id, resourceLabel, context, buildPatched) {
1533
+ const existing = await entity.get({
1534
+ tenantId,
1535
+ workspaceId,
1536
+ id,
1537
+ sk: DATA_ENTITY_SK
1538
+ }).go();
1539
+ if (!existing.data) {
1540
+ throw new NotFoundError(`${resourceLabel} ${id} not found`, {
1541
+ details: { id }
1542
+ });
1543
+ }
1544
+ const existingStr = decompressResource(existing.data.resource);
1545
+ const { resource, lastUpdated } = buildPatched(existingStr);
1546
+ const resourceLike = resource;
1547
+ const summary = JSON.stringify(extractSummary(resourceLike));
1548
+ const gsi1sk = extractSortKey(resourceLike);
1549
+ await entity.patch({
1550
+ tenantId,
1551
+ workspaceId,
1552
+ id,
1553
+ sk: DATA_ENTITY_SK
1554
+ }).set({
1555
+ resource: compressResource(JSON.stringify(resource)),
1556
+ summary,
1557
+ lastUpdated,
1558
+ gsi1sk
1559
+ }).go();
1560
+ return {
1561
+ id,
1562
+ resource
1563
+ };
1564
+ }
1565
+
1360
1566
  // src/data/operations/control/configuration/configuration-list-operation.ts
1567
+ var SK4 = "CURRENT";
1361
1568
  async function listConfigurationsOperation(params) {
1362
1569
  const { context, tableName } = params;
1363
1570
  const { tenantId, workspaceId } = context;
@@ -1368,9 +1575,41 @@ async function listConfigurationsOperation(params) {
1368
1575
  (_, shard) => service.entities.configuration.query.gsi1({ tenantId, workspaceId, gsi1Shard: String(shard) }).go()
1369
1576
  )
1370
1577
  );
1371
- const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
1578
+ const ordered = [];
1579
+ for (const shardResult of shardResults) {
1580
+ for (const item of shardResult.data ?? []) {
1581
+ if (typeof item.summary !== "string") continue;
1582
+ const parsed = JSON.parse(item.summary);
1583
+ if (typeof parsed.id !== "string" || typeof parsed.key !== "string" || typeof parsed.userId !== "string" || typeof parsed.roleId !== "string") {
1584
+ continue;
1585
+ }
1586
+ ordered.push({
1587
+ id: parsed.id,
1588
+ key: parsed.key,
1589
+ userId: parsed.userId,
1590
+ roleId: parsed.roleId
1591
+ });
1592
+ }
1593
+ }
1594
+ if (ordered.length === 0) return { entries: [] };
1595
+ const items = await batchGetWithRetry(
1596
+ service.entities.configuration,
1597
+ ordered.map(({ key, userId, roleId }) => ({
1598
+ tenantId,
1599
+ workspaceId,
1600
+ userId,
1601
+ roleId,
1602
+ key,
1603
+ sk: SK4
1604
+ }))
1605
+ );
1606
+ const byId = new Map(items.map((item) => [item.id, item]));
1607
+ const entries = [];
1608
+ for (const summary of ordered) {
1609
+ const item = byId.get(summary.id);
1610
+ if (!item) continue;
1372
1611
  const resource = JSON.parse(decompressResource(item.resource));
1373
- return {
1612
+ entries.push({
1374
1613
  id: item.id,
1375
1614
  key: item.key,
1376
1615
  resource: {
@@ -1379,8 +1618,8 @@ async function listConfigurationsOperation(params) {
1379
1618
  key: item.key,
1380
1619
  resource
1381
1620
  }
1382
- };
1383
- });
1621
+ });
1622
+ }
1384
1623
  return { entries };
1385
1624
  }
1386
1625
 
@@ -1538,13 +1777,13 @@ async function listConfigurationsRoute(req, res) {
1538
1777
  }
1539
1778
 
1540
1779
  // src/data/operations/control/configuration/configuration-update-operation.ts
1541
- var SK4 = "CURRENT";
1780
+ var SK5 = "CURRENT";
1542
1781
  async function updateConfigurationOperation(params) {
1543
1782
  const { context, id, body, tableName } = params;
1544
1783
  const { tenantId, workspaceId, actorId, date, roleId: ctxRoleId } = context;
1545
1784
  const roleId = ctxRoleId ?? "-";
1546
1785
  const service = getDynamoControlService(tableName);
1547
- const existing = await service.entities.configuration.get({ tenantId, workspaceId, userId: actorId, roleId, key: id, sk: SK4 }).go();
1786
+ const existing = await service.entities.configuration.get({ tenantId, workspaceId, userId: actorId, roleId, key: id, sk: SK5 }).go();
1548
1787
  if (!existing.data) {
1549
1788
  throw new NotFoundError(`Configuration ${id} not found`, {
1550
1789
  details: { id }
@@ -1557,9 +1796,11 @@ async function updateConfigurationOperation(params) {
1557
1796
  const summary = JSON.stringify({
1558
1797
  resourceType: "Configuration",
1559
1798
  id: existing.data.id,
1560
- key: existing.data.key
1799
+ key: existing.data.key,
1800
+ userId: actorId,
1801
+ roleId
1561
1802
  });
1562
- await service.entities.configuration.patch({ tenantId, workspaceId, userId: actorId, roleId, key: id, sk: SK4 }).set({
1803
+ await service.entities.configuration.patch({ tenantId, workspaceId, userId: actorId, roleId, key: id, sk: SK5 }).set({
1563
1804
  resource: compressResource(resourceStr),
1564
1805
  summary,
1565
1806
  lastUpdated,
@@ -1639,7 +1880,7 @@ router.delete("/:id", deleteConfigurationRoute);
1639
1880
  import express2 from "express";
1640
1881
 
1641
1882
  // src/data/operations/control/membership/membership-create-operation.ts
1642
- import { extractSummary } from "@openhi/types";
1883
+ import { extractSummary as extractSummary2 } from "@openhi/types";
1643
1884
  async function createMembershipOperation(params) {
1644
1885
  const { context, body, tableName } = params;
1645
1886
  const service = getDynamoControlService(tableName);
@@ -1648,7 +1889,7 @@ async function createMembershipOperation(params) {
1648
1889
  const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
1649
1890
  const vid = `1`;
1650
1891
  const resource = { resourceType: "Membership", id, ...parsedResource };
1651
- const summary = JSON.stringify(extractSummary(resource));
1892
+ const summary = JSON.stringify(extractSummary2(resource));
1652
1893
  await service.entities.membership.put({
1653
1894
  tenantId: context.tenantId,
1654
1895
  id,
@@ -1764,26 +2005,39 @@ async function getMembershipByIdRoute(req, res) {
1764
2005
  }
1765
2006
 
1766
2007
  // src/data/operations/control/membership/membership-list-operation.ts
2008
+ var SK6 = "CURRENT";
1767
2009
  async function listMembershipsOperation(params) {
1768
2010
  const { context, tableName } = params;
2011
+ const tenantId = context.tenantId;
1769
2012
  const service = getDynamoControlService(tableName);
1770
2013
  const shardResults = await Promise.all(
1771
2014
  Array.from(
1772
2015
  { length: SHARD_COUNT },
1773
- (_, shard) => service.entities.membership.query.gsi1({ tenantId: context.tenantId, gsi1Shard: String(shard) }).go()
2016
+ (_, shard) => service.entities.membership.query.gsi1({ tenantId, gsi1Shard: String(shard) }).go()
1774
2017
  )
1775
2018
  );
1776
- const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
2019
+ const orderedIds = [];
2020
+ for (const shardResult of shardResults) {
2021
+ for (const item of shardResult.data ?? []) {
2022
+ orderedIds.push(item.id);
2023
+ }
2024
+ }
2025
+ if (orderedIds.length === 0) return { entries: [] };
2026
+ const items = await batchGetWithRetry(
2027
+ service.entities.membership,
2028
+ orderedIds.map((id) => ({ tenantId, id, sk: SK6 }))
2029
+ );
2030
+ const byId = new Map(items.map((item) => [item.id, item]));
2031
+ const entries = [];
2032
+ for (const id of orderedIds) {
2033
+ const item = byId.get(id);
2034
+ if (!item) continue;
1777
2035
  const parsedResource = JSON.parse(item.resource);
1778
- return {
1779
- id: item.id,
1780
- resource: {
1781
- resourceType: "Membership",
1782
- id: item.id,
1783
- ...parsedResource
1784
- }
1785
- };
1786
- });
2036
+ entries.push({
2037
+ id,
2038
+ resource: { resourceType: "Membership", id, ...parsedResource }
2039
+ });
2040
+ }
1787
2041
  return { entries };
1788
2042
  }
1789
2043
 
@@ -1810,7 +2064,7 @@ async function listMembershipsRoute(req, res) {
1810
2064
  }
1811
2065
 
1812
2066
  // src/data/operations/control/membership/membership-update-operation.ts
1813
- import { extractSummary as extractSummary2 } from "@openhi/types";
2067
+ import { extractSummary as extractSummary3 } from "@openhi/types";
1814
2068
  async function updateMembershipOperation(params) {
1815
2069
  const { context, id, body, tableName } = params;
1816
2070
  const service = getDynamoControlService(tableName);
@@ -1822,7 +2076,7 @@ async function updateMembershipOperation(params) {
1822
2076
  const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
1823
2077
  const vid = `${Date.now()}`;
1824
2078
  const resource = { resourceType: "Membership", id, ...parsedResource };
1825
- const summary = JSON.stringify(extractSummary2(resource));
2079
+ const summary = JSON.stringify(extractSummary3(resource));
1826
2080
  await service.entities.membership.put({
1827
2081
  tenantId: context.tenantId,
1828
2082
  id,
@@ -1890,7 +2144,7 @@ router2.delete("/:id", deleteMembershipRoute);
1890
2144
  import express3 from "express";
1891
2145
 
1892
2146
  // src/data/operations/control/role/role-create-operation.ts
1893
- import { extractSummary as extractSummary3 } from "@openhi/types";
2147
+ import { extractSummary as extractSummary4 } from "@openhi/types";
1894
2148
  async function createRoleOperation(params) {
1895
2149
  const { context, body, tableName } = params;
1896
2150
  const service = getDynamoControlService(tableName);
@@ -1899,7 +2153,7 @@ async function createRoleOperation(params) {
1899
2153
  const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
1900
2154
  const vid = `1`;
1901
2155
  const resource = { resourceType: "Role", id, ...parsedResource };
1902
- const summary = JSON.stringify(extractSummary3(resource));
2156
+ const summary = JSON.stringify(extractSummary4(resource));
1903
2157
  await service.entities.role.put({
1904
2158
  id,
1905
2159
  resource: JSON.stringify(resource),
@@ -2014,6 +2268,7 @@ async function getRoleByIdRoute(req, res) {
2014
2268
  }
2015
2269
 
2016
2270
  // src/data/operations/control/role/role-list-operation.ts
2271
+ var SK7 = "CURRENT";
2017
2272
  async function listRolesOperation(params) {
2018
2273
  const { tableName } = params;
2019
2274
  const service = getDynamoControlService(tableName);
@@ -2023,13 +2278,28 @@ async function listRolesOperation(params) {
2023
2278
  (_, shard) => service.entities.role.query.gsi1({ gsi1Shard: String(shard) }).go()
2024
2279
  )
2025
2280
  );
2026
- const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
2281
+ const orderedIds = [];
2282
+ for (const shardResult of shardResults) {
2283
+ for (const item of shardResult.data ?? []) {
2284
+ orderedIds.push(item.id);
2285
+ }
2286
+ }
2287
+ if (orderedIds.length === 0) return { entries: [] };
2288
+ const items = await batchGetWithRetry(
2289
+ service.entities.role,
2290
+ orderedIds.map((id) => ({ id, sk: SK7 }))
2291
+ );
2292
+ const byId = new Map(items.map((item) => [item.id, item]));
2293
+ const entries = [];
2294
+ for (const id of orderedIds) {
2295
+ const item = byId.get(id);
2296
+ if (!item) continue;
2027
2297
  const parsedResource = JSON.parse(item.resource);
2028
- return {
2029
- id: item.id,
2030
- resource: { resourceType: "Role", id: item.id, ...parsedResource }
2031
- };
2032
- });
2298
+ entries.push({
2299
+ id,
2300
+ resource: { resourceType: "Role", id, ...parsedResource }
2301
+ });
2302
+ }
2033
2303
  return { entries };
2034
2304
  }
2035
2305
 
@@ -2056,7 +2326,7 @@ async function listRolesRoute(req, res) {
2056
2326
  }
2057
2327
 
2058
2328
  // src/data/operations/control/role/role-update-operation.ts
2059
- import { extractSummary as extractSummary4 } from "@openhi/types";
2329
+ import { extractSummary as extractSummary5 } from "@openhi/types";
2060
2330
  async function updateRoleOperation(params) {
2061
2331
  const { context, id, body, tableName } = params;
2062
2332
  const service = getDynamoControlService(tableName);
@@ -2068,7 +2338,7 @@ async function updateRoleOperation(params) {
2068
2338
  const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
2069
2339
  const vid = `${Date.now()}`;
2070
2340
  const resource = { resourceType: "Role", id, ...parsedResource };
2071
- const summary = JSON.stringify(extractSummary4(resource));
2341
+ const summary = JSON.stringify(extractSummary5(resource));
2072
2342
  await service.entities.role.put({
2073
2343
  id,
2074
2344
  resource: JSON.stringify(resource),
@@ -2135,7 +2405,7 @@ router3.delete("/:id", deleteRoleRoute);
2135
2405
  import express4 from "express";
2136
2406
 
2137
2407
  // src/data/operations/control/roleassignment/roleassignment-create-operation.ts
2138
- import { extractSummary as extractSummary5 } from "@openhi/types";
2408
+ import { extractSummary as extractSummary6 } from "@openhi/types";
2139
2409
  async function createRoleAssignmentOperation(params) {
2140
2410
  const { context, body, tableName } = params;
2141
2411
  const service = getDynamoControlService(tableName);
@@ -2144,7 +2414,7 @@ async function createRoleAssignmentOperation(params) {
2144
2414
  const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
2145
2415
  const vid = `1`;
2146
2416
  const resource = { resourceType: "RoleAssignment", id, ...parsedResource };
2147
- const summary = JSON.stringify(extractSummary5(resource));
2417
+ const summary = JSON.stringify(extractSummary6(resource));
2148
2418
  await service.entities.roleAssignment.put({
2149
2419
  tenantId: context.tenantId,
2150
2420
  id,
@@ -2260,26 +2530,39 @@ async function getRoleAssignmentByIdRoute(req, res) {
2260
2530
  }
2261
2531
 
2262
2532
  // src/data/operations/control/roleassignment/roleassignment-list-operation.ts
2533
+ var SK8 = "CURRENT";
2263
2534
  async function listRoleAssignmentsOperation(params) {
2264
2535
  const { context, tableName } = params;
2536
+ const tenantId = context.tenantId;
2265
2537
  const service = getDynamoControlService(tableName);
2266
2538
  const shardResults = await Promise.all(
2267
2539
  Array.from(
2268
2540
  { length: SHARD_COUNT },
2269
- (_, shard) => service.entities.roleAssignment.query.gsi1({ tenantId: context.tenantId, gsi1Shard: String(shard) }).go()
2541
+ (_, shard) => service.entities.roleAssignment.query.gsi1({ tenantId, gsi1Shard: String(shard) }).go()
2270
2542
  )
2271
2543
  );
2272
- const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
2544
+ const orderedIds = [];
2545
+ for (const shardResult of shardResults) {
2546
+ for (const item of shardResult.data ?? []) {
2547
+ orderedIds.push(item.id);
2548
+ }
2549
+ }
2550
+ if (orderedIds.length === 0) return { entries: [] };
2551
+ const items = await batchGetWithRetry(
2552
+ service.entities.roleAssignment,
2553
+ orderedIds.map((id) => ({ tenantId, id, sk: SK8 }))
2554
+ );
2555
+ const byId = new Map(items.map((item) => [item.id, item]));
2556
+ const entries = [];
2557
+ for (const id of orderedIds) {
2558
+ const item = byId.get(id);
2559
+ if (!item) continue;
2273
2560
  const parsedResource = JSON.parse(item.resource);
2274
- return {
2275
- id: item.id,
2276
- resource: {
2277
- resourceType: "RoleAssignment",
2278
- id: item.id,
2279
- ...parsedResource
2280
- }
2281
- };
2282
- });
2561
+ entries.push({
2562
+ id,
2563
+ resource: { resourceType: "RoleAssignment", id, ...parsedResource }
2564
+ });
2565
+ }
2283
2566
  return { entries };
2284
2567
  }
2285
2568
 
@@ -2306,7 +2589,7 @@ async function listRoleAssignmentsRoute(req, res) {
2306
2589
  }
2307
2590
 
2308
2591
  // src/data/operations/control/roleassignment/roleassignment-update-operation.ts
2309
- import { extractSummary as extractSummary6 } from "@openhi/types";
2592
+ import { extractSummary as extractSummary7 } from "@openhi/types";
2310
2593
  async function updateRoleAssignmentOperation(params) {
2311
2594
  const { context, id, body, tableName } = params;
2312
2595
  const service = getDynamoControlService(tableName);
@@ -2318,7 +2601,7 @@ async function updateRoleAssignmentOperation(params) {
2318
2601
  const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
2319
2602
  const vid = `${Date.now()}`;
2320
2603
  const resource = { resourceType: "RoleAssignment", id, ...parsedResource };
2321
- const summary = JSON.stringify(extractSummary6(resource));
2604
+ const summary = JSON.stringify(extractSummary7(resource));
2322
2605
  await service.entities.roleAssignment.put({
2323
2606
  tenantId: context.tenantId,
2324
2607
  id,
@@ -2386,7 +2669,7 @@ router4.delete("/:id", deleteRoleAssignmentRoute);
2386
2669
  import express5 from "express";
2387
2670
 
2388
2671
  // src/data/operations/control/tenant/tenant-create-operation.ts
2389
- import { extractSummary as extractSummary7 } from "@openhi/types";
2672
+ import { extractSummary as extractSummary8 } from "@openhi/types";
2390
2673
  async function createTenantOperation(params) {
2391
2674
  const { context, body, tableName } = params;
2392
2675
  const service = getDynamoControlService(tableName);
@@ -2395,7 +2678,7 @@ async function createTenantOperation(params) {
2395
2678
  const vid = lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36);
2396
2679
  const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
2397
2680
  const resource = { resourceType: "Tenant", id, ...parsedResource };
2398
- const summary = JSON.stringify(extractSummary7(resource));
2681
+ const summary = JSON.stringify(extractSummary8(resource));
2399
2682
  await service.entities.tenant.put({
2400
2683
  tenantId: id,
2401
2684
  id,
@@ -2506,6 +2789,7 @@ async function getTenantByIdRoute(req, res) {
2506
2789
  }
2507
2790
 
2508
2791
  // src/data/operations/control/tenant/tenant-list-operation.ts
2792
+ var SK9 = "CURRENT";
2509
2793
  async function listTenantsOperation(params) {
2510
2794
  const { tableName } = params;
2511
2795
  const service = getDynamoControlService(tableName);
@@ -2515,13 +2799,28 @@ async function listTenantsOperation(params) {
2515
2799
  (_, shard) => service.entities.tenant.query.gsi1({ gsi1Shard: String(shard) }).go()
2516
2800
  )
2517
2801
  );
2518
- const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
2802
+ const orderedIds = [];
2803
+ for (const shardResult of shardResults) {
2804
+ for (const item of shardResult.data ?? []) {
2805
+ orderedIds.push(item.id);
2806
+ }
2807
+ }
2808
+ if (orderedIds.length === 0) return { entries: [] };
2809
+ const items = await batchGetWithRetry(
2810
+ service.entities.tenant,
2811
+ orderedIds.map((id) => ({ tenantId: id, sk: SK9 }))
2812
+ );
2813
+ const byId = new Map(items.map((item) => [item.id, item]));
2814
+ const entries = [];
2815
+ for (const id of orderedIds) {
2816
+ const item = byId.get(id);
2817
+ if (!item) continue;
2519
2818
  const parsed = JSON.parse(item.resource);
2520
- return {
2521
- id: item.id,
2522
- resource: { resourceType: "Tenant", id: item.id, ...parsed }
2523
- };
2524
- });
2819
+ entries.push({
2820
+ id,
2821
+ resource: { resourceType: "Tenant", id, ...parsed }
2822
+ });
2823
+ }
2525
2824
  return { entries };
2526
2825
  }
2527
2826
 
@@ -2548,7 +2847,7 @@ async function listTenantsRoute(req, res) {
2548
2847
  }
2549
2848
 
2550
2849
  // src/data/operations/control/tenant/tenant-update-operation.ts
2551
- import { extractSummary as extractSummary8 } from "@openhi/types";
2850
+ import { extractSummary as extractSummary9 } from "@openhi/types";
2552
2851
  async function updateTenantOperation(params) {
2553
2852
  const { context, id, body, tableName } = params;
2554
2853
  const service = getDynamoControlService(tableName);
@@ -2566,7 +2865,7 @@ async function updateTenantOperation(params) {
2566
2865
  resourceType: "Tenant",
2567
2866
  id
2568
2867
  };
2569
- const summary = JSON.stringify(extractSummary8(updated));
2868
+ const summary = JSON.stringify(extractSummary9(updated));
2570
2869
  await service.entities.tenant.patch({ tenantId: id, sk: "CURRENT" }).set({ resource: JSON.stringify(updated), summary, vid, lastUpdated }).go();
2571
2870
  return { id, resource: updated, meta: { lastUpdated, versionId: vid } };
2572
2871
  }
@@ -2623,7 +2922,7 @@ router5.delete("/:id", deleteTenantRoute);
2623
2922
  import express6 from "express";
2624
2923
 
2625
2924
  // src/data/operations/control/user/user-create-operation.ts
2626
- import { extractSummary as extractSummary9 } from "@openhi/types";
2925
+ import { extractSummary as extractSummary10 } from "@openhi/types";
2627
2926
  async function createUserOperation(params) {
2628
2927
  const { context, body, tableName } = params;
2629
2928
  const service = getDynamoControlService(tableName);
@@ -2632,7 +2931,7 @@ async function createUserOperation(params) {
2632
2931
  const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
2633
2932
  const vid = `1`;
2634
2933
  const resource = { resourceType: "User", id, ...parsedResource };
2635
- const summary = JSON.stringify(extractSummary9(resource));
2934
+ const summary = JSON.stringify(extractSummary10(resource));
2636
2935
  await service.entities.user.put({
2637
2936
  id,
2638
2937
  resource: JSON.stringify(resource),
@@ -2747,6 +3046,7 @@ async function getUserByIdRoute(req, res) {
2747
3046
  }
2748
3047
 
2749
3048
  // src/data/operations/control/user/user-list-operation.ts
3049
+ var SK10 = "CURRENT";
2750
3050
  async function listUsersOperation(params) {
2751
3051
  const { tableName } = params;
2752
3052
  const service = getDynamoControlService(tableName);
@@ -2756,13 +3056,28 @@ async function listUsersOperation(params) {
2756
3056
  (_, shard) => service.entities.user.query.gsi1({ gsi1Shard: String(shard) }).go()
2757
3057
  )
2758
3058
  );
2759
- const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
3059
+ const orderedIds = [];
3060
+ for (const shardResult of shardResults) {
3061
+ for (const item of shardResult.data ?? []) {
3062
+ orderedIds.push(item.id);
3063
+ }
3064
+ }
3065
+ if (orderedIds.length === 0) return { entries: [] };
3066
+ const items = await batchGetWithRetry(
3067
+ service.entities.user,
3068
+ orderedIds.map((id) => ({ id, sk: SK10 }))
3069
+ );
3070
+ const byId = new Map(items.map((item) => [item.id, item]));
3071
+ const entries = [];
3072
+ for (const id of orderedIds) {
3073
+ const item = byId.get(id);
3074
+ if (!item) continue;
2760
3075
  const parsedResource = JSON.parse(item.resource);
2761
- return {
2762
- id: item.id,
2763
- resource: { resourceType: "User", id: item.id, ...parsedResource }
2764
- };
2765
- });
3076
+ entries.push({
3077
+ id,
3078
+ resource: { resourceType: "User", id, ...parsedResource }
3079
+ });
3080
+ }
2766
3081
  return { entries };
2767
3082
  }
2768
3083
 
@@ -2789,7 +3104,7 @@ async function listUsersRoute(req, res) {
2789
3104
  }
2790
3105
 
2791
3106
  // src/data/operations/control/user/user-update-operation.ts
2792
- import { extractSummary as extractSummary10 } from "@openhi/types";
3107
+ import { extractSummary as extractSummary11 } from "@openhi/types";
2793
3108
  async function updateUserOperation(params) {
2794
3109
  const { context, id, body, tableName } = params;
2795
3110
  const service = getDynamoControlService(tableName);
@@ -2801,7 +3116,7 @@ async function updateUserOperation(params) {
2801
3116
  const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
2802
3117
  const vid = `${Date.now()}`;
2803
3118
  const resource = { resourceType: "User", id, ...parsedResource };
2804
- const summary = JSON.stringify(extractSummary10(resource));
3119
+ const summary = JSON.stringify(extractSummary11(resource));
2805
3120
  await service.entities.user.put({
2806
3121
  id,
2807
3122
  resource: JSON.stringify(resource),
@@ -2868,7 +3183,7 @@ router6.delete("/:id", deleteUserRoute);
2868
3183
  import express7 from "express";
2869
3184
 
2870
3185
  // src/data/operations/control/workspace/workspace-create-operation.ts
2871
- import { extractSummary as extractSummary11 } from "@openhi/types";
3186
+ import { extractSummary as extractSummary12 } from "@openhi/types";
2872
3187
  async function createWorkspaceOperation(params) {
2873
3188
  const { context, body, tableName } = params;
2874
3189
  const { tenantId } = context;
@@ -2878,7 +3193,7 @@ async function createWorkspaceOperation(params) {
2878
3193
  const vid = lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36);
2879
3194
  const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
2880
3195
  const resource = { resourceType: "Workspace", id, ...parsedResource };
2881
- const summary = JSON.stringify(extractSummary11(resource));
3196
+ const summary = JSON.stringify(extractSummary12(resource));
2882
3197
  await service.entities.workspace.put({
2883
3198
  tenantId,
2884
3199
  id,
@@ -2991,6 +3306,7 @@ async function getWorkspaceByIdRoute(req, res) {
2991
3306
  }
2992
3307
 
2993
3308
  // src/data/operations/control/workspace/workspace-list-operation.ts
3309
+ var SK11 = "CURRENT";
2994
3310
  async function listWorkspacesOperation(params) {
2995
3311
  const { context, tableName } = params;
2996
3312
  const { tenantId } = context;
@@ -3001,13 +3317,28 @@ async function listWorkspacesOperation(params) {
3001
3317
  (_, shard) => service.entities.workspace.query.gsi1({ tenantId, gsi1Shard: String(shard) }).go()
3002
3318
  )
3003
3319
  );
3004
- const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
3320
+ const orderedIds = [];
3321
+ for (const shardResult of shardResults) {
3322
+ for (const item of shardResult.data ?? []) {
3323
+ orderedIds.push(item.id);
3324
+ }
3325
+ }
3326
+ if (orderedIds.length === 0) return { entries: [] };
3327
+ const items = await batchGetWithRetry(
3328
+ service.entities.workspace,
3329
+ orderedIds.map((id) => ({ tenantId, id, sk: SK11 }))
3330
+ );
3331
+ const byId = new Map(items.map((item) => [item.id, item]));
3332
+ const entries = [];
3333
+ for (const id of orderedIds) {
3334
+ const item = byId.get(id);
3335
+ if (!item) continue;
3005
3336
  const parsed = JSON.parse(item.resource);
3006
- return {
3007
- id: item.id,
3008
- resource: { resourceType: "Workspace", id: item.id, ...parsed }
3009
- };
3010
- });
3337
+ entries.push({
3338
+ id,
3339
+ resource: { resourceType: "Workspace", id, ...parsed }
3340
+ });
3341
+ }
3011
3342
  return { entries };
3012
3343
  }
3013
3344
 
@@ -3034,7 +3365,7 @@ async function listWorkspacesRoute(req, res) {
3034
3365
  }
3035
3366
 
3036
3367
  // src/data/operations/control/workspace/workspace-update-operation.ts
3037
- import { extractSummary as extractSummary12 } from "@openhi/types";
3368
+ import { extractSummary as extractSummary13 } from "@openhi/types";
3038
3369
  async function updateWorkspaceOperation(params) {
3039
3370
  const { context, id, body, tableName } = params;
3040
3371
  const { tenantId } = context;
@@ -3053,7 +3384,7 @@ async function updateWorkspaceOperation(params) {
3053
3384
  resourceType: "Workspace",
3054
3385
  id
3055
3386
  };
3056
- const summary = JSON.stringify(extractSummary12(updated));
3387
+ const summary = JSON.stringify(extractSummary13(updated));
3057
3388
  await service.entities.workspace.patch({ tenantId, id, sk: "CURRENT" }).set({ resource: JSON.stringify(updated), summary, vid, lastUpdated }).go();
3058
3389
  return { id, resource: updated, meta: { lastUpdated, versionId: vid } };
3059
3390
  }
@@ -3112,30 +3443,6 @@ import express8 from "express";
3112
3443
  // src/data/operations/data/account/account-create-operation.ts
3113
3444
  import { ulid } from "ulid";
3114
3445
 
3115
- // src/data/audit-meta.ts
3116
- var OPENHI_EXT = "http://openhi.org/fhir/StructureDefinition";
3117
- function mergeAuditIntoMeta(meta, audit) {
3118
- const existing = meta ?? {};
3119
- const ext = [
3120
- ...Array.isArray(existing.extension) ? existing.extension : []
3121
- ];
3122
- const byUrl = new Map(ext.map((e) => [e.url, e]));
3123
- function set(url, value, type) {
3124
- if (value == null) return;
3125
- byUrl.set(url, { url, [type]: value });
3126
- }
3127
- set(`${OPENHI_EXT}/created-date`, audit.createdDate, "valueDateTime");
3128
- set(`${OPENHI_EXT}/created-by-id`, audit.createdById, "valueString");
3129
- set(`${OPENHI_EXT}/created-by-name`, audit.createdByName, "valueString");
3130
- set(`${OPENHI_EXT}/modified-date`, audit.modifiedDate, "valueDateTime");
3131
- set(`${OPENHI_EXT}/modified-by-id`, audit.modifiedById, "valueString");
3132
- set(`${OPENHI_EXT}/modified-by-name`, audit.modifiedByName, "valueString");
3133
- set(`${OPENHI_EXT}/deleted-date`, audit.deletedDate, "valueDateTime");
3134
- set(`${OPENHI_EXT}/deleted-by-id`, audit.deletedById, "valueString");
3135
- set(`${OPENHI_EXT}/deleted-by-name`, audit.deletedByName, "valueString");
3136
- return { ...existing, extension: Array.from(byUrl.values()) };
3137
- }
3138
-
3139
3446
  // src/data/dynamo/dynamo-data-service.ts
3140
3447
  import { Service as Service2 } from "electrodb";
3141
3448
 
@@ -4168,136 +4475,6 @@ function getDynamoDataService(tableName) {
4168
4475
  };
4169
4476
  }
4170
4477
 
4171
- // src/data/operations/data-operations-common.ts
4172
- import { extractSortKey, extractSummary as extractSummary13 } from "@openhi/types";
4173
- var DATA_ENTITY_SK = "CURRENT";
4174
- async function getDataEntityById(entity, tenantId, workspaceId, id, resourceLabel) {
4175
- const result = await entity.get({
4176
- tenantId,
4177
- workspaceId,
4178
- id,
4179
- sk: DATA_ENTITY_SK
4180
- }).go();
4181
- if (!result.data) {
4182
- throw new NotFoundError(`${resourceLabel} ${id} not found`, {
4183
- details: { id }
4184
- });
4185
- }
4186
- const parsed = JSON.parse(decompressResource(result.data.resource));
4187
- return {
4188
- id: result.data.id,
4189
- resource: { ...parsed, id: result.data.id }
4190
- };
4191
- }
4192
- async function deleteDataEntityById(entity, tenantId, workspaceId, id) {
4193
- await entity.delete({
4194
- tenantId,
4195
- workspaceId,
4196
- id,
4197
- sk: DATA_ENTITY_SK
4198
- }).go();
4199
- }
4200
- async function listDataEntitiesByWorkspace(entity, tenantId, workspaceId) {
4201
- const shardResults = await Promise.all(
4202
- Array.from(
4203
- { length: SHARD_COUNT },
4204
- (_, shard) => entity.query.gsi1({ tenantId, workspaceId, gsi1Shard: String(shard) }).go()
4205
- )
4206
- );
4207
- const entries = [];
4208
- for (const shardResult of shardResults) {
4209
- for (const item of shardResult.data ?? []) {
4210
- const parsed = JSON.parse(decompressResource(item.resource));
4211
- entries.push({
4212
- id: item.id,
4213
- resource: { ...parsed, id: item.id }
4214
- });
4215
- }
4216
- }
4217
- return { entries };
4218
- }
4219
- async function createDataEntityRecord(entity, tenantId, workspaceId, id, resourceWithAudit, fallbackDate) {
4220
- const lastUpdated = resourceWithAudit.meta?.lastUpdated ?? fallbackDate ?? (/* @__PURE__ */ new Date()).toISOString();
4221
- const vid = lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36);
4222
- const resourceLike = resourceWithAudit;
4223
- const summary = JSON.stringify(extractSummary13(resourceLike));
4224
- const gsi1sk = extractSortKey(resourceLike);
4225
- await entity.put({
4226
- sk: DATA_ENTITY_SK,
4227
- tenantId,
4228
- workspaceId,
4229
- id,
4230
- resource: compressResource(JSON.stringify(resourceWithAudit)),
4231
- summary,
4232
- vid,
4233
- lastUpdated,
4234
- gsi1sk
4235
- }).go();
4236
- return {
4237
- id,
4238
- resource: resourceWithAudit
4239
- };
4240
- }
4241
- function buildUpdatedResourceWithAudit(body, id, date, actorId, actorName, existingResourceStr, resourceType) {
4242
- const existingMeta = JSON.parse(existingResourceStr).meta;
4243
- const bodyWithMeta = body;
4244
- const resourceWithVersion = {
4245
- ...body,
4246
- resourceType,
4247
- id,
4248
- meta: {
4249
- ...bodyWithMeta.meta ?? {},
4250
- lastUpdated: date,
4251
- versionId: "2"
4252
- }
4253
- };
4254
- const resourceWithAudit = {
4255
- ...resourceWithVersion,
4256
- meta: mergeAuditIntoMeta(resourceWithVersion.meta ?? existingMeta, {
4257
- modifiedDate: date,
4258
- modifiedById: actorId,
4259
- modifiedByName: actorName
4260
- })
4261
- };
4262
- return {
4263
- resource: resourceWithAudit,
4264
- lastUpdated: date
4265
- };
4266
- }
4267
- async function updateDataEntityById(entity, tenantId, workspaceId, id, resourceLabel, context, buildPatched) {
4268
- const existing = await entity.get({
4269
- tenantId,
4270
- workspaceId,
4271
- id,
4272
- sk: DATA_ENTITY_SK
4273
- }).go();
4274
- if (!existing.data) {
4275
- throw new NotFoundError(`${resourceLabel} ${id} not found`, {
4276
- details: { id }
4277
- });
4278
- }
4279
- const existingStr = decompressResource(existing.data.resource);
4280
- const { resource, lastUpdated } = buildPatched(existingStr);
4281
- const resourceLike = resource;
4282
- const summary = JSON.stringify(extractSummary13(resourceLike));
4283
- const gsi1sk = extractSortKey(resourceLike);
4284
- await entity.patch({
4285
- tenantId,
4286
- workspaceId,
4287
- id,
4288
- sk: DATA_ENTITY_SK
4289
- }).set({
4290
- resource: compressResource(JSON.stringify(resource)),
4291
- summary,
4292
- lastUpdated,
4293
- gsi1sk
4294
- }).go();
4295
- return {
4296
- id,
4297
- resource
4298
- };
4299
- }
4300
-
4301
4478
  // src/data/operations/data/account/account-create-operation.ts
4302
4479
  async function createAccountOperation(params) {
4303
4480
  const { context, body, tableName } = params;
@@ -25035,7 +25212,7 @@ function extractPatient(parsed) {
25035
25212
  "File must be a FHIR Patient resource or a Bundle containing at least one Patient entry"
25036
25213
  );
25037
25214
  }
25038
- var SK5 = "CURRENT";
25215
+ var SK12 = "CURRENT";
25039
25216
  var defaultAudit = {
25040
25217
  createdDate: (/* @__PURE__ */ new Date()).toISOString(),
25041
25218
  createdById: "import",
@@ -25073,7 +25250,7 @@ function patientToPutAttrs(patient, options) {
25073
25250
  patientWithMeta.meta.lastUpdated = lastUpdated;
25074
25251
  }
25075
25252
  return {
25076
- sk: SK5,
25253
+ sk: SK12,
25077
25254
  tenantId,
25078
25255
  workspaceId,
25079
25256
  id: patient.id,