@openhi/constructs 0.0.87 → 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.
@@ -975,7 +975,13 @@ async function createConfigurationOperation(params) {
975
975
  const roleId = body.roleId ?? context.roleId ?? "-";
976
976
  const lastUpdated = body.lastUpdated ?? date;
977
977
  const vid = body.vid ?? (date.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36));
978
- const summary = JSON.stringify({ resourceType: "Configuration", id, key });
978
+ const summary = JSON.stringify({
979
+ resourceType: "Configuration",
980
+ id,
981
+ key,
982
+ userId,
983
+ roleId
984
+ });
979
985
  const service = getDynamoControlService(tableName);
980
986
  await service.entities.configuration.put({
981
987
  tenantId,
@@ -1387,7 +1393,208 @@ async function getConfigurationByIdRoute(req, res) {
1387
1393
  }
1388
1394
  }
1389
1395
 
1396
+ // src/data/operations/data-operations-common.ts
1397
+ var import_types = require("@openhi/types");
1398
+
1399
+ // src/data/audit-meta.ts
1400
+ var OPENHI_EXT = "http://openhi.org/fhir/StructureDefinition";
1401
+ function mergeAuditIntoMeta(meta, audit) {
1402
+ const existing = meta ?? {};
1403
+ const ext = [
1404
+ ...Array.isArray(existing.extension) ? existing.extension : []
1405
+ ];
1406
+ const byUrl = new Map(ext.map((e) => [e.url, e]));
1407
+ function set(url, value, type) {
1408
+ if (value == null) return;
1409
+ byUrl.set(url, { url, [type]: value });
1410
+ }
1411
+ set(`${OPENHI_EXT}/created-date`, audit.createdDate, "valueDateTime");
1412
+ set(`${OPENHI_EXT}/created-by-id`, audit.createdById, "valueString");
1413
+ set(`${OPENHI_EXT}/created-by-name`, audit.createdByName, "valueString");
1414
+ set(`${OPENHI_EXT}/modified-date`, audit.modifiedDate, "valueDateTime");
1415
+ set(`${OPENHI_EXT}/modified-by-id`, audit.modifiedById, "valueString");
1416
+ set(`${OPENHI_EXT}/modified-by-name`, audit.modifiedByName, "valueString");
1417
+ set(`${OPENHI_EXT}/deleted-date`, audit.deletedDate, "valueDateTime");
1418
+ set(`${OPENHI_EXT}/deleted-by-id`, audit.deletedById, "valueString");
1419
+ set(`${OPENHI_EXT}/deleted-by-name`, audit.deletedByName, "valueString");
1420
+ return { ...existing, extension: Array.from(byUrl.values()) };
1421
+ }
1422
+
1423
+ // src/data/operations/data-operations-common.ts
1424
+ var DATA_ENTITY_SK = "CURRENT";
1425
+ async function getDataEntityById(entity, tenantId, workspaceId, id, resourceLabel) {
1426
+ const result = await entity.get({
1427
+ tenantId,
1428
+ workspaceId,
1429
+ id,
1430
+ sk: DATA_ENTITY_SK
1431
+ }).go();
1432
+ if (!result.data) {
1433
+ throw new NotFoundError(`${resourceLabel} ${id} not found`, {
1434
+ details: { id }
1435
+ });
1436
+ }
1437
+ const parsed = JSON.parse(decompressResource(result.data.resource));
1438
+ return {
1439
+ id: result.data.id,
1440
+ resource: { ...parsed, id: result.data.id }
1441
+ };
1442
+ }
1443
+ async function deleteDataEntityById(entity, tenantId, workspaceId, id) {
1444
+ await entity.delete({
1445
+ tenantId,
1446
+ workspaceId,
1447
+ id,
1448
+ sk: DATA_ENTITY_SK
1449
+ }).go();
1450
+ }
1451
+ var BATCH_GET_MAX_ATTEMPTS = 3;
1452
+ var BATCH_GET_BASE_BACKOFF_MS = 50;
1453
+ async function batchGetWithRetry(entity, keys) {
1454
+ if (keys.length === 0) return [];
1455
+ const collected = [];
1456
+ let pending = keys;
1457
+ let attempt = 0;
1458
+ while (pending.length > 0) {
1459
+ if (attempt > 0) {
1460
+ await new Promise(
1461
+ (resolve2) => setTimeout(resolve2, BATCH_GET_BASE_BACKOFF_MS * 2 ** (attempt - 1))
1462
+ );
1463
+ }
1464
+ attempt++;
1465
+ const result = await entity.get(pending).go();
1466
+ collected.push(...result.data);
1467
+ const unprocessed = result.unprocessed ?? [];
1468
+ if (unprocessed.length === 0) break;
1469
+ if (attempt >= BATCH_GET_MAX_ATTEMPTS) {
1470
+ throw new Error(
1471
+ `BatchGet exhausted retries: ${unprocessed.length} key(s) still unprocessed after ${BATCH_GET_MAX_ATTEMPTS} attempt(s)`
1472
+ );
1473
+ }
1474
+ pending = unprocessed;
1475
+ }
1476
+ return collected;
1477
+ }
1478
+ async function listDataEntitiesByWorkspace(entity, tenantId, workspaceId) {
1479
+ const shardResults = await Promise.all(
1480
+ Array.from(
1481
+ { length: SHARD_COUNT },
1482
+ (_, shard) => entity.query.gsi1({ tenantId, workspaceId, gsi1Shard: String(shard) }).go()
1483
+ )
1484
+ );
1485
+ const orderedIds = [];
1486
+ for (const shardResult of shardResults) {
1487
+ for (const item of shardResult.data ?? []) {
1488
+ orderedIds.push(item.id);
1489
+ }
1490
+ }
1491
+ if (orderedIds.length === 0) return { entries: [] };
1492
+ const items = await batchGetWithRetry(
1493
+ entity,
1494
+ orderedIds.map((id) => ({
1495
+ tenantId,
1496
+ workspaceId,
1497
+ id,
1498
+ sk: DATA_ENTITY_SK
1499
+ }))
1500
+ );
1501
+ const byId = new Map(items.map((item) => [item.id, item]));
1502
+ const entries = [];
1503
+ for (const id of orderedIds) {
1504
+ const item = byId.get(id);
1505
+ if (!item) continue;
1506
+ const parsed = JSON.parse(decompressResource(item.resource));
1507
+ entries.push({
1508
+ id,
1509
+ resource: { ...parsed, id }
1510
+ });
1511
+ }
1512
+ return { entries };
1513
+ }
1514
+ async function createDataEntityRecord(entity, tenantId, workspaceId, id, resourceWithAudit, fallbackDate) {
1515
+ const lastUpdated = resourceWithAudit.meta?.lastUpdated ?? fallbackDate ?? (/* @__PURE__ */ new Date()).toISOString();
1516
+ const vid = lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36);
1517
+ const resourceLike = resourceWithAudit;
1518
+ const summary = JSON.stringify((0, import_types.extractSummary)(resourceLike));
1519
+ const gsi1sk = (0, import_types.extractSortKey)(resourceLike);
1520
+ await entity.put({
1521
+ sk: DATA_ENTITY_SK,
1522
+ tenantId,
1523
+ workspaceId,
1524
+ id,
1525
+ resource: compressResource(JSON.stringify(resourceWithAudit)),
1526
+ summary,
1527
+ vid,
1528
+ lastUpdated,
1529
+ gsi1sk
1530
+ }).go();
1531
+ return {
1532
+ id,
1533
+ resource: resourceWithAudit
1534
+ };
1535
+ }
1536
+ function buildUpdatedResourceWithAudit(body, id, date, actorId, actorName, existingResourceStr, resourceType) {
1537
+ const existingMeta = JSON.parse(existingResourceStr).meta;
1538
+ const bodyWithMeta = body;
1539
+ const resourceWithVersion = {
1540
+ ...body,
1541
+ resourceType,
1542
+ id,
1543
+ meta: {
1544
+ ...bodyWithMeta.meta ?? {},
1545
+ lastUpdated: date,
1546
+ versionId: "2"
1547
+ }
1548
+ };
1549
+ const resourceWithAudit = {
1550
+ ...resourceWithVersion,
1551
+ meta: mergeAuditIntoMeta(resourceWithVersion.meta ?? existingMeta, {
1552
+ modifiedDate: date,
1553
+ modifiedById: actorId,
1554
+ modifiedByName: actorName
1555
+ })
1556
+ };
1557
+ return {
1558
+ resource: resourceWithAudit,
1559
+ lastUpdated: date
1560
+ };
1561
+ }
1562
+ async function updateDataEntityById(entity, tenantId, workspaceId, id, resourceLabel, context, buildPatched) {
1563
+ const existing = await entity.get({
1564
+ tenantId,
1565
+ workspaceId,
1566
+ id,
1567
+ sk: DATA_ENTITY_SK
1568
+ }).go();
1569
+ if (!existing.data) {
1570
+ throw new NotFoundError(`${resourceLabel} ${id} not found`, {
1571
+ details: { id }
1572
+ });
1573
+ }
1574
+ const existingStr = decompressResource(existing.data.resource);
1575
+ const { resource, lastUpdated } = buildPatched(existingStr);
1576
+ const resourceLike = resource;
1577
+ const summary = JSON.stringify((0, import_types.extractSummary)(resourceLike));
1578
+ const gsi1sk = (0, import_types.extractSortKey)(resourceLike);
1579
+ await entity.patch({
1580
+ tenantId,
1581
+ workspaceId,
1582
+ id,
1583
+ sk: DATA_ENTITY_SK
1584
+ }).set({
1585
+ resource: compressResource(JSON.stringify(resource)),
1586
+ summary,
1587
+ lastUpdated,
1588
+ gsi1sk
1589
+ }).go();
1590
+ return {
1591
+ id,
1592
+ resource
1593
+ };
1594
+ }
1595
+
1390
1596
  // src/data/operations/control/configuration/configuration-list-operation.ts
