@openhi/constructs 0.0.104 → 0.0.106

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 (83) hide show
  1. package/README.md +14 -0
  2. package/lib/chunk-2PM2NGXI.mjs +31 -0
  3. package/lib/chunk-2PM2NGXI.mjs.map +1 -0
  4. package/lib/chunk-AGF3RAAZ.mjs +20 -0
  5. package/lib/chunk-AGF3RAAZ.mjs.map +1 -0
  6. package/lib/chunk-AO3E22CS.mjs +108 -0
  7. package/lib/chunk-AO3E22CS.mjs.map +1 -0
  8. package/lib/chunk-CHPEQRXU.mjs +45 -0
  9. package/lib/chunk-CHPEQRXU.mjs.map +1 -0
  10. package/lib/chunk-JUNL76HF.mjs +428 -0
  11. package/lib/chunk-JUNL76HF.mjs.map +1 -0
  12. package/lib/chunk-L6UAP4KP.mjs +27 -0
  13. package/lib/chunk-L6UAP4KP.mjs.map +1 -0
  14. package/lib/{chunk-3QS3WKRC.mjs → chunk-LZOMFHX3.mjs} +9 -2
  15. package/lib/chunk-QMIOLLAS.mjs +531 -0
  16. package/lib/chunk-QMIOLLAS.mjs.map +1 -0
  17. package/lib/chunk-SYBADQXI.mjs +607 -0
  18. package/lib/chunk-SYBADQXI.mjs.map +1 -0
  19. package/lib/chunk-VXX4I3EF.mjs +19 -0
  20. package/lib/chunk-VXX4I3EF.mjs.map +1 -0
  21. package/lib/{chunk-MLTYFMSE.mjs → chunk-VYDIGFIX.mjs} +74 -29
  22. package/lib/chunk-VYDIGFIX.mjs.map +1 -0
  23. package/lib/chunk-YU2HRNUP.mjs +33 -0
  24. package/lib/chunk-YU2HRNUP.mjs.map +1 -0
  25. package/lib/chunk-YZZDUJHI.mjs +37 -0
  26. package/lib/chunk-YZZDUJHI.mjs.map +1 -0
  27. package/lib/cors-options-lambda.handler.mjs +1 -1
  28. package/lib/data-store-postgres-replication.handler.mjs +1 -1
  29. package/lib/events-BfrkMoBD.d.mts +44 -0
  30. package/lib/events-BfrkMoBD.d.ts +44 -0
  31. package/lib/events-CVA3_eEB.d.mts +23 -0
  32. package/lib/events-CVA3_eEB.d.ts +23 -0
  33. package/lib/events-DGep6C7w.d.mts +207 -0
  34. package/lib/events-DGep6C7w.d.ts +207 -0
  35. package/lib/firehose-archive-transform.handler.mjs +1 -1
  36. package/lib/index.d.mts +508 -29
  37. package/lib/index.d.ts +773 -30
  38. package/lib/index.js +2536 -105
  39. package/lib/index.js.map +1 -1
  40. package/lib/index.mjs +899 -106
  41. package/lib/index.mjs.map +1 -1
  42. package/lib/openhi-context-CaBH8SFo.d.mts +39 -0
  43. package/lib/openhi-context-CaBH8SFo.d.ts +39 -0
  44. package/lib/platform-deploy-bridge.handler.d.mts +14 -0
  45. package/lib/platform-deploy-bridge.handler.d.ts +14 -0
  46. package/lib/platform-deploy-bridge.handler.js +762 -0
  47. package/lib/platform-deploy-bridge.handler.js.map +1 -0
  48. package/lib/platform-deploy-bridge.handler.mjs +134 -0
  49. package/lib/platform-deploy-bridge.handler.mjs.map +1 -0
  50. package/lib/post-authentication.handler.mjs +1 -1
  51. package/lib/post-confirmation.handler.js +50 -904
  52. package/lib/post-confirmation.handler.js.map +1 -1
  53. package/lib/post-confirmation.handler.mjs +37 -112
  54. package/lib/post-confirmation.handler.mjs.map +1 -1
  55. package/lib/pre-token-generation.handler.js +135 -55
  56. package/lib/pre-token-generation.handler.js.map +1 -1
  57. package/lib/pre-token-generation.handler.mjs +25 -32
  58. package/lib/pre-token-generation.handler.mjs.map +1 -1
  59. package/lib/provision-default-workspace.handler.d.mts +13 -0
  60. package/lib/provision-default-workspace.handler.d.ts +13 -0
  61. package/lib/provision-default-workspace.handler.js +1172 -0
  62. package/lib/provision-default-workspace.handler.js.map +1 -0
  63. package/lib/provision-default-workspace.handler.mjs +175 -0
  64. package/lib/provision-default-workspace.handler.mjs.map +1 -0
  65. package/lib/rest-api-lambda.handler.js +114 -59
  66. package/lib/rest-api-lambda.handler.js.map +1 -1
  67. package/lib/rest-api-lambda.handler.mjs +60 -587
  68. package/lib/rest-api-lambda.handler.mjs.map +1 -1
  69. package/lib/seed-demo-data.handler.d.mts +107 -0
  70. package/lib/seed-demo-data.handler.d.ts +107 -0
  71. package/lib/seed-demo-data.handler.js +2037 -0
  72. package/lib/seed-demo-data.handler.js.map +1 -0
  73. package/lib/seed-demo-data.handler.mjs +23 -0
  74. package/lib/seed-demo-data.handler.mjs.map +1 -0
  75. package/lib/seed-system-data.handler.d.mts +64 -0
  76. package/lib/seed-system-data.handler.d.ts +64 -0
  77. package/lib/seed-system-data.handler.js +1631 -0
  78. package/lib/seed-system-data.handler.js.map +1 -0
  79. package/lib/seed-system-data.handler.mjs +135 -0
  80. package/lib/seed-system-data.handler.mjs.map +1 -0
  81. package/package.json +4 -2
  82. package/lib/chunk-MLTYFMSE.mjs.map +0 -1
  83. /package/lib/{chunk-3QS3WKRC.mjs.map → chunk-LZOMFHX3.mjs.map} +0 -0
package/lib/index.d.ts CHANGED
@@ -8,18 +8,21 @@ import { Key, KeyProps, IKey } from 'aws-cdk-lib/aws-kms';
8
8
  import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
9
9
  import { AttributeValue } from '@aws-sdk/client-dynamodb';
10
10
  import * as events from 'aws-cdk-lib/aws-events';
11
- import { EventBus, EventBusProps, IEventBus } from 'aws-cdk-lib/aws-events';
11
+ import { EventBus, EventBusProps, Rule, IEventBus } from 'aws-cdk-lib/aws-events';
12
12
  import * as kinesis from 'aws-cdk-lib/aws-kinesis';
13
13
  import * as kinesisfirehose from 'aws-cdk-lib/aws-kinesisfirehose';
14
14
  import * as s3 from 'aws-cdk-lib/aws-s3';
15
15
  import { IBucket, BucketProps } from 'aws-cdk-lib/aws-s3';
16
16
  import { Table, TableProps, ITable } from 'aws-cdk-lib/aws-dynamodb';
17
+ import { IFunction, Function } from 'aws-cdk-lib/aws-lambda';
17
18
  import * as ec2 from 'aws-cdk-lib/aws-ec2';
18
19
  import * as rds from 'aws-cdk-lib/aws-rds';
19
20
  import { HostedZone, HostedZoneProps, IHostedZone, HostedZoneAttributes } from 'aws-cdk-lib/aws-route53';
20
21
  import { StringParameterProps, StringParameter } from 'aws-cdk-lib/aws-ssm';
21
22
  import { Distribution, DistributionProps } from 'aws-cdk-lib/aws-cloudfront';
22
- import { IFunction } from 'aws-cdk-lib/aws-lambda';
23
+ import { ControlPlaneRoleCode } from '@openhi/types';
24
+ import { PostConfirmationTriggerEvent } from 'aws-lambda';
25
+ export { PlatformDeploymentCompletedV1, PlatformSystemDataSeededV1 } from '@openhi/workflows';
23
26
 
