@openhi/constructs 0.0.105 → 0.0.107

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 (73) hide show
  1. package/lib/chunk-36UPY7YQ.mjs +529 -0
  2. package/lib/chunk-36UPY7YQ.mjs.map +1 -0
  3. package/lib/chunk-AGF3RAAZ.mjs +20 -0
  4. package/lib/chunk-AGF3RAAZ.mjs.map +1 -0
  5. package/lib/{chunk-BXEG7IOZ.mjs → chunk-AO3E22CS.mjs} +2 -2
  6. package/lib/{chunk-WNUH2WDZ.mjs → chunk-CHPEQRXU.mjs} +2 -2
  7. package/lib/chunk-JUNL76HF.mjs +428 -0
  8. package/lib/chunk-JUNL76HF.mjs.map +1 -0
  9. package/lib/chunk-L6UAP4KP.mjs +27 -0
  10. package/lib/chunk-L6UAP4KP.mjs.map +1 -0
  11. package/lib/{chunk-3QS3WKRC.mjs → chunk-LZOMFHX3.mjs} +9 -2
  12. package/lib/chunk-SYBADQXI.mjs +607 -0
  13. package/lib/chunk-SYBADQXI.mjs.map +1 -0
  14. package/lib/chunk-VXX4I3EF.mjs +19 -0
  15. package/lib/chunk-VXX4I3EF.mjs.map +1 -0
  16. package/lib/{chunk-36YCDLLA.mjs → chunk-VYDIGFIX.mjs} +75 -481
  17. package/lib/chunk-VYDIGFIX.mjs.map +1 -0
  18. package/lib/chunk-YU2HRNUP.mjs +33 -0
  19. package/lib/chunk-YU2HRNUP.mjs.map +1 -0
  20. package/lib/chunk-YZZDUJHI.mjs +37 -0
  21. package/lib/chunk-YZZDUJHI.mjs.map +1 -0
  22. package/lib/cors-options-lambda.handler.mjs +1 -1
  23. package/lib/data-store-postgres-replication.handler.mjs +1 -1
  24. package/lib/events-BfrkMoBD.d.mts +44 -0
  25. package/lib/events-BfrkMoBD.d.ts +44 -0
  26. package/lib/events-DPodvl07.d.mts +207 -0
  27. package/lib/events-DPodvl07.d.ts +207 -0
  28. package/lib/firehose-archive-transform.handler.mjs +1 -1
  29. package/lib/index.d.mts +417 -9
  30. package/lib/index.d.ts +663 -10
  31. package/lib/index.js +2398 -111
  32. package/lib/index.js.map +1 -1
  33. package/lib/index.mjs +779 -104
  34. package/lib/index.mjs.map +1 -1
  35. package/lib/openhi-context-CaBH8SFo.d.mts +39 -0
  36. package/lib/openhi-context-CaBH8SFo.d.ts +39 -0
  37. package/lib/platform-deploy-bridge.handler.d.mts +14 -0
  38. package/lib/platform-deploy-bridge.handler.d.ts +14 -0
  39. package/lib/platform-deploy-bridge.handler.js +762 -0
  40. package/lib/platform-deploy-bridge.handler.js.map +1 -0
  41. package/lib/platform-deploy-bridge.handler.mjs +134 -0
  42. package/lib/platform-deploy-bridge.handler.mjs.map +1 -0
  43. package/lib/post-authentication.handler.mjs +1 -1
  44. package/lib/post-confirmation.handler.mjs +1 -1
  45. package/lib/pre-token-generation.handler.js +76 -31
  46. package/lib/pre-token-generation.handler.js.map +1 -1
  47. package/lib/pre-token-generation.handler.mjs +5 -3
  48. package/lib/pre-token-generation.handler.mjs.map +1 -1
  49. package/lib/provision-default-workspace.handler.js +86 -41
  50. package/lib/provision-default-workspace.handler.js.map +1 -1
  51. package/lib/provision-default-workspace.handler.mjs +6 -4
  52. package/lib/provision-default-workspace.handler.mjs.map +1 -1
  53. package/lib/rest-api-lambda.handler.js +114 -59
  54. package/lib/rest-api-lambda.handler.js.map +1 -1
  55. package/lib/rest-api-lambda.handler.mjs +40 -61
  56. package/lib/rest-api-lambda.handler.mjs.map +1 -1
  57. package/lib/seed-demo-data.handler.d.mts +107 -0
  58. package/lib/seed-demo-data.handler.d.ts +107 -0
  59. package/lib/seed-demo-data.handler.js +2037 -0
  60. package/lib/seed-demo-data.handler.js.map +1 -0
  61. package/lib/seed-demo-data.handler.mjs +23 -0
  62. package/lib/seed-demo-data.handler.mjs.map +1 -0
  63. package/lib/seed-system-data.handler.d.mts +64 -0
  64. package/lib/seed-system-data.handler.d.ts +64 -0
  65. package/lib/seed-system-data.handler.js +1631 -0
  66. package/lib/seed-system-data.handler.js.map +1 -0
  67. package/lib/seed-system-data.handler.mjs +135 -0
  68. package/lib/seed-system-data.handler.mjs.map +1 -0
  69. package/package.json +4 -2
  70. package/lib/chunk-36YCDLLA.mjs.map +0 -1
  71. /package/lib/{chunk-BXEG7IOZ.mjs.map → chunk-AO3E22CS.mjs.map} +0 -0
  72. /package/lib/{chunk-WNUH2WDZ.mjs.map → chunk-CHPEQRXU.mjs.map} +0 -0
  73. /package/lib/{chunk-3QS3WKRC.mjs.map → chunk-LZOMFHX3.mjs.map} +0 -0