1597
+ var SK4 = "CURRENT";
1391
1598
  async function listConfigurationsOperation(params) {
1392
1599
  const { context, tableName } = params;
1393
1600
  const { tenantId, workspaceId } = context;
@@ -1398,9 +1605,41 @@ async function listConfigurationsOperation(params) {
1398
1605
  (_, shard) => service.entities.configuration.query.gsi1({ tenantId, workspaceId, gsi1Shard: String(shard) }).go()
1399
1606
  )
1400
1607
  );
1401
- const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
1608
+ const ordered = [];
1609
+ for (const shardResult of shardResults) {
1610
+ for (const item of shardResult.data ?? []) {
1611
+ if (typeof item.summary !== "string") continue;
1612
+ const parsed = JSON.parse(item.summary);
1613
+ if (typeof parsed.id !== "string" || typeof parsed.key !== "string" || typeof parsed.userId !== "string" || typeof parsed.roleId !== "string") {
1614
+ continue;
1615
+ }
1616
+ ordered.push({
1617
+ id: parsed.id,
1618
+ key: parsed.key,
1619
+ userId: parsed.userId,
1620
+ roleId: parsed.roleId
1621
+ });
1622
+ }
1623
+ }
1624
+ if (ordered.length === 0) return { entries: [] };
1625
+ const items = await batchGetWithRetry(
1626
+ service.entities.configuration,
1627
+ ordered.map(({ key, userId, roleId }) => ({
1628
+ tenantId,
1629
+ workspaceId,
1630
+ userId,
1631
+ roleId,
1632
+ key,
1633
+ sk: SK4
1634
+ }))
1635
+ );
1636
+ const byId = new Map(items.map((item) => [item.id, item]));
1637
+ const entries = [];
1638
+ for (const summary of ordered) {
1639
+ const item = byId.get(summary.id);
1640
+ if (!item) continue;
1402
1641
  const resource = JSON.parse(decompressResource(item.resource));
1403
- return {
1642
+ entries.push({
1404
1643
  id: item.id,
1405
1644
  key: item.key,
1406
1645
  resource: {
@@ -1409,8 +1648,8 @@ async function listConfigurationsOperation(params) {
1409
1648
  key: item.key,
1410
1649
  resource
1411
1650
  }
1412
- };
1413
- });
1651
+ });
1652
+ }
1414
1653
  return { entries };