24
27
  /*******************************************************************************
25
28
  *
@@ -118,6 +121,273 @@ interface DynamoDbStreamKinesisRecord {
118
121
  };
119
122
  }
120
123
 
124
+ /**
125
+ * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/platform-deploy-bridge/index.md
126
+ */
127
+ /** EventBridge `source` for the AWS-native CloudFormation events the bridge listens to. */
128
+ declare const CLOUDFORMATION_EVENT_SOURCE: "aws.cloudformation";
129
+ /** EventBridge `detail-type` for terminal stack status events. */
130
+ declare const CLOUDFORMATION_STACK_STATUS_CHANGE_DETAIL_TYPE: "CloudFormation Stack Status Change";
131
+ /** Stack statuses the bridge republishes. Other statuses are pre-filtered at the rule. */
132
+ declare const BRIDGED_STATUSES: readonly ["CREATE_COMPLETE", "UPDATE_COMPLETE"];
133
+ type BridgedStatus = (typeof BRIDGED_STATUSES)[number];
134
+ /** Env var the bridge handler reads to discover the control event bus name. */
135
+ declare const CONTROL_EVENT_BUS_NAME_ENV_VAR = "CONTROL_EVENT_BUS_NAME";
136
+ /**
137
+ * Env var the bridge handler reads for the resolved
138
+ * `openhi:repo-name`-shaped tag key. Resolved at synth time from the host
139
+ * stack's `appName` + {@link OPENHI_TAG_SUFFIX_REPO_NAME}.
140
+ */
141
+ declare const OPENHI_REPO_TAG_KEY_ENV_VAR = "OPENHI_REPO_TAG_KEY";
142
+ /**
143
+ * Env var the bridge handler reads for the resolved `openhi:` tag prefix.
144
+ * Resolved at synth time from the host stack's `appName`. Used to project
145
+ * the relevant stack tags into the published envelope.
146
+ */
147
+ declare const OPENHI_TAG_KEY_PREFIX_ENV_VAR = "OPENHI_TAG_KEY_PREFIX";
148
+ /** Free-form `actor.system` value per TR-016 § Decision points #1 (bootstrap-role pattern). */
149
+ declare const PLATFORM_DEPLOY_BRIDGE_ACTOR_SYSTEM = "platform-deploy-bridge";
150
+ /**
151
+ * Subset of the CloudFormation Stack Status Change `detail` field the
152
+ * bridge handler reads. AWS-side schema lives at
153
+ * <https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/monitoring-cloudformation.html>.
154
+ */
155
+ interface CloudFormationStackStatusChangeDetail {
156
+ readonly "stack-id": string;
157
+ readonly "logical-resource-id"?: string;
158
+ readonly "physical-resource-id"?: string;
159
+ readonly "status-details": {
160
+ readonly status: string;
161
+ readonly "status-reason"?: string;
162
+ };
163
+ readonly "resource-type"?: string;
164
+ readonly "client-request-token"?: string;
165
+ }
166
+
167
+ /**
168
+ * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-demo-data/events.md
169
+ */
170
+ /**
171
+ * Stable logical name this workflow registers with the shared
172
+ * `WorkflowDedupTable` (TR-015). Used in both the construct grant
173
+ * (`workflowDedupTable.grantConsumer(lambda, SEED_DEMO_DATA_CONSUMER_NAME)`)
174
+ * and the handler's runtime `recordIfAbsent` call — keep them aligned by
175
+ * importing this constant in both places.
176
+ */
177
+ declare const SEED_DEMO_DATA_CONSUMER_NAME = "seed-demo-data";
178
+ /** FHIR `Identifier.system` for the demo-scenario URN scheme. */
179
+ declare const DEMO_URN_SYSTEM = "urn:openhi:demo";
180
+ /**
181
+ * FHIR `Identifier.system` for the canonical OpenHI resource URN
182
+ * (ADR 2026-03-12-01). The value form is
183
+ * `urn:ohi:<tenantId>:<workspaceId>:<resourceType>:<id>`; empty
184
+ * workspaceId means the resource has tenant-wide scope (or is itself
185
+ * the workspace identity, in the Workspace case).
186
+ */
187
+ declare const OPENHI_RESOURCE_URN_SYSTEM = "http://openhi.org/";
188
+ /**
189
+ * Period stamped on every demo Membership and RoleAssignment. Fixed
190
+ * start so re-runs produce byte-identical bodies (decision #4); no
191
+ * end so demo memberships are open-ended.
192
+ */
193
+ declare const DEMO_PERIOD: {
194
+ readonly start: "2026-01-01T00:00:00Z";
195
+ };
196
+ /**
197
+ * Sentinel `tenantId` used on every dev user's `system-admin`
198
+ * RoleAssignment. A `system-admin` RA is platform-scoped (it spans
199
+ * every tenant), but the RoleAssignment entity requires a tenantId on
200
+ * its key for sharding — there is no real tenant to point at. The
201
+ * `"platform"` literal is a reserved value that never matches a real
202
+ * Tenant id and signals "this RA scopes across all tenants".
203
+ *
204
+ * Renaming this constant is a wire-format break — the IAM grant in
205
+ * `seed-demo-data-lambda.ts` enumerates exact-match `LeadingKeys`
206
+ * computed from this value, and the in-band records written under it
207
+ * become unreachable if the sentinel changes.
208
+ */
209
+ declare const PLATFORM_SCOPE_TENANT_ID = "platform";
210
+ /** Placeholder Tenant id seeded by the workflow as the dev-user `currentTenant`. */
211
+ declare const PLACEHOLDER_TENANT_ID = "placeholder-tenant-id";
212
+ /** Placeholder Workspace id seeded by the workflow as the dev-user `currentWorkspace`. */
213
+ declare const PLACEHOLDER_WORKSPACE_ID = "placeholder-workspace-id";
214
+ /**
215
+ * Dev-user descriptor. Every entry produces:
216
+ * - one Cognito user (idempotent create-then-skip),
217
+ * - one DynamoDB User record (id = `dev-<email-local-part>`),
218
+ * - four Memberships (placeholder + the three demo tenants),
219
+ * - four `tenant-admin` RoleAssignments (one per tenant the user
220
+ * belongs to),
221
+ * - one `system-admin` RoleAssignment scoped to {@link PLATFORM_SCOPE_TENANT_ID}.
222
+ */
223
+ interface DemoDevUser {
224
+ /** Stable DynamoDB User id. */
225
+ readonly id: string;
226
+ /** Email used as the Cognito `Username` and as the seed for the password algorithm. */
227
+ readonly email: string;
228
+ }
229
+ /**
230
+ * Hardcoded dev-user roster. Adding a new developer is a one-line
231
+ * change here plus a re-deploy. The list intentionally lives in-source
232
+ * (not behind an env-var seam) so the IAM grant in
233
+ * `seed-demo-data-lambda.ts` can enumerate per-user PKs at synth time.
234
+ */
235
+ declare const DEV_USERS: ReadonlyArray<DemoDevUser>;
236
+ /**
237
+ * A single workspace inside a demo tenant. The mixed tenant has two
238
+ * workspaces (wound-care and primary-care sub-workspaces); the other
239
+ * two tenants have one each.
240
+ */
241
+ interface DemoWorkspaceSpec {
242
+ /** Stable id (DynamoDB record id; also drives the canonical OHI URN). */
243
+ readonly id: string;
244
+ /** FHIR `Workspace.name`. */
245
+ readonly name: string;
246
+ /**
247
+ * Role suffix used in the demo URN value (`<scenario>:<roleSuffix>`).
248
+ * Mirrors seed-fixtures' role suffix convention: `workspace` for
249
+ * single-workspace tenants, `workspace-<sub>` for the mixed tenant.
250
+ */
251
+ readonly roleSuffix: string;
252
+ }
253
+ /**
254
+ * One demo tenant + the workspaces it owns. Re-exported via {@link
255
+ * DEMO_TENANT_SPECS} as the canonical list; iterate that constant in
256
+ * the handler and the IAM-grant builder so the value sets agree.
257
+ */
258
+ interface DemoTenantSpec {
259
+ /**
260
+ * Scenario slug — `placeholder`, `demo-wound-care`, `demo-primary-care`,
261
+ * `demo-mixed`. The placeholder tenant's slug is `placeholder`; the
262
+ * three demo tenants mirror seed-fixtures' `fixture-*` slugs renamed
263
+ * to `demo-*`.
264
+ */
265
+ readonly scenario: string;
266
+ /** Stable id (DynamoDB record id; also drives the canonical OHI URN). */
267
+ readonly tenantId: string;
268
+ /** FHIR `Tenant.name`. */
269
+ readonly tenantName: string;
270
+ /** Workspaces owned by this tenant. */
271
+ readonly workspaces: ReadonlyArray<DemoWorkspaceSpec>;
272
+ }
273
+ /**
274
+ * The full demo-tenant graph. Four entries: the placeholder tenant the
275
+ * JWT-claim fallback resolves to, plus the three v1 demo scenarios
276
+ * (OPS-009 §"v1 scenarios"):
277
+ *
278
+ * 0. Placeholder tenant — dereferences the JWT-claim fallback in
279
+ * `pre-token-generation.handler.ts` and acts as every dev user's
280
+ * `currentTenant`/`currentWorkspace` so Post-Confirmation skips
281
+ * default provisioning when a seeded User signs in.
282
+ * 1. Single-workspace wound-care tenant.
283
+ * 2. Single-workspace primary-care tenant.
284
+ * 3. Two-workspace mixed tenant — exercises the cross-workspace
285
+ * isolation flow that single-workspace tenants cannot.
286
+ */
287
+ declare const DEMO_TENANT_SPECS: ReadonlyArray<DemoTenantSpec>;
288
+ /** Stable Membership id derived from `(devUserId, tenantId)`. */
289
+ declare const demoMembershipId: (devUserId: string, tenantId: string) => string;
290
+ /**
291
+ * Stable RoleAssignment id derived from `(devUserId, tenantId, roleCode)`.
292
+ * Each (user, tenant, role) tuple maps to exactly one record — re-runs
293
+ * upsert the same id.
294
+ */
295
+ declare const demoRoleAssignmentId: (devUserId: string, tenantId: string, roleCode: ControlPlaneRoleCode) => string;
296
+ /**
297
+ * Demo-scenario FHIR `Identifier` entry — `urn:openhi:demo:<scenario>:<role>`.
298
+ * Mirrors the `urn:openhi:fixture:<scenario>:<role>` pattern from
299
+ * `@openhi/seed-fixtures/src/urn.ts`, renamed to the `demo` namespace.
300
+ */
301
+ declare const demoScenarioIdentifier: (scenario: string, roleSuffix: string) => {
302
+ system: string;
303
+ value: string;
304
+ };
305
+ /**
306
+ * Canonical OpenHI resource FHIR `Identifier` entry per ADR
307
+ * 2026-03-12-01. `workspaceId` is empty for both Tenant resources and
308
+ * Workspace resources — for a Tenant, the resource is tenant-scoped
309
+ * with no workspace context; for a Workspace, the resource IS the
310
+ * workspace identity rather than living inside one.
311
+ */
312
+ declare const openhiResourceIdentifier: (params: {
313
+ tenantId: string;
314
+ workspaceId: string;
315
+ resourceType: string;
316
+ id: string;
317
+ }) => {
318
+ use: string;
319
+ system: string;
320
+ value: string;
321
+ };
322
+ /**
323
+ * Roles every dev user holds in every tenant they belong to. Per scope
324
+ * decision Q3, every dev user is `tenant-admin` in every tenant — there
325
+ * is no per-(user, tenant) variance to drive from.
326
+ */
327
+ declare const demoRolesForUserInTenant: (_user: DemoDevUser, _tenantId: string) => ReadonlyArray<ControlPlaneRoleCode>;
328
+ /**
329
+ * DynamoDB single-table partition-key builders. The IAM grant in
330
+ * `seed-demo-data-lambda.ts` uses these to enumerate exact-match
331
+ * `dynamodb:LeadingKeys` values; the entity definitions in
332
+ * `data/dynamo/entities/control/` own the canonical key templates.
333
+ *
334
+ * These builders MUST emit the keys ElectroDB actually writes — not
335
+ * the entity definition's pretty template. None of the control-plane
336
+ * entities sets `casing: "none"` on the base-table PK template, so
337
+ * ElectroDB applies its default lowercase casing at runtime: the
338
+ * entity's `ROLE#ID#${id}` becomes `role#id#<id>` on the wire. A
339
+ * builder that returns the uppercase template form produces a
340
+ * silently-broken IAM grant (every PutItem denied with "no
341
+ * identity-based policy allows" because the request's leading-key
342
+ * never matches a policy value).
343
+ */
344
+ declare const rolePartitionKey: (roleId: string) => string;
345
+ declare const demoTenantPartitionKey: (tenantId: string) => string;
346
+ declare const demoWorkspacePartitionKey: (tenantId: string, workspaceId: string) => string;
347
+ declare const demoMembershipPartitionKey: (tenantId: string, membershipId: string) => string;
348
+ declare const demoRoleAssignmentPartitionKey: (tenantId: string, roleAssignmentId: string) => string;
349
+ /** User entity PK template — `USER#ID#<id>` → `user#id#<id>` on the wire. */
350
+ declare const demoUserPartitionKey: (userId: string) => string;
351
+ /**
352
+ * Tenant + Workspace PKs the workflow writes on every fire: the 4
353
+ * tenant PKs (placeholder + 3 demo) plus their workspaces (1 + 1 + 1 + 2 = 5).
354
+ */
355
+ declare const demoBasePartitionKeys: () => ReadonlyArray<string>;
356
+ /**
357
+ * Membership + RoleAssignment + User PKs the workflow writes per dev
358
+ * user. Empty when `devUsers` is empty (used by tests). The list
359
+ * mirrors the handler's iteration order so the IAM grant covers every
360
+ * write the handler can make.
361
+ *
362
+ * Per dev user the function emits:
363
+ * - one User PK,
364
+ * - per tenant in {@link DEMO_TENANT_SPECS}: one Membership PK plus
365
+ * one `tenant-admin` RoleAssignment PK,
366
+ * - one platform-scoped `system-admin` RoleAssignment PK keyed by
367
+ * {@link PLATFORM_SCOPE_TENANT_ID}.
368
+ */
369
+ declare const demoDevUserPartitionKeys: (devUsers: ReadonlyArray<DemoDevUser>) => ReadonlyArray<string>;
370
+
371
+ /**
372
+ * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/user-onboarding/events.md
373
+ */
374
+ declare const USER_ONBOARDING_EVENT_SOURCE = "openhi.control.user-onboarding";
375
+ declare const PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE = "ProvisionDefaultWorkspaceRequested";
376
+ interface ProvisionDefaultWorkspaceRequestedDetail {
377
+ readonly cognitoSub: string;
378
+ readonly userId?: string;
379
+ readonly email?: string;
380
+ readonly displayName?: string;
381
+ readonly trigger: {
382
+ readonly source: "cognito.post-confirmation";
383
+ readonly triggerSource?: string;
384
+ readonly userPoolId?: string;
385
+ readonly userName?: string;
386
+ readonly clientId?: string;
387
+ };
388
+ }
389
+ declare const buildProvisionDefaultWorkspaceRequestedDetail: (event: PostConfirmationTriggerEvent) => ProvisionDefaultWorkspaceRequestedDetail | undefined;
390
+
121
391
  /**
122
392
  * Properties for creating an OpenHiStage instance.
123
393
  */
