@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
@@ -0,0 +1,1631 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __commonJS = (cb, mod) => function __require() {
9
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
+ };
11
+ var __export = (target, all) => {
12
+ for (var name in all)
13
+ __defProp(target, name, { get: all[name], enumerable: true });
14
+ };
15
+ var __copyProps = (to, from, except, desc) => {
16
+ if (from && typeof from === "object" || typeof from === "function") {
17
+ for (let key of __getOwnPropNames(from))
18
+ if (!__hasOwnProp.call(to, key) && key !== except)
19
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
20
+ }
21
+ return to;
22
+ };
23
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
24
+ // If the importer is in node compatibility mode or this is not an ESM
25
+ // file that has been converted to a CommonJS file using a Babel-
26
+ // compatible transform (i.e. "__esModule" has not been set), then set
27
+ // "default" to the CommonJS "module.exports" for node compatibility.
28
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
29
+ mod
30
+ ));
31
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
+
33
+ // ../workflows/lib/envelope-version.js
34
+ var require_envelope_version = __commonJS({
35
+ "../workflows/lib/envelope-version.js"(exports2) {
36
+ "use strict";
37
+ Object.defineProperty(exports2, "__esModule", { value: true });
38
+ exports2.ENVELOPE_VERSION = void 0;
39
+ exports2.isSupportedEnvelopeVersion = isSupportedEnvelopeVersion;
40
+ exports2.ENVELOPE_VERSION = "1.0";
41
+ var ENVELOPE_VERSION_PATTERN = /^\d+\.\d+$/;
42
+ var MIN_SUPPORTED_MAJOR = 1;
43
+ var MAX_SUPPORTED_MAJOR = 1;
44
+ function isSupportedEnvelopeVersion(version) {
45
+ if (!ENVELOPE_VERSION_PATTERN.test(version)) {
46
+ return false;
47
+ }
48
+ const major = Number.parseInt(version.split(".")[0], 10);
49
+ return major >= MIN_SUPPORTED_MAJOR && major <= MAX_SUPPORTED_MAJOR;
50
+ }
51
+ }
52
+ });
53
+
54
+ // ../workflows/lib/envelope.js
55
+ var require_envelope = __commonJS({
56
+ "../workflows/lib/envelope.js"(exports2) {
57
+ "use strict";
58
+ Object.defineProperty(exports2, "__esModule", { value: true });
59
+ exports2.MissingActorContextError = void 0;
60
+ exports2.isWorkflowUserActor = isWorkflowUserActor;
61
+ exports2.isWorkflowSystemActor = isWorkflowSystemActor;
62
+ exports2.workflowUserActorFromClaims = workflowUserActorFromClaims;
63
+ function isWorkflowUserActor(actor) {
64
+ return actor.ohi_uid !== void 0;
65
+ }
66
+ function isWorkflowSystemActor(actor) {
67
+ return actor.system !== void 0;
68
+ }
69
+ function workflowUserActorFromClaims(claims) {
70
+ if (claims.ohi_tid === void 0 || claims.ohi_wid === void 0) {
71
+ throw new MissingActorContextError("workflowUserActorFromClaims: ohi_tid and ohi_wid are required on the workflow user-actor; the caller's JWT is missing one or both. Use a system-actor for pre-provisioning bootstrap workflows.");
72
+ }
73
+ return {
74
+ ohi_tid: claims.ohi_tid,
75
+ ohi_wid: claims.ohi_wid,
76
+ ohi_uid: claims.ohi_uid,
77
+ ohi_uname: claims.ohi_uname
78
+ };
79
+ }
80
+ var MissingActorContextError = class extends Error {
81
+ /** @param message - human-readable description of the missing claim. */
82
+ constructor(message) {
83
+ super(message);
84
+ this.name = "MissingActorContextError";
85
+ }
86
+ };
87
+ exports2.MissingActorContextError = MissingActorContextError;
88
+ }
89
+ });
90
+
91
+ // ../workflows/lib/sources.js
92
+ var require_sources = __commonJS({
93
+ "../workflows/lib/sources.js"(exports2) {
94
+ "use strict";
95
+ Object.defineProperty(exports2, "__esModule", { value: true });
96
+ exports2.DEFAULT_BUS_NAME_BY_SOURCE = exports2.OPENHI_OPS_SOURCE = exports2.OPENHI_DATA_SOURCE = exports2.OPENHI_CONTROL_SOURCE = void 0;
97
+ exports2.OPENHI_CONTROL_SOURCE = "openhi.control";
98
+ exports2.OPENHI_DATA_SOURCE = "openhi.data";
99
+ exports2.OPENHI_OPS_SOURCE = "openhi.ops";
100
+ exports2.DEFAULT_BUS_NAME_BY_SOURCE = {
101
+ [exports2.OPENHI_CONTROL_SOURCE]: "openhi-control-event-bus",
102
+ [exports2.OPENHI_DATA_SOURCE]: "openhi-data-event-bus",
103
+ [exports2.OPENHI_OPS_SOURCE]: "openhi-ops-event-bus"
104
+ };
105
+ }
106
+ });
107
+
108
+ // ../workflows/lib/detail-types/registry.js
109
+ var require_registry = __commonJS({
110
+ "../workflows/lib/detail-types/registry.js"(exports2) {
111
+ "use strict";
112
+ Object.defineProperty(exports2, "__esModule", { value: true });
113
+ exports2.InvalidDetailTypeRegistrationError = void 0;
114
+ exports2.defineDetailType = defineDetailType;
115
+ exports2.isWellFormedDetailType = isWellFormedDetailType;
116
+ function defineDetailType(entry) {
117
+ if (!isWellFormedDetailType(entry.detailType)) {
118
+ throw new InvalidDetailTypeRegistrationError(`Detail-type "${entry.detailType}" does not match the platform-wide format <area>.<event>.v<integer>. See TR-016 \xA7Open Items #2.`);
119
+ }
120
+ return entry;
121
+ }
122
+ var DETAIL_TYPE_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*\.[a-z0-9]+(?:-[a-z0-9]+)*\.v\d+$/;
123
+ function isWellFormedDetailType(detailType) {
124
+ return DETAIL_TYPE_PATTERN.test(detailType);
125
+ }
126
+ var InvalidDetailTypeRegistrationError = class extends Error {
127
+ /** @param message - human-readable description of the violation. */
128
+ constructor(message) {
129
+ super(message);
130
+ this.name = "InvalidDetailTypeRegistrationError";
131
+ }
132
+ };
133
+ exports2.InvalidDetailTypeRegistrationError = InvalidDetailTypeRegistrationError;
134
+ }
135
+ });
136
+
137
+ // ../workflows/lib/detail-types/platform.js
138
+ var require_platform = __commonJS({
139
+ "../workflows/lib/detail-types/platform.js"(exports2) {
140
+ "use strict";
141
+ Object.defineProperty(exports2, "__esModule", { value: true });
142
+ exports2.PlatformSystemDataSeededV1 = exports2.PlatformDeploymentCompletedV1 = void 0;
143
+ var sources_1 = require_sources();
144
+ var registry_1 = require_registry();
145
+ exports2.PlatformDeploymentCompletedV1 = (0, registry_1.defineDetailType)({
146
+ detailType: "platform.deployment-completed.v1",
147
+ source: sources_1.OPENHI_CONTROL_SOURCE,
148
+ dedupRequired: true
149
+ });
150
+ exports2.PlatformSystemDataSeededV1 = (0, registry_1.defineDetailType)({
151
+ detailType: "platform.system-data-seeded.v1",
152
+ source: sources_1.OPENHI_CONTROL_SOURCE,
153
+ dedupRequired: true
154
+ });
155
+ }
156
+ });
157
+
158
+ // ../workflows/lib/detail-types/index.js
159
+ var require_detail_types = __commonJS({
160
+ "../workflows/lib/detail-types/index.js"(exports2) {
161
+ "use strict";
162
+ var __createBinding = exports2 && exports2.__createBinding || (Object.create ? (function(o, m, k, k2) {
163
+ if (k2 === void 0) k2 = k;
164
+ var desc = Object.getOwnPropertyDescriptor(m, k);
165
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
166
+ desc = { enumerable: true, get: function() {
167
+ return m[k];
168
+ } };
169
+ }
170
+ Object.defineProperty(o, k2, desc);
171
+ }) : (function(o, m, k, k2) {
172
+ if (k2 === void 0) k2 = k;
173
+ o[k2] = m[k];
174
+ }));
175
+ var __exportStar = exports2 && exports2.__exportStar || function(m, exports3) {
176
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports3, p)) __createBinding(exports3, m, p);
177
+ };
178
+ Object.defineProperty(exports2, "__esModule", { value: true });
179
+ __exportStar(require_platform(), exports2);
180
+ __exportStar(require_registry(), exports2);
181
+ }
182
+ });
183
+
184
+ // ../workflows/lib/publisher.js
185
+ var require_publisher = __commonJS({
186
+ "../workflows/lib/publisher.js"(exports2) {
187
+ "use strict";
188
+ Object.defineProperty(exports2, "__esModule", { value: true });
189
+ exports2.WorkflowPublishError = void 0;
190
+ exports2.workflowsClient = workflowsClient;
191
+ exports2.publishWorkflowEvent = publishWorkflowEvent2;
192
+ var node_crypto_1 = require("crypto");
193
+ var client_eventbridge_1 = require("@aws-sdk/client-eventbridge");
194
+ var envelope_version_1 = require_envelope_version();
195
+ var sources_1 = require_sources();
196
+ function workflowsClient(bridge, options = {}) {
197
+ return {
198
+ publish: (entry, payload, ctx) => publishWorkflowEvent2(bridge, entry, payload, ctx, options)
199
+ };
200
+ }
201
+ async function publishWorkflowEvent2(bridge, entry, payload, ctx, options = {}) {
202
+ const eventIdGenerator = options.eventIdGenerator ?? (() => (0, node_crypto_1.randomUUID)());
203
+ const correlationIdGenerator = options.correlationIdGenerator ?? (() => (0, node_crypto_1.randomUUID)());
204
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
205
+ const envelope = {
206
+ eventId: eventIdGenerator(),
207
+ attempt: 1,
208
+ correlationId: ctx.correlationId ?? correlationIdGenerator(),
209
+ causationId: ctx.causationId ?? null,
210
+ actor: ctx.actor,
211
+ occurredAt: now().toISOString(),
212
+ envelopeVersion: envelope_version_1.ENVELOPE_VERSION,
213
+ payload
214
+ };
215
+ const busName = options.busNameByPlane?.[entry.source] ?? sources_1.DEFAULT_BUS_NAME_BY_SOURCE[entry.source];
216
+ const result = await bridge.send(new client_eventbridge_1.PutEventsCommand({
217
+ Entries: [
218
+ {
219
+ EventBusName: busName,
220
+ Source: entry.source,
221
+ DetailType: entry.detailType,
222
+ Detail: JSON.stringify(envelope)
223
+ }
224
+ ]
225
+ }));
226
+ if ((result.FailedEntryCount ?? 0) > 0) {
227
+ const first = result.Entries?.[0];
228
+ throw new WorkflowPublishError(`EventBridge rejected ${entry.detailType} publish on bus ${busName}: ${first?.ErrorCode ?? "unknown"} \u2014 ${first?.ErrorMessage ?? "no error message"}`);
229
+ }
230
+ return { eventId: envelope.eventId };
231
+ }
232
+ var WorkflowPublishError = class extends Error {
233
+ /** @param message - human-readable description of the failed publish. */
234
+ constructor(message) {
235
+ super(message);
236
+ this.name = "WorkflowPublishError";
237
+ }
238
+ };
239
+ exports2.WorkflowPublishError = WorkflowPublishError;
240
+ }
241
+ });
242
+
243
+ // ../workflows/lib/consumer.js
244
+ var require_consumer = __commonJS({
245
+ "../workflows/lib/consumer.js"(exports2) {
246
+ "use strict";
247
+ Object.defineProperty(exports2, "__esModule", { value: true });
248
+ exports2.UnsupportedEnvelopeVersionError = exports2.InvalidWorkflowEventError = void 0;
249
+ exports2.parseWorkflowEvent = parseWorkflowEvent2;
250
+ var envelope_version_1 = require_envelope_version();
251
+ function parseWorkflowEvent2(event, expected) {
252
+ if (event.source !== expected.source) {
253
+ throw new InvalidWorkflowEventError(`EventBridge source "${event.source}" does not match expected detail-type's source "${expected.source}".`);
254
+ }
255
+ if (event["detail-type"] !== expected.detailType) {
256
+ throw new InvalidWorkflowEventError(`EventBridge detail-type "${event["detail-type"]}" does not match expected "${expected.detailType}".`);
257
+ }
258
+ const candidate = asEnvelopeCandidate(event.detail);
259
+ if (!(0, envelope_version_1.isSupportedEnvelopeVersion)(candidate.envelopeVersion)) {
260
+ throw new UnsupportedEnvelopeVersionError(`Envelope version "${candidate.envelopeVersion}" is outside the SDK's supported range.`);
261
+ }
262
+ const envelope = {
263
+ eventId: candidate.eventId,
264
+ attempt: candidate.attempt,
265
+ correlationId: candidate.correlationId,
266
+ causationId: candidate.causationId,
267
+ actor: candidate.actor,
268
+ occurredAt: candidate.occurredAt,
269
+ envelopeVersion: candidate.envelopeVersion,
270
+ payload: candidate.payload
271
+ };
272
+ return {
273
+ envelope,
274
+ dedupKey: { eventId: envelope.eventId, attempt: envelope.attempt }
275
+ };
276
+ }
277
+ function asEnvelopeCandidate(detail) {
278
+ if (detail === null || typeof detail !== "object") {
279
+ throw new InvalidWorkflowEventError("EventBridge detail is not a non-null object.");
280
+ }
281
+ const obj = detail;
282
+ assertString(obj, "eventId");
283
+ assertPositiveInteger(obj, "attempt");
284
+ assertString(obj, "correlationId");
285
+ assertCausationId(obj);
286
+ assertActor(obj);
287
+ assertString(obj, "occurredAt");
288
+ assertString(obj, "envelopeVersion");
289
+ if (!("payload" in obj)) {
290
+ throw new InvalidWorkflowEventError("Envelope is missing required field: payload.");
291
+ }
292
+ return obj;
293
+ }
294
+ function assertString(obj, field) {
295
+ const value = obj[field];
296
+ if (typeof value !== "string" || value.length === 0) {
297
+ throw new InvalidWorkflowEventError(`Envelope field "${field}" must be a non-empty string.`);
298
+ }
299
+ }
300
+ function assertPositiveInteger(obj, field) {
301
+ const value = obj[field];
302
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 1) {
303
+ throw new InvalidWorkflowEventError(`Envelope field "${field}" must be a 1-indexed integer.`);
304
+ }
305
+ }
306
+ function assertCausationId(obj) {
307
+ if (!("causationId" in obj)) {
308
+ throw new InvalidWorkflowEventError("Envelope is missing required field: causationId.");
309
+ }
310
+ const value = obj.causationId;
311
+ if (value !== null && (typeof value !== "string" || value.length === 0)) {
312
+ throw new InvalidWorkflowEventError('Envelope field "causationId" must be a non-empty string or null.');
313
+ }
314
+ }
315
+ function assertActor(obj) {
316
+ const actor = obj.actor;
317
+ if (actor === null || typeof actor !== "object") {
318
+ throw new InvalidWorkflowEventError('Envelope field "actor" must be an object.');
319
+ }
320
+ const actorObj = actor;
321
+ const isUserActor = typeof actorObj.ohi_uid === "string" && typeof actorObj.ohi_uname === "string" && typeof actorObj.ohi_tid === "string" && typeof actorObj.ohi_wid === "string";
322
+ const isSystemActor = typeof actorObj.system === "string";
323
+ if (!isUserActor && !isSystemActor) {
324
+ throw new InvalidWorkflowEventError('Envelope field "actor" must be either a user-actor (ohi_tid, ohi_wid, ohi_uid, ohi_uname) or a system-actor ({ system: string }).');
325
+ }
326
+ }
327
+ var InvalidWorkflowEventError = class extends Error {
328
+ /** @param message - human-readable description of the validation failure. */
329
+ constructor(message) {
330
+ super(message);
331
+ this.name = "InvalidWorkflowEventError";
332
+ }
333
+ };
334
+ exports2.InvalidWorkflowEventError = InvalidWorkflowEventError;
335
+ var UnsupportedEnvelopeVersionError = class extends Error {
336
+ /** @param message - human-readable description of the unsupported version. */
337
+ constructor(message) {
338
+ super(message);
339
+ this.name = "UnsupportedEnvelopeVersionError";
340
+ }
341
+ };
342
+ exports2.UnsupportedEnvelopeVersionError = UnsupportedEnvelopeVersionError;
343
+ }
344
+ });
345
+
346
+ // ../workflows/lib/dedup/env.js
347
+ var require_env = __commonJS({
348
+ "../workflows/lib/dedup/env.js"(exports2) {
349
+ "use strict";
350
+ Object.defineProperty(exports2, "__esModule", { value: true });
351
+ exports2.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH = exports2.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS = exports2.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR = void 0;
352
+ exports2.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR = "OPENHI_WORKFLOW_DEDUP_TABLE_NAME";
353
+ exports2.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS = 14 * 24 * 60 * 60;
354
+ exports2.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH = 64;
355
+ }
356
+ });
357
+
358
+ // ../workflows/lib/dedup/workflow-dedup-client.js
359
+ var require_workflow_dedup_client = __commonJS({
360
+ "../workflows/lib/dedup/workflow-dedup-client.js"(exports2) {
361
+ "use strict";
362
+ Object.defineProperty(exports2, "__esModule", { value: true });
363
+ exports2.WorkflowDedupInvalidInputError = exports2.WorkflowDedupTableNameMissingError = void 0;
364
+ exports2.workflowDedupClient = workflowDedupClient2;
365
+ exports2.recordIfAbsent = recordIfAbsent;
366
+ exports2.markFailed = markFailed;
367
+ exports2.encodeSortKey = encodeSortKey;
368
+ var client_dynamodb_1 = require("@aws-sdk/client-dynamodb");
369
+ var env_1 = require_env();
370
+ function workflowDedupClient2(dynamodb, options = {}) {
371
+ return {
372
+ recordIfAbsent: (input) => recordIfAbsent(dynamodb, input, options),
373
+ markFailed: (input) => markFailed(dynamodb, input, options)
374
+ };
375
+ }
376
+ async function recordIfAbsent(dynamodb, input, options = {}) {
377
+ assertConsumerName(input.consumerName);
378
+ assertPositiveInteger(input.attempt, "attempt");
379
+ const ttlSeconds = input.ttlSeconds ?? options.defaultTtlSeconds ?? env_1.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS;
380
+ if (!Number.isInteger(ttlSeconds) || ttlSeconds <= 0) {
381
+ throw new WorkflowDedupInvalidInputError(`ttlSeconds must be a positive integer; got ${ttlSeconds}.`);
382
+ }
383
+ const tableName = resolveTableName(options.tableName);
384
+ const now = (options.now ?? defaultNow)();
385
+ const sk = encodeSortKey(input.eventId, input.attempt);
386
+ const expiresAt = Math.floor(now.getTime() / 1e3) + ttlSeconds;
387
+ try {
388
+ await dynamodb.send(new client_dynamodb_1.PutItemCommand({
389
+ TableName: tableName,
390
+ Item: {
391
+ consumerName: { S: input.consumerName },
392
+ sk: { S: sk },
393
+ eventId: { S: input.eventId },
394
+ attempt: { N: String(input.attempt) },
395
+ recordedAt: { S: now.toISOString() },
396
+ expiresAt: { N: String(expiresAt) }
397
+ },
398
+ ConditionExpression: "attribute_not_exists(consumerName) AND attribute_not_exists(sk)"
399
+ }));
400
+ return { recorded: true };
401
+ } catch (err) {
402
+ if (err instanceof client_dynamodb_1.ConditionalCheckFailedException) {
403
+ return { recorded: false, alreadyProcessed: true };
404
+ }
405
+ throw err;
406
+ }
407
+ }
408
+ async function markFailed(dynamodb, input, options = {}) {
409
+ assertConsumerName(input.consumerName);
410
+ assertPositiveInteger(input.attempt, "attempt");
411
+ if (input.reason.length === 0) {
412
+ throw new WorkflowDedupInvalidInputError("reason must be non-empty.");
413
+ }
414
+ const tableName = resolveTableName(options.tableName);
415
+ const now = (options.now ?? defaultNow)();
416
+ const sk = encodeSortKey(input.eventId, input.attempt);
417
+ await dynamodb.send(new client_dynamodb_1.UpdateItemCommand({
418
+ TableName: tableName,
419
+ Key: {
420
+ consumerName: { S: input.consumerName },
421
+ sk: { S: sk }
422
+ },
423
+ UpdateExpression: "SET #failed = :failed, #failureReason = :reason, #failedAt = :failedAt",
424
+ ExpressionAttributeNames: {
425
+ "#failed": "failed",
426
+ "#failureReason": "failureReason",
427
+ "#failedAt": "failedAt"
428
+ },
429
+ ExpressionAttributeValues: {
430
+ ":failed": { BOOL: true },
431
+ ":reason": { S: input.reason },
432
+ ":failedAt": { S: now.toISOString() }
433
+ }
434
+ }));
435
+ }
436
+ function encodeSortKey(eventId, attempt) {
437
+ if (eventId.length === 0) {
438
+ throw new WorkflowDedupInvalidInputError("eventId must be non-empty.");
439
+ }
440
+ return `${eventId}#${attempt}`;
441
+ }
442
+ function resolveTableName(explicit) {
443
+ const name = explicit ?? process.env[env_1.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR];
444
+ if (!name) {
445
+ throw new WorkflowDedupTableNameMissingError(`Workflow dedup table name not set. Pass options.tableName or set ${env_1.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR}.`);
446
+ }
447
+ return name;
448
+ }
449
+ function assertConsumerName(consumerName) {
450
+ if (consumerName.length === 0) {
451
+ throw new WorkflowDedupInvalidInputError("consumerName must be non-empty.");
452
+ }
453
+ if (consumerName.length > env_1.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH) {
454
+ throw new WorkflowDedupInvalidInputError(`consumerName must be \u2264${env_1.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH} chars; got ${consumerName.length}.`);
455
+ }
456
+ if (/\s/.test(consumerName)) {
457
+ throw new WorkflowDedupInvalidInputError("consumerName must not contain whitespace.");
458
+ }
459
+ }
460
+ function assertPositiveInteger(value, field) {
461
+ if (!Number.isInteger(value) || value < 1) {
462
+ throw new WorkflowDedupInvalidInputError(`${field} must be a 1-indexed integer; got ${value}.`);
463
+ }
464
+ }
465
+ function defaultNow() {
466
+ return /* @__PURE__ */ new Date();
467
+ }
468
+ var WorkflowDedupTableNameMissingError = class extends Error {
469
+ /** @param message - human-readable description. */
470
+ constructor(message) {
471
+ super(message);
472
+ this.name = "WorkflowDedupTableNameMissingError";
473
+ }
474
+ };
475
+ exports2.WorkflowDedupTableNameMissingError = WorkflowDedupTableNameMissingError;
476
+ var WorkflowDedupInvalidInputError = class extends Error {
477
+ /** @param message - human-readable description. */
478
+ constructor(message) {
479
+ super(message);
480
+ this.name = "WorkflowDedupInvalidInputError";
481
+ }
482
+ };
483
+ exports2.WorkflowDedupInvalidInputError = WorkflowDedupInvalidInputError;
484
+ }
485
+ });
486
+
487
+ // ../workflows/lib/dedup/index.js
488
+ var require_dedup = __commonJS({
489
+ "../workflows/lib/dedup/index.js"(exports2) {
490
+ "use strict";
491
+ Object.defineProperty(exports2, "__esModule", { value: true });
492
+ exports2.workflowDedupClient = exports2.recordIfAbsent = exports2.markFailed = exports2.encodeSortKey = exports2.WorkflowDedupTableNameMissingError = exports2.WorkflowDedupInvalidInputError = exports2.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR = exports2.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH = exports2.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS = void 0;
493
+ var env_1 = require_env();
494
+ Object.defineProperty(exports2, "WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS", { enumerable: true, get: function() {
495
+ return env_1.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS;
496
+ } });
497
+ Object.defineProperty(exports2, "WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH", { enumerable: true, get: function() {
498
+ return env_1.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH;
499
+ } });
500
+ Object.defineProperty(exports2, "WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR", { enumerable: true, get: function() {
501
+ return env_1.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR;
502
+ } });
503
+ var workflow_dedup_client_1 = require_workflow_dedup_client();
504
+ Object.defineProperty(exports2, "WorkflowDedupInvalidInputError", { enumerable: true, get: function() {
505
+ return workflow_dedup_client_1.WorkflowDedupInvalidInputError;
506
+ } });
507
+ Object.defineProperty(exports2, "WorkflowDedupTableNameMissingError", { enumerable: true, get: function() {
508
+ return workflow_dedup_client_1.WorkflowDedupTableNameMissingError;
509
+ } });
510
+ Object.defineProperty(exports2, "encodeSortKey", { enumerable: true, get: function() {
511
+ return workflow_dedup_client_1.encodeSortKey;
512
+ } });
513
+ Object.defineProperty(exports2, "markFailed", { enumerable: true, get: function() {
514
+ return workflow_dedup_client_1.markFailed;
515
+ } });
516
+ Object.defineProperty(exports2, "recordIfAbsent", { enumerable: true, get: function() {
517
+ return workflow_dedup_client_1.recordIfAbsent;
518
+ } });
519
+ Object.defineProperty(exports2, "workflowDedupClient", { enumerable: true, get: function() {
520
+ return workflow_dedup_client_1.workflowDedupClient;
521
+ } });
522
+ }
523
+ });
524
+
525
+ // ../workflows/lib/index.js
526
+ var require_lib = __commonJS({
527
+ "../workflows/lib/index.js"(exports2) {
528
+ "use strict";
529
+ Object.defineProperty(exports2, "__esModule", { value: true });
530
+ exports2.workflowDedupClient = exports2.recordIfAbsent = exports2.markFailed = exports2.encodeSortKey = exports2.WorkflowDedupTableNameMissingError = exports2.WorkflowDedupInvalidInputError = exports2.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR = exports2.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH = exports2.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS = exports2.parseWorkflowEvent = exports2.UnsupportedEnvelopeVersionError = exports2.InvalidWorkflowEventError = exports2.workflowsClient = exports2.publishWorkflowEvent = exports2.WorkflowPublishError = exports2.isWellFormedDetailType = exports2.defineDetailType = exports2.PlatformSystemDataSeededV1 = exports2.PlatformDeploymentCompletedV1 = exports2.InvalidDetailTypeRegistrationError = exports2.OPENHI_OPS_SOURCE = exports2.OPENHI_DATA_SOURCE = exports2.OPENHI_CONTROL_SOURCE = exports2.DEFAULT_BUS_NAME_BY_SOURCE = exports2.workflowUserActorFromClaims = exports2.isWorkflowUserActor = exports2.isWorkflowSystemActor = exports2.MissingActorContextError = exports2.isSupportedEnvelopeVersion = exports2.ENVELOPE_VERSION = void 0;
531
+ var envelope_version_1 = require_envelope_version();
532
+ Object.defineProperty(exports2, "ENVELOPE_VERSION", { enumerable: true, get: function() {
533
+ return envelope_version_1.ENVELOPE_VERSION;
534
+ } });
535
+ Object.defineProperty(exports2, "isSupportedEnvelopeVersion", { enumerable: true, get: function() {
536
+ return envelope_version_1.isSupportedEnvelopeVersion;
537
+ } });
538
+ var envelope_1 = require_envelope();
539
+ Object.defineProperty(exports2, "MissingActorContextError", { enumerable: true, get: function() {
540
+ return envelope_1.MissingActorContextError;
541
+ } });
542
+ Object.defineProperty(exports2, "isWorkflowSystemActor", { enumerable: true, get: function() {
543
+ return envelope_1.isWorkflowSystemActor;
544
+ } });
545
+ Object.defineProperty(exports2, "isWorkflowUserActor", { enumerable: true, get: function() {
546
+ return envelope_1.isWorkflowUserActor;
547
+ } });
548
+ Object.defineProperty(exports2, "workflowUserActorFromClaims", { enumerable: true, get: function() {
549
+ return envelope_1.workflowUserActorFromClaims;
550
+ } });
551
+ var sources_1 = require_sources();
552
+ Object.defineProperty(exports2, "DEFAULT_BUS_NAME_BY_SOURCE", { enumerable: true, get: function() {
553
+ return sources_1.DEFAULT_BUS_NAME_BY_SOURCE;
554
+ } });
555
+ Object.defineProperty(exports2, "OPENHI_CONTROL_SOURCE", { enumerable: true, get: function() {
556
+ return sources_1.OPENHI_CONTROL_SOURCE;
557
+ } });
558
+ Object.defineProperty(exports2, "OPENHI_DATA_SOURCE", { enumerable: true, get: function() {
559
+ return sources_1.OPENHI_DATA_SOURCE;
560
+ } });
561
+ Object.defineProperty(exports2, "OPENHI_OPS_SOURCE", { enumerable: true, get: function() {
562
+ return sources_1.OPENHI_OPS_SOURCE;
563
+ } });
564
+ var detail_types_1 = require_detail_types();
565
+ Object.defineProperty(exports2, "InvalidDetailTypeRegistrationError", { enumerable: true, get: function() {
566
+ return detail_types_1.InvalidDetailTypeRegistrationError;
567
+ } });
568
+ Object.defineProperty(exports2, "PlatformDeploymentCompletedV1", { enumerable: true, get: function() {
569
+ return detail_types_1.PlatformDeploymentCompletedV1;
570
+ } });
571
+ Object.defineProperty(exports2, "PlatformSystemDataSeededV1", { enumerable: true, get: function() {
572
+ return detail_types_1.PlatformSystemDataSeededV1;
573
+ } });
574
+ Object.defineProperty(exports2, "defineDetailType", { enumerable: true, get: function() {
575
+ return detail_types_1.defineDetailType;
576
+ } });
577
+ Object.defineProperty(exports2, "isWellFormedDetailType", { enumerable: true, get: function() {
578
+ return detail_types_1.isWellFormedDetailType;
579
+ } });
580
+ var publisher_1 = require_publisher();
581
+ Object.defineProperty(exports2, "WorkflowPublishError", { enumerable: true, get: function() {
582
+ return publisher_1.WorkflowPublishError;
583
+ } });
584
+ Object.defineProperty(exports2, "publishWorkflowEvent", { enumerable: true, get: function() {
585
+ return publisher_1.publishWorkflowEvent;
586
+ } });
587
+ Object.defineProperty(exports2, "workflowsClient", { enumerable: true, get: function() {
588
+ return publisher_1.workflowsClient;
589
+ } });
590
+ var consumer_1 = require_consumer();
591
+ Object.defineProperty(exports2, "InvalidWorkflowEventError", { enumerable: true, get: function() {
592
+ return consumer_1.InvalidWorkflowEventError;
593
+ } });
594
+ Object.defineProperty(exports2, "UnsupportedEnvelopeVersionError", { enumerable: true, get: function() {
595
+ return consumer_1.UnsupportedEnvelopeVersionError;
596
+ } });
597
+ Object.defineProperty(exports2, "parseWorkflowEvent", { enumerable: true, get: function() {
598
+ return consumer_1.parseWorkflowEvent;
599
+ } });
600
+ var dedup_1 = require_dedup();
601
+ Object.defineProperty(exports2, "WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS", { enumerable: true, get: function() {
602
+ return dedup_1.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS;
603
+ } });
604
+ Object.defineProperty(exports2, "WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH", { enumerable: true, get: function() {
605
+ return dedup_1.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH;
606
+ } });
607
+ Object.defineProperty(exports2, "WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR", { enumerable: true, get: function() {
608
+ return dedup_1.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR;
609
+ } });
610
+ Object.defineProperty(exports2, "WorkflowDedupInvalidInputError", { enumerable: true, get: function() {
611
+ return dedup_1.WorkflowDedupInvalidInputError;
612
+ } });
613
+ Object.defineProperty(exports2, "WorkflowDedupTableNameMissingError", { enumerable: true, get: function() {
614
+ return dedup_1.WorkflowDedupTableNameMissingError;
615
+ } });
616
+ Object.defineProperty(exports2, "encodeSortKey", { enumerable: true, get: function() {
617
+ return dedup_1.encodeSortKey;
618
+ } });
619
+ Object.defineProperty(exports2, "markFailed", { enumerable: true, get: function() {
620
+ return dedup_1.markFailed;
621
+ } });
622
+ Object.defineProperty(exports2, "recordIfAbsent", { enumerable: true, get: function() {
623
+ return dedup_1.recordIfAbsent;
624
+ } });
625
+ Object.defineProperty(exports2, "workflowDedupClient", { enumerable: true, get: function() {
626
+ return dedup_1.workflowDedupClient;
627
+ } });
628
+ }
629
+ });
630
+
631
+ // src/workflows/control-plane/seed-system-data/seed-system-data.handler.ts
632
+ var seed_system_data_handler_exports = {};
633
+ __export(seed_system_data_handler_exports, {
634
+ handler: () => handler,
635
+ runSeedSystemData: () => runSeedSystemData
636
+ });
637
+ module.exports = __toCommonJS(seed_system_data_handler_exports);
638
+ var import_client_dynamodb2 = require("@aws-sdk/client-dynamodb");
639
+ var import_client_eventbridge = require("@aws-sdk/client-eventbridge");
640
+ var import_types3 = require("@openhi/types");
641
+ var import_workflows2 = __toESM(require_lib());
642
+
643
+ // src/workflows/control-plane/seed-system-data/events.ts
644
+ var import_workflows = __toESM(require_lib());
645
+ var SEED_SYSTEM_DATA_CONSUMER_NAME = "seed-system-data";
646
+ var SEED_SYSTEM_DATA_ACTOR_SYSTEM = "seed-system-data";
647
+ var SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR = "CONTROL_EVENT_BUS_NAME";
648
+
649
+ // src/data/operations/control/role/role-create-operation.ts
650
+ var import_types2 = require("@openhi/types");
651
+
652
+ // src/data/dynamo/dynamo-control-service.ts
653
+ var import_electrodb8 = require("electrodb");
654
+
655
+ // src/data/dynamo/dynamo-client.ts
656
+ var import_client_dynamodb = require("@aws-sdk/client-dynamodb");
657
+ var defaultTableName = process.env.DYNAMO_TABLE_NAME ?? "jesttesttable";
658
+ var dynamoClient = new import_client_dynamodb.DynamoDBClient({
659
+ ...process.env.MOCK_DYNAMODB_ENDPOINT && {
660
+ endpoint: process.env.MOCK_DYNAMODB_ENDPOINT,
661
+ sslEnabled: false,
662
+ region: "local"
663
+ }
664
+ });
665
+
666
+ // src/data/dynamo/entities/control/configuration-entity.ts
667
+ var import_electrodb = require("electrodb");
668
+
669
+ // src/data/dynamo/entities/control/control-entity-common.ts
670
+ var import_types = require("@openhi/types");
671
+
672
+ // src/data/dynamo/shard.ts
673
+ var SHARD_COUNT = 4;
674
+ function computeShard(id) {
675
+ let hash = 2166136261;
676
+ for (let i = 0; i < id.length; i++) {
677
+ hash ^= id.charCodeAt(i);
678
+ hash = Math.imul(hash, 16777619);
679
+ }
680
+ return (hash >>> 0) % SHARD_COUNT;
681
+ }
682
+
683
+ // src/data/dynamo/entities/control/control-entity-common.ts
684
+ var gsi1ShardAttribute = {
685
+ type: "string",
686
+ watch: ["id"],
687
+ set: (_val, item) => {
688
+ if (typeof item?.id !== "string" || item.id.length === 0) {
689
+ return void 0;
690
+ }
691
+ return String(computeShard(item.id));
692
+ }
693
+ };
694
+ var gsi1skAttribute = {
695
+ type: "string",
696
+ watch: ["resource", "lastUpdated", "id"],
697
+ set: (_val, item) => {
698
+ const id = typeof item?.id === "string" ? item.id : "";
699
+ const lastUpdated = typeof item?.lastUpdated === "string" ? item.lastUpdated : "";
700
+ const fallback = `${lastUpdated}#${id}`;
701
+ if (typeof item?.resource !== "string" || item.resource.length === 0) {
702
+ return fallback;
703
+ }
704
+ let parsed;
705
+ try {
706
+ parsed = JSON.parse(item.resource);
707
+ } catch {
708
+ return fallback;
709
+ }
710
+ if (!parsed || typeof parsed !== "object") return fallback;
711
+ const resourceType = parsed.resourceType;
712
+ if (typeof resourceType !== "string") return fallback;
713
+ const label = (0, import_types.extractLabel)(parsed);
714
+ return label !== void 0 ? `${label}#${id}` : fallback;
715
+ }
716
+ };
717
+
718
+ // src/data/dynamo/entities/control/configuration-entity.ts
719
+ var ConfigurationEntity = new import_electrodb.Entity({
720
+ model: {
721
+ entity: "configuration",
722
+ service: "control",
723
+ version: "01"
724
+ },
725
+ attributes: {
726
+ /** Sort key. "CURRENT" for current version; version history in S3. */
727
+ sk: {
728
+ type: "string",
729
+ required: true,
730
+ default: "CURRENT"
731
+ },
732
+ /** Tenant scope. Use "BASELINE" when the config is baseline default (no tenant). */
733
+ tenantId: {
734
+ type: "string",
735
+ required: true,
736
+ default: "BASELINE"
737
+ },
738
+ /** Workspace scope. Use "-" when absent. */
739
+ workspaceId: {
740
+ type: "string",
741
+ required: true,
742
+ default: "-"
743
+ },
744
+ /** User scope. Use "-" when absent. */
745
+ userId: {
746
+ type: "string",
747
+ required: true,
748
+ default: "-"
749
+ },
750
+ /** Role scope. Use "-" when absent. */
751
+ roleId: {
752
+ type: "string",
753
+ required: true,
754
+ default: "-"
755
+ },
756
+ /** Config type (category), e.g. endpoints, branding, display. */
757
+ key: {
758
+ type: "string",
759
+ required: true
760
+ },
761
+ /** FHIR Resource.id; logical id in URL and for the Configuration resource. */
762
+ id: {
763
+ type: "string",
764
+ required: true
765
+ },
766
+ /** Payload as JSON string. JSON.stringify(resource) on write; JSON.parse(item.resource) on read. */
767
+ resource: {
768
+ type: "string",
769
+ required: true
770
+ },
771
+ /**
772
+ * Summary projection (key display fields as JSON string: id, key, status).
773
+ * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
774
+ */
775
+ summary: {
776
+ type: "string",
777
+ required: true
778
+ },
779
+ /** Version id (e.g. ULID). Tracks current version; S3 history key. */
780
+ vid: {
781
+ type: "string",
782
+ required: true
783
+ },
784
+ lastUpdated: {
785
+ type: "string",
786
+ required: true
787
+ },
788
+ gsi1Shard: gsi1ShardAttribute,
789
+ deleted: {
790
+ type: "boolean",
791
+ required: false
792
+ },
793
+ bundleId: {
794
+ type: "string",
795
+ required: false
796
+ },
797
+ msgId: {
798
+ type: "string",
799
+ required: false
800
+ }
801
+ },
802
+ indexes: {
803
+ /** 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. */
804
+ record: {
805
+ pk: {
806
+ field: "PK",
807
+ composite: ["tenantId", "workspaceId", "userId", "roleId"],
808
+ template: "CONFIG#TID#${tenantId}#WID#${workspaceId}#UID#${userId}#RID#${roleId}"
809
+ },
810
+ sk: {
811
+ field: "SK",
812
+ composite: ["key", "sk"],
813
+ template: "KEY#${key}#SK#${sk}"
814
+ }
815
+ },
816
+ /**
817
+ * GSI1 — Unified Sharded List per ADR-011: list all Configuration entries for a
818
+ * (tenant, workspace) across the four shards. Use for "list configs scoped to this tenant"
819
+ * (workspaceId = "-") or "list configs scoped to this workspace". Does not support
820
+ * hierarchical resolution in one query; use base table GetItem in fallback order
821
+ * (user → workspace → tenant → baseline) for that.
822
+ * SK is `<key>#<id>` — Configuration's `key` is a required entity attribute (the
823
+ * config category: endpoints, branding, display, …) and the natural sort/lookup
824
+ * dimension. `casing: "none"` preserves the literal key value.
825
+ */
826
+ gsi1: {
827
+ index: "GSI1",
828
+ pk: {
829
+ field: "GSI1PK",
830
+ composite: ["tenantId", "workspaceId", "gsi1Shard"],
831
+ template: "TID#${tenantId}#WID#${workspaceId}#RT#Configuration#SHARD#${gsi1Shard}"
832
+ },
833
+ sk: {
834
+ field: "GSI1SK",
835
+ casing: "none",
836
+ composite: ["key", "id"],
837
+ template: "${key}#${id}"
838
+ }
839
+ }
840
+ }
841
+ });
842
+
843
+ // src/data/dynamo/entities/control/membership-entity.ts
844
+ var import_electrodb2 = require("electrodb");
845
+ var MembershipEntity = new import_electrodb2.Entity({
846
+ model: {
847
+ entity: "membership",
848
+ service: "control",
849
+ version: "01"
850
+ },
851
+ attributes: {
852
+ /** Sort key sentinel. Always "CURRENT". */
853
+ sk: {
854
+ type: "string",
855
+ required: true,
856
+ default: "CURRENT"
857
+ },
858
+ /** Tenant in which the user has membership (required). */
859
+ tenantId: {
860
+ type: "string",
861
+ required: true
862
+ },
863
+ /** FHIR Resource.id; membership id. */
864
+ id: {
865
+ type: "string",
866
+ required: true
867
+ },
868
+ /** Full Membership resource serialized as JSON string. */
869
+ resource: {
870
+ type: "string",
871
+ required: true
872
+ },
873
+ /**
874
+ * Summary projection (key display fields as JSON string: id, displayName, status).
875
+ * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
876
+ */
877
+ summary: {
878
+ type: "string",
879
+ required: true
880
+ },
881
+ /** Version id (e.g. ULID). */
882
+ vid: {
883
+ type: "string",
884
+ required: true
885
+ },
886
+ lastUpdated: {
887
+ type: "string",
888
+ required: true
889
+ },
890
+ gsi1Shard: gsi1ShardAttribute,
891
+ /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
892
+ gsi1sk: gsi1skAttribute,
893
+ deleted: {
894
+ type: "boolean",
895
+ required: false
896
+ },
897
+ bundleId: {
898
+ type: "string",
899
+ required: false
900
+ },
901
+ msgId: {
902
+ type: "string",
903
+ required: false
904
+ }
905
+ },
906
+ indexes: {
907
+ /** Base table: PK = TID#<tenantId>#MEMBERSHIP#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
908
+ record: {
909
+ pk: {
910
+ field: "PK",
911
+ composite: ["tenantId", "id"],
912
+ template: "TID#${tenantId}#MEMBERSHIP#ID#${id}"
913
+ },
914
+ sk: {
915
+ field: "SK",
916
+ composite: ["sk"],
917
+ template: "${sk}"
918
+ }
919
+ },
920
+ /**
921
+ * GSI1 — Unified Sharded List per ADR-011: list all Memberships for a tenant across the
922
+ * four shards. Membership is tenant-scoped only, so `WID#-` is a sentinel.
923
+ * SK is derived via `gsi1skAttribute` — uses the resource's natural label when
924
+ * extractable, else `<lastUpdated>#<id>` (DR-004). `casing: "none"` preserves the
925
+ * normalized label and ISO-8601 `T`/`Z`.
926
+ */
927
+ gsi1: {
928
+ index: "GSI1",
929
+ pk: {
930
+ field: "GSI1PK",
931
+ composite: ["tenantId", "gsi1Shard"],
932
+ template: "TID#${tenantId}#WID#-#RT#Membership#SHARD#${gsi1Shard}"
933
+ },
934
+ sk: {
935
+ field: "GSI1SK",
936
+ casing: "none",
937
+ composite: ["gsi1sk"],
938
+ template: "${gsi1sk}"
939
+ }
940
+ }
941
+ }
942
+ });
943
+
944
+ // src/data/dynamo/entities/control/role-entity.ts
945
+ var import_electrodb3 = require("electrodb");
946
+ var RoleEntity = new import_electrodb3.Entity({
947
+ model: {
948
+ entity: "role",
949
+ service: "control",
950
+ version: "01"
951
+ },
952
+ attributes: {
953
+ /** Sort key sentinel. Always "CURRENT". */
954
+ sk: {
955
+ type: "string",
956
+ required: true,
957
+ default: "CURRENT"
958
+ },
959
+ /** FHIR Resource.id; role id. */
960
+ id: {
961
+ type: "string",
962
+ required: true
963
+ },
964
+ /** Full Role resource serialized as JSON string. */
965
+ resource: {
966
+ type: "string",
967
+ required: true
968
+ },
969
+ /**
970
+ * Summary projection (key display fields as JSON string: id, displayName, status).
971
+ * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
972
+ */
973
+ summary: {
974
+ type: "string",
975
+ required: true
976
+ },
977
+ /** Version id (e.g. ULID). */
978
+ vid: {
979
+ type: "string",
980
+ required: true
981
+ },
982
+ lastUpdated: {
983
+ type: "string",
984
+ required: true
985
+ },
986
+ gsi1Shard: gsi1ShardAttribute,
987
+ /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
988
+ gsi1sk: gsi1skAttribute,
989
+ deleted: {
990
+ type: "boolean",
991
+ required: false
992
+ },
993
+ bundleId: {
994
+ type: "string",
995
+ required: false
996
+ },
997
+ msgId: {
998
+ type: "string",
999
+ required: false
1000
+ }
1001
+ },
1002
+ indexes: {
1003
+ /** Base table: PK = ROLE#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
1004
+ record: {
1005
+ pk: {
1006
+ field: "PK",
1007
+ composite: ["id"],
1008
+ template: "ROLE#ID#${id}"
1009
+ },
1010
+ sk: {
1011
+ field: "SK",
1012
+ composite: ["sk"],
1013
+ template: "${sk}"
1014
+ }
1015
+ },
1016
+ /**
1017
+ * GSI1 — Unified Sharded List per ADR-011: list all Roles across the four shards.
1018
+ * Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#Role#SHARD#<n>`.
1019
+ * SK is derived via `gsi1skAttribute` — uses the resource's natural label when
1020
+ * extractable, else `<lastUpdated>#<id>` (DR-004). `casing: "none"` preserves the
1021
+ * normalized label and ISO-8601 `T`/`Z`.
1022
+ */
1023
+ gsi1: {
1024
+ index: "GSI1",
1025
+ pk: {
1026
+ field: "GSI1PK",
1027
+ composite: ["gsi1Shard"],
1028
+ template: "TID#-#WID#-#RT#Role#SHARD#${gsi1Shard}"
1029
+ },
1030
+ sk: {
1031
+ field: "GSI1SK",
1032
+ casing: "none",
1033
+ composite: ["gsi1sk"],
1034
+ template: "${gsi1sk}"
1035
+ }
1036
+ }
1037
+ }
1038
+ });
1039
+
1040
+ // src/data/dynamo/entities/control/roleassignment-entity.ts
1041
+ var import_electrodb4 = require("electrodb");
1042
+ var RoleAssignmentEntity = new import_electrodb4.Entity({
1043
+ model: {
1044
+ entity: "roleassignment",
1045
+ service: "control",
1046
+ version: "01"
1047
+ },
1048
+ attributes: {
1049
+ /** Sort key sentinel. Always "CURRENT". */
1050
+ sk: {
1051
+ type: "string",
1052
+ required: true,
1053
+ default: "CURRENT"
1054
+ },
1055
+ /** Tenant in which the role assignment applies (required). */
1056
+ tenantId: {
1057
+ type: "string",
1058
+ required: true
1059
+ },
1060
+ /** FHIR Resource.id; role assignment id. */
1061
+ id: {
1062
+ type: "string",
1063
+ required: true
1064
+ },
1065
+ /** Full RoleAssignment resource serialized as JSON string. */
1066
+ resource: {
1067
+ type: "string",
1068
+ required: true
1069
+ },
1070
+ /**
1071
+ * Summary projection (key display fields as JSON string: id, displayName, status).
1072
+ * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
1073
+ */
1074
+ summary: {
1075
+ type: "string",
1076
+ required: true
1077
+ },
1078
+ /** Version id (e.g. ULID). */
1079
+ vid: {
1080
+ type: "string",
1081
+ required: true
1082
+ },
1083
+ lastUpdated: {
1084
+ type: "string",
1085
+ required: true
1086
+ },
1087
+ gsi1Shard: gsi1ShardAttribute,
1088
+ /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
1089
+ gsi1sk: gsi1skAttribute,
1090
+ deleted: {
1091
+ type: "boolean",
1092
+ required: false
1093
+ },
1094
+ bundleId: {
1095
+ type: "string",
1096
+ required: false
1097
+ },
1098
+ msgId: {
1099
+ type: "string",
1100
+ required: false
1101
+ }
1102
+ },
1103
+ indexes: {
1104
+ /** Base table: PK = TID#<tenantId>#ROLEASSIGNMENT#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
1105
+ record: {
1106
+ pk: {
1107
+ field: "PK",
1108
+ composite: ["tenantId", "id"],
1109
+ template: "TID#${tenantId}#ROLEASSIGNMENT#ID#${id}"
1110
+ },
1111
+ sk: {
1112
+ field: "SK",
1113
+ composite: ["sk"],
1114
+ template: "${sk}"
1115
+ }
1116
+ },
1117
+ /**
1118
+ * GSI1 — Unified Sharded List per ADR-011: list all RoleAssignments for a tenant across the
1119
+ * four shards. Tenant-scoped only, so `WID#-` is a sentinel.
1120
+ * SK is derived via `gsi1skAttribute` — uses the resource's natural label when
1121
+ * extractable, else `<lastUpdated>#<id>` (DR-004). `casing: "none"` preserves the
1122
+ * normalized label and ISO-8601 `T`/`Z`.
1123
+ */
1124
+ gsi1: {
1125
+ index: "GSI1",
1126
+ pk: {
1127
+ field: "GSI1PK",
1128
+ composite: ["tenantId", "gsi1Shard"],
1129
+ template: "TID#${tenantId}#WID#-#RT#RoleAssignment#SHARD#${gsi1Shard}"
1130
+ },
1131
+ sk: {
1132
+ field: "GSI1SK",
1133
+ casing: "none",
1134
+ composite: ["gsi1sk"],
1135
+ template: "${gsi1sk}"
1136
+ }
1137
+ }
1138
+ }
1139
+ });
1140
+
1141
+ // src/data/dynamo/entities/control/tenant-entity.ts
1142
+ var import_electrodb5 = require("electrodb");
1143
+ var TenantEntity = new import_electrodb5.Entity({
1144
+ model: {
1145
+ entity: "tenant",
1146
+ service: "control",
1147
+ version: "01"
1148
+ },
1149
+ attributes: {
1150
+ /** Sort key sentinel. Always "CURRENT". */
1151
+ sk: {
1152
+ type: "string",
1153
+ required: true,
1154
+ default: "CURRENT"
1155
+ },
1156
+ /** The tenant's own id (= resource id). Drives the partition key. */
1157
+ tenantId: {
1158
+ type: "string",
1159
+ required: true
1160
+ },
1161
+ /** FHIR Resource.id; logical id in URL. Equals tenantId. */
1162
+ id: {
1163
+ type: "string",
1164
+ required: true
1165
+ },
1166
+ /** Full Tenant resource serialized as JSON string. */
1167
+ resource: {
1168
+ type: "string",
1169
+ required: true
1170
+ },
1171
+ /**
1172
+ * Summary projection (key display fields as JSON string: id, displayName, status).
1173
+ * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
1174
+ */
1175
+ summary: {
1176
+ type: "string",
1177
+ required: true
1178
+ },
1179
+ /** Version id (e.g. ULID). */
1180
+ vid: {
1181
+ type: "string",
1182
+ required: true
1183
+ },
1184
+ lastUpdated: {
1185
+ type: "string",
1186
+ required: true
1187
+ },
1188
+ gsi1Shard: gsi1ShardAttribute,
1189
+ /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
1190
+ gsi1sk: gsi1skAttribute,
1191
+ deleted: {
1192
+ type: "boolean",
1193
+ required: false
1194
+ },
1195
+ bundleId: {
1196
+ type: "string",
1197
+ required: false
1198
+ },
1199
+ msgId: {
1200
+ type: "string",
1201
+ required: false
1202
+ }
1203
+ },
1204
+ indexes: {
1205
+ /** Base table: PK = TENANT#ID#<tenantId>, SK = CURRENT. Do not supply PK or SK from outside. */
1206
+ record: {
1207
+ pk: {
1208
+ field: "PK",
1209
+ composite: ["tenantId"],
1210
+ template: "TENANT#ID#${tenantId}"
1211
+ },
1212
+ sk: {
1213
+ field: "SK",
1214
+ composite: ["sk"],
1215
+ template: "${sk}"
1216
+ }
1217
+ },
1218
+ /**
1219
+ * GSI1 — Unified Sharded List per ADR-011: list all Tenants across the four shards.
1220
+ * Tenant lives at the platform tier (no parent tenant or workspace), so `TID#-#WID#-`
1221
+ * sentinels precede `RT#Tenant#SHARD#<n>`. SK is derived via `gsi1skAttribute` —
1222
+ * `<normalizedName>#<id>` when the resource carries a `name`, else `<lastUpdated>#<id>`
1223
+ * (DR-004). `casing: "none"` preserves the normalized label and ISO-8601 `T`/`Z`.
1224
+ */
1225
+ gsi1: {
1226
+ index: "GSI1",
1227
+ pk: {
1228
+ field: "GSI1PK",
1229
+ composite: ["gsi1Shard"],
1230
+ template: "TID#-#WID#-#RT#Tenant#SHARD#${gsi1Shard}"
1231
+ },
1232
+ sk: {
1233
+ field: "GSI1SK",
1234
+ casing: "none",
1235
+ composite: ["gsi1sk"],
1236
+ template: "${gsi1sk}"
1237
+ }
1238
+ }
1239
+ }
1240
+ });
1241
+
1242
+ // src/data/dynamo/entities/control/user-entity.ts
1243
+ var import_electrodb6 = require("electrodb");
1244
+ var UserEntity = new import_electrodb6.Entity({
1245
+ model: {
1246
+ entity: "user",
1247
+ service: "control",
1248
+ version: "01"
1249
+ },
1250
+ attributes: {
1251
+ /** Sort key sentinel. Always "CURRENT". */
1252
+ sk: {
1253
+ type: "string",
1254
+ required: true,
1255
+ default: "CURRENT"
1256
+ },
1257
+ /** FHIR Resource.id; platform user id (ohi_uid). */
1258
+ id: {
1259
+ type: "string",
1260
+ required: true
1261
+ },
1262
+ /** Full User resource serialized as JSON string. */
1263
+ resource: {
1264
+ type: "string",
1265
+ required: true
1266
+ },
1267
+ /**
1268
+ * Summary projection (key display fields as JSON string: id, displayName, status).
1269
+ * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
1270
+ */
1271
+ summary: {
1272
+ type: "string",
1273
+ required: true
1274
+ },
1275
+ /**
1276
+ * Immutable Cognito-issued `sub` claim. Drives GSI2 (sub-lookup). Optional until the
1277
+ * Post Confirmation Lambda (#770) lands; required thereafter.
1278
+ */
1279
+ cognitoSub: {
1280
+ type: "string",
1281
+ required: false
1282
+ },
1283
+ /** Version id (e.g. ULID). */
1284
+ vid: {
1285
+ type: "string",
1286
+ required: true
1287
+ },
1288
+ lastUpdated: {
1289
+ type: "string",
1290
+ required: true
1291
+ },
1292
+ gsi1Shard: gsi1ShardAttribute,
1293
+ /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
1294
+ gsi1sk: gsi1skAttribute,
1295
+ deleted: {
1296
+ type: "boolean",
1297
+ required: false
1298
+ },
1299
+ bundleId: {
1300
+ type: "string",
1301
+ required: false
1302
+ },
1303
+ msgId: {
1304
+ type: "string",
1305
+ required: false
1306
+ }
1307
+ },
1308
+ indexes: {
1309
+ /** Base table: PK = USER#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
1310
+ record: {
1311
+ pk: {
1312
+ field: "PK",
1313
+ composite: ["id"],
1314
+ template: "USER#ID#${id}"
1315
+ },
1316
+ sk: {
1317
+ field: "SK",
1318
+ composite: ["sk"],
1319
+ template: "${sk}"
1320
+ }
1321
+ },
1322
+ /**
1323
+ * GSI1 — Unified Sharded List per ADR-011: list all Users across the four shards.
1324
+ * Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#User#SHARD#<n>`.
1325
+ * SK is derived via `gsi1skAttribute` — uses the resource's natural label when
1326
+ * extractable (string `name`/`title` via introspection), else `<lastUpdated>#<id>`
1327
+ * (DR-004). `casing: "none"` preserves the normalized label and ISO-8601 `T`/`Z`.
1328
+ */
1329
+ gsi1: {
1330
+ index: "GSI1",
1331
+ pk: {
1332
+ field: "GSI1PK",
1333
+ composite: ["gsi1Shard"],
1334
+ template: "TID#-#WID#-#RT#User#SHARD#${gsi1Shard}"
1335
+ },
1336
+ sk: {
1337
+ field: "GSI1SK",
1338
+ casing: "none",
1339
+ composite: ["gsi1sk"],
1340
+ template: "${gsi1sk}"
1341
+ }
1342
+ },
1343
+ /**
1344
+ * GSI2 — Cognito sub-lookup per ADR-011: resolves the UserEntity from a Cognito `sub` claim.
1345
+ * `condition` skips the index when `cognitoSub` is missing so legacy items without a sub are
1346
+ * not indexed.
1347
+ */
1348
+ gsi2: {
1349
+ index: "GSI2",
1350
+ condition: (attrs) => typeof attrs.cognitoSub === "string" && attrs.cognitoSub.length > 0,
1351
+ pk: {
1352
+ field: "GSI2PK",
1353
+ casing: "none",
1354
+ composite: ["cognitoSub"],
1355
+ template: "USER#SUB#${cognitoSub}"
1356
+ },
1357
+ sk: {
1358
+ field: "GSI2SK",
1359
+ casing: "none",
1360
+ composite: [],
1361
+ template: "CURRENT"
1362
+ }
1363
+ }
1364
+ }
1365
+ });
1366
+
1367
+ // src/data/dynamo/entities/control/workspace-entity.ts
1368
+ var import_electrodb7 = require("electrodb");
1369
+ var WorkspaceEntity = new import_electrodb7.Entity({
1370
+ model: {
1371
+ entity: "workspace",
1372
+ service: "control",
1373
+ version: "01"
1374
+ },
1375
+ attributes: {
1376
+ /** Sort key sentinel. Always "CURRENT". */
1377
+ sk: {
1378
+ type: "string",
1379
+ required: true,
1380
+ default: "CURRENT"
1381
+ },
1382
+ /** Tenant that contains this workspace (required). */
1383
+ tenantId: {
1384
+ type: "string",
1385
+ required: true
1386
+ },
1387
+ /** FHIR Resource.id; logical id in URL. */
1388
+ id: {
1389
+ type: "string",
1390
+ required: true
1391
+ },
1392
+ /** Full Workspace resource serialized as JSON string. */
1393
+ resource: {
1394
+ type: "string",
1395
+ required: true
1396
+ },
1397
+ /**
1398
+ * Summary projection (key display fields as JSON string: id, displayName, status).
1399
+ * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.
1400
+ */
1401
+ summary: {
1402
+ type: "string",
1403
+ required: true
1404
+ },
1405
+ /** Version id (e.g. ULID). */
1406
+ vid: {
1407
+ type: "string",
1408
+ required: true
1409
+ },
1410
+ lastUpdated: {
1411
+ type: "string",
1412
+ required: true
1413
+ },
1414
+ gsi1Shard: gsi1ShardAttribute,
1415
+ /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */
1416
+ gsi1sk: gsi1skAttribute,
1417
+ deleted: {
1418
+ type: "boolean",
1419
+ required: false
1420
+ },
1421
+ bundleId: {
1422
+ type: "string",
1423
+ required: false
1424
+ },
1425
+ msgId: {
1426
+ type: "string",
1427
+ required: false
1428
+ }
1429
+ },
1430
+ indexes: {
1431
+ /** Base table: PK = TID#<tenantId>#WORKSPACE#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */
1432
+ record: {
1433
+ pk: {
1434
+ field: "PK",
1435
+ composite: ["tenantId", "id"],
1436
+ template: "TID#${tenantId}#WORKSPACE#ID#${id}"
1437
+ },
1438
+ sk: {
1439
+ field: "SK",
1440
+ composite: ["sk"],
1441
+ template: "${sk}"
1442
+ }
1443
+ },
1444
+ /**
1445
+ * GSI1 — Unified Sharded List per ADR-011: list all Workspaces for a tenant across the
1446
+ * four shards. Workspace is itself the workspace identity, so `WID#-` is a sentinel.
1447
+ * SK is derived via `gsi1skAttribute` — `<normalizedName>#<id>` when the resource
1448
+ * carries a `name`, else `<lastUpdated>#<id>` (DR-004). `casing: "none"` preserves
1449
+ * the normalized label and ISO-8601 `T`/`Z`.
1450
+ */
1451
+ gsi1: {
1452
+ index: "GSI1",
1453
+ pk: {
1454
+ field: "GSI1PK",
1455
+ composite: ["tenantId", "gsi1Shard"],
1456
+ template: "TID#${tenantId}#WID#-#RT#Workspace#SHARD#${gsi1Shard}"
1457
+ },
1458
+ sk: {
1459
+ field: "GSI1SK",
1460
+ casing: "none",
1461
+ composite: ["gsi1sk"],
1462
+ template: "${gsi1sk}"
1463
+ }
1464
+ }
1465
+ }
1466
+ });
1467
+
1468
+ // src/data/dynamo/dynamo-control-service.ts
1469
+ var controlPlaneEntities = {
1470
+ configuration: ConfigurationEntity,
1471
+ membership: MembershipEntity,
1472
+ role: RoleEntity,
1473
+ roleAssignment: RoleAssignmentEntity,
1474
+ tenant: TenantEntity,
1475
+ user: UserEntity,
1476
+ workspace: WorkspaceEntity
1477
+ };
1478
+ var controlPlaneService = new import_electrodb8.Service(controlPlaneEntities, {
1479
+ table: defaultTableName,
1480
+ client: dynamoClient
1481
+ });
1482
+ var DynamoControlService = {
1483
+ entities: controlPlaneService.entities
1484
+ };
1485
+ function getDynamoControlService(tableName) {
1486
+ const resolved = tableName ?? defaultTableName;
1487
+ const service = new import_electrodb8.Service(controlPlaneEntities, {
1488
+ table: resolved,
1489
+ client: dynamoClient
1490
+ });
1491
+ return {
1492
+ entities: service.entities
1493
+ };
1494
+ }
1495
+
1496
+ // src/data/operations/control/role/role-create-operation.ts
1497
+ async function createRoleOperation(params) {
1498
+ const { context, body, tableName } = params;
1499
+ const service = getDynamoControlService(tableName);
1500
+ const id = body.id ?? `role-${Date.now()}`;
1501
+ const parsedResource = typeof body.resource === "string" ? JSON.parse(body.resource) : body.resource ?? {};
1502
+ const lastUpdated = context.date ?? (/* @__PURE__ */ new Date()).toISOString();
1503
+ const vid = `1`;
1504
+ const resource = { resourceType: "Role", id, ...parsedResource };
1505
+ const summary = JSON.stringify((0, import_types2.extractSummary)(resource));
1506
+ await service.entities.role.put({
1507
+ id,
1508
+ resource: JSON.stringify(resource),
1509
+ summary,
1510
+ vid,
1511
+ lastUpdated
1512
+ }).go();
1513
+ return {
1514
+ id,
1515
+ resource,
1516
+ meta: { lastUpdated, versionId: vid }
1517
+ };
1518
+ }
1519
+
1520
+ // src/workflows/control-plane/seed-system-data/seed-system-data.handler.ts
1521
+ var errorMessage = (err) => {
1522
+ if (err instanceof Error) {
1523
+ return err.message;
1524
+ }
1525
+ return String(err);
1526
+ };
1527
+ var idForRoleCode = (code) => {
1528
+ for (const key of Object.keys(import_types3.PLATFORM_ROLE_IDS)) {
1529
+ if (import_types3.PLATFORM_ROLE_CONCEPTS[key].code === code) {
1530
+ return import_types3.PLATFORM_ROLE_IDS[key];
1531
+ }
1532
+ }
1533
+ throw new Error(`No id mapping for role code "${code}".`);
1534
+ };
1535
+ var seedSystemRoles = async (context) => {
1536
+ for (const concept of Object.values(import_types3.PLATFORM_ROLE_CONCEPTS)) {
1537
+ const id = idForRoleCode(concept.code);
1538
+ await createRoleOperation({
1539
+ context,
1540
+ body: {
1541
+ id,
1542
+ resource: {
1543
+ resourceType: "Role",
1544
+ code: concept.code,
1545
+ display: concept.display,
1546
+ active: true
1547
+ }
1548
+ }
1549
+ });
1550
+ }
1551
+ };
1552
+ var runSeedSystemData = async (event, deps) => {
1553
+ const parsed = (0, import_workflows2.parseWorkflowEvent)(event, import_workflows.PlatformDeploymentCompletedV1);
1554
+ const recordResult = await deps.dedupClient.recordIfAbsent({
1555
+ consumerName: SEED_SYSTEM_DATA_CONSUMER_NAME,
1556
+ eventId: parsed.dedupKey.eventId,
1557
+ attempt: parsed.dedupKey.attempt
1558
+ });
1559
+ if (!recordResult.recorded) {
1560
+ return;
1561
+ }
1562
+ const lastUpdated = parsed.envelope.occurredAt;
1563
+ const context = {
1564
+ tenantId: "",
1565
+ workspaceId: "",
1566
+ date: lastUpdated,
1567
+ actorId: "platform-deploy-bridge",
1568
+ actorName: "Platform Deploy Bridge",
1569
+ actorType: "internal-system",
1570
+ source: "step-function"
1571
+ };
1572
+ try {
1573
+ await deps.seedRoles(context);
1574
+ } catch (err) {
1575
+ await deps.dedupClient.markFailed({
1576
+ consumerName: SEED_SYSTEM_DATA_CONSUMER_NAME,
1577
+ eventId: parsed.dedupKey.eventId,
1578
+ attempt: parsed.dedupKey.attempt,
1579
+ reason: errorMessage(err)
1580
+ });
1581
+ throw err;
1582
+ }
1583
+ const sourcePayload = parsed.envelope.payload;
1584
+ await deps.publishSeeded({
1585
+ payload: {
1586
+ sourceEventId: parsed.envelope.eventId,
1587
+ sourceStackId: sourcePayload.stackId,
1588
+ seededRecordCount: Object.keys(import_types3.PLATFORM_ROLE_IDS).length
1589
+ },
1590
+ envelope: {
1591
+ correlationId: parsed.envelope.correlationId,
1592
+ causationId: parsed.envelope.eventId
1593
+ }
1594
+ });
1595
+ };
1596
+ var productionDependencies = () => {
1597
+ const dynamodb = new import_client_dynamodb2.DynamoDBClient({});
1598
+ const eventBridge = new import_client_eventbridge.EventBridgeClient({});
1599
+ return {
1600
+ dedupClient: (0, import_workflows2.workflowDedupClient)(dynamodb),
1601
+ seedRoles: seedSystemRoles,
1602
+ publishSeeded: async (input) => {
1603
+ const controlBusName = process.env[SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR];
1604
+ if (!controlBusName) {
1605
+ throw new Error(
1606
+ `seed-system-data: ${SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR} env var is required to publish system-data-seeded events.`
1607
+ );
1608
+ }
1609
+ return (0, import_workflows2.publishWorkflowEvent)(
1610
+ eventBridge,
1611
+ import_workflows.PlatformSystemDataSeededV1,
1612
+ input.payload,
1613
+ {
1614
+ actor: { system: SEED_SYSTEM_DATA_ACTOR_SYSTEM },
1615
+ correlationId: input.envelope.correlationId,
1616
+ causationId: input.envelope.causationId
1617
+ },
1618
+ {
1619
+ busNameByPlane: { [import_workflows2.OPENHI_CONTROL_SOURCE]: controlBusName }
1620
+ }
1621
+ );
1622
+ }
1623
+ };
1624
+ };
1625
+ var handler = async (event) => runSeedSystemData(event, productionDependencies());
1626
+ // Annotate the CommonJS export names for ESM import in node:
1627
+ 0 && (module.exports = {
1628
+ handler,
1629
+ runSeedSystemData
1630
+ });
1631
+ //# sourceMappingURL=seed-system-data.handler.js.map