@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.
- package/lib/chunk-36UPY7YQ.mjs +529 -0
- package/lib/chunk-36UPY7YQ.mjs.map +1 -0
- package/lib/chunk-AGF3RAAZ.mjs +20 -0
- package/lib/chunk-AGF3RAAZ.mjs.map +1 -0
- package/lib/{chunk-BXEG7IOZ.mjs → chunk-AO3E22CS.mjs} +2 -2
- package/lib/{chunk-WNUH2WDZ.mjs → chunk-CHPEQRXU.mjs} +2 -2
- package/lib/chunk-JUNL76HF.mjs +428 -0
- package/lib/chunk-JUNL76HF.mjs.map +1 -0
- package/lib/chunk-L6UAP4KP.mjs +27 -0
- package/lib/chunk-L6UAP4KP.mjs.map +1 -0
- package/lib/{chunk-3QS3WKRC.mjs → chunk-LZOMFHX3.mjs} +9 -2
- package/lib/chunk-SYBADQXI.mjs +607 -0
- package/lib/chunk-SYBADQXI.mjs.map +1 -0
- package/lib/chunk-VXX4I3EF.mjs +19 -0
- package/lib/chunk-VXX4I3EF.mjs.map +1 -0
- package/lib/{chunk-36YCDLLA.mjs → chunk-VYDIGFIX.mjs} +75 -481
- package/lib/chunk-VYDIGFIX.mjs.map +1 -0
- package/lib/chunk-YU2HRNUP.mjs +33 -0
- package/lib/chunk-YU2HRNUP.mjs.map +1 -0
- package/lib/chunk-YZZDUJHI.mjs +37 -0
- package/lib/chunk-YZZDUJHI.mjs.map +1 -0
- package/lib/cors-options-lambda.handler.mjs +1 -1
- package/lib/data-store-postgres-replication.handler.mjs +1 -1
- package/lib/events-BfrkMoBD.d.mts +44 -0
- package/lib/events-BfrkMoBD.d.ts +44 -0
- package/lib/events-DPodvl07.d.mts +207 -0
- package/lib/events-DPodvl07.d.ts +207 -0
- package/lib/firehose-archive-transform.handler.mjs +1 -1
- package/lib/index.d.mts +417 -9
- package/lib/index.d.ts +663 -10
- package/lib/index.js +2398 -111
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +779 -104
- package/lib/index.mjs.map +1 -1
- package/lib/openhi-context-CaBH8SFo.d.mts +39 -0
- package/lib/openhi-context-CaBH8SFo.d.ts +39 -0
- package/lib/platform-deploy-bridge.handler.d.mts +14 -0
- package/lib/platform-deploy-bridge.handler.d.ts +14 -0
- package/lib/platform-deploy-bridge.handler.js +762 -0
- package/lib/platform-deploy-bridge.handler.js.map +1 -0
- package/lib/platform-deploy-bridge.handler.mjs +134 -0
- package/lib/platform-deploy-bridge.handler.mjs.map +1 -0
- package/lib/post-authentication.handler.mjs +1 -1
- package/lib/post-confirmation.handler.mjs +1 -1
- package/lib/pre-token-generation.handler.js +76 -31
- package/lib/pre-token-generation.handler.js.map +1 -1
- package/lib/pre-token-generation.handler.mjs +5 -3
- package/lib/pre-token-generation.handler.mjs.map +1 -1
- package/lib/provision-default-workspace.handler.js +86 -41
- package/lib/provision-default-workspace.handler.js.map +1 -1
- package/lib/provision-default-workspace.handler.mjs +6 -4
- package/lib/provision-default-workspace.handler.mjs.map +1 -1
- package/lib/rest-api-lambda.handler.js +114 -59
- package/lib/rest-api-lambda.handler.js.map +1 -1
- package/lib/rest-api-lambda.handler.mjs +40 -61
- package/lib/rest-api-lambda.handler.mjs.map +1 -1
- package/lib/seed-demo-data.handler.d.mts +107 -0
- package/lib/seed-demo-data.handler.d.ts +107 -0
- package/lib/seed-demo-data.handler.js +2037 -0
- package/lib/seed-demo-data.handler.js.map +1 -0
- package/lib/seed-demo-data.handler.mjs +23 -0
- package/lib/seed-demo-data.handler.mjs.map +1 -0
- package/lib/seed-system-data.handler.d.mts +64 -0
- package/lib/seed-system-data.handler.d.ts +64 -0
- package/lib/seed-system-data.handler.js +1631 -0
- package/lib/seed-system-data.handler.js.map +1 -0
- package/lib/seed-system-data.handler.mjs +135 -0
- package/lib/seed-system-data.handler.mjs.map +1 -0
- package/package.json +4 -2
- package/lib/chunk-36YCDLLA.mjs.map +0 -1
- /package/lib/{chunk-BXEG7IOZ.mjs.map → chunk-AO3E22CS.mjs.map} +0 -0
- /package/lib/{chunk-WNUH2WDZ.mjs.map → chunk-CHPEQRXU.mjs.map} +0 -0
- /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 `<
|
|
146
|
-
*
|
|
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: ["
|
|
159
|
-
template: "${
|
|
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
|
|
244
|
-
* `casing: "none"`
|
|
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: ["
|
|
257
|
-
template: "${
|
|
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
|
|
337
|
-
* `casing: "none"`
|
|
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: ["
|
|
350
|
-
template: "${
|
|
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
|
|
435
|
-
* `casing: "none"`
|
|
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: ["
|
|
448
|
-
template: "${
|
|
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
|
|
533
|
-
*
|
|
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: ["
|
|
546
|
-
template: "${
|
|
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
|
|
634
|
-
*
|
|
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: ["
|
|
647
|
-
template: "${
|
|
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 `<
|
|
753
|
-
* `casing: "none"`
|
|
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: ["
|
|
766
|
-
template: "${
|
|
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-
|
|
852
|
+
//# sourceMappingURL=chunk-VYDIGFIX.mjs.map
|