@@ -332,6 +602,28 @@ declare class OpenHiApp extends App {
332
602
  * @public
333
603
  */
334
604
  type OpenHiServiceType = "auth" | "rest-api" | "data" | "global" | "graphql-api";
605
+ /**
606
+ * Tag-key suffixes applied by every OpenHiService stack via Tags.of().
607
+ * Full keys are composed `${appName}:${suffix}` — see {@link openHiTagKey}.
608
+ * Consumers that filter or project these tags (e.g. the platform-deploy
609
+ * bridge) import these suffixes rather than redeclaring the strings.
610
+ *
611
+ * @public
612
+ */
613
+ declare const OPENHI_TAG_SUFFIX_REPO_NAME = "repo-name";
614
+ /** @public */
615
+ declare const OPENHI_TAG_SUFFIX_BRANCH_NAME = "branch-name";
616
+ /** @public */
617
+ declare const OPENHI_TAG_SUFFIX_SERVICE_TYPE = "service-type";
618
+ /** @public */
619
+ declare const OPENHI_TAG_SUFFIX_STAGE_TYPE = "stage-type";
620
+ /**
621
+ * Compose a full stack-tag key from an `appName` and a suffix from
622
+ * {@link OPENHI_TAG_SUFFIX_REPO_NAME} et al.
623
+ *
624
+ * @public
625
+ */
626
+ declare const openHiTagKey: (appName: string, suffix: string) => string;
335
627
  /**
336
628
  * Properties for creating an {@link OpenHiService} stack.
337
629
  *
@@ -579,16 +871,14 @@ declare class PostAuthenticationLambda extends Construct {
579
871
 
580
872
  interface PostConfirmationLambdaProps {
581
873
  /**
582
- * DynamoDB data store table name. Passed to the Lambda as DYNAMO_TABLE_NAME
583
- * so the control-plane ElectroDB service writes to the same single-table store.
874
+ * Control-plane EventBridge bus name. Passed to the Lambda as
875
+ * CONTROL_EVENT_BUS_NAME so it can publish onboarding workflow events.
584
876
  */
585
- readonly dynamoTableName: string;
877
+ readonly controlEventBusName: string;
586
878
  }