@@ -15,6 +15,9 @@ var dynamoClient = new DynamoDBClient({
15
15
  // src/data/dynamo/entities/control/configuration-entity.ts
16
16
  import { Entity } from "electrodb";
17
17
 
18
+ // src/data/dynamo/entities/control/control-entity-common.ts
19
+ import { extractLabel } from "@openhi/types";
20
+
18
21
  // src/data/dynamo/shard.ts
19
22
  var SHARD_COUNT = 4;
20
23
  function computeShard(id) {
@@ -37,6 +40,29 @@ var gsi1ShardAttribute = {
37
40
  return String(computeShard(item.id));
38
41
  }
39
42
  };
43
+ var gsi1skAttribute = {
44
+ type: "string",
45
+ watch: ["resource", "lastUpdated", "id"],
46
+ set: (_val, item) => {
47
+ const id = typeof item?.id === "string" ? item.id : "";
48
+ const lastUpdated = typeof item?.lastUpdated === "string" ? item.lastUpdated : "";
49
+ const fallback = `${lastUpdated}#${id}`;
50
+ if (typeof item?.resource !== "string" || item.resource.length === 0) {
51
+ return fallback;
52
+ }
53
+ let parsed;
54
+ try {
55
+ parsed = JSON.parse(item.resource);
56
+ } catch {
57
+ return fallback;
58
+ }
59
+ if (!parsed || typeof parsed !== "object") return fallback;
60
+ const resourceType = parsed.resourceType;
61
+ if (typeof resourceType !== "string") return fallback;
62
+ const label = extractLabel(parsed);
63
+ return label !== void 0 ? `${label}#${id}` : fallback;
64
+ }
65
+ };
40
66
 
41
67
  // src/data/dynamo/entities/control/configuration-entity.ts
42
68
  var ConfigurationEntity = new Entity({
@@ -142,8 +168,9 @@ var ConfigurationEntity = new Entity({
142
168
  * (workspaceId = "-") or "list configs scoped to this workspace". Does not support
143
169
  * hierarchical resolution in one query; use base table GetItem in fallback order
144
170
  * (user → workspace → tenant → baseline) for that.
145
- * SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
146
- * `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
171
+ * SK is `<key>#<id>` Configuration's `key` is a required entity attribute (the
172
+ * config category: endpoints, branding, display, …) and the natural sort/lookup
173
+ * dimension. `casing: "none"` preserves the literal key value.
147
174
  */
148
175
  gsi1: {
149
176
  index: "GSI1",
@@ -155,8 +182,8 @@ var ConfigurationEntity = new Entity({
155
182
  sk: {
156
183
  field: "GSI1SK",
157
184
  casing: "none",
158
- composite: ["lastUpdated", "id"],
159
- template: "${lastUpdated}#${id}"
185
+ composite: ["key", "id"],
186
+ template: "${key}#${id}"
160
187
  }
161
188
  }
162
189
  }
@@ -210,6 +237,8 @@ var MembershipEntity = new Entity2({
210
237
  required: true
211
238
  },
212
239
  gsi1Shard: gsi1ShardAttribute,
240
+ /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
241
+ gsi1sk: gsi1skAttribute,
213
242
  deleted: {
214
243
  type: "boolean",
215
244
  required: false
@@ -240,8 +269,9 @@ var MembershipEntity = new Entity2({
240
269
  /**
241
270
  * GSI1 — Unified Sharded List per ADR-011: list all Memberships for a tenant across the
242
271
  * four shards. Membership is tenant-scoped only, so `WID#-` is a sentinel.
243
- * SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
244
- * `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
272
+ * SK is derived via `gsi1skAttribute` uses the resource's natural label when
273
+ * extractable, else `<lastUpdated>#<id>` (DR-004). `casing: "none"` preserves the
274
+ * normalized label and ISO-8601 `T`/`Z`.
245
275
  */
246
276
  gsi1: {
247
277
  index: "GSI1",
@@ -253,8 +283,8 @@ var MembershipEntity = new Entity2({
253
283
  sk: {
254
284
  field: "GSI1SK",
255
285
  casing: "none",
256
- composite: ["lastUpdated", "id"],
257
- template: "${lastUpdated}#${id}"
286
+ composite: ["gsi1sk"],
287
+ template: "${gsi1sk}"
258
288
  }
259
289
  }
260
290
  }
@@ -303,6 +333,8 @@ var RoleEntity = new Entity3({
303
333
  required: true
304
334
  },
305
335
  gsi1Shard: gsi1ShardAttribute,
336
+ /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
337
+ gsi1sk: gsi1skAttribute,
306
338
  deleted: {
307
339
  type: "boolean",
308
340
  required: false
@@ -333,8 +365,9 @@ var RoleEntity = new Entity3({
333
365
  /**
334
366
  * GSI1 — Unified Sharded List per ADR-011: list all Roles across the four shards.
335
367
  * Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#Role#SHARD#<n>`.
336
- * SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
337
- * `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
368
+ * SK is derived via `gsi1skAttribute` uses the resource's natural label when
369
+ * extractable, else `<lastUpdated>#<id>` (DR-004). `casing: "none"` preserves the
370
+ * normalized label and ISO-8601 `T`/`Z`.
338
371
  */
339
372
  gsi1: {
340
373
  index: "GSI1",
@@ -346,8 +379,8 @@ var RoleEntity = new Entity3({
346
379
  sk: {
347
380
  field: "GSI1SK",
348
381
  casing: "none",
349
- composite: ["lastUpdated", "id"],
350
- template: "${lastUpdated}#${id}"
382
+ composite: ["gsi1sk"],
383
+ template: "${gsi1sk}"
351
384
  }
352
385
  }
353
386
  }
@@ -401,6 +434,8 @@ var RoleAssignmentEntity = new Entity4({
401
434
  required: true
402
435
  },
403
436
  gsi1Shard: gsi1ShardAttribute,
437
+ /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
438
+ gsi1sk: gsi1skAttribute,
404
439
  deleted: {
405
440
  type: "boolean",
406
441
  required: false
@@ -431,8 +466,9 @@ var RoleAssignmentEntity = new Entity4({
431
466
  /**
432
467
  * GSI1 — Unified Sharded List per ADR-011: list all RoleAssignments for a tenant across the
433
468
  * four shards. Tenant-scoped only, so `WID#-` is a sentinel.
434
- * SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
435
- * `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
469
+ * SK is derived via `gsi1skAttribute` uses the resource's natural label when
470
+ * extractable, else `<lastUpdated>#<id>` (DR-004). `casing: "none"` preserves the
471
+ * normalized label and ISO-8601 `T`/`Z`.
436
472
  */
437
473
  gsi1: {
438
474
  index: "GSI1",
@@ -444,8 +480,8 @@ var RoleAssignmentEntity = new Entity4({
444
480
  sk: {
445
481
  field: "GSI1SK",
446
482
  casing: "none",
447
- composite: ["lastUpdated", "id"],
448
- template: "${lastUpdated}#${id}"
483
+ composite: ["gsi1sk"],
484
+ template: "${gsi1sk}"
449
485
  }
450
486
  }
451
487
  }
@@ -499,6 +535,8 @@ var TenantEntity = new Entity5({
499
535
  required: true
500
536
  },
501
537
  gsi1Shard: gsi1ShardAttribute,
538
+ /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
539
+ gsi1sk: gsi1skAttribute,
502
540
  deleted: {
503
541
  type: "boolean",
504
542
  required: false
@@ -529,8 +567,9 @@ var TenantEntity = new Entity5({
529
567
  /**
530
568
  * GSI1 — Unified Sharded List per ADR-011: list all Tenants across the four shards.
531
569
  * Tenant lives at the platform tier (no parent tenant or workspace), so `TID#-#WID#-`
532
- * sentinels precede `RT#Tenant#SHARD#<n>`. SK is `<ISO-8601 lastUpdated>#<id>` (control-plane
533
- * unlabeled per DR-004). `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
570
+ * sentinels precede `RT#Tenant#SHARD#<n>`. SK is derived via `gsi1skAttribute` —
571
+ * `<normalizedName>#<id>` when the resource carries a `name`, else `<lastUpdated>#<id>`
572
+ * (DR-004). `casing: "none"` preserves the normalized label and ISO-8601 `T`/`Z`.
534
573
  */
535
574
  gsi1: {
536
575
  index: "GSI1",
@@ -542,8 +581,8 @@ var TenantEntity = new Entity5({
542
581
  sk: {
543
582
  field: "GSI1SK",
544
583
  casing: "none",
545
- composite: ["lastUpdated", "id"],
546
- template: "${lastUpdated}#${id}"
584
+ composite: ["gsi1sk"],
585
+ template: "${gsi1sk}"
547
586
  }
548
587
  }
549
588
  }
@@ -600,6 +639,8 @@ var UserEntity = new Entity6({
600
639
  required: true
601
640
  },
602
641
  gsi1Shard: gsi1ShardAttribute,
642
+ /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
643
+ gsi1sk: gsi1skAttribute,
603
644
  deleted: {
604
645
  type: "boolean",
605
646
  required: false
@@ -630,8 +671,9 @@ var UserEntity = new Entity6({
630
671
  /**
631
672
  * GSI1 — Unified Sharded List per ADR-011: list all Users across the four shards.
632
673
  * Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#User#SHARD#<n>`.
633
- * SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
634
- * `casing: "none"` on the SK preserves ISO-8601 `T`/`Z` characters.
674
+ * SK is derived via `gsi1skAttribute` uses the resource's natural label when
675
+ * extractable (string `name`/`title` via introspection), else `<lastUpdated>#<id>`
676
+ * (DR-004). `casing: "none"` preserves the normalized label and ISO-8601 `T`/`Z`.
635
677
  */
636
678
  gsi1: {
637
679
  index: "GSI1",
@@ -643,8 +685,8 @@ var UserEntity = new Entity6({
643
685
  sk: {
644
686
  field: "GSI1SK",
645
687
  casing: "none",
646
- composite: ["lastUpdated", "id"],
647
- template: "${lastUpdated}#${id}"
688
+ composite: ["gsi1sk"],
689
+ template: "${gsi1sk}"
648
690
  }
649
691
  },
650
692
  /**
@@ -719,6 +761,8 @@ var WorkspaceEntity = new Entity7({
719
761
  required: true
720
762
  },
721
763
  gsi1Shard: gsi1ShardAttribute,
764
+ /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
765
+ gsi1sk: gsi1skAttribute,
722
766
  deleted: {
723
767
  type: "boolean",
724
768
  required: false
@@ -749,8 +793,9 @@ var WorkspaceEntity = new Entity7({
749
793
  /**
750
794
  * GSI1 — Unified Sharded List per ADR-011: list all Workspaces for a tenant across the
751
795
  * four shards. Workspace is itself the workspace identity, so `WID#-` is a sentinel.
752
- * SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
753
- * `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
796
+ * SK is derived via `gsi1skAttribute` — `<normalizedName>#<id>` when the resource
797
+ * carries a `name`, else `<lastUpdated>#<id>` (DR-004). `casing: "none"` preserves
798
+ * the normalized label and ISO-8601 `T`/`Z`.
754
799
  */
755
800
  gsi1: {
756
801
  index: "GSI1",
@@ -762,8 +807,8 @@ var WorkspaceEntity = new Entity7({
762
807
  sk: {
763
808
  field: "GSI1SK",
764
809
  casing: "none",
765
- composite: ["lastUpdated", "id"],
766
- template: "${lastUpdated}#${id}"
810
+ composite: ["gsi1sk"],
811
+ template: "${gsi1sk}"
767
812
  }
768
813
  }
769
814
  }
@@ -797,462 +842,11 @@ function getDynamoControlService(tableName) {
797
842
  };
798
843
  }
799
844
 
800
- // src/data/operations/control/user/user-create-operation.ts
801
- import { extractSummary } from "@openhi/types";
802
- async function createUserOperation(params) {
803
- const { context, body, tableName } = params;
804
- const service = getDynamoControlService(tableName);
805
- const id = body.id ?? `user-${Date.now()}`;
806
- const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
807
- const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
808
- const vid = `1`;
809
- const resource = { resourceType: "User", id, ...parsedResource };
810
- const summary = JSON.stringify(extractSummary(resource));
811
- await service.entities.user.put({
812
- id,
813
- resource: JSON.stringify(resource),
814
- summary,
815
- vid,
816
- lastUpdated
817
- }).go();
818
- return {
819
- id,
820
- resource,
821
- meta: { lastUpdated, versionId: vid }
822
- };
823
- }
824
-
825
- // src/data/operations/control/user/user-delete-operation.ts
826
- async function deleteUserOperation(params) {
827
- const { id, tableName } = params;
828
- const service = getDynamoControlService(tableName);
829
- await service.entities.user.delete({ id, sk: "CURRENT" }).go();
830
- }
831
-
832
- // src/data/errors/domain-errors.ts
833
- var DomainError = class extends Error {
834
- constructor(message, code, options) {
835
- super(message, options);
836
- this.name = this.constructor.name;
837
- this.code = code;
838
- this.details = options?.details;
839
- Object.setPrototypeOf(this, new.target.prototype);
840
- }
841
- };
842
- var NotFoundError = class extends DomainError {
843
- constructor(message, options) {
844
- super(message, "NOT_FOUND", options);
845
- }
846
- };
847
- var ValidationError = class extends DomainError {
848
- constructor(message, options) {
849
- super(message, "VALIDATION", options);
850
- }
851
- };
852
- var ConflictError = class extends DomainError {
853
- constructor(message, options) {
854
- super(message, "CONFLICT", options);
855
- }
856
- };
857
- function domainErrorToHttpStatus(err) {
858
- if (err instanceof NotFoundError) return 404;
859
- if (err instanceof ValidationError) return 400;
860
- if (err instanceof ConflictError) return 409;
861
- return null;
862
- }
863
-
864
- // src/data/operations/control/user/user-get-by-id-operation.ts
865
- async function getUserByIdOperation(params) {
866
- const { id, tableName } = params;
867
- const service = getDynamoControlService(tableName);
868
- const response = await service.entities.user.get({ id, sk: "CURRENT" }).go();
869
- const item = response.data;
870
- if (!item) {
871
- throw new NotFoundError(`User not found: ${id}`);
872
- }
873
- const parsedResource = JSON.parse(item.resource);
874
- return {
875
- id,
876
- resource: { resourceType: "User", id, ...parsedResource }
877
- };
878
- }
879
-
880
- // src/data/operations/data-operations-common.ts
881
- import { extractSortKey, extractSummary as extractSummary2 } from "@openhi/types";
882
-
883
- // src/lib/compression.ts
884
- import { gzipSync, gunzipSync } from "zlib";
885
- var ENVELOPE_VERSION = 1;
886
- var COMPRESSION_ALGOS = {
887
- NONE: "none",
888
- GZIP: "gzip",
889
- BROTLI: "brotli",
890
- DEFLATE: "deflate"
891
- };
892
- function isEnvelope(obj) {
893
- return typeof obj === "object" && obj !== null && "v" in obj && "algo" in obj && "payload" in obj && typeof obj.payload === "string";
894
- }
895
- function compressResource(jsonString, options) {
896
- const algo = options?.algo ?? COMPRESSION_ALGOS.GZIP;
897
- if (algo === COMPRESSION_ALGOS.NONE) {
898
- const envelope2 = {
899
- v: ENVELOPE_VERSION,
900
- algo: COMPRESSION_ALGOS.NONE,
901
- payload: jsonString
902
- };
903
- return JSON.stringify(envelope2);
904
- }
905
- const buf = Buffer.from(jsonString, "utf-8");
906
- const payload = gzipSync(buf).toString("base64");
907
- const envelope = {
908
- v: ENVELOPE_VERSION,
909
- algo: COMPRESSION_ALGOS.GZIP,
910
- payload
911
- };
912
- return JSON.stringify(envelope);
913
- }
914
- function decompressResource(compressedOrRaw) {
915
- try {
916
- const parsed = JSON.parse(compressedOrRaw);
917
- if (isEnvelope(parsed)) {
918
- if (parsed.algo === COMPRESSION_ALGOS.GZIP) {
919
- const buf = Buffer.from(parsed.payload, "base64");
920
- return gunzipSync(buf).toString("utf-8");
921
- }
922
- if (parsed.algo === COMPRESSION_ALGOS.NONE) {
923
- return parsed.payload;
924
- }
925
- return parsed.payload;
926
- }
927
- } catch {
928
- }
929
- try {
930
- const buf = Buffer.from(compressedOrRaw, "base64");
931
- if (buf.length >= 2 && buf[0] === 31 && buf[1] === 139) {
932
- return gunzipSync(buf).toString("utf-8");
933
- }
934
- } catch {
935
- }
936
- return compressedOrRaw;
937
- }
938
-
939
- // src/data/audit-meta.ts
940
- var OPENHI_EXT = "http://openhi.org/fhir/StructureDefinition";
941
- function mergeAuditIntoMeta(meta, audit) {
942
- const existing = meta ?? {};
943
- const ext = [
944
- ...Array.isArray(existing.extension) ? existing.extension : []
945
- ];
946
- const byUrl = new Map(ext.map((e) => [e.url, e]));
947
- function set(url, value, type) {
948
- if (value == null) return;
949
- byUrl.set(url, { url, [type]: value });
950
- }
951
- set(`${OPENHI_EXT}/created-date`, audit.createdDate, "valueDateTime");
952
- set(`${OPENHI_EXT}/created-by-id`, audit.createdById, "valueString");
953
- set(`${OPENHI_EXT}/created-by-name`, audit.createdByName, "valueString");
954
- set(`${OPENHI_EXT}/modified-date`, audit.modifiedDate, "valueDateTime");
955
- set(`${OPENHI_EXT}/modified-by-id`, audit.modifiedById, "valueString");
956
- set(`${OPENHI_EXT}/modified-by-name`, audit.modifiedByName, "valueString");
957
- set(`${OPENHI_EXT}/deleted-date`, audit.deletedDate, "valueDateTime");
958
- set(`${OPENHI_EXT}/deleted-by-id`, audit.deletedById, "valueString");
959
- set(`${OPENHI_EXT}/deleted-by-name`, audit.deletedByName, "valueString");
960
- return { ...existing, extension: Array.from(byUrl.values()) };
961
- }
962
-
963
- // src/data/operations/data-operations-common.ts
964
- var DATA_ENTITY_SK = "CURRENT";
965
- async function getDataEntityById(entity, tenantId, workspaceId, id, resourceLabel) {
966
- const result = await entity.get({
967
- tenantId,
968
- workspaceId,
969
- id,
970
- sk: DATA_ENTITY_SK
971
- }).go();
972
- if (!result.data) {
973
- throw new NotFoundError(`${resourceLabel} ${id} not found`, {
974
- details: { id }
975
- });
976
- }
977
- const parsed = JSON.parse(decompressResource(result.data.resource));
978
- return {
979
- id: result.data.id,
980
- resource: { ...parsed, id: result.data.id }
981
- };
982
- }
983
- async function deleteDataEntityById(entity, tenantId, workspaceId, id) {
984
- await entity.delete({
985
- tenantId,
986
- workspaceId,
987
- id,
988
- sk: DATA_ENTITY_SK
989
- }).go();
990
- }
991
- var BATCH_GET_MAX_ATTEMPTS = 3;
992
- var BATCH_GET_BASE_BACKOFF_MS = 50;
993
- async function batchGetWithRetry(entity, keys) {
994
- if (keys.length === 0) return [];
995
- const collected = [];
996
- let pending = keys;
997
- let attempt = 0;
998
- while (pending.length > 0) {
999
- if (attempt > 0) {
1000
- await new Promise(
1001
- (resolve) => setTimeout(resolve, BATCH_GET_BASE_BACKOFF_MS * 2 ** (attempt - 1))
1002
- );
1003
- }
1004
- attempt++;
1005
- const result = await entity.get(pending).go();
1006
- collected.push(...result.data);
1007
- const unprocessed = result.unprocessed ?? [];
1008
- if (unprocessed.length === 0) break;
1009
- if (attempt >= BATCH_GET_MAX_ATTEMPTS) {
1010
- throw new Error(
1011
- `BatchGet exhausted retries: ${unprocessed.length} key(s) still unprocessed after ${BATCH_GET_MAX_ATTEMPTS} attempt(s)`
1012
- );
1013
- }
1014
- pending = unprocessed;
1015
- }
1016
- return collected;
1017
- }
1018
- async function dispatchListMode(mode, shardResults, hooks) {
1019
- if (mode === "count") {
1020
- let total = 0;
1021
- for (const shardResult of shardResults) {
1022
- total += (shardResult.data ?? []).length;
1023
- }
1024
- return { entries: [], total };
1025
- }
1026
- if (mode === "summary") {
1027
- const entries2 = [];
1028
- for (const shardResult of shardResults) {
1029
- for (const item of shardResult.data ?? []) {
1030
- if (typeof item.summary !== "string") continue;
1031
- let parsed;
1032
- try {
1033
- parsed = JSON.parse(item.summary);
1034
- } catch {
1035
- continue;
1036
- }
1037
- entries2.push(hooks.buildSummaryEntry(item.id, parsed));
1038
- }
1039
- }
1040
- return { entries: entries2, total: entries2.length };
1041
- }
1042
- const orderedIds = [];
1043
- for (const shardResult of shardResults) {
1044
- for (const item of shardResult.data ?? []) {
1045
- orderedIds.push(item.id);
1046
- }
1047
- }
1048
- if (orderedIds.length === 0) return { entries: [], total: 0 };
1049
- const items = await hooks.hydrate(orderedIds);
1050
- const byId = new Map(items.map((item) => [hooks.getId(item), item]));
1051
- const entries = [];
1052
- for (const id of orderedIds) {
1053
- const item = byId.get(id);
1054
- if (!item) continue;
1055
- entries.push(hooks.buildEntry(id, item));
1056
- }
1057
- return { entries, total: entries.length };
1058
- }
1059
- async function listDataEntitiesByWorkspace(entity, tenantId, workspaceId, mode = "full") {
1060
- const shardResults = await Promise.all(
1061
- Array.from(
1062
- { length: SHARD_COUNT },
1063
- (_, shard) => entity.query.gsi1({ tenantId, workspaceId, gsi1Shard: String(shard) }).go()
1064
- )
1065
- );
1066
- return dispatchListMode(
1067
- mode,
1068
- shardResults,
1069
- {
1070
- hydrate: (orderedIds) => batchGetWithRetry(
1071
- entity,
1072
- orderedIds.map((id) => ({
1073
- tenantId,
1074
- workspaceId,
1075
- id,
1076
- sk: DATA_ENTITY_SK
1077
- }))
1078
- ),
1079
- getId: (item) => item.id,
1080
- buildEntry: (id, item) => {
1081
- const parsed = JSON.parse(decompressResource(item.resource));
1082
- return { id, resource: { ...parsed, id } };
1083
- },
1084
- buildSummaryEntry: (id, parsed) => ({
1085
- id,
1086
- resource: { ...parsed, id }
1087
- })
1088
- }
1089
- );
1090
- }
1091
- async function createDataEntityRecord(entity, tenantId, workspaceId, id, resourceWithAudit, fallbackDate) {
1092
- const lastUpdated = resourceWithAudit.meta?.lastUpdated ?? fallbackDate ?? (/* @__PURE__ */ new Date()).toISOString();
1093
- const vid = lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36);
1094
- const resourceLike = resourceWithAudit;
1095
- const summary = JSON.stringify(extractSummary2(resourceLike));
1096
- const gsi1sk = extractSortKey(resourceLike);
1097
- await entity.put({
1098
- sk: DATA_ENTITY_SK,
1099
- tenantId,
1100
- workspaceId,
1101
- id,
1102
- resource: compressResource(JSON.stringify(resourceWithAudit)),
1103
- summary,
1104
- vid,
1105
- lastUpdated,
1106
- gsi1sk
1107
- }).go();
1108
- return {
1109
- id,
1110
- resource: resourceWithAudit
1111
- };
1112
- }
1113
- function buildUpdatedResourceWithAudit(body, id, date, actorId, actorName, existingResourceStr, resourceType) {
1114
- const existingMeta = JSON.parse(existingResourceStr).meta;
1115
- const bodyWithMeta = body;
1116
- const resourceWithVersion = {
1117
- ...body,
1118
- resourceType,
1119
- id,
1120
- meta: {
1121
- ...bodyWithMeta.meta ?? {},
1122
- lastUpdated: date,
1123
- versionId: "2"
1124
- }
1125
- };
1126
- const resourceWithAudit = {
1127
- ...resourceWithVersion,
1128
- meta: mergeAuditIntoMeta(resourceWithVersion.meta ?? existingMeta, {
1129
- modifiedDate: date,
1130
- modifiedById: actorId,
1131
- modifiedByName: actorName
1132
- })
1133
- };
1134
- return {
1135
- resource: resourceWithAudit,
1136
- lastUpdated: date
1137
- };
1138
- }
1139
- async function updateDataEntityById(entity, tenantId, workspaceId, id, resourceLabel, context, buildPatched) {
1140
- const existing = await entity.get({
1141
- tenantId,
1142
- workspaceId,
1143
- id,
1144
- sk: DATA_ENTITY_SK
1145
- }).go();
1146
- if (!existing.data) {
1147
- throw new NotFoundError(`${resourceLabel} ${id} not found`, {
1148
- details: { id }
1149
- });
1150
- }
1151
- const existingStr = decompressResource(existing.data.resource);
1152
- const { resource, lastUpdated } = buildPatched(existingStr);
1153
- const resourceLike = resource;
1154
- const summary = JSON.stringify(extractSummary2(resourceLike));
1155
- const gsi1sk = extractSortKey(resourceLike);
1156
- await entity.patch({
1157
- tenantId,
1158
- workspaceId,
1159
- id,
1160
- sk: DATA_ENTITY_SK
1161
- }).set({
1162
- resource: compressResource(JSON.stringify(resource)),
1163
- summary,
1164
- lastUpdated,
1165
- gsi1sk
1166
- }).go();
1167
- return {
1168
- id,
1169
- resource
1170
- };
1171
- }
1172
-
1173
- // src/data/operations/control/user/user-list-operation.ts
1174
- var SK = "CURRENT";
1175
- async function listUsersOperation(params) {
1176
- const { tableName, mode = "full" } = params;
1177
- const service = getDynamoControlService(tableName);
1178
- const shardResults = await Promise.all(
1179
- Array.from(
1180
- { length: SHARD_COUNT },
1181
- (_, shard) => service.entities.user.query.gsi1({ gsi1Shard: String(shard) }).go()
1182
- )
1183
- );
1184
- return dispatchListMode(mode, shardResults, {
1185
- hydrate: (orderedIds) => batchGetWithRetry(
1186
- service.entities.user,
1187
- orderedIds.map((id) => ({ id, sk: SK }))
1188
- ),
1189
- getId: (item) => item.id,
1190
- buildEntry: (id, item) => ({
1191
- id,
1192
- resource: {
1193
- resourceType: "User",
1194
- id,
1195
- ...JSON.parse(item.resource)
1196
- }
1197
- }),
1198
- buildSummaryEntry: (id, parsed) => ({
1199
- id,
1200
- resource: { resourceType: "User", id, ...parsed }
1201
- })
1202
- });
1203
- }
1204
-
1205
- // src/data/operations/control/user/user-update-operation.ts
1206
- import { extractSummary as extractSummary3 } from "@openhi/types";
1207
- async function updateUserOperation(params) {
1208
- const { context, id, body, tableName } = params;
1209
- const service = getDynamoControlService(tableName);
1210
- const existing = await service.entities.user.get({ id, sk: "CURRENT" }).go();
1211
- if (!existing.data) {
1212
- throw new NotFoundError(`User not found: ${id}`);
1213
- }
1214
- const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
1215
- const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
1216
- const vid = `${Date.now()}`;
1217
- const resource = { resourceType: "User", id, ...parsedResource };
1218
- const summary = JSON.stringify(extractSummary3(resource));
1219
- await service.entities.user.put({
1220
- id,
1221
- resource: JSON.stringify(resource),
1222
- summary,
1223
- vid,
1224
- lastUpdated
1225
- }).go();
1226
- return {
1227
- id,
1228
- resource,
1229
- meta: { lastUpdated, versionId: vid }
1230
- };
1231
- }
1232
-
1233
845
  export {
1234
- compressResource,
1235
- decompressResource,
1236
846
  defaultTableName,
1237
847
  dynamoClient,
1238
848
  SHARD_COUNT,
1239
849
  computeShard,
1240
- getDynamoControlService,
1241
- NotFoundError,
1242
- domainErrorToHttpStatus,
1243
- mergeAuditIntoMeta,
1244
- getDataEntityById,
1245
- deleteDataEntityById,
1246
- batchGetWithRetry,
1247
- dispatchListMode,
1248
- listDataEntitiesByWorkspace,
1249
- createDataEntityRecord,
1250
- buildUpdatedResourceWithAudit,
1251
- updateDataEntityById,
1252
- createUserOperation,
1253
- deleteUserOperation,
1254
- getUserByIdOperation,
1255
- listUsersOperation,
1256
- updateUserOperation
850
+ getDynamoControlService
1257
851
  };
1258
- //# sourceMappingURL=chunk-36YCDLLA.mjs.map
852
+ //# sourceMappingURL=chunk-VYDIGFIX.mjs.map