@openhi/constructs 0.0.90 → 0.0.92

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.
@@ -1,6 +1,11 @@
1
1
  import {
2
- __require
3
- } from "./chunk-LZOMFHX3.mjs";
2
+ SHARD_COUNT,
3
+ computeShard,
4
+ defaultTableName,
5
+ dynamoClient,
6
+ getDynamoControlService
7
+ } from "./chunk-MLTYFMSE.mjs";
8
+ import "./chunk-3QS3WKRC.mjs";
4
9
 
5
10
  // src/data/lambda/rest-api-lambda.handler.ts
6
11
  import serverlessExpress from "@codegenie/serverless-express";
@@ -126,805 +131,6 @@ function decompressResource(compressedOrRaw) {
126
131
  return compressedOrRaw;
127
132
  }
128
133
 
129
- // src/data/dynamo/dynamo-control-service.ts
130
- import { Service } from "electrodb";
131
-
132
- // src/data/dynamo/dynamo-client.ts
133
- import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
134
- var defaultTableName = process.env.DYNAMO_TABLE_NAME ?? "jesttesttable";
135
- var dynamoClient = new DynamoDBClient({
136
- ...process.env.MOCK_DYNAMODB_ENDPOINT && {
137
- endpoint: process.env.MOCK_DYNAMODB_ENDPOINT,
138
- sslEnabled: false,
139
- region: "local"
140
- }
141
- });
142
-
143
- // src/data/dynamo/entities/control/configuration-entity.ts
144
- import { Entity } from "electrodb";
145
-
146
- // src/data/dynamo/shard.ts
147
- var SHARD_COUNT = 4;
148
- function computeShard(id) {
149
- let hash = 2166136261;
150
- for (let i = 0; i < id.length; i++) {
151
- hash ^= id.charCodeAt(i);
152
- hash = Math.imul(hash, 16777619);
153
- }
154
- return (hash >>> 0) % SHARD_COUNT;
155
- }
156
-
157
- // src/data/dynamo/entities/control/control-entity-common.ts
158
- var gsi1ShardAttribute = {
159
- type: "string",
160
- watch: ["id"],
161
- set: (_val, item) => {
162
- if (typeof item?.id !== "string" || item.id.length === 0) {
163
- return void 0;
164
- }
165
- return String(computeShard(item.id));
166
- }
167
- };
168
-
169
- // src/data/dynamo/entities/control/configuration-entity.ts
170
- var ConfigurationEntity = new Entity({
171
- model: {
172
- entity: "configuration",
173
- service: "control",
174
- version: "01"
175
- },
176
- attributes: {
177
- /** Sort key. "CURRENT" for current version; version history in S3. */
178
- sk: {
179
- type: "string",
180
- required: true,
181
- default: "CURRENT"
182
- },
183
- /** Tenant scope. Use "BASELINE" when the config is baseline default (no tenant). */
184
- tenantId: {
185
- type: "string",
186
- required: true,
187
- default: "BASELINE"
188
- },
189
- /** Workspace scope. Use "-" when absent. */
190
- workspaceId: {
191
- type: "string",
192
- required: true,
193
- default: "-"
194
- },
195
- /** User scope. Use "-" when absent. */
196
- userId: {
197
- type: "string",
198
- required: true,
199
- default: "-"
200
- },
201
- /** Role scope. Use "-" when absent. */
202
- roleId: {
203
- type: "string",
204
- required: true,
205
- default: "-"
206
- },
207
- /** Config type (category), e.g. endpoints, branding, display. */
208
- key: {
209
- type: "string",
210
- required: true
211
- },
212
- /** FHIR Resource.id; logical id in URL and for the Configuration resource. */
213
- id: {
214
- type: "string",
215
- required: true
216
- },
217
- /** Payload as JSON string. JSON.stringify(resource) on write; JSON.parse(item.resource) on read. */
218
- resource: {
219
- type: "string",
220
- required: true
221
- },
222
- /**
223
- * Summary projection (key display fields as JSON string: id, key, status).
224
- * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
225
- */
226
- summary: {
227
- type: "string",
228
- required: true
229
- },
230
- /** Version id (e.g. ULID). Tracks current version; S3 history key. */
231
- vid: {
232
- type: "string",
233
- required: true
234
- },
235
- lastUpdated: {
236
- type: "string",
237
- required: true
238
- },
239
- gsi1Shard: gsi1ShardAttribute,
240
- deleted: {
241
- type: "boolean",
242
- required: false
243
- },
244
- bundleId: {
245
- type: "string",
246
- required: false
247
- },
248
- msgId: {
249
- type: "string",
250
- required: false
251
- }
252
- },
253
- indexes: {
254
- /** Base table: PK, SK (data store key names). PK is built from tenantId, workspaceId, userId, roleId; SK is built from key and sk. Do not supply PK or SK from outside. */
255
- record: {
256
- pk: {
257
- field: "PK",
258
- composite: ["tenantId", "workspaceId", "userId", "roleId"],
259
- template: "CONFIG#TID#${tenantId}#WID#${workspaceId}#UID#${userId}#RID#${roleId}"
260
- },
261
- sk: {
262
- field: "SK",
263
- composite: ["key", "sk"],
264
- template: "KEY#${key}#SK#${sk}"
265
- }
266
- },
267
- /**
268
- * GSI1 — Unified Sharded List per ADR-011: list all Configuration entries for a
269
- * (tenant, workspace) across the four shards. Use for "list configs scoped to this tenant"
270
- * (workspaceId = "-") or "list configs scoped to this workspace". Does not support
271
- * hierarchical resolution in one query; use base table GetItem in fallback order
272
- * (user → workspace → tenant → baseline) for that.
273
- * SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
274
- * `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
275
- */
276
- gsi1: {
277
- index: "GSI1",
278
- pk: {
279
- field: "GSI1PK",
280
- composite: ["tenantId", "workspaceId", "gsi1Shard"],
281
- template: "TID#${tenantId}#WID#${workspaceId}#RT#Configuration#SHARD#${gsi1Shard}"
282
- },
283
- sk: {
284
- field: "GSI1SK",
285
- casing: "none",
286
- composite: ["lastUpdated", "id"],
287
- template: "${lastUpdated}#${id}"
288
- }
289
- }
290
- }
291
- });
292
-
293
- // src/data/dynamo/entities/control/membership-entity.ts
294
- import { Entity as Entity2 } from "electrodb";
295
- var MembershipEntity = new Entity2({
296
- model: {
297
- entity: "membership",
298
- service: "control",
299
- version: "01"
300
- },
301
- attributes: {
302
- /** Sort key sentinel. Always "CURRENT". */
303
- sk: {
304
- type: "string",
305
- required: true,
306
- default: "CURRENT"
307
- },
308
- /** Tenant in which the user has membership (required). */
309
- tenantId: {
310
- type: "string",
311
- required: true
312
- },
313
- /** FHIR Resource.id; membership id. */
314
- id: {
315
- type: "string",
316
- required: true
317
- },
318
- /** Full Membership resource serialized as JSON string. */
319
- resource: {
320
- type: "string",
321
- required: true
322
- },
323
- /**
324
- * Summary projection (key display fields as JSON string: id, displayName, status).
325
- * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
326
- */
327
- summary: {
328
- type: "string",
329
- required: true
330
- },
331
- /** Version id (e.g. ULID). */
332
- vid: {
333
- type: "string",
334
- required: true
335
- },
336
- lastUpdated: {
337
- type: "string",
338
- required: true
339
- },
340
- gsi1Shard: gsi1ShardAttribute,
341
- deleted: {
342
- type: "boolean",
343
- required: false
344
- },
345
- bundleId: {
346
- type: "string",
347
- required: false
348
- },
349
- msgId: {
350
- type: "string",
351
- required: false
352
- }
353
- },
354
- indexes: {
355
- /** Base table: PK = TID#<tenantId>#MEMBERSHIP#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
356
- record: {
357
- pk: {
358
- field: "PK",
359
- composite: ["tenantId", "id"],
360
- template: "TID#${tenantId}#MEMBERSHIP#ID#${id}"
361
- },
362
- sk: {
363
- field: "SK",
364
- composite: ["sk"],
365
- template: "${sk}"
366
- }
367
- },
368
- /**
369
- * GSI1 — Unified Sharded List per ADR-011: list all Memberships for a tenant across the
370
- * four shards. Membership is tenant-scoped only, so `WID#-` is a sentinel.
371
- * SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
372
- * `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
373
- */
374
- gsi1: {
375
- index: "GSI1",
376
- pk: {
377
- field: "GSI1PK",
378
- composite: ["tenantId", "gsi1Shard"],
379
- template: "TID#${tenantId}#WID#-#RT#Membership#SHARD#${gsi1Shard}"
380
- },
381
- sk: {
382
- field: "GSI1SK",
383
- casing: "none",
384
- composite: ["lastUpdated", "id"],
385
- template: "${lastUpdated}#${id}"
386
- }
387
- }
388
- }
389
- });
390
-
391
- // src/data/dynamo/entities/control/role-entity.ts
392
- import { Entity as Entity3 } from "electrodb";
393
- var RoleEntity = new Entity3({
394
- model: {
395
- entity: "role",
396
- service: "control",
397
- version: "01"
398
- },
399
- attributes: {
400
- /** Sort key sentinel. Always "CURRENT". */
401
- sk: {
402
- type: "string",
403
- required: true,
404
- default: "CURRENT"
405
- },
406
- /** FHIR Resource.id; role id. */
407
- id: {
408
- type: "string",
409
- required: true
410
- },
411
- /** Full Role resource serialized as JSON string. */
412
- resource: {
413
- type: "string",
414
- required: true
415
- },
416
- /**
417
- * Summary projection (key display fields as JSON string: id, displayName, status).
418
- * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
419
- */
420
- summary: {
421
- type: "string",
422
- required: true
423
- },
424
- /** Version id (e.g. ULID). */
425
- vid: {
426
- type: "string",
427
- required: true
428
- },
429
- lastUpdated: {
430
- type: "string",
431
- required: true
432
- },
433
- gsi1Shard: gsi1ShardAttribute,
434
- deleted: {
435
- type: "boolean",
436
- required: false
437
- },
438
- bundleId: {
439
- type: "string",
440
- required: false
441
- },
442
- msgId: {
443
- type: "string",
444
- required: false
445
- }
446
- },
447
- indexes: {
448
- /** Base table: PK = ROLE#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
449
- record: {
450
- pk: {
451
- field: "PK",
452
- composite: ["id"],
453
- template: "ROLE#ID#${id}"
454
- },
455
- sk: {
456
- field: "SK",
457
- composite: ["sk"],
458
- template: "${sk}"
459
- }
460
- },
461
- /**
462
- * GSI1 — Unified Sharded List per ADR-011: list all Roles across the four shards.
463
- * Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#Role#SHARD#<n>`.
464
- * SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
465
- * `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
466
- */
467
- gsi1: {
468
- index: "GSI1",
469
- pk: {
470
- field: "GSI1PK",
471
- composite: ["gsi1Shard"],
472
- template: "TID#-#WID#-#RT#Role#SHARD#${gsi1Shard}"
473
- },
474
- sk: {
475
- field: "GSI1SK",
476
- casing: "none",
477
- composite: ["lastUpdated", "id"],
478
- template: "${lastUpdated}#${id}"
479
- }
480
- }
481
- }
482
- });
483
-
484
- // src/data/dynamo/entities/control/roleassignment-entity.ts
485
- import { Entity as Entity4 } from "electrodb";
486
- var RoleAssignmentEntity = new Entity4({
487
- model: {
488
- entity: "roleassignment",
489
- service: "control",
490
- version: "01"
491
- },
492
- attributes: {
493
- /** Sort key sentinel. Always "CURRENT". */
494
- sk: {
495
- type: "string",
496
- required: true,
497
- default: "CURRENT"
498
- },
499
- /** Tenant in which the role assignment applies (required). */
500
- tenantId: {
501
- type: "string",
502
- required: true
503
- },
504
- /** FHIR Resource.id; role assignment id. */
505
- id: {
506
- type: "string",
507
- required: true
508
- },
509
- /** Full RoleAssignment resource serialized as JSON string. */
510
- resource: {
511
- type: "string",
512
- required: true
513
- },
514
- /**
515
- * Summary projection (key display fields as JSON string: id, displayName, status).
516
- * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
517
- */
518
- summary: {
519
- type: "string",
520
- required: true
521
- },
522
- /** Version id (e.g. ULID). */
523
- vid: {
524
- type: "string",
525
- required: true
526
- },
527
- lastUpdated: {
528
- type: "string",
529
- required: true
530
- },
531
- gsi1Shard: gsi1ShardAttribute,
532
- deleted: {
533
- type: "boolean",
534
- required: false
535
- },
536
- bundleId: {
537
- type: "string",
538
- required: false
539
- },
540
- msgId: {
541
- type: "string",
542
- required: false
543
- }
544
- },
545
- indexes: {
546
- /** Base table: PK = TID#<tenantId>#ROLEASSIGNMENT#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
547
- record: {
548
- pk: {
549
- field: "PK",
550
- composite: ["tenantId", "id"],
551
- template: "TID#${tenantId}#ROLEASSIGNMENT#ID#${id}"
552
- },
553
- sk: {
554
- field: "SK",
555
- composite: ["sk"],
556
- template: "${sk}"
557
- }
558
- },
559
- /**
560
- * GSI1 — Unified Sharded List per ADR-011: list all RoleAssignments for a tenant across the
561
- * four shards. Tenant-scoped only, so `WID#-` is a sentinel.
562
- * SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
563
- * `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
564
- */
565
- gsi1: {
566
- index: "GSI1",
567
- pk: {
568
- field: "GSI1PK",
569
- composite: ["tenantId", "gsi1Shard"],
570
- template: "TID#${tenantId}#WID#-#RT#RoleAssignment#SHARD#${gsi1Shard}"
571
- },
572
- sk: {
573
- field: "GSI1SK",
574
- casing: "none",
575
- composite: ["lastUpdated", "id"],
576
- template: "${lastUpdated}#${id}"
577
- }
578
- }
579
- }
580
- });
581
-
582
- // src/data/dynamo/entities/control/tenant-entity.ts
583
- import { Entity as Entity5 } from "electrodb";
584
- var TenantEntity = new Entity5({
585
- model: {
586
- entity: "tenant",
587
- service: "control",
588
- version: "01"
589
- },
590
- attributes: {
591
- /** Sort key sentinel. Always "CURRENT". */
592
- sk: {
593
- type: "string",
594
- required: true,
595
- default: "CURRENT"
596
- },
597
- /** The tenant's own id (= resource id). Drives the partition key. */
598
- tenantId: {
599
- type: "string",
600
- required: true
601
- },
602
- /** FHIR Resource.id; logical id in URL. Equals tenantId. */
603
- id: {
604
- type: "string",
605
- required: true
606
- },
607
- /** Full Tenant resource serialized as JSON string. */
608
- resource: {
609
- type: "string",
610
- required: true
611
- },
612
- /**
613
- * Summary projection (key display fields as JSON string: id, displayName, status).
614
- * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
615
- */
616
- summary: {
617
- type: "string",
618
- required: true
619
- },
620
- /** Version id (e.g. ULID). */
621
- vid: {
622
- type: "string",
623
- required: true
624
- },
625
- lastUpdated: {
626
- type: "string",
627
- required: true
628
- },
629
- gsi1Shard: gsi1ShardAttribute,
630
- deleted: {
631
- type: "boolean",
632
- required: false
633
- },
634
- bundleId: {
635
- type: "string",
636
- required: false
637
- },
638
- msgId: {
639
- type: "string",
640
- required: false
641
- }
642
- },
643
- indexes: {
644
- /** Base table: PK = TENANT#ID#<tenantId>, SK = CURRENT. Do not supply PK or SK from outside. */
645
- record: {
646
- pk: {
647
- field: "PK",
648
- composite: ["tenantId"],
649
- template: "TENANT#ID#${tenantId}"
650
- },
651
- sk: {
652
- field: "SK",
653
- composite: ["sk"],
654
- template: "${sk}"
655
- }
656
- },
657
- /**
658
- * GSI1 — Unified Sharded List per ADR-011: list all Tenants across the four shards.
659
- * Tenant lives at the platform tier (no parent tenant or workspace), so `TID#-#WID#-`
660
- * sentinels precede `RT#Tenant#SHARD#<n>`. SK is `<ISO-8601 lastUpdated>#<id>` (control-plane
661
- * unlabeled per DR-004). `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
662
- */
663
- gsi1: {
664
- index: "GSI1",
665
- pk: {
666
- field: "GSI1PK",
667
- composite: ["gsi1Shard"],
668
- template: "TID#-#WID#-#RT#Tenant#SHARD#${gsi1Shard}"
669
- },
670
- sk: {
671
- field: "GSI1SK",
672
- casing: "none",
673
- composite: ["lastUpdated", "id"],
674
- template: "${lastUpdated}#${id}"
675
- }
676
- }
677
- }
678
- });
679
-
680
- // src/data/dynamo/entities/control/user-entity.ts
681
- import { Entity as Entity6 } from "electrodb";
682
- var UserEntity = new Entity6({
683
- model: {
684
- entity: "user",
685
- service: "control",
686
- version: "01"
687
- },
688
- attributes: {
689
- /** Sort key sentinel. Always "CURRENT". */
690
- sk: {
691
- type: "string",
692
- required: true,
693
- default: "CURRENT"
694
- },
695
- /** FHIR Resource.id; platform user id (ohi_uid). */
696
- id: {
697
- type: "string",
698
- required: true
699
- },
700
- /** Full User resource serialized as JSON string. */
701
- resource: {
702
- type: "string",
703
- required: true
704
- },
705
- /**
706
- * Summary projection (key display fields as JSON string: id, displayName, status).
707
- * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
708
- */
709
- summary: {
710
- type: "string",
711
- required: true
712
- },
713
- /**
714
- * Immutable Cognito-issued `sub` claim. Drives GSI2 (sub-lookup). Optional until the
715
- * Post Confirmation Lambda (#770) lands; required thereafter.
716
- */
717
- cognitoSub: {
718
- type: "string",
719
- required: false
720
- },
721
- /** Version id (e.g. ULID). */
722
- vid: {
723
- type: "string",
724
- required: true
725
- },
726
- lastUpdated: {
727
- type: "string",
728
- required: true
729
- },
730
- gsi1Shard: gsi1ShardAttribute,
731
- deleted: {
732
- type: "boolean",
733
- required: false
734
- },
735
- bundleId: {
736
- type: "string",
737
- required: false
738
- },
739
- msgId: {
740
- type: "string",
741
- required: false
742
- }
743
- },
744
- indexes: {
745
- /** Base table: PK = USER#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
746
- record: {
747
- pk: {
748
- field: "PK",
749
- composite: ["id"],
750
- template: "USER#ID#${id}"
751
- },
752
- sk: {
753
- field: "SK",
754
- composite: ["sk"],
755
- template: "${sk}"
756
- }
757
- },
758
- /**
759
- * GSI1 — Unified Sharded List per ADR-011: list all Users across the four shards.
760
- * Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#User#SHARD#<n>`.
761
- * SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
762
- * `casing: "none"` on the SK preserves ISO-8601 `T`/`Z` characters.
763
- */
764
- gsi1: {
765
- index: "GSI1",
766
- pk: {
767
- field: "GSI1PK",
768
- composite: ["gsi1Shard"],
769
- template: "TID#-#WID#-#RT#User#SHARD#${gsi1Shard}"
770
- },
771
- sk: {
772
- field: "GSI1SK",
773
- casing: "none",
774
- composite: ["lastUpdated", "id"],
775
- template: "${lastUpdated}#${id}"
776
- }
777
- },
778
- /**
779
- * GSI2 — Cognito sub-lookup per ADR-011: resolves the UserEntity from a Cognito `sub` claim.
780
- * `condition` skips the index when `cognitoSub` is missing so legacy items without a sub are
781
- * not indexed.
782
- */
783
- gsi2: {
784
- index: "GSI2",
785
- condition: (attrs) => typeof attrs.cognitoSub === "string" && attrs.cognitoSub.length > 0,
786
- pk: {
787
- field: "GSI2PK",
788
- casing: "none",
789
- composite: ["cognitoSub"],
790
- template: "USER#SUB#${cognitoSub}"
791
- },
792
- sk: {
793
- field: "GSI2SK",
794
- casing: "none",
795
- composite: [],
796
- template: "CURRENT"
797
- }
798
- }
799
- }
800
- });
801
-
802
- // src/data/dynamo/entities/control/workspace-entity.ts
803
- import { Entity as Entity7 } from "electrodb";
804
- var WorkspaceEntity = new Entity7({
805
- model: {
806
- entity: "workspace",
807
- service: "control",
808
- version: "01"
809
- },
810
- attributes: {
811
- /** Sort key sentinel. Always "CURRENT". */
812
- sk: {
813
- type: "string",
814
- required: true,
815
- default: "CURRENT"
816
- },
817
- /** Tenant that contains this workspace (required). */
818
- tenantId: {
819
- type: "string",
820
- required: true
821
- },
822
- /** FHIR Resource.id; logical id in URL. */
823
- id: {
824
- type: "string",
825
- required: true
826
- },
827
- /** Full Workspace resource serialized as JSON string. */
828
- resource: {
829
- type: "string",
830
- required: true
831
- },
832
- /**
833
- * Summary projection (key display fields as JSON string: id, displayName, status).
834
- * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
835
- */
836
- summary: {
837
- type: "string",
838
- required: true
839
- },
840
- /** Version id (e.g. ULID). */
841
- vid: {
842
- type: "string",
843
- required: true
844
- },
845
- lastUpdated: {
846
- type: "string",
847
- required: true
848
- },
849
- gsi1Shard: gsi1ShardAttribute,
850
- deleted: {
851
- type: "boolean",
852
- required: false
853
- },
854
- bundleId: {
855
- type: "string",
856
- required: false
857
- },
858
- msgId: {
859
- type: "string",
860
- required: false
861
- }
862
- },
863
- indexes: {
864
- /** Base table: PK = TID#<tenantId>#WORKSPACE#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
865
- record: {
866
- pk: {
867
- field: "PK",
868
- composite: ["tenantId", "id"],
869
- template: "TID#${tenantId}#WORKSPACE#ID#${id}"
870
- },
871
- sk: {
872
- field: "SK",
873
- composite: ["sk"],
874
- template: "${sk}"
875
- }
876
- },
877
- /**
878
- * GSI1 — Unified Sharded List per ADR-011: list all Workspaces for a tenant across the
879
- * four shards. Workspace is itself the workspace identity, so `WID#-` is a sentinel.
880
- * SK is `<ISO-8601 lastUpdated>#<id>` (control-plane unlabeled per DR-004).
881
- * `casing: "none"` on the SK preserves ISO-8601 `T`/`Z`.
882
- */
883
- gsi1: {
884
- index: "GSI1",
885
- pk: {
886
- field: "GSI1PK",
887
- composite: ["tenantId", "gsi1Shard"],
888
- template: "TID#${tenantId}#WID#-#RT#Workspace#SHARD#${gsi1Shard}"
889
- },
890
- sk: {
891
- field: "GSI1SK",
892
- casing: "none",
893
- composite: ["lastUpdated", "id"],
894
- template: "${lastUpdated}#${id}"
895
- }
896
- }
897
- }
898
- });
899
-
900
- // src/data/dynamo/dynamo-control-service.ts
901
- var controlPlaneEntities = {
902
- configuration: ConfigurationEntity,
903
- membership: MembershipEntity,
904
- role: RoleEntity,
905
- roleAssignment: RoleAssignmentEntity,
906
- tenant: TenantEntity,
907
- user: UserEntity,
908
- workspace: WorkspaceEntity
909
- };
910
- var controlPlaneService = new Service(controlPlaneEntities, {
911
- table: defaultTableName,
912
- client: dynamoClient
913
- });
914
- var DynamoControlService = {
915
- entities: controlPlaneService.entities
916
- };
917
- function getDynamoControlService(tableName) {
918
- const resolved = tableName ?? defaultTableName;
919
- const service = new Service(controlPlaneEntities, {
920
- table: resolved,
921
- client: dynamoClient
922
- });
923
- return {
924
- entities: service.entities
925
- };
926
- }
927
-
928
134
  // src/data/operations/control/configuration/configuration-create-operation.ts