587
879
  /**
588
- * Lambda used as Cognito Post Confirmation trigger. Creates the new user's
589
- * default Tenant, Workspace, Memberships, and RoleAssignment, plus a User
590
- * record carrying the Cognito `sub` and current tenant/workspace pointers
591
- * (ADR 2026-03-17-01).
880
+ * Lambda used as Cognito Post Confirmation trigger. It publishes a control
881
+ * event and returns quickly; workflow Lambdas own provisioning.
592
882
  */
593
883
  declare class PostConfirmationLambda extends Construct {
594
884
  readonly lambda: NodejsFunction;
@@ -726,6 +1016,108 @@ declare class DynamoDbDataStore extends Table {
726
1016
  constructor(scope: Construct, id: string, props?: DynamoDbDataStoreProps);
727
1017
  }
728
1018
 
1019
+ /**
1020
+ * @see sites/www-docs/content/packages/@openhi/constructs/components/dynamodb/workflow-dedup-table.md
1021
+ */
1022
+ /**
1023
+ * Deterministic table name for the shared workflow dedup table.
1024
+ * Mirrors `getDynamoDbDataStoreTableName` naming: `workflow-dedup-${branchHash}`.
1025
+ */
1026
+ declare function getWorkflowDedupTableName(scope: Construct): string;
1027
+ /** Props for `WorkflowDedupTable`. */
1028
+ interface WorkflowDedupTableProps {
1029
+ /**
1030
+ * Optional removal policy override. Defaults to the service's default
1031
+ * (RETAIN for prod, DESTROY otherwise).
1032
+ */
1033
+ readonly removalPolicy?: RemovalPolicy;
1034
+ }
1035
+ /** Options for `WorkflowDedupTable.grantConsumer`. */
1036
+ interface GrantConsumerOptions {
1037
+ /**
1038
+ * Override the default TTL applied by the runtime client. The 14-day
1039
+ * default lives in `@openhi/workflows`; per-consumer overrides clamp
1040
+ * shorter per TR-015. Stored in the consumer's environment so the
1041
+ * `WorkflowDedupClient` factory can pick it up.
1042
+ */
1043
+ readonly defaultTtlSeconds?: number;
1044
+ }
1045
+ /**
1046
+ * Shared platform-level dedup table every retryable workflow consumer
1047
+ * dedupes against. Provisioned exactly once at the platform stack.
1048
+ *
1049
+ * Schema (per TR-015):
1050
+ * - Partition key `consumerName` (S)
1051
+ * - Sort key `sk` (S) — encodes `<eventId>#<attempt>`
1052
+ * - TTL attribute `expiresAt` (N, Unix epoch seconds)
1053
+ * - On-demand billing
1054
+ *
1055
+ * @see https://github.com/codedrifters/openhi-planning/blob/main/docs/src/content/docs/requirements/technical-requirements/TR-015-workflow-dedup-table.md
1056
+ */
1057
+ declare class WorkflowDedupTable extends Construct {
1058
+ /** SSM param name (short) used by `DiscoverableStringParameter` for the table name lookup. */
1059
+ static readonly TABLE_NAME_SSM_PARAM_NAME = "workflow-dedup-table-name";
1060
+ /** SSM param name (short) used by `DiscoverableStringParameter` for the table ARN lookup. */
1061
+ static readonly TABLE_ARN_SSM_PARAM_NAME = "workflow-dedup-table-arn";
1062
+ /** Cross-stack lookup for the table name. */
1063
+ static tableNameFromLookup(scope: Construct): string;
1064
+ /** Cross-stack lookup for the table ARN. */
1065
+ static tableArnFromLookup(scope: Construct): string;
1066
+ /**
1067
+ * Cross-stack equivalent of {@link grantConsumer}. Use when the dedup
1068
+ * table is on a different stack than the consumer Lambda — the
1069
+ * grant resolves the table name + ARN via SSM at synth time, so the
1070
+ * consumer stack does not pick up a CloudFormation export dependency
1071
+ * on the global stack.
1072
+ *
1073
+ * Inverts the singleton-guard semantics of `grantConsumer`: there is
1074
+ * no synth-time check that the same `consumerName` was registered
1075
+ * twice across stacks. Consumer names are agreed by convention
1076
+ * (see TR-015); double-registration is operator error caught at
1077
+ * design time, not synth time.
1078
+ */
1079
+ static grantConsumerFromLookup(scope: Construct, fn: Function, consumerName: string, options?: GrantConsumerOptions): void;
1080
+ /**
1081
+ * Service-type the publishing stack runs under. The cross-stack lookups
1082
+ * pin to this value so consumer stacks on a different service-type
1083
+ * (e.g. `data`, `auth`) resolve the parameter at the publisher's SSM
1084
+ * path instead of their own. Typed against `OpenHiServiceType` so a
1085
+ * future rename of the literal triggers a compile error; not pulled
1086
+ * from `OpenHiGlobalService.SERVICE_TYPE` because
1087
+ * `OpenHiGlobalService` already imports `WorkflowDedupTable` — a
1088
+ * back-import would create a circular dependency.
1089
+ */
1090
+ private static readonly PUBLISHER_SERVICE_TYPE;
1091
+ /**
1092
+ * Standalone consumer-name validator shared by the instance method
1093
+ * and `grantConsumerFromLookup` so the two grants enforce identical
1094
+ * invariants.
1095
+ */
1096
+ private static assertConsumerNameStatic;
1097
+ /** The underlying DynamoDB table. */
1098
+ readonly table: Table;
1099
+ private readonly registeredConsumers;
1100
+ constructor(scope: Construct, id: string, props?: WorkflowDedupTableProps);
1101
+ /**
1102
+ * Wire a Lambda consumer to this table. Injects the table-name env var
1103
+ * so the runtime `WorkflowDedupClient` can resolve it, then attaches a
1104
+ * per-consumer IAM grant scoped by `dynamodb:LeadingKeys` so the
1105
+ * consumer can only read/write its own partition.
1106
+ */
1107
+ grantConsumer(fn: Function, consumerName: string, options?: GrantConsumerOptions): void;
1108
+ private assertConsumerName;
1109
+ }
1110
+ /** Thrown when a second `WorkflowDedupTable` is instantiated in the same app. */
1111
+ declare class WorkflowDedupTableDuplicateError extends Error {
1112
+ /** @param message - human-readable description of the duplicate. */
1113
+ constructor(message: string);
1114
+ }
1115
+ /** Thrown when a consumerName violates the TR-015 invariants. */
1116
+ declare class WorkflowDedupConsumerNameInvalidError extends Error {
1117
+ /** @param message - human-readable description of the invariant violation. */
1118
+ constructor(message: string);
1119
+ }
1120
+
729
1121
  /**
730
1122
  * @see sites/www-docs/content/packages/@openhi/constructs/components/event-bridge/data-event-bus.md
731
1123
  */
@@ -756,6 +1148,21 @@ declare class OpsEventBus extends EventBus {
756
1148
  constructor(scope: Construct, props?: EventBusProps);
757
1149
  }
758
1150
 
1151
+ /**
1152
+ * @see sites/www-docs/content/packages/@openhi/constructs/components/event-bridge/control-event-bus.md
1153
+ */
1154
+ declare class ControlEventBus extends EventBus {
1155
+ /*****************************************************************************
1156
+ *
1157
+ * Return a name for this EventBus based on the stack environment hash. This
1158
+ * name is common across all stacks since it's using the environment hash in
1159
+ * its name.
1160
+ *
1161
+ ****************************************************************************/
1162
+ static getEventBusName(scope: Construct): string;
1163
+ constructor(scope: Construct, props?: EventBusProps);
1164
+ }
1165
+
759
1166
  /**
760
1167
  * SSM parameter names that publish the Postgres replica's coordinates so other
761
1168
  * stacks (notably the REST API stack) can discover them without a direct CDK
@@ -1000,6 +1407,47 @@ declare class StaticHosting extends Construct {
1000
1407
  constructor(scope: Construct, id: string, props?: StaticHostingProps);
1001
1408
  }
1002
1409
 
1410
+ interface ProvisionDefaultWorkspaceLambdaProps {
1411
+ /**
1412
+ * DynamoDB data store table. Used for the Lambda's `DYNAMO_TABLE_NAME`
1413
+ * env var and for granting the Lambda the writes + GSI queries it needs
1414
+ * to provision default control-plane resources.
1415
+ */
1416
+ readonly dataStoreTable: ITable;
1417
+ /**
1418
+ * Control-plane event bus that the EventBridge Rule listens on.
1419
+ */
1420
+ readonly controlEventBus: IEventBus;
1421
+ }
1422
+ /**
1423
+ * Lambda used by the user-onboarding workflow to create a user's default
1424
+ * Tenant, Workspace, Memberships, and RoleAssignment.
1425
+ *
1426
+ * Owns the EventBridge Rule that routes the default-workspace onboarding
1427
+ * event to itself, and the IAM permissions it needs on the data store
1428
+ * table — colocating routing + permissions with the function they target.
1429
+ */
1430
+ declare class ProvisionDefaultWorkspaceLambda extends Construct {
1431
+ readonly lambda: NodejsFunction;
1432
+ readonly rule: Rule;
1433
+ constructor(scope: Construct, props: ProvisionDefaultWorkspaceLambdaProps);
1434
+ }
1435
+
1436
+ /**
1437
+ * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/user-onboarding/user-onboarding-workflow.md
1438
+ */
1439
+ interface UserOnboardingWorkflowProps {
1440
+ readonly controlEventBus: IEventBus;
1441
+ readonly dataStoreTable: ITable;
1442
+ }
1443
+ /**
1444
+ * Control-plane workflow for onboarding users after Cognito confirmation.
1445
+ */
1446
+ declare class UserOnboardingWorkflow extends Construct {
1447
+ readonly provisionDefaultWorkspace: ProvisionDefaultWorkspaceLambda;
1448
+ constructor(scope: Construct, props: UserOnboardingWorkflowProps);
1449
+ }
1450
+
1003
1451
  /**
1004
1452
  * @see sites/www-docs/content/packages/@openhi/constructs/services/open-hi-auth-service.md
1005
1453
  */