1415
1654
  }
1416
1655
 
@@ -1564,13 +1803,13 @@ async function listConfigurationsRoute(req, res) {
1564
1803
  }
1565
1804
 
1566
1805
  // src/data/operations/control/configuration/configuration-update-operation.ts
1567
- var SK4 = "CURRENT";
1806
+ var SK5 = "CURRENT";
1568
1807
  async function updateConfigurationOperation(params) {
1569
1808
  const { context, id, body, tableName } = params;
1570
1809
  const { tenantId, workspaceId, actorId, date, roleId: ctxRoleId } = context;
1571
1810
  const roleId = ctxRoleId ?? "-";
1572
1811
  const service = getDynamoControlService(tableName);
1573
- const existing = await service.entities.configuration.get({ tenantId, workspaceId, userId: actorId, roleId, key: id, sk: SK4 }).go();
1812
+ const existing = await service.entities.configuration.get({ tenantId, workspaceId, userId: actorId, roleId, key: id, sk: SK5 }).go();
1574
1813
  if (!existing.data) {
1575
1814
  throw new NotFoundError(`Configuration ${id} not found`, {
1576
1815
  details: { id }
@@ -1583,9 +1822,11 @@ async function updateConfigurationOperation(params) {
1583
1822
  const summary = JSON.stringify({
1584
1823
  resourceType: "Configuration",
1585
1824
  id: existing.data.id,
1586
- key: existing.data.key
1825
+ key: existing.data.key,
1826
+ userId: actorId,
1827
+ roleId
1587
1828
  });
1588
- await service.entities.configuration.patch({ tenantId, workspaceId, userId: actorId, roleId, key: id, sk: SK4 }).set({
1829
+ await service.entities.configuration.patch({ tenantId, workspaceId, userId: actorId, roleId, key: id, sk: SK5 }).set({
1589
1830
  resource: compressResource(resourceStr),
1590
1831
  summary,
1591
1832
  lastUpdated,
@@ -1665,7 +1906,7 @@ router.delete("/:id", deleteConfigurationRoute);
1665
1906
  var import_express2 = __toESM(require("express"));
1666
1907
 
1667
1908
  // src/data/operations/control/membership/membership-create-operation.ts
1668
- var import_types = require("@openhi/types");
1909
+ var import_types2 = require("@openhi/types");
1669
1910
  async function createMembershipOperation(params) {
1670
1911
  const { context, body, tableName } = params;
1671
1912
  const service = getDynamoControlService(tableName);
@@ -1674,7 +1915,7 @@ async function createMembershipOperation(params) {
1674
1915
  const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
1675
1916
  const vid = `1`;
1676
1917
  const resource = { resourceType: "Membership", id, ...parsedResource };
1677
- const summary = JSON.stringify((0, import_types.extractSummary)(resource));
1918
+ const summary = JSON.stringify((0, import_types2.extractSummary)(resource));
1678
1919
  await service.entities.membership.put({
1679
1920
  tenantId: context.tenantId,
1680
1921
  id,
@@ -1790,26 +2031,39 @@ async function getMembershipByIdRoute(req, res) {
1790
2031
  }
1791
2032
 
1792
2033
  // src/data/operations/control/membership/membership-list-operation.ts
2034
+ var SK6 = "CURRENT";
1793
2035
  async function listMembershipsOperation(params) {
1794
2036
  const { context, tableName } = params;
2037
+ const tenantId = context.tenantId;
1795
2038
  const service = getDynamoControlService(tableName);
1796
2039
  const shardResults = await Promise.all(
1797
2040
  Array.from(
1798
2041
  { length: SHARD_COUNT },
1799
- (_, shard) => service.entities.membership.query.gsi1({ tenantId: context.tenantId, gsi1Shard: String(shard) }).go()
2042
+ (_, shard) => service.entities.membership.query.gsi1({ tenantId, gsi1Shard: String(shard) }).go()
1800
2043
  )
1801
2044
  );
1802
- const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
2045
+ const orderedIds = [];
2046
+ for (const shardResult of shardResults) {
2047
+ for (const item of shardResult.data ?? []) {
2048
+ orderedIds.push(item.id);
2049
+ }
2050
+ }
2051
+ if (orderedIds.length === 0) return { entries: [] };
2052
+ const items = await batchGetWithRetry(
2053
+ service.entities.membership,
2054
+ orderedIds.map((id) => ({ tenantId, id, sk: SK6 }))
2055
+ );
2056
+ const byId = new Map(items.map((item) => [item.id, item]));
2057
+ const entries = [];
2058
+ for (const id of orderedIds) {
2059
+ const item = byId.get(id);
2060
+ if (!item) continue;
1803
2061
  const parsedResource = JSON.parse(item.resource);
1804
- return {
1805
- id: item.id,
1806
- resource: {
1807
- resourceType: "Membership",
1808
- id: item.id,
1809
- ...parsedResource
1810
- }
1811
- };
1812
- });
2062
+ entries.push({
2063
+ id,
2064
+ resource: { resourceType: "Membership", id, ...parsedResource }
2065
+ });
2066
+ }
1813
2067
  return { entries };
1814
2068
  }
1815
2069
 
@@ -1836,7 +2090,7 @@ async function listMembershipsRoute(req, res) {
1836
2090
  }
1837
2091
 
1838
2092
  // src/data/operations/control/membership/membership-update-operation.ts
1839
- var import_types2 = require("@openhi/types");
2093
+ var import_types3 = require("@openhi/types");
1840
2094
  async function updateMembershipOperation(params) {
1841
2095
  const { context, id, body, tableName } = params;
1842
2096
  const service = getDynamoControlService(tableName);
@@ -1848,7 +2102,7 @@ async function updateMembershipOperation(params) {
1848
2102
  const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
1849
2103
  const vid = `${Date.now()}`;
1850
2104
  const resource = { resourceType: "Membership", id, ...parsedResource };
1851
- const summary = JSON.stringify((0, import_types2.extractSummary)(resource));
2105
+ const summary = JSON.stringify((0, import_types3.extractSummary)(resource));
1852
2106
  await service.entities.membership.put({
1853
2107
  tenantId: context.tenantId,
1854
2108
  id,
@@ -1916,7 +2170,7 @@ router2.delete("/:id", deleteMembershipRoute);
1916
2170
  var import_express3 = __toESM(require("express"));
1917
2171
 
1918
2172
  // src/data/operations/control/role/role-create-operation.ts
1919
- var import_types3 = require("@openhi/types");
2173
+ var import_types4 = require("@openhi/types");
1920
2174
  async function createRoleOperation(params) {
1921
2175
  const { context, body, tableName } = params;
1922
2176
  const service = getDynamoControlService(tableName);
@@ -1925,7 +2179,7 @@ async function createRoleOperation(params) {
1925
2179
  const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
1926
2180
  const vid = `1`;
1927
2181
  const resource = { resourceType: "Role", id, ...parsedResource };
1928
- const summary = JSON.stringify((0, import_types3.extractSummary)(resource));
2182
+ const summary = JSON.stringify((0, import_types4.extractSummary)(resource));
1929
2183
  await service.entities.role.put({
1930
2184
  id,
1931
2185
  resource: JSON.stringify(resource),
@@ -2040,6 +2294,7 @@ async function getRoleByIdRoute(req, res) {
2040
2294
  }
2041
2295
 
2042
2296
  // src/data/operations/control/role/role-list-operation.ts
2297
+ var SK7 = "CURRENT";
2043
2298
  async function listRolesOperation(params) {
2044
2299
  const { tableName } = params;
2045
2300
  const service = getDynamoControlService(tableName);
@@ -2049,13 +2304,28 @@ async function listRolesOperation(params) {
2049
2304
  (_, shard) => service.entities.role.query.gsi1({ gsi1Shard: String(shard) }).go()
2050
2305
  )
2051
2306
  );
2052
- const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
2307
+ const orderedIds = [];
2308
+ for (const shardResult of shardResults) {
2309
+ for (const item of shardResult.data ?? []) {
2310
+ orderedIds.push(item.id);
2311
+ }
2312
+ }
2313
+ if (orderedIds.length === 0) return { entries: [] };
2314
+ const items = await batchGetWithRetry(
2315
+ service.entities.role,
2316
+ orderedIds.map((id) => ({ id, sk: SK7 }))
2317
+ );
2318
+ const byId = new Map(items.map((item) => [item.id, item]));
2319
+ const entries = [];
2320
+ for (const id of orderedIds) {
2321
+ const item = byId.get(id);
2322
+ if (!item) continue;
2053
2323
  const parsedResource = JSON.parse(item.resource);
2054
- return {
2055
- id: item.id,
2056
- resource: { resourceType: "Role", id: item.id, ...parsedResource }
2057
- };
2058
- });
2324
+ entries.push({
2325
+ id,
2326
+ resource: { resourceType: "Role", id, ...parsedResource }
2327
+ });
2328
+ }
2059
2329
  return { entries };
2060
2330
  }
2061
2331
 
@@ -2082,7 +2352,7 @@ async function listRolesRoute(req, res) {
2082
2352
  }
2083
2353
 
2084
2354
  // src/data/operations/control/role/role-update-operation.ts
2085
- var import_types4 = require("@openhi/types");
2355
+ var import_types5 = require("@openhi/types");
2086
2356
  async function updateRoleOperation(params) {
2087
2357
  const { context, id, body, tableName } = params;
2088
2358
  const service = getDynamoControlService(tableName);
@@ -2094,7 +2364,7 @@ async function updateRoleOperation(params) {
2094
2364
  const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
2095
2365
  const vid = `${Date.now()}`;
2096
2366
  const resource = { resourceType: "Role", id, ...parsedResource };
2097
- const summary = JSON.stringify((0, import_types4.extractSummary)(resource));
2367
+ const summary = JSON.stringify((0, import_types5.extractSummary)(resource));
2098
2368
  await service.entities.role.put({
2099
2369
  id,
2100
2370
  resource: JSON.stringify(resource),
@@ -2161,7 +2431,7 @@ router3.delete("/:id", deleteRoleRoute);
2161
2431
  var import_express4 = __toESM(require("express"));
2162
2432
 
2163
2433
  // src/data/operations/control/roleassignment/roleassignment-create-operation.ts
2164
- var import_types5 = require("@openhi/types");
2434
+ var import_types6 = require("@openhi/types");
2165
2435
  async function createRoleAssignmentOperation(params) {
2166
2436
  const { context, body, tableName } = params;
2167
2437
  const service = getDynamoControlService(tableName);
@@ -2170,7 +2440,7 @@ async function createRoleAssignmentOperation(params) {
2170
2440
  const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
2171
2441
  const vid = `1`;
2172
2442
  const resource = { resourceType: "RoleAssignment", id, ...parsedResource };
2173
- const summary = JSON.stringify((0, import_types5.extractSummary)(resource));
2443
+ const summary = JSON.stringify((0, import_types6.extractSummary)(resource));
2174
2444
  await service.entities.roleAssignment.put({
2175
2445
  tenantId: context.tenantId,
2176
2446
  id,
@@ -2286,26 +2556,39 @@ async function getRoleAssignmentByIdRoute(req, res) {
2286
2556
  }
2287
2557
 
2288
2558
  // src/data/operations/control/roleassignment/roleassignment-list-operation.ts
2559
+ var SK8 = "CURRENT";
2289
2560
  async function listRoleAssignmentsOperation(params) {
2290
2561
  const { context, tableName } = params;
2562
+ const tenantId = context.tenantId;
2291
2563
  const service = getDynamoControlService(tableName);
2292
2564
  const shardResults = await Promise.all(
2293
2565
  Array.from(
2294
2566
  { length: SHARD_COUNT },
2295
- (_, shard) => service.entities.roleAssignment.query.gsi1({ tenantId: context.tenantId, gsi1Shard: String(shard) }).go()
2567
+ (_, shard) => service.entities.roleAssignment.query.gsi1({ tenantId, gsi1Shard: String(shard) }).go()
2296
2568
  )
2297
2569
  );
2298
- const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
2570
+ const orderedIds = [];
2571
+ for (const shardResult of shardResults) {
2572
+ for (const item of shardResult.data ?? []) {
2573
+ orderedIds.push(item.id);
2574
+ }
2575
+ }
2576
+ if (orderedIds.length === 0) return { entries: [] };
2577
+ const items = await batchGetWithRetry(
2578
+ service.entities.roleAssignment,
2579
+ orderedIds.map((id) => ({ tenantId, id, sk: SK8 }))
2580
+ );
2581
+ const byId = new Map(items.map((item) => [item.id, item]));
2582
+ const entries = [];
2583
+ for (const id of orderedIds) {
2584
+ const item = byId.get(id);
2585
+ if (!item) continue;
2299
2586
  const parsedResource = JSON.parse(item.resource);
2300
- return {
2301
- id: item.id,
2302
- resource: {
2303
- resourceType: "RoleAssignment",
2304
- id: item.id,
2305
- ...parsedResource
2306
- }
2307
- };
2308
- });
2587
+ entries.push({
2588
+ id,
2589
+ resource: { resourceType: "RoleAssignment", id, ...parsedResource }
2590
+ });
2591
+ }
2309
2592
  return { entries };
2310
2593
  }
2311
2594
 
@@ -2332,7 +2615,7 @@ async function listRoleAssignmentsRoute(req, res) {
2332
2615
  }
2333
2616
 
2334
2617
  // src/data/operations/control/roleassignment/roleassignment-update-operation.ts
2335
- var import_types6 = require("@openhi/types");
2618
+ var import_types7 = require("@openhi/types");
2336
2619
  async function updateRoleAssignmentOperation(params) {
2337
2620
  const { context, id, body, tableName } = params;
2338
2621
  const service = getDynamoControlService(tableName);
@@ -2344,7 +2627,7 @@ async function updateRoleAssignmentOperation(params) {
2344
2627
  const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
2345
2628
  const vid = `${Date.now()}`;
2346
2629
  const resource = { resourceType: "RoleAssignment", id, ...parsedResource };
2347
- const summary = JSON.stringify((0, import_types6.extractSummary)(resource));
2630
+ const summary = JSON.stringify((0, import_types7.extractSummary)(resource));
2348
2631
  await service.entities.roleAssignment.put({
2349
2632
  tenantId: context.tenantId,
2350
2633
  id,
@@ -2412,7 +2695,7 @@ router4.delete("/:id", deleteRoleAssignmentRoute);
2412
2695
  var import_express5 = __toESM(require("express"));
2413
2696
 
2414
2697
  // src/data/operations/control/tenant/tenant-create-operation.ts
2415
- var import_types7 = require("@openhi/types");
2698
+ var import_types8 = require("@openhi/types");
2416
2699
  async function createTenantOperation(params) {
2417
2700
  const { context, body, tableName } = params;
2418
2701
  const service = getDynamoControlService(tableName);
@@ -2421,7 +2704,7 @@ async function createTenantOperation(params) {
2421
2704
  const vid = lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36);
2422
2705
  const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
2423
2706
  const resource = { resourceType: "Tenant", id, ...parsedResource };
2424
- const summary = JSON.stringify((0, import_types7.extractSummary)(resource));
2707
+ const summary = JSON.stringify((0, import_types8.extractSummary)(resource));
2425
2708
  await service.entities.tenant.put({
2426
2709
  tenantId: id,
2427
2710
  id,
@@ -2532,6 +2815,7 @@ async function getTenantByIdRoute(req, res) {
2532
2815
  }
2533
2816
 
2534
2817
  // src/data/operations/control/tenant/tenant-list-operation.ts
2818
+ var SK9 = "CURRENT";
2535
2819
  async function listTenantsOperation(params) {
2536
2820
  const { tableName } = params;
2537
2821
  const service = getDynamoControlService(tableName);
@@ -2541,13 +2825,28 @@ async function listTenantsOperation(params) {
2541
2825
  (_, shard) => service.entities.tenant.query.gsi1({ gsi1Shard: String(shard) }).go()
2542
2826
  )
2543
2827
  );
2544
- const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
2828
+ const orderedIds = [];
2829
+ for (const shardResult of shardResults) {
2830
+ for (const item of shardResult.data ?? []) {
2831
+ orderedIds.push(item.id);
2832
+ }
2833
+ }
2834
+ if (orderedIds.length === 0) return { entries: [] };
2835
+ const items = await batchGetWithRetry(
2836
+ service.entities.tenant,
2837
+ orderedIds.map((id) => ({ tenantId: id, sk: SK9 }))
2838
+ );
2839
+ const byId = new Map(items.map((item) => [item.id, item]));
2840
+ const entries = [];
2841
+ for (const id of orderedIds) {
2842
+ const item = byId.get(id);
2843
+ if (!item) continue;
2545
2844
  const parsed = JSON.parse(item.resource);
2546
- return {
2547
- id: item.id,
2548
- resource: { resourceType: "Tenant", id: item.id, ...parsed }
2549
- };
2550
- });
2845
+ entries.push({
2846
+ id,
2847
+ resource: { resourceType: "Tenant", id, ...parsed }
2848
+ });
2849
+ }
2551
2850
  return { entries };
2552
2851
  }
2553
2852
 
@@ -2574,7 +2873,7 @@ async function listTenantsRoute(req, res) {
2574
2873
  }
2575
2874
 
2576
2875
  // src/data/operations/control/tenant/tenant-update-operation.ts
2577
- var import_types8 = require("@openhi/types");
2876
+ var import_types9 = require("@openhi/types");
2578
2877
  async function updateTenantOperation(params) {
2579
2878
  const { context, id, body, tableName } = params;
2580
2879
  const service = getDynamoControlService(tableName);
@@ -2592,7 +2891,7 @@ async function updateTenantOperation(params) {
2592
2891
  resourceType: "Tenant",
2593
2892
  id
2594
2893
  };
2595
- const summary = JSON.stringify((0, import_types8.extractSummary)(updated));
2894
+ const summary = JSON.stringify((0, import_types9.extractSummary)(updated));
2596
2895
  await service.entities.tenant.patch({ tenantId: id, sk: "CURRENT" }).set({ resource: JSON.stringify(updated), summary, vid, lastUpdated }).go();
2597
2896
  return { id, resource: updated, meta: { lastUpdated, versionId: vid } };
2598
2897
  }
@@ -2649,7 +2948,7 @@ router5.delete("/:id", deleteTenantRoute);
2649
2948
  var import_express6 = __toESM(require("express"));
2650
2949
 
2651
2950
  // src/data/operations/control/user/user-create-operation.ts
2652
- var import_types9 = require("@openhi/types");
2951
+ var import_types10 = require("@openhi/types");
2653
2952
  async function createUserOperation(params) {
2654
2953
  const { context, body, tableName } = params;
2655
2954
  const service = getDynamoControlService(tableName);
@@ -2658,7 +2957,7 @@ async function createUserOperation(params) {
2658
2957
  const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
2659
2958
  const vid = `1`;
2660
2959
  const resource = { resourceType: "User", id, ...parsedResource };
2661
- const summary = JSON.stringify((0, import_types9.extractSummary)(resource));
2960
+ const summary = JSON.stringify((0, import_types10.extractSummary)(resource));
2662
2961
  await service.entities.user.put({
2663
2962
  id,
2664
2963
  resource: JSON.stringify(resource),
@@ -2773,6 +3072,7 @@ async function getUserByIdRoute(req, res) {
2773
3072
  }
2774
3073
 
2775
3074
  // src/data/operations/control/user/user-list-operation.ts
3075
+ var SK10 = "CURRENT";
2776
3076
  async function listUsersOperation(params) {
2777
3077
  const { tableName } = params;
2778
3078
  const service = getDynamoControlService(tableName);
@@ -2782,13 +3082,28 @@ async function listUsersOperation(params) {
2782
3082
  (_, shard) => service.entities.user.query.gsi1({ gsi1Shard: String(shard) }).go()
2783
3083
  )
2784
3084
  );
2785
- const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
3085
+ const orderedIds = [];
3086
+ for (const shardResult of shardResults) {
3087
+ for (const item of shardResult.data ?? []) {
3088
+ orderedIds.push(item.id);
3089
+ }
3090
+ }
3091
+ if (orderedIds.length === 0) return { entries: [] };
3092
+ const items = await batchGetWithRetry(
3093
+ service.entities.user,
3094
+ orderedIds.map((id) => ({ id, sk: SK10 }))
3095
+ );
3096
+ const byId = new Map(items.map((item) => [item.id, item]));
3097
+ const entries = [];
3098
+ for (const id of orderedIds) {
3099
+ const item = byId.get(id);
3100
+ if (!item) continue;
2786
3101
  const parsedResource = JSON.parse(item.resource);
2787
- return {
2788
- id: item.id,
2789
- resource: { resourceType: "User", id: item.id, ...parsedResource }
2790
- };
2791
- });
3102
+ entries.push({
3103
+ id,
3104
+ resource: { resourceType: "User", id, ...parsedResource }
3105
+ });
3106
+ }
2792
3107
  return { entries };
2793
3108
  }
2794
3109
 
@@ -2815,7 +3130,7 @@ async function listUsersRoute(req, res) {
2815
3130
  }
2816
3131
 
2817
3132
  // src/data/operations/control/user/user-update-operation.ts
2818
- var import_types10 = require("@openhi/types");
3133
+ var import_types11 = require("@openhi/types");
2819
3134
  async function updateUserOperation(params) {
2820
3135
  const { context, id, body, tableName } = params;
2821
3136
  const service = getDynamoControlService(tableName);
@@ -2827,7 +3142,7 @@ async function updateUserOperation(params) {
2827
3142
  const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
2828
3143
  const vid = `${Date.now()}`;
2829
3144
  const resource = { resourceType: "User", id, ...parsedResource };
2830
- const summary = JSON.stringify((0, import_types10.extractSummary)(resource));
3145
+ const summary = JSON.stringify((0, import_types11.extractSummary)(resource));
2831
3146
  await service.entities.user.put({
2832
3147
  id,
2833
3148
  resource: JSON.stringify(resource),
@@ -2894,7 +3209,7 @@ router6.delete("/:id", deleteUserRoute);
2894
3209
  var import_express7 = __toESM(require("express"));
2895
3210
 
2896
3211
  // src/data/operations/control/workspace/workspace-create-operation.ts
2897
- var import_types11 = require("@openhi/types");
3212
+ var import_types12 = require("@openhi/types");
2898
3213
  async function createWorkspaceOperation(params) {
2899
3214
  const { context, body, tableName } = params;
2900
3215
  const { tenantId } = context;
@@ -2904,7 +3219,7 @@ async function createWorkspaceOperation(params) {
2904
3219
  const vid = lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36);
2905
3220
  const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
2906
3221
  const resource = { resourceType: "Workspace", id, ...parsedResource };
2907
- const summary = JSON.stringify((0, import_types11.extractSummary)(resource));
3222
+ const summary = JSON.stringify((0, import_types12.extractSummary)(resource));
2908
3223
  await service.entities.workspace.put({
2909
3224
  tenantId,
2910
3225
  id,
@@ -3017,6 +3332,7 @@ async function getWorkspaceByIdRoute(req, res) {
3017
3332
  }
3018
3333
 
3019
3334
  // src/data/operations/control/workspace/workspace-list-operation.ts
3335
+ var SK11 = "CURRENT";
3020
3336
  async function listWorkspacesOperation(params) {
3021
3337
  const { context, tableName } = params;
3022
3338
  const { tenantId } = context;
@@ -3027,13 +3343,28 @@ async function listWorkspacesOperation(params) {
3027
3343
  (_, shard) => service.entities.workspace.query.gsi1({ tenantId, gsi1Shard: String(shard) }).go()
3028
3344
  )
3029
3345
  );
3030
- const entries = shardResults.flatMap((shardResult) => shardResult.data ?? []).map((item) => {
3346
+ const orderedIds = [];
3347
+ for (const shardResult of shardResults) {
3348
+ for (const item of shardResult.data ?? []) {
3349
+ orderedIds.push(item.id);
3350
+ }
3351
+ }
3352
+ if (orderedIds.length === 0) return { entries: [] };
3353
+ const items = await batchGetWithRetry(
3354
+ service.entities.workspace,
3355
+ orderedIds.map((id) => ({ tenantId, id, sk: SK11 }))
3356
+ );
3357
+ const byId = new Map(items.map((item) => [item.id, item]));
3358
+ const entries = [];
3359
+ for (const id of orderedIds) {
3360
+ const item = byId.get(id);
3361
+ if (!item) continue;
3031
3362
  const parsed = JSON.parse(item.resource);
3032
- return {
3033
- id: item.id,
3034
- resource: { resourceType: "Workspace", id: item.id, ...parsed }
3035
- };
3036
- });
3363
+ entries.push({
3364
+ id,
3365
+ resource: { resourceType: "Workspace", id, ...parsed }
3366
+ });
3367
+ }
3037
3368
  return { entries };
3038
3369
  }
3039
3370
 
@@ -3060,7 +3391,7 @@ async function listWorkspacesRoute(req, res) {
3060
3391
  }
3061
3392
 
3062
3393
  // src/data/operations/control/workspace/workspace-update-operation.ts
3063
- var import_types12 = require("@openhi/types");
3394
+ var import_types13 = require("@openhi/types");
3064
3395
  async function updateWorkspaceOperation(params) {
3065
3396
  const { context, id, body, tableName } = params;
3066
3397
  const { tenantId } = context;
@@ -3079,7 +3410,7 @@ async function updateWorkspaceOperation(params) {
3079
3410
  resourceType: "Workspace",
3080
3411
  id
3081
3412
  };
3082
- const summary = JSON.stringify((0, import_types12.extractSummary)(updated));
3413
+ const summary = JSON.stringify((0, import_types13.extractSummary)(updated));
3083
3414
  await service.entities.workspace.patch({ tenantId, id, sk: "CURRENT" }).set({ resource: JSON.stringify(updated), summary, vid, lastUpdated }).go();
3084
3415
  return { id, resource: updated, meta: { lastUpdated, versionId: vid } };
3085
3416
  }
@@ -3138,30 +3469,6 @@ var import_express8 = __toESM(require("express"));
3138
3469
  // src/data/operations/data/account/account-create-operation.ts
3139
3470
  var import_ulid = require("ulid");
3140
3471
 
3141
- // src/data/audit-meta.ts
3142
- var OPENHI_EXT = "http://openhi.org/fhir/StructureDefinition";
3143
- function mergeAuditIntoMeta(meta, audit) {
3144
- const existing = meta ?? {};
3145
- const ext = [
3146
- ...Array.isArray(existing.extension) ? existing.extension : []
3147
- ];
3148
- const byUrl = new Map(ext.map((e) => [e.url, e]));
3149
- function set(url, value, type) {
3150
- if (value == null) return;
3151
- byUrl.set(url, { url, [type]: value });
3152
- }
3153
- set(`${OPENHI_EXT}/created-date`, audit.createdDate, "valueDateTime");
3154
- set(`${OPENHI_EXT}/created-by-id`, audit.createdById, "valueString");
3155
- set(`${OPENHI_EXT}/created-by-name`, audit.createdByName, "valueString");
3156
- set(`${OPENHI_EXT}/modified-date`, audit.modifiedDate, "valueDateTime");
3157
- set(`${OPENHI_EXT}/modified-by-id`, audit.modifiedById, "valueString");
3158
- set(`${OPENHI_EXT}/modified-by-name`, audit.modifiedByName, "valueString");
3159
- set(`${OPENHI_EXT}/deleted-date`, audit.deletedDate, "valueDateTime");
3160
- set(`${OPENHI_EXT}/deleted-by-id`, audit.deletedById, "valueString");
3161
- set(`${OPENHI_EXT}/deleted-by-name`, audit.deletedByName, "valueString");
3162
- return { ...existing, extension: Array.from(byUrl.values()) };
3163
- }
3164
-
3165
3472
  // src/data/dynamo/dynamo-data-service.ts
3166
3473
  var import_electrodb10 = require("electrodb");
3167
3474
 
@@ -4194,136 +4501,6 @@ function getDynamoDataService(tableName) {
4194
4501
  };
4195
4502
  }
4196
4503
 
4197
- // src/data/operations/data-operations-common.ts
4198
- var import_types13 = require("@openhi/types");
4199
- var DATA_ENTITY_SK = "CURRENT";
4200
- async function getDataEntityById(entity, tenantId, workspaceId, id, resourceLabel) {
4201
- const result = await entity.get({
4202
- tenantId,
4203
- workspaceId,
4204
- id,
4205
- sk: DATA_ENTITY_SK
4206
- }).go();
4207
- if (!result.data) {
4208
- throw new NotFoundError(`${resourceLabel} ${id} not found`, {
4209
- details: { id }
4210
- });
4211
- }
4212
- const parsed = JSON.parse(decompressResource(result.data.resource));
4213
- return {
4214
- id: result.data.id,
4215
- resource: { ...parsed, id: result.data.id }
4216
- };
4217
- }
4218
- async function deleteDataEntityById(entity, tenantId, workspaceId, id) {
4219
- await entity.delete({
4220
- tenantId,
4221
- workspaceId,
4222
- id,
4223
- sk: DATA_ENTITY_SK
4224
- }).go();
4225
- }
4226
- async function listDataEntitiesByWorkspace(entity, tenantId, workspaceId) {
4227
- const shardResults = await Promise.all(
4228
- Array.from(
4229
- { length: SHARD_COUNT },
4230
- (_, shard) => entity.query.gsi1({ tenantId, workspaceId, gsi1Shard: String(shard) }).go()
4231
- )
4232
- );
4233
- const entries = [];
4234
- for (const shardResult of shardResults) {
4235
- for (const item of shardResult.data ?? []) {
4236
- const parsed = JSON.parse(decompressResource(item.resource));
4237
- entries.push({
4238
- id: item.id,
4239
- resource: { ...parsed, id: item.id }
4240
- });
4241
- }
4242
- }
4243
- return { entries };
4244
- }
4245
- async function createDataEntityRecord(entity, tenantId, workspaceId, id, resourceWithAudit, fallbackDate) {
4246
- const lastUpdated = resourceWithAudit.meta?.lastUpdated ?? fallbackDate ?? (/* @__PURE__ */ new Date()).toISOString();
4247
- const vid = lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36);
4248
- const resourceLike = resourceWithAudit;
4249
- const summary = JSON.stringify((0, import_types13.extractSummary)(resourceLike));
4250
- const gsi1sk = (0, import_types13.extractSortKey)(resourceLike);
4251
- await entity.put({
4252
- sk: DATA_ENTITY_SK,
4253
- tenantId,
4254
- workspaceId,
4255
- id,
4256
- resource: compressResource(JSON.stringify(resourceWithAudit)),
4257
- summary,
4258
- vid,
4259
- lastUpdated,
4260
- gsi1sk
4261
- }).go();
4262
- return {
4263
- id,
4264
- resource: resourceWithAudit
4265
- };
4266
- }
4267
- function buildUpdatedResourceWithAudit(body, id, date, actorId, actorName, existingResourceStr, resourceType) {
4268
- const existingMeta = JSON.parse(existingResourceStr).meta;
4269
- const bodyWithMeta = body;
4270
- const resourceWithVersion = {
4271
- ...body,
4272
- resourceType,
4273
- id,
4274
- meta: {
4275
- ...bodyWithMeta.meta ?? {},
4276
- lastUpdated: date,
4277
- versionId: "2"
4278
- }
4279
- };
4280
- const resourceWithAudit = {
4281
- ...resourceWithVersion,
4282
- meta: mergeAuditIntoMeta(resourceWithVersion.meta ?? existingMeta, {
4283
- modifiedDate: date,
4284
- modifiedById: actorId,
4285
- modifiedByName: actorName
4286
- })
4287
- };
4288
- return {
4289
- resource: resourceWithAudit,
4290
- lastUpdated: date
4291
- };
4292
- }
4293
- async function updateDataEntityById(entity, tenantId, workspaceId, id, resourceLabel, context, buildPatched) {
4294
- const existing = await entity.get({
4295
- tenantId,
4296
- workspaceId,
4297
- id,
4298
- sk: DATA_ENTITY_SK
4299
- }).go();
4300
- if (!existing.data) {
4301
- throw new NotFoundError(`${resourceLabel} ${id} not found`, {
4302
- details: { id }
4303
- });
4304
- }
4305
- const existingStr = decompressResource(existing.data.resource);
4306
- const { resource, lastUpdated } = buildPatched(existingStr);
4307
- const resourceLike = resource;
4308
- const summary = JSON.stringify((0, import_types13.extractSummary)(resourceLike));
4309
- const gsi1sk = (0, import_types13.extractSortKey)(resourceLike);
4310
- await entity.patch({
4311
- tenantId,
4312
- workspaceId,
4313
- id,
4314
- sk: DATA_ENTITY_SK
4315
- }).set({
4316
- resource: compressResource(JSON.stringify(resource)),
4317
- summary,
4318
- lastUpdated,
4319
- gsi1sk
4320
- }).go();
4321
- return {
4322
- id,
4323
- resource
4324
- };
4325
- }
4326
-
4327
4504
  // src/data/operations/data/account/account-create-operation.ts
4328
4505
  async function createAccountOperation(params) {
4329
4506
  const { context, body, tableName } = params;
@@ -25058,7 +25235,7 @@ function extractPatient(parsed) {
25058
25235
  "File must be a FHIR Patient resource or a Bundle containing at least one Patient entry"
25059
25236
  );
25060
25237
  }
25061
- var SK5 = "CURRENT";
25238
+ var SK12 = "CURRENT";
25062
25239
  var defaultAudit = {
25063
25240
  createdDate: (/* @__PURE__ */ new Date()).toISOString(),
25064
25241
  createdById: "import",
@@ -25096,7 +25273,7 @@ function patientToPutAttrs(patient, options) {
25096
25273
  patientWithMeta.meta.lastUpdated = lastUpdated;
25097
25274
  }
25098
25275
  return {
25099
- sk: SK5,
25276
+ sk: SK12,
25100
25277
  tenantId,
25101
25278
  workspaceId,
25102
25279
  id: patient.id,