929
135
  var SK = "CURRENT";
930
136
  async function createConfigurationOperation(params) {
@@ -1458,7 +664,7 @@ async function batchGetWithRetry(entity, keys) {
1458
664
  while (pending.length > 0) {
1459
665
  if (attempt > 0) {
1460
666
  await new Promise(
1461
- (resolve2) => setTimeout(resolve2, BATCH_GET_BASE_BACKOFF_MS * 2 ** (attempt - 1))
667
+ (resolve) => setTimeout(resolve, BATCH_GET_BASE_BACKOFF_MS * 2 ** (attempt - 1))
1462
668
  );
1463
669
  }
1464
670
  attempt++;
@@ -3581,10 +2787,10 @@ import express8 from "express";
3581
2787
  import { ulid } from "ulid";
3582
2788
 
3583
2789
  // src/data/dynamo/dynamo-data-service.ts
3584
- import { Service as Service2 } from "electrodb";
2790
+ import { Service } from "electrodb";
3585
2791
 
3586
2792
  // src/data/dynamo/entities/data-entity-common.ts
3587
- import { Entity as Entity8 } from "electrodb";
2793
+ import { Entity } from "electrodb";
3588
2794
  var dataEntityAttributes = {
3589
2795
  /** Sort key. "CURRENT" for current version; version history in S3. */
3590
2796
  sk: {
@@ -3680,7 +2886,7 @@ var dataEntityAttributes = {
3680
2886
  }
3681
2887
  };
3682
2888
  function createDataEntity(entity, resourceTypeLabel) {
3683
- return new Entity8({
2889
+ return new Entity({
3684
2890
  model: {
3685
2891
  entity,
3686
2892
  service: "data",
@@ -4594,7 +3800,7 @@ var dataPlaneEntities = {
4594
3800
  visionprescription: VisionPrescriptionEntity,
4595
3801
  verificationresult: VerificationResultEntity
4596
3802
  };
4597
- var dataPlaneService = new Service2(dataPlaneEntities, {
3803
+ var dataPlaneService = new Service(dataPlaneEntities, {
4598
3804
  table: defaultTableName,
4599
3805
  client: dynamoClient
4600
3806
  });
@@ -4603,7 +3809,7 @@ var DynamoDataService = {
4603
3809
  };
4604
3810
  function getDynamoDataService(tableName) {
4605
3811
  const resolved = tableName ?? defaultTableName;
4606
- const service = new Service2(dataPlaneEntities, {
3812
+ const service = new Service(dataPlaneEntities, {
4607
3813
  table: resolved,
4608
3814
  client: dynamoClient
4609
3815
  });
@@ -24905,133 +24111,8 @@ import express106 from "express";
24905
24111
 
24906
24112
  // src/data/operations/data/patient/patient-create-operation.ts
24907
24113
  import { ulid as ulid99 } from "ulid";
24908
-
24909
- // src/data/import-patient.ts
24910
- import { readFileSync } from "fs";
24911
- import { resolve } from "path";
24912
- import { extractSummary as extractSummary14 } from "@openhi/types";
24913
- function extractPatient(parsed) {
24914
- if (parsed && typeof parsed === "object" && "resourceType" in parsed) {
24915
- const root = parsed;
24916
- if (root.resourceType === "Patient" && root.id) {
24917
- return root;
24918
- }
24919
- if (root.resourceType === "Bundle" && "entry" in parsed) {
24920
- const entries = parsed.entry;
24921
- if (Array.isArray(entries)) {
24922
- const patientEntry = entries.find(
24923
- (e) => e?.resource?.resourceType === "Patient" && e.resource.id
24924
- );
24925
- if (patientEntry?.resource) {
24926
- return patientEntry.resource;
24927
- }
24928
- }
24929
- }
24930
- }
24931
- throw new Error(
24932
- "File must be a FHIR Patient resource or a Bundle containing at least one Patient entry"
24933
- );
24934
- }
24935
- var SK12 = "CURRENT";
24936
- var defaultAudit = {
24937
- createdDate: (/* @__PURE__ */ new Date()).toISOString(),
24938
- createdById: "import",
24939
- createdByName: "Bulk import",
24940
- modifiedDate: (/* @__PURE__ */ new Date()).toISOString(),
24941
- modifiedById: "import",
24942
- modifiedByName: "Bulk import"
24943
- };
24944
- function patientToPutAttrs(patient, options) {
24945
- const {
24946
- tenantId,
24947
- workspaceId,
24948
- createdDate,
24949
- createdById,
24950
- createdByName,
24951
- modifiedDate,
24952
- modifiedById,
24953
- modifiedByName
24954
- } = options;
24955
- const lastUpdated = patient.meta?.lastUpdated ?? modifiedDate ?? defaultAudit.modifiedDate ?? (/* @__PURE__ */ new Date()).toISOString();
24956
- const auditMerged = {
24957
- ...defaultAudit,
24958
- ...createdDate != null && { createdDate },
24959
- ...createdById != null && { createdById },
24960
- ...createdByName != null && { createdByName },
24961
- ...modifiedDate != null && { modifiedDate },
24962
- ...modifiedById != null && { modifiedById },
24963
- ...modifiedByName != null && { modifiedByName }
24964
- };
24965
- const patientWithMeta = {
24966
- ...patient,
24967
- meta: mergeAuditIntoMeta(patient.meta, auditMerged)
24968
- };
24969
- if (lastUpdated && !patientWithMeta.meta.lastUpdated) {
24970
- patientWithMeta.meta.lastUpdated = lastUpdated;
24971
- }
24972
- return {
24973
- sk: SK12,
24974
- tenantId,
24975
- workspaceId,
24976
- id: patient.id,
24977
- resource: compressResource(JSON.stringify(patientWithMeta)),
24978
- summary: JSON.stringify(
24979
- extractSummary14(patientWithMeta)
24980
- ),
24981
- vid: lastUpdated.replace(/[-:T.Z]/g, "").slice(0, 12) || Date.now().toString(36),
24982
- lastUpdated,
24983
- identifierSystem: "",
24984
- identifierValue: "",
24985
- facilityId: "",
24986
- normalizedName: ""
24987
- };
24988
- }
24989
- async function importPatientFromFile(filePath, options) {
24990
- const resolved = resolve(filePath);
24991
- const raw = readFileSync(resolved, "utf-8");
24992
- const parsed = JSON.parse(raw);
24993
- const patient = extractPatient(parsed);
24994
- const service = getDynamoDataService(options.tableName);
24995
- const attrs = patientToPutAttrs(patient, options);
24996
- const result = await service.entities.patient.put(attrs).go();
24997
- const data = result.data;
24998
- if (!data) {
24999
- throw new Error(`Put failed for Patient ${patient.id}`);
25000
- }
25001
- return {
25002
- id: data.id,
25003
- tenantId: data.tenantId,
25004
- workspaceId: data.workspaceId
25005
- };
25006
- }
25007
- async function main() {
25008
- const [, , fileArg, tenantId = "tenant-1", workspaceId = "ws-1"] = process.argv;
25009
- if (!fileArg) {
25010
- console.error(
25011
- "Usage: import-patient.ts <path-to-patient.json> [tenantId] [workspaceId]"
25012
- );
25013
- process.exit(1);
25014
- }
25015
- try {
25016
- const result = await importPatientFromFile(fileArg, {
25017
- tenantId,
25018
- workspaceId
25019
- });
25020
- console.log(
25021
- `Imported Patient ${result.id} (tenant=${result.tenantId}, workspace=${result.workspaceId})`
25022
- );
25023
- } catch (err) {
25024
- console.error(err);
25025
- process.exit(1);
25026
- }
25027
- }
25028
- if (__require.main === module) {
25029
- void main();
25030
- }
25031
-
25032
- // src/data/operations/data/patient/patient-create-operation.ts
25033
24114
  async function createPatientOperation(params) {
25034
- const { context, body } = params;
24115
+ const { context, body, tableName } = params;
25035
24116
  const { tenantId, workspaceId, date, actorId, actorName } = context;
25036
24117
  const id = body.id ?? ulid99();
25037
24118
  const meta = {
@@ -25039,29 +24120,28 @@ async function createPatientOperation(params) {
25039
24120
  lastUpdated: date,
25040
24121
  versionId: "1"
25041
24122
  };
25042
- const patient = {
24123
+ const patientWithAudit = {
25043
24124
  ...body,
25044
24125
  resourceType: "Patient",
25045
24126
  id,
25046
- meta
24127
+ meta: mergeAuditIntoMeta(meta, {
24128
+ createdDate: date,
24129
+ createdById: actorId,
24130
+ createdByName: actorName,
24131
+ modifiedDate: date,
24132
+ modifiedById: actorId,
24133
+ modifiedByName: actorName
24134
+ })
25047
24135
  };
25048
- const options = {
24136
+ const service = getDynamoDataService(tableName);
24137
+ return createDataEntityRecord(
24138
+ service.entities.patient,
25049
24139
  tenantId,
25050
24140
  workspaceId,
25051
- createdDate: date,
25052
- createdById: actorId,
25053
- createdByName: actorName,
25054
- modifiedDate: date,
25055
- modifiedById: actorId,
25056
- modifiedByName: actorName
25057
- };
25058
- const service = getDynamoDataService(params.tableName);
25059
- const attrs = patientToPutAttrs(patient, options);
25060
- await service.entities.patient.put(attrs).go();
25061
- return {
25062
24141
  id,
25063
- resource: patient
25064
- };
24142
+ patientWithAudit,
24143
+ date
24144
+ );
25065
24145
  }
25066
24146
 
25067
24147
  // src/data/rest-api/routes/data/patient/patient-create-route.ts