@@ -1030,7 +1478,7 @@ interface OpenHiAuthServiceProps extends OpenHiServiceProps {
1030
1478
  * @public
1031
1479
  */
1032
1480
  declare class OpenHiAuthService extends OpenHiService {
1033
- static readonly SERVICE_TYPE = "auth";
1481
+ static readonly SERVICE_TYPE: "auth";
1034
1482
  /**
1035
1483
  * Returns an IUserPool by looking up the Auth stack's User Pool ID from SSM.
1036
1484
  */
@@ -1065,6 +1513,7 @@ declare class OpenHiAuthService extends OpenHiService {
1065
1513
  readonly preTokenGenerationLambda: IFunction;
1066
1514
  readonly postAuthenticationLambda: IFunction;
1067
1515
  readonly postConfirmationLambda: IFunction;
1516
+ readonly userOnboardingWorkflow: UserOnboardingWorkflow;
1068
1517
  readonly userPool: IUserPool;
1069
1518
  readonly userPoolClient: IUserPoolClient;
1070
1519
  readonly userPoolDomain: IUserPoolDomain;
@@ -1081,6 +1530,7 @@ declare class OpenHiAuthService extends OpenHiService {
1081
1530
  * would collide.
1082
1531
  */
1083
1532
  private _dataStoreTable;
1533
+ private _controlEventBus;
1084
1534
  constructor(ohEnv: OpenHiEnvironment, props?: OpenHiAuthServiceProps);
1085
1535
  /**
1086
1536
  * Creates the KMS key for the Cognito User Pool and exports its ARN to SSM.
@@ -1103,13 +1553,13 @@ declare class OpenHiAuthService extends OpenHiService {
1103
1553
  protected createPostAuthenticationLambda(): IFunction;
1104
1554
  /**
1105
1555
  * Creates the Post Confirmation Lambda (Cognito trigger). On sign-up
1106
- * confirmation, writes the new user's default Tenant, Workspace,
1107
- * Memberships, and `tenant-user` RoleAssignment, plus a User record
1108
- * carrying the Cognito `sub` and current tenant/workspace pointers
1109
- * (ADR 2026-03-17-01 invariants).
1556
+ * confirmation, publishes a control-plane workflow event; provisioning lives
1557
+ * behind EventBridge.
1110
1558
  */
1111
1559
  protected createPostConfirmationLambda(): IFunction;
1560
+ protected createUserOnboardingWorkflow(): UserOnboardingWorkflow;
1112
1561
  private dataStoreTable;
1562
+ private controlEventBus;
1113
1563
  /**
1114
1564
  * Creates the Cognito User Pool and exports its ID to SSM.
1115
1565
  * Look up via {@link OpenHiAuthService.userPoolFromConstruct}.
@@ -1141,9 +1591,8 @@ declare class OpenHiAuthService extends OpenHiService {
1141
1591
  */
1142
1592
  protected grantPostAuthenticationPermissions(): void;
1143
1593
  /**
1144
- * Grants the Post Confirmation Lambda write access to the data store
1145
- * table (and its GSIs) so it can seed the new user's Tenant, Workspace,
1146
- * Memberships, RoleAssignment, and User records on sign-up confirmation.
1594
+ * Grants the Post Confirmation Lambda publish-only access to the
1595
+ * control-plane event bus. Workflow Lambdas own DynamoDB writes.
1147
1596
  */
1148
1597
  protected grantPostConfirmationPermissions(): void;
1149
1598
  /**
@@ -1172,6 +1621,52 @@ declare class OpenHiAuthService extends OpenHiService {
1172
1621
  protected createUserPoolDomain(): IUserPoolDomain;
1173
1622
  }
1174
1623
 
1624
+ interface PlatformDeployBridgeLambdaProps {
1625
+ /** Destination control event bus the bridge republishes onto. */
1626
+ readonly controlEventBus: IEventBus;
1627
+ }
1628
+ /**
1629
+ * Lambda that bridges CloudFormation Stack Status Change events from the
1630
+ * default AWS bus into typed `platform.deployment-completed.v1` envelopes on
1631
+ * the OpenHI control event bus.
1632
+ *
1633
+ * Owns its EventBridge Rule (on the default AWS bus) and the IAM
1634
+ * permissions it needs — colocating routing + permissions with the
1635
+ * function they target.
1636
+ *
1637
+ * The EventBridge rule pre-filters by stack-id prefix so the rule (and
1638
+ * therefore the Lambda) only fires on the host stack's own branch deploys.
1639
+ * This prevents cross-branch leak when multiple branches are deployed into
1640
+ * the same account.
1641
+ */
1642
+ declare class PlatformDeployBridgeLambda extends Construct {
1643
+ readonly lambda: NodejsFunction;
1644
+ readonly rule: Rule;
1645
+ constructor(scope: Construct, props: PlatformDeployBridgeLambdaProps);
1646
+ }
1647
+
1648
+ /**
1649
+ * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/platform-deploy-bridge/index.md
1650
+ */
1651
+ interface PlatformDeployBridgeProps {
1652
+ /** Destination control event bus the bridge republishes onto. */
1653
+ readonly controlEventBus: IEventBus;
1654
+ }
1655
+ /**
1656
+ * Source-side reactor that watches CloudFormation Stack Status Change
1657
+ * events on the default AWS bus and republishes terminal-success events
1658
+ * (`CREATE_COMPLETE` / `UPDATE_COMPLETE`) for OpenHi-tagged stacks onto
1659
+ * the control event bus as `platform.deployment-completed.v1`.
1660
+ *
1661
+ * Implements row 4 of the workflow placement matrix
1662
+ * (codedrifters/openhi#953): ops-plane reactor → republishes to
1663
+ * control event bus.
1664
+ */
1665
+ declare class PlatformDeployBridge extends Construct {
1666
+ readonly bridgeLambda: PlatformDeployBridgeLambda;
1667
+ constructor(scope: Construct, props: PlatformDeployBridgeProps);
1668
+ }
1669
+
1175
1670
  /**
1176
1671
  * @see sites/www-docs/content/packages/@openhi/constructs/services/open-hi-global-service.md
1177
1672
  */
@@ -1179,12 +1674,12 @@ interface OpenHiGlobalServiceProps extends OpenHiServiceProps {
1179
1674
  }
1180
1675
  /**
1181
1676
  * Global Infrastructure stack: owns global DNS, certificates, and the
1182
- * cross-region EventBridge buses (data, ops). Resources (root zone, optional
1183
- * child zone, wildcard cert, data/ops buses) are created in protected methods;
1184
- * subclasses may override to customize.
1677
+ * cross-region EventBridge buses (data, ops, control). Resources (root zone,
1678
+ * optional child zone, wildcard cert, data/ops/control buses) are created in
1679
+ * protected methods; subclasses may override to customize.
1185
1680
  */
1186
1681
  declare class OpenHiGlobalService extends OpenHiService {
1187
- static readonly SERVICE_TYPE = "global";
1682
+ static readonly SERVICE_TYPE: "global";
1188
1683
  /**
1189
1684
  * Returns an IHostedZone from the given attributes (no SSM). Use when the zone is imported from config.
1190
1685
  */
@@ -1208,6 +1703,19 @@ declare class OpenHiGlobalService extends OpenHiService {
1208
1703
  * Returns the ops event bus by name (deterministic per branch). Use from other stacks to obtain an IEventBus reference.
1209
1704
  */
1210
1705
  static opsEventBusFromConstruct(scope: Construct): IEventBus;
1706
+ /**
1707
+ * Returns the control-plane event bus by name (deterministic per branch). Use from other stacks to obtain an IEventBus reference.
1708
+ */
1709
+ static controlEventBusFromConstruct(scope: Construct): IEventBus;
1710
+ /**
1711
+ * Returns the workflow dedup table by name (deterministic per branch).
1712
+ * Use from other stacks to obtain an ITable reference. Consumer Lambdas
1713
+ * are typically wired via `WorkflowDedupTable.grantConsumer(fn, name)`
1714
+ * on the owning service's `workflowDedupTable` reference; the
1715
+ * `tableNameFromLookup` / `tableArnFromLookup` SSM helpers on the
1716
+ * construct cover cross-stack consumers that need only the name/ARN.
1717
+ */
1718
+ static workflowDedupTableNameFromLookup(scope: Construct): string;
1211
1719
  get serviceType(): string;
1212
1720
  /** Override so this.props is typed with this service's options. */
1213
1721
  props: OpenHiGlobalServiceProps;
@@ -1224,6 +1732,23 @@ declare class OpenHiGlobalService extends OpenHiService {
1224
1732
  * Other stacks obtain it via {@link OpenHiGlobalService.opsEventBusFromConstruct}.
1225
1733
  */
1226
1734
  readonly opsEventBus: IEventBus;
1735
+ /**
1736
+ * Event bus for control-plane lifecycle and command events.
1737
+ * Other stacks obtain it via {@link OpenHiGlobalService.controlEventBusFromConstruct}.
1738
+ */
1739
+ readonly controlEventBus: IEventBus;
1740
+ /**
1741
+ * Bridge that watches CloudFormation Stack Status Change events on the
1742
+ * default AWS bus and republishes terminal-success events for OpenHi-tagged
1743
+ * stacks onto {@link controlEventBus} as `platform.deployment-completed.v1`.
1744
+ */
1745
+ readonly platformDeployBridge: PlatformDeployBridge;
1746
+ /**
1747
+ * Shared dedup table every retryable workflow consumer dedupes against
1748
+ * (TR-015). Singleton per deployment — provisioned here on the global
1749
+ * stack so consumer stacks reach it via SSM lookups, not props.
1750
+ */
1751
+ readonly workflowDedupTable: WorkflowDedupTable;
1227
1752
  constructor(ohEnv: OpenHiEnvironment, props?: OpenHiGlobalServiceProps);
1228
1753
  /**
1229
1754
  * Validates that config required for the Global stack is present.
@@ -1257,6 +1782,22 @@ declare class OpenHiGlobalService extends OpenHiService {
1257
1782
  * Override to customize.
1258
1783
  */
1259
1784
  protected createOpsEventBus(): IEventBus;
1785
+ /**
1786
+ * Creates the control-plane event bus.
1787
+ * Override to customize.
1788
+ */
1789
+ protected createControlEventBus(): IEventBus;
1790
+ /**
1791
+ * Creates the platform deploy bridge that republishes CloudFormation
1792
+ * Stack Status Change events onto the control event bus.
1793
+ * Override to customize.
1794
+ */
1795
+ protected createPlatformDeployBridge(): PlatformDeployBridge;
1796
+ /**
1797
+ * Creates the shared workflow dedup table (TR-015 singleton).
1798
+ * Override to customize.
1799
+ */
1800
+ protected createWorkflowDedupTable(): WorkflowDedupTable;
1260
1801
  }
1261
1802
 
1262
1803
  /**
@@ -1279,7 +1820,7 @@ declare const REST_API_BASE_URL_SSM_NAME = "REST_API_BASE_URL";
1279
1820
  * Resources are created in protected methods; subclasses may override to customize.
1280
1821
  */
1281
1822
  declare class OpenHiRestApiService extends OpenHiService {
1282
- static readonly SERVICE_TYPE = "rest-api";
1823
+ static readonly SERVICE_TYPE: "rest-api";
1283
1824
  /**
1284
1825
  * Returns an IHttpApi by looking up the REST API stack's HTTP API ID from SSM.
1285
1826
  */
@@ -1337,20 +1878,176 @@ declare class OpenHiRestApiService extends OpenHiService {
1337
1878
  protected createRootHttpApi(domainName: DomainName): RootHttpApi;
1338
1879
  }
1339
1880
 
1881
+ interface SeedDemoDataLambdaProps {
1882
+ /**
1883
+ * Data-store table the workflow upserts demo-data records into.
1884
+ * Wired via `DYNAMO_TABLE_NAME` env var; granted scoped read on the
1885
+ * Role PKs (pre-flight check) and scoped write on the enumerated
1886
+ * demo Tenant / Workspace / Membership / RoleAssignment / User PKs.
1887
+ */
1888
+ readonly dataStoreTable: ITable;
1889
+ /**
1890
+ * Control event bus that re-publishes
1891
+ * `platform.deployment-completed.v1` from the platform-deploy bridge.
1892
+ * The Rule mounts here.
1893
+ */
1894
+ readonly controlEventBus: IEventBus;
1895
+ /**
1896
+ * Cognito User Pool the workflow provisions dev users into. The
1897
+ * Lambda's IAM grant is scoped to this exact user-pool ARN — the
1898
+ * grant uses the user-pool ARN, **not** the wildcard formatArn
1899
+ * pattern used by `post-authentication-lambda` (that Lambda's
1900
+ * trigger-driven dependency cycle does not apply here, so the
1901
+ * tighter scope is safe).
1902
+ */
1903
+ readonly userPool: IUserPool;
1904
+ }
1905
+ /**
1906
+ * Lambda + EventBridge Rule pair for the seed-demo-data workflow.
1907
+ * Owns the routing (`source` / `detail-type` pattern), the scoped
1908
+ * DynamoDB grants, and the scoped Cognito Admin grant — co-locating
1909
+ * routing + permissions with the function they target. Wiring to the
1910
+ * workflow dedup table is the parent construct's job (it has the
1911
+ * singleton reference) and happens via `WorkflowDedupTable.grantConsumer`.
1912
+ *
1913
+ * Stage-gating is the parent's job too — this construct itself never
1914
+ * checks the stage. The CDK stage-router (`OpenHiDataService`)
1915
+ * decides whether to instantiate it at all on each stage.
1916
+ */
1917
+ declare class SeedDemoDataLambda extends Construct {
1918
+ readonly lambda: NodejsFunction;
1919
+ readonly rule: Rule;
1920
+ constructor(scope: Construct, props: SeedDemoDataLambdaProps);
1921
+ }
1922
+
1340
1923
  /**
1341
- * @see sites/www-docs/content/packages/@openhi/constructs/services/open-hi-data-service.md
1924
+ * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-demo-data/seed-demo-data-workflow.md
1925
+ */
1926
+ interface SeedDemoDataWorkflowProps {
1927
+ /** Control event bus carrying `platform.system-data-seeded.v1`. */
1928
+ readonly controlEventBus: IEventBus;
1929
+ /** Data-store table the workflow upserts demo-data records into. */
1930
+ readonly dataStoreTable: ITable;
1931
+ /** Cognito User Pool the workflow provisions dev users into. */
1932
+ readonly userPool: IUserPool;
1933
+ }
1934
+ /**
1935
+ * Control-plane workflow that fires on every platform deploy and
1936
+ * idempotently re-asserts the demo-data graph: placeholder tenant +
1937
+ * workspace, 3 demo tenants + 4 workspaces, and per-dev-user Cognito
1938
+ * users with their DynamoDB User records, Memberships, and
1939
+ * RoleAssignments.
1940
+ *
1941
+ * Mounted on the data-service stack so the IAM grants against the
1942
+ * data-store table stay local. The control event bus and the workflow
1943
+ * dedup table reach in cross-stack via the SSM lookups
1944
+ * `OpenHiGlobalService.controlEventBusFromConstruct` and
1945
+ * `WorkflowDedupTable.grantConsumerFromLookup` respectively. The
1946
+ * Cognito User Pool similarly reaches in via
1947
+ * `OpenHiAuthService.userPoolFromConstruct`.
1948
+ *
1949
+ * Non-prod-only: the CDK stage-router (`OpenHiDataService`)
1950
+ * conditionally constructs this workflow only on non-prod stages.
1951
+ * The construct itself never checks the stage — its absence in prod
1952
+ * stacks is the gate.
1342
1953
  */
1343
- interface OpenHiDataServiceProps extends OpenHiServiceProps {
1954
+ declare class SeedDemoDataWorkflow extends Construct {
1955
+ readonly seedDemoData: SeedDemoDataLambda;
1956
+ constructor(scope: Construct, props: SeedDemoDataWorkflowProps);
1344
1957
  }
1958
+
1959
+ /**
1960
+ * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-system-data/events.md
1961
+ */
1962
+ /**
1963
+ * Stable logical name this workflow registers with the shared
1964
+ * `WorkflowDedupTable` (TR-015). Used in both the construct grant
1965
+ * (`workflowDedupTable.grantConsumer(lambda, SEED_SYSTEM_DATA_CONSUMER_NAME)`)
1966
+ * and the handler's runtime `recordIfAbsent` call — keep them aligned by
1967
+ * importing this constant in both places.
1968
+ */
1969
+ declare const SEED_SYSTEM_DATA_CONSUMER_NAME = "seed-system-data";
1970
+ /**
1971
+ * Free-form `actor.system` value the handler stamps on the
1972
+ * `platform.system-data-seeded.v1` event it publishes when seeding
1973
+ * completes. Pinned here so the test can assert the wire value without
1974
+ * importing private handler internals.
1975
+ */
1976
+ declare const SEED_SYSTEM_DATA_ACTOR_SYSTEM = "seed-system-data";
1977
+ /**
1978
+ * Env var the Lambda construct injects with the control event bus
1979
+ * name. The handler reads it to build the publisher target when
1980
+ * emitting `platform.system-data-seeded.v1` after a successful seed.
1981
+ */
1982
+ declare const SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR = "CONTROL_EVENT_BUS_NAME";
1983
+
1984
+ interface SeedSystemDataLambdaProps {
1985
+ /**
1986
+ * Data-store table the workflow upserts platform-singleton control-plane
1987
+ * records into. Wired via `DYNAMO_TABLE_NAME` env var; granted scoped
1988
+ * write permission to the role records' partition keys only.
1989
+ */
1990
+ readonly dataStoreTable: ITable;
1991
+ /**
1992
+ * Control event bus that re-publishes
1993
+ * `platform.deployment-completed.v1` from the platform-deploy bridge.
1994
+ * The Rule mounts here.
1995
+ */
1996
+ readonly controlEventBus: IEventBus;
1997
+ }
1998
+ /**
1999
+ * Lambda + EventBridge Rule pair for the seed-system-data workflow. Owns
2000
+ * the routing (`source` / `detail-type` pattern) and the scoped data-store
2001
+ * grants — co-locating routing + permissions with the function they
2002
+ * target. Wiring to the workflow dedup table is the parent construct's
2003
+ * job (it has the singleton reference) and happens via
2004
+ * `WorkflowDedupTable.grantConsumer`.
2005
+ */
2006
+ declare class SeedSystemDataLambda extends Construct {
2007
+ readonly lambda: NodejsFunction;
2008
+ readonly rule: Rule;
2009
+ constructor(scope: Construct, props: SeedSystemDataLambdaProps);
2010
+ }
2011
+
2012
+ /**
2013
+ * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-system-data/seed-system-data-workflow.md
2014
+ */
2015
+ interface SeedSystemDataWorkflowProps {
2016
+ /** Control event bus carrying `platform.deployment-completed.v1`. */
2017
+ readonly controlEventBus: IEventBus;
2018
+ /** Data-store table the workflow upserts platform-singleton records into. */
2019
+ readonly dataStoreTable: ITable;
2020
+ }
2021
+ /**
2022
+ * Control-plane workflow that fires on every platform deploy and
2023
+ * idempotently re-asserts the platform-singleton control-plane records
2024
+ * (today: the three canonical Roles; future: additional system data
2025
+ * slotted in as sibling steps under the same dedup record).
2026
+ *
2027
+ * Mounted on the data-service stack so the IAM grants against the
2028
+ * data-store table stay local. The control event bus and the
2029
+ * workflow dedup table reach in cross-stack via the SSM lookups
2030
+ * `OpenHiGlobalService.controlEventBusFromConstruct` and
2031
+ * `WorkflowDedupTable.grantConsumerFromLookup` respectively.
2032
+ */
2033
+ declare class SeedSystemDataWorkflow extends Construct {
2034
+ readonly seedSystemData: SeedSystemDataLambda;
2035
+ constructor(scope: Construct, props: SeedSystemDataWorkflowProps);
2036
+ }
2037
+
2038
+ /**
2039
+ * @see sites/www-docs/content/packages/@openhi/constructs/services/open-hi-data-service.md
2040
+ */
2041
+ type OpenHiDataServiceProps = OpenHiServiceProps;
1345
2042
  /**
1346
2043
  * Data storage service stack: centralizes DynamoDB, S3, and other persistence
1347
2044
  * resources for OpenHI. Creates the single-table data store in a protected
1348
2045
  * method; subclasses may override to customize. EventBridge event buses
1349
- * (data, ops) are owned by {@link OpenHiGlobalService} so they deploy ahead of
1350
- * regional services.
2046
+ * (data, ops, control) are owned by {@link OpenHiGlobalService} so they deploy
2047
+ * ahead of regional services.
1351
2048
  */
1352
2049
  declare class OpenHiDataService extends OpenHiService {
1353
- static readonly SERVICE_TYPE = "data";
2050
+ static readonly SERVICE_TYPE: "data";
1354
2051
  /**
1355
2052
  * Returns the data store table by name. Use from other stacks (e.g. REST API Lambda) to obtain an ITable reference.
1356
2053
  */
@@ -1379,7 +2076,53 @@ declare class OpenHiDataService extends OpenHiService {
1379
2076
  * the read path is not wired up yet.
1380
2077
  */
1381
2078
  readonly dataStorePostgresReplica: DataStorePostgresReplica;
2079
+ /**
2080
+ * Deploy-triggered workflow that idempotently re-asserts the
2081
+ * platform-singleton control-plane records (today: the three canonical
2082
+ * Roles via `CONTROL_PLANE_ROLE_CONCEPTS`; future: additional system
2083
+ * data). Subscribes to `platform.deployment-completed.v1` on the
2084
+ * control event bus and dedups via the shared `WorkflowDedupTable`.
2085
+ */
2086
+ readonly seedSystemDataWorkflow: SeedSystemDataWorkflow;
2087
+ /**
2088
+ * Deploy-triggered workflow that idempotently re-asserts the demo
2089
+ * data graph (placeholder + 3 demo Tenants + 5 Workspaces; per
2090
+ * dev-user Cognito users with their DynamoDB User records,
2091
+ * Memberships, and RoleAssignments). **Non-prod only** —
2092
+ * `undefined` on prod stages. The synth-time stage gate in
2093
+ * {@link createSeedDemoDataWorkflow} is the only guarantee
2094
+ * separating prod stacks from the workflow's IAM grants and rule
2095
+ * target; the construct itself never checks the stage.
2096
+ */
2097
+ readonly seedDemoDataWorkflow?: SeedDemoDataWorkflow;
2098
+ /**
2099
+ * Cached control-event-bus lookup. `OpenHiGlobalService.controlEventBusFromConstruct`
2100
+ * registers a child `EventBus.fromEventBusName` construct with a
2101
+ * fixed id under the scope it is passed, so calling it twice on the
2102
+ * same `OpenHiDataService` instance collides. The cache mirrors the
2103
+ * `private controlEventBus()` pattern already used in
2104
+ * `OpenHiAuthService`. Use {@link controlEventBus} from this class
2105
+ * — never call the static lookup from inside `OpenHiDataService`.
2106
+ */
2107
+ private _controlEventBus;
1382
2108
  constructor(ohEnv: OpenHiEnvironment, props?: OpenHiDataServiceProps);
2109
+ /**
2110
+ * Lazily looks up the control event bus exactly once per
2111
+ * `OpenHiDataService` instance and caches the reference. Every
2112
+ * workflow that consumes the bus must read it through this method
2113
+ * — see {@link _controlEventBus} for the underlying collision risk.
2114
+ */
2115
+ private controlEventBus;
2116
+ /**
2117
+ * Creates the seed-system-data workflow. Override to customize.
2118
+ */
2119
+ protected createSeedSystemDataWorkflow(): SeedSystemDataWorkflow;
2120
+ /**
2121
+ * Creates the seed-demo-data workflow — but only on non-prod
2122
+ * stages. Returns `undefined` on prod so the workflow literally
2123
+ * does not exist in prod stacks. Override to customize.
2124
+ */
2125
+ protected createSeedDemoDataWorkflow(): SeedDemoDataWorkflow | undefined;
1383
2126
  /**
1384
2127
  * Creates the single-table DynamoDB data store.
1385
2128
  * Override to customize.
@@ -1395,7 +2138,7 @@ interface OpenHiGraphqlServiceProps extends OpenHiServiceProps {
1395
2138
  * {@link OpenHiGraphqlService.graphqlApiFromConstruct}.
1396
2139
  */
1397
2140
  declare class OpenHiGraphqlService extends OpenHiService {
1398
- static readonly SERVICE_TYPE = "graphql-api";
2141
+ static readonly SERVICE_TYPE: "graphql-api";
1399
2142
  /**
1400
2143
  * Returns the GraphQL API by looking up the GraphQL stack's API ID from SSM.
1401
2144
  * Use from other stacks to obtain an IGraphqlApi reference.
@@ -1409,5 +2152,5 @@ declare class OpenHiGraphqlService extends OpenHiService {
1409
2152
  protected createRootGraphqlApi(): RootGraphqlApi;
1410
2153
  }
1411
2154
 
1412
- export { ChildHostedZone, CognitoFixtureSeederClient, CognitoUserPool, CognitoUserPoolClient, CognitoUserPoolDomain, CognitoUserPoolKmsKey, DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES, DATA_STORE_CHANGE_DETAIL_TYPE, DATA_STORE_CHANGE_EVENT_SOURCE, DataEventBus, DataStoreHistoricalArchive, DataStorePostgresReplica, DiscoverableStringParameter, DynamoDbDataStore, OpenHiApp, OpenHiAuthService, OpenHiDataService, OpenHiEnvironment, OpenHiGlobalService, OpenHiGraphqlService, OpenHiRestApiService, OpenHiService, OpenHiStage, OpsEventBus, POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME, POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME, POSTGRES_REPLICA_SECRET_ARN_SSM_NAME, PostAuthenticationLambda, PostConfirmationLambda, PreTokenGenerationLambda, REST_API_BASE_URL_SSM_NAME, RootGraphqlApi, RootHostedZone, RootHttpApi, RootWildcardCertificate, STATIC_HOSTING_SERVICE_TYPE, StaticHosting, buildFhirCurrentResourceChangeDetail, getDynamoDbDataStoreTableName, getPostgresReplicaSchemaName };
1413
- export type { BuildParameterNameProps, ChildHostedZoneProps, CognitoFixtureSeederClientProps, DataStoreHistoricalArchiveProps, DataStorePostgresReplicaProps, DiscoverableStringParameterProps, DynamoDbDataStoreProps, FhirCurrentResourceChangeDetail, OpenHiAppProps, OpenHiAuthServiceProps, OpenHiDataServiceProps, OpenHiEnvironmentProps, OpenHiGlobalServiceProps, OpenHiGraphqlServiceProps, OpenHiRestApiServiceProps, OpenHiServiceProps, OpenHiServiceType, OpenHiStageProps, PostConfirmationLambdaProps, PreTokenGenerationLambdaProps, RootGraphqlApiProps, RootHttpApiProps, StaticHostingProps };
2155
+ export { BRIDGED_STATUSES, CLOUDFORMATION_EVENT_SOURCE, CLOUDFORMATION_STACK_STATUS_CHANGE_DETAIL_TYPE, CONTROL_EVENT_BUS_NAME_ENV_VAR, ChildHostedZone, CognitoFixtureSeederClient, CognitoUserPool, CognitoUserPoolClient, CognitoUserPoolDomain, CognitoUserPoolKmsKey, ControlEventBus, DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES, DATA_STORE_CHANGE_DETAIL_TYPE, DATA_STORE_CHANGE_EVENT_SOURCE, DEMO_PERIOD, DEMO_TENANT_SPECS, DEMO_URN_SYSTEM, DEV_USERS, DataEventBus, DataStoreHistoricalArchive, DataStorePostgresReplica, DiscoverableStringParameter, DynamoDbDataStore, OPENHI_REPO_TAG_KEY_ENV_VAR, OPENHI_RESOURCE_URN_SYSTEM, OPENHI_TAG_KEY_PREFIX_ENV_VAR, OPENHI_TAG_SUFFIX_BRANCH_NAME, OPENHI_TAG_SUFFIX_REPO_NAME, OPENHI_TAG_SUFFIX_SERVICE_TYPE, OPENHI_TAG_SUFFIX_STAGE_TYPE, OpenHiApp, OpenHiAuthService, OpenHiDataService, OpenHiEnvironment, OpenHiGlobalService, OpenHiGraphqlService, OpenHiRestApiService, OpenHiService, OpenHiStage, OpsEventBus, PLACEHOLDER_TENANT_ID, PLACEHOLDER_WORKSPACE_ID, PLATFORM_DEPLOY_BRIDGE_ACTOR_SYSTEM, PLATFORM_SCOPE_TENANT_ID, POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME, POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME, POSTGRES_REPLICA_SECRET_ARN_SSM_NAME, PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE, PlatformDeployBridge, PlatformDeployBridgeLambda, PostAuthenticationLambda, PostConfirmationLambda, PreTokenGenerationLambda, ProvisionDefaultWorkspaceLambda, REST_API_BASE_URL_SSM_NAME, RootGraphqlApi, RootHostedZone, RootHttpApi, RootWildcardCertificate, SEED_DEMO_DATA_CONSUMER_NAME, SEED_SYSTEM_DATA_ACTOR_SYSTEM, SEED_SYSTEM_DATA_CONSUMER_NAME, SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR, STATIC_HOSTING_SERVICE_TYPE, SeedDemoDataLambda, SeedDemoDataWorkflow, SeedSystemDataLambda, SeedSystemDataWorkflow, StaticHosting, USER_ONBOARDING_EVENT_SOURCE, UserOnboardingWorkflow, WorkflowDedupConsumerNameInvalidError, WorkflowDedupTable, WorkflowDedupTableDuplicateError, buildFhirCurrentResourceChangeDetail, buildProvisionDefaultWorkspaceRequestedDetail, demoBasePartitionKeys, demoDevUserPartitionKeys, demoMembershipId, demoMembershipPartitionKey, demoRoleAssignmentId, demoRoleAssignmentPartitionKey, demoRolesForUserInTenant, demoScenarioIdentifier, demoTenantPartitionKey, demoUserPartitionKey, demoWorkspacePartitionKey, getDynamoDbDataStoreTableName, getPostgresReplicaSchemaName, getWorkflowDedupTableName, openHiTagKey, openhiResourceIdentifier, rolePartitionKey };
2156
+ export type { BridgedStatus, BuildParameterNameProps, ChildHostedZoneProps, CloudFormationStackStatusChangeDetail, CognitoFixtureSeederClientProps, DataStoreHistoricalArchiveProps, DataStorePostgresReplicaProps, DemoDevUser, DemoTenantSpec, DemoWorkspaceSpec, DiscoverableStringParameterProps, DynamoDbDataStoreProps, FhirCurrentResourceChangeDetail, GrantConsumerOptions, OpenHiAppProps, OpenHiAuthServiceProps, OpenHiDataServiceProps, OpenHiEnvironmentProps, OpenHiGlobalServiceProps, OpenHiGraphqlServiceProps, OpenHiRestApiServiceProps, OpenHiServiceProps, OpenHiServiceType, OpenHiStageProps, PlatformDeployBridgeLambdaProps, PlatformDeployBridgeProps, PostConfirmationLambdaProps, PreTokenGenerationLambdaProps, ProvisionDefaultWorkspaceLambdaProps, ProvisionDefaultWorkspaceRequestedDetail, RootGraphqlApiProps, RootHttpApiProps, SeedDemoDataLambdaProps, SeedDemoDataWorkflowProps, SeedSystemDataLambdaProps, SeedSystemDataWorkflowProps, StaticHostingProps, UserOnboardingWorkflowProps, WorkflowDedupTableProps };