@openhi/constructs 0.0.107 → 0.0.108
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/{chunk-CHPEQRXU.mjs → chunk-6J7NQ6A4.mjs} +2 -2
- package/lib/{chunk-YU2HRNUP.mjs → chunk-AJ3G3THO.mjs} +2 -2
- package/lib/{chunk-L6UAP4KP.mjs → chunk-EST32BJ2.mjs} +3 -3
- package/lib/chunk-GT7SFZLP.mjs +126 -0
- package/lib/chunk-GT7SFZLP.mjs.map +1 -0
- package/lib/{chunk-36UPY7YQ.mjs → chunk-L3QHWDHB.mjs} +5 -5
- package/lib/chunk-LKG3I536.mjs +1383 -0
- package/lib/chunk-LKG3I536.mjs.map +1 -0
- package/lib/{chunk-VYDIGFIX.mjs → chunk-QR5JVSCF.mjs} +11 -1
- package/lib/chunk-QR5JVSCF.mjs.map +1 -0
- package/lib/{chunk-AO3E22CS.mjs → chunk-WWGJZNXJ.mjs} +74 -4
- package/lib/chunk-WWGJZNXJ.mjs.map +1 -0
- package/lib/chunk-ZGOHB4RA.mjs +96 -0
- package/lib/chunk-ZGOHB4RA.mjs.map +1 -0
- package/lib/index.js +1043 -5
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +5 -5
- package/lib/pre-token-generation.handler.js +1356 -10
- package/lib/pre-token-generation.handler.js.map +1 -1
- package/lib/pre-token-generation.handler.mjs +103 -5
- package/lib/pre-token-generation.handler.mjs.map +1 -1
- package/lib/provision-default-workspace.handler.js +1172 -1
- package/lib/provision-default-workspace.handler.js.map +1 -1
- package/lib/provision-default-workspace.handler.mjs +5 -5
- package/lib/rest-api-lambda.handler.js +2769 -2434
- package/lib/rest-api-lambda.handler.js.map +1 -1
- package/lib/rest-api-lambda.handler.mjs +630 -1464
- package/lib/rest-api-lambda.handler.mjs.map +1 -1
- package/lib/seed-demo-data.handler.js +1173 -9
- package/lib/seed-demo-data.handler.js.map +1 -1
- package/lib/seed-demo-data.handler.mjs +5 -5
- package/lib/seed-system-data.handler.js +10 -0
- package/lib/seed-system-data.handler.js.map +1 -1
- package/lib/seed-system-data.handler.mjs +2 -2
- package/package.json +2 -2
- package/lib/chunk-AO3E22CS.mjs.map +0 -1
- package/lib/chunk-JUNL76HF.mjs +0 -428
- package/lib/chunk-JUNL76HF.mjs.map +0 -1
- package/lib/chunk-VYDIGFIX.mjs.map +0 -1
- package/lib/chunk-YZZDUJHI.mjs +0 -37
- package/lib/chunk-YZZDUJHI.mjs.map +0 -1
- /package/lib/{chunk-CHPEQRXU.mjs.map → chunk-6J7NQ6A4.mjs.map} +0 -0
- /package/lib/{chunk-YU2HRNUP.mjs.map → chunk-AJ3G3THO.mjs.map} +0 -0
- /package/lib/{chunk-L6UAP4KP.mjs.map → chunk-EST32BJ2.mjs.map} +0 -0
- /package/lib/{chunk-36UPY7YQ.mjs.map → chunk-L3QHWDHB.mjs.map} +0 -0
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../config/src/open-hi-config.ts","../../config/src/index.ts","../../workflows/src/envelope-version.ts","../../workflows/src/envelope.ts","../../workflows/src/sources.ts","../../workflows/src/detail-types/registry.ts","../../workflows/src/detail-types/platform.ts","../../workflows/src/detail-types/index.ts","../../workflows/src/publisher.ts","../../workflows/src/consumer.ts","../../workflows/src/dedup/env.ts","../../workflows/src/dedup/workflow-dedup-client.ts","../../workflows/src/dedup/index.ts","../../workflows/src/index.ts","../src/index.ts","../src/app/open-hi-app.ts","../src/app/open-hi-environment.ts","../src/app/open-hi-stage.ts","../src/app/open-hi-service.ts","../src/components/acm/root-wildcard-certificate.ts","../src/components/api-gateway/root-http-api.ts","../src/components/app-sync/root-graphql-api.ts","../src/components/ssm/discoverable-string-parameter.ts","../src/components/cognito/cognito-fixture-seeder-client.ts","../src/components/cognito/cognito-user-pool.ts","../src/components/cognito/cognito-user-pool-client.ts","../src/components/cognito/cognito-user-pool-domain.ts","../src/components/cognito/cognito-user-pool-kms-key.ts","../src/components/cognito/post-authentication-lambda.ts","../src/components/cognito/post-confirmation-lambda.ts","../src/components/cognito/pre-token-generation-lambda.ts","../src/components/dynamodb/dynamodb-stream-record.ts","../src/components/dynamodb/data-store-change-events.ts","../src/components/dynamodb/data-store-historical-archive.ts","../src/components/dynamodb/dynamo-db-data-store.ts","../src/components/dynamodb/workflow-dedup-table.ts","../src/components/event-bridge/data-event-bus.ts","../src/components/event-bridge/ops-event-bus.ts","../src/components/event-bridge/control-event-bus.ts","../src/components/postgres/data-store-postgres-replica.ts","../src/components/route-53/child-hosted-zone.ts","../src/components/route-53/root-hosted-zone.ts","../src/components/static-hosting/static-hosting.ts","../src/services/open-hi-auth-service.ts","../src/services/open-hi-data-service.ts","../src/services/open-hi-global-service.ts","../src/workflows/control-plane/platform-deploy-bridge/events.ts","../src/workflows/control-plane/platform-deploy-bridge/platform-deploy-bridge.ts","../src/workflows/control-plane/platform-deploy-bridge/platform-deploy-bridge-lambda.ts","../src/workflows/control-plane/seed-demo-data/events.ts","../src/workflows/control-plane/seed-demo-data/seed-demo-data-lambda.ts","../src/workflows/control-plane/seed-demo-data/seed-demo-data.handler.ts","../src/data/dynamo/dynamo-control-service.ts","../src/data/dynamo/dynamo-client.ts","../src/data/dynamo/entities/control/configuration-entity.ts","../src/data/dynamo/entities/control/control-entity-common.ts","../src/data/dynamo/shard.ts","../src/data/dynamo/entities/control/membership-entity.ts","../src/data/dynamo/entities/control/role-entity.ts","../src/data/dynamo/entities/control/roleassignment-entity.ts","../src/data/dynamo/entities/control/tenant-entity.ts","../src/data/dynamo/entities/control/user-entity.ts","../src/data/dynamo/entities/control/workspace-entity.ts","../src/data/operations/control/membership/membership-create-operation.ts","../src/data/operations/control/roleassignment/roleassignment-create-operation.ts","../src/data/operations/control/tenant/tenant-create-operation.ts","../src/data/operations/control/workspace/workspace-create-operation.ts","../src/workflows/control-plane/seed-demo-data/seed-demo-data-workflow.ts","../src/workflows/control-plane/seed-system-data/events.ts","../src/workflows/control-plane/seed-system-data/seed-system-data-lambda.ts","../src/workflows/control-plane/seed-system-data/seed-system-data-workflow.ts","../src/workflows/control-plane/user-onboarding/events.ts","../src/workflows/control-plane/user-onboarding/provision-default-workspace-lambda.ts","../src/workflows/control-plane/user-onboarding/user-onboarding-workflow.ts","../src/services/open-hi-rest-api-service.ts","../src/data/lambda/cors-options-lambda.ts","../src/data/lambda/rest-api-lambda.ts","../src/services/open-hi-graphql-service.ts"],"sourcesContent":["/*******************************************************************************\n *\n * OpenHi Config\n *\n * These types are kept in their own package to prevent dependency conflicts and\n * conditions between @openhi/constructs and @openhi/platform..\n *\n ******************************************************************************/\n\n/**\n * Stage Types\n *\n * What stage of deployment is this? Dev, staging, or prod?\n */\nexport const OPEN_HI_STAGE = {\n /**\n * Development environment, typically used for testing and development.\n */\n DEV: \"dev\",\n /**\n * Staging environment, used for pre-production testing.\n */\n STAGE: \"stage\",\n /**\n * Production environment, used for live deployments.\n */\n PROD: \"prod\",\n} as const;\n\n/**\n * Above const as a type.\n */\nexport type OpenHiStageType =\n (typeof OPEN_HI_STAGE)[keyof typeof OPEN_HI_STAGE];\n\n/**\n * Deployment Target Role\n *\n * Is this (account, region) the primary or a secondary deployment target for the stage?\n * Works for both multi-region (different regions) and cellular (same region, different accounts).\n */\nexport const OPEN_HI_DEPLOYMENT_TARGET_ROLE = {\n /**\n * The primary deployment target for this stage (main account/region).\n * For example, the base DynamoDB region for global tables.\n */\n PRIMARY: \"primary\",\n\n /**\n * A secondary deployment target for this stage (additional account/region).\n * For example, a replica region for a global DynamoDB table, or another cell in the same region.\n */\n SECONDARY: \"secondary\",\n} as const;\n\n/**\n * Above const as a type.\n */\nexport type OpenHiDeploymentTargetRoleType =\n (typeof OPEN_HI_DEPLOYMENT_TARGET_ROLE)[keyof typeof OPEN_HI_DEPLOYMENT_TARGET_ROLE];\n\nexport interface OpenHiEnvironmentConfig {\n account: string;\n region: string;\n /**\n * Route53 zone containing DNS for this service.\n */\n hostedZoneId?: string;\n zoneName?: string;\n}\n\n/**\n * Represents the configuration for OpenHi services across different stages and\n * deployment targets.\n */\nexport interface OpenHiConfig {\n versions?: {\n cdk?: {\n cdkLibVersion?: string;\n cdkCliVersion?: string;\n };\n };\n deploymentTargets?: {\n [OPEN_HI_STAGE.DEV]?: {\n [OPEN_HI_DEPLOYMENT_TARGET_ROLE.PRIMARY]?: OpenHiEnvironmentConfig;\n [OPEN_HI_DEPLOYMENT_TARGET_ROLE.SECONDARY]?: Array<OpenHiEnvironmentConfig>;\n };\n [OPEN_HI_STAGE.STAGE]?: {\n [OPEN_HI_DEPLOYMENT_TARGET_ROLE.PRIMARY]?: OpenHiEnvironmentConfig;\n [OPEN_HI_DEPLOYMENT_TARGET_ROLE.SECONDARY]?: Array<OpenHiEnvironmentConfig>;\n };\n [OPEN_HI_STAGE.PROD]?: {\n [OPEN_HI_DEPLOYMENT_TARGET_ROLE.PRIMARY]?: OpenHiEnvironmentConfig;\n [OPEN_HI_DEPLOYMENT_TARGET_ROLE.SECONDARY]?: Array<OpenHiEnvironmentConfig>;\n };\n };\n}\n","export * from \"./open-hi-config\";\n","/**\n * Envelope version pinned by the publisher SDK on every emitted envelope.\n *\n * Shape is `\"<major>.<minor>\"` per TR-016 §Open Items #3 (P1).\n * Additive optional fields bump the minor; breaking changes bump the\n * major and require a documented overlap window in the consumer SDK.\n */\nexport const ENVELOPE_VERSION = \"1.0\";\n\nconst ENVELOPE_VERSION_PATTERN = /^\\d+\\.\\d+$/;\n\n/**\n * Lowest envelope major version this SDK's consumer parser accepts.\n *\n * Bump this when a deprecated major reaches end-of-life and the\n * overlap window closes.\n */\nconst MIN_SUPPORTED_MAJOR = 1;\n\n/**\n * Highest envelope major version this SDK's consumer parser accepts.\n *\n * Bump this in lockstep with `ENVELOPE_VERSION` whenever a new major\n * ships; the consumer always supports its own publish major.\n */\nconst MAX_SUPPORTED_MAJOR = 1;\n\n/**\n * Return `true` when `version` is shaped `<major>.<minor>` and its\n * major lies within `[MIN_SUPPORTED_MAJOR, MAX_SUPPORTED_MAJOR]`.\n */\nexport function isSupportedEnvelopeVersion(version: string): boolean {\n if (!ENVELOPE_VERSION_PATTERN.test(version)) {\n return false;\n }\n const major = Number.parseInt(version.split(\".\")[0], 10);\n return major >= MIN_SUPPORTED_MAJOR && major <= MAX_SUPPORTED_MAJOR;\n}\n","import type { OhiJwtClaims } from \"@openhi/types\";\n\n/**\n * Discriminated-union actor field per ADR-016 §Decision #3.\n *\n * User-initiated workflows project the four ADR-014 JWT claims (with\n * `ohi_tid` / `ohi_wid` required — a workflow event without a tenant\n * + workspace context belongs to the `system` variant); bootstrap\n * workflows run before a User exists and carry the bootstrap-role\n * name instead. The `system` value is free-form pending the\n * onboarding-bootstrap IAM role TR (TR-016 §Open Items #4).\n */\nexport type WorkflowActor = WorkflowUserActor | WorkflowSystemActor;\n\n/** User-actor variant — required projection of the ADR-014 JWT claims. */\nexport interface WorkflowUserActor {\n readonly ohi_tid: string;\n readonly ohi_wid: string;\n readonly ohi_uid: string;\n readonly ohi_uname: string;\n}\n\n/** Bootstrap-actor variant — carries the bootstrap-role name. */\nexport interface WorkflowSystemActor {\n readonly system: string;\n}\n\n/**\n * Type guard for the user-actor variant.\n */\nexport function isWorkflowUserActor(\n actor: WorkflowActor,\n): actor is WorkflowUserActor {\n return (actor as WorkflowUserActor).ohi_uid !== undefined;\n}\n\n/**\n * Type guard for the system-actor variant.\n */\nexport function isWorkflowSystemActor(\n actor: WorkflowActor,\n): actor is WorkflowSystemActor {\n return (actor as WorkflowSystemActor).system !== undefined;\n}\n\n/**\n * Standard workflow event envelope per ADR-016 §Decision #3 / TR-016.\n *\n * The generic `TPayload` parameter narrows the per-workflow payload.\n *\n * Field naming note: the payload lives under `payload` (not `detail`)\n * deliberately. EventBridge's outer event already has its own `detail`\n * field that carries this whole envelope; nesting another `detail`\n * inside it produced double-`detail` paths in rule patterns\n * (`detail.detail.<x>`) that consistently bit consumers writing\n * filters. The unambiguous name is worth the rename.\n */\nexport interface WorkflowEvent<TPayload = Record<string, unknown>> {\n /** Per-event UUID. Dedup keys on `(eventId, attempt)`. */\n readonly eventId: string;\n /** 1-indexed delivery attempt. */\n readonly attempt: number;\n /** Originating cross-plane chain identifier. */\n readonly correlationId: string;\n /** Immediate predecessor event id, or null for a chain origin. */\n readonly causationId: string | null;\n /** Discriminated actor — JWT-claim projection or bootstrap-role marker. */\n readonly actor: WorkflowActor;\n /** ISO-8601 timestamp marking when the envelope was constructed. */\n readonly occurredAt: string;\n /** Semver-shaped envelope version (e.g. `\"1.0\"`). */\n readonly envelopeVersion: string;\n /** Per-workflow payload narrowed by the generic parameter. */\n readonly payload: TPayload;\n}\n\n/**\n * Promote a raw `OhiJwtClaims` projection to a fully-required\n * `WorkflowUserActor`.\n *\n * Throws `MissingActorContextError` when `ohi_tid` or `ohi_wid` is\n * absent — those claims are optional on the JWT but required on the\n * workflow user-actor.\n */\nexport function workflowUserActorFromClaims(\n claims: OhiJwtClaims,\n): WorkflowUserActor {\n if (claims.ohi_tid === undefined || claims.ohi_wid === undefined) {\n throw new MissingActorContextError(\n \"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.\",\n );\n }\n return {\n ohi_tid: claims.ohi_tid,\n ohi_wid: claims.ohi_wid,\n ohi_uid: claims.ohi_uid,\n ohi_uname: claims.ohi_uname,\n };\n}\n\n/** Thrown when JWT claims lack a field required by the workflow user-actor. */\nexport class MissingActorContextError extends Error {\n /** @param message - human-readable description of the missing claim. */\n constructor(message: string) {\n super(message);\n this.name = \"MissingActorContextError\";\n }\n}\n","/**\n * Per-bus `Source` constants for the three OpenHI EventBridge buses.\n *\n * Every workflow event carries one of these values in its EventBridge\n * `Source` field. Bus selection follows ADR-016 §Decision #1.\n *\n * @see https://github.com/codedrifters/openhi-planning/blob/main/docs/src/content/docs/requirements/architectural-decisions/ADR-016-workflow-boundary-strategy.md\n */\n\n/** Source for control-plane workflow events (Tenant / Workspace / User / Membership / Role / RoleAssignment / Configuration). */\nexport const OPENHI_CONTROL_SOURCE = \"openhi.control\" as const;\n\n/** Source for data-plane workflow events (committed writes to the FHIR data store). */\nexport const OPENHI_DATA_SOURCE = \"openhi.data\" as const;\n\n/** Source for ops-plane workflow events (platform telemetry, deployments, build events). */\nexport const OPENHI_OPS_SOURCE = \"openhi.ops\" as const;\n\n/**\n * Discriminated union over the three OpenHI EventBridge sources.\n *\n * A publisher that passes any other string for the EventBridge `Source`\n * field is rejected at compile time.\n */\nexport type OpenHiSource =\n | typeof OPENHI_CONTROL_SOURCE\n | typeof OPENHI_DATA_SOURCE\n | typeof OPENHI_OPS_SOURCE;\n\n/**\n * Default EventBridge bus name for each OpenHI source.\n *\n * Deployments may override per-source via\n * `PublisherOptions.busNameByPlane`.\n */\nexport const DEFAULT_BUS_NAME_BY_SOURCE: Record<OpenHiSource, string> = {\n [OPENHI_CONTROL_SOURCE]: \"openhi-control-event-bus\",\n [OPENHI_DATA_SOURCE]: \"openhi-data-event-bus\",\n [OPENHI_OPS_SOURCE]: \"openhi-ops-event-bus\",\n};\n","import type { OpenHiSource } from \"../sources\";\n\n/**\n * One entry in the workflow detail-type registry.\n *\n * Each registered detail-type binds a typed `detail` payload to:\n * - the EventBridge `Source` (and therefore the bus) it ships on,\n * - the `detail-type` string consumers subscribe to,\n * - per-event metadata (`crossPlane`, `dedupRequired`) read by\n * downstream tooling (AsyncAPI generator, placement matrix).\n *\n * The `_detail` property is a phantom marker type only — never\n * read, never written, never instantiated. It carries the\n * compile-time TDetail through the entry so callers of\n * `publishWorkflowEvent` / `parseWorkflowEvent` see the typed\n * payload at the call site.\n */\nexport interface WorkflowDetailTypeEntry<TDetail> {\n /** Versioned detail-type string, e.g. `tenant.onboarded.v1`. */\n readonly detailType: string;\n /** The bus this detail-type ships on (compile-time enforced). */\n readonly source: OpenHiSource;\n /** Phantom marker carrying the payload shape. */\n readonly _detail?: TDetail;\n /** Marks events that cross plane boundaries (placement-matrix metadata). */\n readonly crossPlane?: boolean;\n /** Marks events that retryable consumers MUST dedupe on. */\n readonly dedupRequired?: boolean;\n}\n\n/**\n * Declare a registry entry with type inference.\n *\n * Call sites:\n *\n * ```ts\n * export const TenantOnboardedV1 = defineDetailType<TenantOnboardedV1Detail>({\n * detailType: \"tenant.onboarded.v1\",\n * source: OPENHI_CONTROL_SOURCE,\n * dedupRequired: true,\n * });\n * ```\n *\n * Detail-type strings follow `<area>.<event>.<version>` (TR-016 §Open\n * Items #2). The conformance regex below is the platform-wide format.\n */\nexport function defineDetailType<TDetail>(\n entry: Omit<WorkflowDetailTypeEntry<TDetail>, \"_detail\">,\n): WorkflowDetailTypeEntry<TDetail> {\n if (!isWellFormedDetailType(entry.detailType)) {\n throw new InvalidDetailTypeRegistrationError(\n `Detail-type \"${entry.detailType}\" does not match the platform-wide format <area>.<event>.v<integer>. See TR-016 §Open Items #2.`,\n );\n }\n return entry;\n}\n\n/**\n * Pattern enforced on every registered detail-type:\n * `<area>.<event>.v<integer>` where each segment is lowercase\n * alphanumeric with optional dashes.\n *\n * Multi-level areas (e.g. `fhir.audit-event.recorded.v1`) are\n * intentionally not yet allowed; TR-016 §Open Items #2 defers that.\n */\nconst DETAIL_TYPE_PATTERN =\n /^[a-z0-9]+(?:-[a-z0-9]+)*\\.[a-z0-9]+(?:-[a-z0-9]+)*\\.v\\d+$/;\n\n/** Return `true` when `detailType` matches the platform-wide format. */\nexport function isWellFormedDetailType(detailType: string): boolean {\n return DETAIL_TYPE_PATTERN.test(detailType);\n}\n\n/** Thrown by `defineDetailType` when the supplied string violates the format. */\nexport class InvalidDetailTypeRegistrationError extends Error {\n /** @param message - human-readable description of the violation. */\n constructor(message: string) {\n super(message);\n this.name = \"InvalidDetailTypeRegistrationError\";\n }\n}\n","import { OPENHI_CONTROL_SOURCE } from \"../sources\";\nimport { defineDetailType } from \"./registry\";\n\n/**\n * `detail` payload for `platform.deployment-completed.v1`.\n *\n * Projected by the platform-deploy bridge from a CloudFormation\n * `Stack Status Change` event (`CREATE_COMPLETE` / `UPDATE_COMPLETE`)\n * on a tagged OpenHI platform stack. Downstream control-plane\n * workflows (e.g. seed-system-roles, seed-demo-data) subscribe\n * to this detail-type on the control event bus.\n */\nexport interface PlatformDeploymentCompletedV1Detail {\n /** CloudFormation stack name (`AWS::CloudFormation::Stack` `StackName`). */\n readonly stackName: string;\n /** Full CloudFormation stack ARN. */\n readonly stackId: string;\n /** AWS region the stack deployed into (e.g. `us-east-1`). */\n readonly region: string;\n /** 12-digit AWS account id the stack deployed into. */\n readonly accountId: string;\n /** Terminal stack status that triggered the bridge. */\n readonly status: \"CREATE_COMPLETE\" | \"UPDATE_COMPLETE\";\n /** Free-form reason text from CloudFormation; absent on most events. */\n readonly statusReason?: string;\n /**\n * Projected subset of stack tags. The bridge resolves tags via\n * `cloudformation:DescribeStacks` because the source EventBridge\n * event omits them.\n */\n readonly stackTags: ReadonlyArray<{\n readonly key: string;\n readonly value: string;\n }>;\n /** ISO-8601 timestamp from the source EventBridge `time` field. */\n readonly cloudformationEventTime: string;\n}\n\n/**\n * Registry entry for `platform.deployment-completed.v1`.\n *\n * Published on the control event bus (`OPENHI_CONTROL_SOURCE`) per\n * the workflow placement matrix (codedrifters/openhi#953 row 4):\n * the AWS-native source is the ops-plane default bus, but the bridge\n * republishes onto the control bus because the downstream consumers\n * are control-plane workflows.\n *\n * `dedupRequired: true` — at-least-once redelivery from EventBridge\n * means retryable consumers MUST dedupe on `(eventId, attempt)` via\n * `WorkflowDedupClient`.\n */\nexport const PlatformDeploymentCompletedV1 =\n defineDetailType<PlatformDeploymentCompletedV1Detail>({\n detailType: \"platform.deployment-completed.v1\",\n source: OPENHI_CONTROL_SOURCE,\n dedupRequired: true,\n });\n\n/**\n * `detail` payload for `platform.system-data-seeded.v1`.\n *\n * Published by the `seed-system-data` workflow after it has\n * idempotently re-asserted every platform-singleton control-plane\n * record (today: the three canonical Roles; future: additional system\n * data) on the back of a `platform.deployment-completed.v1` event.\n *\n * Downstream control-plane workflows that depend on the\n * platform-singleton records existing — `seed-demo-data`, for\n * example — subscribe to this detail-type instead of the raw\n * deploy-completion event so the dependency is enforced by a\n * happens-before edge rather than by EventBridge retry timing.\n */\nexport interface PlatformSystemDataSeededV1Detail {\n /**\n * EventBridge `eventId` of the originating\n * `platform.deployment-completed.v1` event that triggered the\n * system-data seeding. Propagated for correlation in logs and\n * downstream causation chains.\n */\n readonly sourceEventId: string;\n /**\n * Full CloudFormation stack ARN of the deploy that triggered the\n * system-data seeding. Mirrors the field on the originating\n * `PlatformDeploymentCompletedV1Detail`; downstream consumers can\n * filter by stack-id prefix without re-reading the source event.\n */\n readonly sourceStackId: string;\n /**\n * Number of platform-singleton records re-asserted on this run.\n * Useful for sanity checks and observability — divergence between\n * deploys signals either a generator-emitted catalog change or a\n * partial-failure recovery from the replay tooling.\n */\n readonly seededRecordCount: number;\n}\n\n/**\n * Registry entry for `platform.system-data-seeded.v1`.\n *\n * Published onto the control event bus (`OPENHI_CONTROL_SOURCE`).\n * `dedupRequired: true` — downstream consumers MUST dedup on\n * `(eventId, attempt)` via `WorkflowDedupClient`, same as every other\n * retryable consumer.\n */\nexport const PlatformSystemDataSeededV1 =\n defineDetailType<PlatformSystemDataSeededV1Detail>({\n detailType: \"platform.system-data-seeded.v1\",\n source: OPENHI_CONTROL_SOURCE,\n dedupRequired: true,\n });\n","export * from \"./platform\";\nexport * from \"./registry\";\n","import { randomUUID } from \"node:crypto\";\nimport {\n EventBridgeClient,\n PutEventsCommand,\n} from \"@aws-sdk/client-eventbridge\";\n\nimport type { WorkflowDetailTypeEntry } from \"./detail-types/registry\";\nimport type { WorkflowActor, WorkflowEvent } from \"./envelope\";\nimport { ENVELOPE_VERSION } from \"./envelope-version\";\nimport { DEFAULT_BUS_NAME_BY_SOURCE, type OpenHiSource } from \"./sources\";\n\n/**\n * Caller-supplied envelope context the publisher consumes.\n *\n * The actor is required on every publish — pre-provisioning bootstrap\n * workflows pass a `{ system: <role-name> }` actor.\n *\n * `correlationId` and `causationId` propagate from an upstream event\n * when this publisher is consuming-then-publishing; pass them through\n * verbatim from the inbound envelope's fields. Both are optional;\n * when omitted the publisher treats the publish as a chain origin\n * (`correlationId` = fresh UUID, `causationId` = null).\n */\nexport interface PublishContext {\n readonly actor: WorkflowActor;\n readonly correlationId?: string;\n readonly causationId?: string | null;\n}\n\n/**\n * Per-call output of a successful publish.\n */\nexport interface PublishResult {\n readonly eventId: string;\n}\n\n/**\n * Publisher overrides applied to every call against a single client.\n *\n * `eventIdGenerator`, `correlationIdGenerator`, and `now` are\n * test-only seams; production callers omit them and the publisher\n * uses `crypto.randomUUID()` and `new Date()`.\n */\nexport interface PublisherOptions {\n /** Override the default bus name for one or more sources. */\n readonly busNameByPlane?: Partial<Record<OpenHiSource, string>>;\n /** Test seam — supply a deterministic UUID generator for `eventId`. */\n readonly eventIdGenerator?: () => string;\n /** Test seam — supply a deterministic UUID generator for new `correlationId` values. */\n readonly correlationIdGenerator?: () => string;\n /** Test seam — supply a deterministic clock for `occurredAt`. */\n readonly now?: () => Date;\n}\n\n/**\n * Tree-shaped publisher client per ADR-016 Recommendation.\n *\n * The `publish` primitive accepts any registered detail-type and\n * returns a typed `PublishResult`. Downstream tree shaping\n * (`client.<bus>.<area>.<event>.publish(payload, ctx)`) is built from\n * the detail-type registry once entries are registered; until then,\n * callers invoke `client.publish(entry, payload, ctx)` directly.\n */\nexport interface WorkflowsClient {\n /**\n * Construct a workflow envelope around `payload` and publish it to\n * the EventBridge bus configured for `entry.source`.\n */\n publish<TPayload>(\n entry: WorkflowDetailTypeEntry<TPayload>,\n payload: TPayload,\n ctx: PublishContext,\n ): Promise<PublishResult>;\n}\n\n/**\n * Factory that returns a `WorkflowsClient` bound to a single\n * `EventBridgeClient`.\n */\nexport function workflowsClient(\n bridge: EventBridgeClient,\n options: PublisherOptions = {},\n): WorkflowsClient {\n return {\n publish: (entry, payload, ctx) =>\n publishWorkflowEvent(bridge, entry, payload, ctx, options),\n };\n}\n\n/**\n * Construct a workflow envelope and publish it via\n * `EventBridge.PutEvents`.\n *\n * Exposed as a stand-alone function for callers that prefer the\n * primitive over the `WorkflowsClient` indirection.\n */\nexport async function publishWorkflowEvent<TPayload>(\n bridge: EventBridgeClient,\n entry: WorkflowDetailTypeEntry<TPayload>,\n payload: TPayload,\n ctx: PublishContext,\n options: PublisherOptions = {},\n): Promise<PublishResult> {\n const eventIdGenerator = options.eventIdGenerator ?? (() => randomUUID());\n const correlationIdGenerator =\n options.correlationIdGenerator ?? (() => randomUUID());\n const now = options.now ?? (() => new Date());\n\n const envelope: WorkflowEvent<TPayload> = {\n eventId: eventIdGenerator(),\n attempt: 1,\n correlationId: ctx.correlationId ?? correlationIdGenerator(),\n causationId: ctx.causationId ?? null,\n actor: ctx.actor,\n occurredAt: now().toISOString(),\n envelopeVersion: ENVELOPE_VERSION,\n payload,\n };\n\n const busName =\n options.busNameByPlane?.[entry.source] ??\n DEFAULT_BUS_NAME_BY_SOURCE[entry.source];\n\n const result = await bridge.send(\n new PutEventsCommand({\n Entries: [\n {\n EventBusName: busName,\n Source: entry.source,\n DetailType: entry.detailType,\n Detail: JSON.stringify(envelope),\n },\n ],\n }),\n );\n\n if ((result.FailedEntryCount ?? 0) > 0) {\n const first = result.Entries?.[0];\n throw new WorkflowPublishError(\n `EventBridge rejected ${entry.detailType} publish on bus ${busName}: ${first?.ErrorCode ?? \"unknown\"} — ${first?.ErrorMessage ?? \"no error message\"}`,\n );\n }\n\n return { eventId: envelope.eventId };\n}\n\n/** Thrown when EventBridge rejects a `PutEvents` entry. */\nexport class WorkflowPublishError extends Error {\n /** @param message - human-readable description of the failed publish. */\n constructor(message: string) {\n super(message);\n this.name = \"WorkflowPublishError\";\n }\n}\n","import type { WorkflowDetailTypeEntry } from \"./detail-types/registry\";\nimport type { WorkflowEvent } from \"./envelope\";\nimport { isSupportedEnvelopeVersion } from \"./envelope-version\";\n\n/**\n * Structural shape of the EventBridge event objects this SDK's\n * consumer parses.\n *\n * Matches `@types/aws-lambda`'s `EventBridgeEvent<string, unknown>`\n * by structural compatibility without requiring callers to import\n * that types package — consumers may pass either an\n * `EventBridgeEvent` from `aws-lambda` or any record-shaped object\n * carrying the same keys.\n */\nexport interface EventBridgeEventLike {\n readonly source: string;\n readonly \"detail-type\": string;\n readonly detail: unknown;\n}\n\n/**\n * The `(eventId, attempt)` tuple every retryable consumer hands to\n * the `WorkflowDedupTable` client.\n */\nexport interface DedupKey {\n readonly eventId: string;\n readonly attempt: number;\n}\n\n/**\n * Output of `parseWorkflowEvent` — the validated envelope plus the\n * dedup tuple.\n */\nexport interface ParsedWorkflowEvent<TPayload> {\n readonly envelope: WorkflowEvent<TPayload>;\n readonly dedupKey: DedupKey;\n}\n\n/**\n * Parse an EventBridge event into a typed envelope and surface the\n * `(eventId, attempt)` tuple the dedup-table client consumes.\n *\n * Validates:\n * - `event.source` matches `expected.source`\n * - `event[\"detail-type\"]` matches `expected.detailType`\n * - the envelope's `envelopeVersion` is within the SDK's supported range\n * - every required envelope field is present and well-shaped\n */\nexport function parseWorkflowEvent<TPayload>(\n event: EventBridgeEventLike,\n expected: WorkflowDetailTypeEntry<TPayload>,\n): ParsedWorkflowEvent<TPayload> {\n if (event.source !== expected.source) {\n throw new InvalidWorkflowEventError(\n `EventBridge source \"${event.source}\" does not match expected detail-type's source \"${expected.source}\".`,\n );\n }\n\n if (event[\"detail-type\"] !== expected.detailType) {\n throw new InvalidWorkflowEventError(\n `EventBridge detail-type \"${event[\"detail-type\"]}\" does not match expected \"${expected.detailType}\".`,\n );\n }\n\n const candidate = asEnvelopeCandidate(event.detail);\n\n if (!isSupportedEnvelopeVersion(candidate.envelopeVersion)) {\n throw new UnsupportedEnvelopeVersionError(\n `Envelope version \"${candidate.envelopeVersion}\" is outside the SDK's supported range.`,\n );\n }\n\n const envelope: WorkflowEvent<TPayload> = {\n eventId: candidate.eventId,\n attempt: candidate.attempt,\n correlationId: candidate.correlationId,\n causationId: candidate.causationId,\n actor: candidate.actor,\n occurredAt: candidate.occurredAt,\n envelopeVersion: candidate.envelopeVersion,\n payload: candidate.payload as TPayload,\n };\n\n return {\n envelope,\n dedupKey: { eventId: envelope.eventId, attempt: envelope.attempt },\n };\n}\n\n/**\n * Validate that the EventBridge `detail` (which carries the workflow\n * envelope) has every required field with a plausible type. Returns a\n * typed `WorkflowEvent<unknown>` so the caller can narrow `payload`\n * once routing has succeeded.\n */\nfunction asEnvelopeCandidate(detail: unknown): WorkflowEvent<unknown> {\n if (detail === null || typeof detail !== \"object\") {\n throw new InvalidWorkflowEventError(\n \"EventBridge detail is not a non-null object.\",\n );\n }\n\n const obj = detail as Record<string, unknown>;\n\n assertString(obj, \"eventId\");\n assertPositiveInteger(obj, \"attempt\");\n assertString(obj, \"correlationId\");\n assertCausationId(obj);\n assertActor(obj);\n assertString(obj, \"occurredAt\");\n assertString(obj, \"envelopeVersion\");\n\n if (!(\"payload\" in obj)) {\n throw new InvalidWorkflowEventError(\n \"Envelope is missing required field: payload.\",\n );\n }\n\n return obj as unknown as WorkflowEvent<unknown>;\n}\n\nfunction assertString(\n obj: Record<string, unknown>,\n field: string,\n): asserts obj is Record<string, unknown> & Record<typeof field, string> {\n const value = obj[field];\n if (typeof value !== \"string\" || value.length === 0) {\n throw new InvalidWorkflowEventError(\n `Envelope field \"${field}\" must be a non-empty string.`,\n );\n }\n}\n\nfunction assertPositiveInteger(\n obj: Record<string, unknown>,\n field: string,\n): void {\n const value = obj[field];\n if (typeof value !== \"number\" || !Number.isInteger(value) || value < 1) {\n throw new InvalidWorkflowEventError(\n `Envelope field \"${field}\" must be a 1-indexed integer.`,\n );\n }\n}\n\nfunction assertCausationId(obj: Record<string, unknown>): void {\n if (!(\"causationId\" in obj)) {\n throw new InvalidWorkflowEventError(\n \"Envelope is missing required field: causationId.\",\n );\n }\n const value = obj.causationId;\n if (value !== null && (typeof value !== \"string\" || value.length === 0)) {\n throw new InvalidWorkflowEventError(\n 'Envelope field \"causationId\" must be a non-empty string or null.',\n );\n }\n}\n\nfunction assertActor(obj: Record<string, unknown>): void {\n const actor = obj.actor;\n if (actor === null || typeof actor !== \"object\") {\n throw new InvalidWorkflowEventError(\n 'Envelope field \"actor\" must be an object.',\n );\n }\n const actorObj = actor as Record<string, unknown>;\n const isUserActor =\n typeof actorObj.ohi_uid === \"string\" &&\n typeof actorObj.ohi_uname === \"string\" &&\n typeof actorObj.ohi_tid === \"string\" &&\n typeof actorObj.ohi_wid === \"string\";\n const isSystemActor = typeof actorObj.system === \"string\";\n if (!isUserActor && !isSystemActor) {\n throw new InvalidWorkflowEventError(\n 'Envelope field \"actor\" must be either a user-actor (ohi_tid, ohi_wid, ohi_uid, ohi_uname) or a system-actor ({ system: string }).',\n );\n }\n}\n\n/** Thrown when the event does not match the expected detail-type entry. */\nexport class InvalidWorkflowEventError extends Error {\n /** @param message - human-readable description of the validation failure. */\n constructor(message: string) {\n super(message);\n this.name = \"InvalidWorkflowEventError\";\n }\n}\n\n/** Thrown when the envelope version is outside the SDK's supported range. */\nexport class UnsupportedEnvelopeVersionError extends Error {\n /** @param message - human-readable description of the unsupported version. */\n constructor(message: string) {\n super(message);\n this.name = \"UnsupportedEnvelopeVersionError\";\n }\n}\n","/**\n * Environment-variable name the construct's `grantConsumer` integration\n * injects into a consumer Lambda; the runtime `WorkflowDedupClient`\n * reads it to discover the shared dedup table without a prop or import.\n *\n * The constant is the single cross-package contract between\n * `@openhi/constructs` (which emits the env var) and `@openhi/workflows`\n * (which consumes it). Renaming or removing it is a breaking change.\n */\nexport const WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR =\n \"OPENHI_WORKFLOW_DEDUP_TABLE_NAME\";\n\n/** Default TTL for dedup rows: 14 days, expressed in seconds (per TR-015). */\nexport const WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS = 14 * 24 * 60 * 60;\n\n/** Maximum length of a `consumerName` (per TR-015). */\nexport const WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH = 64;\n","import {\n ConditionalCheckFailedException,\n DynamoDBClient,\n PutItemCommand,\n UpdateItemCommand,\n} from \"@aws-sdk/client-dynamodb\";\n\nimport {\n WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS,\n WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH,\n WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR,\n} from \"./env\";\n\n/**\n * Inputs to `recordIfAbsent`.\n *\n * `eventId` and `attempt` are the dedup tuple every retryable\n * consumer derives from the standard envelope (see `parseWorkflowEvent`\n * and the `DedupKey` type); call sites typically spread the dedupKey\n * directly alongside the consumer name.\n */\nexport interface RecordIfAbsentInput {\n /** Stable logical name of the consumer. At most 64 chars; no whitespace. */\n readonly consumerName: string;\n /** Per-event UUID from the standard envelope. */\n readonly eventId: string;\n /** 1-indexed delivery attempt from the standard envelope. */\n readonly attempt: number;\n /** Override the 14-day default TTL. Must be a positive integer. */\n readonly ttlSeconds?: number;\n}\n\n/**\n * Result shape per TR-015. `recorded` is true on first delivery and\n * false on a duplicate; on a duplicate `alreadyProcessed` is also\n * true so callers can pattern-match without re-checking the boolean.\n */\nexport type RecordIfAbsentResult =\n | { readonly recorded: true }\n | { readonly recorded: false; readonly alreadyProcessed: true };\n\n/**\n * Inputs to `markFailed`.\n *\n * Updates the existing dedup row with `failed: true`, `failureReason`,\n * `failedAt` so the replay tooling (TR-016 follow-up) can re-publish\n * the originating event with a fresh `attempt`.\n */\nexport interface MarkFailedInput {\n /** Stable logical name of the consumer. */\n readonly consumerName: string;\n /** Per-event UUID. */\n readonly eventId: string;\n /** 1-indexed delivery attempt. */\n readonly attempt: number;\n /** Short string describing why the consumer gave up. */\n readonly reason: string;\n}\n\n/**\n * Runtime SDK every retryable workflow consumer calls before\n * performing its side-effect. See TR-015 for the contract.\n */\nexport interface WorkflowDedupClient {\n /**\n * Conditionally record a dedup token for the supplied consumer name\n * and dedup tuple. See `RecordIfAbsentResult` for the return shape.\n */\n recordIfAbsent(input: RecordIfAbsentInput): Promise<RecordIfAbsentResult>;\n /**\n * Mark the existing dedup row as permanently failed. Fire-and-forget\n * semantics for the caller; unexpected DynamoDB errors propagate.\n */\n markFailed(input: MarkFailedInput): Promise<void>;\n}\n\n/** Options shared by the factory and the standalone primitives. */\nexport interface WorkflowDedupClientOptions {\n /**\n * Table name. Defaults to `process.env[WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR]`\n * (populated by the `WorkflowDedupTable` construct's `grantConsumer`).\n */\n readonly tableName?: string;\n /** Override the 14-day default TTL for every `recordIfAbsent` call. */\n readonly defaultTtlSeconds?: number;\n /** Test seam — deterministic clock for `recordedAt` / `expiresAt`. */\n readonly now?: () => Date;\n}\n\n/** Factory that returns a `WorkflowDedupClient` bound to a single DynamoDB client. */\nexport function workflowDedupClient(\n dynamodb: DynamoDBClient,\n options: WorkflowDedupClientOptions = {},\n): WorkflowDedupClient {\n return {\n recordIfAbsent: (input) => recordIfAbsent(dynamodb, input, options),\n markFailed: (input) => markFailed(dynamodb, input, options),\n };\n}\n\n/**\n * Standalone primitive — exposed for callers that prefer it over the\n * `WorkflowDedupClient` indirection.\n */\nexport async function recordIfAbsent(\n dynamodb: DynamoDBClient,\n input: RecordIfAbsentInput,\n options: WorkflowDedupClientOptions = {},\n): Promise<RecordIfAbsentResult> {\n assertConsumerName(input.consumerName);\n assertPositiveInteger(input.attempt, \"attempt\");\n const ttlSeconds =\n input.ttlSeconds ??\n options.defaultTtlSeconds ??\n WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS;\n if (!Number.isInteger(ttlSeconds) || ttlSeconds <= 0) {\n throw new WorkflowDedupInvalidInputError(\n `ttlSeconds must be a positive integer; got ${ttlSeconds}.`,\n );\n }\n\n const tableName = resolveTableName(options.tableName);\n const now = (options.now ?? defaultNow)();\n const sk = encodeSortKey(input.eventId, input.attempt);\n const expiresAt = Math.floor(now.getTime() / 1000) + ttlSeconds;\n\n try {\n await dynamodb.send(\n new PutItemCommand({\n TableName: tableName,\n Item: {\n consumerName: { S: input.consumerName },\n sk: { S: sk },\n eventId: { S: input.eventId },\n attempt: { N: String(input.attempt) },\n recordedAt: { S: now.toISOString() },\n expiresAt: { N: String(expiresAt) },\n },\n ConditionExpression:\n \"attribute_not_exists(consumerName) AND attribute_not_exists(sk)\",\n }),\n );\n return { recorded: true };\n } catch (err) {\n if (err instanceof ConditionalCheckFailedException) {\n return { recorded: false, alreadyProcessed: true };\n }\n throw err;\n }\n}\n\n/** Standalone primitive — flips `failed: true` on an existing dedup row. */\nexport async function markFailed(\n dynamodb: DynamoDBClient,\n input: MarkFailedInput,\n options: WorkflowDedupClientOptions = {},\n): Promise<void> {\n assertConsumerName(input.consumerName);\n assertPositiveInteger(input.attempt, \"attempt\");\n if (input.reason.length === 0) {\n throw new WorkflowDedupInvalidInputError(\"reason must be non-empty.\");\n }\n\n const tableName = resolveTableName(options.tableName);\n const now = (options.now ?? defaultNow)();\n const sk = encodeSortKey(input.eventId, input.attempt);\n\n await dynamodb.send(\n new UpdateItemCommand({\n TableName: tableName,\n Key: {\n consumerName: { S: input.consumerName },\n sk: { S: sk },\n },\n UpdateExpression:\n \"SET #failed = :failed, #failureReason = :reason, #failedAt = :failedAt\",\n ExpressionAttributeNames: {\n \"#failed\": \"failed\",\n \"#failureReason\": \"failureReason\",\n \"#failedAt\": \"failedAt\",\n },\n ExpressionAttributeValues: {\n \":failed\": { BOOL: true },\n \":reason\": { S: input.reason },\n \":failedAt\": { S: now.toISOString() },\n },\n }),\n );\n}\n\n/** Compose the composite sort key per the TR-015 encoding. */\nexport function encodeSortKey(eventId: string, attempt: number): string {\n if (eventId.length === 0) {\n throw new WorkflowDedupInvalidInputError(\"eventId must be non-empty.\");\n }\n return `${eventId}#${attempt}`;\n}\n\nfunction resolveTableName(explicit?: string): string {\n const name = explicit ?? process.env[WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR];\n if (!name) {\n throw new WorkflowDedupTableNameMissingError(\n `Workflow dedup table name not set. Pass options.tableName or set ${WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR}.`,\n );\n }\n return name;\n}\n\nfunction assertConsumerName(consumerName: string): void {\n if (consumerName.length === 0) {\n throw new WorkflowDedupInvalidInputError(\"consumerName must be non-empty.\");\n }\n if (consumerName.length > WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH) {\n throw new WorkflowDedupInvalidInputError(\n `consumerName must be ≤${WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH} chars; got ${consumerName.length}.`,\n );\n }\n if (/\\s/.test(consumerName)) {\n throw new WorkflowDedupInvalidInputError(\n \"consumerName must not contain whitespace.\",\n );\n }\n}\n\nfunction assertPositiveInteger(value: number, field: string): void {\n if (!Number.isInteger(value) || value < 1) {\n throw new WorkflowDedupInvalidInputError(\n `${field} must be a 1-indexed integer; got ${value}.`,\n );\n }\n}\n\nfunction defaultNow(): Date {\n return new Date();\n}\n\n/** Thrown when the dedup table name cannot be resolved. */\nexport class WorkflowDedupTableNameMissingError extends Error {\n /** @param message - human-readable description. */\n constructor(message: string) {\n super(message);\n this.name = \"WorkflowDedupTableNameMissingError\";\n }\n}\n\n/** Thrown when an input violates a TR-015 invariant. */\nexport class WorkflowDedupInvalidInputError extends Error {\n /** @param message - human-readable description. */\n constructor(message: string) {\n super(message);\n this.name = \"WorkflowDedupInvalidInputError\";\n }\n}\n","export {\n WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS,\n WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH,\n WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR,\n} from \"./env\";\nexport {\n WorkflowDedupInvalidInputError,\n WorkflowDedupTableNameMissingError,\n encodeSortKey,\n markFailed,\n recordIfAbsent,\n workflowDedupClient,\n} from \"./workflow-dedup-client\";\nexport type {\n MarkFailedInput,\n RecordIfAbsentInput,\n RecordIfAbsentResult,\n WorkflowDedupClient,\n WorkflowDedupClientOptions,\n} from \"./workflow-dedup-client\";\n","export {\n ENVELOPE_VERSION,\n isSupportedEnvelopeVersion,\n} from \"./envelope-version\";\nexport {\n MissingActorContextError,\n isWorkflowSystemActor,\n isWorkflowUserActor,\n workflowUserActorFromClaims,\n} from \"./envelope\";\nexport type {\n WorkflowActor,\n WorkflowEvent,\n WorkflowSystemActor,\n WorkflowUserActor,\n} from \"./envelope\";\nexport {\n DEFAULT_BUS_NAME_BY_SOURCE,\n OPENHI_CONTROL_SOURCE,\n OPENHI_DATA_SOURCE,\n OPENHI_OPS_SOURCE,\n} from \"./sources\";\nexport type { OpenHiSource } from \"./sources\";\nexport {\n InvalidDetailTypeRegistrationError,\n PlatformDeploymentCompletedV1,\n PlatformSystemDataSeededV1,\n defineDetailType,\n isWellFormedDetailType,\n} from \"./detail-types\";\nexport type {\n PlatformDeploymentCompletedV1Detail,\n PlatformSystemDataSeededV1Detail,\n WorkflowDetailTypeEntry,\n} from \"./detail-types\";\nexport {\n WorkflowPublishError,\n publishWorkflowEvent,\n workflowsClient,\n} from \"./publisher\";\nexport type {\n PublishContext,\n PublishResult,\n PublisherOptions,\n WorkflowsClient,\n} from \"./publisher\";\nexport {\n InvalidWorkflowEventError,\n UnsupportedEnvelopeVersionError,\n parseWorkflowEvent,\n} from \"./consumer\";\nexport type {\n DedupKey,\n EventBridgeEventLike,\n ParsedWorkflowEvent,\n} from \"./consumer\";\nexport {\n WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS,\n WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH,\n WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR,\n WorkflowDedupInvalidInputError,\n WorkflowDedupTableNameMissingError,\n encodeSortKey,\n markFailed,\n recordIfAbsent,\n workflowDedupClient,\n} from \"./dedup\";\nexport type {\n MarkFailedInput,\n RecordIfAbsentInput,\n RecordIfAbsentResult,\n WorkflowDedupClient,\n WorkflowDedupClientOptions,\n} from \"./dedup\";\n","/**\n * @see sites/www-docs/content/packages/@openhi/constructs/index.md\n */\n/** @openhi/constructs — root barrel exports. */\nexport * from \"./app\";\nexport * from \"./components\";\nexport * from \"./services\";\nexport * from \"./workflows\";\n","import {\n OPEN_HI_DEPLOYMENT_TARGET_ROLE,\n OPEN_HI_STAGE,\n OpenHiConfig,\n OpenHiEnvironmentConfig,\n} from \"@openhi/config\";\nimport { App, AppProps } from \"aws-cdk-lib\";\nimport { IConstruct } from \"constructs\";\nimport { OpenHiEnvironment } from \"./open-hi-environment\";\nimport { OpenHiStage } from \"./open-hi-stage\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/app/open-hi-app.md\n */\n\n/**\n * Symbol used for runtime type checking to identify OpenHiApp instances.\n *\n * @internal\n */\nconst OPEN_HI_APP_SYMBOL = Symbol.for(\"@openhi/constructs/core.OpenHiApp\");\n\n/**\n * Properties for creating an OpenHiApp instance.\n */\nexport interface OpenHiAppProps extends AppProps {\n /**\n * Optional name for the application.\n * ```\n */\n readonly appName?: string;\n\n /**\n * The OpenHi configuration object that defines stages, environments, and\n * their associated AWS account and region settings.\n */\n readonly config: OpenHiConfig;\n}\n\n/**\n * Root application construct for OpenHi CDK applications.\n */\nexport class OpenHiApp extends App {\n /**\n * Finds the OpenHiApp instance that contains the given construct in its\n * construct tree.\n */\n public static of(construct: IConstruct): OpenHiApp | undefined {\n return construct.node.scopes.reverse().find(OpenHiApp.isOpenHiApp);\n }\n\n /**\n * Type guard that checks if a value is an OpenHiApp instance.\n */\n public static isOpenHiApp(this: void, x: any): x is OpenHiApp {\n return x !== null && typeof x === \"object\" && OPEN_HI_APP_SYMBOL in x;\n }\n\n /**\n * Name for the application.\n */\n readonly appName: string;\n\n /**\n * The OpenHi configuration object for this application.\n */\n readonly config: OpenHiConfig;\n\n /**\n * Creates a new OpenHiApp instance.\n */\n constructor(props: OpenHiAppProps) {\n super(props);\n\n // Set runtime symbol for type checking\n Object.defineProperty(this, OPEN_HI_APP_SYMBOL, { value: true });\n\n // Store app name, defaulting to \"openhi\" if not provided\n this.appName = props.appName ?? \"openhi\";\n\n // Store configuration for use by child constructs\n this.config = props.config;\n\n // Create stages and environments based on configuration\n // Iterate through all possible stage types (dev, stage, prod)\n Object.values(OPEN_HI_STAGE).forEach((stageType) => {\n // Only create a stage if it's configured in the config\n if (this.config.deploymentTargets?.[stageType]) {\n const stage = new OpenHiStage(this, { stageType });\n\n // Create primary deployment target if configured\n // Each stage can have at most one primary deployment target\n if (\n this.config.deploymentTargets?.[stageType]?.[\n OPEN_HI_DEPLOYMENT_TARGET_ROLE.PRIMARY\n ]\n ) {\n const envConfig =\n this.config.deploymentTargets[stageType][\n OPEN_HI_DEPLOYMENT_TARGET_ROLE.PRIMARY\n ]!;\n new OpenHiEnvironment(stage, {\n deploymentTargetRole: OPEN_HI_DEPLOYMENT_TARGET_ROLE.PRIMARY,\n config: envConfig,\n env: { account: envConfig.account, region: envConfig.region },\n });\n }\n\n // Create secondary deployment targets if configured\n // Each stage can have zero or more secondary deployment targets\n if (\n this.config.deploymentTargets?.[stageType]?.[\n OPEN_HI_DEPLOYMENT_TARGET_ROLE.SECONDARY\n ]\n ) {\n this.config.deploymentTargets[stageType][\n OPEN_HI_DEPLOYMENT_TARGET_ROLE.SECONDARY\n ]!.forEach((envConfig: OpenHiEnvironmentConfig) => {\n new OpenHiEnvironment(stage, {\n deploymentTargetRole: OPEN_HI_DEPLOYMENT_TARGET_ROLE.SECONDARY,\n config: envConfig,\n env: { account: envConfig.account, region: envConfig.region },\n });\n });\n }\n }\n });\n }\n\n /*****************************************************************************\n *\n * Stages\n *\n ****************************************************************************/\n\n /**\n * Gets all OpenHiStage instances that are direct children of this app.\n\n */\n public get stages(): Array<OpenHiStage> {\n return this.node.children.filter(OpenHiStage.isOpenHiStage);\n }\n\n /**\n * Gets the development stage, if it exists.\n */\n public get devStage(): OpenHiStage | undefined {\n return this.stages.find((stage) => stage.stageType === OPEN_HI_STAGE.DEV);\n }\n\n /**\n * Gets the staging stage, if it exists.\n */\n public get stageStage(): OpenHiStage | undefined {\n return this.stages.find((stage) => stage.stageType === OPEN_HI_STAGE.STAGE);\n }\n\n /**\n * Gets the production stage, if it exists.\n */\n public get prodStage(): OpenHiStage | undefined {\n return this.stages.find((stage) => stage.stageType === OPEN_HI_STAGE.PROD);\n }\n\n /*****************************************************************************\n *\n * Environments\n *\n ****************************************************************************/\n\n /**\n * Gets all OpenHiEnvironment instances across all stages in this app.\n */\n public get environments(): Array<OpenHiEnvironment> {\n return this.stages.flatMap((stage) => stage.environments);\n }\n\n /**\n * Gets all primary environments across all stages in this app.\n */\n public get primaryEnvironments(): Array<OpenHiEnvironment> {\n return this.environments.filter(\n (env) => env.deploymentTargetRole === \"primary\",\n );\n }\n\n /**\n * Gets all secondary environments across all stages in this app.\n */\n public get secondaryEnvironments(): Array<OpenHiEnvironment> {\n return this.environments.filter(\n (env) => env.deploymentTargetRole === \"secondary\",\n );\n }\n}\n","import {\n OPEN_HI_DEPLOYMENT_TARGET_ROLE,\n OpenHiEnvironmentConfig,\n} from \"@openhi/config\";\nimport { Stage, StageProps } from \"aws-cdk-lib\";\nimport { IConstruct } from \"constructs\";\nimport { OpenHiStage } from \"./open-hi-stage\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/app/open-hi-environment.md\n */\n\n/**\n * Symbol used to identify OpenHiEnvironment instances at runtime.\n */\nconst OPEN_HI_ENVIRONMENT_SYMBOL = Symbol.for(\n \"@openhi/constructs/core.OpenHiEnvironment\",\n);\n\n/**\n * Properties for creating an OpenHiEnvironment.\n */\nexport interface OpenHiEnvironmentProps extends StageProps {\n /**\n * The deployment target role for this (account, region).\n */\n readonly deploymentTargetRole: (typeof OPEN_HI_DEPLOYMENT_TARGET_ROLE)[keyof typeof OPEN_HI_DEPLOYMENT_TARGET_ROLE];\n\n /**\n * Configuration for this specific environment.\n */\n readonly config: OpenHiEnvironmentConfig;\n}\n\n/**\n * Represents an OpenHi environment within an AWS CDK stage.\n */\nexport class OpenHiEnvironment extends Stage {\n /**\n * Finds the OpenHiEnvironment that contains the given construct.\n * ```\n */\n public static of(construct: IConstruct): OpenHiEnvironment | undefined {\n return construct.node.scopes\n .reverse()\n .find(OpenHiEnvironment.isOpenHiEnvironment);\n }\n\n /**\n * Type guard to check if a value is an OpenHiEnvironment instance.\n */\n public static isOpenHiEnvironment(\n this: void,\n x: any,\n ): x is OpenHiEnvironment {\n return (\n x !== null && typeof x === \"object\" && OPEN_HI_ENVIRONMENT_SYMBOL in x\n );\n }\n\n /**\n * Configuration for this specific environment.\n */\n readonly config: OpenHiEnvironmentConfig;\n\n /**\n * The deployment target role for this (account, region).\n */\n public readonly deploymentTargetRole: (typeof OPEN_HI_DEPLOYMENT_TARGET_ROLE)[keyof typeof OPEN_HI_DEPLOYMENT_TARGET_ROLE];\n\n /**\n * Creates a new OpenHiEnvironment.\n */\n constructor(\n /**\n * The OpenHiStage that contains this environment.\n */\n public ohStage: OpenHiStage,\n /**\n * Properties for creating the environment.\n */\n public props: OpenHiEnvironmentProps,\n ) {\n // Copy account and region from config into env, if provided.\n // This allows all resources in this environment to default to the correct\n // account and region without having to specify it on each stack or resource.\n if (props.config.account && props.config.region) {\n props = {\n ...props,\n env: {\n account: props.config.account,\n region: props.config.region,\n },\n };\n }\n\n // Determine the stage name:\n // - Primary environments use the environment type as the name\n // - Secondary deployment targets use \"{deploymentTargetRole}-{index}\" format\n const stageName =\n props.deploymentTargetRole === OPEN_HI_DEPLOYMENT_TARGET_ROLE.PRIMARY\n ? props.deploymentTargetRole\n : [props.deploymentTargetRole, ohStage.environments.length].join(\"-\");\n\n super(ohStage, stageName, {\n env: props.env ?? ohStage.props.env,\n ...props,\n });\n\n // Mark this instance as an OpenHiEnvironment for runtime type checking\n Object.defineProperty(this, OPEN_HI_ENVIRONMENT_SYMBOL, { value: true });\n\n this.deploymentTargetRole = props.deploymentTargetRole;\n this.config = props.config;\n }\n}\n","import { OPEN_HI_STAGE } from \"@openhi/config\";\nimport { Stage, StageProps } from \"aws-cdk-lib\";\nimport { IConstruct } from \"constructs\";\nimport { OpenHiApp } from \"./open-hi-app\";\nimport { OpenHiEnvironment } from \"./open-hi-environment\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/app/open-hi-stage.md\n */\n\n/**\n * Symbol used to identify OpenHiStage instances at runtime.\n *\n * @internal\n */\nconst OPEN_HI_STAGE_SYMBOL = Symbol.for(\"@openhi/constructs/core.OpenHiStage\");\n\n/**\n * Properties for creating an OpenHiStage instance.\n */\nexport interface OpenHiStageProps extends StageProps {\n /**\n * The type of the OpenHi stage.\n */\n readonly stageType: (typeof OPEN_HI_STAGE)[keyof typeof OPEN_HI_STAGE];\n}\n\n/**\n * Represents a deployment stage in the OpenHi infrastructure hierarchy.\n */\nexport class OpenHiStage extends Stage {\n /**\n * Finds the OpenHiStage that contains the given construct.\n */\n public static of(construct: IConstruct): OpenHiStage | undefined {\n return construct.node.scopes.reverse().find(OpenHiStage.isOpenHiStage);\n }\n\n /**\n * Type guard to check if a value is an OpenHiStage instance.\n */\n public static isOpenHiStage(this: void, x: any): x is OpenHiStage {\n return x !== null && typeof x === \"object\" && OPEN_HI_STAGE_SYMBOL in x;\n }\n\n /**\n * The type of this OpenHi stage.\n */\n public readonly stageType: (typeof OPEN_HI_STAGE)[keyof typeof OPEN_HI_STAGE];\n\n /**\n * Creates a new OpenHiStage instance.\n */\n constructor(\n /**\n * The OpenHiApp that this stage belongs to.\n *\n * @public\n */\n public ohApp: OpenHiApp,\n\n /**\n * Properties for configuring the stage.\n *\n * @public\n */\n public props: OpenHiStageProps,\n ) {\n super(ohApp, props.stageType, props);\n\n Object.defineProperty(this, OPEN_HI_STAGE_SYMBOL, { value: true });\n\n this.stageType = props.stageType;\n }\n\n /**\n * Gets all OpenHiEnvironment instances contained within this stage.\n */\n public get environments(): Array<OpenHiEnvironment> {\n return this.node.children.filter(OpenHiEnvironment.isOpenHiEnvironment);\n }\n\n /**\n * Gets the primary OpenHiEnvironment for this stage, if one exists.\n */\n public get primaryEnvironment(): OpenHiEnvironment | undefined {\n return this.environments.find(\n (env) => env.deploymentTargetRole === \"primary\",\n );\n }\n\n /**\n * Gets all secondary OpenHiEnvironment instances for this stage.\n */\n public get secondaryEnvironments(): Array<OpenHiEnvironment> {\n return this.environments.filter(\n (env) => env.deploymentTargetRole === \"secondary\",\n );\n }\n}\n","import {\n findGitBranch,\n findGitRepoName,\n hashString,\n} from \"@codedrifters/utils\";\nimport { OPEN_HI_STAGE, OpenHiEnvironmentConfig } from \"@openhi/config\";\nimport { RemovalPolicy, Stack, StackProps, Tags } from \"aws-cdk-lib\";\nimport { paramCase } from \"change-case\";\nimport { OpenHiEnvironment } from \"./open-hi-environment\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/app/open-hi-service.md\n */\n\n/**\n * Known OpenHI service type strings. Each service class defines its own\n * static SERVICE_TYPE (e.g. OpenHiAuthService.SERVICE_TYPE === \"auth\").\n *\n * @public\n */\nexport type OpenHiServiceType =\n | \"auth\"\n | \"rest-api\"\n | \"data\"\n | \"global\"\n | \"graphql-api\";\n\n/**\n * Tag-key suffixes applied by every OpenHiService stack via Tags.of().\n * Full keys are composed `${appName}:${suffix}` — see {@link openHiTagKey}.\n * Consumers that filter or project these tags (e.g. the platform-deploy\n * bridge) import these suffixes rather than redeclaring the strings.\n *\n * @public\n */\nexport const OPENHI_TAG_SUFFIX_REPO_NAME = \"repo-name\";\n/** @public */\nexport const OPENHI_TAG_SUFFIX_BRANCH_NAME = \"branch-name\";\n/** @public */\nexport const OPENHI_TAG_SUFFIX_SERVICE_TYPE = \"service-type\";\n/** @public */\nexport const OPENHI_TAG_SUFFIX_STAGE_TYPE = \"stage-type\";\n\n/**\n * Compose a full stack-tag key from an `appName` and a suffix from\n * {@link OPENHI_TAG_SUFFIX_REPO_NAME} et al.\n *\n * @public\n */\nexport const openHiTagKey = (appName: string, suffix: string): string =>\n `${appName}:${suffix}`;\n\n/**\n * Properties for creating an {@link OpenHiService} stack.\n *\n * @public\n */\nexport interface OpenHiServiceProps extends StackProps {\n /**\n * Optional branch name override.\n */\n readonly branchName?: string;\n\n /**\n * Optional repository name override.\n */\n readonly repoName?: string;\n\n /**\n * Optional application name override.\n */\n readonly appName?: string;\n\n /**\n * Default release branch name.\n */\n readonly defaultReleaseBranch?: string;\n\n /**\n * The removal policy for persistent stack resources.\n */\n readonly removalPolicy?: RemovalPolicy;\n\n /**\n * Environment configuration for this service.\n */\n readonly config?: OpenHiEnvironmentConfig;\n\n /**\n * A constant that identifies the service type.\n */\n readonly serviceType?: OpenHiServiceType;\n}\n\n/**\n * Represents an OpenHI service stack within the OpenHI platform.\n * Subclasses must override {@link serviceType} to return their static SERVICE_TYPE.\n */\nexport abstract class OpenHiService extends Stack {\n /**\n * The service/stack ID that was passed to the constructor.\n */\n public readonly serviceId: string;\n\n /**\n * The deployment target role identifier.\n */\n public readonly deploymentTargetRole: string;\n\n /**\n * Repository name used in resource tagging.\n */\n public readonly repoName: string;\n\n /**\n * Application name identifier.\n */\n public readonly appName: string;\n\n /**\n * Default release branch name.\n */\n public readonly defaultReleaseBranch: string;\n\n /**\n * Branch name used when calculating resource names and hashes.\n */\n public readonly branchName: string;\n\n /**\n * Short hash unique to the deployment target (app name, deployment target role, account, region).\n */\n public readonly environmentHash: string;\n\n /**\n * Short hash unique to the environment and branch combination.\n */\n public readonly branchHash: string;\n\n /**\n * Short hash unique to the specific stack/service.\n */\n public readonly stackHash: string;\n\n /**\n * The removal policy for persistent stack resources.\n */\n public readonly removalPolicy: RemovalPolicy;\n\n /**\n * Environment configuration for this service.\n * This is either the value passed in or the default config\n */\n public readonly config: OpenHiEnvironmentConfig;\n\n /**\n * Service type identifier. Override in subclasses to return the class's static SERVICE_TYPE.\n * Used for parameter names, tags, and service discovery.\n */\n abstract get serviceType(): OpenHiServiceType | string;\n\n /**\n * Creates a new OpenHI service stack.\n *\n * @param ohEnv - The OpenHI environment (stage) this service belongs to\n * @param id - Unique identifier for this service stack (e.g., \"user-service\")\n * @param props - Optional properties for configuring the service\n *\n * @throws {Error} If account and region are not defined in props or environment\n *\n */\n constructor(\n public ohEnv: OpenHiEnvironment,\n id: string,\n public props: OpenHiServiceProps = {},\n ) {\n // Determine the account and region based on environment or user passed props.\n // This must be done before calling super() as it's needed for stack naming.\n const { account, region } = props.env || ohEnv;\n if (!account || !region) {\n throw new Error(\n \"Account and region must be defined in OpenHiServiceProps or OpenHiEnvironment\",\n );\n }\n\n // Get app name from the app in the hierarchy (via environment -> stage -> app)\n const appName = props.appName ?? ohEnv.ohStage.ohApp.appName ?? \"openhi\";\n\n // Initialize deployment context properties\n // Repo name is used in tagging. This tag value is important for tracking\n // when tearing preview stacks back down. If not provided, detect from git.\n const repoName = props.repoName ?? findGitRepoName();\n\n // Default release branch is used when not in dev stage. Defaults to \"main\" if not provided.\n const defaultReleaseBranch = props.defaultReleaseBranch ?? \"main\";\n\n // Branch name is used to calculate hashes and names for resources.\n // Detection logic:\n // - If explicitly provided, use that value\n // - If Jest is running, use \"test-branch\" to avoid snapshot test issues\n // - If GIT_BRANCH_NAME env is set (e.g. by CI), use it\n // - If in dev stage, detect from git using findGitBranch()\n // - Otherwise (stage/prod), default to defaultReleaseBranch\n const branchName =\n props.branchName ??\n (process.env.JEST_WORKER_ID\n ? \"test-branch\"\n : process.env.GIT_BRANCH_NAME?.trim() ||\n (ohEnv.ohStage.stageType === OPEN_HI_STAGE.DEV\n ? findGitBranch()\n : defaultReleaseBranch));\n\n // Compute environment hash: unique to deployment target (app name, role, account, region)\n // Mainly used for DNS names and deployment-target-scoped resources\n const environmentHash = hashString(\n [appName, ohEnv.deploymentTargetRole, account, region].join(\"-\"),\n 6,\n );\n\n // Compute branch hash: unique to deployment target and branch combination\n // Useful for resources shared across stacks within the same branch\n const branchHash = hashString(\n [appName, ohEnv.deploymentTargetRole, account, region, branchName].join(\n \"-\",\n ),\n 6,\n );\n\n // Compute stack hash: unique to the specific stack/service\n // Useful for stack-specific resources like S3 buckets, KMS key aliases\n // This ensures two PR builds or different services don't collide\n const stackHash = hashString(\n [\n appName,\n ohEnv.deploymentTargetRole,\n account,\n region,\n branchName,\n id,\n ].join(\"-\"),\n 6,\n );\n\n // Set the removal policy for this stack based on the deployment target role.\n // Production stages retain resources, others destroy them on stack deletion.\n const removalPolicy =\n props.removalPolicy ??\n (ohEnv.ohStage.stageType === OPEN_HI_STAGE.PROD\n ? RemovalPolicy.RETAIN\n : RemovalPolicy.DESTROY);\n Object.assign(props, { removalPolicy });\n\n // Description to use for the stack and all resources within it.\n // Includes service ID, branch name, and hash for easy identification.\n const description = `OpenHi Service: ${id} [${branchName}] - ${branchHash}`;\n\n // Call the super constructor of Stack.\n // This initializes the AWS CDK Stack with:\n // - Scope: the OpenHI environment\n // - ID: unique stack name including branch hash\n // - Props: stack properties including description and removal policy\n super(ohEnv, [branchHash, id, account, region].join(\"-\"), {\n ...props,\n description,\n });\n\n // Store the service ID for use in deployment context and other operations.\n this.serviceId = id;\n\n // Set the removal policy for this stack based on the deployment target role.\n this.removalPolicy = removalPolicy;\n\n /**\n * Explicit config or use the environment config as a backup,\n */\n this.config = props.config ?? ohEnv.props.config;\n\n // Initialize deployment context properties directly on the service\n this.deploymentTargetRole = ohEnv.deploymentTargetRole;\n this.repoName = repoName;\n this.appName = appName;\n this.defaultReleaseBranch = defaultReleaseBranch;\n this.branchName = branchName;\n this.environmentHash = environmentHash;\n this.branchHash = branchHash;\n this.stackHash = stackHash;\n\n // Pre-populate the AZ context cache for this stack so any construct that\n // calls `stack.availabilityZones` (notably RDS DatabaseCluster, ELBs, and\n // anything else that fans out across AZs) gets concrete values without\n // triggering a CDK context lookup. Without this, CI synth records the\n // lookup as \"missing\" and deploy fails because the GitHubOpenHiDeployer\n // role can't assume `cdk-…-lookup-role-…` (only granted to dev machines\n // by default). AZ names follow the stable `<region>a/b/c…` pattern across\n // all current commercial AWS regions; if a deployment ever targets a\n // region where this assumption breaks, override here per region.\n this.node.setContext(\n `availability-zones:account=${account}:region=${region}`,\n [`${region}a`, `${region}b`, `${region}c`],\n );\n\n // Standard tagging across all resources in the stack.\n // Use id (the service type string passed to super) since abstract serviceType cannot be accessed in constructor.\n Tags.of(this).add(\n openHiTagKey(appName, OPENHI_TAG_SUFFIX_REPO_NAME),\n repoName.slice(0, 255),\n );\n Tags.of(this).add(\n openHiTagKey(appName, OPENHI_TAG_SUFFIX_BRANCH_NAME),\n branchName.slice(0, 255),\n );\n Tags.of(this).add(\n openHiTagKey(appName, OPENHI_TAG_SUFFIX_SERVICE_TYPE),\n id.slice(0, 255),\n );\n Tags.of(this).add(\n openHiTagKey(appName, OPENHI_TAG_SUFFIX_STAGE_TYPE),\n ohEnv.ohStage.stageType.slice(0, 255),\n );\n }\n\n /**\n * DNS prefix for this branche's child zone.\n */\n public get childZonePrefix(): string {\n return paramCase(this.branchName).slice(0, 200);\n }\n}\n","import {\n Certificate,\n CertificateProps,\n} from \"aws-cdk-lib/aws-certificatemanager\";\nimport { StringParameter } from \"aws-cdk-lib/aws-ssm\";\nimport { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/acm/root-wildcard-certificate.md\n */\n\nexport class RootWildcardCertificate extends Certificate {\n /**\n * Used when storing the Certificate ARN in SSM.\n */\n public static readonly SSM_PARAM_NAME = \"ROOT_WILDCARD_CERT_ARN\";\n\n /**\n * Using a special name here since this will be shared and used among many\n * stacks and services. Use with OpenHiGlobalService.rootWildcardCertificateFromConstruct.\n */\n public static ssmParameterName(): string {\n return (\n \"/\" +\n [\"GLOBAL\", RootWildcardCertificate.SSM_PARAM_NAME].join(\"/\").toUpperCase()\n );\n }\n\n constructor(scope: Construct, props: CertificateProps) {\n super(scope, \"root-wildcard-certificate\", { ...props });\n\n /**\n * Generate the SSM Parameter used to store this Certificate's ARN.\n */\n new StringParameter(this, \"wildcard-cert-param\", {\n parameterName: RootWildcardCertificate.ssmParameterName(),\n stringValue: this.certificateArn,\n });\n }\n}\n","import { HttpApi, HttpApiProps } from \"aws-cdk-lib/aws-apigatewayv2\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app/open-hi-service\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/api-gateway/root-http-api.md\n */\n\nexport interface RootHttpApiProps extends HttpApiProps {}\n\nexport class RootHttpApi extends HttpApi {\n /**\n * Used when storing the API ID in SSM.\n */\n public static readonly SSM_PARAM_NAME = \"ROOT_HTTP_API\";\n\n constructor(scope: Construct, props: RootHttpApiProps = {}) {\n const stack = OpenHiService.of(scope) as OpenHiService;\n\n const origins = props.corsPreflight?.allowOrigins;\n if (origins?.length) {\n const withTrailingSlash = origins.filter((o) => o.endsWith(\"/\"));\n if (withTrailingSlash.length > 0) {\n throw new Error(\n `CORS allowOrigins must not include a trailing slash. The browser Origin header is scheme + host + port only (no path), so API Gateway will not match origins like \"https://example.com/\" and CORS can fail. Invalid: ${withTrailingSlash.join(\", \")}. Use e.g. \"https://example.com\" instead.`,\n );\n }\n }\n\n super(scope, \"http-api\", {\n /**\n * User provided props\n */\n ...props,\n\n /**\n * Required\n */\n apiName: [\"root\", \"http\", \"api\", stack.branchHash].join(\"-\"),\n });\n }\n}\n","import {\n Definition,\n GraphqlApi,\n GraphqlApiProps,\n IGraphqlApi,\n} from \"aws-cdk-lib/aws-appsync\";\nimport { CodeFirstSchema, GraphqlType, ObjectType } from \"awscdk-appsync-utils\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app\";\nimport { DiscoverableStringParameter } from \"../ssm\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/app-sync/root-graphql-api.md\n */\n\nexport interface RootGraphqlApiProps extends GraphqlApiProps {}\n\nexport class RootGraphqlApi extends GraphqlApi {\n /**\n * Used when storing the GraphQl API ID in SSM\n */\n public static readonly SSM_PARAM_NAME = \"ROOT_GRAPHQL_API\";\n\n public static fromConstruct(scope: Construct): IGraphqlApi {\n const graphqlApiId = DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: RootGraphqlApi.SSM_PARAM_NAME,\n serviceType: \"graphql-api\",\n });\n\n return GraphqlApi.fromGraphqlApiAttributes(scope, \"root-graphql-api\", {\n graphqlApiId,\n });\n }\n\n constructor(scope: Construct, props?: Omit<RootGraphqlApiProps, \"name\">) {\n const stack = OpenHiService.of(scope) as OpenHiService;\n\n const schema = new CodeFirstSchema();\n schema.addType(\n new ObjectType(\"Query\", {\n definition: { HelloWorld: GraphqlType.string() },\n }),\n );\n\n super(scope, \"root-graphql-api\", {\n /**\n * Defaults\n */\n queryDepthLimit: 2,\n resolverCountLimit: 50,\n definition: Definition.fromSchema(schema),\n\n /**\n * Overrideable props\n */\n ...props,\n\n /**\n * Required\n */\n name: [\"root\", \"graphql\", \"api\", stack.branchHash].join(\"-\"),\n });\n\n /**\n * Generate the SSM Parameter used to store this GraphQL API's ID.\n */\n new DiscoverableStringParameter(this, \"graphql-api-param\", {\n ssmParamName: RootGraphqlApi.SSM_PARAM_NAME,\n serviceType: \"graphql-api\",\n stringValue: this.apiId,\n });\n }\n}\n","import { Tags } from \"aws-cdk-lib\";\nimport {\n StringParameter,\n type StringParameterProps,\n} from \"aws-cdk-lib/aws-ssm\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/ssm/discoverable-string-parameter.md\n */\n\n/*******************************************************************************\n *\n * DiscoverableStringParameterProps: props for creating or looking up SSM\n * parameters. Includes StringParameterProps (minus parameterName) plus\n * name-building fields used by buildParameterName.\n *\n ******************************************************************************/\n\nexport interface DiscoverableStringParameterProps extends Omit<\n StringParameterProps,\n \"parameterName\"\n> {\n /**\n * SSM param name used to build the SSM parameter name via buildParameterName\n * and stored as a tag on the parameter for discoverability.\n */\n readonly ssmParamName: string;\n\n /**\n * The environment hash the parameter belongs to.\n * @default - the current stack's environment hash\n */\n readonly branchHash?: string;\n\n /**\n * The service type the parameter belongs to.\n * @default - the current stack's service type\n */\n readonly serviceType?: string;\n\n /**\n * The AWS account the parameter belongs to.\n * @default - the current stack's account\n */\n readonly account?: string;\n\n /**\n * The AWS region the parameter belongs to.\n * @default - the current stack's region\n */\n readonly region?: string;\n}\n\n/**\n * Props for buildParameterName and valueForLookupName.\n * Includes ssmParamName (required) and optional overrides (branchHash, serviceType, account, region).\n */\nexport type BuildParameterNameProps = Pick<\n DiscoverableStringParameterProps,\n \"ssmParamName\" | \"branchHash\" | \"serviceType\" | \"account\" | \"region\"\n>;\n\n/**\n * Discoverable SSM string parameter construct. Extends CDK StringParameter:\n * builds parameterName from the given name via buildParameterName and tags\n * the parameter with the name constant.\n */\nexport class DiscoverableStringParameter extends StringParameter {\n /**\n * Version of the parameter name format / discoverability schema.\n * Bump when buildParameterName or tagging semantics change.\n * Also used to drive replacement of parameters during CloudFormation deploys.\n */\n public static readonly version = \"v1\";\n\n /**\n * Build a param name based on predictable attributes found in services and\n * constructs. Used for storage and retrieval of SSM values across services.\n */\n public static buildParameterName(\n scope: Construct,\n props: BuildParameterNameProps,\n ): string {\n const stack = OpenHiService.of(scope) as OpenHiService;\n return (\n \"/\" +\n [\n DiscoverableStringParameter.version,\n props.branchHash ?? stack.branchHash,\n props.serviceType ?? stack.serviceType,\n props.account ?? stack.account,\n props.region ?? stack.region,\n props.ssmParamName,\n ]\n .join(\"/\")\n .toUpperCase()\n );\n }\n\n /**\n * Read the string value of an SSM parameter created with DiscoverableStringParameter,\n * using props that include ssmParamName and optional overrides (e.g. serviceType).\n */\n public static valueForLookupName(\n scope: Construct,\n props: BuildParameterNameProps,\n ): string {\n const paramName = DiscoverableStringParameter.buildParameterName(\n scope,\n props,\n );\n return StringParameter.valueForStringParameter(scope, paramName);\n }\n\n constructor(\n scope: Construct,\n id: string,\n props: DiscoverableStringParameterProps,\n ) {\n const { ssmParamName, branchHash, serviceType, account, region, ...rest } =\n props;\n\n const parameterName = DiscoverableStringParameter.buildParameterName(\n scope,\n props,\n );\n\n super(scope, id + \"-\" + DiscoverableStringParameter.version, {\n ...rest,\n parameterName,\n });\n\n const { appName } = OpenHiService.of(scope) as OpenHiService;\n Tags.of(this).add(`${appName}:param-name`, ssmParamName);\n }\n}\n","import { Duration } from \"aws-cdk-lib\";\nimport {\n IUserPool,\n UserPoolClient,\n UserPoolClientProps,\n} from \"aws-cdk-lib/aws-cognito\";\nimport { Construct } from \"constructs\";\n\nexport interface CognitoFixtureSeederClientProps extends Partial<\n Omit<UserPoolClientProps, \"userPool\" | \"generateSecret\">\n> {\n readonly userPool: IUserPool;\n}\n\n/**\n * Dedicated Cognito app client for the OpenHI fixture-seeder CLI\n * (`@openhi/seed-fixtures`).\n *\n * Why a dedicated client (vs reusing the SPA client):\n * - Tightly scoped: only the seeder consumes tokens issued here, so an\n * audit trail of seeder activity is cleanly separable.\n * - Decoupled from the SPA client's OAuth flows — no risk of breaking\n * web-app sign-in by tweaking auth-flow settings here.\n * - Stage-conditional creation upstream (only provisioned in non-prod\n * environments) means prod stacks never carry a code path that could\n * issue a fixture-seeder token in the first place.\n *\n * Why USER_PASSWORD_AUTH (vs M2M client-credentials):\n * - Cognito's M2M tier has a per-app-client monthly fee plus per-token\n * activity charges. For sporadic non-prod fixture runs the per-client\n * fee dominates the bill, especially if every dev branch spins up\n * its own auth stack.\n * - USER_PASSWORD_AUTH against a service `fixture-seeder` user keeps\n * the cost in MAU territory (free under the 50K MAU tier).\n * - Tradeoff: passwords need rotation and the service user must be\n * provisioned per non-prod environment (manual or scripted post-deploy).\n *\n * No client secret (`generateSecret: false`): USER_PASSWORD_AUTH\n * authenticates with the password directly; a secret would just add\n * another credential to manage without strengthening anything.\n */\nexport class CognitoFixtureSeederClient extends UserPoolClient {\n /**\n * SSM parameter name suffix used to publish this client's ID for\n * cross-stack lookups. Built into a full parameter name via\n * `buildParameterName` with `serviceType` AUTH (since the auth stack\n * owns this resource).\n */\n public static readonly SSM_PARAM_NAME = \"COGNITO_FIXTURE_SEEDER_CLIENT\";\n\n constructor(scope: Construct, props: CognitoFixtureSeederClientProps) {\n const { userPool, ...rest } = props;\n super(scope, \"fixture-seeder-client\", {\n userPool,\n generateSecret: false,\n authFlows: {\n userPassword: true,\n },\n // No OAuth flows — the seeder calls Cognito's `InitiateAuth`\n // directly with USER_PASSWORD_AUTH, not through the hosted-UI\n // OAuth grant flows the SPA client uses. `disableOAuth: true`\n // causes CDK to omit `AllowedOAuthFlowsUserPoolClient` entirely;\n // passing an empty `oAuth` block instead still flips that flag on\n // and Cognito rejects the create call for missing flows/scopes.\n disableOAuth: true,\n // Short-lived tokens: a seeder run takes seconds, not hours.\n // 1h access-token validity is the minimum Cognito permits and is\n // plenty for a fixture run.\n accessTokenValidity: Duration.hours(1),\n idTokenValidity: Duration.hours(1),\n refreshTokenValidity: Duration.days(1),\n preventUserExistenceErrors: true,\n ...rest,\n });\n }\n}\n","import {\n FeaturePlan,\n UserPool,\n UserPoolProps,\n VerificationEmailStyle,\n} from \"aws-cdk-lib/aws-cognito\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app/open-hi-service\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/cognito/cognito-user-pool.md\n */\n\nexport class CognitoUserPool extends UserPool {\n /**\n * Used when storing the User Pool ID in SSM.\n */\n public static readonly SSM_PARAM_NAME = \"COGNITO_USER_POOL\";\n\n constructor(scope: Construct, props: UserPoolProps = {}) {\n const service = OpenHiService.of(scope) as OpenHiService;\n\n super(scope, \"user-pool\", {\n /**\n * Defaults\n */\n selfSignUpEnabled: true,\n signInAliases: {\n email: true,\n },\n userVerification: {\n emailSubject: \"Verify your email!\",\n emailBody: \"Your verification code is {####}.\",\n emailStyle: VerificationEmailStyle.CODE,\n },\n removalPolicy: props.removalPolicy ?? service.removalPolicy,\n // Plus is required for access-token V2 claim customization in the\n // pre-token-generation Lambda. Essentials silently drops\n // claimsAndScopeOverrideDetails.accessTokenGeneration.claimsToAddOrOverride.\n featurePlan: FeaturePlan.PLUS,\n\n /**\n * Over-rideable props\n */\n ...props,\n\n /**\n * Required\n */\n userPoolName: [\"cognito\", \"user\", \"pool\", service.branchHash].join(\"-\"),\n });\n }\n}\n","import { UserPoolClient, UserPoolClientProps } from \"aws-cdk-lib/aws-cognito\";\nimport { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/cognito/cognito-user-pool-client.md\n */\n\nexport class CognitoUserPoolClient extends UserPoolClient {\n /**\n * Used when storing the User Pool Client ID in SSM.\n */\n public static readonly SSM_PARAM_NAME = \"COGNITO_USER_POOL_CLIENT\";\n\n constructor(scope: Construct, props: UserPoolClientProps) {\n super(scope, \"user-pool-client\", {\n /**\n * Defaults\n */\n generateSecret: false,\n oAuth: {\n flows: {\n authorizationCodeGrant: true,\n implicitCodeGrant: true,\n },\n callbackUrls: [`https://localhost:3000/oauth/callback`],\n },\n\n /**\n * Overrideable props\n */\n ...props,\n });\n }\n}\n","import { UserPoolDomain, UserPoolDomainProps } from \"aws-cdk-lib/aws-cognito\";\nimport { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/cognito/cognito-user-pool-domain.md\n */\n\nexport class CognitoUserPoolDomain extends UserPoolDomain {\n /**\n * Used when storing the User Pool Domain in SSM.\n */\n public static readonly SSM_PARAM_NAME = \"COGNITO_USER_POOL_DOMAIN\";\n\n constructor(scope: Construct, props: UserPoolDomainProps) {\n /**\n * This supports both custom and native Cognito domains, but we need to\n * name them uniquely so that swap outs work and don't cause conflicts\n * when cloudformation does it's deploy.\n */\n const id = props.cognitoDomain?.domainPrefix\n ? \"cognito-domain\"\n : \"custom-domain\";\n\n super(scope, id, {\n ...props,\n });\n }\n}\n","import { Key, KeyProps } from \"aws-cdk-lib/aws-kms\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app/open-hi-service\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/cognito/cognito-user-pool-kms-key.md\n */\n\nexport class CognitoUserPoolKmsKey extends Key {\n /**\n * Used when storing the KMS Key in SSM.\n */\n public static readonly SSM_PARAM_NAME = \"COGNITO_USER_POOL_KMS_KEY\";\n\n constructor(scope: Construct, props: KeyProps = {}) {\n const service = OpenHiService.of(scope) as OpenHiService;\n\n super(scope, \"kms-key\", {\n ...props,\n // alias: [\"alias\", \"cognito\", service.branchHash].join(\"/\"),\n description: `KMS Key for Cognito User Pool - ${service.branchHash}`,\n removalPolicy: props.removalPolicy ?? service.removalPolicy,\n });\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/cognito/post-authentication-lambda.md\n */\n\nconst HANDLER_NAME = \"post-authentication.handler.js\";\n\n/**\n * Resolve Lambda entry so it works when running from src/ (tests) or from lib/ (built).\n */\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n\n const fromLib = path.join(dirname, \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n return fromLib;\n}\n\n/**\n * Lambda used as Cognito Post Authentication trigger.\n */\nexport class PostAuthenticationLambda extends Construct {\n public readonly lambda: NodejsFunction;\n\n constructor(scope: Construct) {\n super(scope, \"post-authentication-lambda\");\n\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 1024,\n });\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/cognito/post-confirmation-lambda.md\n */\n\nconst HANDLER_NAME = \"post-confirmation.handler.js\";\n\n/**\n * Resolve Lambda entry so it works when running from src/ (tests) or from lib/ (built).\n */\nconst resolveHandlerEntry = (dirname: string): string => {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n\n return path.join(dirname, \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n};\n\nexport interface PostConfirmationLambdaProps {\n /**\n * Control-plane EventBridge bus name. Passed to the Lambda as\n * CONTROL_EVENT_BUS_NAME so it can publish onboarding workflow events.\n */\n readonly controlEventBusName: string;\n}\n\n/**\n * Lambda used as Cognito Post Confirmation trigger. It publishes a control\n * event and returns quickly; workflow Lambdas own provisioning.\n */\nexport class PostConfirmationLambda extends Construct {\n public readonly lambda: NodejsFunction;\n\n constructor(scope: Construct, props: PostConfirmationLambdaProps) {\n super(scope, \"post-confirmation-lambda\");\n\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 1024,\n environment: {\n CONTROL_EVENT_BUS_NAME: props.controlEventBusName,\n },\n });\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/cognito/pre-token-generation-lambda.md\n */\n\nconst HANDLER_NAME = \"pre-token-generation.handler.js\";\n\n/**\n * Resolve Lambda entry so it works when running from src/ (tests) or from lib/ (built).\n */\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n\n const fromLib = path.join(dirname, \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n return fromLib;\n}\n\nexport interface PreTokenGenerationLambdaProps {\n /**\n * DynamoDB data store table name. Passed to the Lambda as DYNAMO_TABLE_NAME\n * so the control-plane ElectroDB service reads the User by Cognito `sub`\n * (GSI2) and the user's first active Membership (fallback path).\n */\n readonly dynamoTableName: string;\n}\n\n/**\n * Lambda used as Cognito Pre Token Generation trigger. Resolves the OpenHI\n * User from the request's Cognito `sub` and injects `ohi_tid`, `ohi_wid`,\n * `ohi_uid`, `ohi_uname` into both the ID token and the access token\n * (ADR 2026-03-17-01).\n */\nexport class PreTokenGenerationLambda extends Construct {\n public readonly lambda: NodejsFunction;\n\n constructor(scope: Construct, props: PreTokenGenerationLambdaProps) {\n super(scope, \"pre-token-generation-lambda\");\n\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 1024,\n environment: {\n DYNAMO_TABLE_NAME: props.dynamoTableName,\n },\n });\n }\n}\n","import type { AttributeValue } from \"@aws-sdk/client-dynamodb\";\n\n/**\n * Shape of a DynamoDB change record as delivered inside Kinesis (table stream\n * destination) and decoded by the Firehose transform Lambda.\n */\nexport interface DynamoDbStreamKinesisRecord {\n eventName?: string;\n userIdentity?: unknown;\n dynamodb?: {\n Keys?: Record<string, AttributeValue>;\n NewImage?: Record<string, AttributeValue>;\n OldImage?: Record<string, AttributeValue>;\n SequenceNumber?: string;\n ApproximateCreationDateTime?: number;\n };\n}\n\nexport function dynamodbValueToJs(av: AttributeValue): unknown {\n if (av.S !== undefined) {\n return av.S;\n }\n if (av.N !== undefined) {\n return av.N.includes(\".\")\n ? Number.parseFloat(av.N)\n : Number.parseInt(av.N, 10);\n }\n if (av.BOOL !== undefined) {\n return av.BOOL;\n }\n if (av.NULL !== undefined) {\n return null;\n }\n if (av.M !== undefined) {\n return dynamodbImageToPlain(av.M);\n }\n if (av.L !== undefined) {\n return av.L.map((x: AttributeValue) => dynamodbValueToJs(x));\n }\n if (av.SS !== undefined) {\n return av.SS;\n }\n if (av.NS !== undefined) {\n return av.NS.map((n: string) =>\n n.includes(\".\") ? Number.parseFloat(n) : Number.parseInt(n, 10),\n );\n }\n return undefined;\n}\n\nexport function dynamodbImageToPlain(\n image: Record<string, AttributeValue>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(image)) {\n out[k] = dynamodbValueToJs(v);\n }\n return out;\n}\n","import type { AttributeValue } from \"@aws-sdk/client-dynamodb\";\nimport type { DynamoDbStreamKinesisRecord } from \"./dynamodb-stream-record\";\nimport { dynamodbImageToPlain } from \"./dynamodb-stream-record\";\n\n/**\n * EventBridge envelope constants for data-store CDC (no CDK imports).\n * @see docs/architecture/adr/2026-03-02-01-dynamodb-stream-to-data-event-bus.md\n */\nexport const DATA_STORE_CHANGE_EVENT_SOURCE = \"openhi.data.store\";\n\nexport const DATA_STORE_CHANGE_DETAIL_TYPE = \"FhirCurrentResourceChanged\";\n\n/** AWS PutEvents per-entry detail limit is 256 KiB; stay under for headroom. */\nexport const DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES = 250 * 1024;\n\nconst EXCLUDED_CHANGE_DETAIL_KEYS = new Set([\n \"PK\",\n \"SK\",\n \"GSI1PK\",\n \"GSI1SK\",\n \"GSI2PK\",\n \"GSI2SK\",\n /** Full FHIR JSON may contain PII; never list or ship in the bus payload. */\n \"resource\",\n]);\n\nfunction shallowValueEqual(a: unknown, b: unknown): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n\nfunction changedNonResourceAttributeNames(\n oldImage: Record<string, unknown> | undefined,\n newImage: Record<string, unknown> | undefined,\n): string[] | undefined {\n if (!oldImage || !newImage) {\n return undefined;\n }\n const names = new Set<string>();\n const keys = new Set([...Object.keys(oldImage), ...Object.keys(newImage)]);\n for (const k of keys) {\n if (EXCLUDED_CHANGE_DETAIL_KEYS.has(k)) {\n continue;\n }\n if (!shallowValueEqual(oldImage[k], newImage[k])) {\n names.add(k);\n }\n }\n return names.size > 0 ? [...names].sort() : undefined;\n}\n\n/** Non-excluded attribute names present on an item (for INSERT / REMOVE). */\nfunction presentMetadataAttributeNames(\n image: Record<string, unknown> | undefined,\n): string[] | undefined {\n if (!image) {\n return undefined;\n }\n const names = Object.keys(image).filter(\n (k) => !EXCLUDED_CHANGE_DETAIL_KEYS.has(k),\n );\n return names.length > 0 ? names.sort() : undefined;\n}\n\nfunction plainImage(\n image: Record<string, AttributeValue> | undefined,\n): Record<string, unknown> | undefined {\n if (!image) {\n return undefined;\n }\n return dynamodbImageToPlain(image);\n}\n\nexport interface FhirCurrentResourceChangeDetail {\n changeType: \"INSERT\" | \"MODIFY\" | \"REMOVE\";\n tenantId: string;\n workspaceId: string;\n resourceType: string;\n resourceId: string;\n resourceVersion: string;\n streamSequenceNumber?: string;\n /** Seconds since UNIX epoch (DynamoDB Streams `ApproximateCreationDateTime`). */\n approximateCreationEpochSec?: number;\n /**\n * MODIFY: attributes whose values differ between old and new images.\n * INSERT / REMOVE: attributes present on the written or removed image (metadata only).\n */\n changedAttributeNames?: string[];\n}\n\nexport function buildFhirCurrentResourceChangeDetail(\n record: DynamoDbStreamKinesisRecord,\n keys: {\n tenantId: string;\n workspaceId: string;\n resourceType: string;\n resourceId: string;\n version: string;\n },\n): FhirCurrentResourceChangeDetail {\n const rawName = record.eventName;\n const changeType =\n rawName === \"INSERT\" || rawName === \"MODIFY\" || rawName === \"REMOVE\"\n ? rawName\n : \"MODIFY\";\n\n const seq = record.dynamodb?.SequenceNumber;\n const approxEpochSec = record.dynamodb?.ApproximateCreationDateTime;\n\n const newPlain = plainImage(\n record.dynamodb?.NewImage as Record<string, AttributeValue> | undefined,\n );\n const oldPlain = plainImage(\n record.dynamodb?.OldImage as Record<string, AttributeValue> | undefined,\n );\n\n let changedAttributeNames: string[] | undefined;\n if (changeType === \"MODIFY\") {\n changedAttributeNames = changedNonResourceAttributeNames(\n oldPlain,\n newPlain,\n );\n } else if (changeType === \"INSERT\") {\n changedAttributeNames = presentMetadataAttributeNames(newPlain);\n } else {\n changedAttributeNames = presentMetadataAttributeNames(oldPlain);\n }\n\n return {\n changeType,\n tenantId: keys.tenantId,\n workspaceId: keys.workspaceId,\n resourceType: keys.resourceType,\n resourceId: keys.resourceId,\n resourceVersion: keys.version,\n ...(typeof seq === \"string\" && seq.length > 0\n ? { streamSequenceNumber: seq }\n : {}),\n ...(typeof approxEpochSec === \"number\" && Number.isFinite(approxEpochSec)\n ? { approximateCreationEpochSec: approxEpochSec }\n : {}),\n ...(changedAttributeNames ? { changedAttributeNames } : {}),\n };\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Duration, RemovalPolicy, Size } from \"aws-cdk-lib\";\nimport * as events from \"aws-cdk-lib/aws-events\";\nimport * as kinesis from \"aws-cdk-lib/aws-kinesis\";\nimport * as kinesisfirehose from \"aws-cdk-lib/aws-kinesisfirehose\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport * as s3 from \"aws-cdk-lib/aws-s3\";\nimport { Construct } from \"constructs\";\n\nconst HANDLER_NAME = \"firehose-archive-transform.handler.js\";\n\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n return path.join(dirname, \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n}\n\nexport interface DataStoreHistoricalArchiveProps {\n /**\n * Kinesis stream that receives DynamoDB item-level changes (table Kinesis destination).\n */\n readonly kinesisStream: kinesis.IStream;\n /**\n * Removal policy for the archive bucket and related resources.\n */\n readonly removalPolicy: RemovalPolicy;\n /**\n * Short hash for unique stream/bucket naming within the deployment.\n */\n readonly stackHash: string;\n /**\n * When set, the Firehose transform Lambda publishes qualifying changes to\n * this bus via PutEvents (ADR 2026-03-02-01).\n */\n readonly dataEventBus?: events.IEventBus;\n}\n\n/**\n * DynamoDB change stream → Kinesis → Firehose → S3 with a transform Lambda for\n * scope filtering and dynamic partitioning (ADR 2026-03-11-02). The same Lambda\n * publishes qualifying current-resource changes to the data event bus (ADR 2026-03-02-01)\n * when {@link DataStoreHistoricalArchiveProps.dataEventBus} is set.\n */\nexport class DataStoreHistoricalArchive extends Construct {\n public readonly archiveBucket: s3.Bucket;\n /**\n * Receives PutEvents payloads that still fail after in-Lambda retries when\n * {@link DataStoreHistoricalArchiveProps.dataEventBus} is configured.\n */\n public readonly putEventsFailureDlqBucket?: s3.Bucket;\n public readonly deliveryStream: kinesisfirehose.IDeliveryStream;\n public readonly transformFunction: NodejsFunction;\n\n constructor(\n scope: Construct,\n id: string,\n props: DataStoreHistoricalArchiveProps,\n ) {\n super(scope, id);\n\n this.archiveBucket = new s3.Bucket(this, \"ArchiveBucket\", {\n blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,\n encryption: s3.BucketEncryption.S3_MANAGED,\n enforceSSL: true,\n removalPolicy: props.removalPolicy,\n autoDeleteObjects: props.removalPolicy === RemovalPolicy.DESTROY,\n versioned: true,\n });\n\n const putEventsFailureDlqBucket = props.dataEventBus\n ? new s3.Bucket(this, \"PutEventsFailureDlq\", {\n blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,\n encryption: s3.BucketEncryption.S3_MANAGED,\n enforceSSL: true,\n removalPolicy: props.removalPolicy,\n autoDeleteObjects: props.removalPolicy === RemovalPolicy.DESTROY,\n versioned: false,\n })\n : undefined;\n this.putEventsFailureDlqBucket = putEventsFailureDlqBucket;\n\n this.transformFunction = new NodejsFunction(this, \"FirehoseTransform\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 512,\n timeout: Duration.minutes(1),\n description:\n \"Firehose transform: filter CURRENT resource rows, S3 keys, EventBridge PutEvents\",\n environment:\n props.dataEventBus && putEventsFailureDlqBucket\n ? {\n DATA_EVENT_BUS_NAME: props.dataEventBus.eventBusName,\n DATA_STORE_PUT_EVENTS_DLQ_BUCKET:\n putEventsFailureDlqBucket.bucketName,\n }\n : undefined,\n bundling: {\n minify: true,\n sourceMap: false,\n },\n });\n\n props.dataEventBus?.grantPutEventsTo(this.transformFunction);\n putEventsFailureDlqBucket?.grantPut(this.transformFunction);\n\n const processor = new kinesisfirehose.LambdaFunctionProcessor(\n this.transformFunction,\n {\n bufferInterval: Duration.seconds(60),\n bufferSize: Size.mebibytes(3),\n retries: 3,\n },\n );\n\n const destination = new kinesisfirehose.S3Bucket(this.archiveBucket, {\n compression: kinesisfirehose.Compression.GZIP,\n bufferingInterval: Duration.seconds(300),\n // Firehose requires SizeInMBs ≥ 64 when dynamic partitioning is enabled.\n bufferingSize: Size.mebibytes(64),\n processors: [processor],\n errorOutputPrefix:\n \"errors/!{firehose:error-output-type}/!{timestamp:yyyy/MM/dd/HH}/\",\n loggingConfig: new kinesisfirehose.EnableLogging(),\n });\n\n this.deliveryStream = new kinesisfirehose.DeliveryStream(\n this,\n \"ArchiveDeliveryStream\",\n {\n deliveryStreamName: `openhi-dstore-arch-${props.stackHash}`,\n source: new kinesisfirehose.KinesisStreamSource(props.kinesisStream),\n destination,\n },\n );\n\n const cfn = this.deliveryStream.node\n .defaultChild as kinesisfirehose.CfnDeliveryStream;\n cfn.addPropertyOverride(\n \"ExtendedS3DestinationConfiguration.DynamicPartitioningConfiguration\",\n {\n Enabled: true,\n RetryOptions: { DurationInSeconds: 300 },\n },\n );\n cfn.addPropertyOverride(\n \"ExtendedS3DestinationConfiguration.Prefix\",\n \"!{partitionKeyFromLambda:tenantId}/!{partitionKeyFromLambda:workspaceId}/!{partitionKeyFromLambda:resourceType}/!{partitionKeyFromLambda:resourceId}/!{partitionKeyFromLambda:version}/\",\n );\n }\n}\n","import { RemovalPolicy } from \"aws-cdk-lib\";\nimport {\n AttributeType,\n BillingMode,\n ProjectionType,\n Table,\n TableProps,\n} from \"aws-cdk-lib/aws-dynamodb\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/dynamodb/dynamo-db-data-store.md\n */\n\n/**\n * DynamoDB table name for the data store. Used for cross-stack reference and\n * deterministic naming per branch. The table backs the app data store.\n */\nexport function getDynamoDbDataStoreTableName(scope: Construct): string {\n const stack = OpenHiService.of(scope) as OpenHiService;\n return `data-store-${stack.branchHash}`;\n}\n\nexport interface DynamoDbDataStoreProps extends Omit<\n TableProps,\n \"tableName\" | \"removalPolicy\"\n> {\n /**\n * Optional removal policy override. If not set, uses the service's default\n * removal policy (RETAIN for prod, DESTROY otherwise).\n */\n readonly removalPolicy?: RemovalPolicy;\n}\n\n/**\n * DynamoDB table implementing the single-table design for app data (FHIR\n * resources data plane and platform control plane), per planning ADR-011 and\n * DR-004.\n *\n * @see {@link https://github.com/codedrifters/openhi/blob/main/sites/www-docs/content/architecture/dynamodb-single-table-design.md | DynamoDB Single-Table Design}\n *\n * Primary key: PK (String), SK (String).\n *\n * GSIs:\n * - **GSI1 — Unified Sharded List** (`GSI1PK`/`GSI1SK`, INCLUDE projection per\n * DR-004). Primary list/lookup index for both data-plane FHIR resources and\n * control-plane entities (User, Tenant, Workspace, Membership, Role,\n * RoleAssignment, Configuration). PK shape:\n * `TID#<tid>#WID#<wid>#RT#<Type>#SHARD#<n>` with 4 shards\n * (`n = hash(id) mod 4`). SK shape per `extractSortKey`: labeled types use\n * `<normalizedLabel>#<id>`; unlabeled use `<ISO-8601 lastUpdated>#<id>`.\n * - **GSI2 — Sub-Lookup** (`GSI2PK`/`GSI2SK`, INCLUDE projection). Resolves\n * `UserEntity` from a Cognito `sub` for the Pre Token Generation Lambda.\n * PK shape: `USER#SUB#<cognitoSub>`. SK shape: `CURRENT`.\n *\n * For historical archive to S3, pass `kinesisStream` and `stream` (e.g.\n * `StreamViewType.NEW_AND_OLD_IMAGES`) on the table props per ADR 2026-03-11-02.\n */\nexport class DynamoDbDataStore extends Table {\n constructor(\n scope: Construct,\n id: string,\n props: DynamoDbDataStoreProps = {},\n ) {\n const service = OpenHiService.of(scope) as OpenHiService;\n\n super(scope, id, {\n ...props,\n tableName: getDynamoDbDataStoreTableName(scope),\n partitionKey: {\n name: \"PK\",\n type: AttributeType.STRING,\n },\n sortKey: {\n name: \"SK\",\n type: AttributeType.STRING,\n },\n billingMode: BillingMode.PAY_PER_REQUEST,\n removalPolicy: props.removalPolicy ?? service.removalPolicy,\n });\n\n // GSI1 — Unified Sharded List (data plane + control plane) per ADR-011 and DR-004.\n this.addGlobalSecondaryIndex({\n indexName: \"GSI1\",\n partitionKey: {\n name: \"GSI1PK\",\n type: AttributeType.STRING,\n },\n sortKey: {\n name: \"GSI1SK\",\n type: AttributeType.STRING,\n },\n projectionType: ProjectionType.INCLUDE,\n nonKeyAttributes: [\n \"id\",\n \"summary\",\n \"vid\",\n \"lastUpdated\",\n \"createdDate\",\n \"modifiedDate\",\n \"createdById\",\n \"modifiedById\",\n // ElectroDB filters every query result through `ownsItem`, which\n // verifies `__edb_e__` (entity name) and `__edb_v__` (version) match\n // the entity. Without these projected, every GSI1 query returns 0\n // results — list endpoints silently return empty bundles.\n \"__edb_e__\",\n \"__edb_v__\",\n ],\n });\n\n // GSI2 — Sub-Lookup: Cognito sub → UserEntity (Pre Token Generation Lambda).\n this.addGlobalSecondaryIndex({\n indexName: \"GSI2\",\n partitionKey: {\n name: \"GSI2PK\",\n type: AttributeType.STRING,\n },\n sortKey: {\n name: \"GSI2SK\",\n type: AttributeType.STRING,\n },\n projectionType: ProjectionType.INCLUDE,\n nonKeyAttributes: [\n \"id\",\n \"currentTenant\",\n \"currentWorkspace\",\n \"displayName\",\n // See GSI1 above: ElectroDB's `ownsItem` filter rejects items\n // without these, so any query against GSI2 returns 0 results\n // unless they're projected.\n \"__edb_e__\",\n \"__edb_v__\",\n ],\n });\n }\n}\n","import {\n WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH,\n WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR,\n} from \"@openhi/workflows\";\nimport { Annotations, RemovalPolicy } from \"aws-cdk-lib\";\nimport { AttributeType, BillingMode, Table } from \"aws-cdk-lib/aws-dynamodb\";\nimport { Effect, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport { Function } from \"aws-cdk-lib/aws-lambda\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService, type OpenHiServiceType } from \"../../app\";\nimport { DiscoverableStringParameter } from \"../ssm\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/dynamodb/workflow-dedup-table.md\n */\n\n/**\n * Deterministic table name for the shared workflow dedup table.\n * Mirrors `getDynamoDbDataStoreTableName` naming: `workflow-dedup-${branchHash}`.\n */\nexport function getWorkflowDedupTableName(scope: Construct): string {\n const stack = OpenHiService.of(scope) as OpenHiService;\n return `workflow-dedup-${stack.branchHash}`;\n}\n\n/** Props for `WorkflowDedupTable`. */\nexport interface WorkflowDedupTableProps {\n /**\n * Optional removal policy override. Defaults to the service's default\n * (RETAIN for prod, DESTROY otherwise).\n */\n readonly removalPolicy?: RemovalPolicy;\n}\n\n/** Options for `WorkflowDedupTable.grantConsumer`. */\nexport interface GrantConsumerOptions {\n /**\n * Override the default TTL applied by the runtime client. The 14-day\n * default lives in `@openhi/workflows`; per-consumer overrides clamp\n * shorter per TR-015. Stored in the consumer's environment so the\n * `WorkflowDedupClient` factory can pick it up.\n */\n readonly defaultTtlSeconds?: number;\n}\n\n/**\n * Shared platform-level dedup table every retryable workflow consumer\n * dedupes against. Provisioned exactly once at the platform stack.\n *\n * Schema (per TR-015):\n * - Partition key `consumerName` (S)\n * - Sort key `sk` (S) — encodes `<eventId>#<attempt>`\n * - TTL attribute `expiresAt` (N, Unix epoch seconds)\n * - On-demand billing\n *\n * @see https://github.com/codedrifters/openhi-planning/blob/main/docs/src/content/docs/requirements/technical-requirements/TR-015-workflow-dedup-table.md\n */\nexport class WorkflowDedupTable extends Construct {\n /** SSM param name (short) used by `DiscoverableStringParameter` for the table name lookup. */\n public static readonly TABLE_NAME_SSM_PARAM_NAME =\n \"workflow-dedup-table-name\";\n /** SSM param name (short) used by `DiscoverableStringParameter` for the table ARN lookup. */\n public static readonly TABLE_ARN_SSM_PARAM_NAME = \"workflow-dedup-table-arn\";\n\n /** Cross-stack lookup for the table name. */\n public static tableNameFromLookup(scope: Construct): string {\n return DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: WorkflowDedupTable.TABLE_NAME_SSM_PARAM_NAME,\n serviceType: WorkflowDedupTable.PUBLISHER_SERVICE_TYPE,\n });\n }\n\n /** Cross-stack lookup for the table ARN. */\n public static tableArnFromLookup(scope: Construct): string {\n return DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: WorkflowDedupTable.TABLE_ARN_SSM_PARAM_NAME,\n serviceType: WorkflowDedupTable.PUBLISHER_SERVICE_TYPE,\n });\n }\n\n /**\n * Cross-stack equivalent of {@link grantConsumer}. Use when the dedup\n * table is on a different stack than the consumer Lambda — the\n * grant resolves the table name + ARN via SSM at synth time, so the\n * consumer stack does not pick up a CloudFormation export dependency\n * on the global stack.\n *\n * Inverts the singleton-guard semantics of `grantConsumer`: there is\n * no synth-time check that the same `consumerName` was registered\n * twice across stacks. Consumer names are agreed by convention\n * (see TR-015); double-registration is operator error caught at\n * design time, not synth time.\n */\n public static grantConsumerFromLookup(\n scope: Construct,\n fn: Function,\n consumerName: string,\n options: GrantConsumerOptions = {},\n ): void {\n WorkflowDedupTable.assertConsumerNameStatic(consumerName);\n const tableName = WorkflowDedupTable.tableNameFromLookup(scope);\n const tableArn = WorkflowDedupTable.tableArnFromLookup(scope);\n\n fn.addEnvironment(WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR, tableName);\n if (options.defaultTtlSeconds !== undefined) {\n fn.addEnvironment(\n \"OPENHI_WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS\",\n String(options.defaultTtlSeconds),\n );\n }\n\n fn.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\n \"dynamodb:PutItem\",\n \"dynamodb:UpdateItem\",\n \"dynamodb:GetItem\",\n \"dynamodb:Query\",\n ],\n resources: [tableArn],\n conditions: {\n \"ForAllValues:StringEquals\": {\n \"dynamodb:LeadingKeys\": [consumerName],\n },\n },\n }),\n );\n }\n\n /**\n * Service-type the publishing stack runs under. The cross-stack lookups\n * pin to this value so consumer stacks on a different service-type\n * (e.g. `data`, `auth`) resolve the parameter at the publisher's SSM\n * path instead of their own. Typed against `OpenHiServiceType` so a\n * future rename of the literal triggers a compile error; not pulled\n * from `OpenHiGlobalService.SERVICE_TYPE` because\n * `OpenHiGlobalService` already imports `WorkflowDedupTable` — a\n * back-import would create a circular dependency.\n */\n private static readonly PUBLISHER_SERVICE_TYPE: OpenHiServiceType = \"global\";\n\n /**\n * Standalone consumer-name validator shared by the instance method\n * and `grantConsumerFromLookup` so the two grants enforce identical\n * invariants.\n */\n private static assertConsumerNameStatic(consumerName: string): void {\n if (consumerName.length === 0) {\n throw new WorkflowDedupConsumerNameInvalidError(\n \"consumerName must be non-empty.\",\n );\n }\n if (consumerName.length > WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH) {\n throw new WorkflowDedupConsumerNameInvalidError(\n `consumerName must be at most ${WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH} chars; got ${consumerName.length}.`,\n );\n }\n if (/\\s/.test(consumerName)) {\n throw new WorkflowDedupConsumerNameInvalidError(\n \"consumerName must not contain whitespace.\",\n );\n }\n }\n\n /** The underlying DynamoDB table. */\n public readonly table: Table;\n\n private readonly registeredConsumers = new Set<string>();\n\n constructor(\n scope: Construct,\n id: string,\n props: WorkflowDedupTableProps = {},\n ) {\n super(scope, id);\n\n const service = OpenHiService.of(scope) as OpenHiService;\n\n // Synth-time singleton guard: refuse a second WorkflowDedupTable in\n // the same owning service (one stack per deploy target). The check is\n // intentionally scoped to the host service rather than to the CDK\n // app root: openhi/global/src/app.ts maps over `app.environments` and\n // instantiates one `OpenHiGlobalService` per environment, and each of\n // those is a separate deployment target that owns its own dedup\n // table per TR-015.\n const others = service.node\n .findAll()\n .filter(\n (c): c is WorkflowDedupTable =>\n c instanceof WorkflowDedupTable && c !== this,\n );\n if (others.length > 0) {\n throw new WorkflowDedupTableDuplicateError(\n `WorkflowDedupTable already exists at ${others[0].node.path}; ` +\n \"only one shared dedup table is allowed per service stack (TR-015).\",\n );\n }\n\n this.table = new Table(this, \"Table\", {\n tableName: getWorkflowDedupTableName(scope),\n partitionKey: {\n name: \"consumerName\",\n type: AttributeType.STRING,\n },\n sortKey: {\n name: \"sk\",\n type: AttributeType.STRING,\n },\n billingMode: BillingMode.PAY_PER_REQUEST,\n timeToLiveAttribute: \"expiresAt\",\n removalPolicy: props.removalPolicy ?? service.removalPolicy,\n });\n\n // Publish the table name and ARN so consumer stacks can discover\n // them without a cross-stack prop dependency.\n new DiscoverableStringParameter(this, \"table-name-param\", {\n ssmParamName: WorkflowDedupTable.TABLE_NAME_SSM_PARAM_NAME,\n stringValue: this.table.tableName,\n });\n new DiscoverableStringParameter(this, \"table-arn-param\", {\n ssmParamName: WorkflowDedupTable.TABLE_ARN_SSM_PARAM_NAME,\n stringValue: this.table.tableArn,\n });\n }\n\n /**\n * Wire a Lambda consumer to this table. Injects the table-name env var\n * so the runtime `WorkflowDedupClient` can resolve it, then attaches a\n * per-consumer IAM grant scoped by `dynamodb:LeadingKeys` so the\n * consumer can only read/write its own partition.\n */\n public grantConsumer(\n fn: Function,\n consumerName: string,\n options: GrantConsumerOptions = {},\n ): void {\n this.assertConsumerName(consumerName);\n\n if (this.registeredConsumers.has(consumerName)) {\n Annotations.of(this).addWarning(\n `WorkflowDedupTable: consumerName \"${consumerName}\" registered more than once; ` +\n \"subsequent grantConsumer calls add policy statements but do not re-inject the env var.\",\n );\n }\n this.registeredConsumers.add(consumerName);\n\n fn.addEnvironment(WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR, this.table.tableName);\n if (options.defaultTtlSeconds !== undefined) {\n fn.addEnvironment(\n \"OPENHI_WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS\",\n String(options.defaultTtlSeconds),\n );\n }\n\n fn.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\n \"dynamodb:PutItem\",\n \"dynamodb:UpdateItem\",\n \"dynamodb:GetItem\",\n \"dynamodb:Query\",\n ],\n resources: [this.table.tableArn],\n conditions: {\n \"ForAllValues:StringEquals\": {\n \"dynamodb:LeadingKeys\": [consumerName],\n },\n },\n }),\n );\n }\n\n private assertConsumerName(consumerName: string): void {\n WorkflowDedupTable.assertConsumerNameStatic(consumerName);\n }\n}\n\n/** Thrown when a second `WorkflowDedupTable` is instantiated in the same app. */\nexport class WorkflowDedupTableDuplicateError extends Error {\n /** @param message - human-readable description of the duplicate. */\n constructor(message: string) {\n super(message);\n this.name = \"WorkflowDedupTableDuplicateError\";\n }\n}\n\n/** Thrown when a consumerName violates the TR-015 invariants. */\nexport class WorkflowDedupConsumerNameInvalidError extends Error {\n /** @param message - human-readable description of the invariant violation. */\n constructor(message: string) {\n super(message);\n this.name = \"WorkflowDedupConsumerNameInvalidError\";\n }\n}\n","import { EventBus, EventBusProps } from \"aws-cdk-lib/aws-events\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/event-bridge/data-event-bus.md\n */\n\nexport class DataEventBus extends EventBus {\n /*****************************************************************************\n *\n * Return a name for this EventBus based on the stack environment hash. This\n * name is common across all stacks since it's using the environment hash in\n * it's name.\n *\n ****************************************************************************/\n\n public static getEventBusName(scope: Construct): string {\n const stack = OpenHiService.of(scope) as OpenHiService;\n return `datav1${stack.branchHash}`;\n }\n\n constructor(scope: Construct, props?: EventBusProps) {\n super(scope, \"data-event-bus-v1\", {\n ...props,\n eventBusName: DataEventBus.getEventBusName(scope),\n });\n }\n}\n","import { EventBus, EventBusProps } from \"aws-cdk-lib/aws-events\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/event-bridge/ops-event-bus.md\n */\n\nexport class OpsEventBus extends EventBus {\n /*****************************************************************************\n *\n * Return a name for this EventBus based on the stack environment hash. This\n * name is common across all stacks since it's using the environment hash in\n * it's name.\n *\n ****************************************************************************/\n\n public static getEventBusName(scope: Construct): string {\n const stack = OpenHiService.of(scope) as OpenHiService;\n return `opsv1${stack.branchHash}`;\n }\n\n constructor(scope: Construct, props?: EventBusProps) {\n super(scope, \"ops-event-bus-v1\", {\n ...props,\n eventBusName: OpsEventBus.getEventBusName(scope),\n });\n }\n}\n","import { EventBus, EventBusProps } from \"aws-cdk-lib/aws-events\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/event-bridge/control-event-bus.md\n */\n\nexport class ControlEventBus extends EventBus {\n /*****************************************************************************\n *\n * Return a name for this EventBus based on the stack environment hash. This\n * name is common across all stacks since it's using the environment hash in\n * its name.\n *\n ****************************************************************************/\n\n public static getEventBusName(scope: Construct): string {\n const stack = OpenHiService.of(scope) as OpenHiService;\n return `controlv1${stack.branchHash}`;\n }\n\n constructor(scope: Construct, props?: EventBusProps) {\n super(scope, \"control-event-bus-v1\", {\n ...props,\n eventBusName: ControlEventBus.getEventBusName(scope),\n });\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Duration, RemovalPolicy, Stack } from \"aws-cdk-lib\";\nimport * as ec2 from \"aws-cdk-lib/aws-ec2\";\nimport * as kinesis from \"aws-cdk-lib/aws-kinesis\";\nimport { Runtime, StartingPosition } from \"aws-cdk-lib/aws-lambda\";\nimport { KinesisEventSource } from \"aws-cdk-lib/aws-lambda-event-sources\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport * as rds from \"aws-cdk-lib/aws-rds\";\nimport { Construct } from \"constructs\";\nimport { DiscoverableStringParameter } from \"../ssm/discoverable-string-parameter\";\n\nconst HANDLER_NAME = \"data-store-postgres-replication.handler.js\";\nconst DEFAULT_DATABASE_NAME = \"openhi\";\nconst SCHEMA_NAME_PATTERN = /^[a-z_][a-z0-9_]{0,62}$/;\n\n/**\n * SSM parameter names that publish the Postgres replica's coordinates so other\n * stacks (notably the REST API stack) can discover them without a direct CDK\n * cross-stack reference. The schema name is intentionally NOT published — it\n * is a deterministic function of `branchHash` and consumers compute it locally\n * via {@link getPostgresReplicaSchemaName}.\n */\nexport const POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME =\n \"POSTGRES_REPLICA_CLUSTER_ARN\";\nexport const POSTGRES_REPLICA_SECRET_ARN_SSM_NAME =\n \"POSTGRES_REPLICA_SECRET_ARN\";\nexport const POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME =\n \"POSTGRES_REPLICA_DATABASE_NAME\";\n\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n return path.join(dirname, \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n}\n\n/**\n * Derive the per-branch Postgres schema name from a branch hash. The `b_`\n * prefix guarantees a leading letter (Postgres identifier rule). Branch hashes\n * are 6 hex chars from {@link OpenHiService.branchHash} so the resulting\n * `b_xxxxxx` is well within the 63-byte identifier limit.\n */\nexport function getPostgresReplicaSchemaName(branchHash: string): string {\n const candidate = `b_${branchHash.toLowerCase()}`;\n if (!SCHEMA_NAME_PATTERN.test(candidate)) {\n throw new Error(\n `Branch hash ${JSON.stringify(branchHash)} produces an invalid Postgres ` +\n `schema name ${JSON.stringify(candidate)}; expected /[a-z_][a-z0-9_]{0,62}/.`,\n );\n }\n return candidate;\n}\n\nexport interface DataStorePostgresReplicaProps {\n /**\n * Kinesis stream that receives DynamoDB item-level changes (the same stream\n * that backs {@link DataStoreHistoricalArchive}). The replication Lambda is\n * registered as a parallel consumer.\n */\n readonly kinesisStream: kinesis.IStream;\n /**\n * Removal policy for the cluster, secret, and dependent resources.\n */\n readonly removalPolicy: RemovalPolicy;\n /**\n * Short hash unique to the stack — used in the cluster identifier.\n */\n readonly stackHash: string;\n /**\n * Short hash unique to the branch — used to derive the per-branch schema\n * name (`b_<branchHash>`) inside the Postgres database.\n */\n readonly branchHash: string;\n /**\n * Optional VPC override. If absent, the construct creates a minimal isolated\n * VPC (2 AZs, no NAT gateways) just for the cluster and replication Lambda.\n */\n readonly vpc?: ec2.IVpc;\n /**\n * Optional database name override.\n * @default \"openhi\"\n */\n readonly databaseName?: string;\n /**\n * Aurora Serverless v2 minimum capacity in ACUs. Defaults to 1 so the\n * writer stays warm — avoids the ~10–20s scale-up wait that a cold\n * (0 ACU) cluster imposes on the next request. Set explicitly to 0 to\n * opt back into scale-to-zero if idle cost becomes the dominant concern.\n */\n readonly minCapacity?: number;\n /**\n * Aurora Serverless v2 maximum capacity in ACUs. Defaults to 2 — adequate\n * for the PoC's replication-only workload.\n */\n readonly maxCapacity?: number;\n}\n\n/**\n * DynamoDB change stream → Postgres replication tier (ADR 2026-04-17-01,\n * phase 1). Provisions an Aurora Serverless v2 PostgreSQL cluster and a\n * Lambda consumer on the existing change-stream that projects each current\n * FHIR resource into a JSONB `resources` table under a per-branch schema.\n *\n * Phase 1 is replication-only; query routing and SearchParameter-specific\n * indexes are intentionally deferred. Per-branch *clusters* (rather than the\n * shared cluster suggested by the ADR) are an explicit PoC simplification —\n * see the ADR's \"Operational notes\" section for the long-term direction.\n *\n * @see sites/www-docs/content/architecture/adr/2026-04-17-01-ad-hoc-query-support-fhir-api.md\n */\nexport class DataStorePostgresReplica extends Construct {\n /**\n * Resolve the cluster ARN published by an upstream {@link DataStorePostgresReplica}.\n * Use from any stack that needs to grant `rds-data:ExecuteStatement` against\n * the cluster.\n */\n public static clusterArnFromConstruct(scope: Construct): string {\n return DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME,\n serviceType: \"data\",\n });\n }\n\n /**\n * Resolve the credentials secret ARN published by an upstream\n * {@link DataStorePostgresReplica}. Use from any stack that needs to grant\n * `secretsmanager:GetSecretValue` against the secret.\n */\n public static secretArnFromConstruct(scope: Construct): string {\n return DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: POSTGRES_REPLICA_SECRET_ARN_SSM_NAME,\n serviceType: \"data\",\n });\n }\n\n /**\n * Resolve the database name published by an upstream\n * {@link DataStorePostgresReplica}.\n */\n public static databaseNameFromConstruct(scope: Construct): string {\n return DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME,\n serviceType: \"data\",\n });\n }\n\n public readonly vpc: ec2.IVpc;\n public readonly cluster: rds.DatabaseCluster;\n public readonly replicationFunction: NodejsFunction;\n public readonly databaseName: string;\n public readonly schemaName: string;\n\n constructor(\n scope: Construct,\n id: string,\n props: DataStorePostgresReplicaProps,\n ) {\n super(scope, id);\n\n this.databaseName = props.databaseName ?? DEFAULT_DATABASE_NAME;\n this.schemaName = getPostgresReplicaSchemaName(props.branchHash);\n\n // Pass explicit AZ names (derived from the stack region) instead of using\n // `maxAzs`, which triggers a CDK availability-zones context lookup. CI's\n // synth step doesn't have full deploy-account creds, so an unresolved AZ\n // lookup gets recorded as \"missing\" in the cdk.out manifest and the deploy\n // step then refuses to proceed. AWS region AZ names follow a stable\n // `<region>a/b/c…` pattern across all current commercial regions.\n const region = Stack.of(this).region;\n this.vpc =\n props.vpc ??\n new ec2.Vpc(this, \"Vpc\", {\n availabilityZones: [`${region}a`, `${region}b`],\n natGateways: 0,\n subnetConfiguration: [\n {\n name: \"isolated\",\n subnetType: ec2.SubnetType.PRIVATE_ISOLATED,\n cidrMask: 24,\n },\n ],\n });\n\n this.cluster = new rds.DatabaseCluster(this, \"Cluster\", {\n clusterIdentifier: `openhi-dstore-pg-${props.stackHash}`,\n engine: rds.DatabaseClusterEngine.auroraPostgres({\n version: rds.AuroraPostgresEngineVersion.VER_16_4,\n }),\n vpc: this.vpc,\n vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },\n writer: rds.ClusterInstance.serverlessV2(\"writer\"),\n serverlessV2MinCapacity: props.minCapacity ?? 1,\n serverlessV2MaxCapacity: props.maxCapacity ?? 2,\n defaultDatabaseName: this.databaseName,\n credentials: rds.Credentials.fromGeneratedSecret(\"openhi_admin\"),\n storageEncrypted: true,\n removalPolicy: props.removalPolicy,\n // Phase 2 of ADR 2026-04-17-01: the REST API Lambda queries Postgres\n // via the RDS Data API (HTTPS) so it can stay out of the cluster's VPC.\n // Direct `pg` from the replication Lambda continues to work in parallel.\n enableDataApi: true,\n });\n\n this.publishCoordinatesToSsm();\n\n this.replicationFunction = new NodejsFunction(this, \"ReplicationFunction\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 512,\n timeout: Duration.minutes(1),\n vpc: this.vpc,\n vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },\n description:\n \"Replicates DynamoDB current-resource changes into the Postgres `resources` JSONB table (ADR 2026-04-17-01).\",\n environment: {\n OPENHI_PG_HOST: this.cluster.clusterEndpoint.hostname,\n OPENHI_PG_PORT: this.cluster.clusterEndpoint.port.toString(),\n OPENHI_PG_DATABASE: this.databaseName,\n OPENHI_PG_SCHEMA: this.schemaName,\n OPENHI_PG_SECRET_ARN: this.cluster.secret!.secretArn,\n OPENHI_PG_SSL: \"true\",\n },\n bundling: {\n minify: true,\n sourceMap: false,\n // pg has conditional/optional deps (pg-native, pg-cloudflare) that\n // historically misbehave when bundled by esbuild; keep it as a real\n // node_module in the Lambda zip instead.\n nodeModules: [\"pg\"],\n },\n });\n\n this.cluster.secret!.grantRead(this.replicationFunction);\n this.cluster.connections.allowDefaultPortFrom(this.replicationFunction);\n\n this.replicationFunction.addEventSource(\n new KinesisEventSource(props.kinesisStream, {\n startingPosition: StartingPosition.LATEST,\n batchSize: 100,\n maxBatchingWindow: Duration.seconds(5),\n retryAttempts: 10,\n bisectBatchOnError: true,\n parallelizationFactor: 2,\n reportBatchItemFailures: true,\n }),\n );\n }\n\n /**\n * Publishes the cluster ARN, secret ARN, and database name as discoverable\n * SSM parameters so the REST API stack (and any future read-side consumer)\n * can wire RDS Data API access without a direct CDK cross-stack reference.\n */\n private publishCoordinatesToSsm(): void {\n new DiscoverableStringParameter(this, \"cluster-arn-param\", {\n ssmParamName: POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME,\n stringValue: this.cluster.clusterArn,\n description:\n \"ARN of the Aurora Serverless v2 cluster backing the Postgres replication tier (ADR 2026-04-17-01).\",\n });\n new DiscoverableStringParameter(this, \"secret-arn-param\", {\n ssmParamName: POSTGRES_REPLICA_SECRET_ARN_SSM_NAME,\n stringValue: this.cluster.secret!.secretArn,\n description:\n \"ARN of the Secrets Manager secret with credentials for the Postgres replication tier.\",\n });\n new DiscoverableStringParameter(this, \"database-name-param\", {\n ssmParamName: POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME,\n stringValue: this.databaseName,\n description: \"Database name within the Postgres replication cluster.\",\n });\n }\n}\n","import { Duration } from \"aws-cdk-lib\";\nimport {\n HostedZone,\n HostedZoneProps,\n IHostedZone,\n NsRecord,\n} from \"aws-cdk-lib/aws-route53\";\nimport { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/route-53/child-hosted-zone.md\n */\n\nexport interface ChildHostedZoneProps extends HostedZoneProps {\n /**\n * The root zone we will attach this sub-zone to.\n */\n readonly parentHostedZone: IHostedZone;\n}\n\nexport class ChildHostedZone extends HostedZone {\n /**\n * Used when storing the child zone ID in SSM. Use {@link OpenHiGlobalService.childHostedZoneFromConstruct} to look up.\n */\n public static readonly SSM_PARAM_NAME = \"CHILDHOSTEDZONE\";\n\n constructor(scope: Construct, id: string, props: ChildHostedZoneProps) {\n super(scope, id, { ...props });\n\n /**\n * Chain the child zone to the parent zone using NS record.\n */\n new NsRecord(this, \"child-ns-record\", {\n zone: props.parentHostedZone,\n recordName: this.zoneName,\n values: this.hostedZoneNameServers || [],\n ttl: Duration.minutes(5),\n });\n }\n}\n","import { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/route-53/root-hosted-zone.md\n */\n\n/**\n * Placeholder for root hosted zone. Use {@link OpenHiGlobalService.rootHostedZoneFromConstruct}\n * to obtain an IHostedZone from attributes (e.g. from config). The root zone is always\n * created manually and imported via config.\n */\nexport class RootHostedZone extends Construct {}\n","import {\n CachePolicy,\n Distribution,\n type DistributionProps,\n} from \"aws-cdk-lib/aws-cloudfront\";\nimport { S3BucketOrigin } from \"aws-cdk-lib/aws-cloudfront-origins\";\nimport { Bucket, type BucketProps, type IBucket } from \"aws-cdk-lib/aws-s3\";\nimport { Duration } from \"aws-cdk-lib/core\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app\";\nimport { DiscoverableStringParameter } from \"../ssm\";\n\n/**\n * Service type for the website service. Used in SSM parameter paths and by\n * OpenHiWebsiteService for fromConstruct() lookups.\n */\nexport const STATIC_HOSTING_SERVICE_TYPE = \"website\";\n\n/**\n * Props for the StaticHosting construct.\n */\nexport interface StaticHostingProps {\n /**\n * Optional S3 bucket props. Bucket name must not be set statically.\n */\n readonly bucketProps?: Omit<BucketProps, \"bucketName\">;\n\n /**\n * Optional CloudFront distribution props. Do not enable invalidation.\n * Default TTL is 10 seconds via a custom cache policy.\n */\n readonly distributionProps?: Omit<\n DistributionProps,\n \"defaultBehavior\" | \"defaultRootObject\"\n >;\n\n /**\n * Service type for SSM parameter paths.\n * @default STATIC_HOSTING_SERVICE_TYPE (\"website\")\n */\n readonly serviceType?: string;\n}\n\n/**\n * Static hosting: S3 bucket (private) + CloudFront distribution with Origin\n * Access Control (OAC). Stores bucket ARN and distribution ARN in SSM via\n * DiscoverableStringParameter for cross-stack lookup. No cache invalidation;\n * default TTL 10 seconds.\n */\nexport class StaticHosting extends Construct {\n /**\n * SSM parameter name for the S3 bucket ARN.\n */\n public static readonly SSM_PARAM_NAME_BUCKET_ARN =\n \"STATIC_HOSTING_BUCKET_ARN\";\n\n /**\n * SSM parameter name for the CloudFront distribution ARN.\n */\n public static readonly SSM_PARAM_NAME_DISTRIBUTION_ARN =\n \"STATIC_HOSTING_DISTRIBUTION_ARN\";\n\n public readonly bucket: IBucket;\n public readonly distribution: Distribution;\n\n constructor(scope: Construct, id: string, props: StaticHostingProps = {}) {\n super(scope, id);\n\n const stack = OpenHiService.of(scope) as OpenHiService;\n const serviceType = props.serviceType ?? STATIC_HOSTING_SERVICE_TYPE;\n\n this.bucket = new Bucket(this, \"bucket\", {\n blockPublicAccess: {\n blockPublicAcls: true,\n blockPublicPolicy: true,\n ignorePublicAcls: true,\n restrictPublicBuckets: true,\n },\n ...props.bucketProps,\n });\n\n const origin = S3BucketOrigin.withOriginAccessControl(this.bucket);\n\n const cachePolicy = new CachePolicy(this, \"cache-policy\", {\n cachePolicyName: `static-hosting-10s-${stack.branchHash}`,\n comment: \"Low TTL (10s) for static hosting; no invalidation\",\n defaultTtl: Duration.seconds(10),\n minTtl: Duration.seconds(0),\n maxTtl: Duration.seconds(10),\n });\n\n this.distribution = new Distribution(this, \"distribution\", {\n defaultBehavior: {\n origin,\n cachePolicy,\n },\n ...props.distributionProps,\n });\n\n new DiscoverableStringParameter(this, \"bucket-arn-param\", {\n ssmParamName: StaticHosting.SSM_PARAM_NAME_BUCKET_ARN,\n serviceType,\n stringValue: this.bucket.bucketArn,\n });\n\n new DiscoverableStringParameter(this, \"distribution-arn-param\", {\n ssmParamName: StaticHosting.SSM_PARAM_NAME_DISTRIBUTION_ARN,\n serviceType,\n stringValue: this.distribution.distributionArn,\n });\n }\n}\n","import { OPEN_HI_STAGE } from \"@openhi/config\";\nimport {\n IUserPool,\n IUserPoolClient,\n IUserPoolDomain,\n LambdaVersion,\n UserPool,\n UserPoolClient,\n UserPoolDomain,\n UserPoolOperation,\n UserPoolProps,\n} from \"aws-cdk-lib/aws-cognito\";\nimport { IEventBus } from \"aws-cdk-lib/aws-events\";\nimport { Effect, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport { IKey, Key } from \"aws-cdk-lib/aws-kms\";\nimport { IFunction } from \"aws-cdk-lib/aws-lambda\";\nimport { Stack } from \"aws-cdk-lib/core\";\nimport { Construct } from \"constructs\";\nimport { OpenHiDataService } from \"./open-hi-data-service\";\nimport { OpenHiGlobalService } from \"./open-hi-global-service\";\nimport { OpenHiEnvironment } from \"../app/open-hi-environment\";\nimport {\n OpenHiService,\n OpenHiServiceProps,\n OpenHiServiceType,\n} from \"../app/open-hi-service\";\nimport { CognitoFixtureSeederClient } from \"../components/cognito/cognito-fixture-seeder-client\";\nimport { CognitoUserPool } from \"../components/cognito/cognito-user-pool\";\nimport { CognitoUserPoolClient } from \"../components/cognito/cognito-user-pool-client\";\nimport { CognitoUserPoolDomain } from \"../components/cognito/cognito-user-pool-domain\";\nimport { CognitoUserPoolKmsKey } from \"../components/cognito/cognito-user-pool-kms-key\";\nimport { PostAuthenticationLambda } from \"../components/cognito/post-authentication-lambda\";\nimport { PostConfirmationLambda } from \"../components/cognito/post-confirmation-lambda\";\nimport { PreTokenGenerationLambda } from \"../components/cognito/pre-token-generation-lambda\";\nimport { DiscoverableStringParameter } from \"../components/ssm\";\nimport { UserOnboardingWorkflow } from \"../workflows/control-plane/user-onboarding\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/services/open-hi-auth-service.md\n */\n\nexport interface OpenHiAuthServiceProps extends OpenHiServiceProps {\n /**\n * Optional props for the Cognito User Pool.\n */\n readonly userPoolProps?: UserPoolProps;\n}\n\n/**\n * OpenHI Auth Service stack.\n *\n * @remarks\n * The Auth service manages authentication infrastructure including:\n * - Cognito User Pool for user management and authentication\n * - User Pool Client for application integration\n * - User Pool Domain for hosting the Cognito hosted UI\n * - KMS Key for Cognito User Pool encryption\n *\n * Resources are created in protected methods; subclasses may override to customize.\n * Other stacks obtain auth by calling **OpenHiAuthService.userPoolFromConstruct(scope)**,\n * **OpenHiAuthService.userPoolClientFromConstruct(scope)**,\n * **OpenHiAuthService.userPoolDomainFromConstruct(scope)**,\n * and **OpenHiAuthService.userPoolKmsKeyFromConstruct(scope)** for each resource needed.\n *\n * Only one instance of the auth service should exist per environment.\n *\n * @public\n */\nexport class OpenHiAuthService extends OpenHiService {\n static readonly SERVICE_TYPE = \"auth\" as const satisfies OpenHiServiceType;\n\n /**\n * Returns an IUserPool by looking up the Auth stack's User Pool ID from SSM.\n */\n static userPoolFromConstruct(scope: Construct): IUserPool {\n const userPoolId = DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: CognitoUserPool.SSM_PARAM_NAME,\n serviceType: OpenHiAuthService.SERVICE_TYPE,\n });\n return UserPool.fromUserPoolId(scope, \"user-pool\", userPoolId);\n }\n\n /**\n * Returns an IUserPoolClient by looking up the Auth stack's User Pool Client ID from SSM.\n */\n static userPoolClientFromConstruct(scope: Construct): IUserPoolClient {\n const userPoolClientId = DiscoverableStringParameter.valueForLookupName(\n scope,\n {\n ssmParamName: CognitoUserPoolClient.SSM_PARAM_NAME,\n serviceType: OpenHiAuthService.SERVICE_TYPE,\n },\n );\n return UserPoolClient.fromUserPoolClientId(\n scope,\n \"user-pool-client\",\n userPoolClientId,\n );\n }\n\n /**\n * Returns the dedicated fixture-seeder IUserPoolClient by looking up\n * its ID from SSM. Only non-prod auth stacks publish this parameter\n * (per the conditional in {@link createFixtureSeederClient}); calling\n * this against a prod-deployed stack will fail at lookup time.\n *\n * Consumed by `OpenHiRestApiService` (in non-prod) so the authorizer\n * accepts tokens issued by this client, and by the seed-fixtures CLI\n * to drive USER_PASSWORD_AUTH against this client's ID.\n */\n static fixtureSeederClientFromConstruct(scope: Construct): IUserPoolClient {\n const clientId = DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: CognitoFixtureSeederClient.SSM_PARAM_NAME,\n serviceType: OpenHiAuthService.SERVICE_TYPE,\n });\n return UserPoolClient.fromUserPoolClientId(\n scope,\n \"fixture-seeder-client\",\n clientId,\n );\n }\n\n /**\n * Returns an IUserPoolDomain by looking up the Auth stack's User Pool Domain from SSM.\n */\n static userPoolDomainFromConstruct(scope: Construct): IUserPoolDomain {\n const domainName = DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: CognitoUserPoolDomain.SSM_PARAM_NAME,\n serviceType: OpenHiAuthService.SERVICE_TYPE,\n });\n return UserPoolDomain.fromDomainName(scope, \"user-pool-domain\", domainName);\n }\n\n /**\n * Returns an IKey (KMS) by looking up the Auth stack's User Pool KMS Key ARN from SSM.\n */\n static userPoolKmsKeyFromConstruct(scope: Construct): IKey {\n const keyArn = DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: CognitoUserPoolKmsKey.SSM_PARAM_NAME,\n serviceType: OpenHiAuthService.SERVICE_TYPE,\n });\n return Key.fromKeyArn(scope, \"kms-key\", keyArn);\n }\n\n get serviceType(): string {\n return OpenHiAuthService.SERVICE_TYPE;\n }\n\n /** Override so this.props is typed with this service's options (e.g. userPoolProps). */\n public override props: OpenHiAuthServiceProps;\n\n public readonly userPoolKmsKey: IKey;\n public readonly preTokenGenerationLambda: IFunction;\n public readonly postAuthenticationLambda: IFunction;\n public readonly postConfirmationLambda: IFunction;\n public readonly userOnboardingWorkflow: UserOnboardingWorkflow;\n public readonly userPool: IUserPool;\n public readonly userPoolClient: IUserPoolClient;\n public readonly userPoolDomain: IUserPoolDomain;\n /**\n * Dedicated USER_PASSWORD_AUTH client for the seed-fixtures CLI.\n * Only created in non-prod environments (see\n * {@link createFixtureSeederClient}). `undefined` in prod.\n */\n public readonly fixtureSeederClient?: IUserPoolClient;\n\n /**\n * Cross-stack reference to the data store table. Cached so repeated\n * lookups share a single CDK construct id (\"dynamo-db-data-store\") in\n * this stack — a second `Table.fromTableName` call under the same scope\n * would collide.\n */\n private _dataStoreTable: ReturnType<\n typeof OpenHiDataService.dynamoDbDataStoreFromConstruct\n > | null = null;\n private _controlEventBus: IEventBus | null = null;\n\n constructor(ohEnv: OpenHiEnvironment, props: OpenHiAuthServiceProps = {}) {\n super(ohEnv, OpenHiAuthService.SERVICE_TYPE, props);\n this.props = props;\n\n this.userPoolKmsKey = this.createUserPoolKmsKey();\n this.preTokenGenerationLambda = this.createPreTokenGenerationLambda();\n this.postAuthenticationLambda = this.createPostAuthenticationLambda();\n this.postConfirmationLambda = this.createPostConfirmationLambda();\n this.userOnboardingWorkflow = this.createUserOnboardingWorkflow();\n this.userPool = this.createUserPool();\n this.grantPreTokenGenerationPermissions();\n this.grantPostAuthenticationPermissions();\n this.grantPostConfirmationPermissions();\n this.userPoolClient = this.createUserPoolClient();\n this.userPoolDomain = this.createUserPoolDomain();\n this.fixtureSeederClient = this.createFixtureSeederClient();\n }\n\n /**\n * Creates the KMS key for the Cognito User Pool and exports its ARN to SSM.\n * Look up via {@link OpenHiAuthService.userPoolKmsKeyFromConstruct}.\n * Override to customize.\n */\n protected createUserPoolKmsKey(): IKey {\n const key = new CognitoUserPoolKmsKey(this);\n new DiscoverableStringParameter(this, \"kms-key-param\", {\n ssmParamName: CognitoUserPoolKmsKey.SSM_PARAM_NAME,\n stringValue: key.keyArn,\n description:\n \"KMS key ARN for Cognito User Pool (e.g. custom sender); cross-stack reference\",\n });\n return key;\n }\n\n /**\n * Creates the Pre Token Generation Lambda (Cognito trigger). On every\n * sign-in and token refresh the Lambda resolves the User by Cognito `sub`\n * (GSI2) and injects `ohi_tid`, `ohi_wid`, `ohi_uid`, `ohi_uname` into\n * both the ID token and the access token (ADR 2026-03-17-01).\n */\n protected createPreTokenGenerationLambda(): IFunction {\n const construct = new PreTokenGenerationLambda(this, {\n dynamoTableName: this.dataStoreTable().tableName,\n });\n return construct.lambda;\n }\n\n /**\n * Creates the Post Authentication Lambda (Cognito trigger). Calls\n * AdminUserGlobalSignOut on every sign-in to enforce single-device-per-user\n * sessions per ADR 2026-03-17-01.\n */\n protected createPostAuthenticationLambda(): IFunction {\n const construct = new PostAuthenticationLambda(this);\n return construct.lambda;\n }\n\n /**\n * Creates the Post Confirmation Lambda (Cognito trigger). On sign-up\n * confirmation, publishes a control-plane workflow event; provisioning lives\n * behind EventBridge.\n */\n protected createPostConfirmationLambda(): IFunction {\n const construct = new PostConfirmationLambda(this, {\n controlEventBusName: this.controlEventBus().eventBusName,\n });\n return construct.lambda;\n }\n\n protected createUserOnboardingWorkflow(): UserOnboardingWorkflow {\n return new UserOnboardingWorkflow(this, {\n controlEventBus: this.controlEventBus(),\n dataStoreTable: this.dataStoreTable(),\n });\n }\n\n private dataStoreTable() {\n if (this._dataStoreTable === null) {\n this._dataStoreTable =\n OpenHiDataService.dynamoDbDataStoreFromConstruct(this);\n }\n return this._dataStoreTable;\n }\n\n private controlEventBus() {\n if (this._controlEventBus === null) {\n this._controlEventBus =\n OpenHiGlobalService.controlEventBusFromConstruct(this);\n }\n return this._controlEventBus;\n }\n\n /**\n * Creates the Cognito User Pool and exports its ID to SSM.\n * Look up via {@link OpenHiAuthService.userPoolFromConstruct}.\n * Override to customize.\n */\n protected createUserPool(): IUserPool {\n const userPool = new CognitoUserPool(this, {\n ...this.props.userPoolProps,\n customSenderKmsKey: this.userPoolKmsKey,\n });\n // Access-token-only claims require Pre Token Generation V2_0.\n userPool.addTrigger(\n UserPoolOperation.PRE_TOKEN_GENERATION_CONFIG,\n this.preTokenGenerationLambda,\n LambdaVersion.V2_0,\n );\n userPool.addTrigger(\n UserPoolOperation.POST_AUTHENTICATION,\n this.postAuthenticationLambda,\n );\n userPool.addTrigger(\n UserPoolOperation.POST_CONFIRMATION,\n this.postConfirmationLambda,\n );\n new DiscoverableStringParameter(this, \"user-pool-param\", {\n ssmParamName: CognitoUserPool.SSM_PARAM_NAME,\n stringValue: userPool.userPoolId,\n description:\n \"Cognito User Pool ID for this Auth stack; cross-stack reference\",\n });\n return userPool;\n }\n\n /**\n * Grants the Pre Token Generation Lambda read-only access on the data\n * store table and its GSIs. The Lambda only needs:\n * - `Query` on GSI2 to resolve a User by Cognito `sub`\n * - `GetItem` on the base table for direct User reads\n *\n * No write or scan access: a User missing `currentTenant`/`currentWorkspace`\n * falls into the absent-claims path; repair belongs in a separate backfill.\n */\n protected grantPreTokenGenerationPermissions(): void {\n const dataStoreTable = this.dataStoreTable();\n const dynamoActions = [\"dynamodb:GetItem\", \"dynamodb:Query\"] as const;\n dataStoreTable.grant(this.preTokenGenerationLambda, ...dynamoActions);\n this.preTokenGenerationLambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [...dynamoActions],\n resources: [`${dataStoreTable.tableArn}/index/*`],\n }),\n );\n }\n\n /**\n * Grants the Post Authentication Lambda permission to call\n * `cognito-idp:AdminUserGlobalSignOut`.\n *\n * Scoped via `Stack.of(this).formatArn` rather than `userPool.userPoolArn`\n * because the User Pool registers this Lambda as a Post Authentication\n * trigger, creating the cycle:\n * userPool → lambda (trigger ARN) → role policy → userPool ARN.\n * Using `formatArn` avoids referencing the User Pool resource directly\n * while still scoping to user pools in this account+region. The Lambda\n * is invoked only by Cognito with a Cognito-provided `event.userPoolId`,\n * so the runtime target is constrained by the trigger contract.\n */\n protected grantPostAuthenticationPermissions(): void {\n this.postAuthenticationLambda.addToRolePolicy(\n new PolicyStatement({\n actions: [\"cognito-idp:AdminUserGlobalSignOut\"],\n resources: [\n Stack.of(this).formatArn({\n service: \"cognito-idp\",\n resource: \"userpool\",\n resourceName: \"*\",\n }),\n ],\n }),\n );\n }\n\n /**\n * Grants the Post Confirmation Lambda publish-only access to the\n * control-plane event bus. Workflow Lambdas own DynamoDB writes.\n */\n protected grantPostConfirmationPermissions(): void {\n this.controlEventBus().grantPutEventsTo(this.postConfirmationLambda);\n }\n\n /**\n * Creates the User Pool Client and exports its ID to SSM (AUTH service type).\n * Look up via {@link OpenHiAuthService.userPoolClientFromConstruct}.\n * Override to customize.\n */\n protected createUserPoolClient(): IUserPoolClient {\n const client = new CognitoUserPoolClient(this, {\n userPool: this.userPool,\n });\n new DiscoverableStringParameter(this, \"user-pool-client-param\", {\n ssmParamName: CognitoUserPoolClient.SSM_PARAM_NAME,\n stringValue: client.userPoolClientId,\n description:\n \"Cognito User Pool Client ID for this Auth stack; cross-stack reference\",\n });\n return client;\n }\n\n /**\n * Creates the dedicated USER_PASSWORD_AUTH app client for the\n * `@openhi/seed-fixtures` CLI, **only** in non-prod environments.\n * Returns `undefined` when this stack is being deployed to a prod\n * stage so the prod auth stack carries no fixture-seeder code path.\n *\n * Operator post-deploy: create a `fixture-seeder` Cognito user with\n * a service password (manually via console or scripted with\n * `aws cognito-idp admin-create-user`); the CLI consumes those creds\n * via env vars to drive `InitiateAuth`.\n */\n protected createFixtureSeederClient(): IUserPoolClient | undefined {\n if (this.ohEnv.ohStage.stageType === OPEN_HI_STAGE.PROD) {\n return undefined;\n }\n const client = new CognitoFixtureSeederClient(this, {\n userPool: this.userPool,\n });\n new DiscoverableStringParameter(this, \"fixture-seeder-client-param\", {\n ssmParamName: CognitoFixtureSeederClient.SSM_PARAM_NAME,\n stringValue: client.userPoolClientId,\n description:\n \"Cognito User Pool Client ID for the OpenHI fixture-seeder CLI \" +\n \"(USER_PASSWORD_AUTH; non-prod only); cross-stack reference\",\n });\n return client;\n }\n\n /**\n * Creates the User Pool Domain (Cognito hosted UI) and exports domain name to SSM.\n * Look up via {@link OpenHiAuthService.userPoolDomainFromConstruct}.\n * Override to customize.\n */\n protected createUserPoolDomain(): IUserPoolDomain {\n const domain = new CognitoUserPoolDomain(this, {\n userPool: this.userPool,\n cognitoDomain: {\n domainPrefix: `auth-${this.branchHash}`,\n },\n });\n new DiscoverableStringParameter(this, \"user-pool-domain-param\", {\n ssmParamName: CognitoUserPoolDomain.SSM_PARAM_NAME,\n stringValue: domain.domainName,\n description:\n \"Cognito User Pool Domain (hosted UI) for this Auth stack; cross-stack reference\",\n });\n return domain;\n }\n}\n","import { OPEN_HI_STAGE } from \"@openhi/config\";\nimport { StreamViewType, ITable, Table } from \"aws-cdk-lib/aws-dynamodb\";\nimport { IEventBus } from \"aws-cdk-lib/aws-events\";\nimport * as kinesis from \"aws-cdk-lib/aws-kinesis\";\nimport { Construct } from \"constructs\";\nimport { OpenHiAuthService } from \"./open-hi-auth-service\";\nimport { OpenHiGlobalService } from \"./open-hi-global-service\";\nimport { OpenHiEnvironment } from \"../app/open-hi-environment\";\nimport {\n OpenHiService,\n OpenHiServiceProps,\n OpenHiServiceType,\n} from \"../app/open-hi-service\";\nimport { DataStoreHistoricalArchive } from \"../components/dynamodb/data-store-historical-archive\";\nimport {\n DynamoDbDataStore,\n getDynamoDbDataStoreTableName,\n} from \"../components/dynamodb/dynamo-db-data-store\";\nimport { DataStorePostgresReplica } from \"../components/postgres/data-store-postgres-replica\";\nimport { SeedDemoDataWorkflow } from \"../workflows/control-plane/seed-demo-data\";\nimport { SeedSystemDataWorkflow } from \"../workflows/control-plane/seed-system-data\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/services/open-hi-data-service.md\n */\n\nexport type OpenHiDataServiceProps = OpenHiServiceProps;\n\n/**\n * Data storage service stack: centralizes DynamoDB, S3, and other persistence\n * resources for OpenHI. Creates the single-table data store in a protected\n * method; subclasses may override to customize. EventBridge event buses\n * (data, ops, control) are owned by {@link OpenHiGlobalService} so they deploy\n * ahead of regional services.\n */\nexport class OpenHiDataService extends OpenHiService {\n static readonly SERVICE_TYPE = \"data\" as const satisfies OpenHiServiceType;\n\n /**\n * Returns the data store table by name. Use from other stacks (e.g. REST API Lambda) to obtain an ITable reference.\n */\n static dynamoDbDataStoreFromConstruct(\n scope: Construct,\n id = \"dynamo-db-data-store\",\n ): ITable {\n return Table.fromTableName(scope, id, getDynamoDbDataStoreTableName(scope));\n }\n\n get serviceType(): string {\n return OpenHiDataService.SERVICE_TYPE;\n }\n\n /** Override so this.props is typed with this service's options. */\n public override props: OpenHiDataServiceProps;\n\n /**\n * The single-table DynamoDB data store. Use {@link OpenHiDataService.dynamoDbDataStoreFromConstruct}\n * from other stacks to obtain an ITable reference by name.\n */\n public readonly dataStore: ITable;\n\n /**\n * Kinesis stream receiving DynamoDB item-level changes for the data store table.\n */\n public readonly dataStoreChangeStream: kinesis.IStream;\n\n /**\n * Historical archive pipeline (Kinesis → Firehose → S3) and data-event-bus\n * notifications for current FHIR resources (ADRs 2026-03-11-02, 2026-03-02-01).\n */\n public readonly dataStoreHistoricalArchive: DataStoreHistoricalArchive;\n\n /**\n * Postgres replication tier (ADR 2026-04-17-01, phase 1). A second consumer\n * on the change stream that projects current FHIR resources into a JSONB\n * `resources` table on Aurora Serverless v2. Phase 1 is replication-only;\n * the read path is not wired up yet.\n */\n public readonly dataStorePostgresReplica: DataStorePostgresReplica;\n\n /**\n * Deploy-triggered workflow that idempotently re-asserts the\n * platform-singleton control-plane records (today: the three canonical\n * Roles via `PLATFORM_ROLE_CONCEPTS`; future: additional system\n * data). Subscribes to `platform.deployment-completed.v1` on the\n * control event bus and dedups via the shared `WorkflowDedupTable`.\n */\n public readonly seedSystemDataWorkflow: SeedSystemDataWorkflow;\n\n /**\n * Deploy-triggered workflow that idempotently re-asserts the demo\n * data graph (placeholder + 3 demo Tenants + 5 Workspaces; per\n * dev-user Cognito users with their DynamoDB User records,\n * Memberships, and RoleAssignments). **Non-prod only** —\n * `undefined` on prod stages. The synth-time stage gate in\n * {@link createSeedDemoDataWorkflow} is the only guarantee\n * separating prod stacks from the workflow's IAM grants and rule\n * target; the construct itself never checks the stage.\n */\n public readonly seedDemoDataWorkflow?: SeedDemoDataWorkflow;\n\n /**\n * Cached control-event-bus lookup. `OpenHiGlobalService.controlEventBusFromConstruct`\n * registers a child `EventBus.fromEventBusName` construct with a\n * fixed id under the scope it is passed, so calling it twice on the\n * same `OpenHiDataService` instance collides. The cache mirrors the\n * `private controlEventBus()` pattern already used in\n * `OpenHiAuthService`. Use {@link controlEventBus} from this class\n * — never call the static lookup from inside `OpenHiDataService`.\n */\n private _controlEventBus: IEventBus | null = null;\n\n constructor(ohEnv: OpenHiEnvironment, props: OpenHiDataServiceProps = {}) {\n super(ohEnv, OpenHiDataService.SERVICE_TYPE, props);\n this.props = props;\n\n this.dataStoreChangeStream = new kinesis.Stream(\n this,\n \"data-store-change-stream\",\n {\n streamName: `openhi-dstore-cdc-${this.branchHash}`,\n streamMode: kinesis.StreamMode.ON_DEMAND,\n // CDK default for kinesis.Stream is RETAIN, which strands the stream\n // when a non-prod stack is destroyed. Use the service's policy so\n // non-prod tears down cleanly while prod retains.\n removalPolicy: this.removalPolicy,\n },\n );\n\n this.dataStore = this.createDataStore();\n\n this.dataStoreHistoricalArchive = new DataStoreHistoricalArchive(\n this,\n \"data-store-historical-archive\",\n {\n kinesisStream: this.dataStoreChangeStream,\n removalPolicy: this.removalPolicy,\n stackHash: this.stackHash,\n dataEventBus: OpenHiGlobalService.dataEventBusFromConstruct(this),\n },\n );\n\n this.dataStorePostgresReplica = new DataStorePostgresReplica(\n this,\n \"data-store-postgres-replica\",\n {\n kinesisStream: this.dataStoreChangeStream,\n removalPolicy: this.removalPolicy,\n stackHash: this.stackHash,\n branchHash: this.branchHash,\n },\n );\n\n this.seedSystemDataWorkflow = this.createSeedSystemDataWorkflow();\n this.seedDemoDataWorkflow = this.createSeedDemoDataWorkflow();\n }\n\n /**\n * Lazily looks up the control event bus exactly once per\n * `OpenHiDataService` instance and caches the reference. Every\n * workflow that consumes the bus must read it through this method\n * — see {@link _controlEventBus} for the underlying collision risk.\n */\n private controlEventBus(): IEventBus {\n if (this._controlEventBus === null) {\n this._controlEventBus =\n OpenHiGlobalService.controlEventBusFromConstruct(this);\n }\n return this._controlEventBus;\n }\n\n /**\n * Creates the seed-system-data workflow. Override to customize.\n */\n protected createSeedSystemDataWorkflow(): SeedSystemDataWorkflow {\n return new SeedSystemDataWorkflow(this, {\n controlEventBus: this.controlEventBus(),\n dataStoreTable: this.dataStore,\n });\n }\n\n /**\n * Creates the seed-demo-data workflow — but only on non-prod\n * stages. Returns `undefined` on prod so the workflow literally\n * does not exist in prod stacks. Override to customize.\n */\n protected createSeedDemoDataWorkflow(): SeedDemoDataWorkflow | undefined {\n if (this.ohEnv.ohStage.stageType === OPEN_HI_STAGE.PROD) {\n return undefined;\n }\n return new SeedDemoDataWorkflow(this, {\n controlEventBus: this.controlEventBus(),\n dataStoreTable: this.dataStore,\n userPool: OpenHiAuthService.userPoolFromConstruct(this),\n });\n }\n\n /**\n * Creates the single-table DynamoDB data store.\n * Override to customize.\n */\n protected createDataStore(): ITable {\n return new DynamoDbDataStore(this, \"dynamo-db-data-store\", {\n kinesisStream: this.dataStoreChangeStream,\n stream: StreamViewType.NEW_AND_OLD_IMAGES,\n });\n }\n}\n","import {\n Certificate,\n CertificateValidation,\n ICertificate,\n} from \"aws-cdk-lib/aws-certificatemanager\";\nimport { EventBus, IEventBus } from \"aws-cdk-lib/aws-events\";\nimport {\n HostedZone,\n HostedZoneAttributes,\n IHostedZone,\n} from \"aws-cdk-lib/aws-route53\";\nimport { StringParameter } from \"aws-cdk-lib/aws-ssm\";\nimport { Construct } from \"constructs\";\nimport { OpenHiEnvironment } from \"../app/open-hi-environment\";\nimport {\n OpenHiService,\n OpenHiServiceProps,\n OpenHiServiceType,\n} from \"../app/open-hi-service\";\nimport { RootWildcardCertificate } from \"../components/acm/root-wildcard-certificate\";\nimport { WorkflowDedupTable } from \"../components/dynamodb/workflow-dedup-table\";\nimport { ControlEventBus } from \"../components/event-bridge/control-event-bus\";\nimport { DataEventBus } from \"../components/event-bridge/data-event-bus\";\nimport { OpsEventBus } from \"../components/event-bridge/ops-event-bus\";\nimport { ChildHostedZone } from \"../components/route-53/child-hosted-zone\";\nimport { DiscoverableStringParameter } from \"../components/ssm\";\nimport { PlatformDeployBridge } from \"../workflows/control-plane/platform-deploy-bridge\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/services/open-hi-global-service.md\n */\n\nexport interface OpenHiGlobalServiceProps extends OpenHiServiceProps {}\n\n/**\n * Global Infrastructure stack: owns global DNS, certificates, and the\n * cross-region EventBridge buses (data, ops, control). Resources (root zone,\n * optional child zone, wildcard cert, data/ops/control buses) are created in\n * protected methods; subclasses may override to customize.\n */\nexport class OpenHiGlobalService extends OpenHiService {\n static readonly SERVICE_TYPE = \"global\" as const satisfies OpenHiServiceType;\n\n /**\n * Returns an IHostedZone from the given attributes (no SSM). Use when the zone is imported from config.\n */\n static rootHostedZoneFromConstruct(\n scope: Construct,\n props: HostedZoneAttributes,\n ): IHostedZone {\n return HostedZone.fromHostedZoneAttributes(scope, \"root-zone\", props);\n }\n\n /**\n * Returns an ICertificate by looking up the Global stack's wildcard cert ARN from SSM.\n */\n static rootWildcardCertificateFromConstruct(scope: Construct): ICertificate {\n const certificateArn = StringParameter.valueForStringParameter(\n scope,\n RootWildcardCertificate.ssmParameterName(),\n );\n return Certificate.fromCertificateArn(\n scope,\n \"wildcard-certificate\",\n certificateArn,\n );\n }\n\n /**\n * Returns an IHostedZone by looking up the child hosted zone ID from SSM. Defaults to GLOBAL service type.\n */\n static childHostedZoneFromConstruct(\n scope: Construct,\n props: { zoneName: string; serviceType?: OpenHiServiceType },\n ): IHostedZone {\n const hostedZoneId = DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: ChildHostedZone.SSM_PARAM_NAME,\n serviceType: props.serviceType ?? OpenHiGlobalService.SERVICE_TYPE,\n });\n return HostedZone.fromHostedZoneAttributes(scope, \"child-zone\", {\n hostedZoneId,\n zoneName: props.zoneName,\n });\n }\n\n /**\n * Returns the data event bus by name (deterministic per branch). Use from other stacks to obtain an IEventBus reference.\n */\n static dataEventBusFromConstruct(scope: Construct): IEventBus {\n return EventBus.fromEventBusName(\n scope,\n \"data-event-bus\",\n DataEventBus.getEventBusName(scope),\n );\n }\n\n /**\n * Returns the ops event bus by name (deterministic per branch). Use from other stacks to obtain an IEventBus reference.\n */\n static opsEventBusFromConstruct(scope: Construct): IEventBus {\n return EventBus.fromEventBusName(\n scope,\n \"ops-event-bus\",\n OpsEventBus.getEventBusName(scope),\n );\n }\n\n /**\n * Returns the control-plane event bus by name (deterministic per branch). Use from other stacks to obtain an IEventBus reference.\n */\n static controlEventBusFromConstruct(scope: Construct): IEventBus {\n return EventBus.fromEventBusName(\n scope,\n \"control-event-bus\",\n ControlEventBus.getEventBusName(scope),\n );\n }\n\n /**\n * Returns the workflow dedup table by name (deterministic per branch).\n * Use from other stacks to obtain an ITable reference. Consumer Lambdas\n * are typically wired via `WorkflowDedupTable.grantConsumer(fn, name)`\n * on the owning service's `workflowDedupTable` reference; the\n * `tableNameFromLookup` / `tableArnFromLookup` SSM helpers on the\n * construct cover cross-stack consumers that need only the name/ARN.\n */\n static workflowDedupTableNameFromLookup(scope: Construct): string {\n return WorkflowDedupTable.tableNameFromLookup(scope);\n }\n\n get serviceType(): string {\n return OpenHiGlobalService.SERVICE_TYPE;\n }\n\n /** Override so this.props is typed with this service's options. */\n public override props: OpenHiGlobalServiceProps;\n\n public readonly rootHostedZone: IHostedZone;\n public readonly childHostedZone?: IHostedZone;\n public readonly rootWildcardCertificate: ICertificate;\n\n /**\n * Event bus for data-related events (ingestion, transformation, storage).\n * Other stacks obtain it via {@link OpenHiGlobalService.dataEventBusFromConstruct}.\n */\n public readonly dataEventBus: IEventBus;\n\n /**\n * Event bus for operational events (monitoring, alerting, system health).\n * Other stacks obtain it via {@link OpenHiGlobalService.opsEventBusFromConstruct}.\n */\n public readonly opsEventBus: IEventBus;\n\n /**\n * Event bus for control-plane lifecycle and command events.\n * Other stacks obtain it via {@link OpenHiGlobalService.controlEventBusFromConstruct}.\n */\n public readonly controlEventBus: IEventBus;\n\n /**\n * Bridge that watches CloudFormation Stack Status Change events on the\n * default AWS bus and republishes terminal-success events for OpenHi-tagged\n * stacks onto {@link controlEventBus} as `platform.deployment-completed.v1`.\n */\n public readonly platformDeployBridge: PlatformDeployBridge;\n\n /**\n * Shared dedup table every retryable workflow consumer dedupes against\n * (TR-015). Singleton per deployment — provisioned here on the global\n * stack so consumer stacks reach it via SSM lookups, not props.\n */\n public readonly workflowDedupTable: WorkflowDedupTable;\n\n constructor(ohEnv: OpenHiEnvironment, props: OpenHiGlobalServiceProps = {}) {\n super(ohEnv, OpenHiGlobalService.SERVICE_TYPE, props);\n this.props = props;\n\n this.validateConfig(props);\n\n this.rootHostedZone = this.createRootHostedZone();\n this.childHostedZone = this.createChildHostedZone();\n this.rootWildcardCertificate = this.createRootWildcardCertificate();\n this.dataEventBus = this.createDataEventBus();\n this.opsEventBus = this.createOpsEventBus();\n this.controlEventBus = this.createControlEventBus();\n this.workflowDedupTable = this.createWorkflowDedupTable();\n this.platformDeployBridge = this.createPlatformDeployBridge();\n }\n\n /**\n * Validates that config required for the Global stack is present.\n */\n protected validateConfig(props: OpenHiGlobalServiceProps): void {\n const { config } = props;\n if (!config) {\n throw new Error(\"Config is required\");\n }\n if (!config.zoneName) {\n throw new Error(\"Zone name is required to import the root zone\");\n }\n if (!config.hostedZoneId) {\n throw new Error(\"Hosted zone ID is required to import the root zone\");\n }\n }\n\n /**\n * Creates the root hosted zone (imported via attributes from config).\n * Override to customize or create the zone.\n */\n protected createRootHostedZone(): IHostedZone {\n return OpenHiGlobalService.rootHostedZoneFromConstruct(this, {\n zoneName: this.config.zoneName!,\n hostedZoneId: this.config.hostedZoneId!,\n });\n }\n\n /**\n * Creates the optional child hosted zone (e.g. branch subdomain).\n * Override to create a child zone when config provides childHostedZoneAttributes.\n * If you create a ChildHostedZone, also create a DiscoverableStringParameter\n * with ChildHostedZone.SSM_PARAM_NAME and the zone's hostedZoneId.\n */\n protected createChildHostedZone(): IHostedZone | undefined {\n return undefined;\n }\n\n /**\n * Creates the root wildcard certificate. On main branch, creates a new cert\n * with DNS validation; otherwise imports from SSM.\n * Override to customize certificate creation.\n */\n protected createRootWildcardCertificate(): ICertificate {\n if (this.branchName === \"main\") {\n return new RootWildcardCertificate(this, {\n domainName: `*.${this.rootHostedZone.zoneName}`,\n subjectAlternativeNames: [this.rootHostedZone.zoneName],\n validation: CertificateValidation.fromDns(this.rootHostedZone),\n });\n }\n return OpenHiGlobalService.rootWildcardCertificateFromConstruct(this);\n }\n\n /**\n * Creates the data event bus.\n * Override to customize.\n */\n protected createDataEventBus(): IEventBus {\n return new DataEventBus(this);\n }\n\n /**\n * Creates the ops event bus.\n * Override to customize.\n */\n protected createOpsEventBus(): IEventBus {\n return new OpsEventBus(this);\n }\n\n /**\n * Creates the control-plane event bus.\n * Override to customize.\n */\n protected createControlEventBus(): IEventBus {\n return new ControlEventBus(this);\n }\n\n /**\n * Creates the platform deploy bridge that republishes CloudFormation\n * Stack Status Change events onto the control event bus.\n * Override to customize.\n */\n protected createPlatformDeployBridge(): PlatformDeployBridge {\n return new PlatformDeployBridge(this, {\n controlEventBus: this.controlEventBus,\n });\n }\n\n /**\n * Creates the shared workflow dedup table (TR-015 singleton).\n * Override to customize.\n */\n protected createWorkflowDedupTable(): WorkflowDedupTable {\n return new WorkflowDedupTable(this, \"workflow-dedup-table\");\n }\n}\n","/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/platform-deploy-bridge/index.md\n */\n\n/** EventBridge `source` for the AWS-native CloudFormation events the bridge listens to. */\nexport const CLOUDFORMATION_EVENT_SOURCE = \"aws.cloudformation\" as const;\n\n/** EventBridge `detail-type` for terminal stack status events. */\nexport const CLOUDFORMATION_STACK_STATUS_CHANGE_DETAIL_TYPE =\n \"CloudFormation Stack Status Change\" as const;\n\n/** Stack statuses the bridge republishes. Other statuses are pre-filtered at the rule. */\nexport const BRIDGED_STATUSES = [\"CREATE_COMPLETE\", \"UPDATE_COMPLETE\"] as const;\nexport type BridgedStatus = (typeof BRIDGED_STATUSES)[number];\n\n/** Env var the bridge handler reads to discover the control event bus name. */\nexport const CONTROL_EVENT_BUS_NAME_ENV_VAR = \"CONTROL_EVENT_BUS_NAME\";\n\n/**\n * Env var the bridge handler reads for the resolved\n * `openhi:repo-name`-shaped tag key. Resolved at synth time from the host\n * stack's `appName` + {@link OPENHI_TAG_SUFFIX_REPO_NAME}.\n */\nexport const OPENHI_REPO_TAG_KEY_ENV_VAR = \"OPENHI_REPO_TAG_KEY\";\n\n/**\n * Env var the bridge handler reads for the resolved `openhi:` tag prefix.\n * Resolved at synth time from the host stack's `appName`. Used to project\n * the relevant stack tags into the published envelope.\n */\nexport const OPENHI_TAG_KEY_PREFIX_ENV_VAR = \"OPENHI_TAG_KEY_PREFIX\";\n\n/** Free-form `actor.system` value per TR-016 § Decision points #1 (bootstrap-role pattern). */\nexport const PLATFORM_DEPLOY_BRIDGE_ACTOR_SYSTEM = \"platform-deploy-bridge\";\n\n/**\n * Subset of the CloudFormation Stack Status Change `detail` field the\n * bridge handler reads. AWS-side schema lives at\n * <https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/monitoring-cloudformation.html>.\n */\nexport interface CloudFormationStackStatusChangeDetail {\n readonly \"stack-id\": string;\n readonly \"logical-resource-id\"?: string;\n readonly \"physical-resource-id\"?: string;\n readonly \"status-details\": {\n readonly status: string;\n readonly \"status-reason\"?: string;\n };\n readonly \"resource-type\"?: string;\n readonly \"client-request-token\"?: string;\n}\n","import { IEventBus } from \"aws-cdk-lib/aws-events\";\nimport { Construct } from \"constructs\";\nimport { PlatformDeployBridgeLambda } from \"./platform-deploy-bridge-lambda\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/platform-deploy-bridge/index.md\n */\n\nexport interface PlatformDeployBridgeProps {\n /** Destination control event bus the bridge republishes onto. */\n readonly controlEventBus: IEventBus;\n}\n\n/**\n * Source-side reactor that watches CloudFormation Stack Status Change\n * events on the default AWS bus and republishes terminal-success events\n * (`CREATE_COMPLETE` / `UPDATE_COMPLETE`) for OpenHi-tagged stacks onto\n * the control event bus as `platform.deployment-completed.v1`.\n *\n * Implements row 4 of the workflow placement matrix\n * (codedrifters/openhi#953): ops-plane reactor → republishes to\n * control event bus.\n */\nexport class PlatformDeployBridge extends Construct {\n public readonly bridgeLambda: PlatformDeployBridgeLambda;\n\n constructor(scope: Construct, props: PlatformDeployBridgeProps) {\n super(scope, \"platform-deploy-bridge\");\n\n this.bridgeLambda = new PlatformDeployBridgeLambda(this, {\n controlEventBus: props.controlEventBus,\n });\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Duration, Stack } from \"aws-cdk-lib\";\nimport { IEventBus, Rule } from \"aws-cdk-lib/aws-events\";\nimport { LambdaFunction } from \"aws-cdk-lib/aws-events-targets\";\nimport { Effect, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\nimport {\n BRIDGED_STATUSES,\n CLOUDFORMATION_EVENT_SOURCE,\n CLOUDFORMATION_STACK_STATUS_CHANGE_DETAIL_TYPE,\n CONTROL_EVENT_BUS_NAME_ENV_VAR,\n OPENHI_REPO_TAG_KEY_ENV_VAR,\n OPENHI_TAG_KEY_PREFIX_ENV_VAR,\n} from \"./events\";\nimport {\n OPENHI_TAG_SUFFIX_REPO_NAME,\n OpenHiService,\n openHiTagKey,\n} from \"../../../app/open-hi-service\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/platform-deploy-bridge/index.md\n */\n\nconst HANDLER_NAME = \"platform-deploy-bridge.handler.js\";\n\n/**\n * Resolve handler entry so it works from src/ (tests) or lib/ (built).\n */\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n return path.join(dirname, \"..\", \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n}\n\nexport interface PlatformDeployBridgeLambdaProps {\n /** Destination control event bus the bridge republishes onto. */\n readonly controlEventBus: IEventBus;\n}\n\n/**\n * Lambda that bridges CloudFormation Stack Status Change events from the\n * default AWS bus into typed `platform.deployment-completed.v1` envelopes on\n * the OpenHI control event bus.\n *\n * Owns its EventBridge Rule (on the default AWS bus) and the IAM\n * permissions it needs — colocating routing + permissions with the\n * function they target.\n *\n * The EventBridge rule pre-filters by stack-id prefix so the rule (and\n * therefore the Lambda) only fires on the host stack's own branch deploys.\n * This prevents cross-branch leak when multiple branches are deployed into\n * the same account.\n */\nexport class PlatformDeployBridgeLambda extends Construct {\n public readonly lambda: NodejsFunction;\n public readonly rule: Rule;\n\n constructor(scope: Construct, props: PlatformDeployBridgeLambdaProps) {\n super(scope, \"platform-deploy-bridge-lambda\");\n\n // Resolve the host OpenHiService so we can pin the rule to this\n // branch's stacks and project the appName-aware tag key.\n const service = OpenHiService.of(this) as OpenHiService;\n const repoTagKey = openHiTagKey(\n service.appName,\n OPENHI_TAG_SUFFIX_REPO_NAME,\n );\n const tagKeyPrefix = `${service.appName}:`;\n\n // Derive the shared sibling-stack prefix from this stack's own\n // synthesized name. `service.branchHash` alone (e.g. `4e4512-`)\n // is wrong because CDK prepends the Stage hierarchy\n // (`<stage>-<environment>-…`) to every stack name, so the\n // CloudFormation events carry stack-ids like\n // `dev-primary-4e4512-data-…`. Stripping the known\n // `-<serviceId>-<account>-<region>` suffix off the bridge's own\n // stack name yields the prefix every sibling stack shares.\n const ownStackName = Stack.of(this).stackName;\n const ownSuffix = `-${service.serviceId}-${Stack.of(this).account}-${\n Stack.of(this).region\n }`;\n const sharedPrefix = ownStackName.endsWith(ownSuffix)\n ? ownStackName.slice(0, -ownSuffix.length)\n : service.branchHash;\n const stackIdPrefix = `arn:aws:cloudformation:${Stack.of(this).region}:${\n Stack.of(this).account\n }:stack/${sharedPrefix}-`;\n\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 256,\n timeout: Duration.seconds(30),\n environment: {\n [CONTROL_EVENT_BUS_NAME_ENV_VAR]: props.controlEventBus.eventBusName,\n [OPENHI_REPO_TAG_KEY_ENV_VAR]: repoTagKey,\n [OPENHI_TAG_KEY_PREFIX_ENV_VAR]: tagKeyPrefix,\n },\n });\n\n // Fetch stack tags so the handler can project them into the envelope.\n // Scope to stacks in the deploying account/region.\n this.lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\"cloudformation:DescribeStacks\"],\n resources: [\n `arn:aws:cloudformation:${Stack.of(this).region}:${\n Stack.of(this).account\n }:stack/*`,\n ],\n }),\n );\n\n // Republish the projected envelope onto the control event bus.\n props.controlEventBus.grantPutEventsTo(this.lambda);\n\n // Rule lives on the default AWS bus — that is where AWS publishes\n // its own CloudFormation Stack Status Change events. Omitting\n // `eventBus` defaults to the account's default bus.\n //\n // The `stack-id` prefix scopes the rule to this branch's own stacks\n // only (OpenHi stack names start with the deterministic per-branch\n // hash), so other branches' deploys never trigger this Lambda even\n // though every branch deploys its own bridge into the same account.\n this.rule = new Rule(this, \"rule\", {\n eventPattern: {\n source: [CLOUDFORMATION_EVENT_SOURCE],\n detailType: [CLOUDFORMATION_STACK_STATUS_CHANGE_DETAIL_TYPE],\n detail: {\n \"stack-id\": [{ prefix: stackIdPrefix }],\n \"status-details\": {\n status: [...BRIDGED_STATUSES],\n },\n },\n },\n targets: [\n new LambdaFunction(this.lambda, {\n retryAttempts: 2,\n maxEventAge: Duration.hours(2),\n }),\n ],\n });\n }\n}\n","import { PLATFORM_ROLE_CODE, type PlatformRoleCode } from \"@openhi/types\";\nimport { PlatformSystemDataSeededV1 } from \"@openhi/workflows\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-demo-data/events.md\n */\n\n/**\n * Stable logical name this workflow registers with the shared\n * `WorkflowDedupTable` (TR-015). Used in both the construct grant\n * (`workflowDedupTable.grantConsumer(lambda, SEED_DEMO_DATA_CONSUMER_NAME)`)\n * and the handler's runtime `recordIfAbsent` call — keep them aligned by\n * importing this constant in both places.\n */\nexport const SEED_DEMO_DATA_CONSUMER_NAME = \"seed-demo-data\";\n\n/** FHIR `Identifier.system` for the demo-scenario URN scheme. */\nexport const DEMO_URN_SYSTEM = \"urn:openhi:demo\";\n\n/**\n * FHIR `Identifier.system` for the canonical OpenHI resource URN\n * (ADR 2026-03-12-01). The value form is\n * `urn:ohi:<tenantId>:<workspaceId>:<resourceType>:<id>`; empty\n * workspaceId means the resource has tenant-wide scope (or is itself\n * the workspace identity, in the Workspace case).\n */\nexport const OPENHI_RESOURCE_URN_SYSTEM = \"http://openhi.org/\";\n\n/**\n * Period stamped on every demo Membership and RoleAssignment. Fixed\n * start so re-runs produce byte-identical bodies (decision #4); no\n * end so demo memberships are open-ended.\n */\nexport const DEMO_PERIOD = { start: \"2026-01-01T00:00:00Z\" } as const;\n\n/**\n * Sentinel `tenantId` used on every dev user's `system-admin`\n * RoleAssignment. A `system-admin` RA is platform-scoped (it spans\n * every tenant), but the RoleAssignment entity requires a tenantId on\n * its key for sharding — there is no real tenant to point at. The\n * `\"platform\"` literal is a reserved value that never matches a real\n * Tenant id and signals \"this RA scopes across all tenants\".\n *\n * Renaming this constant is a wire-format break — the IAM grant in\n * `seed-demo-data-lambda.ts` enumerates exact-match `LeadingKeys`\n * computed from this value, and the in-band records written under it\n * become unreachable if the sentinel changes.\n */\nexport const PLATFORM_SCOPE_TENANT_ID = \"platform\";\n\n/** Placeholder Tenant id seeded by the workflow as the dev-user `currentTenant`. */\nexport const PLACEHOLDER_TENANT_ID = \"placeholder-tenant-id\";\n\n/** Placeholder Workspace id seeded by the workflow as the dev-user `currentWorkspace`. */\nexport const PLACEHOLDER_WORKSPACE_ID = \"placeholder-workspace-id\";\n\n/**\n * Dev-user descriptor. Every entry produces:\n * - one Cognito user (idempotent create-then-skip),\n * - one DynamoDB User record (id = `dev-<email-local-part>`),\n * - four Memberships (placeholder + the three demo tenants),\n * - four `tenant-admin` RoleAssignments (one per tenant the user\n * belongs to),\n * - one `system-admin` RoleAssignment scoped to {@link PLATFORM_SCOPE_TENANT_ID}.\n */\nexport interface DemoDevUser {\n /** Stable DynamoDB User id. */\n readonly id: string;\n /** Email used as the Cognito `Username` and as the seed for the password algorithm. */\n readonly email: string;\n}\n\n/**\n * Hardcoded dev-user roster. Adding a new developer is a one-line\n * change here plus a re-deploy. The list intentionally lives in-source\n * (not behind an env-var seam) so the IAM grant in\n * `seed-demo-data-lambda.ts` can enumerate per-user PKs at synth time.\n */\nexport const DEV_USERS: ReadonlyArray<DemoDevUser> = [\n { id: \"dev-russell\", email: \"russell@codedrifters.com\" },\n { id: \"dev-cameron\", email: \"cameron@codedrifters.com\" },\n { id: \"dev-neelima\", email: \"neelima@codedrifters.com\" },\n { id: \"dev-garon\", email: \"garon@codedrifters.com\" },\n { id: \"dev-dave\", email: \"dave@codedrifters.com\" },\n { id: \"dev-drew\", email: \"drew@codedrifters.com\" },\n { id: \"dev-jessica\", email: \"jessica@codedrifters.com\" },\n { id: \"dev-jared\", email: \"jared@codedrifters.com\" },\n { id: \"dev-goddess\", email: \"goddess@codedrifters.com\" },\n];\n\n/**\n * A single workspace inside a demo tenant. The mixed tenant has two\n * workspaces (wound-care and primary-care sub-workspaces); the other\n * two tenants have one each.\n */\nexport interface DemoWorkspaceSpec {\n /** Stable id (DynamoDB record id; also drives the canonical OHI URN). */\n readonly id: string;\n /** FHIR `Workspace.name`. */\n readonly name: string;\n /**\n * Role suffix used in the demo URN value (`<scenario>:<roleSuffix>`).\n * Mirrors seed-fixtures' role suffix convention: `workspace` for\n * single-workspace tenants, `workspace-<sub>` for the mixed tenant.\n */\n readonly roleSuffix: string;\n}\n\n/**\n * One demo tenant + the workspaces it owns. Re-exported via {@link\n * DEMO_TENANT_SPECS} as the canonical list; iterate that constant in\n * the handler and the IAM-grant builder so the value sets agree.\n */\nexport interface DemoTenantSpec {\n /**\n * Scenario slug — `placeholder`, `demo-wound-care`, `demo-primary-care`,\n * `demo-mixed`. The placeholder tenant's slug is `placeholder`; the\n * three demo tenants mirror seed-fixtures' `fixture-*` slugs renamed\n * to `demo-*`.\n */\n readonly scenario: string;\n /** Stable id (DynamoDB record id; also drives the canonical OHI URN). */\n readonly tenantId: string;\n /** FHIR `Tenant.name`. */\n readonly tenantName: string;\n /** Workspaces owned by this tenant. */\n readonly workspaces: ReadonlyArray<DemoWorkspaceSpec>;\n}\n\n/**\n * The full demo-tenant graph. Four entries: the placeholder tenant the\n * JWT-claim fallback resolves to, plus the three v1 demo scenarios\n * (OPS-009 §\"v1 scenarios\"):\n *\n * 0. Placeholder tenant — dereferences the JWT-claim fallback in\n * `pre-token-generation.handler.ts` and acts as every dev user's\n * `currentTenant`/`currentWorkspace` so Post-Confirmation skips\n * default provisioning when a seeded User signs in.\n * 1. Single-workspace wound-care tenant.\n * 2. Single-workspace primary-care tenant.\n * 3. Two-workspace mixed tenant — exercises the cross-workspace\n * isolation flow that single-workspace tenants cannot.\n */\nexport const DEMO_TENANT_SPECS: ReadonlyArray<DemoTenantSpec> = [\n {\n scenario: \"placeholder\",\n tenantId: PLACEHOLDER_TENANT_ID,\n tenantName: \"OpenHI Placeholder Tenant\",\n workspaces: [\n {\n id: PLACEHOLDER_WORKSPACE_ID,\n name: \"OpenHI Placeholder Workspace\",\n roleSuffix: \"workspace\",\n },\n ],\n },\n {\n scenario: \"demo-wound-care\",\n tenantId: \"demo-wound-care-tenant\",\n tenantName: \"Cedarbrook Wound Healing Institute\",\n workspaces: [\n {\n id: \"demo-wound-care-workspace\",\n name: \"Cedarbrook Outpatient Wound Clinic\",\n roleSuffix: \"workspace\",\n },\n ],\n },\n {\n scenario: \"demo-primary-care\",\n tenantId: \"demo-primary-care-tenant\",\n tenantName: \"Maple Ridge Family Medicine\",\n workspaces: [\n {\n id: \"demo-primary-care-workspace\",\n name: \"Maple Ridge Main Street Office\",\n roleSuffix: \"workspace\",\n },\n ],\n },\n {\n scenario: \"demo-mixed\",\n tenantId: \"demo-mixed-tenant\",\n tenantName: \"Northbridge Health Network\",\n workspaces: [\n {\n id: \"demo-mixed-workspace-wound-care\",\n name: \"Northbridge Wound Care Center\",\n roleSuffix: \"workspace-wound-care\",\n },\n {\n id: \"demo-mixed-workspace-primary-care\",\n name: \"Northbridge Family Practice\",\n roleSuffix: \"workspace-primary-care\",\n },\n ],\n },\n];\n\n/** Stable Membership id derived from `(devUserId, tenantId)`. */\nexport const demoMembershipId = (devUserId: string, tenantId: string): string =>\n `demo-membership-${devUserId}-${tenantId}`;\n\n/**\n * Stable RoleAssignment id derived from `(devUserId, tenantId, roleCode)`.\n * Each (user, tenant, role) tuple maps to exactly one record — re-runs\n * upsert the same id.\n */\nexport const demoRoleAssignmentId = (\n devUserId: string,\n tenantId: string,\n roleCode: PlatformRoleCode,\n): string => `demo-roleassignment-${devUserId}-${tenantId}-${roleCode}`;\n\n/**\n * Demo-scenario FHIR `Identifier` entry — `urn:openhi:demo:<scenario>:<role>`.\n * Mirrors the `urn:openhi:fixture:<scenario>:<role>` pattern from\n * `@openhi/seed-fixtures/src/urn.ts`, renamed to the `demo` namespace.\n */\nexport const demoScenarioIdentifier = (\n scenario: string,\n roleSuffix: string,\n): { system: string; value: string } => ({\n system: DEMO_URN_SYSTEM,\n value: `${scenario}:${roleSuffix}`,\n});\n\n/**\n * Canonical OpenHI resource FHIR `Identifier` entry per ADR\n * 2026-03-12-01. `workspaceId` is empty for both Tenant resources and\n * Workspace resources — for a Tenant, the resource is tenant-scoped\n * with no workspace context; for a Workspace, the resource IS the\n * workspace identity rather than living inside one.\n */\nexport const openhiResourceIdentifier = (params: {\n tenantId: string;\n workspaceId: string;\n resourceType: string;\n id: string;\n}): { use: string; system: string; value: string } => ({\n use: \"unversioned\",\n system: OPENHI_RESOURCE_URN_SYSTEM,\n value: `urn:ohi:${params.tenantId}:${params.workspaceId}:${params.resourceType}:${params.id}`,\n});\n\n/**\n * Roles every dev user holds in every tenant they belong to. Per scope\n * decision Q3, every dev user is `tenant-admin` in every tenant — there\n * is no per-(user, tenant) variance to drive from.\n */\nexport const demoRolesForUserInTenant = (\n _user: DemoDevUser,\n _tenantId: string,\n): ReadonlyArray<PlatformRoleCode> => {\n void _user;\n void _tenantId;\n return [PLATFORM_ROLE_CODE.TENANT_ADMIN];\n};\n\n/**\n * DynamoDB single-table partition-key builders. The IAM grant in\n * `seed-demo-data-lambda.ts` uses these to enumerate exact-match\n * `dynamodb:LeadingKeys` values; the entity definitions in\n * `data/dynamo/entities/control/` own the canonical key templates.\n *\n * These builders MUST emit the keys ElectroDB actually writes — not\n * the entity definition's pretty template. None of the control-plane\n * entities sets `casing: \"none\"` on the base-table PK template, so\n * ElectroDB applies its default lowercase casing at runtime: the\n * entity's `ROLE#ID#${id}` becomes `role#id#<id>` on the wire. A\n * builder that returns the uppercase template form produces a\n * silently-broken IAM grant (every PutItem denied with \"no\n * identity-based policy allows\" because the request's leading-key\n * never matches a policy value).\n */\nexport const rolePartitionKey = (roleId: string): string => `role#id#${roleId}`;\n\nexport const demoTenantPartitionKey = (tenantId: string): string =>\n `tenant#id#${tenantId}`;\n\nexport const demoWorkspacePartitionKey = (\n tenantId: string,\n workspaceId: string,\n): string => `tid#${tenantId}#workspace#id#${workspaceId}`;\n\nexport const demoMembershipPartitionKey = (\n tenantId: string,\n membershipId: string,\n): string => `tid#${tenantId}#membership#id#${membershipId}`;\n\nexport const demoRoleAssignmentPartitionKey = (\n tenantId: string,\n roleAssignmentId: string,\n): string => `tid#${tenantId}#roleassignment#id#${roleAssignmentId}`;\n\n/** User entity PK template — `USER#ID#<id>` → `user#id#<id>` on the wire. */\nexport const demoUserPartitionKey = (userId: string): string =>\n `user#id#${userId}`;\n\n/**\n * Tenant + Workspace PKs the workflow writes on every fire: the 4\n * tenant PKs (placeholder + 3 demo) plus their workspaces (1 + 1 + 1 + 2 = 5).\n */\nexport const demoBasePartitionKeys = (): ReadonlyArray<string> => {\n const keys: string[] = [];\n for (const spec of DEMO_TENANT_SPECS) {\n keys.push(demoTenantPartitionKey(spec.tenantId));\n for (const workspace of spec.workspaces) {\n keys.push(demoWorkspacePartitionKey(spec.tenantId, workspace.id));\n }\n }\n return keys;\n};\n\n/**\n * Membership + RoleAssignment + User PKs the workflow writes per dev\n * user. Empty when `devUsers` is empty (used by tests). The list\n * mirrors the handler's iteration order so the IAM grant covers every\n * write the handler can make.\n *\n * Per dev user the function emits:\n * - one User PK,\n * - per tenant in {@link DEMO_TENANT_SPECS}: one Membership PK plus\n * one `tenant-admin` RoleAssignment PK,\n * - one platform-scoped `system-admin` RoleAssignment PK keyed by\n * {@link PLATFORM_SCOPE_TENANT_ID}.\n */\nexport const demoDevUserPartitionKeys = (\n devUsers: ReadonlyArray<DemoDevUser>,\n): ReadonlyArray<string> => {\n const keys: string[] = [];\n for (const user of devUsers) {\n keys.push(demoUserPartitionKey(user.id));\n for (const spec of DEMO_TENANT_SPECS) {\n keys.push(\n demoMembershipPartitionKey(\n spec.tenantId,\n demoMembershipId(user.id, spec.tenantId),\n ),\n );\n for (const roleCode of demoRolesForUserInTenant(user, spec.tenantId)) {\n keys.push(\n demoRoleAssignmentPartitionKey(\n spec.tenantId,\n demoRoleAssignmentId(user.id, spec.tenantId, roleCode),\n ),\n );\n }\n }\n // Platform-scoped system-admin RoleAssignment.\n keys.push(\n demoRoleAssignmentPartitionKey(\n PLATFORM_SCOPE_TENANT_ID,\n demoRoleAssignmentId(\n user.id,\n PLATFORM_SCOPE_TENANT_ID,\n PLATFORM_ROLE_CODE.SYSTEM_ADMIN,\n ),\n ),\n );\n }\n return keys;\n};\n\n/**\n * The trigger this workflow subscribes to on the control event bus.\n * The `seed-system-data` workflow publishes\n * `platform.system-data-seeded.v1` after it has finished re-asserting\n * the platform-singleton Role records; on every fire this workflow\n * re-asserts the demo-tenant graph.\n *\n * The indirection (subscribing to `system-data-seeded` rather than to\n * the raw `platform.deployment-completed.v1` event) enforces a strict\n * happens-before edge between the system-data seed and the demo-data\n * seed. Without it, both workflows would fire in parallel from the\n * same deploy event and seed-demo-data's pre-flight Role check would\n * race seed-system-data's puts — a race that EventBridge retries\n * resolve, but only after firing the failure alarm at least once on a\n * fresh environment's first deploy.\n *\n * Re-exported from `@openhi/workflows` for symmetry with the handler's\n * import — keep the construct + handler reading from the same registry\n * entry so a rename upstream surfaces at both call sites.\n */\nexport { PlatformSystemDataSeededV1 };\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { PLATFORM_ROLE_IDS } from \"@openhi/types\";\nimport { Duration, Stack } from \"aws-cdk-lib\";\nimport { IUserPool } from \"aws-cdk-lib/aws-cognito\";\nimport { ITable } from \"aws-cdk-lib/aws-dynamodb\";\nimport { IEventBus, Rule } from \"aws-cdk-lib/aws-events\";\nimport { LambdaFunction } from \"aws-cdk-lib/aws-events-targets\";\nimport { Effect, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\nimport {\n DEV_USERS,\n PlatformSystemDataSeededV1,\n demoBasePartitionKeys,\n demoDevUserPartitionKeys,\n rolePartitionKey,\n} from \"./events\";\nimport { SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR } from \"./seed-demo-data.handler\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-demo-data/seed-demo-data-lambda.md\n */\n\nconst HANDLER_NAME = \"seed-demo-data.handler.js\";\n\n/**\n * Resolve the bundled handler entry. Same dual-path lookup the\n * seed-system-data Lambda uses: src/ for tests (the file lives next\n * to this one) or lib/ for the compiled bundle.\n */\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n\n return path.join(dirname, \"..\", \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n}\n\nexport interface SeedDemoDataLambdaProps {\n /**\n * Data-store table the workflow upserts demo-data records into.\n * Wired via `DYNAMO_TABLE_NAME` env var; granted scoped read on the\n * Role PKs (pre-flight check) and scoped write on the enumerated\n * demo Tenant / Workspace / Membership / RoleAssignment / User PKs.\n */\n readonly dataStoreTable: ITable;\n\n /**\n * Control event bus that re-publishes\n * `platform.deployment-completed.v1` from the platform-deploy bridge.\n * The Rule mounts here.\n */\n readonly controlEventBus: IEventBus;\n\n /**\n * Cognito User Pool the workflow provisions dev users into. The\n * Lambda's IAM grant is scoped to this exact user-pool ARN — the\n * grant uses the user-pool ARN, **not** the wildcard formatArn\n * pattern used by `post-authentication-lambda` (that Lambda's\n * trigger-driven dependency cycle does not apply here, so the\n * tighter scope is safe).\n */\n readonly userPool: IUserPool;\n}\n\n/**\n * Lambda + EventBridge Rule pair for the seed-demo-data workflow.\n * Owns the routing (`source` / `detail-type` pattern), the scoped\n * DynamoDB grants, and the scoped Cognito Admin grant — co-locating\n * routing + permissions with the function they target. Wiring to the\n * workflow dedup table is the parent construct's job (it has the\n * singleton reference) and happens via `WorkflowDedupTable.grantConsumer`.\n *\n * Stage-gating is the parent's job too — this construct itself never\n * checks the stage. The CDK stage-router (`OpenHiDataService`)\n * decides whether to instantiate it at all on each stage.\n */\nexport class SeedDemoDataLambda extends Construct {\n public readonly lambda: NodejsFunction;\n public readonly rule: Rule;\n\n constructor(scope: Construct, props: SeedDemoDataLambdaProps) {\n super(scope, \"seed-demo-data-lambda\");\n\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 512,\n timeout: Duration.minutes(2),\n environment: {\n DYNAMO_TABLE_NAME: props.dataStoreTable.tableName,\n [SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR]: props.userPool.userPoolId,\n },\n });\n\n // Pre-flight Role check: read the three platform-singleton Role\n // PKs only. A regression that tried to read other records would\n // be rejected by IAM, not by application code.\n const roleReadKeys = Object.values(PLATFORM_ROLE_IDS).map(rolePartitionKey);\n this.lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\"dynamodb:GetItem\"],\n resources: [props.dataStoreTable.tableArn],\n conditions: {\n \"ForAllValues:StringEquals\": {\n \"dynamodb:LeadingKeys\": roleReadKeys,\n },\n },\n }),\n );\n\n // Write grant: the deterministic set of demo Tenant / Workspace /\n // Membership / RoleAssignment / User PKs the workflow writes.\n // {@link DEV_USERS} is a compile-time constant so the set is\n // fully enumerable at synth time.\n const writeKeys: string[] = [\n ...demoBasePartitionKeys(),\n ...demoDevUserPartitionKeys(DEV_USERS),\n ];\n this.lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\"dynamodb:PutItem\", \"dynamodb:UpdateItem\"],\n resources: [props.dataStoreTable.tableArn],\n conditions: {\n \"ForAllValues:StringEquals\": {\n \"dynamodb:LeadingKeys\": writeKeys,\n },\n },\n }),\n );\n\n // Cognito grant. The User Pool is imported by ARN from the auth\n // stack via SSM lookup, so reconstructing the ARN with\n // `Stack.of(this).formatArn` scopes the grant to this exact\n // account+region+pool without depending on the auth stack's\n // exported ARN.\n this.lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\n \"cognito-idp:AdminCreateUser\",\n \"cognito-idp:AdminGetUser\",\n \"cognito-idp:AdminSetUserPassword\",\n ],\n resources: [\n Stack.of(this).formatArn({\n service: \"cognito-idp\",\n resource: \"userpool\",\n resourceName: props.userPool.userPoolId,\n }),\n ],\n }),\n );\n\n this.rule = new Rule(this, \"rule\", {\n eventBus: props.controlEventBus,\n eventPattern: {\n source: [PlatformSystemDataSeededV1.source],\n detailType: [PlatformSystemDataSeededV1.detailType],\n },\n targets: [\n new LambdaFunction(this.lambda, {\n retryAttempts: 2,\n maxEventAge: Duration.hours(2),\n }),\n ],\n });\n }\n}\n","import { createHash } from \"node:crypto\";\nimport {\n AdminCreateUserCommand,\n AdminGetUserCommand,\n AdminSetUserPasswordCommand,\n CognitoIdentityProviderClient,\n UsernameExistsException,\n type AttributeType,\n} from \"@aws-sdk/client-cognito-identity-provider\";\nimport { DynamoDBClient } from \"@aws-sdk/client-dynamodb\";\nimport {\n PLATFORM_ROLE_CODE,\n PLATFORM_ROLE_CONCEPTS,\n PLATFORM_ROLE_IDS,\n extractSummary,\n type PlatformRoleCode,\n type FhirResourceLike,\n} from \"@openhi/types\";\nimport {\n parseWorkflowEvent,\n workflowDedupClient,\n type WorkflowDedupClient,\n} from \"@openhi/workflows\";\nimport type { EventBridgeEvent } from \"aws-lambda\";\nimport {\n DEMO_PERIOD,\n DEMO_TENANT_SPECS,\n DEV_USERS,\n PLACEHOLDER_TENANT_ID,\n PLACEHOLDER_WORKSPACE_ID,\n PLATFORM_SCOPE_TENANT_ID,\n PlatformSystemDataSeededV1,\n SEED_DEMO_DATA_CONSUMER_NAME,\n demoMembershipId,\n demoRoleAssignmentId,\n demoRolesForUserInTenant,\n demoScenarioIdentifier,\n openhiResourceIdentifier,\n type DemoDevUser,\n} from \"./events\";\nimport { getDynamoControlService } from \"../../../data/dynamo/dynamo-control-service\";\nimport { NotFoundError } from \"../../../data/errors\";\nimport type { OpenHiContext } from \"../../../data/openhi-context\";\nimport { createMembershipOperation } from \"../../../data/operations/control/membership/membership-create-operation\";\nimport { getRoleByIdOperation } from \"../../../data/operations/control/role/role-get-by-id-operation\";\nimport { createRoleAssignmentOperation } from \"../../../data/operations/control/roleassignment/roleassignment-create-operation\";\nimport { createTenantOperation } from \"../../../data/operations/control/tenant/tenant-create-operation\";\nimport { createWorkspaceOperation } from \"../../../data/operations/control/workspace/workspace-create-operation\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-demo-data/seed-demo-data-handler.md\n *\n * EventBridge workflow handler invoked once per platform-deploy event\n * on the control event bus. Pre-flight verifies that\n * `seed-system-data` has already seeded the canonical Role records,\n * then idempotently re-asserts the demo-data graph: the placeholder\n * Tenant + Workspace, the three demo tenants + their workspaces, and\n * for each entry in {@link DEV_USERS} a Cognito user, a DynamoDB User,\n * 4 Memberships, 4 `tenant-admin` RoleAssignments, plus 1\n * platform-scoped `system-admin` RoleAssignment.\n */\n\ntype SeedDemoDataEvent = EventBridgeEvent<\n \"platform.system-data-seeded.v1\",\n unknown\n>;\n\n/** Env var the lambda construct injects with the Cognito User Pool ID. */\nexport const SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR =\n \"SEED_DEMO_DATA_USER_POOL_ID\";\n\n/** Inputs the handler needs to provision Cognito users for every dev. */\nexport interface CognitoProvisioner {\n /**\n * Ensure a Cognito user exists for `email`. Returns the user's\n * `sub`. Implementations MUST be idempotent: a second invocation\n * for the same email returns the existing user's sub without\n * resetting its password or touching any attribute.\n */\n readonly ensureUser: (email: string) => Promise<string>;\n}\n\n/**\n * Dependency seam for tests. The factory mirrors the production\n * arrangement: a real DynamoDB client + the real `workflowDedupClient`\n * factory bound to the `OPENHI_WORKFLOW_DEDUP_TABLE_NAME` env var the\n * construct injects via `grantConsumer`.\n */\nexport interface SeedDemoDataDependencies {\n readonly dedupClient: WorkflowDedupClient;\n /**\n * Reads every id in `PLATFORM_ROLE_IDS`. Throws when any Role\n * record is missing — that means `seed-system-data` has not yet\n * run on this environment, and emitting demo RoleAssignments that\n * reference non-existent Roles would produce orphaned records.\n */\n readonly verifyRoles: () => Promise<void>;\n /**\n * Upserts every Tenant + Workspace in {@link DEMO_TENANT_SPECS},\n * plus per-dev-user Cognito users, DynamoDB User records,\n * Memberships, and RoleAssignments.\n */\n readonly seedDemoGraph: (params: {\n readonly baseContext: OpenHiContext;\n readonly devUsers: ReadonlyArray<DemoDevUser>;\n readonly cognito: CognitoProvisioner;\n }) => Promise<void>;\n /** Cognito provisioner threaded into `seedDemoGraph`. */\n readonly cognito: CognitoProvisioner;\n}\n\nconst errorMessage = (err: unknown): string => {\n if (err instanceof Error) {\n return err.message;\n }\n return String(err);\n};\n\n/**\n * Map a role code back to its canonical stable id from\n * `PLATFORM_ROLE_IDS`. Mirrors the lookup in\n * `seed-system-data.handler.ts` — the generator emits `_IDS` and\n * `_CONCEPTS` in lockstep, so a single SCREAMING_SNAKE key resolves\n * both projections.\n */\nconst idForRoleCode = (code: PlatformRoleCode): string => {\n for (const key of Object.keys(PLATFORM_ROLE_IDS) as Array<\n keyof typeof PLATFORM_ROLE_IDS\n >) {\n if (PLATFORM_ROLE_CONCEPTS[key].code === code) {\n return PLATFORM_ROLE_IDS[key];\n }\n }\n throw new Error(`No id mapping for role code \"${code}\".`);\n};\n\n/**\n * Pre-flight Role check. Iterates every id in\n * `PLATFORM_ROLE_IDS` and reads the record. If any read returns\n * not-found, this throws and `markFailed`'s the dedup row — the\n * replay tooling can retry once `seed-system-data` has populated the\n * Roles.\n */\nconst verifySystemRolesExist = async (): Promise<void> => {\n const probeContext: OpenHiContext = {\n tenantId: \"\",\n workspaceId: \"\",\n date: new Date(0).toISOString(),\n actorId: \"platform-deploy-bridge\",\n actorName: \"Platform Deploy Bridge\",\n actorType: \"internal-system\",\n source: \"step-function\",\n };\n for (const id of Object.values(PLATFORM_ROLE_IDS)) {\n try {\n await getRoleByIdOperation({ context: probeContext, id });\n } catch (err) {\n if (err instanceof NotFoundError) {\n throw new Error(\n `seed-demo-data pre-flight: control-plane Role \"${id}\" is missing. ` +\n \"Ensure seed-system-data has run on this environment before retrying.\",\n );\n }\n throw err;\n }\n }\n};\n\n/**\n * Build the resource body for one demo Tenant. Carries the two\n * identifiers per scope decision #6: the demo-scenario URN and the\n * canonical OpenHI resource URN (ADR 2026-03-12-01).\n */\nconst tenantResourceBody = (\n spec: (typeof DEMO_TENANT_SPECS)[number],\n): Record<string, unknown> => ({\n name: spec.tenantName,\n active: true,\n identifier: [\n demoScenarioIdentifier(spec.scenario, \"tenant\"),\n openhiResourceIdentifier({\n tenantId: spec.tenantId,\n workspaceId: \"\",\n resourceType: \"Tenant\",\n id: spec.tenantId,\n }),\n ],\n});\n\nconst workspaceResourceBody = (\n spec: (typeof DEMO_TENANT_SPECS)[number],\n workspace: (typeof DEMO_TENANT_SPECS)[number][\"workspaces\"][number],\n): Record<string, unknown> => ({\n name: workspace.name,\n active: true,\n identifier: [\n demoScenarioIdentifier(spec.scenario, workspace.roleSuffix),\n openhiResourceIdentifier({\n tenantId: spec.tenantId,\n workspaceId: \"\",\n resourceType: \"Workspace\",\n id: workspace.id,\n }),\n ],\n tenant: { reference: `Tenant/${spec.tenantId}`, type: \"Tenant\" },\n});\n\nconst membershipResourceBody = (\n spec: (typeof DEMO_TENANT_SPECS)[number],\n user: DemoDevUser,\n membershipId: string,\n): Record<string, unknown> => ({\n identifier: [\n demoScenarioIdentifier(spec.scenario, `membership-${user.id}`),\n openhiResourceIdentifier({\n tenantId: spec.tenantId,\n workspaceId: \"\",\n resourceType: \"Membership\",\n id: membershipId,\n }),\n ],\n user: { reference: `User/${user.id}`, type: \"User\" },\n tenant: { reference: `Tenant/${spec.tenantId}`, type: \"Tenant\" },\n period: DEMO_PERIOD,\n});\n\nconst roleAssignmentResourceBody = (\n scenario: string,\n tenantId: string,\n user: DemoDevUser,\n roleCode: PlatformRoleCode,\n roleAssignmentId: string,\n): Record<string, unknown> => ({\n identifier: [\n demoScenarioIdentifier(scenario, `roleassignment-${user.id}-${roleCode}`),\n openhiResourceIdentifier({\n tenantId,\n workspaceId: \"\",\n resourceType: \"RoleAssignment\",\n id: roleAssignmentId,\n }),\n ],\n user: { reference: `User/${user.id}`, type: \"User\" },\n role: { reference: `Role/${idForRoleCode(roleCode)}`, type: \"Role\" },\n tenant: { reference: `Tenant/${tenantId}`, type: \"Tenant\" },\n period: DEMO_PERIOD,\n});\n\n/** Build the FHIR User resource body for a seeded dev user. */\nconst userResourceBody = (\n user: DemoDevUser,\n cognitoSub: string,\n): Record<string, unknown> => ({\n resourceType: \"User\",\n id: user.id,\n name: [{ text: user.email }],\n status: \"active\",\n cognitoSub,\n currentTenant: { reference: `Tenant/${PLACEHOLDER_TENANT_ID}` },\n currentWorkspace: { reference: `Workspace/${PLACEHOLDER_WORKSPACE_ID}` },\n});\n\n/**\n * Idempotent User upsert. The User entity is non-tenant-isolated, so\n * the put is unconditional — the entity's PK is `USER#ID#<id>` and\n * supplying the same id re-asserts the same row.\n */\nconst upsertUser = async (\n context: OpenHiContext,\n user: DemoDevUser,\n cognitoSub: string,\n): Promise<void> => {\n const service = getDynamoControlService();\n const resource = userResourceBody(user, cognitoSub);\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n await service.entities.user\n .put({\n id: user.id,\n cognitoSub,\n resource: JSON.stringify(resource),\n summary,\n vid: \"1\",\n lastUpdated: context.date ?? new Date().toISOString(),\n })\n .go();\n};\n\n/**\n * Upsert the full demo-data graph. Walks each spec in\n * {@link DEMO_TENANT_SPECS}: Tenant → Workspaces. Then per dev user:\n * Cognito user → DynamoDB User → per-tenant Membership +\n * `tenant-admin` RoleAssignment → platform-scoped `system-admin`\n * RoleAssignment. Every put is keyed by a deterministic stable id so\n * re-runs after dedup-TTL expiry upsert the same records.\n *\n * Exported so the seeder test file can exercise it directly against\n * a mocked DynamoControlService; the production handler reaches it\n * through {@link SeedDemoDataDependencies.seedDemoGraph}.\n */\nexport const seedDemoGraph = async (params: {\n baseContext: OpenHiContext;\n devUsers: ReadonlyArray<DemoDevUser>;\n cognito: CognitoProvisioner;\n}): Promise<void> => {\n const { baseContext, devUsers, cognito } = params;\n\n // Phase 1: Tenants + Workspaces (no Memberships / RoleAssignments\n // yet — those need the per-user Cognito sub).\n for (const spec of DEMO_TENANT_SPECS) {\n const tenantContext: OpenHiContext = {\n ...baseContext,\n tenantId: spec.tenantId,\n };\n\n await createTenantOperation({\n context: tenantContext,\n body: { id: spec.tenantId, resource: tenantResourceBody(spec) },\n });\n\n for (const workspace of spec.workspaces) {\n await createWorkspaceOperation({\n context: tenantContext,\n body: {\n id: workspace.id,\n resource: workspaceResourceBody(spec, workspace),\n },\n });\n }\n }\n\n // Phase 2: per-dev-user records. The Cognito provisioner is the\n // only step that can fail in a way EventBridge can usefully retry —\n // it runs first so a failed Cognito call short-circuits before any\n // DynamoDB writes for that user.\n for (const user of devUsers) {\n const cognitoSub = await cognito.ensureUser(user.email);\n\n await upsertUser(baseContext, user, cognitoSub);\n\n for (const spec of DEMO_TENANT_SPECS) {\n const tenantContext: OpenHiContext = {\n ...baseContext,\n tenantId: spec.tenantId,\n };\n\n const membershipId = demoMembershipId(user.id, spec.tenantId);\n await createMembershipOperation({\n context: tenantContext,\n body: {\n id: membershipId,\n resource: membershipResourceBody(spec, user, membershipId),\n },\n });\n\n for (const roleCode of demoRolesForUserInTenant(user, spec.tenantId)) {\n const raId = demoRoleAssignmentId(user.id, spec.tenantId, roleCode);\n await createRoleAssignmentOperation({\n context: tenantContext,\n body: {\n id: raId,\n resource: roleAssignmentResourceBody(\n spec.scenario,\n spec.tenantId,\n user,\n roleCode,\n raId,\n ),\n },\n });\n }\n }\n\n // Platform-scoped system-admin RoleAssignment. The sentinel\n // tenantId is PLATFORM_SCOPE_TENANT_ID — there is no real Tenant\n // to point at, so the operation runs with an OpenHiContext whose\n // tenantId is the sentinel.\n const platformContext: OpenHiContext = {\n ...baseContext,\n tenantId: PLATFORM_SCOPE_TENANT_ID,\n };\n const platformRoleCode = PLATFORM_ROLE_CODE.SYSTEM_ADMIN;\n const platformRaId = demoRoleAssignmentId(\n user.id,\n PLATFORM_SCOPE_TENANT_ID,\n platformRoleCode,\n );\n await createRoleAssignmentOperation({\n context: platformContext,\n body: {\n id: platformRaId,\n resource: roleAssignmentResourceBody(\n \"platform\",\n PLATFORM_SCOPE_TENANT_ID,\n user,\n platformRoleCode,\n platformRaId,\n ),\n },\n });\n }\n};\n\n/**\n * Test-visible orchestrator. The production `handler` calls this with\n * real dependencies and the hardcoded {@link DEV_USERS} list; unit\n * tests inject fakes and pass the dev-user list directly.\n */\nexport const runSeedDemoData = async (\n event: SeedDemoDataEvent,\n deps: SeedDemoDataDependencies,\n devUsers: ReadonlyArray<DemoDevUser>,\n): Promise<void> => {\n const parsed = parseWorkflowEvent(event, PlatformSystemDataSeededV1);\n\n const recordResult = await deps.dedupClient.recordIfAbsent({\n consumerName: SEED_DEMO_DATA_CONSUMER_NAME,\n eventId: parsed.dedupKey.eventId,\n attempt: parsed.dedupKey.attempt,\n });\n if (!recordResult.recorded) {\n return;\n }\n\n const baseContext: OpenHiContext = {\n tenantId: \"\",\n workspaceId: \"\",\n date: parsed.envelope.occurredAt,\n actorId: \"platform-deploy-bridge\",\n actorName: \"Platform Deploy Bridge\",\n actorType: \"internal-system\",\n source: \"step-function\",\n };\n\n try {\n await deps.verifyRoles();\n await deps.seedDemoGraph({\n baseContext,\n devUsers,\n cognito: deps.cognito,\n });\n } catch (err) {\n // Mark the dedup row failed so the replay tooling (TR-016 follow-up)\n // can re-publish the originating event with a fresh attempt. The\n // re-throw keeps EventBridge's failure-detection path intact — the\n // event ends up in the DLQ + the workflow Lambda's failure\n // CloudWatch alarm fires.\n await deps.dedupClient.markFailed({\n consumerName: SEED_DEMO_DATA_CONSUMER_NAME,\n eventId: parsed.dedupKey.eventId,\n attempt: parsed.dedupKey.attempt,\n reason: errorMessage(err),\n });\n throw err;\n }\n};\n\n/**\n * Deterministic password derived from the user's email. Re-running\n * the algorithm with the same email reproduces the password, so devs\n * can recover their own credentials from the docs page without the\n * workflow ever surfacing them. The shape satisfies the default\n * Cognito password policy (≥8 chars, upper + lower + number + symbol).\n */\nexport const devPasswordForEmail = (email: string): string => {\n const digest = createHash(\"sha256\").update(email).digest();\n const base64url = digest\n .toString(\"base64\")\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/g, \"\");\n return `Dev-${base64url.slice(0, 20)}!1`;\n};\n\n/**\n * Production Cognito provisioner backed by the AWS SDK. Reads the\n * user-pool id from the env var the lambda construct injects.\n *\n * Idempotency contract:\n * - On first invocation, calls `AdminCreateUser` (with\n * `MessageAction: SUPPRESS` so no invitation email fires) then\n * `AdminSetUserPassword` (permanent). Returns the new user's sub.\n * - On subsequent invocations, `AdminCreateUser` throws\n * `UsernameExistsException`; the provisioner catches it, calls\n * `AdminGetUser` to read the existing user's sub, and returns\n * **without** touching the password or any attribute.\n */\nexport const productionCognitoProvisioner = (): CognitoProvisioner => {\n const client = new CognitoIdentityProviderClient({});\n const userPoolId = process.env[SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR];\n if (!userPoolId || userPoolId.trim() === \"\") {\n throw new Error(\n `${SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR} is not set; the seed-demo-data Lambda ` +\n \"cannot provision Cognito users without a user-pool id.\",\n );\n }\n const subFromAttributes = (\n attrs: ReadonlyArray<AttributeType> | undefined,\n ): string | undefined => {\n for (const attr of attrs ?? []) {\n if (attr.Name === \"sub\" && typeof attr.Value === \"string\") {\n return attr.Value;\n }\n }\n return undefined;\n };\n return {\n ensureUser: async (email: string): Promise<string> => {\n try {\n const created = await client.send(\n new AdminCreateUserCommand({\n UserPoolId: userPoolId,\n Username: email,\n MessageAction: \"SUPPRESS\",\n UserAttributes: [\n { Name: \"email\", Value: email },\n { Name: \"email_verified\", Value: \"true\" },\n ],\n }),\n );\n await client.send(\n new AdminSetUserPasswordCommand({\n UserPoolId: userPoolId,\n Username: email,\n Password: devPasswordForEmail(email),\n Permanent: true,\n }),\n );\n const sub = subFromAttributes(created.User?.Attributes);\n if (!sub) {\n throw new Error(\n `AdminCreateUser response for \"${email}\" did not carry a sub attribute.`,\n );\n }\n return sub;\n } catch (err) {\n if (err instanceof UsernameExistsException) {\n // User already exists — skip password reset entirely and\n // just read the existing sub.\n const got = await client.send(\n new AdminGetUserCommand({\n UserPoolId: userPoolId,\n Username: email,\n }),\n );\n const sub = subFromAttributes(got.UserAttributes);\n if (!sub) {\n throw new Error(\n `AdminGetUser response for \"${email}\" did not carry a sub attribute.`,\n );\n }\n return sub;\n }\n throw err;\n }\n },\n };\n};\n\nconst productionDependencies = (): SeedDemoDataDependencies => {\n const dynamodb = new DynamoDBClient({});\n const cognito = productionCognitoProvisioner();\n return {\n dedupClient: workflowDedupClient(dynamodb),\n verifyRoles: verifySystemRolesExist,\n seedDemoGraph,\n cognito,\n };\n};\n\nexport const handler = async (event: SeedDemoDataEvent): Promise<void> =>\n runSeedDemoData(event, productionDependencies(), DEV_USERS);\n","import { Service } from \"electrodb\";\nimport { defaultTableName, dynamoClient } from \"./dynamo-client\";\nimport { ConfigurationEntity } from \"./entities/control/configuration-entity\";\nimport { MembershipEntity } from \"./entities/control/membership-entity\";\nimport { RoleEntity } from \"./entities/control/role-entity\";\nimport { RoleAssignmentEntity } from \"./entities/control/roleassignment-entity\";\nimport { TenantEntity } from \"./entities/control/tenant-entity\";\nimport { UserEntity } from \"./entities/control/user-entity\";\nimport { WorkspaceEntity } from \"./entities/control/workspace-entity\";\n\n/**\n * Control-plane entities only (service \"control\"). Same table as data plane; use\n * DynamoDataService for data-plane entities.\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n */\n\nconst controlPlaneEntities = {\n configuration: ConfigurationEntity,\n membership: MembershipEntity,\n role: RoleEntity,\n roleAssignment: RoleAssignmentEntity,\n tenant: TenantEntity,\n user: UserEntity,\n workspace: WorkspaceEntity,\n};\n\nconst controlPlaneService = new Service(controlPlaneEntities, {\n table: defaultTableName,\n client: dynamoClient,\n});\n\n/**\n * Control-plane service: entities for configuration and control. Use with the\n * data store table (PK, SK, GSI1; UserEntity also uses GSI2).\n */\nexport const DynamoControlService = {\n entities: controlPlaneService.entities,\n};\n\nexport type DynamoControlServiceType = typeof DynamoControlService;\n\n/**\n * Returns the control-plane service. Table name is resolved from tableName (optional override),\n * then DYNAMO_TABLE_NAME, then \"jesttesttable\".\n */\nexport function getDynamoControlService(\n tableName?: string,\n): DynamoControlServiceType {\n const resolved = tableName ?? defaultTableName;\n const service = new Service(controlPlaneEntities, {\n table: resolved,\n client: dynamoClient,\n });\n return {\n entities: service.entities,\n };\n}\n","import { DynamoDBClient } from \"@aws-sdk/client-dynamodb\";\n\n/**\n * DynamoDB table name for the data store. Set via DYNAMO_TABLE_NAME at runtime\n * (e.g. from Lambda env); defaults for local/test.\n */\nexport const defaultTableName =\n process.env.DYNAMO_TABLE_NAME ?? \"jesttesttable\";\n\n/**\n * DynamoDB client. When MOCK_DYNAMODB_ENDPOINT is set (e.g. local DynamoDB or\n * jest-dynalite), uses that endpoint with no SSL and region \"local\".\n */\nexport const dynamoClient = new DynamoDBClient({\n ...(process.env.MOCK_DYNAMODB_ENDPOINT && {\n endpoint: process.env.MOCK_DYNAMODB_ENDPOINT,\n sslEnabled: false,\n region: \"local\",\n }),\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute } from \"./control-entity-common\";\n\n/**\n * Configuration data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Partially tenant-isolated, control plane. Cascade of scope\n * levels: resolution order user → workspace → tenant → baseline. Sentinels: tenantId \"BASELINE\" for\n * baseline tier; workspaceId/userId/roleId \"-\" for absent dimension.\n *\n * Key structure: PK = CONFIG#TID#<tenantId>#WID#<workspaceId>#UID#<userId>#RID#<roleId>,\n * SK = KEY#<key>#SK#<sk>. Uniqueness: one Configuration per (tenantId, workspaceId, userId, roleId, key).\n * Standard attributes and key-building conventions align with single-table design.\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Configuration entries in a tenant/workspace\n * across the four shards.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entities/configuration.md\n * @see sites/www-docs/content/architecture/control-plane/configuration.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Key-building conventions (keys built inside entity)\n */\nexport const ConfigurationEntity = new Entity({\n model: {\n entity: \"configuration\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key. \"CURRENT\" for current version; version history in S3. */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** Tenant scope. Use \"BASELINE\" when the config is baseline default (no tenant). */\n tenantId: {\n type: \"string\" as const,\n required: true,\n default: \"BASELINE\",\n },\n /** Workspace scope. Use \"-\" when absent. */\n workspaceId: {\n type: \"string\" as const,\n required: true,\n default: \"-\",\n },\n /** User scope. Use \"-\" when absent. */\n userId: {\n type: \"string\" as const,\n required: true,\n default: \"-\",\n },\n /** Role scope. Use \"-\" when absent. */\n roleId: {\n type: \"string\" as const,\n required: true,\n default: \"-\",\n },\n /** Config type (category), e.g. endpoints, branding, display. */\n key: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR Resource.id; logical id in URL and for the Configuration resource. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Payload as JSON string. JSON.stringify(resource) on write; JSON.parse(item.resource) on read. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, key, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). Tracks current version; S3 history key. */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** 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. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"tenantId\", \"workspaceId\", \"userId\", \"roleId\"],\n template:\n \"CONFIG#TID#${tenantId}#WID#${workspaceId}#UID#${userId}#RID#${roleId}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"key\", \"sk\"],\n template: \"KEY#${key}#SK#${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Configuration entries for a\n * (tenant, workspace) across the four shards. Use for \"list configs scoped to this tenant\"\n * (workspaceId = \"-\") or \"list configs scoped to this workspace\". Does not support\n * hierarchical resolution in one query; use base table GetItem in fallback order\n * (user → workspace → tenant → baseline) for that.\n * SK is `<key>#<id>` — Configuration's `key` is a required entity attribute (the\n * config category: endpoints, branding, display, …) and the natural sort/lookup\n * dimension. `casing: \"none\"` preserves the literal key value.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"tenantId\", \"workspaceId\", \"gsi1Shard\"],\n template:\n \"TID#${tenantId}#WID#${workspaceId}#RT#Configuration#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"key\", \"id\"],\n template: \"${key}#${id}\",\n },\n },\n },\n});\n","import { extractLabel } from \"@openhi/types\";\nimport { computeShard } from \"../../shard\";\n\n/**\n * Shared GSI1 shard attribute for control-plane entities.\n *\n * Control-plane entities (User, Tenant, Workspace, Membership, Role, RoleAssignment,\n * Configuration) use the same `TID#/WID#/RT#/SHARD#` PK shape on GSI1 as data-plane\n * FHIR resources per ADR-011. The shard index is derived deterministically from `id`\n * via `computeShard` so updates always land on the same shard. Stored as a string\n * because it appears as a literal segment in the GSI1 PK template; the underlying\n * value is 0..3.\n *\n * Not `required` because the value is derived via `watch`/`set`; ElectroDB's\n * required-field check runs before watch propagation, so callers must not fail\n * validation on a derived field.\n */\nexport const gsi1ShardAttribute = {\n type: \"string\" as const,\n watch: [\"id\"] as const,\n set: (_val?: string, item?: { id?: string }) => {\n if (typeof item?.id !== \"string\" || item.id.length === 0) {\n return undefined;\n }\n return String(computeShard(item.id));\n },\n};\n\n/**\n * Shared GSI1 sort-key attribute for control-plane entities.\n *\n * Derives the GSI1SK value at write time from the entity's `resource` JSON\n * string, applying the same label-extraction strategy as the data plane\n * (DR-004 / `@openhi/types` `extractLabel`). When the resource carries a\n * natural label (Tenant.name, Workspace.name, Configuration.key, …) the\n * sort key is `<normalizedLabel>#<id>` so list endpoints sort alphabetically\n * and `BEGINS_WITH` queries serve prefix searches. When no label is\n * extractable, falls back to `<entity.lastUpdated>#<id>` for stable\n * reverse-chronological ordering.\n *\n * Falls back gracefully on malformed `resource` payloads — JSON parse\n * failures and missing fields both route to the lastUpdated fallback so a\n * single bad write never blocks an entity put. The entity-level\n * `lastUpdated` is preferred over `resource.meta.lastUpdated` so the\n * fallback uses the authoritative timestamp the entity write supplies.\n *\n * Not `required` because the value is derived via `watch`/`set`.\n */\nexport const gsi1skAttribute = {\n type: \"string\" as const,\n watch: [\"resource\", \"lastUpdated\", \"id\"] as const,\n set: (\n _val?: string,\n item?: { resource?: string; lastUpdated?: string; id?: string },\n ) => {\n const id = typeof item?.id === \"string\" ? item.id : \"\";\n const lastUpdated =\n typeof item?.lastUpdated === \"string\" ? item.lastUpdated : \"\";\n const fallback = `${lastUpdated}#${id}`;\n\n if (typeof item?.resource !== \"string\" || item.resource.length === 0) {\n return fallback;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(item.resource);\n } catch {\n return fallback;\n }\n if (!parsed || typeof parsed !== \"object\") return fallback;\n const resourceType = (parsed as { resourceType?: unknown }).resourceType;\n if (typeof resourceType !== \"string\") return fallback;\n\n const label = extractLabel(parsed as Parameters<typeof extractLabel>[0]);\n return label !== undefined ? `${label}#${id}` : fallback;\n },\n};\n","/**\n * Shard selection for the data-plane single-table GSI1 partitioning per ADR-011.\n *\n * GSI1's partition key embeds a `SHARD#<n>` segment with `n = computeShard(id)`.\n * The hash is deterministic so updates to the same resource id always land on\n * the same shard (no cross-shard migration on update); reads fan out to all\n * shards in parallel and merge by SK.\n *\n * @see sites/www-docs/content/architecture/adr/ — ADR-011 (single-table DynamoDB)\n */\n\n/** Number of shards in the GSI1 partition key. Fixed at 4 in v1; raising it later is a backfill, not a schema migration. */\nexport const SHARD_COUNT = 4;\n\n/**\n * Returns a deterministic shard index in [0, SHARD_COUNT) for the given resource id.\n *\n * Implementation: 32-bit FNV-1a over the UTF-16 code units of the id, modulo SHARD_COUNT.\n * The function is pure and stable; the same id always returns the same shard.\n *\n * ESLint's `no-bitwise` rule is disabled inside this function because FNV-1a is\n * defined in terms of XOR and unsigned-right-shift — the bitwise ops are the\n * algorithm, not an accidental logical-operator confusion.\n */\nexport function computeShard(id: string): number {\n /* eslint-disable no-bitwise */\n let hash = 0x811c9dc5;\n for (let i = 0; i < id.length; i++) {\n hash ^= id.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0) % SHARD_COUNT;\n /* eslint-enable no-bitwise */\n}\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * Membership data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, control plane. Membership links a User\n * to a Tenant (and optionally a Workspace). One record per (tenantId, id).\n *\n * Key structure: PK = TID#<tenantId>#MEMBERSHIP#ID#<id>, SK = CURRENT.\n * Uniqueness: one Membership per (tenantId, id).\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Memberships in a tenant across the four\n * shards. Membership is tenant-scoped (not workspace-scoped), so the GSI1 PK uses `WID#-` as a\n * sentinel.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-13-02-control-plane-roles-and-user-tenant-workspace-linkage.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MembershipEntity = new Entity({\n model: {\n entity: \"membership\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** Tenant in which the user has membership (required). */\n tenantId: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR Resource.id; membership id. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full Membership resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = TID#<tenantId>#MEMBERSHIP#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"tenantId\", \"id\"],\n template: \"TID#${tenantId}#MEMBERSHIP#ID#${id}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Memberships for a tenant across the\n * four shards. Membership is tenant-scoped only, so `WID#-` is a sentinel.\n * SK is derived via `gsi1skAttribute` — uses the resource's natural label when\n * extractable, else `<lastUpdated>#<id>` (DR-004). `casing: \"none\"` preserves the\n * normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"tenantId\", \"gsi1Shard\"],\n template: \"TID#${tenantId}#WID#-#RT#Membership#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n },\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * Role data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Non-tenant-isolated, control plane. Role is a\n * platform-wide role catalog (e.g. tenant-admin, tenant-user, system-admin); not scoped by tenant.\n * RoleAssignment references Role to assign a role to a User in a Tenant/Workspace context.\n *\n * Key structure: PK = ROLE#ID#<id>, SK = CURRENT.\n * The ROLE# prefix prevents key collisions with other non-tenant-isolated entities (User, etc.)\n * sharing the same table (ADR 2026-03-11-01 — preferred pattern for all control plane entities).\n * Uniqueness: one Role per id.\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Roles across the four shards. Non-tenant-\n * isolated, so the PK uses `TID#-#WID#-` sentinels.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-13-02-control-plane-roles-and-user-tenant-workspace-linkage.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const RoleEntity = new Entity({\n model: {\n entity: \"role\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** FHIR Resource.id; role id. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full Role resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = ROLE#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"id\"],\n template: \"ROLE#ID#${id}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Roles across the four shards.\n * Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#Role#SHARD#<n>`.\n * SK is derived via `gsi1skAttribute` — uses the resource's natural label when\n * extractable, else `<lastUpdated>#<id>` (DR-004). `casing: \"none\"` preserves the\n * normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"gsi1Shard\"],\n template: \"TID#-#WID#-#RT#Role#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n },\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * RoleAssignment data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, control plane. RoleAssignment assigns\n * a Role to a User in a Tenant (and optionally Workspace) context.\n *\n * Key structure: PK = TID#<tenantId>#ROLEASSIGNMENT#ID#<id>, SK = CURRENT.\n * Uniqueness: one RoleAssignment per (tenantId, id).\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all RoleAssignments in a tenant across the four\n * shards. Tenant-scoped only (workspace context lives inside the resource), so the GSI1 PK uses\n * `WID#-` as a sentinel.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-13-02-control-plane-roles-and-user-tenant-workspace-linkage.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const RoleAssignmentEntity = new Entity({\n model: {\n entity: \"roleassignment\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** Tenant in which the role assignment applies (required). */\n tenantId: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR Resource.id; role assignment id. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full RoleAssignment resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = TID#<tenantId>#ROLEASSIGNMENT#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"tenantId\", \"id\"],\n template: \"TID#${tenantId}#ROLEASSIGNMENT#ID#${id}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all RoleAssignments for a tenant across the\n * four shards. Tenant-scoped only, so `WID#-` is a sentinel.\n * SK is derived via `gsi1skAttribute` — uses the resource's natural label when\n * extractable, else `<lastUpdated>#<id>` (DR-004). `casing: \"none\"` preserves the\n * normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"tenantId\", \"gsi1Shard\"],\n template: \"TID#${tenantId}#WID#-#RT#RoleAssignment#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n },\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * Tenant data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, control plane. Tenant IS the top scope;\n * the workspace dimension is not applicable and uses the sentinel `TENANT`. The tenant's own `id`\n * is stored as `tenantId` to drive the partition key.\n *\n * Key structure: PK = TENANT#ID#<tenantId>, SK = CURRENT.\n * Uniqueness: one Tenant per tenantId (id).\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Tenants across the four shards. Tenant has\n * no parent tenant or workspace, so the PK uses `TID#-#WID#-` sentinels.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-13-01-tenant-and-workspace-fhir-types-control-plane.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const TenantEntity = new Entity({\n model: {\n entity: \"tenant\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** The tenant's own id (= resource id). Drives the partition key. */\n tenantId: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR Resource.id; logical id in URL. Equals tenantId. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full Tenant resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = TENANT#ID#<tenantId>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"tenantId\"],\n template: \"TENANT#ID#${tenantId}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Tenants across the four shards.\n * Tenant lives at the platform tier (no parent tenant or workspace), so `TID#-#WID#-`\n * sentinels precede `RT#Tenant#SHARD#<n>`. SK is derived via `gsi1skAttribute` —\n * `<normalizedName>#<id>` when the resource carries a `name`, else `<lastUpdated>#<id>`\n * (DR-004). `casing: \"none\"` preserves the normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"gsi1Shard\"],\n template: \"TID#-#WID#-#RT#Tenant#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n },\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * User data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Non-tenant-isolated, control plane. User is a\n * platform-wide identity; association with tenants and workspaces is through Membership and\n * RoleAssignment, not the User entity's own key.\n *\n * Key structure: PK = USER#ID#<id>, SK = CURRENT.\n * The USER# prefix prevents key collisions with other non-tenant-isolated entities (Role, etc.)\n * sharing the same table (ADR 2026-03-11-01 — preferred pattern for all control plane entities).\n * Uniqueness: one User per id.\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Users across the four shards. Non-tenant-\n * isolated, so the PK uses `TID#-#WID#-` sentinels.\n * GSI2 — Cognito sub-lookup per ADR-011: resolves a UserEntity from a Cognito `sub` claim\n * (`USER#SUB#<cognitoSub>` PK, `CURRENT` SK). The `cognitoSub` attribute is populated by the\n * Post Confirmation Lambda (Epic #765 / #770); kept optional here until that write path lands.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-11-01-user-type-definition-fhir-and-data-layer.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const UserEntity = new Entity({\n model: {\n entity: \"user\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** FHIR Resource.id; platform user id (ohi_uid). */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full User resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Immutable Cognito-issued `sub` claim. Drives GSI2 (sub-lookup). Optional until the\n * Post Confirmation Lambda (#770) lands; required thereafter.\n */\n cognitoSub: {\n type: \"string\" as const,\n required: false,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = USER#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"id\"],\n template: \"USER#ID#${id}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Users across the four shards.\n * Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#User#SHARD#<n>`.\n * SK is derived via `gsi1skAttribute` — uses the resource's natural label when\n * extractable (string `name`/`title` via introspection), else `<lastUpdated>#<id>`\n * (DR-004). `casing: \"none\"` preserves the normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"gsi1Shard\"],\n template: \"TID#-#WID#-#RT#User#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n\n /**\n * GSI2 — Cognito sub-lookup per ADR-011: resolves the UserEntity from a Cognito `sub` claim.\n * `condition` skips the index when `cognitoSub` is missing so legacy items without a sub are\n * not indexed.\n */\n gsi2: {\n index: \"GSI2\",\n condition: (attrs: { cognitoSub?: string }) =>\n typeof attrs.cognitoSub === \"string\" && attrs.cognitoSub.length > 0,\n pk: {\n field: \"GSI2PK\",\n casing: \"none\" as const,\n composite: [\"cognitoSub\"],\n template: \"USER#SUB#${cognitoSub}\",\n },\n sk: {\n field: \"GSI2SK\",\n casing: \"none\" as const,\n composite: [],\n template: \"CURRENT\",\n },\n },\n },\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * Workspace data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, control plane. Each workspace belongs\n * to exactly one tenant; both tenantId and workspace id are in the partition key.\n *\n * Key structure: PK = TID#<tenantId>#WORKSPACE#ID#<id>, SK = CURRENT.\n * Uniqueness: one Workspace per (tenantId, id).\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Workspaces in a tenant across the four\n * shards. Workspace is itself the workspace identity, so the GSI1 PK uses `WID#-` as a sentinel.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-13-01-tenant-and-workspace-fhir-types-control-plane.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const WorkspaceEntity = new Entity({\n model: {\n entity: \"workspace\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** Tenant that contains this workspace (required). */\n tenantId: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR Resource.id; logical id in URL. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full Workspace resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = TID#<tenantId>#WORKSPACE#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"tenantId\", \"id\"],\n template: \"TID#${tenantId}#WORKSPACE#ID#${id}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Workspaces for a tenant across the\n * four shards. Workspace is itself the workspace identity, so `WID#-` is a sentinel.\n * SK is derived via `gsi1skAttribute` — `<normalizedName>#<id>` when the resource\n * carries a `name`, else `<lastUpdated>#<id>` (DR-004). `casing: \"none\"` preserves\n * the normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"tenantId\", \"gsi1Shard\"],\n template: \"TID#${tenantId}#WID#-#RT#Workspace#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n },\n});\n","import { extractSummary, type FhirResourceLike } from \"@openhi/types\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface MembershipCreateParams {\n context: OpenHiContext;\n body: { id?: string; resource?: Record<string, unknown> | string };\n tableName?: string;\n}\n\nexport interface MembershipCreateResult {\n id: string;\n resource: { resourceType: string; id: string; [key: string]: unknown };\n meta: { lastUpdated: string; versionId: string };\n}\n\nexport async function createMembershipOperation(\n params: MembershipCreateParams,\n): Promise<MembershipCreateResult> {\n const { context, body, tableName } = params;\n const service = getDynamoControlService(tableName);\n\n const id = body.id ?? `membership-${Date.now()}`;\n const parsedResource =\n typeof body.resource === \"string\"\n ? (JSON.parse(body.resource) as Record<string, unknown>)\n : (body.resource ?? {});\n\n const lastUpdated = context.date ?? new Date().toISOString();\n const vid = `1`;\n\n const resource = { resourceType: \"Membership\", id, ...parsedResource };\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n await service.entities.membership\n .put({\n tenantId: context.tenantId,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n })\n .go();\n\n return {\n id,\n resource,\n meta: { lastUpdated, versionId: vid },\n };\n}\n","import { extractSummary, type FhirResourceLike } from \"@openhi/types\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface RoleAssignmentCreateParams {\n context: OpenHiContext;\n body: { id?: string; resource?: Record<string, unknown> | string };\n tableName?: string;\n}\n\nexport interface RoleAssignmentCreateResult {\n id: string;\n resource: { resourceType: string; id: string; [key: string]: unknown };\n meta: { lastUpdated: string; versionId: string };\n}\n\nexport async function createRoleAssignmentOperation(\n params: RoleAssignmentCreateParams,\n): Promise<RoleAssignmentCreateResult> {\n const { context, body, tableName } = params;\n const service = getDynamoControlService(tableName);\n\n const id = body.id ?? `roleassignment-${Date.now()}`;\n const parsedResource =\n typeof body.resource === \"string\"\n ? (JSON.parse(body.resource) as Record<string, unknown>)\n : (body.resource ?? {});\n\n const lastUpdated = context.date ?? new Date().toISOString();\n const vid = `1`;\n\n const resource = { resourceType: \"RoleAssignment\", id, ...parsedResource };\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n await service.entities.roleAssignment\n .put({\n tenantId: context.tenantId,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n })\n .go();\n\n return {\n id,\n resource,\n meta: { lastUpdated, versionId: vid },\n };\n}\n","import { extractSummary, type FhirResourceLike } from \"@openhi/types\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport type { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface CreateTenantParams {\n context: OpenHiContext;\n body: {\n id?: string;\n resource?: Record<string, unknown> | string;\n };\n tableName?: string;\n}\n\nexport interface CreateTenantResult {\n id: string;\n resource: Record<string, unknown>;\n meta: { lastUpdated: string; versionId: string };\n}\n\n/**\n * Creates a Tenant. Generates an id if not provided.\n */\nexport async function createTenantOperation(\n params: CreateTenantParams,\n): Promise<CreateTenantResult> {\n const { context, body, tableName } = params;\n const service = getDynamoControlService(tableName);\n\n const id = body.id ?? `tenant-${Date.now()}`;\n const lastUpdated = context.date;\n const vid =\n lastUpdated.replace(/[-:T.Z]/g, \"\").slice(0, 12) || Date.now().toString(36);\n\n const parsedResource =\n typeof body.resource === \"string\"\n ? (JSON.parse(body.resource) as Record<string, unknown>)\n : (body.resource ?? {});\n\n const resource = { resourceType: \"Tenant\", id, ...parsedResource };\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n await service.entities.tenant\n .put({\n tenantId: id,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n })\n .go();\n\n return { id, resource, meta: { lastUpdated, versionId: vid } };\n}\n","import { extractSummary, type FhirResourceLike } from \"@openhi/types\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport type { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface CreateWorkspaceParams {\n context: OpenHiContext;\n body: {\n id?: string;\n resource?: Record<string, unknown> | string;\n };\n tableName?: string;\n}\n\nexport interface CreateWorkspaceResult {\n id: string;\n resource: Record<string, unknown>;\n meta: { lastUpdated: string; versionId: string };\n}\n\n/**\n * Creates a Workspace scoped to the context tenant. Generates an id if not provided.\n */\nexport async function createWorkspaceOperation(\n params: CreateWorkspaceParams,\n): Promise<CreateWorkspaceResult> {\n const { context, body, tableName } = params;\n const { tenantId } = context;\n const service = getDynamoControlService(tableName);\n\n const id = body.id ?? `workspace-${Date.now()}`;\n const lastUpdated = context.date;\n const vid =\n lastUpdated.replace(/[-:T.Z]/g, \"\").slice(0, 12) || Date.now().toString(36);\n\n const parsedResource =\n typeof body.resource === \"string\"\n ? (JSON.parse(body.resource) as Record<string, unknown>)\n : (body.resource ?? {});\n\n const resource = { resourceType: \"Workspace\", id, ...parsedResource };\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n await service.entities.workspace\n .put({\n tenantId,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n })\n .go();\n\n return { id, resource, meta: { lastUpdated, versionId: vid } };\n}\n","import { IUserPool } from \"aws-cdk-lib/aws-cognito\";\nimport { ITable } from \"aws-cdk-lib/aws-dynamodb\";\nimport { IEventBus } from \"aws-cdk-lib/aws-events\";\nimport { Construct } from \"constructs\";\nimport { SEED_DEMO_DATA_CONSUMER_NAME } from \"./events\";\nimport { SeedDemoDataLambda } from \"./seed-demo-data-lambda\";\nimport { WorkflowDedupTable } from \"../../../components/dynamodb/workflow-dedup-table\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-demo-data/seed-demo-data-workflow.md\n */\n\nexport interface SeedDemoDataWorkflowProps {\n /** Control event bus carrying `platform.system-data-seeded.v1`. */\n readonly controlEventBus: IEventBus;\n /** Data-store table the workflow upserts demo-data records into. */\n readonly dataStoreTable: ITable;\n /** Cognito User Pool the workflow provisions dev users into. */\n readonly userPool: IUserPool;\n}\n\n/**\n * Control-plane workflow that fires on every platform deploy and\n * idempotently re-asserts the demo-data graph: placeholder tenant +\n * workspace, 3 demo tenants + 4 workspaces, and per-dev-user Cognito\n * users with their DynamoDB User records, Memberships, and\n * RoleAssignments.\n *\n * Mounted on the data-service stack so the IAM grants against the\n * data-store table stay local. The control event bus and the workflow\n * dedup table reach in cross-stack via the SSM lookups\n * `OpenHiGlobalService.controlEventBusFromConstruct` and\n * `WorkflowDedupTable.grantConsumerFromLookup` respectively. The\n * Cognito User Pool similarly reaches in via\n * `OpenHiAuthService.userPoolFromConstruct`.\n *\n * Non-prod-only: the CDK stage-router (`OpenHiDataService`)\n * conditionally constructs this workflow only on non-prod stages.\n * The construct itself never checks the stage — its absence in prod\n * stacks is the gate.\n */\nexport class SeedDemoDataWorkflow extends Construct {\n public readonly seedDemoData: SeedDemoDataLambda;\n\n constructor(scope: Construct, props: SeedDemoDataWorkflowProps) {\n super(scope, \"seed-demo-data-workflow\");\n\n this.seedDemoData = new SeedDemoDataLambda(this, {\n controlEventBus: props.controlEventBus,\n dataStoreTable: props.dataStoreTable,\n userPool: props.userPool,\n });\n\n // Cross-stack grant resolves the dedup table's name + ARN via SSM\n // at synth time, so the data-service stack does not pick up a\n // CloudFormation export dependency on the global stack that owns\n // the dedup table.\n WorkflowDedupTable.grantConsumerFromLookup(\n this,\n this.seedDemoData.lambda,\n SEED_DEMO_DATA_CONSUMER_NAME,\n );\n }\n}\n","import {\n PlatformDeploymentCompletedV1,\n PlatformSystemDataSeededV1,\n} from \"@openhi/workflows\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-system-data/events.md\n */\n\n/**\n * Stable logical name this workflow registers with the shared\n * `WorkflowDedupTable` (TR-015). Used in both the construct grant\n * (`workflowDedupTable.grantConsumer(lambda, SEED_SYSTEM_DATA_CONSUMER_NAME)`)\n * and the handler's runtime `recordIfAbsent` call — keep them aligned by\n * importing this constant in both places.\n */\nexport const SEED_SYSTEM_DATA_CONSUMER_NAME = \"seed-system-data\";\n\n/**\n * Free-form `actor.system` value the handler stamps on the\n * `platform.system-data-seeded.v1` event it publishes when seeding\n * completes. Pinned here so the test can assert the wire value without\n * importing private handler internals.\n */\nexport const SEED_SYSTEM_DATA_ACTOR_SYSTEM = \"seed-system-data\";\n\n/**\n * Env var the Lambda construct injects with the control event bus\n * name. The handler reads it to build the publisher target when\n * emitting `platform.system-data-seeded.v1` after a successful seed.\n */\nexport const SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR = \"CONTROL_EVENT_BUS_NAME\";\n\n/**\n * The trigger this workflow subscribes to on the control event bus. The\n * `platform-deploy-bridge` workflow (#960) republishes terminal CloudFormation\n * stack-success events as `platform.deployment-completed.v1`; on every fire\n * this workflow re-asserts the platform-singleton control-plane records\n * (today: the three canonical Roles; future: additional system data).\n *\n * Re-exported from `@openhi/workflows` for symmetry with the handler's\n * import — keep the construct + handler reading from the same registry\n * entry so a rename upstream surfaces at both call sites.\n */\nexport { PlatformDeploymentCompletedV1, PlatformSystemDataSeededV1 };\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { PLATFORM_ROLE_IDS } from \"@openhi/types\";\nimport { Duration, Stack } from \"aws-cdk-lib\";\nimport { ITable } from \"aws-cdk-lib/aws-dynamodb\";\nimport { IEventBus, Rule } from \"aws-cdk-lib/aws-events\";\nimport { LambdaFunction } from \"aws-cdk-lib/aws-events-targets\";\nimport { Effect, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\nimport {\n PlatformDeploymentCompletedV1,\n SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR,\n} from \"./events\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-system-data/seed-system-data-lambda.md\n */\n\nconst HANDLER_NAME = \"seed-system-data.handler.js\";\n\n/**\n * Resolve the bundled handler entry. Same dual-path lookup the user-onboarding\n * Lambda uses: src/ for tests (the file lives next to this one) or lib/ for\n * the compiled bundle.\n */\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n\n return path.join(dirname, \"..\", \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n}\n\nexport interface SeedSystemDataLambdaProps {\n /**\n * Data-store table the workflow upserts platform-singleton control-plane\n * records into. Wired via `DYNAMO_TABLE_NAME` env var; granted scoped\n * write permission to the role records' partition keys only.\n */\n readonly dataStoreTable: ITable;\n\n /**\n * Control event bus that re-publishes\n * `platform.deployment-completed.v1` from the platform-deploy bridge.\n * The Rule mounts here.\n */\n readonly controlEventBus: IEventBus;\n}\n\n/**\n * Lambda + EventBridge Rule pair for the seed-system-data workflow. Owns\n * the routing (`source` / `detail-type` pattern) and the scoped data-store\n * grants — co-locating routing + permissions with the function they\n * target. Wiring to the workflow dedup table is the parent construct's\n * job (it has the singleton reference) and happens via\n * `WorkflowDedupTable.grantConsumer`.\n */\nexport class SeedSystemDataLambda extends Construct {\n public readonly lambda: NodejsFunction;\n public readonly rule: Rule;\n\n constructor(scope: Construct, props: SeedSystemDataLambdaProps) {\n super(scope, \"seed-system-data-lambda\");\n\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 512,\n timeout: Duration.minutes(1),\n environment: {\n DYNAMO_TABLE_NAME: props.dataStoreTable.tableName,\n [SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR]:\n props.controlEventBus.eventBusName,\n },\n });\n\n // Least-privilege grant: only the three known role PKs are\n // writable. A regression that tried to write a non-Role record\n // (or a Role with an unrecognized id) would be rejected by IAM,\n // not by application code. Updating the role-id set means\n // amending the generator's `ID_PREFIX_BY_VALUE_SET_URL` table\n // and regenerating — the role-id values flow through.\n //\n // The leading-keys values must match what ElectroDB *actually\n // writes*, not the entity definition's pretty template. The\n // base-table PK template `ROLE#ID#${id}` has no\n // `casing: \"none\"`, so ElectroDB applies its default casing\n // (lowercase) at runtime and the on-the-wire PK is\n // `role#id#<id>`. Authoring the policy in the uppercase\n // template form silently produces an IAM denial on every\n // PutItem the seeder attempts.\n const roleArns = Object.values(PLATFORM_ROLE_IDS).map(\n (id) => `role#id#${id}`,\n );\n this.lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\"dynamodb:PutItem\", \"dynamodb:UpdateItem\"],\n resources: [props.dataStoreTable.tableArn],\n conditions: {\n \"ForAllValues:StringEquals\": {\n \"dynamodb:LeadingKeys\": roleArns,\n },\n },\n }),\n );\n\n // Allow the handler to publish `platform.system-data-seeded.v1`\n // onto the control event bus after a successful seed. Downstream\n // consumers (`seed-demo-data`) subscribe to that event instead\n // of the raw deploy-completion event so the dependency is\n // enforced by a happens-before edge rather than by EventBridge\n // retry timing.\n props.controlEventBus.grantPutEventsTo(this.lambda);\n\n // Gate the rule on the host (data) stack's own completion event.\n // The data-store table is created by this stack, so seeding only\n // becomes safe once this stack reaches CREATE/UPDATE_COMPLETE.\n // Without this filter, sibling stacks (global, auth, rest-api,\n // graphql) completing before the data stack would trigger the\n // seeder against a not-yet-created table.\n //\n // The filter targets `detail.payload.stackName`: the outer\n // `detail` is EventBridge's envelope (which holds the whole\n // workflow envelope), and `payload` is the per-workflow payload\n // field on `WorkflowEvent` — i.e. the projection the bridge\n // produced from the CloudFormation Stack Status Change event.\n const hostStackName = Stack.of(this).stackName;\n\n this.rule = new Rule(this, \"rule\", {\n eventBus: props.controlEventBus,\n eventPattern: {\n source: [PlatformDeploymentCompletedV1.source],\n detailType: [PlatformDeploymentCompletedV1.detailType],\n detail: {\n payload: {\n stackName: [hostStackName],\n },\n },\n },\n targets: [\n new LambdaFunction(this.lambda, {\n retryAttempts: 2,\n maxEventAge: Duration.hours(2),\n }),\n ],\n });\n }\n}\n","import { ITable } from \"aws-cdk-lib/aws-dynamodb\";\nimport { IEventBus } from \"aws-cdk-lib/aws-events\";\nimport { Construct } from \"constructs\";\nimport { SEED_SYSTEM_DATA_CONSUMER_NAME } from \"./events\";\nimport { SeedSystemDataLambda } from \"./seed-system-data-lambda\";\nimport { WorkflowDedupTable } from \"../../../components/dynamodb/workflow-dedup-table\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-system-data/seed-system-data-workflow.md\n */\n\nexport interface SeedSystemDataWorkflowProps {\n /** Control event bus carrying `platform.deployment-completed.v1`. */\n readonly controlEventBus: IEventBus;\n /** Data-store table the workflow upserts platform-singleton records into. */\n readonly dataStoreTable: ITable;\n}\n\n/**\n * Control-plane workflow that fires on every platform deploy and\n * idempotently re-asserts the platform-singleton control-plane records\n * (today: the three canonical Roles; future: additional system data\n * slotted in as sibling steps under the same dedup record).\n *\n * Mounted on the data-service stack so the IAM grants against the\n * data-store table stay local. The control event bus and the\n * workflow dedup table reach in cross-stack via the SSM lookups\n * `OpenHiGlobalService.controlEventBusFromConstruct` and\n * `WorkflowDedupTable.grantConsumerFromLookup` respectively.\n */\nexport class SeedSystemDataWorkflow extends Construct {\n public readonly seedSystemData: SeedSystemDataLambda;\n\n constructor(scope: Construct, props: SeedSystemDataWorkflowProps) {\n super(scope, \"seed-system-data-workflow\");\n\n this.seedSystemData = new SeedSystemDataLambda(this, {\n controlEventBus: props.controlEventBus,\n dataStoreTable: props.dataStoreTable,\n });\n\n // Cross-stack grant resolves the dedup table's name + ARN via SSM\n // at synth time, so the data-service stack does not pick up a\n // CloudFormation export dependency on the global stack that owns\n // the dedup table.\n WorkflowDedupTable.grantConsumerFromLookup(\n this,\n this.seedSystemData.lambda,\n SEED_SYSTEM_DATA_CONSUMER_NAME,\n );\n }\n}\n","import type { PostConfirmationTriggerEvent } from \"aws-lambda\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/user-onboarding/events.md\n */\n\n// EventBridge routing values shared by the publisher and workflow rule.\nexport const USER_ONBOARDING_EVENT_SOURCE = \"openhi.control.user-onboarding\";\nexport const PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE =\n \"ProvisionDefaultWorkspaceRequested\";\n\n// Minimal workflow payload needed to provision and diagnose onboarding.\nexport interface ProvisionDefaultWorkspaceRequestedDetail {\n readonly cognitoSub: string;\n readonly userId?: string;\n readonly email?: string;\n readonly displayName?: string;\n readonly trigger: {\n readonly source: \"cognito.post-confirmation\";\n readonly triggerSource?: string;\n readonly userPoolId?: string;\n readonly userName?: string;\n readonly clientId?: string;\n };\n}\n\n// Convert Cognito's Post Confirmation trigger event into the workflow event.\nexport const buildProvisionDefaultWorkspaceRequestedDetail = (\n event: PostConfirmationTriggerEvent,\n): ProvisionDefaultWorkspaceRequestedDetail | undefined => {\n // Cognito sub is the required stable identity key for onboarding records.\n const attrs = event.request?.userAttributes ?? {};\n const cognitoSub = attrs.sub?.trim();\n if (!cognitoSub) {\n return undefined;\n }\n\n // Prefer email for display where available; fall back to Cognito metadata.\n const email = attrs.email?.trim();\n const displayName = email || event.userName || cognitoSub;\n\n // Include trigger metadata so failed or duplicate events can be traced.\n return {\n cognitoSub,\n ...(email ? { email } : {}),\n displayName,\n trigger: {\n source: \"cognito.post-confirmation\",\n triggerSource: event.triggerSource,\n userPoolId: event.userPoolId,\n userName: event.userName,\n clientId: event.callerContext?.clientId,\n },\n };\n};\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Duration } from \"aws-cdk-lib\";\nimport { ITable } from \"aws-cdk-lib/aws-dynamodb\";\nimport { IEventBus, Rule } from \"aws-cdk-lib/aws-events\";\nimport { LambdaFunction } from \"aws-cdk-lib/aws-events-targets\";\nimport { Effect, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\nimport {\n PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE,\n USER_ONBOARDING_EVENT_SOURCE,\n} from \"./events\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/user-onboarding/provision-default-workspace-lambda.md\n */\n\nconst HANDLER_NAME = \"provision-default-workspace.handler.js\";\n\n/**\n * Resolve Lambda entry so it works when running from src/ (tests) or from lib/ (built).\n */\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n\n return path.join(dirname, \"..\", \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n}\n\nexport interface ProvisionDefaultWorkspaceLambdaProps {\n /**\n * DynamoDB data store table. Used for the Lambda's `DYNAMO_TABLE_NAME`\n * env var and for granting the Lambda the writes + GSI queries it needs\n * to provision default control-plane resources.\n */\n readonly dataStoreTable: ITable;\n\n /**\n * Control-plane event bus that the EventBridge Rule listens on.\n */\n readonly controlEventBus: IEventBus;\n}\n\n/**\n * Lambda used by the user-onboarding workflow to create a user's default\n * Tenant, Workspace, Memberships, and RoleAssignment.\n *\n * Owns the EventBridge Rule that routes the default-workspace onboarding\n * event to itself, and the IAM permissions it needs on the data store\n * table — colocating routing + permissions with the function they target.\n */\nexport class ProvisionDefaultWorkspaceLambda extends Construct {\n public readonly lambda: NodejsFunction;\n public readonly rule: Rule;\n\n constructor(scope: Construct, props: ProvisionDefaultWorkspaceLambdaProps) {\n super(scope, \"provision-default-workspace-lambda\");\n\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 1024,\n environment: {\n DYNAMO_TABLE_NAME: props.dataStoreTable.tableName,\n },\n });\n\n // Grant table writes for default resources and User repair.\n props.dataStoreTable.grant(\n this.lambda,\n \"dynamodb:PutItem\",\n \"dynamodb:UpdateItem\",\n );\n\n // Table.grant(\"dynamodb:Query\") only covers the table itself, not its\n // GSIs, so an explicit policy is required to query GSI2 by cognitoSub.\n this.lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\"dynamodb:Query\"],\n resources: [`${props.dataStoreTable.tableArn}/index/*`],\n }),\n );\n\n // Route only the default-workspace onboarding event to this worker.\n this.rule = new Rule(this, \"rule\", {\n eventBus: props.controlEventBus,\n eventPattern: {\n source: [USER_ONBOARDING_EVENT_SOURCE],\n detailType: [PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE],\n },\n targets: [\n new LambdaFunction(this.lambda, {\n retryAttempts: 2,\n maxEventAge: Duration.hours(2),\n }),\n ],\n });\n }\n}\n","import { ITable } from \"aws-cdk-lib/aws-dynamodb\";\nimport { IEventBus } from \"aws-cdk-lib/aws-events\";\nimport { Construct } from \"constructs\";\nimport { ProvisionDefaultWorkspaceLambda } from \"./provision-default-workspace-lambda\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/user-onboarding/user-onboarding-workflow.md\n */\n\nexport interface UserOnboardingWorkflowProps {\n readonly controlEventBus: IEventBus;\n readonly dataStoreTable: ITable;\n}\n\n/**\n * Control-plane workflow for onboarding users after Cognito confirmation.\n */\nexport class UserOnboardingWorkflow extends Construct {\n public readonly provisionDefaultWorkspace: ProvisionDefaultWorkspaceLambda;\n\n constructor(scope: Construct, props: UserOnboardingWorkflowProps) {\n super(scope, \"user-onboarding-workflow\");\n\n this.provisionDefaultWorkspace = new ProvisionDefaultWorkspaceLambda(this, {\n dataStoreTable: props.dataStoreTable,\n controlEventBus: props.controlEventBus,\n });\n }\n}\n","import { OPEN_HI_STAGE } from \"@openhi/config\";\nimport {\n CorsHttpMethod,\n DomainName,\n HttpApi,\n HttpMethod,\n HttpNoneAuthorizer,\n HttpRoute,\n HttpRouteKey,\n IHttpApi,\n} from \"aws-cdk-lib/aws-apigatewayv2\";\nimport { HttpUserPoolAuthorizer } from \"aws-cdk-lib/aws-apigatewayv2-authorizers\";\nimport { HttpLambdaIntegration } from \"aws-cdk-lib/aws-apigatewayv2-integrations\";\nimport { ICertificate } from \"aws-cdk-lib/aws-certificatemanager\";\nimport { Effect, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport {\n ARecord,\n HostedZone,\n IHostedZone,\n RecordTarget,\n} from \"aws-cdk-lib/aws-route53\";\nimport { ApiGatewayv2DomainProperties } from \"aws-cdk-lib/aws-route53-targets\";\nimport { Duration } from \"aws-cdk-lib/core\";\nimport { Construct } from \"constructs\";\nimport { OpenHiAuthService } from \"./open-hi-auth-service\";\nimport { OpenHiDataService } from \"./open-hi-data-service\";\nimport { OpenHiGlobalService } from \"./open-hi-global-service\";\nimport { OpenHiEnvironment } from \"../app/open-hi-environment\";\nimport {\n OpenHiService,\n OpenHiServiceProps,\n OpenHiServiceType,\n} from \"../app/open-hi-service\";\nimport {\n RootHttpApi,\n RootHttpApiProps,\n} from \"../components/api-gateway/root-http-api\";\nimport {\n DataStorePostgresReplica,\n getPostgresReplicaSchemaName,\n} from \"../components/postgres/data-store-postgres-replica\";\nimport { DiscoverableStringParameter } from \"../components/ssm\";\nimport { CorsOptionsLambda } from \"../data/lambda/cors-options-lambda\";\nimport { RestApiLambda } from \"../data/lambda/rest-api-lambda\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/services/open-hi-rest-api-service.md\n */\n\nexport interface OpenHiRestApiServiceProps extends OpenHiServiceProps {\n /**\n * Optional props passed through to the RootHttpApi (API Gateway HTTP API) construct.\n * Use corsPreflight (CDK CorsPreflightOptions) for CORS; other HttpApiProps (e.g. description, disableExecuteApiEndpoint) apply as well.\n */\n readonly rootHttpApiProps?: RootHttpApiProps;\n}\n\n/**\n * SSM parameter name suffix for the REST API base URL.\n * Full parameter name is built via buildParameterName with serviceType REST_API.\n */\nexport const REST_API_BASE_URL_SSM_NAME = \"REST_API_BASE_URL\";\n\n/**\n * REST API service stack: HTTP API, custom domain, and Lambda; exports base URL via SSM.\n * Resources are created in protected methods; subclasses may override to customize.\n */\nexport class OpenHiRestApiService extends OpenHiService {\n static readonly SERVICE_TYPE =\n \"rest-api\" as const satisfies OpenHiServiceType;\n\n /**\n * Returns an IHttpApi by looking up the REST API stack's HTTP API ID from SSM.\n */\n static rootHttpApiFromConstruct(scope: Construct): IHttpApi {\n const httpApiId = DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: RootHttpApi.SSM_PARAM_NAME,\n serviceType: OpenHiRestApiService.SERVICE_TYPE,\n });\n return HttpApi.fromHttpApiAttributes(scope, \"http-api\", { httpApiId });\n }\n\n /**\n * Returns the REST API base URL (e.g. https://api.example.com) by looking it up from SSM.\n * Use in other stacks for E2E, scripts, or config.\n */\n static restApiBaseUrlFromConstruct(scope: Construct): string {\n return DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: REST_API_BASE_URL_SSM_NAME,\n serviceType: OpenHiRestApiService.SERVICE_TYPE,\n });\n }\n\n get serviceType(): string {\n return OpenHiRestApiService.SERVICE_TYPE;\n }\n\n /** Override so this.props is typed with this service's options (e.g. rootHttpApiProps). */\n public override props: OpenHiRestApiServiceProps;\n\n public readonly rootHttpApi: RootHttpApi;\n\n constructor(ohEnv: OpenHiEnvironment, props: OpenHiRestApiServiceProps = {}) {\n super(ohEnv, OpenHiRestApiService.SERVICE_TYPE, props);\n this.props = props;\n\n this.validateConfig(props);\n\n const hostedZone = this.createHostedZone();\n const certificate = this.createCertificate();\n const apiDomainName = this.createApiDomainNameString(hostedZone);\n this.createRestApiBaseUrlParameter(apiDomainName);\n const domainName = this.createDomainName(hostedZone, certificate);\n this.rootHttpApi = this.createRootHttpApi(domainName);\n this.createRestApiLambdaAndRoutes(hostedZone, domainName);\n }\n\n /**\n * Validates that config required for the REST API stack is present.\n */\n protected validateConfig(props: OpenHiRestApiServiceProps): void {\n const { config } = props;\n if (!config) {\n throw new Error(\"Config is required\");\n }\n if (!config.hostedZoneId) {\n throw new Error(\"Hosted zone ID is required\");\n }\n if (!config.zoneName) {\n throw new Error(\"Zone name is required\");\n }\n }\n\n /**\n * Creates the hosted zone reference (imported from config).\n * Override to customize.\n */\n protected createHostedZone(): IHostedZone {\n const { config } = this.props;\n return HostedZone.fromHostedZoneAttributes(this, \"root-zone\", {\n hostedZoneId: config!.hostedZoneId!,\n zoneName: config!.zoneName!,\n });\n }\n\n /**\n * Creates the wildcard certificate (imported from Global stack via SSM).\n * Override to customize.\n */\n protected createCertificate() {\n return OpenHiGlobalService.rootWildcardCertificateFromConstruct(this);\n }\n\n /**\n * Returns the API domain name string (e.g. api.example.com or api-{prefix}.example.com).\n * Override to customize.\n */\n protected createApiDomainNameString(hostedZone: IHostedZone): string {\n const apiPrefix =\n this.branchName === \"main\" ? `api` : `api-${this.childZonePrefix}`;\n return [apiPrefix, hostedZone.zoneName].join(\".\");\n }\n\n /**\n * Creates the SSM parameter for the REST API base URL.\n * Look up via {@link OpenHiRestApiService.restApiBaseUrlFromConstruct}.\n * Override to customize.\n */\n protected createRestApiBaseUrlParameter(apiDomainName: string): void {\n const restApiBaseUrl = `https://${apiDomainName}`;\n new DiscoverableStringParameter(this, \"rest-api-base-url-param\", {\n ssmParamName: REST_API_BASE_URL_SSM_NAME,\n stringValue: restApiBaseUrl,\n description: \"REST API base URL for this deployment (E2E, scripts)\",\n });\n }\n\n /**\n * Creates the API Gateway custom domain name resource.\n * Override to customize.\n */\n protected createDomainName(\n _hostedZone: IHostedZone,\n certificate: ICertificate,\n ): DomainName {\n const apiDomainName = this.createApiDomainNameString(_hostedZone);\n return new DomainName(this, \"domain\", {\n domainName: apiDomainName,\n certificate,\n });\n }\n\n /**\n * Creates the Lambda integration, HTTP routes, and API DNS record.\n * Override to customize. Uses {@link rootHttpApi} set by the constructor.\n */\n protected createRestApiLambdaAndRoutes(\n hostedZone: IHostedZone,\n domainName: DomainName,\n ): void {\n const dataStoreTable =\n OpenHiDataService.dynamoDbDataStoreFromConstruct(this);\n\n // Phase 2 of ADR 2026-04-17-01: REST API Lambda queries Postgres via the\n // RDS Data API. Cluster ARN, secret ARN, and database name come from SSM\n // (cross-stack discovery); the per-branch schema name is deterministic\n // from `branchHash` and computed locally to avoid an extra SSM lookup.\n const postgresClusterArn =\n DataStorePostgresReplica.clusterArnFromConstruct(this);\n const postgresSecretArn =\n DataStorePostgresReplica.secretArnFromConstruct(this);\n const postgresDatabase =\n DataStorePostgresReplica.databaseNameFromConstruct(this);\n const postgresSchema = getPostgresReplicaSchemaName(this.branchHash);\n\n const { lambda } = new RestApiLambda(this, {\n dynamoTableName: dataStoreTable.tableName,\n branchTagValue: this.branchName,\n httpApiTagValue: RootHttpApi.SSM_PARAM_NAME,\n postgresClusterArn,\n postgresSecretArn,\n postgresDatabase,\n postgresSchema,\n });\n\n // Allow the Lambda to issue Data API statements against this cluster and\n // read the cluster credentials secret. These are scoped to the specific\n // ARNs published by the data stack — no wildcards.\n lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\n \"rds-data:ExecuteStatement\",\n \"rds-data:BatchExecuteStatement\",\n ],\n resources: [postgresClusterArn],\n }),\n );\n lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\"secretsmanager:GetSecretValue\"],\n resources: [postgresSecretArn],\n }),\n );\n const dynamoActions = [\n \"dynamodb:GetItem\",\n \"dynamodb:Query\",\n \"dynamodb:BatchGetItem\",\n \"dynamodb:ConditionCheckItem\",\n \"dynamodb:DescribeTable\",\n \"dynamodb:BatchWriteItem\",\n \"dynamodb:PutItem\",\n \"dynamodb:UpdateItem\",\n \"dynamodb:DeleteItem\",\n ] as const;\n dataStoreTable.grant(lambda, ...dynamoActions);\n // Query (and other operations) on GSIs require the index resource ARN\n lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [...dynamoActions],\n resources: [`${dataStoreTable.tableArn}/index/*`],\n }),\n );\n // Temporary: broad SSM read for dynamic config (test only)\n lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\n \"ssm:GetParameter\",\n \"ssm:GetParameters\",\n \"ssm:DescribeParameters\",\n ],\n resources: [\"*\"],\n }),\n );\n const integration = new HttpLambdaIntegration(\"lambda-integration\", lambda);\n const { lambda: optionsLambda } = new CorsOptionsLambda(this);\n const optionsIntegration = new HttpLambdaIntegration(\n \"options-integration\",\n optionsLambda,\n );\n const noAuth = new HttpNoneAuthorizer();\n // OPTIONS routes use dedicated low-memory Lambda so main REST API Lambda is not invoked for preflight (#694).\n new HttpRoute(this, \"options-route-root\", {\n httpApi: this.rootHttpApi,\n routeKey: HttpRouteKey.with(\"/\", HttpMethod.OPTIONS),\n integration: optionsIntegration,\n authorizer: noAuth,\n });\n new HttpRoute(this, \"options-route-proxy\", {\n httpApi: this.rootHttpApi,\n routeKey: HttpRouteKey.with(\"/{proxy+}\", HttpMethod.OPTIONS),\n integration: optionsIntegration,\n authorizer: noAuth,\n });\n new HttpRoute(this, \"proxy-route-root\", {\n httpApi: this.rootHttpApi,\n routeKey: HttpRouteKey.with(\"/\", HttpMethod.ANY),\n integration,\n });\n new HttpRoute(this, \"proxy-route\", {\n httpApi: this.rootHttpApi,\n routeKey: HttpRouteKey.with(\"/{proxy+}\", HttpMethod.ANY),\n integration,\n });\n const apiPrefix =\n this.branchName === \"main\" ? `api` : `api-${this.childZonePrefix}`;\n new ARecord(this, \"api-a-record\", {\n zone: hostedZone,\n recordName: apiPrefix,\n target: RecordTarget.fromAlias(\n new ApiGatewayv2DomainProperties(\n domainName.regionalDomainName,\n domainName.regionalHostedZoneId,\n ),\n ),\n });\n }\n\n /**\n * Creates the Root HTTP API with default domain mapping, Cognito JWT authorizer, and exports API ID to SSM.\n * Look up via {@link OpenHiRestApiService.rootHttpApiFromConstruct}.\n * Override to customize.\n */\n protected createRootHttpApi(domainName: DomainName): RootHttpApi {\n const userPool = OpenHiAuthService.userPoolFromConstruct(this);\n const userPoolClient = OpenHiAuthService.userPoolClientFromConstruct(this);\n // In non-prod, also accept tokens issued by the dedicated\n // fixture-seeder client (provisioned in the auth stack only when\n // stage !== prod). Adding it to the authorizer's accepted-clients\n // list lets the seed-fixtures CLI authenticate against the same\n // API the SPA uses, without weakening prod which never sees this\n // client at all.\n const userPoolClients = [userPoolClient];\n if (this.ohEnv.ohStage.stageType !== OPEN_HI_STAGE.PROD) {\n userPoolClients.push(\n OpenHiAuthService.fixtureSeederClientFromConstruct(this),\n );\n }\n const cognitoAuthorizer = new HttpUserPoolAuthorizer(\n \"cognito-authorizer\",\n userPool,\n { userPoolClients },\n );\n const { corsPreflight: cors, ...restRootHttpApiProps } =\n this.props.rootHttpApiProps ?? {};\n const corsPreflight =\n cors !== undefined\n ? {\n allowOrigins: cors.allowOrigins,\n allowMethods: cors.allowMethods ?? [\n CorsHttpMethod.GET,\n CorsHttpMethod.HEAD,\n CorsHttpMethod.POST,\n CorsHttpMethod.PUT,\n CorsHttpMethod.PATCH,\n CorsHttpMethod.DELETE,\n CorsHttpMethod.OPTIONS,\n ],\n allowHeaders: cors.allowHeaders ?? [\n \"Content-Type\",\n \"Authorization\",\n ],\n allowCredentials: cors.allowCredentials ?? true,\n maxAge: cors.maxAge ?? Duration.days(1),\n ...(cors.exposeHeaders !== undefined && {\n exposeHeaders: cors.exposeHeaders,\n }),\n }\n : undefined;\n const rootHttpApi = new RootHttpApi(this, {\n ...restRootHttpApiProps,\n ...(corsPreflight !== undefined && { corsPreflight }),\n defaultDomainMapping: {\n domainName,\n mappingKey: undefined,\n },\n defaultAuthorizer: cognitoAuthorizer,\n });\n new DiscoverableStringParameter(this, \"http-api-url-param\", {\n ssmParamName: RootHttpApi.SSM_PARAM_NAME,\n stringValue: rootHttpApi.httpApiId,\n description:\n \"API Gateway HTTP API ID for this REST API stack (cross-stack reference)\",\n });\n return rootHttpApi;\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\n\n/**\n * Dedicated Lambda for CORS preflight (OPTIONS) requests. Returns 204 so API Gateway\n * can add CORS headers from the API's corsPreflight config. Low memory footprint.\n * @see #694\n */\n\nconst HANDLER_NAME = \"cors-options-lambda.handler.js\";\n\n/**\n * Resolve Lambda entry so it works when running from src/ (tests) or from lib/ (built).\n */\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n\n const fromLib = path.join(dirname, \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n return fromLib;\n}\n\nexport class CorsOptionsLambda extends Construct {\n public readonly lambda: NodejsFunction;\n\n constructor(scope: Construct, id: string = \"cors-options-lambda\") {\n super(scope, id);\n\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 128,\n });\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/data/lambda/rest-api-lambda.md\n */\n\nconst HANDLER_NAME = \"rest-api-lambda.handler.js\";\n\n/**\n * Resolve Lambda entry so it works when running from src/ (tests) or from lib/ (built).\n */\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n\n const fromLib = path.join(dirname, \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n return fromLib;\n}\n\nexport interface RestApiLambdaProps {\n /**\n * DynamoDB table name for the data store. The Lambda receives it as the\n * environment variable DYNAMO_TABLE_NAME at runtime.\n */\n readonly dynamoTableName: string;\n\n /**\n * Branch name from the service. Passed as BRANCH_TAG_VALUE at runtime.\n */\n readonly branchTagValue: string;\n\n /**\n * SSM parameter name for the HTTP API (e.g. RootHttpApi.SSM_PARAM_NAME).\n * Passed as HTTP_API_TAG_VALUE at runtime.\n */\n readonly httpApiTagValue: string;\n\n /**\n * Aurora cluster ARN published by `DataStorePostgresReplica`. Passed as\n * `OPENHI_PG_CLUSTER_ARN` at runtime so the Lambda can target the cluster\n * via the RDS Data API (ADR 2026-04-17-01, phase 2).\n */\n readonly postgresClusterArn: string;\n\n /**\n * Secrets Manager ARN with the cluster credentials. Passed as\n * `OPENHI_PG_SECRET_ARN` at runtime; consumed by the RDS Data API client.\n */\n readonly postgresSecretArn: string;\n\n /**\n * Database name on the cluster. Passed as `OPENHI_PG_DATABASE` at runtime.\n */\n readonly postgresDatabase: string;\n\n /**\n * Per-branch schema name on the cluster (e.g. `b_<branchHash>`). Passed as\n * `OPENHI_PG_SCHEMA` at runtime.\n */\n readonly postgresSchema: string;\n}\n\nexport class RestApiLambda extends Construct {\n public readonly lambda: NodejsFunction;\n\n constructor(scope: Construct, props: RestApiLambdaProps) {\n super(scope, \"rest-api-lambda\");\n\n /**\n * Create a Lambda function\n */\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 1024,\n environment: {\n DYNAMO_TABLE_NAME: props.dynamoTableName,\n BRANCH_TAG_VALUE: props.branchTagValue,\n HTTP_API_TAG_VALUE: props.httpApiTagValue,\n OPENHI_PG_CLUSTER_ARN: props.postgresClusterArn,\n OPENHI_PG_SECRET_ARN: props.postgresSecretArn,\n OPENHI_PG_DATABASE: props.postgresDatabase,\n OPENHI_PG_SCHEMA: props.postgresSchema,\n },\n bundling: {\n minify: true,\n sourceMap: false,\n },\n });\n }\n}\n","import {\n AuthorizationType,\n IGraphqlApi,\n UserPoolDefaultAction,\n} from \"aws-cdk-lib/aws-appsync\";\nimport { Construct } from \"constructs\";\nimport { OpenHiAuthService } from \"./open-hi-auth-service\";\nimport { OpenHiEnvironment } from \"../app/open-hi-environment\";\nimport {\n OpenHiService,\n OpenHiServiceProps,\n OpenHiServiceType,\n} from \"../app/open-hi-service\";\nimport { RootGraphqlApi } from \"../components/app-sync/root-graphql-api\";\n\nexport interface OpenHiGraphqlServiceProps extends OpenHiServiceProps {}\n\n/**\n * GraphQL API service stack: creates the AppSync API via {@link RootGraphqlApi}\n * and exports its ID via SSM. Look up from other stacks via\n * {@link OpenHiGraphqlService.graphqlApiFromConstruct}.\n */\nexport class OpenHiGraphqlService extends OpenHiService {\n static readonly SERVICE_TYPE =\n \"graphql-api\" as const satisfies OpenHiServiceType;\n\n /**\n * Returns the GraphQL API by looking up the GraphQL stack's API ID from SSM.\n * Use from other stacks to obtain an IGraphqlApi reference.\n */\n static graphqlApiFromConstruct(scope: Construct): IGraphqlApi {\n return RootGraphqlApi.fromConstruct(scope);\n }\n\n get serviceType(): string {\n return OpenHiGraphqlService.SERVICE_TYPE;\n }\n\n /* Override so this.props is typed with this service's options */\n public override props: OpenHiGraphqlServiceProps;\n\n public readonly rootGraphqlApi: RootGraphqlApi;\n\n constructor(ohEnv: OpenHiEnvironment, props: OpenHiGraphqlServiceProps = {}) {\n super(ohEnv, OpenHiGraphqlService.SERVICE_TYPE, props);\n this.props = props;\n this.rootGraphqlApi = this.createRootGraphqlApi();\n }\n\n /** Creates the root GraphQL API with Cognito user pool. */\n protected createRootGraphqlApi(): RootGraphqlApi {\n const userPool = OpenHiAuthService.userPoolFromConstruct(this);\n return new RootGraphqlApi(this, {\n authorizationConfig: {\n defaultAuthorization: {\n authorizationType: AuthorizationType.USER_POOL,\n userPoolConfig: {\n userPool,\n defaultAction: UserPoolDefaultAction.ALLOW,\n },\n },\n },\n });\n }\n}\n"],"mappings":"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","names":["exports","exports","exports","exports","exports","exports","exports","exports","exports","exports","parseWorkflowEvent","exports","exports","workflowDedupClient","exports","exports","import_config","import_aws_cdk_lib","import_aws_cdk_lib","import_config","import_aws_cdk_lib","import_aws_cdk_lib","import_aws_ssm","import_aws_cdk_lib","import_aws_cognito","import_aws_cognito","import_aws_cognito","path","fs","import_node_fs","import_node_path","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_node_fs","import_node_path","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_node_fs","import_node_path","import_aws_cdk_lib","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_aws_cdk_lib","import_aws_dynamodb","import_constructs","import_aws_events","import_aws_events","import_node_fs","import_node_path","import_aws_cdk_lib","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_aws_cdk_lib","import_constructs","import_constructs","import_config","import_aws_cognito","import_aws_iam","import_aws_kms","import_core","import_config","import_aws_dynamodb","import_aws_certificatemanager","import_aws_events","import_aws_route53","import_aws_ssm","import_constructs","import_node_fs","import_node_path","import_aws_cdk_lib","import_aws_events","import_aws_iam","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_workflows","import_node_fs","import_node_path","import_types","import_aws_cdk_lib","import_aws_events","import_aws_events_targets","import_aws_iam","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","import_client_dynamodb","import_types","import_workflows","import_electrodb","import_types","import_electrodb","import_electrodb","import_electrodb","import_electrodb","import_electrodb","import_electrodb","import_types","import_types","import_types","import_types","HANDLER_NAME","resolveHandlerEntry","path","fs","import_constructs","import_workflows","import_node_fs","import_node_path","import_types","import_aws_cdk_lib","import_aws_events","import_aws_events_targets","import_aws_iam","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_constructs","import_node_fs","import_node_path","import_aws_cdk_lib","import_aws_events","import_aws_events_targets","import_aws_iam","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_constructs","import_config","import_aws_apigatewayv2","import_aws_iam","import_aws_route53","import_core","import_node_fs","import_node_path","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_node_fs","import_node_path","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_aws_appsync"]}
|
|
1
|
+
{"version":3,"sources":["../../config/src/open-hi-config.ts","../../config/src/index.ts","../../workflows/src/envelope-version.ts","../../workflows/src/envelope.ts","../../workflows/src/sources.ts","../../workflows/src/detail-types/registry.ts","../../workflows/src/detail-types/platform.ts","../../workflows/src/detail-types/index.ts","../../workflows/src/publisher.ts","../../workflows/src/consumer.ts","../../workflows/src/dedup/env.ts","../../workflows/src/dedup/workflow-dedup-client.ts","../../workflows/src/dedup/index.ts","../../workflows/src/index.ts","../src/index.ts","../src/app/open-hi-app.ts","../src/app/open-hi-environment.ts","../src/app/open-hi-stage.ts","../src/app/open-hi-service.ts","../src/components/acm/root-wildcard-certificate.ts","../src/components/api-gateway/root-http-api.ts","../src/components/app-sync/root-graphql-api.ts","../src/components/ssm/discoverable-string-parameter.ts","../src/components/cognito/cognito-fixture-seeder-client.ts","../src/components/cognito/cognito-user-pool.ts","../src/components/cognito/cognito-user-pool-client.ts","../src/components/cognito/cognito-user-pool-domain.ts","../src/components/cognito/cognito-user-pool-kms-key.ts","../src/components/cognito/post-authentication-lambda.ts","../src/components/cognito/post-confirmation-lambda.ts","../src/components/cognito/pre-token-generation-lambda.ts","../src/components/dynamodb/dynamodb-stream-record.ts","../src/components/dynamodb/data-store-change-events.ts","../src/components/dynamodb/data-store-historical-archive.ts","../src/components/dynamodb/dynamo-db-data-store.ts","../src/components/dynamodb/workflow-dedup-table.ts","../src/components/event-bridge/data-event-bus.ts","../src/components/event-bridge/ops-event-bus.ts","../src/components/event-bridge/control-event-bus.ts","../src/components/postgres/data-store-postgres-replica.ts","../src/components/route-53/child-hosted-zone.ts","../src/components/route-53/root-hosted-zone.ts","../src/components/static-hosting/static-hosting.ts","../src/services/open-hi-auth-service.ts","../src/services/open-hi-data-service.ts","../src/services/open-hi-global-service.ts","../src/workflows/control-plane/platform-deploy-bridge/events.ts","../src/workflows/control-plane/platform-deploy-bridge/platform-deploy-bridge.ts","../src/workflows/control-plane/platform-deploy-bridge/platform-deploy-bridge-lambda.ts","../src/workflows/control-plane/seed-demo-data/events.ts","../src/workflows/control-plane/seed-demo-data/seed-demo-data-lambda.ts","../src/workflows/control-plane/seed-demo-data/seed-demo-data.handler.ts","../src/data/dynamo/dynamo-control-service.ts","../src/data/dynamo/dynamo-client.ts","../src/data/dynamo/entities/control/configuration-entity.ts","../src/data/dynamo/entities/control/control-entity-common.ts","../src/data/dynamo/shard.ts","../src/data/dynamo/entities/control/membership-entity.ts","../src/data/dynamo/entities/control/role-entity.ts","../src/data/dynamo/entities/control/roleassignment-entity.ts","../src/data/dynamo/entities/control/tenant-entity.ts","../src/data/dynamo/entities/control/user-entity.ts","../src/data/dynamo/entities/control/workspace-entity.ts","../src/data/operations/control/membership/membership-create-operation.ts","../src/data/operations/control/roleassignment/roleassignment-create-operation.ts","../src/data/operations/control/tenant/tenant-create-operation.ts","../src/data/operations/control/workspace/workspace-create-operation.ts","../src/data/dynamo/dynamo-data-service.ts","../src/data/dynamo/entities/data-entity-common.ts","../src/data/dynamo/entities/data/account-entity.ts","../src/data/dynamo/entities/data/activity-definition-entity.ts","../src/data/dynamo/entities/data/adverse-event-entity.ts","../src/data/dynamo/entities/data/allergy-intolerance-entity.ts","../src/data/dynamo/entities/data/appointment-entity.ts","../src/data/dynamo/entities/data/appointment-response-entity.ts","../src/data/dynamo/entities/data/audit-event-entity.ts","../src/data/dynamo/entities/data/basic-entity.ts","../src/data/dynamo/entities/data/biologically-derived-product-entity.ts","../src/data/dynamo/entities/data/body-structure-entity.ts","../src/data/dynamo/entities/data/capability-statement-entity.ts","../src/data/dynamo/entities/data/care-plan-entity.ts","../src/data/dynamo/entities/data/care-team-entity.ts","../src/data/dynamo/entities/data/catalog-entry-entity.ts","../src/data/dynamo/entities/data/charge-item-definition-entity.ts","../src/data/dynamo/entities/data/charge-item-entity.ts","../src/data/dynamo/entities/data/claim-entity.ts","../src/data/dynamo/entities/data/claim-response-entity.ts","../src/data/dynamo/entities/data/clinical-impression-entity.ts","../src/data/dynamo/entities/data/code-system-entity.ts","../src/data/dynamo/entities/data/communication-entity.ts","../src/data/dynamo/entities/data/communication-request-entity.ts","../src/data/dynamo/entities/data/compartment-definition-entity.ts","../src/data/dynamo/entities/data/composition-entity.ts","../src/data/dynamo/entities/data/concept-map-entity.ts","../src/data/dynamo/entities/data/condition-entity.ts","../src/data/dynamo/entities/data/consent-entity.ts","../src/data/dynamo/entities/data/contract-entity.ts","../src/data/dynamo/entities/data/coverage-eligibility-request-entity.ts","../src/data/dynamo/entities/data/coverage-eligibility-response-entity.ts","../src/data/dynamo/entities/data/coverage-entity.ts","../src/data/dynamo/entities/data/detected-issue-entity.ts","../src/data/dynamo/entities/data/device-definition-entity.ts","../src/data/dynamo/entities/data/device-entity.ts","../src/data/dynamo/entities/data/device-metric-entity.ts","../src/data/dynamo/entities/data/device-request-entity.ts","../src/data/dynamo/entities/data/device-use-statement-entity.ts","../src/data/dynamo/entities/data/diagnostic-report-entity.ts","../src/data/dynamo/entities/data/document-manifest-entity.ts","../src/data/dynamo/entities/data/document-reference-entity.ts","../src/data/dynamo/entities/data/effect-evidence-synthesis-entity.ts","../src/data/dynamo/entities/data/encounter-entity.ts","../src/data/dynamo/entities/data/endpoint-entity.ts","../src/data/dynamo/entities/data/enrollment-request-entity.ts","../src/data/dynamo/entities/data/enrollment-response-entity.ts","../src/data/dynamo/entities/data/episode-of-care-entity.ts","../src/data/dynamo/entities/data/event-definition-entity.ts","../src/data/dynamo/entities/data/evidence-entity.ts","../src/data/dynamo/entities/data/evidence-variable-entity.ts","../src/data/dynamo/entities/data/example-scenario-entity.ts","../src/data/dynamo/entities/data/explanation-of-benefit-entity.ts","../src/data/dynamo/entities/data/family-member-history-entity.ts","../src/data/dynamo/entities/data/flag-entity.ts","../src/data/dynamo/entities/data/goal-entity.ts","../src/data/dynamo/entities/data/graph-definition-entity.ts","../src/data/dynamo/entities/data/group-entity.ts","../src/data/dynamo/entities/data/guidance-response-entity.ts","../src/data/dynamo/entities/data/healthcare-service-entity.ts","../src/data/dynamo/entities/data/imaging-study-entity.ts","../src/data/dynamo/entities/data/immunization-entity.ts","../src/data/dynamo/entities/data/immunization-evaluation-entity.ts","../src/data/dynamo/entities/data/immunization-recommendation-entity.ts","../src/data/dynamo/entities/data/implementation-guide-entity.ts","../src/data/dynamo/entities/data/insurance-plan-entity.ts","../src/data/dynamo/entities/data/invoice-entity.ts","../src/data/dynamo/entities/data/library-entity.ts","../src/data/dynamo/entities/data/linkage-entity.ts","../src/data/dynamo/entities/data/list-entity.ts","../src/data/dynamo/entities/data/location-entity.ts","../src/data/dynamo/entities/data/measure-entity.ts","../src/data/dynamo/entities/data/measure-report-entity.ts","../src/data/dynamo/entities/data/media-entity.ts","../src/data/dynamo/entities/data/medication-administration-entity.ts","../src/data/dynamo/entities/data/medication-dispense-entity.ts","../src/data/dynamo/entities/data/medication-entity.ts","../src/data/dynamo/entities/data/medication-knowledge-entity.ts","../src/data/dynamo/entities/data/medication-request-entity.ts","../src/data/dynamo/entities/data/medication-statement-entity.ts","../src/data/dynamo/entities/data/medicinal-product-authorization-entity.ts","../src/data/dynamo/entities/data/medicinal-product-contraindication-entity.ts","../src/data/dynamo/entities/data/medicinal-product-entity.ts","../src/data/dynamo/entities/data/medicinal-product-indication-entity.ts","../src/data/dynamo/entities/data/medicinal-product-ingredient-entity.ts","../src/data/dynamo/entities/data/medicinal-product-interaction-entity.ts","../src/data/dynamo/entities/data/medicinal-product-manufactured-entity.ts","../src/data/dynamo/entities/data/medicinal-product-packaged-entity.ts","../src/data/dynamo/entities/data/medicinal-product-pharmaceutical-entity.ts","../src/data/dynamo/entities/data/medicinal-product-undesirable-effect-entity.ts","../src/data/dynamo/entities/data/message-definition-entity.ts","../src/data/dynamo/entities/data/message-header-entity.ts","../src/data/dynamo/entities/data/molecular-sequence-entity.ts","../src/data/dynamo/entities/data/naming-system-entity.ts","../src/data/dynamo/entities/data/nutrition-order-entity.ts","../src/data/dynamo/entities/data/observation-definition-entity.ts","../src/data/dynamo/entities/data/observation-entity.ts","../src/data/dynamo/entities/data/operation-definition-entity.ts","../src/data/dynamo/entities/data/organization-affiliation-entity.ts","../src/data/dynamo/entities/data/organization-entity.ts","../src/data/dynamo/entities/data/patient-entity.ts","../src/data/dynamo/entities/data/payment-notice-entity.ts","../src/data/dynamo/entities/data/payment-reconciliation-entity.ts","../src/data/dynamo/entities/data/person-entity.ts","../src/data/dynamo/entities/data/plan-definition-entity.ts","../src/data/dynamo/entities/data/practitioner-entity.ts","../src/data/dynamo/entities/data/practitioner-role-entity.ts","../src/data/dynamo/entities/data/procedure-entity.ts","../src/data/dynamo/entities/data/provenance-entity.ts","../src/data/dynamo/entities/data/questionnaire-entity.ts","../src/data/dynamo/entities/data/questionnaire-response-entity.ts","../src/data/dynamo/entities/data/related-person-entity.ts","../src/data/dynamo/entities/data/request-group-entity.ts","../src/data/dynamo/entities/data/research-definition-entity.ts","../src/data/dynamo/entities/data/research-element-definition-entity.ts","../src/data/dynamo/entities/data/research-study-entity.ts","../src/data/dynamo/entities/data/research-subject-entity.ts","../src/data/dynamo/entities/data/risk-assessment-entity.ts","../src/data/dynamo/entities/data/risk-evidence-synthesis-entity.ts","../src/data/dynamo/entities/data/schedule-entity.ts","../src/data/dynamo/entities/data/search-parameter-entity.ts","../src/data/dynamo/entities/data/service-request-entity.ts","../src/data/dynamo/entities/data/slot-entity.ts","../src/data/dynamo/entities/data/specimen-definition-entity.ts","../src/data/dynamo/entities/data/specimen-entity.ts","../src/data/dynamo/entities/data/structure-definition-entity.ts","../src/data/dynamo/entities/data/structure-map-entity.ts","../src/data/dynamo/entities/data/subscription-entity.ts","../src/data/dynamo/entities/data/substance-entity.ts","../src/data/dynamo/entities/data/substance-nucleic-acid-entity.ts","../src/data/dynamo/entities/data/substance-polymer-entity.ts","../src/data/dynamo/entities/data/substance-protein-entity.ts","../src/data/dynamo/entities/data/substance-reference-information-entity.ts","../src/data/dynamo/entities/data/substance-source-material-entity.ts","../src/data/dynamo/entities/data/substance-specification-entity.ts","../src/data/dynamo/entities/data/supply-delivery-entity.ts","../src/data/dynamo/entities/data/supply-request-entity.ts","../src/data/dynamo/entities/data/task-entity.ts","../src/data/dynamo/entities/data/terminology-capabilities-entity.ts","../src/data/dynamo/entities/data/test-report-entity.ts","../src/data/dynamo/entities/data/test-script-entity.ts","../src/data/dynamo/entities/data/value-set-entity.ts","../src/data/dynamo/entities/data/verification-result-entity.ts","../src/data/dynamo/entities/data/vision-prescription-entity.ts","../src/data/operations/data-operations-common.ts","../src/lib/compression.ts","../src/workflows/control-plane/seed-demo-data/seed-demo-data-workflow.ts","../src/workflows/control-plane/seed-system-data/events.ts","../src/workflows/control-plane/seed-system-data/seed-system-data-lambda.ts","../src/workflows/control-plane/seed-system-data/seed-system-data-workflow.ts","../src/workflows/control-plane/user-onboarding/events.ts","../src/workflows/control-plane/user-onboarding/provision-default-workspace-lambda.ts","../src/workflows/control-plane/user-onboarding/user-onboarding-workflow.ts","../src/services/open-hi-rest-api-service.ts","../src/data/lambda/cors-options-lambda.ts","../src/data/lambda/rest-api-lambda.ts","../src/services/open-hi-graphql-service.ts"],"sourcesContent":["/*******************************************************************************\n *\n * OpenHi Config\n *\n * These types are kept in their own package to prevent dependency conflicts and\n * conditions between @openhi/constructs and @openhi/platform..\n *\n ******************************************************************************/\n\n/**\n * Stage Types\n *\n * What stage of deployment is this? Dev, staging, or prod?\n */\nexport const OPEN_HI_STAGE = {\n /**\n * Development environment, typically used for testing and development.\n */\n DEV: \"dev\",\n /**\n * Staging environment, used for pre-production testing.\n */\n STAGE: \"stage\",\n /**\n * Production environment, used for live deployments.\n */\n PROD: \"prod\",\n} as const;\n\n/**\n * Above const as a type.\n */\nexport type OpenHiStageType =\n (typeof OPEN_HI_STAGE)[keyof typeof OPEN_HI_STAGE];\n\n/**\n * Deployment Target Role\n *\n * Is this (account, region) the primary or a secondary deployment target for the stage?\n * Works for both multi-region (different regions) and cellular (same region, different accounts).\n */\nexport const OPEN_HI_DEPLOYMENT_TARGET_ROLE = {\n /**\n * The primary deployment target for this stage (main account/region).\n * For example, the base DynamoDB region for global tables.\n */\n PRIMARY: \"primary\",\n\n /**\n * A secondary deployment target for this stage (additional account/region).\n * For example, a replica region for a global DynamoDB table, or another cell in the same region.\n */\n SECONDARY: \"secondary\",\n} as const;\n\n/**\n * Above const as a type.\n */\nexport type OpenHiDeploymentTargetRoleType =\n (typeof OPEN_HI_DEPLOYMENT_TARGET_ROLE)[keyof typeof OPEN_HI_DEPLOYMENT_TARGET_ROLE];\n\nexport interface OpenHiEnvironmentConfig {\n account: string;\n region: string;\n /**\n * Route53 zone containing DNS for this service.\n */\n hostedZoneId?: string;\n zoneName?: string;\n}\n\n/**\n * Represents the configuration for OpenHi services across different stages and\n * deployment targets.\n */\nexport interface OpenHiConfig {\n versions?: {\n cdk?: {\n cdkLibVersion?: string;\n cdkCliVersion?: string;\n };\n };\n deploymentTargets?: {\n [OPEN_HI_STAGE.DEV]?: {\n [OPEN_HI_DEPLOYMENT_TARGET_ROLE.PRIMARY]?: OpenHiEnvironmentConfig;\n [OPEN_HI_DEPLOYMENT_TARGET_ROLE.SECONDARY]?: Array<OpenHiEnvironmentConfig>;\n };\n [OPEN_HI_STAGE.STAGE]?: {\n [OPEN_HI_DEPLOYMENT_TARGET_ROLE.PRIMARY]?: OpenHiEnvironmentConfig;\n [OPEN_HI_DEPLOYMENT_TARGET_ROLE.SECONDARY]?: Array<OpenHiEnvironmentConfig>;\n };\n [OPEN_HI_STAGE.PROD]?: {\n [OPEN_HI_DEPLOYMENT_TARGET_ROLE.PRIMARY]?: OpenHiEnvironmentConfig;\n [OPEN_HI_DEPLOYMENT_TARGET_ROLE.SECONDARY]?: Array<OpenHiEnvironmentConfig>;\n };\n };\n}\n","export * from \"./open-hi-config\";\n","/**\n * Envelope version pinned by the publisher SDK on every emitted envelope.\n *\n * Shape is `\"<major>.<minor>\"` per TR-016 §Open Items #3 (P1).\n * Additive optional fields bump the minor; breaking changes bump the\n * major and require a documented overlap window in the consumer SDK.\n */\nexport const ENVELOPE_VERSION = \"1.0\";\n\nconst ENVELOPE_VERSION_PATTERN = /^\\d+\\.\\d+$/;\n\n/**\n * Lowest envelope major version this SDK's consumer parser accepts.\n *\n * Bump this when a deprecated major reaches end-of-life and the\n * overlap window closes.\n */\nconst MIN_SUPPORTED_MAJOR = 1;\n\n/**\n * Highest envelope major version this SDK's consumer parser accepts.\n *\n * Bump this in lockstep with `ENVELOPE_VERSION` whenever a new major\n * ships; the consumer always supports its own publish major.\n */\nconst MAX_SUPPORTED_MAJOR = 1;\n\n/**\n * Return `true` when `version` is shaped `<major>.<minor>` and its\n * major lies within `[MIN_SUPPORTED_MAJOR, MAX_SUPPORTED_MAJOR]`.\n */\nexport function isSupportedEnvelopeVersion(version: string): boolean {\n if (!ENVELOPE_VERSION_PATTERN.test(version)) {\n return false;\n }\n const major = Number.parseInt(version.split(\".\")[0], 10);\n return major >= MIN_SUPPORTED_MAJOR && major <= MAX_SUPPORTED_MAJOR;\n}\n","import type { OhiJwtClaims } from \"@openhi/types\";\n\n/**\n * Discriminated-union actor field per ADR-016 §Decision #3.\n *\n * User-initiated workflows project the four ADR-014 JWT claims (with\n * `ohi_tid` / `ohi_wid` required — a workflow event without a tenant\n * + workspace context belongs to the `system` variant); bootstrap\n * workflows run before a User exists and carry the bootstrap-role\n * name instead. The `system` value is free-form pending the\n * onboarding-bootstrap IAM role TR (TR-016 §Open Items #4).\n */\nexport type WorkflowActor = WorkflowUserActor | WorkflowSystemActor;\n\n/** User-actor variant — required projection of the ADR-014 JWT claims. */\nexport interface WorkflowUserActor {\n readonly ohi_tid: string;\n readonly ohi_wid: string;\n readonly ohi_uid: string;\n readonly ohi_uname: string;\n}\n\n/** Bootstrap-actor variant — carries the bootstrap-role name. */\nexport interface WorkflowSystemActor {\n readonly system: string;\n}\n\n/**\n * Type guard for the user-actor variant.\n */\nexport function isWorkflowUserActor(\n actor: WorkflowActor,\n): actor is WorkflowUserActor {\n return (actor as WorkflowUserActor).ohi_uid !== undefined;\n}\n\n/**\n * Type guard for the system-actor variant.\n */\nexport function isWorkflowSystemActor(\n actor: WorkflowActor,\n): actor is WorkflowSystemActor {\n return (actor as WorkflowSystemActor).system !== undefined;\n}\n\n/**\n * Standard workflow event envelope per ADR-016 §Decision #3 / TR-016.\n *\n * The generic `TPayload` parameter narrows the per-workflow payload.\n *\n * Field naming note: the payload lives under `payload` (not `detail`)\n * deliberately. EventBridge's outer event already has its own `detail`\n * field that carries this whole envelope; nesting another `detail`\n * inside it produced double-`detail` paths in rule patterns\n * (`detail.detail.<x>`) that consistently bit consumers writing\n * filters. The unambiguous name is worth the rename.\n */\nexport interface WorkflowEvent<TPayload = Record<string, unknown>> {\n /** Per-event UUID. Dedup keys on `(eventId, attempt)`. */\n readonly eventId: string;\n /** 1-indexed delivery attempt. */\n readonly attempt: number;\n /** Originating cross-plane chain identifier. */\n readonly correlationId: string;\n /** Immediate predecessor event id, or null for a chain origin. */\n readonly causationId: string | null;\n /** Discriminated actor — JWT-claim projection or bootstrap-role marker. */\n readonly actor: WorkflowActor;\n /** ISO-8601 timestamp marking when the envelope was constructed. */\n readonly occurredAt: string;\n /** Semver-shaped envelope version (e.g. `\"1.0\"`). */\n readonly envelopeVersion: string;\n /** Per-workflow payload narrowed by the generic parameter. */\n readonly payload: TPayload;\n}\n\n/**\n * Promote a raw `OhiJwtClaims` projection to a fully-required\n * `WorkflowUserActor`.\n *\n * Throws `MissingActorContextError` when `ohi_tid` or `ohi_wid` is\n * absent — those claims are optional on the JWT but required on the\n * workflow user-actor.\n */\nexport function workflowUserActorFromClaims(\n claims: OhiJwtClaims,\n): WorkflowUserActor {\n if (claims.ohi_tid === undefined || claims.ohi_wid === undefined) {\n throw new MissingActorContextError(\n \"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.\",\n );\n }\n return {\n ohi_tid: claims.ohi_tid,\n ohi_wid: claims.ohi_wid,\n ohi_uid: claims.ohi_uid,\n ohi_uname: claims.ohi_uname,\n };\n}\n\n/** Thrown when JWT claims lack a field required by the workflow user-actor. */\nexport class MissingActorContextError extends Error {\n /** @param message - human-readable description of the missing claim. */\n constructor(message: string) {\n super(message);\n this.name = \"MissingActorContextError\";\n }\n}\n","/**\n * Per-bus `Source` constants for the three OpenHI EventBridge buses.\n *\n * Every workflow event carries one of these values in its EventBridge\n * `Source` field. Bus selection follows ADR-016 §Decision #1.\n *\n * @see https://github.com/codedrifters/openhi-planning/blob/main/docs/src/content/docs/requirements/architectural-decisions/ADR-016-workflow-boundary-strategy.md\n */\n\n/** Source for control-plane workflow events (Tenant / Workspace / User / Membership / Role / RoleAssignment / Configuration). */\nexport const OPENHI_CONTROL_SOURCE = \"openhi.control\" as const;\n\n/** Source for data-plane workflow events (committed writes to the FHIR data store). */\nexport const OPENHI_DATA_SOURCE = \"openhi.data\" as const;\n\n/** Source for ops-plane workflow events (platform telemetry, deployments, build events). */\nexport const OPENHI_OPS_SOURCE = \"openhi.ops\" as const;\n\n/**\n * Discriminated union over the three OpenHI EventBridge sources.\n *\n * A publisher that passes any other string for the EventBridge `Source`\n * field is rejected at compile time.\n */\nexport type OpenHiSource =\n | typeof OPENHI_CONTROL_SOURCE\n | typeof OPENHI_DATA_SOURCE\n | typeof OPENHI_OPS_SOURCE;\n\n/**\n * Default EventBridge bus name for each OpenHI source.\n *\n * Deployments may override per-source via\n * `PublisherOptions.busNameByPlane`.\n */\nexport const DEFAULT_BUS_NAME_BY_SOURCE: Record<OpenHiSource, string> = {\n [OPENHI_CONTROL_SOURCE]: \"openhi-control-event-bus\",\n [OPENHI_DATA_SOURCE]: \"openhi-data-event-bus\",\n [OPENHI_OPS_SOURCE]: \"openhi-ops-event-bus\",\n};\n","import type { OpenHiSource } from \"../sources\";\n\n/**\n * One entry in the workflow detail-type registry.\n *\n * Each registered detail-type binds a typed `detail` payload to:\n * - the EventBridge `Source` (and therefore the bus) it ships on,\n * - the `detail-type` string consumers subscribe to,\n * - per-event metadata (`crossPlane`, `dedupRequired`) read by\n * downstream tooling (AsyncAPI generator, placement matrix).\n *\n * The `_detail` property is a phantom marker type only — never\n * read, never written, never instantiated. It carries the\n * compile-time TDetail through the entry so callers of\n * `publishWorkflowEvent` / `parseWorkflowEvent` see the typed\n * payload at the call site.\n */\nexport interface WorkflowDetailTypeEntry<TDetail> {\n /** Versioned detail-type string, e.g. `tenant.onboarded.v1`. */\n readonly detailType: string;\n /** The bus this detail-type ships on (compile-time enforced). */\n readonly source: OpenHiSource;\n /** Phantom marker carrying the payload shape. */\n readonly _detail?: TDetail;\n /** Marks events that cross plane boundaries (placement-matrix metadata). */\n readonly crossPlane?: boolean;\n /** Marks events that retryable consumers MUST dedupe on. */\n readonly dedupRequired?: boolean;\n}\n\n/**\n * Declare a registry entry with type inference.\n *\n * Call sites:\n *\n * ```ts\n * export const TenantOnboardedV1 = defineDetailType<TenantOnboardedV1Detail>({\n * detailType: \"tenant.onboarded.v1\",\n * source: OPENHI_CONTROL_SOURCE,\n * dedupRequired: true,\n * });\n * ```\n *\n * Detail-type strings follow `<area>.<event>.<version>` (TR-016 §Open\n * Items #2). The conformance regex below is the platform-wide format.\n */\nexport function defineDetailType<TDetail>(\n entry: Omit<WorkflowDetailTypeEntry<TDetail>, \"_detail\">,\n): WorkflowDetailTypeEntry<TDetail> {\n if (!isWellFormedDetailType(entry.detailType)) {\n throw new InvalidDetailTypeRegistrationError(\n `Detail-type \"${entry.detailType}\" does not match the platform-wide format <area>.<event>.v<integer>. See TR-016 §Open Items #2.`,\n );\n }\n return entry;\n}\n\n/**\n * Pattern enforced on every registered detail-type:\n * `<area>.<event>.v<integer>` where each segment is lowercase\n * alphanumeric with optional dashes.\n *\n * Multi-level areas (e.g. `fhir.audit-event.recorded.v1`) are\n * intentionally not yet allowed; TR-016 §Open Items #2 defers that.\n */\nconst DETAIL_TYPE_PATTERN =\n /^[a-z0-9]+(?:-[a-z0-9]+)*\\.[a-z0-9]+(?:-[a-z0-9]+)*\\.v\\d+$/;\n\n/** Return `true` when `detailType` matches the platform-wide format. */\nexport function isWellFormedDetailType(detailType: string): boolean {\n return DETAIL_TYPE_PATTERN.test(detailType);\n}\n\n/** Thrown by `defineDetailType` when the supplied string violates the format. */\nexport class InvalidDetailTypeRegistrationError extends Error {\n /** @param message - human-readable description of the violation. */\n constructor(message: string) {\n super(message);\n this.name = \"InvalidDetailTypeRegistrationError\";\n }\n}\n","import { OPENHI_CONTROL_SOURCE } from \"../sources\";\nimport { defineDetailType } from \"./registry\";\n\n/**\n * `detail` payload for `platform.deployment-completed.v1`.\n *\n * Projected by the platform-deploy bridge from a CloudFormation\n * `Stack Status Change` event (`CREATE_COMPLETE` / `UPDATE_COMPLETE`)\n * on a tagged OpenHI platform stack. Downstream control-plane\n * workflows (e.g. seed-system-roles, seed-demo-data) subscribe\n * to this detail-type on the control event bus.\n */\nexport interface PlatformDeploymentCompletedV1Detail {\n /** CloudFormation stack name (`AWS::CloudFormation::Stack` `StackName`). */\n readonly stackName: string;\n /** Full CloudFormation stack ARN. */\n readonly stackId: string;\n /** AWS region the stack deployed into (e.g. `us-east-1`). */\n readonly region: string;\n /** 12-digit AWS account id the stack deployed into. */\n readonly accountId: string;\n /** Terminal stack status that triggered the bridge. */\n readonly status: \"CREATE_COMPLETE\" | \"UPDATE_COMPLETE\";\n /** Free-form reason text from CloudFormation; absent on most events. */\n readonly statusReason?: string;\n /**\n * Projected subset of stack tags. The bridge resolves tags via\n * `cloudformation:DescribeStacks` because the source EventBridge\n * event omits them.\n */\n readonly stackTags: ReadonlyArray<{\n readonly key: string;\n readonly value: string;\n }>;\n /** ISO-8601 timestamp from the source EventBridge `time` field. */\n readonly cloudformationEventTime: string;\n}\n\n/**\n * Registry entry for `platform.deployment-completed.v1`.\n *\n * Published on the control event bus (`OPENHI_CONTROL_SOURCE`) per\n * the workflow placement matrix (codedrifters/openhi#953 row 4):\n * the AWS-native source is the ops-plane default bus, but the bridge\n * republishes onto the control bus because the downstream consumers\n * are control-plane workflows.\n *\n * `dedupRequired: true` — at-least-once redelivery from EventBridge\n * means retryable consumers MUST dedupe on `(eventId, attempt)` via\n * `WorkflowDedupClient`.\n */\nexport const PlatformDeploymentCompletedV1 =\n defineDetailType<PlatformDeploymentCompletedV1Detail>({\n detailType: \"platform.deployment-completed.v1\",\n source: OPENHI_CONTROL_SOURCE,\n dedupRequired: true,\n });\n\n/**\n * `detail` payload for `platform.system-data-seeded.v1`.\n *\n * Published by the `seed-system-data` workflow after it has\n * idempotently re-asserted every platform-singleton control-plane\n * record (today: the three canonical Roles; future: additional system\n * data) on the back of a `platform.deployment-completed.v1` event.\n *\n * Downstream control-plane workflows that depend on the\n * platform-singleton records existing — `seed-demo-data`, for\n * example — subscribe to this detail-type instead of the raw\n * deploy-completion event so the dependency is enforced by a\n * happens-before edge rather than by EventBridge retry timing.\n */\nexport interface PlatformSystemDataSeededV1Detail {\n /**\n * EventBridge `eventId` of the originating\n * `platform.deployment-completed.v1` event that triggered the\n * system-data seeding. Propagated for correlation in logs and\n * downstream causation chains.\n */\n readonly sourceEventId: string;\n /**\n * Full CloudFormation stack ARN of the deploy that triggered the\n * system-data seeding. Mirrors the field on the originating\n * `PlatformDeploymentCompletedV1Detail`; downstream consumers can\n * filter by stack-id prefix without re-reading the source event.\n */\n readonly sourceStackId: string;\n /**\n * Number of platform-singleton records re-asserted on this run.\n * Useful for sanity checks and observability — divergence between\n * deploys signals either a generator-emitted catalog change or a\n * partial-failure recovery from the replay tooling.\n */\n readonly seededRecordCount: number;\n}\n\n/**\n * Registry entry for `platform.system-data-seeded.v1`.\n *\n * Published onto the control event bus (`OPENHI_CONTROL_SOURCE`).\n * `dedupRequired: true` — downstream consumers MUST dedup on\n * `(eventId, attempt)` via `WorkflowDedupClient`, same as every other\n * retryable consumer.\n */\nexport const PlatformSystemDataSeededV1 =\n defineDetailType<PlatformSystemDataSeededV1Detail>({\n detailType: \"platform.system-data-seeded.v1\",\n source: OPENHI_CONTROL_SOURCE,\n dedupRequired: true,\n });\n","export * from \"./platform\";\nexport * from \"./registry\";\n","import { randomUUID } from \"node:crypto\";\nimport {\n EventBridgeClient,\n PutEventsCommand,\n} from \"@aws-sdk/client-eventbridge\";\n\nimport type { WorkflowDetailTypeEntry } from \"./detail-types/registry\";\nimport type { WorkflowActor, WorkflowEvent } from \"./envelope\";\nimport { ENVELOPE_VERSION } from \"./envelope-version\";\nimport { DEFAULT_BUS_NAME_BY_SOURCE, type OpenHiSource } from \"./sources\";\n\n/**\n * Caller-supplied envelope context the publisher consumes.\n *\n * The actor is required on every publish — pre-provisioning bootstrap\n * workflows pass a `{ system: <role-name> }` actor.\n *\n * `correlationId` and `causationId` propagate from an upstream event\n * when this publisher is consuming-then-publishing; pass them through\n * verbatim from the inbound envelope's fields. Both are optional;\n * when omitted the publisher treats the publish as a chain origin\n * (`correlationId` = fresh UUID, `causationId` = null).\n */\nexport interface PublishContext {\n readonly actor: WorkflowActor;\n readonly correlationId?: string;\n readonly causationId?: string | null;\n}\n\n/**\n * Per-call output of a successful publish.\n */\nexport interface PublishResult {\n readonly eventId: string;\n}\n\n/**\n * Publisher overrides applied to every call against a single client.\n *\n * `eventIdGenerator`, `correlationIdGenerator`, and `now` are\n * test-only seams; production callers omit them and the publisher\n * uses `crypto.randomUUID()` and `new Date()`.\n */\nexport interface PublisherOptions {\n /** Override the default bus name for one or more sources. */\n readonly busNameByPlane?: Partial<Record<OpenHiSource, string>>;\n /** Test seam — supply a deterministic UUID generator for `eventId`. */\n readonly eventIdGenerator?: () => string;\n /** Test seam — supply a deterministic UUID generator for new `correlationId` values. */\n readonly correlationIdGenerator?: () => string;\n /** Test seam — supply a deterministic clock for `occurredAt`. */\n readonly now?: () => Date;\n}\n\n/**\n * Tree-shaped publisher client per ADR-016 Recommendation.\n *\n * The `publish` primitive accepts any registered detail-type and\n * returns a typed `PublishResult`. Downstream tree shaping\n * (`client.<bus>.<area>.<event>.publish(payload, ctx)`) is built from\n * the detail-type registry once entries are registered; until then,\n * callers invoke `client.publish(entry, payload, ctx)` directly.\n */\nexport interface WorkflowsClient {\n /**\n * Construct a workflow envelope around `payload` and publish it to\n * the EventBridge bus configured for `entry.source`.\n */\n publish<TPayload>(\n entry: WorkflowDetailTypeEntry<TPayload>,\n payload: TPayload,\n ctx: PublishContext,\n ): Promise<PublishResult>;\n}\n\n/**\n * Factory that returns a `WorkflowsClient` bound to a single\n * `EventBridgeClient`.\n */\nexport function workflowsClient(\n bridge: EventBridgeClient,\n options: PublisherOptions = {},\n): WorkflowsClient {\n return {\n publish: (entry, payload, ctx) =>\n publishWorkflowEvent(bridge, entry, payload, ctx, options),\n };\n}\n\n/**\n * Construct a workflow envelope and publish it via\n * `EventBridge.PutEvents`.\n *\n * Exposed as a stand-alone function for callers that prefer the\n * primitive over the `WorkflowsClient` indirection.\n */\nexport async function publishWorkflowEvent<TPayload>(\n bridge: EventBridgeClient,\n entry: WorkflowDetailTypeEntry<TPayload>,\n payload: TPayload,\n ctx: PublishContext,\n options: PublisherOptions = {},\n): Promise<PublishResult> {\n const eventIdGenerator = options.eventIdGenerator ?? (() => randomUUID());\n const correlationIdGenerator =\n options.correlationIdGenerator ?? (() => randomUUID());\n const now = options.now ?? (() => new Date());\n\n const envelope: WorkflowEvent<TPayload> = {\n eventId: eventIdGenerator(),\n attempt: 1,\n correlationId: ctx.correlationId ?? correlationIdGenerator(),\n causationId: ctx.causationId ?? null,\n actor: ctx.actor,\n occurredAt: now().toISOString(),\n envelopeVersion: ENVELOPE_VERSION,\n payload,\n };\n\n const busName =\n options.busNameByPlane?.[entry.source] ??\n DEFAULT_BUS_NAME_BY_SOURCE[entry.source];\n\n const result = await bridge.send(\n new PutEventsCommand({\n Entries: [\n {\n EventBusName: busName,\n Source: entry.source,\n DetailType: entry.detailType,\n Detail: JSON.stringify(envelope),\n },\n ],\n }),\n );\n\n if ((result.FailedEntryCount ?? 0) > 0) {\n const first = result.Entries?.[0];\n throw new WorkflowPublishError(\n `EventBridge rejected ${entry.detailType} publish on bus ${busName}: ${first?.ErrorCode ?? \"unknown\"} — ${first?.ErrorMessage ?? \"no error message\"}`,\n );\n }\n\n return { eventId: envelope.eventId };\n}\n\n/** Thrown when EventBridge rejects a `PutEvents` entry. */\nexport class WorkflowPublishError extends Error {\n /** @param message - human-readable description of the failed publish. */\n constructor(message: string) {\n super(message);\n this.name = \"WorkflowPublishError\";\n }\n}\n","import type { WorkflowDetailTypeEntry } from \"./detail-types/registry\";\nimport type { WorkflowEvent } from \"./envelope\";\nimport { isSupportedEnvelopeVersion } from \"./envelope-version\";\n\n/**\n * Structural shape of the EventBridge event objects this SDK's\n * consumer parses.\n *\n * Matches `@types/aws-lambda`'s `EventBridgeEvent<string, unknown>`\n * by structural compatibility without requiring callers to import\n * that types package — consumers may pass either an\n * `EventBridgeEvent` from `aws-lambda` or any record-shaped object\n * carrying the same keys.\n */\nexport interface EventBridgeEventLike {\n readonly source: string;\n readonly \"detail-type\": string;\n readonly detail: unknown;\n}\n\n/**\n * The `(eventId, attempt)` tuple every retryable consumer hands to\n * the `WorkflowDedupTable` client.\n */\nexport interface DedupKey {\n readonly eventId: string;\n readonly attempt: number;\n}\n\n/**\n * Output of `parseWorkflowEvent` — the validated envelope plus the\n * dedup tuple.\n */\nexport interface ParsedWorkflowEvent<TPayload> {\n readonly envelope: WorkflowEvent<TPayload>;\n readonly dedupKey: DedupKey;\n}\n\n/**\n * Parse an EventBridge event into a typed envelope and surface the\n * `(eventId, attempt)` tuple the dedup-table client consumes.\n *\n * Validates:\n * - `event.source` matches `expected.source`\n * - `event[\"detail-type\"]` matches `expected.detailType`\n * - the envelope's `envelopeVersion` is within the SDK's supported range\n * - every required envelope field is present and well-shaped\n */\nexport function parseWorkflowEvent<TPayload>(\n event: EventBridgeEventLike,\n expected: WorkflowDetailTypeEntry<TPayload>,\n): ParsedWorkflowEvent<TPayload> {\n if (event.source !== expected.source) {\n throw new InvalidWorkflowEventError(\n `EventBridge source \"${event.source}\" does not match expected detail-type's source \"${expected.source}\".`,\n );\n }\n\n if (event[\"detail-type\"] !== expected.detailType) {\n throw new InvalidWorkflowEventError(\n `EventBridge detail-type \"${event[\"detail-type\"]}\" does not match expected \"${expected.detailType}\".`,\n );\n }\n\n const candidate = asEnvelopeCandidate(event.detail);\n\n if (!isSupportedEnvelopeVersion(candidate.envelopeVersion)) {\n throw new UnsupportedEnvelopeVersionError(\n `Envelope version \"${candidate.envelopeVersion}\" is outside the SDK's supported range.`,\n );\n }\n\n const envelope: WorkflowEvent<TPayload> = {\n eventId: candidate.eventId,\n attempt: candidate.attempt,\n correlationId: candidate.correlationId,\n causationId: candidate.causationId,\n actor: candidate.actor,\n occurredAt: candidate.occurredAt,\n envelopeVersion: candidate.envelopeVersion,\n payload: candidate.payload as TPayload,\n };\n\n return {\n envelope,\n dedupKey: { eventId: envelope.eventId, attempt: envelope.attempt },\n };\n}\n\n/**\n * Validate that the EventBridge `detail` (which carries the workflow\n * envelope) has every required field with a plausible type. Returns a\n * typed `WorkflowEvent<unknown>` so the caller can narrow `payload`\n * once routing has succeeded.\n */\nfunction asEnvelopeCandidate(detail: unknown): WorkflowEvent<unknown> {\n if (detail === null || typeof detail !== \"object\") {\n throw new InvalidWorkflowEventError(\n \"EventBridge detail is not a non-null object.\",\n );\n }\n\n const obj = detail as Record<string, unknown>;\n\n assertString(obj, \"eventId\");\n assertPositiveInteger(obj, \"attempt\");\n assertString(obj, \"correlationId\");\n assertCausationId(obj);\n assertActor(obj);\n assertString(obj, \"occurredAt\");\n assertString(obj, \"envelopeVersion\");\n\n if (!(\"payload\" in obj)) {\n throw new InvalidWorkflowEventError(\n \"Envelope is missing required field: payload.\",\n );\n }\n\n return obj as unknown as WorkflowEvent<unknown>;\n}\n\nfunction assertString(\n obj: Record<string, unknown>,\n field: string,\n): asserts obj is Record<string, unknown> & Record<typeof field, string> {\n const value = obj[field];\n if (typeof value !== \"string\" || value.length === 0) {\n throw new InvalidWorkflowEventError(\n `Envelope field \"${field}\" must be a non-empty string.`,\n );\n }\n}\n\nfunction assertPositiveInteger(\n obj: Record<string, unknown>,\n field: string,\n): void {\n const value = obj[field];\n if (typeof value !== \"number\" || !Number.isInteger(value) || value < 1) {\n throw new InvalidWorkflowEventError(\n `Envelope field \"${field}\" must be a 1-indexed integer.`,\n );\n }\n}\n\nfunction assertCausationId(obj: Record<string, unknown>): void {\n if (!(\"causationId\" in obj)) {\n throw new InvalidWorkflowEventError(\n \"Envelope is missing required field: causationId.\",\n );\n }\n const value = obj.causationId;\n if (value !== null && (typeof value !== \"string\" || value.length === 0)) {\n throw new InvalidWorkflowEventError(\n 'Envelope field \"causationId\" must be a non-empty string or null.',\n );\n }\n}\n\nfunction assertActor(obj: Record<string, unknown>): void {\n const actor = obj.actor;\n if (actor === null || typeof actor !== \"object\") {\n throw new InvalidWorkflowEventError(\n 'Envelope field \"actor\" must be an object.',\n );\n }\n const actorObj = actor as Record<string, unknown>;\n const isUserActor =\n typeof actorObj.ohi_uid === \"string\" &&\n typeof actorObj.ohi_uname === \"string\" &&\n typeof actorObj.ohi_tid === \"string\" &&\n typeof actorObj.ohi_wid === \"string\";\n const isSystemActor = typeof actorObj.system === \"string\";\n if (!isUserActor && !isSystemActor) {\n throw new InvalidWorkflowEventError(\n 'Envelope field \"actor\" must be either a user-actor (ohi_tid, ohi_wid, ohi_uid, ohi_uname) or a system-actor ({ system: string }).',\n );\n }\n}\n\n/** Thrown when the event does not match the expected detail-type entry. */\nexport class InvalidWorkflowEventError extends Error {\n /** @param message - human-readable description of the validation failure. */\n constructor(message: string) {\n super(message);\n this.name = \"InvalidWorkflowEventError\";\n }\n}\n\n/** Thrown when the envelope version is outside the SDK's supported range. */\nexport class UnsupportedEnvelopeVersionError extends Error {\n /** @param message - human-readable description of the unsupported version. */\n constructor(message: string) {\n super(message);\n this.name = \"UnsupportedEnvelopeVersionError\";\n }\n}\n","/**\n * Environment-variable name the construct's `grantConsumer` integration\n * injects into a consumer Lambda; the runtime `WorkflowDedupClient`\n * reads it to discover the shared dedup table without a prop or import.\n *\n * The constant is the single cross-package contract between\n * `@openhi/constructs` (which emits the env var) and `@openhi/workflows`\n * (which consumes it). Renaming or removing it is a breaking change.\n */\nexport const WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR =\n \"OPENHI_WORKFLOW_DEDUP_TABLE_NAME\";\n\n/** Default TTL for dedup rows: 14 days, expressed in seconds (per TR-015). */\nexport const WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS = 14 * 24 * 60 * 60;\n\n/** Maximum length of a `consumerName` (per TR-015). */\nexport const WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH = 64;\n","import {\n ConditionalCheckFailedException,\n DynamoDBClient,\n PutItemCommand,\n UpdateItemCommand,\n} from \"@aws-sdk/client-dynamodb\";\n\nimport {\n WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS,\n WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH,\n WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR,\n} from \"./env\";\n\n/**\n * Inputs to `recordIfAbsent`.\n *\n * `eventId` and `attempt` are the dedup tuple every retryable\n * consumer derives from the standard envelope (see `parseWorkflowEvent`\n * and the `DedupKey` type); call sites typically spread the dedupKey\n * directly alongside the consumer name.\n */\nexport interface RecordIfAbsentInput {\n /** Stable logical name of the consumer. At most 64 chars; no whitespace. */\n readonly consumerName: string;\n /** Per-event UUID from the standard envelope. */\n readonly eventId: string;\n /** 1-indexed delivery attempt from the standard envelope. */\n readonly attempt: number;\n /** Override the 14-day default TTL. Must be a positive integer. */\n readonly ttlSeconds?: number;\n}\n\n/**\n * Result shape per TR-015. `recorded` is true on first delivery and\n * false on a duplicate; on a duplicate `alreadyProcessed` is also\n * true so callers can pattern-match without re-checking the boolean.\n */\nexport type RecordIfAbsentResult =\n | { readonly recorded: true }\n | { readonly recorded: false; readonly alreadyProcessed: true };\n\n/**\n * Inputs to `markFailed`.\n *\n * Updates the existing dedup row with `failed: true`, `failureReason`,\n * `failedAt` so the replay tooling (TR-016 follow-up) can re-publish\n * the originating event with a fresh `attempt`.\n */\nexport interface MarkFailedInput {\n /** Stable logical name of the consumer. */\n readonly consumerName: string;\n /** Per-event UUID. */\n readonly eventId: string;\n /** 1-indexed delivery attempt. */\n readonly attempt: number;\n /** Short string describing why the consumer gave up. */\n readonly reason: string;\n}\n\n/**\n * Runtime SDK every retryable workflow consumer calls before\n * performing its side-effect. See TR-015 for the contract.\n */\nexport interface WorkflowDedupClient {\n /**\n * Conditionally record a dedup token for the supplied consumer name\n * and dedup tuple. See `RecordIfAbsentResult` for the return shape.\n */\n recordIfAbsent(input: RecordIfAbsentInput): Promise<RecordIfAbsentResult>;\n /**\n * Mark the existing dedup row as permanently failed. Fire-and-forget\n * semantics for the caller; unexpected DynamoDB errors propagate.\n */\n markFailed(input: MarkFailedInput): Promise<void>;\n}\n\n/** Options shared by the factory and the standalone primitives. */\nexport interface WorkflowDedupClientOptions {\n /**\n * Table name. Defaults to `process.env[WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR]`\n * (populated by the `WorkflowDedupTable` construct's `grantConsumer`).\n */\n readonly tableName?: string;\n /** Override the 14-day default TTL for every `recordIfAbsent` call. */\n readonly defaultTtlSeconds?: number;\n /** Test seam — deterministic clock for `recordedAt` / `expiresAt`. */\n readonly now?: () => Date;\n}\n\n/** Factory that returns a `WorkflowDedupClient` bound to a single DynamoDB client. */\nexport function workflowDedupClient(\n dynamodb: DynamoDBClient,\n options: WorkflowDedupClientOptions = {},\n): WorkflowDedupClient {\n return {\n recordIfAbsent: (input) => recordIfAbsent(dynamodb, input, options),\n markFailed: (input) => markFailed(dynamodb, input, options),\n };\n}\n\n/**\n * Standalone primitive — exposed for callers that prefer it over the\n * `WorkflowDedupClient` indirection.\n */\nexport async function recordIfAbsent(\n dynamodb: DynamoDBClient,\n input: RecordIfAbsentInput,\n options: WorkflowDedupClientOptions = {},\n): Promise<RecordIfAbsentResult> {\n assertConsumerName(input.consumerName);\n assertPositiveInteger(input.attempt, \"attempt\");\n const ttlSeconds =\n input.ttlSeconds ??\n options.defaultTtlSeconds ??\n WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS;\n if (!Number.isInteger(ttlSeconds) || ttlSeconds <= 0) {\n throw new WorkflowDedupInvalidInputError(\n `ttlSeconds must be a positive integer; got ${ttlSeconds}.`,\n );\n }\n\n const tableName = resolveTableName(options.tableName);\n const now = (options.now ?? defaultNow)();\n const sk = encodeSortKey(input.eventId, input.attempt);\n const expiresAt = Math.floor(now.getTime() / 1000) + ttlSeconds;\n\n try {\n await dynamodb.send(\n new PutItemCommand({\n TableName: tableName,\n Item: {\n consumerName: { S: input.consumerName },\n sk: { S: sk },\n eventId: { S: input.eventId },\n attempt: { N: String(input.attempt) },\n recordedAt: { S: now.toISOString() },\n expiresAt: { N: String(expiresAt) },\n },\n ConditionExpression:\n \"attribute_not_exists(consumerName) AND attribute_not_exists(sk)\",\n }),\n );\n return { recorded: true };\n } catch (err) {\n if (err instanceof ConditionalCheckFailedException) {\n return { recorded: false, alreadyProcessed: true };\n }\n throw err;\n }\n}\n\n/** Standalone primitive — flips `failed: true` on an existing dedup row. */\nexport async function markFailed(\n dynamodb: DynamoDBClient,\n input: MarkFailedInput,\n options: WorkflowDedupClientOptions = {},\n): Promise<void> {\n assertConsumerName(input.consumerName);\n assertPositiveInteger(input.attempt, \"attempt\");\n if (input.reason.length === 0) {\n throw new WorkflowDedupInvalidInputError(\"reason must be non-empty.\");\n }\n\n const tableName = resolveTableName(options.tableName);\n const now = (options.now ?? defaultNow)();\n const sk = encodeSortKey(input.eventId, input.attempt);\n\n await dynamodb.send(\n new UpdateItemCommand({\n TableName: tableName,\n Key: {\n consumerName: { S: input.consumerName },\n sk: { S: sk },\n },\n UpdateExpression:\n \"SET #failed = :failed, #failureReason = :reason, #failedAt = :failedAt\",\n ExpressionAttributeNames: {\n \"#failed\": \"failed\",\n \"#failureReason\": \"failureReason\",\n \"#failedAt\": \"failedAt\",\n },\n ExpressionAttributeValues: {\n \":failed\": { BOOL: true },\n \":reason\": { S: input.reason },\n \":failedAt\": { S: now.toISOString() },\n },\n }),\n );\n}\n\n/** Compose the composite sort key per the TR-015 encoding. */\nexport function encodeSortKey(eventId: string, attempt: number): string {\n if (eventId.length === 0) {\n throw new WorkflowDedupInvalidInputError(\"eventId must be non-empty.\");\n }\n return `${eventId}#${attempt}`;\n}\n\nfunction resolveTableName(explicit?: string): string {\n const name = explicit ?? process.env[WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR];\n if (!name) {\n throw new WorkflowDedupTableNameMissingError(\n `Workflow dedup table name not set. Pass options.tableName or set ${WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR}.`,\n );\n }\n return name;\n}\n\nfunction assertConsumerName(consumerName: string): void {\n if (consumerName.length === 0) {\n throw new WorkflowDedupInvalidInputError(\"consumerName must be non-empty.\");\n }\n if (consumerName.length > WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH) {\n throw new WorkflowDedupInvalidInputError(\n `consumerName must be ≤${WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH} chars; got ${consumerName.length}.`,\n );\n }\n if (/\\s/.test(consumerName)) {\n throw new WorkflowDedupInvalidInputError(\n \"consumerName must not contain whitespace.\",\n );\n }\n}\n\nfunction assertPositiveInteger(value: number, field: string): void {\n if (!Number.isInteger(value) || value < 1) {\n throw new WorkflowDedupInvalidInputError(\n `${field} must be a 1-indexed integer; got ${value}.`,\n );\n }\n}\n\nfunction defaultNow(): Date {\n return new Date();\n}\n\n/** Thrown when the dedup table name cannot be resolved. */\nexport class WorkflowDedupTableNameMissingError extends Error {\n /** @param message - human-readable description. */\n constructor(message: string) {\n super(message);\n this.name = \"WorkflowDedupTableNameMissingError\";\n }\n}\n\n/** Thrown when an input violates a TR-015 invariant. */\nexport class WorkflowDedupInvalidInputError extends Error {\n /** @param message - human-readable description. */\n constructor(message: string) {\n super(message);\n this.name = \"WorkflowDedupInvalidInputError\";\n }\n}\n","export {\n WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS,\n WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH,\n WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR,\n} from \"./env\";\nexport {\n WorkflowDedupInvalidInputError,\n WorkflowDedupTableNameMissingError,\n encodeSortKey,\n markFailed,\n recordIfAbsent,\n workflowDedupClient,\n} from \"./workflow-dedup-client\";\nexport type {\n MarkFailedInput,\n RecordIfAbsentInput,\n RecordIfAbsentResult,\n WorkflowDedupClient,\n WorkflowDedupClientOptions,\n} from \"./workflow-dedup-client\";\n","export {\n ENVELOPE_VERSION,\n isSupportedEnvelopeVersion,\n} from \"./envelope-version\";\nexport {\n MissingActorContextError,\n isWorkflowSystemActor,\n isWorkflowUserActor,\n workflowUserActorFromClaims,\n} from \"./envelope\";\nexport type {\n WorkflowActor,\n WorkflowEvent,\n WorkflowSystemActor,\n WorkflowUserActor,\n} from \"./envelope\";\nexport {\n DEFAULT_BUS_NAME_BY_SOURCE,\n OPENHI_CONTROL_SOURCE,\n OPENHI_DATA_SOURCE,\n OPENHI_OPS_SOURCE,\n} from \"./sources\";\nexport type { OpenHiSource } from \"./sources\";\nexport {\n InvalidDetailTypeRegistrationError,\n PlatformDeploymentCompletedV1,\n PlatformSystemDataSeededV1,\n defineDetailType,\n isWellFormedDetailType,\n} from \"./detail-types\";\nexport type {\n PlatformDeploymentCompletedV1Detail,\n PlatformSystemDataSeededV1Detail,\n WorkflowDetailTypeEntry,\n} from \"./detail-types\";\nexport {\n WorkflowPublishError,\n publishWorkflowEvent,\n workflowsClient,\n} from \"./publisher\";\nexport type {\n PublishContext,\n PublishResult,\n PublisherOptions,\n WorkflowsClient,\n} from \"./publisher\";\nexport {\n InvalidWorkflowEventError,\n UnsupportedEnvelopeVersionError,\n parseWorkflowEvent,\n} from \"./consumer\";\nexport type {\n DedupKey,\n EventBridgeEventLike,\n ParsedWorkflowEvent,\n} from \"./consumer\";\nexport {\n WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS,\n WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH,\n WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR,\n WorkflowDedupInvalidInputError,\n WorkflowDedupTableNameMissingError,\n encodeSortKey,\n markFailed,\n recordIfAbsent,\n workflowDedupClient,\n} from \"./dedup\";\nexport type {\n MarkFailedInput,\n RecordIfAbsentInput,\n RecordIfAbsentResult,\n WorkflowDedupClient,\n WorkflowDedupClientOptions,\n} from \"./dedup\";\n","/**\n * @see sites/www-docs/content/packages/@openhi/constructs/index.md\n */\n/** @openhi/constructs — root barrel exports. */\nexport * from \"./app\";\nexport * from \"./components\";\nexport * from \"./services\";\nexport * from \"./workflows\";\n","import {\n OPEN_HI_DEPLOYMENT_TARGET_ROLE,\n OPEN_HI_STAGE,\n OpenHiConfig,\n OpenHiEnvironmentConfig,\n} from \"@openhi/config\";\nimport { App, AppProps } from \"aws-cdk-lib\";\nimport { IConstruct } from \"constructs\";\nimport { OpenHiEnvironment } from \"./open-hi-environment\";\nimport { OpenHiStage } from \"./open-hi-stage\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/app/open-hi-app.md\n */\n\n/**\n * Symbol used for runtime type checking to identify OpenHiApp instances.\n *\n * @internal\n */\nconst OPEN_HI_APP_SYMBOL = Symbol.for(\"@openhi/constructs/core.OpenHiApp\");\n\n/**\n * Properties for creating an OpenHiApp instance.\n */\nexport interface OpenHiAppProps extends AppProps {\n /**\n * Optional name for the application.\n * ```\n */\n readonly appName?: string;\n\n /**\n * The OpenHi configuration object that defines stages, environments, and\n * their associated AWS account and region settings.\n */\n readonly config: OpenHiConfig;\n}\n\n/**\n * Root application construct for OpenHi CDK applications.\n */\nexport class OpenHiApp extends App {\n /**\n * Finds the OpenHiApp instance that contains the given construct in its\n * construct tree.\n */\n public static of(construct: IConstruct): OpenHiApp | undefined {\n return construct.node.scopes.reverse().find(OpenHiApp.isOpenHiApp);\n }\n\n /**\n * Type guard that checks if a value is an OpenHiApp instance.\n */\n public static isOpenHiApp(this: void, x: any): x is OpenHiApp {\n return x !== null && typeof x === \"object\" && OPEN_HI_APP_SYMBOL in x;\n }\n\n /**\n * Name for the application.\n */\n readonly appName: string;\n\n /**\n * The OpenHi configuration object for this application.\n */\n readonly config: OpenHiConfig;\n\n /**\n * Creates a new OpenHiApp instance.\n */\n constructor(props: OpenHiAppProps) {\n super(props);\n\n // Set runtime symbol for type checking\n Object.defineProperty(this, OPEN_HI_APP_SYMBOL, { value: true });\n\n // Store app name, defaulting to \"openhi\" if not provided\n this.appName = props.appName ?? \"openhi\";\n\n // Store configuration for use by child constructs\n this.config = props.config;\n\n // Create stages and environments based on configuration\n // Iterate through all possible stage types (dev, stage, prod)\n Object.values(OPEN_HI_STAGE).forEach((stageType) => {\n // Only create a stage if it's configured in the config\n if (this.config.deploymentTargets?.[stageType]) {\n const stage = new OpenHiStage(this, { stageType });\n\n // Create primary deployment target if configured\n // Each stage can have at most one primary deployment target\n if (\n this.config.deploymentTargets?.[stageType]?.[\n OPEN_HI_DEPLOYMENT_TARGET_ROLE.PRIMARY\n ]\n ) {\n const envConfig =\n this.config.deploymentTargets[stageType][\n OPEN_HI_DEPLOYMENT_TARGET_ROLE.PRIMARY\n ]!;\n new OpenHiEnvironment(stage, {\n deploymentTargetRole: OPEN_HI_DEPLOYMENT_TARGET_ROLE.PRIMARY,\n config: envConfig,\n env: { account: envConfig.account, region: envConfig.region },\n });\n }\n\n // Create secondary deployment targets if configured\n // Each stage can have zero or more secondary deployment targets\n if (\n this.config.deploymentTargets?.[stageType]?.[\n OPEN_HI_DEPLOYMENT_TARGET_ROLE.SECONDARY\n ]\n ) {\n this.config.deploymentTargets[stageType][\n OPEN_HI_DEPLOYMENT_TARGET_ROLE.SECONDARY\n ]!.forEach((envConfig: OpenHiEnvironmentConfig) => {\n new OpenHiEnvironment(stage, {\n deploymentTargetRole: OPEN_HI_DEPLOYMENT_TARGET_ROLE.SECONDARY,\n config: envConfig,\n env: { account: envConfig.account, region: envConfig.region },\n });\n });\n }\n }\n });\n }\n\n /*****************************************************************************\n *\n * Stages\n *\n ****************************************************************************/\n\n /**\n * Gets all OpenHiStage instances that are direct children of this app.\n\n */\n public get stages(): Array<OpenHiStage> {\n return this.node.children.filter(OpenHiStage.isOpenHiStage);\n }\n\n /**\n * Gets the development stage, if it exists.\n */\n public get devStage(): OpenHiStage | undefined {\n return this.stages.find((stage) => stage.stageType === OPEN_HI_STAGE.DEV);\n }\n\n /**\n * Gets the staging stage, if it exists.\n */\n public get stageStage(): OpenHiStage | undefined {\n return this.stages.find((stage) => stage.stageType === OPEN_HI_STAGE.STAGE);\n }\n\n /**\n * Gets the production stage, if it exists.\n */\n public get prodStage(): OpenHiStage | undefined {\n return this.stages.find((stage) => stage.stageType === OPEN_HI_STAGE.PROD);\n }\n\n /*****************************************************************************\n *\n * Environments\n *\n ****************************************************************************/\n\n /**\n * Gets all OpenHiEnvironment instances across all stages in this app.\n */\n public get environments(): Array<OpenHiEnvironment> {\n return this.stages.flatMap((stage) => stage.environments);\n }\n\n /**\n * Gets all primary environments across all stages in this app.\n */\n public get primaryEnvironments(): Array<OpenHiEnvironment> {\n return this.environments.filter(\n (env) => env.deploymentTargetRole === \"primary\",\n );\n }\n\n /**\n * Gets all secondary environments across all stages in this app.\n */\n public get secondaryEnvironments(): Array<OpenHiEnvironment> {\n return this.environments.filter(\n (env) => env.deploymentTargetRole === \"secondary\",\n );\n }\n}\n","import {\n OPEN_HI_DEPLOYMENT_TARGET_ROLE,\n OpenHiEnvironmentConfig,\n} from \"@openhi/config\";\nimport { Stage, StageProps } from \"aws-cdk-lib\";\nimport { IConstruct } from \"constructs\";\nimport { OpenHiStage } from \"./open-hi-stage\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/app/open-hi-environment.md\n */\n\n/**\n * Symbol used to identify OpenHiEnvironment instances at runtime.\n */\nconst OPEN_HI_ENVIRONMENT_SYMBOL = Symbol.for(\n \"@openhi/constructs/core.OpenHiEnvironment\",\n);\n\n/**\n * Properties for creating an OpenHiEnvironment.\n */\nexport interface OpenHiEnvironmentProps extends StageProps {\n /**\n * The deployment target role for this (account, region).\n */\n readonly deploymentTargetRole: (typeof OPEN_HI_DEPLOYMENT_TARGET_ROLE)[keyof typeof OPEN_HI_DEPLOYMENT_TARGET_ROLE];\n\n /**\n * Configuration for this specific environment.\n */\n readonly config: OpenHiEnvironmentConfig;\n}\n\n/**\n * Represents an OpenHi environment within an AWS CDK stage.\n */\nexport class OpenHiEnvironment extends Stage {\n /**\n * Finds the OpenHiEnvironment that contains the given construct.\n * ```\n */\n public static of(construct: IConstruct): OpenHiEnvironment | undefined {\n return construct.node.scopes\n .reverse()\n .find(OpenHiEnvironment.isOpenHiEnvironment);\n }\n\n /**\n * Type guard to check if a value is an OpenHiEnvironment instance.\n */\n public static isOpenHiEnvironment(\n this: void,\n x: any,\n ): x is OpenHiEnvironment {\n return (\n x !== null && typeof x === \"object\" && OPEN_HI_ENVIRONMENT_SYMBOL in x\n );\n }\n\n /**\n * Configuration for this specific environment.\n */\n readonly config: OpenHiEnvironmentConfig;\n\n /**\n * The deployment target role for this (account, region).\n */\n public readonly deploymentTargetRole: (typeof OPEN_HI_DEPLOYMENT_TARGET_ROLE)[keyof typeof OPEN_HI_DEPLOYMENT_TARGET_ROLE];\n\n /**\n * Creates a new OpenHiEnvironment.\n */\n constructor(\n /**\n * The OpenHiStage that contains this environment.\n */\n public ohStage: OpenHiStage,\n /**\n * Properties for creating the environment.\n */\n public props: OpenHiEnvironmentProps,\n ) {\n // Copy account and region from config into env, if provided.\n // This allows all resources in this environment to default to the correct\n // account and region without having to specify it on each stack or resource.\n if (props.config.account && props.config.region) {\n props = {\n ...props,\n env: {\n account: props.config.account,\n region: props.config.region,\n },\n };\n }\n\n // Determine the stage name:\n // - Primary environments use the environment type as the name\n // - Secondary deployment targets use \"{deploymentTargetRole}-{index}\" format\n const stageName =\n props.deploymentTargetRole === OPEN_HI_DEPLOYMENT_TARGET_ROLE.PRIMARY\n ? props.deploymentTargetRole\n : [props.deploymentTargetRole, ohStage.environments.length].join(\"-\");\n\n super(ohStage, stageName, {\n env: props.env ?? ohStage.props.env,\n ...props,\n });\n\n // Mark this instance as an OpenHiEnvironment for runtime type checking\n Object.defineProperty(this, OPEN_HI_ENVIRONMENT_SYMBOL, { value: true });\n\n this.deploymentTargetRole = props.deploymentTargetRole;\n this.config = props.config;\n }\n}\n","import { OPEN_HI_STAGE } from \"@openhi/config\";\nimport { Stage, StageProps } from \"aws-cdk-lib\";\nimport { IConstruct } from \"constructs\";\nimport { OpenHiApp } from \"./open-hi-app\";\nimport { OpenHiEnvironment } from \"./open-hi-environment\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/app/open-hi-stage.md\n */\n\n/**\n * Symbol used to identify OpenHiStage instances at runtime.\n *\n * @internal\n */\nconst OPEN_HI_STAGE_SYMBOL = Symbol.for(\"@openhi/constructs/core.OpenHiStage\");\n\n/**\n * Properties for creating an OpenHiStage instance.\n */\nexport interface OpenHiStageProps extends StageProps {\n /**\n * The type of the OpenHi stage.\n */\n readonly stageType: (typeof OPEN_HI_STAGE)[keyof typeof OPEN_HI_STAGE];\n}\n\n/**\n * Represents a deployment stage in the OpenHi infrastructure hierarchy.\n */\nexport class OpenHiStage extends Stage {\n /**\n * Finds the OpenHiStage that contains the given construct.\n */\n public static of(construct: IConstruct): OpenHiStage | undefined {\n return construct.node.scopes.reverse().find(OpenHiStage.isOpenHiStage);\n }\n\n /**\n * Type guard to check if a value is an OpenHiStage instance.\n */\n public static isOpenHiStage(this: void, x: any): x is OpenHiStage {\n return x !== null && typeof x === \"object\" && OPEN_HI_STAGE_SYMBOL in x;\n }\n\n /**\n * The type of this OpenHi stage.\n */\n public readonly stageType: (typeof OPEN_HI_STAGE)[keyof typeof OPEN_HI_STAGE];\n\n /**\n * Creates a new OpenHiStage instance.\n */\n constructor(\n /**\n * The OpenHiApp that this stage belongs to.\n *\n * @public\n */\n public ohApp: OpenHiApp,\n\n /**\n * Properties for configuring the stage.\n *\n * @public\n */\n public props: OpenHiStageProps,\n ) {\n super(ohApp, props.stageType, props);\n\n Object.defineProperty(this, OPEN_HI_STAGE_SYMBOL, { value: true });\n\n this.stageType = props.stageType;\n }\n\n /**\n * Gets all OpenHiEnvironment instances contained within this stage.\n */\n public get environments(): Array<OpenHiEnvironment> {\n return this.node.children.filter(OpenHiEnvironment.isOpenHiEnvironment);\n }\n\n /**\n * Gets the primary OpenHiEnvironment for this stage, if one exists.\n */\n public get primaryEnvironment(): OpenHiEnvironment | undefined {\n return this.environments.find(\n (env) => env.deploymentTargetRole === \"primary\",\n );\n }\n\n /**\n * Gets all secondary OpenHiEnvironment instances for this stage.\n */\n public get secondaryEnvironments(): Array<OpenHiEnvironment> {\n return this.environments.filter(\n (env) => env.deploymentTargetRole === \"secondary\",\n );\n }\n}\n","import {\n findGitBranch,\n findGitRepoName,\n hashString,\n} from \"@codedrifters/utils\";\nimport { OPEN_HI_STAGE, OpenHiEnvironmentConfig } from \"@openhi/config\";\nimport { RemovalPolicy, Stack, StackProps, Tags } from \"aws-cdk-lib\";\nimport { paramCase } from \"change-case\";\nimport { OpenHiEnvironment } from \"./open-hi-environment\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/app/open-hi-service.md\n */\n\n/**\n * Known OpenHI service type strings. Each service class defines its own\n * static SERVICE_TYPE (e.g. OpenHiAuthService.SERVICE_TYPE === \"auth\").\n *\n * @public\n */\nexport type OpenHiServiceType =\n | \"auth\"\n | \"rest-api\"\n | \"data\"\n | \"global\"\n | \"graphql-api\";\n\n/**\n * Tag-key suffixes applied by every OpenHiService stack via Tags.of().\n * Full keys are composed `${appName}:${suffix}` — see {@link openHiTagKey}.\n * Consumers that filter or project these tags (e.g. the platform-deploy\n * bridge) import these suffixes rather than redeclaring the strings.\n *\n * @public\n */\nexport const OPENHI_TAG_SUFFIX_REPO_NAME = \"repo-name\";\n/** @public */\nexport const OPENHI_TAG_SUFFIX_BRANCH_NAME = \"branch-name\";\n/** @public */\nexport const OPENHI_TAG_SUFFIX_SERVICE_TYPE = \"service-type\";\n/** @public */\nexport const OPENHI_TAG_SUFFIX_STAGE_TYPE = \"stage-type\";\n\n/**\n * Compose a full stack-tag key from an `appName` and a suffix from\n * {@link OPENHI_TAG_SUFFIX_REPO_NAME} et al.\n *\n * @public\n */\nexport const openHiTagKey = (appName: string, suffix: string): string =>\n `${appName}:${suffix}`;\n\n/**\n * Properties for creating an {@link OpenHiService} stack.\n *\n * @public\n */\nexport interface OpenHiServiceProps extends StackProps {\n /**\n * Optional branch name override.\n */\n readonly branchName?: string;\n\n /**\n * Optional repository name override.\n */\n readonly repoName?: string;\n\n /**\n * Optional application name override.\n */\n readonly appName?: string;\n\n /**\n * Default release branch name.\n */\n readonly defaultReleaseBranch?: string;\n\n /**\n * The removal policy for persistent stack resources.\n */\n readonly removalPolicy?: RemovalPolicy;\n\n /**\n * Environment configuration for this service.\n */\n readonly config?: OpenHiEnvironmentConfig;\n\n /**\n * A constant that identifies the service type.\n */\n readonly serviceType?: OpenHiServiceType;\n}\n\n/**\n * Represents an OpenHI service stack within the OpenHI platform.\n * Subclasses must override {@link serviceType} to return their static SERVICE_TYPE.\n */\nexport abstract class OpenHiService extends Stack {\n /**\n * The service/stack ID that was passed to the constructor.\n */\n public readonly serviceId: string;\n\n /**\n * The deployment target role identifier.\n */\n public readonly deploymentTargetRole: string;\n\n /**\n * Repository name used in resource tagging.\n */\n public readonly repoName: string;\n\n /**\n * Application name identifier.\n */\n public readonly appName: string;\n\n /**\n * Default release branch name.\n */\n public readonly defaultReleaseBranch: string;\n\n /**\n * Branch name used when calculating resource names and hashes.\n */\n public readonly branchName: string;\n\n /**\n * Short hash unique to the deployment target (app name, deployment target role, account, region).\n */\n public readonly environmentHash: string;\n\n /**\n * Short hash unique to the environment and branch combination.\n */\n public readonly branchHash: string;\n\n /**\n * Short hash unique to the specific stack/service.\n */\n public readonly stackHash: string;\n\n /**\n * The removal policy for persistent stack resources.\n */\n public readonly removalPolicy: RemovalPolicy;\n\n /**\n * Environment configuration for this service.\n * This is either the value passed in or the default config\n */\n public readonly config: OpenHiEnvironmentConfig;\n\n /**\n * Service type identifier. Override in subclasses to return the class's static SERVICE_TYPE.\n * Used for parameter names, tags, and service discovery.\n */\n abstract get serviceType(): OpenHiServiceType | string;\n\n /**\n * Creates a new OpenHI service stack.\n *\n * @param ohEnv - The OpenHI environment (stage) this service belongs to\n * @param id - Unique identifier for this service stack (e.g., \"user-service\")\n * @param props - Optional properties for configuring the service\n *\n * @throws {Error} If account and region are not defined in props or environment\n *\n */\n constructor(\n public ohEnv: OpenHiEnvironment,\n id: string,\n public props: OpenHiServiceProps = {},\n ) {\n // Determine the account and region based on environment or user passed props.\n // This must be done before calling super() as it's needed for stack naming.\n const { account, region } = props.env || ohEnv;\n if (!account || !region) {\n throw new Error(\n \"Account and region must be defined in OpenHiServiceProps or OpenHiEnvironment\",\n );\n }\n\n // Get app name from the app in the hierarchy (via environment -> stage -> app)\n const appName = props.appName ?? ohEnv.ohStage.ohApp.appName ?? \"openhi\";\n\n // Initialize deployment context properties\n // Repo name is used in tagging. This tag value is important for tracking\n // when tearing preview stacks back down. If not provided, detect from git.\n const repoName = props.repoName ?? findGitRepoName();\n\n // Default release branch is used when not in dev stage. Defaults to \"main\" if not provided.\n const defaultReleaseBranch = props.defaultReleaseBranch ?? \"main\";\n\n // Branch name is used to calculate hashes and names for resources.\n // Detection logic:\n // - If explicitly provided, use that value\n // - If Jest is running, use \"test-branch\" to avoid snapshot test issues\n // - If GIT_BRANCH_NAME env is set (e.g. by CI), use it\n // - If in dev stage, detect from git using findGitBranch()\n // - Otherwise (stage/prod), default to defaultReleaseBranch\n const branchName =\n props.branchName ??\n (process.env.JEST_WORKER_ID\n ? \"test-branch\"\n : process.env.GIT_BRANCH_NAME?.trim() ||\n (ohEnv.ohStage.stageType === OPEN_HI_STAGE.DEV\n ? findGitBranch()\n : defaultReleaseBranch));\n\n // Compute environment hash: unique to deployment target (app name, role, account, region)\n // Mainly used for DNS names and deployment-target-scoped resources\n const environmentHash = hashString(\n [appName, ohEnv.deploymentTargetRole, account, region].join(\"-\"),\n 6,\n );\n\n // Compute branch hash: unique to deployment target and branch combination\n // Useful for resources shared across stacks within the same branch\n const branchHash = hashString(\n [appName, ohEnv.deploymentTargetRole, account, region, branchName].join(\n \"-\",\n ),\n 6,\n );\n\n // Compute stack hash: unique to the specific stack/service\n // Useful for stack-specific resources like S3 buckets, KMS key aliases\n // This ensures two PR builds or different services don't collide\n const stackHash = hashString(\n [\n appName,\n ohEnv.deploymentTargetRole,\n account,\n region,\n branchName,\n id,\n ].join(\"-\"),\n 6,\n );\n\n // Set the removal policy for this stack based on the deployment target role.\n // Production stages retain resources, others destroy them on stack deletion.\n const removalPolicy =\n props.removalPolicy ??\n (ohEnv.ohStage.stageType === OPEN_HI_STAGE.PROD\n ? RemovalPolicy.RETAIN\n : RemovalPolicy.DESTROY);\n Object.assign(props, { removalPolicy });\n\n // Description to use for the stack and all resources within it.\n // Includes service ID, branch name, and hash for easy identification.\n const description = `OpenHi Service: ${id} [${branchName}] - ${branchHash}`;\n\n // Call the super constructor of Stack.\n // This initializes the AWS CDK Stack with:\n // - Scope: the OpenHI environment\n // - ID: unique stack name including branch hash\n // - Props: stack properties including description and removal policy\n super(ohEnv, [branchHash, id, account, region].join(\"-\"), {\n ...props,\n description,\n });\n\n // Store the service ID for use in deployment context and other operations.\n this.serviceId = id;\n\n // Set the removal policy for this stack based on the deployment target role.\n this.removalPolicy = removalPolicy;\n\n /**\n * Explicit config or use the environment config as a backup,\n */\n this.config = props.config ?? ohEnv.props.config;\n\n // Initialize deployment context properties directly on the service\n this.deploymentTargetRole = ohEnv.deploymentTargetRole;\n this.repoName = repoName;\n this.appName = appName;\n this.defaultReleaseBranch = defaultReleaseBranch;\n this.branchName = branchName;\n this.environmentHash = environmentHash;\n this.branchHash = branchHash;\n this.stackHash = stackHash;\n\n // Pre-populate the AZ context cache for this stack so any construct that\n // calls `stack.availabilityZones` (notably RDS DatabaseCluster, ELBs, and\n // anything else that fans out across AZs) gets concrete values without\n // triggering a CDK context lookup. Without this, CI synth records the\n // lookup as \"missing\" and deploy fails because the GitHubOpenHiDeployer\n // role can't assume `cdk-…-lookup-role-…` (only granted to dev machines\n // by default). AZ names follow the stable `<region>a/b/c…` pattern across\n // all current commercial AWS regions; if a deployment ever targets a\n // region where this assumption breaks, override here per region.\n this.node.setContext(\n `availability-zones:account=${account}:region=${region}`,\n [`${region}a`, `${region}b`, `${region}c`],\n );\n\n // Standard tagging across all resources in the stack.\n // Use id (the service type string passed to super) since abstract serviceType cannot be accessed in constructor.\n Tags.of(this).add(\n openHiTagKey(appName, OPENHI_TAG_SUFFIX_REPO_NAME),\n repoName.slice(0, 255),\n );\n Tags.of(this).add(\n openHiTagKey(appName, OPENHI_TAG_SUFFIX_BRANCH_NAME),\n branchName.slice(0, 255),\n );\n Tags.of(this).add(\n openHiTagKey(appName, OPENHI_TAG_SUFFIX_SERVICE_TYPE),\n id.slice(0, 255),\n );\n Tags.of(this).add(\n openHiTagKey(appName, OPENHI_TAG_SUFFIX_STAGE_TYPE),\n ohEnv.ohStage.stageType.slice(0, 255),\n );\n }\n\n /**\n * DNS prefix for this branche's child zone.\n */\n public get childZonePrefix(): string {\n return paramCase(this.branchName).slice(0, 200);\n }\n}\n","import {\n Certificate,\n CertificateProps,\n} from \"aws-cdk-lib/aws-certificatemanager\";\nimport { StringParameter } from \"aws-cdk-lib/aws-ssm\";\nimport { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/acm/root-wildcard-certificate.md\n */\n\nexport class RootWildcardCertificate extends Certificate {\n /**\n * Used when storing the Certificate ARN in SSM.\n */\n public static readonly SSM_PARAM_NAME = \"ROOT_WILDCARD_CERT_ARN\";\n\n /**\n * Using a special name here since this will be shared and used among many\n * stacks and services. Use with OpenHiGlobalService.rootWildcardCertificateFromConstruct.\n */\n public static ssmParameterName(): string {\n return (\n \"/\" +\n [\"GLOBAL\", RootWildcardCertificate.SSM_PARAM_NAME].join(\"/\").toUpperCase()\n );\n }\n\n constructor(scope: Construct, props: CertificateProps) {\n super(scope, \"root-wildcard-certificate\", { ...props });\n\n /**\n * Generate the SSM Parameter used to store this Certificate's ARN.\n */\n new StringParameter(this, \"wildcard-cert-param\", {\n parameterName: RootWildcardCertificate.ssmParameterName(),\n stringValue: this.certificateArn,\n });\n }\n}\n","import { HttpApi, HttpApiProps } from \"aws-cdk-lib/aws-apigatewayv2\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app/open-hi-service\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/api-gateway/root-http-api.md\n */\n\nexport interface RootHttpApiProps extends HttpApiProps {}\n\nexport class RootHttpApi extends HttpApi {\n /**\n * Used when storing the API ID in SSM.\n */\n public static readonly SSM_PARAM_NAME = \"ROOT_HTTP_API\";\n\n constructor(scope: Construct, props: RootHttpApiProps = {}) {\n const stack = OpenHiService.of(scope) as OpenHiService;\n\n const origins = props.corsPreflight?.allowOrigins;\n if (origins?.length) {\n const withTrailingSlash = origins.filter((o) => o.endsWith(\"/\"));\n if (withTrailingSlash.length > 0) {\n throw new Error(\n `CORS allowOrigins must not include a trailing slash. The browser Origin header is scheme + host + port only (no path), so API Gateway will not match origins like \"https://example.com/\" and CORS can fail. Invalid: ${withTrailingSlash.join(\", \")}. Use e.g. \"https://example.com\" instead.`,\n );\n }\n }\n\n super(scope, \"http-api\", {\n /**\n * User provided props\n */\n ...props,\n\n /**\n * Required\n */\n apiName: [\"root\", \"http\", \"api\", stack.branchHash].join(\"-\"),\n });\n }\n}\n","import {\n Definition,\n GraphqlApi,\n GraphqlApiProps,\n IGraphqlApi,\n} from \"aws-cdk-lib/aws-appsync\";\nimport { CodeFirstSchema, GraphqlType, ObjectType } from \"awscdk-appsync-utils\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app\";\nimport { DiscoverableStringParameter } from \"../ssm\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/app-sync/root-graphql-api.md\n */\n\nexport interface RootGraphqlApiProps extends GraphqlApiProps {}\n\nexport class RootGraphqlApi extends GraphqlApi {\n /**\n * Used when storing the GraphQl API ID in SSM\n */\n public static readonly SSM_PARAM_NAME = \"ROOT_GRAPHQL_API\";\n\n public static fromConstruct(scope: Construct): IGraphqlApi {\n const graphqlApiId = DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: RootGraphqlApi.SSM_PARAM_NAME,\n serviceType: \"graphql-api\",\n });\n\n return GraphqlApi.fromGraphqlApiAttributes(scope, \"root-graphql-api\", {\n graphqlApiId,\n });\n }\n\n constructor(scope: Construct, props?: Omit<RootGraphqlApiProps, \"name\">) {\n const stack = OpenHiService.of(scope) as OpenHiService;\n\n const schema = new CodeFirstSchema();\n schema.addType(\n new ObjectType(\"Query\", {\n definition: { HelloWorld: GraphqlType.string() },\n }),\n );\n\n super(scope, \"root-graphql-api\", {\n /**\n * Defaults\n */\n queryDepthLimit: 2,\n resolverCountLimit: 50,\n definition: Definition.fromSchema(schema),\n\n /**\n * Overrideable props\n */\n ...props,\n\n /**\n * Required\n */\n name: [\"root\", \"graphql\", \"api\", stack.branchHash].join(\"-\"),\n });\n\n /**\n * Generate the SSM Parameter used to store this GraphQL API's ID.\n */\n new DiscoverableStringParameter(this, \"graphql-api-param\", {\n ssmParamName: RootGraphqlApi.SSM_PARAM_NAME,\n serviceType: \"graphql-api\",\n stringValue: this.apiId,\n });\n }\n}\n","import { Tags } from \"aws-cdk-lib\";\nimport {\n StringParameter,\n type StringParameterProps,\n} from \"aws-cdk-lib/aws-ssm\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/ssm/discoverable-string-parameter.md\n */\n\n/*******************************************************************************\n *\n * DiscoverableStringParameterProps: props for creating or looking up SSM\n * parameters. Includes StringParameterProps (minus parameterName) plus\n * name-building fields used by buildParameterName.\n *\n ******************************************************************************/\n\nexport interface DiscoverableStringParameterProps extends Omit<\n StringParameterProps,\n \"parameterName\"\n> {\n /**\n * SSM param name used to build the SSM parameter name via buildParameterName\n * and stored as a tag on the parameter for discoverability.\n */\n readonly ssmParamName: string;\n\n /**\n * The environment hash the parameter belongs to.\n * @default - the current stack's environment hash\n */\n readonly branchHash?: string;\n\n /**\n * The service type the parameter belongs to.\n * @default - the current stack's service type\n */\n readonly serviceType?: string;\n\n /**\n * The AWS account the parameter belongs to.\n * @default - the current stack's account\n */\n readonly account?: string;\n\n /**\n * The AWS region the parameter belongs to.\n * @default - the current stack's region\n */\n readonly region?: string;\n}\n\n/**\n * Props for buildParameterName and valueForLookupName.\n * Includes ssmParamName (required) and optional overrides (branchHash, serviceType, account, region).\n */\nexport type BuildParameterNameProps = Pick<\n DiscoverableStringParameterProps,\n \"ssmParamName\" | \"branchHash\" | \"serviceType\" | \"account\" | \"region\"\n>;\n\n/**\n * Discoverable SSM string parameter construct. Extends CDK StringParameter:\n * builds parameterName from the given name via buildParameterName and tags\n * the parameter with the name constant.\n */\nexport class DiscoverableStringParameter extends StringParameter {\n /**\n * Version of the parameter name format / discoverability schema.\n * Bump when buildParameterName or tagging semantics change.\n * Also used to drive replacement of parameters during CloudFormation deploys.\n */\n public static readonly version = \"v1\";\n\n /**\n * Build a param name based on predictable attributes found in services and\n * constructs. Used for storage and retrieval of SSM values across services.\n */\n public static buildParameterName(\n scope: Construct,\n props: BuildParameterNameProps,\n ): string {\n const stack = OpenHiService.of(scope) as OpenHiService;\n return (\n \"/\" +\n [\n DiscoverableStringParameter.version,\n props.branchHash ?? stack.branchHash,\n props.serviceType ?? stack.serviceType,\n props.account ?? stack.account,\n props.region ?? stack.region,\n props.ssmParamName,\n ]\n .join(\"/\")\n .toUpperCase()\n );\n }\n\n /**\n * Read the string value of an SSM parameter created with DiscoverableStringParameter,\n * using props that include ssmParamName and optional overrides (e.g. serviceType).\n */\n public static valueForLookupName(\n scope: Construct,\n props: BuildParameterNameProps,\n ): string {\n const paramName = DiscoverableStringParameter.buildParameterName(\n scope,\n props,\n );\n return StringParameter.valueForStringParameter(scope, paramName);\n }\n\n constructor(\n scope: Construct,\n id: string,\n props: DiscoverableStringParameterProps,\n ) {\n const { ssmParamName, branchHash, serviceType, account, region, ...rest } =\n props;\n\n const parameterName = DiscoverableStringParameter.buildParameterName(\n scope,\n props,\n );\n\n super(scope, id + \"-\" + DiscoverableStringParameter.version, {\n ...rest,\n parameterName,\n });\n\n const { appName } = OpenHiService.of(scope) as OpenHiService;\n Tags.of(this).add(`${appName}:param-name`, ssmParamName);\n }\n}\n","import { Duration } from \"aws-cdk-lib\";\nimport {\n IUserPool,\n UserPoolClient,\n UserPoolClientProps,\n} from \"aws-cdk-lib/aws-cognito\";\nimport { Construct } from \"constructs\";\n\nexport interface CognitoFixtureSeederClientProps extends Partial<\n Omit<UserPoolClientProps, \"userPool\" | \"generateSecret\">\n> {\n readonly userPool: IUserPool;\n}\n\n/**\n * Dedicated Cognito app client for the OpenHI fixture-seeder CLI\n * (`@openhi/seed-fixtures`).\n *\n * Why a dedicated client (vs reusing the SPA client):\n * - Tightly scoped: only the seeder consumes tokens issued here, so an\n * audit trail of seeder activity is cleanly separable.\n * - Decoupled from the SPA client's OAuth flows — no risk of breaking\n * web-app sign-in by tweaking auth-flow settings here.\n * - Stage-conditional creation upstream (only provisioned in non-prod\n * environments) means prod stacks never carry a code path that could\n * issue a fixture-seeder token in the first place.\n *\n * Why USER_PASSWORD_AUTH (vs M2M client-credentials):\n * - Cognito's M2M tier has a per-app-client monthly fee plus per-token\n * activity charges. For sporadic non-prod fixture runs the per-client\n * fee dominates the bill, especially if every dev branch spins up\n * its own auth stack.\n * - USER_PASSWORD_AUTH against a service `fixture-seeder` user keeps\n * the cost in MAU territory (free under the 50K MAU tier).\n * - Tradeoff: passwords need rotation and the service user must be\n * provisioned per non-prod environment (manual or scripted post-deploy).\n *\n * No client secret (`generateSecret: false`): USER_PASSWORD_AUTH\n * authenticates with the password directly; a secret would just add\n * another credential to manage without strengthening anything.\n */\nexport class CognitoFixtureSeederClient extends UserPoolClient {\n /**\n * SSM parameter name suffix used to publish this client's ID for\n * cross-stack lookups. Built into a full parameter name via\n * `buildParameterName` with `serviceType` AUTH (since the auth stack\n * owns this resource).\n */\n public static readonly SSM_PARAM_NAME = \"COGNITO_FIXTURE_SEEDER_CLIENT\";\n\n constructor(scope: Construct, props: CognitoFixtureSeederClientProps) {\n const { userPool, ...rest } = props;\n super(scope, \"fixture-seeder-client\", {\n userPool,\n generateSecret: false,\n authFlows: {\n userPassword: true,\n },\n // No OAuth flows — the seeder calls Cognito's `InitiateAuth`\n // directly with USER_PASSWORD_AUTH, not through the hosted-UI\n // OAuth grant flows the SPA client uses. `disableOAuth: true`\n // causes CDK to omit `AllowedOAuthFlowsUserPoolClient` entirely;\n // passing an empty `oAuth` block instead still flips that flag on\n // and Cognito rejects the create call for missing flows/scopes.\n disableOAuth: true,\n // Short-lived tokens: a seeder run takes seconds, not hours.\n // 1h access-token validity is the minimum Cognito permits and is\n // plenty for a fixture run.\n accessTokenValidity: Duration.hours(1),\n idTokenValidity: Duration.hours(1),\n refreshTokenValidity: Duration.days(1),\n preventUserExistenceErrors: true,\n ...rest,\n });\n }\n}\n","import {\n FeaturePlan,\n UserPool,\n UserPoolProps,\n VerificationEmailStyle,\n} from \"aws-cdk-lib/aws-cognito\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app/open-hi-service\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/cognito/cognito-user-pool.md\n */\n\nexport class CognitoUserPool extends UserPool {\n /**\n * Used when storing the User Pool ID in SSM.\n */\n public static readonly SSM_PARAM_NAME = \"COGNITO_USER_POOL\";\n\n constructor(scope: Construct, props: UserPoolProps = {}) {\n const service = OpenHiService.of(scope) as OpenHiService;\n\n super(scope, \"user-pool\", {\n /**\n * Defaults\n */\n selfSignUpEnabled: true,\n signInAliases: {\n email: true,\n },\n userVerification: {\n emailSubject: \"Verify your email!\",\n emailBody: \"Your verification code is {####}.\",\n emailStyle: VerificationEmailStyle.CODE,\n },\n removalPolicy: props.removalPolicy ?? service.removalPolicy,\n // Plus is required for access-token V2 claim customization in the\n // pre-token-generation Lambda. Essentials silently drops\n // claimsAndScopeOverrideDetails.accessTokenGeneration.claimsToAddOrOverride.\n featurePlan: FeaturePlan.PLUS,\n\n /**\n * Over-rideable props\n */\n ...props,\n\n /**\n * Required\n */\n userPoolName: [\"cognito\", \"user\", \"pool\", service.branchHash].join(\"-\"),\n });\n }\n}\n","import { UserPoolClient, UserPoolClientProps } from \"aws-cdk-lib/aws-cognito\";\nimport { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/cognito/cognito-user-pool-client.md\n */\n\nexport class CognitoUserPoolClient extends UserPoolClient {\n /**\n * Used when storing the User Pool Client ID in SSM.\n */\n public static readonly SSM_PARAM_NAME = \"COGNITO_USER_POOL_CLIENT\";\n\n constructor(scope: Construct, props: UserPoolClientProps) {\n super(scope, \"user-pool-client\", {\n /**\n * Defaults\n */\n generateSecret: false,\n oAuth: {\n flows: {\n authorizationCodeGrant: true,\n implicitCodeGrant: true,\n },\n callbackUrls: [`https://localhost:3000/oauth/callback`],\n },\n\n /**\n * Overrideable props\n */\n ...props,\n });\n }\n}\n","import { UserPoolDomain, UserPoolDomainProps } from \"aws-cdk-lib/aws-cognito\";\nimport { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/cognito/cognito-user-pool-domain.md\n */\n\nexport class CognitoUserPoolDomain extends UserPoolDomain {\n /**\n * Used when storing the User Pool Domain in SSM.\n */\n public static readonly SSM_PARAM_NAME = \"COGNITO_USER_POOL_DOMAIN\";\n\n constructor(scope: Construct, props: UserPoolDomainProps) {\n /**\n * This supports both custom and native Cognito domains, but we need to\n * name them uniquely so that swap outs work and don't cause conflicts\n * when cloudformation does it's deploy.\n */\n const id = props.cognitoDomain?.domainPrefix\n ? \"cognito-domain\"\n : \"custom-domain\";\n\n super(scope, id, {\n ...props,\n });\n }\n}\n","import { Key, KeyProps } from \"aws-cdk-lib/aws-kms\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app/open-hi-service\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/cognito/cognito-user-pool-kms-key.md\n */\n\nexport class CognitoUserPoolKmsKey extends Key {\n /**\n * Used when storing the KMS Key in SSM.\n */\n public static readonly SSM_PARAM_NAME = \"COGNITO_USER_POOL_KMS_KEY\";\n\n constructor(scope: Construct, props: KeyProps = {}) {\n const service = OpenHiService.of(scope) as OpenHiService;\n\n super(scope, \"kms-key\", {\n ...props,\n // alias: [\"alias\", \"cognito\", service.branchHash].join(\"/\"),\n description: `KMS Key for Cognito User Pool - ${service.branchHash}`,\n removalPolicy: props.removalPolicy ?? service.removalPolicy,\n });\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/cognito/post-authentication-lambda.md\n */\n\nconst HANDLER_NAME = \"post-authentication.handler.js\";\n\n/**\n * Resolve Lambda entry so it works when running from src/ (tests) or from lib/ (built).\n */\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n\n const fromLib = path.join(dirname, \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n return fromLib;\n}\n\n/**\n * Lambda used as Cognito Post Authentication trigger.\n */\nexport class PostAuthenticationLambda extends Construct {\n public readonly lambda: NodejsFunction;\n\n constructor(scope: Construct) {\n super(scope, \"post-authentication-lambda\");\n\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 1024,\n });\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/cognito/post-confirmation-lambda.md\n */\n\nconst HANDLER_NAME = \"post-confirmation.handler.js\";\n\n/**\n * Resolve Lambda entry so it works when running from src/ (tests) or from lib/ (built).\n */\nconst resolveHandlerEntry = (dirname: string): string => {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n\n return path.join(dirname, \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n};\n\nexport interface PostConfirmationLambdaProps {\n /**\n * Control-plane EventBridge bus name. Passed to the Lambda as\n * CONTROL_EVENT_BUS_NAME so it can publish onboarding workflow events.\n */\n readonly controlEventBusName: string;\n}\n\n/**\n * Lambda used as Cognito Post Confirmation trigger. It publishes a control\n * event and returns quickly; workflow Lambdas own provisioning.\n */\nexport class PostConfirmationLambda extends Construct {\n public readonly lambda: NodejsFunction;\n\n constructor(scope: Construct, props: PostConfirmationLambdaProps) {\n super(scope, \"post-confirmation-lambda\");\n\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 1024,\n environment: {\n CONTROL_EVENT_BUS_NAME: props.controlEventBusName,\n },\n });\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/cognito/pre-token-generation-lambda.md\n */\n\nconst HANDLER_NAME = \"pre-token-generation.handler.js\";\n\n/**\n * Resolve Lambda entry so it works when running from src/ (tests) or from lib/ (built).\n */\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n\n const fromLib = path.join(dirname, \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n return fromLib;\n}\n\nexport interface PreTokenGenerationLambdaProps {\n /**\n * DynamoDB data store table name. Passed to the Lambda as DYNAMO_TABLE_NAME\n * so the control-plane ElectroDB service reads the User by Cognito `sub`\n * (GSI2) and the user's first active Membership (fallback path).\n */\n readonly dynamoTableName: string;\n}\n\n/**\n * Lambda used as Cognito Pre Token Generation trigger. Resolves the OpenHI\n * User from the request's Cognito `sub` and injects `ohi_tid`, `ohi_wid`,\n * `ohi_uid`, `ohi_uname` into both the ID token and the access token\n * (ADR 2026-03-17-01).\n */\nexport class PreTokenGenerationLambda extends Construct {\n public readonly lambda: NodejsFunction;\n\n constructor(scope: Construct, props: PreTokenGenerationLambdaProps) {\n super(scope, \"pre-token-generation-lambda\");\n\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 1024,\n environment: {\n DYNAMO_TABLE_NAME: props.dynamoTableName,\n },\n });\n }\n}\n","import type { AttributeValue } from \"@aws-sdk/client-dynamodb\";\n\n/**\n * Shape of a DynamoDB change record as delivered inside Kinesis (table stream\n * destination) and decoded by the Firehose transform Lambda.\n */\nexport interface DynamoDbStreamKinesisRecord {\n eventName?: string;\n userIdentity?: unknown;\n dynamodb?: {\n Keys?: Record<string, AttributeValue>;\n NewImage?: Record<string, AttributeValue>;\n OldImage?: Record<string, AttributeValue>;\n SequenceNumber?: string;\n ApproximateCreationDateTime?: number;\n };\n}\n\nexport function dynamodbValueToJs(av: AttributeValue): unknown {\n if (av.S !== undefined) {\n return av.S;\n }\n if (av.N !== undefined) {\n return av.N.includes(\".\")\n ? Number.parseFloat(av.N)\n : Number.parseInt(av.N, 10);\n }\n if (av.BOOL !== undefined) {\n return av.BOOL;\n }\n if (av.NULL !== undefined) {\n return null;\n }\n if (av.M !== undefined) {\n return dynamodbImageToPlain(av.M);\n }\n if (av.L !== undefined) {\n return av.L.map((x: AttributeValue) => dynamodbValueToJs(x));\n }\n if (av.SS !== undefined) {\n return av.SS;\n }\n if (av.NS !== undefined) {\n return av.NS.map((n: string) =>\n n.includes(\".\") ? Number.parseFloat(n) : Number.parseInt(n, 10),\n );\n }\n return undefined;\n}\n\nexport function dynamodbImageToPlain(\n image: Record<string, AttributeValue>,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(image)) {\n out[k] = dynamodbValueToJs(v);\n }\n return out;\n}\n","import type { AttributeValue } from \"@aws-sdk/client-dynamodb\";\nimport type { DynamoDbStreamKinesisRecord } from \"./dynamodb-stream-record\";\nimport { dynamodbImageToPlain } from \"./dynamodb-stream-record\";\n\n/**\n * EventBridge envelope constants for data-store CDC (no CDK imports).\n * @see docs/architecture/adr/2026-03-02-01-dynamodb-stream-to-data-event-bus.md\n */\nexport const DATA_STORE_CHANGE_EVENT_SOURCE = \"openhi.data.store\";\n\nexport const DATA_STORE_CHANGE_DETAIL_TYPE = \"FhirCurrentResourceChanged\";\n\n/** AWS PutEvents per-entry detail limit is 256 KiB; stay under for headroom. */\nexport const DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES = 250 * 1024;\n\nconst EXCLUDED_CHANGE_DETAIL_KEYS = new Set([\n \"PK\",\n \"SK\",\n \"GSI1PK\",\n \"GSI1SK\",\n \"GSI2PK\",\n \"GSI2SK\",\n /** Full FHIR JSON may contain PII; never list or ship in the bus payload. */\n \"resource\",\n]);\n\nfunction shallowValueEqual(a: unknown, b: unknown): boolean {\n return JSON.stringify(a) === JSON.stringify(b);\n}\n\nfunction changedNonResourceAttributeNames(\n oldImage: Record<string, unknown> | undefined,\n newImage: Record<string, unknown> | undefined,\n): string[] | undefined {\n if (!oldImage || !newImage) {\n return undefined;\n }\n const names = new Set<string>();\n const keys = new Set([...Object.keys(oldImage), ...Object.keys(newImage)]);\n for (const k of keys) {\n if (EXCLUDED_CHANGE_DETAIL_KEYS.has(k)) {\n continue;\n }\n if (!shallowValueEqual(oldImage[k], newImage[k])) {\n names.add(k);\n }\n }\n return names.size > 0 ? [...names].sort() : undefined;\n}\n\n/** Non-excluded attribute names present on an item (for INSERT / REMOVE). */\nfunction presentMetadataAttributeNames(\n image: Record<string, unknown> | undefined,\n): string[] | undefined {\n if (!image) {\n return undefined;\n }\n const names = Object.keys(image).filter(\n (k) => !EXCLUDED_CHANGE_DETAIL_KEYS.has(k),\n );\n return names.length > 0 ? names.sort() : undefined;\n}\n\nfunction plainImage(\n image: Record<string, AttributeValue> | undefined,\n): Record<string, unknown> | undefined {\n if (!image) {\n return undefined;\n }\n return dynamodbImageToPlain(image);\n}\n\nexport interface FhirCurrentResourceChangeDetail {\n changeType: \"INSERT\" | \"MODIFY\" | \"REMOVE\";\n tenantId: string;\n workspaceId: string;\n resourceType: string;\n resourceId: string;\n resourceVersion: string;\n streamSequenceNumber?: string;\n /** Seconds since UNIX epoch (DynamoDB Streams `ApproximateCreationDateTime`). */\n approximateCreationEpochSec?: number;\n /**\n * MODIFY: attributes whose values differ between old and new images.\n * INSERT / REMOVE: attributes present on the written or removed image (metadata only).\n */\n changedAttributeNames?: string[];\n}\n\nexport function buildFhirCurrentResourceChangeDetail(\n record: DynamoDbStreamKinesisRecord,\n keys: {\n tenantId: string;\n workspaceId: string;\n resourceType: string;\n resourceId: string;\n version: string;\n },\n): FhirCurrentResourceChangeDetail {\n const rawName = record.eventName;\n const changeType =\n rawName === \"INSERT\" || rawName === \"MODIFY\" || rawName === \"REMOVE\"\n ? rawName\n : \"MODIFY\";\n\n const seq = record.dynamodb?.SequenceNumber;\n const approxEpochSec = record.dynamodb?.ApproximateCreationDateTime;\n\n const newPlain = plainImage(\n record.dynamodb?.NewImage as Record<string, AttributeValue> | undefined,\n );\n const oldPlain = plainImage(\n record.dynamodb?.OldImage as Record<string, AttributeValue> | undefined,\n );\n\n let changedAttributeNames: string[] | undefined;\n if (changeType === \"MODIFY\") {\n changedAttributeNames = changedNonResourceAttributeNames(\n oldPlain,\n newPlain,\n );\n } else if (changeType === \"INSERT\") {\n changedAttributeNames = presentMetadataAttributeNames(newPlain);\n } else {\n changedAttributeNames = presentMetadataAttributeNames(oldPlain);\n }\n\n return {\n changeType,\n tenantId: keys.tenantId,\n workspaceId: keys.workspaceId,\n resourceType: keys.resourceType,\n resourceId: keys.resourceId,\n resourceVersion: keys.version,\n ...(typeof seq === \"string\" && seq.length > 0\n ? { streamSequenceNumber: seq }\n : {}),\n ...(typeof approxEpochSec === \"number\" && Number.isFinite(approxEpochSec)\n ? { approximateCreationEpochSec: approxEpochSec }\n : {}),\n ...(changedAttributeNames ? { changedAttributeNames } : {}),\n };\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Duration, RemovalPolicy, Size } from \"aws-cdk-lib\";\nimport * as events from \"aws-cdk-lib/aws-events\";\nimport * as kinesis from \"aws-cdk-lib/aws-kinesis\";\nimport * as kinesisfirehose from \"aws-cdk-lib/aws-kinesisfirehose\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport * as s3 from \"aws-cdk-lib/aws-s3\";\nimport { Construct } from \"constructs\";\n\nconst HANDLER_NAME = \"firehose-archive-transform.handler.js\";\n\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n return path.join(dirname, \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n}\n\nexport interface DataStoreHistoricalArchiveProps {\n /**\n * Kinesis stream that receives DynamoDB item-level changes (table Kinesis destination).\n */\n readonly kinesisStream: kinesis.IStream;\n /**\n * Removal policy for the archive bucket and related resources.\n */\n readonly removalPolicy: RemovalPolicy;\n /**\n * Short hash for unique stream/bucket naming within the deployment.\n */\n readonly stackHash: string;\n /**\n * When set, the Firehose transform Lambda publishes qualifying changes to\n * this bus via PutEvents (ADR 2026-03-02-01).\n */\n readonly dataEventBus?: events.IEventBus;\n}\n\n/**\n * DynamoDB change stream → Kinesis → Firehose → S3 with a transform Lambda for\n * scope filtering and dynamic partitioning (ADR 2026-03-11-02). The same Lambda\n * publishes qualifying current-resource changes to the data event bus (ADR 2026-03-02-01)\n * when {@link DataStoreHistoricalArchiveProps.dataEventBus} is set.\n */\nexport class DataStoreHistoricalArchive extends Construct {\n public readonly archiveBucket: s3.Bucket;\n /**\n * Receives PutEvents payloads that still fail after in-Lambda retries when\n * {@link DataStoreHistoricalArchiveProps.dataEventBus} is configured.\n */\n public readonly putEventsFailureDlqBucket?: s3.Bucket;\n public readonly deliveryStream: kinesisfirehose.IDeliveryStream;\n public readonly transformFunction: NodejsFunction;\n\n constructor(\n scope: Construct,\n id: string,\n props: DataStoreHistoricalArchiveProps,\n ) {\n super(scope, id);\n\n this.archiveBucket = new s3.Bucket(this, \"ArchiveBucket\", {\n blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,\n encryption: s3.BucketEncryption.S3_MANAGED,\n enforceSSL: true,\n removalPolicy: props.removalPolicy,\n autoDeleteObjects: props.removalPolicy === RemovalPolicy.DESTROY,\n versioned: true,\n });\n\n const putEventsFailureDlqBucket = props.dataEventBus\n ? new s3.Bucket(this, \"PutEventsFailureDlq\", {\n blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,\n encryption: s3.BucketEncryption.S3_MANAGED,\n enforceSSL: true,\n removalPolicy: props.removalPolicy,\n autoDeleteObjects: props.removalPolicy === RemovalPolicy.DESTROY,\n versioned: false,\n })\n : undefined;\n this.putEventsFailureDlqBucket = putEventsFailureDlqBucket;\n\n this.transformFunction = new NodejsFunction(this, \"FirehoseTransform\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 512,\n timeout: Duration.minutes(1),\n description:\n \"Firehose transform: filter CURRENT resource rows, S3 keys, EventBridge PutEvents\",\n environment:\n props.dataEventBus && putEventsFailureDlqBucket\n ? {\n DATA_EVENT_BUS_NAME: props.dataEventBus.eventBusName,\n DATA_STORE_PUT_EVENTS_DLQ_BUCKET:\n putEventsFailureDlqBucket.bucketName,\n }\n : undefined,\n bundling: {\n minify: true,\n sourceMap: false,\n },\n });\n\n props.dataEventBus?.grantPutEventsTo(this.transformFunction);\n putEventsFailureDlqBucket?.grantPut(this.transformFunction);\n\n const processor = new kinesisfirehose.LambdaFunctionProcessor(\n this.transformFunction,\n {\n bufferInterval: Duration.seconds(60),\n bufferSize: Size.mebibytes(3),\n retries: 3,\n },\n );\n\n const destination = new kinesisfirehose.S3Bucket(this.archiveBucket, {\n compression: kinesisfirehose.Compression.GZIP,\n bufferingInterval: Duration.seconds(300),\n // Firehose requires SizeInMBs ≥ 64 when dynamic partitioning is enabled.\n bufferingSize: Size.mebibytes(64),\n processors: [processor],\n errorOutputPrefix:\n \"errors/!{firehose:error-output-type}/!{timestamp:yyyy/MM/dd/HH}/\",\n loggingConfig: new kinesisfirehose.EnableLogging(),\n });\n\n this.deliveryStream = new kinesisfirehose.DeliveryStream(\n this,\n \"ArchiveDeliveryStream\",\n {\n deliveryStreamName: `openhi-dstore-arch-${props.stackHash}`,\n source: new kinesisfirehose.KinesisStreamSource(props.kinesisStream),\n destination,\n },\n );\n\n const cfn = this.deliveryStream.node\n .defaultChild as kinesisfirehose.CfnDeliveryStream;\n cfn.addPropertyOverride(\n \"ExtendedS3DestinationConfiguration.DynamicPartitioningConfiguration\",\n {\n Enabled: true,\n RetryOptions: { DurationInSeconds: 300 },\n },\n );\n cfn.addPropertyOverride(\n \"ExtendedS3DestinationConfiguration.Prefix\",\n \"!{partitionKeyFromLambda:tenantId}/!{partitionKeyFromLambda:workspaceId}/!{partitionKeyFromLambda:resourceType}/!{partitionKeyFromLambda:resourceId}/!{partitionKeyFromLambda:version}/\",\n );\n }\n}\n","import { RemovalPolicy } from \"aws-cdk-lib\";\nimport {\n AttributeType,\n BillingMode,\n ProjectionType,\n Table,\n TableProps,\n} from \"aws-cdk-lib/aws-dynamodb\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/dynamodb/dynamo-db-data-store.md\n */\n\n/**\n * DynamoDB table name for the data store. Used for cross-stack reference and\n * deterministic naming per branch. The table backs the app data store.\n */\nexport function getDynamoDbDataStoreTableName(scope: Construct): string {\n const stack = OpenHiService.of(scope) as OpenHiService;\n return `data-store-${stack.branchHash}`;\n}\n\nexport interface DynamoDbDataStoreProps extends Omit<\n TableProps,\n \"tableName\" | \"removalPolicy\"\n> {\n /**\n * Optional removal policy override. If not set, uses the service's default\n * removal policy (RETAIN for prod, DESTROY otherwise).\n */\n readonly removalPolicy?: RemovalPolicy;\n}\n\n/**\n * DynamoDB table implementing the single-table design for app data (FHIR\n * resources data plane and platform control plane), per planning ADR-011 and\n * DR-004.\n *\n * @see {@link https://github.com/codedrifters/openhi/blob/main/sites/www-docs/content/architecture/dynamodb-single-table-design.md | DynamoDB Single-Table Design}\n *\n * Primary key: PK (String), SK (String).\n *\n * GSIs:\n * - **GSI1 — Unified Sharded List** (`GSI1PK`/`GSI1SK`, INCLUDE projection per\n * DR-004). Primary list/lookup index for both data-plane FHIR resources and\n * control-plane entities (User, Tenant, Workspace, Membership, Role,\n * RoleAssignment, Configuration). PK shape:\n * `TID#<tid>#WID#<wid>#RT#<Type>#SHARD#<n>` with 4 shards\n * (`n = hash(id) mod 4`). SK shape per `extractSortKey`: labeled types use\n * `<normalizedLabel>#<id>`; unlabeled use `<ISO-8601 lastUpdated>#<id>`.\n * - **GSI2 — Sub-Lookup** (`GSI2PK`/`GSI2SK`, INCLUDE projection). Resolves\n * `UserEntity` from a Cognito `sub` for the Pre Token Generation Lambda.\n * PK shape: `USER#SUB#<cognitoSub>`. SK shape: `CURRENT`.\n *\n * For historical archive to S3, pass `kinesisStream` and `stream` (e.g.\n * `StreamViewType.NEW_AND_OLD_IMAGES`) on the table props per ADR 2026-03-11-02.\n */\nexport class DynamoDbDataStore extends Table {\n constructor(\n scope: Construct,\n id: string,\n props: DynamoDbDataStoreProps = {},\n ) {\n const service = OpenHiService.of(scope) as OpenHiService;\n\n super(scope, id, {\n ...props,\n tableName: getDynamoDbDataStoreTableName(scope),\n partitionKey: {\n name: \"PK\",\n type: AttributeType.STRING,\n },\n sortKey: {\n name: \"SK\",\n type: AttributeType.STRING,\n },\n billingMode: BillingMode.PAY_PER_REQUEST,\n removalPolicy: props.removalPolicy ?? service.removalPolicy,\n });\n\n // GSI1 — Unified Sharded List (data plane + control plane) per ADR-011 and DR-004.\n this.addGlobalSecondaryIndex({\n indexName: \"GSI1\",\n partitionKey: {\n name: \"GSI1PK\",\n type: AttributeType.STRING,\n },\n sortKey: {\n name: \"GSI1SK\",\n type: AttributeType.STRING,\n },\n projectionType: ProjectionType.INCLUDE,\n nonKeyAttributes: [\n \"id\",\n \"summary\",\n \"vid\",\n \"lastUpdated\",\n \"createdDate\",\n \"modifiedDate\",\n \"createdById\",\n \"modifiedById\",\n // ElectroDB filters every query result through `ownsItem`, which\n // verifies `__edb_e__` (entity name) and `__edb_v__` (version) match\n // the entity. Without these projected, every GSI1 query returns 0\n // results — list endpoints silently return empty bundles.\n \"__edb_e__\",\n \"__edb_v__\",\n ],\n });\n\n // GSI2 — Sub-Lookup: Cognito sub → UserEntity (Pre Token Generation Lambda).\n this.addGlobalSecondaryIndex({\n indexName: \"GSI2\",\n partitionKey: {\n name: \"GSI2PK\",\n type: AttributeType.STRING,\n },\n sortKey: {\n name: \"GSI2SK\",\n type: AttributeType.STRING,\n },\n projectionType: ProjectionType.INCLUDE,\n nonKeyAttributes: [\n \"id\",\n \"currentTenant\",\n \"currentWorkspace\",\n \"displayName\",\n // See GSI1 above: ElectroDB's `ownsItem` filter rejects items\n // without these, so any query against GSI2 returns 0 results\n // unless they're projected.\n \"__edb_e__\",\n \"__edb_v__\",\n ],\n });\n }\n}\n","import {\n WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH,\n WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR,\n} from \"@openhi/workflows\";\nimport { Annotations, RemovalPolicy } from \"aws-cdk-lib\";\nimport { AttributeType, BillingMode, Table } from \"aws-cdk-lib/aws-dynamodb\";\nimport { Effect, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport { Function } from \"aws-cdk-lib/aws-lambda\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService, type OpenHiServiceType } from \"../../app\";\nimport { DiscoverableStringParameter } from \"../ssm\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/dynamodb/workflow-dedup-table.md\n */\n\n/**\n * Deterministic table name for the shared workflow dedup table.\n * Mirrors `getDynamoDbDataStoreTableName` naming: `workflow-dedup-${branchHash}`.\n */\nexport function getWorkflowDedupTableName(scope: Construct): string {\n const stack = OpenHiService.of(scope) as OpenHiService;\n return `workflow-dedup-${stack.branchHash}`;\n}\n\n/** Props for `WorkflowDedupTable`. */\nexport interface WorkflowDedupTableProps {\n /**\n * Optional removal policy override. Defaults to the service's default\n * (RETAIN for prod, DESTROY otherwise).\n */\n readonly removalPolicy?: RemovalPolicy;\n}\n\n/** Options for `WorkflowDedupTable.grantConsumer`. */\nexport interface GrantConsumerOptions {\n /**\n * Override the default TTL applied by the runtime client. The 14-day\n * default lives in `@openhi/workflows`; per-consumer overrides clamp\n * shorter per TR-015. Stored in the consumer's environment so the\n * `WorkflowDedupClient` factory can pick it up.\n */\n readonly defaultTtlSeconds?: number;\n}\n\n/**\n * Shared platform-level dedup table every retryable workflow consumer\n * dedupes against. Provisioned exactly once at the platform stack.\n *\n * Schema (per TR-015):\n * - Partition key `consumerName` (S)\n * - Sort key `sk` (S) — encodes `<eventId>#<attempt>`\n * - TTL attribute `expiresAt` (N, Unix epoch seconds)\n * - On-demand billing\n *\n * @see https://github.com/codedrifters/openhi-planning/blob/main/docs/src/content/docs/requirements/technical-requirements/TR-015-workflow-dedup-table.md\n */\nexport class WorkflowDedupTable extends Construct {\n /** SSM param name (short) used by `DiscoverableStringParameter` for the table name lookup. */\n public static readonly TABLE_NAME_SSM_PARAM_NAME =\n \"workflow-dedup-table-name\";\n /** SSM param name (short) used by `DiscoverableStringParameter` for the table ARN lookup. */\n public static readonly TABLE_ARN_SSM_PARAM_NAME = \"workflow-dedup-table-arn\";\n\n /** Cross-stack lookup for the table name. */\n public static tableNameFromLookup(scope: Construct): string {\n return DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: WorkflowDedupTable.TABLE_NAME_SSM_PARAM_NAME,\n serviceType: WorkflowDedupTable.PUBLISHER_SERVICE_TYPE,\n });\n }\n\n /** Cross-stack lookup for the table ARN. */\n public static tableArnFromLookup(scope: Construct): string {\n return DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: WorkflowDedupTable.TABLE_ARN_SSM_PARAM_NAME,\n serviceType: WorkflowDedupTable.PUBLISHER_SERVICE_TYPE,\n });\n }\n\n /**\n * Cross-stack equivalent of {@link grantConsumer}. Use when the dedup\n * table is on a different stack than the consumer Lambda — the\n * grant resolves the table name + ARN via SSM at synth time, so the\n * consumer stack does not pick up a CloudFormation export dependency\n * on the global stack.\n *\n * Inverts the singleton-guard semantics of `grantConsumer`: there is\n * no synth-time check that the same `consumerName` was registered\n * twice across stacks. Consumer names are agreed by convention\n * (see TR-015); double-registration is operator error caught at\n * design time, not synth time.\n */\n public static grantConsumerFromLookup(\n scope: Construct,\n fn: Function,\n consumerName: string,\n options: GrantConsumerOptions = {},\n ): void {\n WorkflowDedupTable.assertConsumerNameStatic(consumerName);\n const tableName = WorkflowDedupTable.tableNameFromLookup(scope);\n const tableArn = WorkflowDedupTable.tableArnFromLookup(scope);\n\n fn.addEnvironment(WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR, tableName);\n if (options.defaultTtlSeconds !== undefined) {\n fn.addEnvironment(\n \"OPENHI_WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS\",\n String(options.defaultTtlSeconds),\n );\n }\n\n fn.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\n \"dynamodb:PutItem\",\n \"dynamodb:UpdateItem\",\n \"dynamodb:GetItem\",\n \"dynamodb:Query\",\n ],\n resources: [tableArn],\n conditions: {\n \"ForAllValues:StringEquals\": {\n \"dynamodb:LeadingKeys\": [consumerName],\n },\n },\n }),\n );\n }\n\n /**\n * Service-type the publishing stack runs under. The cross-stack lookups\n * pin to this value so consumer stacks on a different service-type\n * (e.g. `data`, `auth`) resolve the parameter at the publisher's SSM\n * path instead of their own. Typed against `OpenHiServiceType` so a\n * future rename of the literal triggers a compile error; not pulled\n * from `OpenHiGlobalService.SERVICE_TYPE` because\n * `OpenHiGlobalService` already imports `WorkflowDedupTable` — a\n * back-import would create a circular dependency.\n */\n private static readonly PUBLISHER_SERVICE_TYPE: OpenHiServiceType = \"global\";\n\n /**\n * Standalone consumer-name validator shared by the instance method\n * and `grantConsumerFromLookup` so the two grants enforce identical\n * invariants.\n */\n private static assertConsumerNameStatic(consumerName: string): void {\n if (consumerName.length === 0) {\n throw new WorkflowDedupConsumerNameInvalidError(\n \"consumerName must be non-empty.\",\n );\n }\n if (consumerName.length > WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH) {\n throw new WorkflowDedupConsumerNameInvalidError(\n `consumerName must be at most ${WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH} chars; got ${consumerName.length}.`,\n );\n }\n if (/\\s/.test(consumerName)) {\n throw new WorkflowDedupConsumerNameInvalidError(\n \"consumerName must not contain whitespace.\",\n );\n }\n }\n\n /** The underlying DynamoDB table. */\n public readonly table: Table;\n\n private readonly registeredConsumers = new Set<string>();\n\n constructor(\n scope: Construct,\n id: string,\n props: WorkflowDedupTableProps = {},\n ) {\n super(scope, id);\n\n const service = OpenHiService.of(scope) as OpenHiService;\n\n // Synth-time singleton guard: refuse a second WorkflowDedupTable in\n // the same owning service (one stack per deploy target). The check is\n // intentionally scoped to the host service rather than to the CDK\n // app root: openhi/global/src/app.ts maps over `app.environments` and\n // instantiates one `OpenHiGlobalService` per environment, and each of\n // those is a separate deployment target that owns its own dedup\n // table per TR-015.\n const others = service.node\n .findAll()\n .filter(\n (c): c is WorkflowDedupTable =>\n c instanceof WorkflowDedupTable && c !== this,\n );\n if (others.length > 0) {\n throw new WorkflowDedupTableDuplicateError(\n `WorkflowDedupTable already exists at ${others[0].node.path}; ` +\n \"only one shared dedup table is allowed per service stack (TR-015).\",\n );\n }\n\n this.table = new Table(this, \"Table\", {\n tableName: getWorkflowDedupTableName(scope),\n partitionKey: {\n name: \"consumerName\",\n type: AttributeType.STRING,\n },\n sortKey: {\n name: \"sk\",\n type: AttributeType.STRING,\n },\n billingMode: BillingMode.PAY_PER_REQUEST,\n timeToLiveAttribute: \"expiresAt\",\n removalPolicy: props.removalPolicy ?? service.removalPolicy,\n });\n\n // Publish the table name and ARN so consumer stacks can discover\n // them without a cross-stack prop dependency.\n new DiscoverableStringParameter(this, \"table-name-param\", {\n ssmParamName: WorkflowDedupTable.TABLE_NAME_SSM_PARAM_NAME,\n stringValue: this.table.tableName,\n });\n new DiscoverableStringParameter(this, \"table-arn-param\", {\n ssmParamName: WorkflowDedupTable.TABLE_ARN_SSM_PARAM_NAME,\n stringValue: this.table.tableArn,\n });\n }\n\n /**\n * Wire a Lambda consumer to this table. Injects the table-name env var\n * so the runtime `WorkflowDedupClient` can resolve it, then attaches a\n * per-consumer IAM grant scoped by `dynamodb:LeadingKeys` so the\n * consumer can only read/write its own partition.\n */\n public grantConsumer(\n fn: Function,\n consumerName: string,\n options: GrantConsumerOptions = {},\n ): void {\n this.assertConsumerName(consumerName);\n\n if (this.registeredConsumers.has(consumerName)) {\n Annotations.of(this).addWarning(\n `WorkflowDedupTable: consumerName \"${consumerName}\" registered more than once; ` +\n \"subsequent grantConsumer calls add policy statements but do not re-inject the env var.\",\n );\n }\n this.registeredConsumers.add(consumerName);\n\n fn.addEnvironment(WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR, this.table.tableName);\n if (options.defaultTtlSeconds !== undefined) {\n fn.addEnvironment(\n \"OPENHI_WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS\",\n String(options.defaultTtlSeconds),\n );\n }\n\n fn.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\n \"dynamodb:PutItem\",\n \"dynamodb:UpdateItem\",\n \"dynamodb:GetItem\",\n \"dynamodb:Query\",\n ],\n resources: [this.table.tableArn],\n conditions: {\n \"ForAllValues:StringEquals\": {\n \"dynamodb:LeadingKeys\": [consumerName],\n },\n },\n }),\n );\n }\n\n private assertConsumerName(consumerName: string): void {\n WorkflowDedupTable.assertConsumerNameStatic(consumerName);\n }\n}\n\n/** Thrown when a second `WorkflowDedupTable` is instantiated in the same app. */\nexport class WorkflowDedupTableDuplicateError extends Error {\n /** @param message - human-readable description of the duplicate. */\n constructor(message: string) {\n super(message);\n this.name = \"WorkflowDedupTableDuplicateError\";\n }\n}\n\n/** Thrown when a consumerName violates the TR-015 invariants. */\nexport class WorkflowDedupConsumerNameInvalidError extends Error {\n /** @param message - human-readable description of the invariant violation. */\n constructor(message: string) {\n super(message);\n this.name = \"WorkflowDedupConsumerNameInvalidError\";\n }\n}\n","import { EventBus, EventBusProps } from \"aws-cdk-lib/aws-events\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/event-bridge/data-event-bus.md\n */\n\nexport class DataEventBus extends EventBus {\n /*****************************************************************************\n *\n * Return a name for this EventBus based on the stack environment hash. This\n * name is common across all stacks since it's using the environment hash in\n * it's name.\n *\n ****************************************************************************/\n\n public static getEventBusName(scope: Construct): string {\n const stack = OpenHiService.of(scope) as OpenHiService;\n return `datav1${stack.branchHash}`;\n }\n\n constructor(scope: Construct, props?: EventBusProps) {\n super(scope, \"data-event-bus-v1\", {\n ...props,\n eventBusName: DataEventBus.getEventBusName(scope),\n });\n }\n}\n","import { EventBus, EventBusProps } from \"aws-cdk-lib/aws-events\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/event-bridge/ops-event-bus.md\n */\n\nexport class OpsEventBus extends EventBus {\n /*****************************************************************************\n *\n * Return a name for this EventBus based on the stack environment hash. This\n * name is common across all stacks since it's using the environment hash in\n * it's name.\n *\n ****************************************************************************/\n\n public static getEventBusName(scope: Construct): string {\n const stack = OpenHiService.of(scope) as OpenHiService;\n return `opsv1${stack.branchHash}`;\n }\n\n constructor(scope: Construct, props?: EventBusProps) {\n super(scope, \"ops-event-bus-v1\", {\n ...props,\n eventBusName: OpsEventBus.getEventBusName(scope),\n });\n }\n}\n","import { EventBus, EventBusProps } from \"aws-cdk-lib/aws-events\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/event-bridge/control-event-bus.md\n */\n\nexport class ControlEventBus extends EventBus {\n /*****************************************************************************\n *\n * Return a name for this EventBus based on the stack environment hash. This\n * name is common across all stacks since it's using the environment hash in\n * its name.\n *\n ****************************************************************************/\n\n public static getEventBusName(scope: Construct): string {\n const stack = OpenHiService.of(scope) as OpenHiService;\n return `controlv1${stack.branchHash}`;\n }\n\n constructor(scope: Construct, props?: EventBusProps) {\n super(scope, \"control-event-bus-v1\", {\n ...props,\n eventBusName: ControlEventBus.getEventBusName(scope),\n });\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Duration, RemovalPolicy, Stack } from \"aws-cdk-lib\";\nimport * as ec2 from \"aws-cdk-lib/aws-ec2\";\nimport * as kinesis from \"aws-cdk-lib/aws-kinesis\";\nimport { Runtime, StartingPosition } from \"aws-cdk-lib/aws-lambda\";\nimport { KinesisEventSource } from \"aws-cdk-lib/aws-lambda-event-sources\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport * as rds from \"aws-cdk-lib/aws-rds\";\nimport { Construct } from \"constructs\";\nimport { DiscoverableStringParameter } from \"../ssm/discoverable-string-parameter\";\n\nconst HANDLER_NAME = \"data-store-postgres-replication.handler.js\";\nconst DEFAULT_DATABASE_NAME = \"openhi\";\nconst SCHEMA_NAME_PATTERN = /^[a-z_][a-z0-9_]{0,62}$/;\n\n/**\n * SSM parameter names that publish the Postgres replica's coordinates so other\n * stacks (notably the REST API stack) can discover them without a direct CDK\n * cross-stack reference. The schema name is intentionally NOT published — it\n * is a deterministic function of `branchHash` and consumers compute it locally\n * via {@link getPostgresReplicaSchemaName}.\n */\nexport const POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME =\n \"POSTGRES_REPLICA_CLUSTER_ARN\";\nexport const POSTGRES_REPLICA_SECRET_ARN_SSM_NAME =\n \"POSTGRES_REPLICA_SECRET_ARN\";\nexport const POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME =\n \"POSTGRES_REPLICA_DATABASE_NAME\";\n\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n return path.join(dirname, \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n}\n\n/**\n * Derive the per-branch Postgres schema name from a branch hash. The `b_`\n * prefix guarantees a leading letter (Postgres identifier rule). Branch hashes\n * are 6 hex chars from {@link OpenHiService.branchHash} so the resulting\n * `b_xxxxxx` is well within the 63-byte identifier limit.\n */\nexport function getPostgresReplicaSchemaName(branchHash: string): string {\n const candidate = `b_${branchHash.toLowerCase()}`;\n if (!SCHEMA_NAME_PATTERN.test(candidate)) {\n throw new Error(\n `Branch hash ${JSON.stringify(branchHash)} produces an invalid Postgres ` +\n `schema name ${JSON.stringify(candidate)}; expected /[a-z_][a-z0-9_]{0,62}/.`,\n );\n }\n return candidate;\n}\n\nexport interface DataStorePostgresReplicaProps {\n /**\n * Kinesis stream that receives DynamoDB item-level changes (the same stream\n * that backs {@link DataStoreHistoricalArchive}). The replication Lambda is\n * registered as a parallel consumer.\n */\n readonly kinesisStream: kinesis.IStream;\n /**\n * Removal policy for the cluster, secret, and dependent resources.\n */\n readonly removalPolicy: RemovalPolicy;\n /**\n * Short hash unique to the stack — used in the cluster identifier.\n */\n readonly stackHash: string;\n /**\n * Short hash unique to the branch — used to derive the per-branch schema\n * name (`b_<branchHash>`) inside the Postgres database.\n */\n readonly branchHash: string;\n /**\n * Optional VPC override. If absent, the construct creates a minimal isolated\n * VPC (2 AZs, no NAT gateways) just for the cluster and replication Lambda.\n */\n readonly vpc?: ec2.IVpc;\n /**\n * Optional database name override.\n * @default \"openhi\"\n */\n readonly databaseName?: string;\n /**\n * Aurora Serverless v2 minimum capacity in ACUs. Defaults to 1 so the\n * writer stays warm — avoids the ~10–20s scale-up wait that a cold\n * (0 ACU) cluster imposes on the next request. Set explicitly to 0 to\n * opt back into scale-to-zero if idle cost becomes the dominant concern.\n */\n readonly minCapacity?: number;\n /**\n * Aurora Serverless v2 maximum capacity in ACUs. Defaults to 2 — adequate\n * for the PoC's replication-only workload.\n */\n readonly maxCapacity?: number;\n}\n\n/**\n * DynamoDB change stream → Postgres replication tier (ADR 2026-04-17-01,\n * phase 1). Provisions an Aurora Serverless v2 PostgreSQL cluster and a\n * Lambda consumer on the existing change-stream that projects each current\n * FHIR resource into a JSONB `resources` table under a per-branch schema.\n *\n * Phase 1 is replication-only; query routing and SearchParameter-specific\n * indexes are intentionally deferred. Per-branch *clusters* (rather than the\n * shared cluster suggested by the ADR) are an explicit PoC simplification —\n * see the ADR's \"Operational notes\" section for the long-term direction.\n *\n * @see sites/www-docs/content/architecture/adr/2026-04-17-01-ad-hoc-query-support-fhir-api.md\n */\nexport class DataStorePostgresReplica extends Construct {\n /**\n * Resolve the cluster ARN published by an upstream {@link DataStorePostgresReplica}.\n * Use from any stack that needs to grant `rds-data:ExecuteStatement` against\n * the cluster.\n */\n public static clusterArnFromConstruct(scope: Construct): string {\n return DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME,\n serviceType: \"data\",\n });\n }\n\n /**\n * Resolve the credentials secret ARN published by an upstream\n * {@link DataStorePostgresReplica}. Use from any stack that needs to grant\n * `secretsmanager:GetSecretValue` against the secret.\n */\n public static secretArnFromConstruct(scope: Construct): string {\n return DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: POSTGRES_REPLICA_SECRET_ARN_SSM_NAME,\n serviceType: \"data\",\n });\n }\n\n /**\n * Resolve the database name published by an upstream\n * {@link DataStorePostgresReplica}.\n */\n public static databaseNameFromConstruct(scope: Construct): string {\n return DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME,\n serviceType: \"data\",\n });\n }\n\n public readonly vpc: ec2.IVpc;\n public readonly cluster: rds.DatabaseCluster;\n public readonly replicationFunction: NodejsFunction;\n public readonly databaseName: string;\n public readonly schemaName: string;\n\n constructor(\n scope: Construct,\n id: string,\n props: DataStorePostgresReplicaProps,\n ) {\n super(scope, id);\n\n this.databaseName = props.databaseName ?? DEFAULT_DATABASE_NAME;\n this.schemaName = getPostgresReplicaSchemaName(props.branchHash);\n\n // Pass explicit AZ names (derived from the stack region) instead of using\n // `maxAzs`, which triggers a CDK availability-zones context lookup. CI's\n // synth step doesn't have full deploy-account creds, so an unresolved AZ\n // lookup gets recorded as \"missing\" in the cdk.out manifest and the deploy\n // step then refuses to proceed. AWS region AZ names follow a stable\n // `<region>a/b/c…` pattern across all current commercial regions.\n const region = Stack.of(this).region;\n this.vpc =\n props.vpc ??\n new ec2.Vpc(this, \"Vpc\", {\n availabilityZones: [`${region}a`, `${region}b`],\n natGateways: 0,\n subnetConfiguration: [\n {\n name: \"isolated\",\n subnetType: ec2.SubnetType.PRIVATE_ISOLATED,\n cidrMask: 24,\n },\n ],\n });\n\n this.cluster = new rds.DatabaseCluster(this, \"Cluster\", {\n clusterIdentifier: `openhi-dstore-pg-${props.stackHash}`,\n engine: rds.DatabaseClusterEngine.auroraPostgres({\n version: rds.AuroraPostgresEngineVersion.VER_16_4,\n }),\n vpc: this.vpc,\n vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },\n writer: rds.ClusterInstance.serverlessV2(\"writer\"),\n serverlessV2MinCapacity: props.minCapacity ?? 1,\n serverlessV2MaxCapacity: props.maxCapacity ?? 2,\n defaultDatabaseName: this.databaseName,\n credentials: rds.Credentials.fromGeneratedSecret(\"openhi_admin\"),\n storageEncrypted: true,\n removalPolicy: props.removalPolicy,\n // Phase 2 of ADR 2026-04-17-01: the REST API Lambda queries Postgres\n // via the RDS Data API (HTTPS) so it can stay out of the cluster's VPC.\n // Direct `pg` from the replication Lambda continues to work in parallel.\n enableDataApi: true,\n });\n\n this.publishCoordinatesToSsm();\n\n this.replicationFunction = new NodejsFunction(this, \"ReplicationFunction\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 512,\n timeout: Duration.minutes(1),\n vpc: this.vpc,\n vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },\n description:\n \"Replicates DynamoDB current-resource changes into the Postgres `resources` JSONB table (ADR 2026-04-17-01).\",\n environment: {\n OPENHI_PG_HOST: this.cluster.clusterEndpoint.hostname,\n OPENHI_PG_PORT: this.cluster.clusterEndpoint.port.toString(),\n OPENHI_PG_DATABASE: this.databaseName,\n OPENHI_PG_SCHEMA: this.schemaName,\n OPENHI_PG_SECRET_ARN: this.cluster.secret!.secretArn,\n OPENHI_PG_SSL: \"true\",\n },\n bundling: {\n minify: true,\n sourceMap: false,\n // pg has conditional/optional deps (pg-native, pg-cloudflare) that\n // historically misbehave when bundled by esbuild; keep it as a real\n // node_module in the Lambda zip instead.\n nodeModules: [\"pg\"],\n },\n });\n\n this.cluster.secret!.grantRead(this.replicationFunction);\n this.cluster.connections.allowDefaultPortFrom(this.replicationFunction);\n\n this.replicationFunction.addEventSource(\n new KinesisEventSource(props.kinesisStream, {\n startingPosition: StartingPosition.LATEST,\n batchSize: 100,\n maxBatchingWindow: Duration.seconds(5),\n retryAttempts: 10,\n bisectBatchOnError: true,\n parallelizationFactor: 2,\n reportBatchItemFailures: true,\n }),\n );\n }\n\n /**\n * Publishes the cluster ARN, secret ARN, and database name as discoverable\n * SSM parameters so the REST API stack (and any future read-side consumer)\n * can wire RDS Data API access without a direct CDK cross-stack reference.\n */\n private publishCoordinatesToSsm(): void {\n new DiscoverableStringParameter(this, \"cluster-arn-param\", {\n ssmParamName: POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME,\n stringValue: this.cluster.clusterArn,\n description:\n \"ARN of the Aurora Serverless v2 cluster backing the Postgres replication tier (ADR 2026-04-17-01).\",\n });\n new DiscoverableStringParameter(this, \"secret-arn-param\", {\n ssmParamName: POSTGRES_REPLICA_SECRET_ARN_SSM_NAME,\n stringValue: this.cluster.secret!.secretArn,\n description:\n \"ARN of the Secrets Manager secret with credentials for the Postgres replication tier.\",\n });\n new DiscoverableStringParameter(this, \"database-name-param\", {\n ssmParamName: POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME,\n stringValue: this.databaseName,\n description: \"Database name within the Postgres replication cluster.\",\n });\n }\n}\n","import { Duration } from \"aws-cdk-lib\";\nimport {\n HostedZone,\n HostedZoneProps,\n IHostedZone,\n NsRecord,\n} from \"aws-cdk-lib/aws-route53\";\nimport { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/route-53/child-hosted-zone.md\n */\n\nexport interface ChildHostedZoneProps extends HostedZoneProps {\n /**\n * The root zone we will attach this sub-zone to.\n */\n readonly parentHostedZone: IHostedZone;\n}\n\nexport class ChildHostedZone extends HostedZone {\n /**\n * Used when storing the child zone ID in SSM. Use {@link OpenHiGlobalService.childHostedZoneFromConstruct} to look up.\n */\n public static readonly SSM_PARAM_NAME = \"CHILDHOSTEDZONE\";\n\n constructor(scope: Construct, id: string, props: ChildHostedZoneProps) {\n super(scope, id, { ...props });\n\n /**\n * Chain the child zone to the parent zone using NS record.\n */\n new NsRecord(this, \"child-ns-record\", {\n zone: props.parentHostedZone,\n recordName: this.zoneName,\n values: this.hostedZoneNameServers || [],\n ttl: Duration.minutes(5),\n });\n }\n}\n","import { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/components/route-53/root-hosted-zone.md\n */\n\n/**\n * Placeholder for root hosted zone. Use {@link OpenHiGlobalService.rootHostedZoneFromConstruct}\n * to obtain an IHostedZone from attributes (e.g. from config). The root zone is always\n * created manually and imported via config.\n */\nexport class RootHostedZone extends Construct {}\n","import {\n CachePolicy,\n Distribution,\n type DistributionProps,\n} from \"aws-cdk-lib/aws-cloudfront\";\nimport { S3BucketOrigin } from \"aws-cdk-lib/aws-cloudfront-origins\";\nimport { Bucket, type BucketProps, type IBucket } from \"aws-cdk-lib/aws-s3\";\nimport { Duration } from \"aws-cdk-lib/core\";\nimport { Construct } from \"constructs\";\nimport { OpenHiService } from \"../../app\";\nimport { DiscoverableStringParameter } from \"../ssm\";\n\n/**\n * Service type for the website service. Used in SSM parameter paths and by\n * OpenHiWebsiteService for fromConstruct() lookups.\n */\nexport const STATIC_HOSTING_SERVICE_TYPE = \"website\";\n\n/**\n * Props for the StaticHosting construct.\n */\nexport interface StaticHostingProps {\n /**\n * Optional S3 bucket props. Bucket name must not be set statically.\n */\n readonly bucketProps?: Omit<BucketProps, \"bucketName\">;\n\n /**\n * Optional CloudFront distribution props. Do not enable invalidation.\n * Default TTL is 10 seconds via a custom cache policy.\n */\n readonly distributionProps?: Omit<\n DistributionProps,\n \"defaultBehavior\" | \"defaultRootObject\"\n >;\n\n /**\n * Service type for SSM parameter paths.\n * @default STATIC_HOSTING_SERVICE_TYPE (\"website\")\n */\n readonly serviceType?: string;\n}\n\n/**\n * Static hosting: S3 bucket (private) + CloudFront distribution with Origin\n * Access Control (OAC). Stores bucket ARN and distribution ARN in SSM via\n * DiscoverableStringParameter for cross-stack lookup. No cache invalidation;\n * default TTL 10 seconds.\n */\nexport class StaticHosting extends Construct {\n /**\n * SSM parameter name for the S3 bucket ARN.\n */\n public static readonly SSM_PARAM_NAME_BUCKET_ARN =\n \"STATIC_HOSTING_BUCKET_ARN\";\n\n /**\n * SSM parameter name for the CloudFront distribution ARN.\n */\n public static readonly SSM_PARAM_NAME_DISTRIBUTION_ARN =\n \"STATIC_HOSTING_DISTRIBUTION_ARN\";\n\n public readonly bucket: IBucket;\n public readonly distribution: Distribution;\n\n constructor(scope: Construct, id: string, props: StaticHostingProps = {}) {\n super(scope, id);\n\n const stack = OpenHiService.of(scope) as OpenHiService;\n const serviceType = props.serviceType ?? STATIC_HOSTING_SERVICE_TYPE;\n\n this.bucket = new Bucket(this, \"bucket\", {\n blockPublicAccess: {\n blockPublicAcls: true,\n blockPublicPolicy: true,\n ignorePublicAcls: true,\n restrictPublicBuckets: true,\n },\n ...props.bucketProps,\n });\n\n const origin = S3BucketOrigin.withOriginAccessControl(this.bucket);\n\n const cachePolicy = new CachePolicy(this, \"cache-policy\", {\n cachePolicyName: `static-hosting-10s-${stack.branchHash}`,\n comment: \"Low TTL (10s) for static hosting; no invalidation\",\n defaultTtl: Duration.seconds(10),\n minTtl: Duration.seconds(0),\n maxTtl: Duration.seconds(10),\n });\n\n this.distribution = new Distribution(this, \"distribution\", {\n defaultBehavior: {\n origin,\n cachePolicy,\n },\n ...props.distributionProps,\n });\n\n new DiscoverableStringParameter(this, \"bucket-arn-param\", {\n ssmParamName: StaticHosting.SSM_PARAM_NAME_BUCKET_ARN,\n serviceType,\n stringValue: this.bucket.bucketArn,\n });\n\n new DiscoverableStringParameter(this, \"distribution-arn-param\", {\n ssmParamName: StaticHosting.SSM_PARAM_NAME_DISTRIBUTION_ARN,\n serviceType,\n stringValue: this.distribution.distributionArn,\n });\n }\n}\n","import { OPEN_HI_STAGE } from \"@openhi/config\";\nimport {\n IUserPool,\n IUserPoolClient,\n IUserPoolDomain,\n LambdaVersion,\n UserPool,\n UserPoolClient,\n UserPoolDomain,\n UserPoolOperation,\n UserPoolProps,\n} from \"aws-cdk-lib/aws-cognito\";\nimport { IEventBus } from \"aws-cdk-lib/aws-events\";\nimport { Effect, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport { IKey, Key } from \"aws-cdk-lib/aws-kms\";\nimport { IFunction } from \"aws-cdk-lib/aws-lambda\";\nimport { Stack } from \"aws-cdk-lib/core\";\nimport { Construct } from \"constructs\";\nimport { OpenHiDataService } from \"./open-hi-data-service\";\nimport { OpenHiGlobalService } from \"./open-hi-global-service\";\nimport { OpenHiEnvironment } from \"../app/open-hi-environment\";\nimport {\n OpenHiService,\n OpenHiServiceProps,\n OpenHiServiceType,\n} from \"../app/open-hi-service\";\nimport { CognitoFixtureSeederClient } from \"../components/cognito/cognito-fixture-seeder-client\";\nimport { CognitoUserPool } from \"../components/cognito/cognito-user-pool\";\nimport { CognitoUserPoolClient } from \"../components/cognito/cognito-user-pool-client\";\nimport { CognitoUserPoolDomain } from \"../components/cognito/cognito-user-pool-domain\";\nimport { CognitoUserPoolKmsKey } from \"../components/cognito/cognito-user-pool-kms-key\";\nimport { PostAuthenticationLambda } from \"../components/cognito/post-authentication-lambda\";\nimport { PostConfirmationLambda } from \"../components/cognito/post-confirmation-lambda\";\nimport { PreTokenGenerationLambda } from \"../components/cognito/pre-token-generation-lambda\";\nimport { DiscoverableStringParameter } from \"../components/ssm\";\nimport { UserOnboardingWorkflow } from \"../workflows/control-plane/user-onboarding\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/services/open-hi-auth-service.md\n */\n\nexport interface OpenHiAuthServiceProps extends OpenHiServiceProps {\n /**\n * Optional props for the Cognito User Pool.\n */\n readonly userPoolProps?: UserPoolProps;\n}\n\n/**\n * OpenHI Auth Service stack.\n *\n * @remarks\n * The Auth service manages authentication infrastructure including:\n * - Cognito User Pool for user management and authentication\n * - User Pool Client for application integration\n * - User Pool Domain for hosting the Cognito hosted UI\n * - KMS Key for Cognito User Pool encryption\n *\n * Resources are created in protected methods; subclasses may override to customize.\n * Other stacks obtain auth by calling **OpenHiAuthService.userPoolFromConstruct(scope)**,\n * **OpenHiAuthService.userPoolClientFromConstruct(scope)**,\n * **OpenHiAuthService.userPoolDomainFromConstruct(scope)**,\n * and **OpenHiAuthService.userPoolKmsKeyFromConstruct(scope)** for each resource needed.\n *\n * Only one instance of the auth service should exist per environment.\n *\n * @public\n */\nexport class OpenHiAuthService extends OpenHiService {\n static readonly SERVICE_TYPE = \"auth\" as const satisfies OpenHiServiceType;\n\n /**\n * Returns an IUserPool by looking up the Auth stack's User Pool ID from SSM.\n */\n static userPoolFromConstruct(scope: Construct): IUserPool {\n const userPoolId = DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: CognitoUserPool.SSM_PARAM_NAME,\n serviceType: OpenHiAuthService.SERVICE_TYPE,\n });\n return UserPool.fromUserPoolId(scope, \"user-pool\", userPoolId);\n }\n\n /**\n * Returns an IUserPoolClient by looking up the Auth stack's User Pool Client ID from SSM.\n */\n static userPoolClientFromConstruct(scope: Construct): IUserPoolClient {\n const userPoolClientId = DiscoverableStringParameter.valueForLookupName(\n scope,\n {\n ssmParamName: CognitoUserPoolClient.SSM_PARAM_NAME,\n serviceType: OpenHiAuthService.SERVICE_TYPE,\n },\n );\n return UserPoolClient.fromUserPoolClientId(\n scope,\n \"user-pool-client\",\n userPoolClientId,\n );\n }\n\n /**\n * Returns the dedicated fixture-seeder IUserPoolClient by looking up\n * its ID from SSM. Only non-prod auth stacks publish this parameter\n * (per the conditional in {@link createFixtureSeederClient}); calling\n * this against a prod-deployed stack will fail at lookup time.\n *\n * Consumed by `OpenHiRestApiService` (in non-prod) so the authorizer\n * accepts tokens issued by this client, and by the seed-fixtures CLI\n * to drive USER_PASSWORD_AUTH against this client's ID.\n */\n static fixtureSeederClientFromConstruct(scope: Construct): IUserPoolClient {\n const clientId = DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: CognitoFixtureSeederClient.SSM_PARAM_NAME,\n serviceType: OpenHiAuthService.SERVICE_TYPE,\n });\n return UserPoolClient.fromUserPoolClientId(\n scope,\n \"fixture-seeder-client\",\n clientId,\n );\n }\n\n /**\n * Returns an IUserPoolDomain by looking up the Auth stack's User Pool Domain from SSM.\n */\n static userPoolDomainFromConstruct(scope: Construct): IUserPoolDomain {\n const domainName = DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: CognitoUserPoolDomain.SSM_PARAM_NAME,\n serviceType: OpenHiAuthService.SERVICE_TYPE,\n });\n return UserPoolDomain.fromDomainName(scope, \"user-pool-domain\", domainName);\n }\n\n /**\n * Returns an IKey (KMS) by looking up the Auth stack's User Pool KMS Key ARN from SSM.\n */\n static userPoolKmsKeyFromConstruct(scope: Construct): IKey {\n const keyArn = DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: CognitoUserPoolKmsKey.SSM_PARAM_NAME,\n serviceType: OpenHiAuthService.SERVICE_TYPE,\n });\n return Key.fromKeyArn(scope, \"kms-key\", keyArn);\n }\n\n get serviceType(): string {\n return OpenHiAuthService.SERVICE_TYPE;\n }\n\n /** Override so this.props is typed with this service's options (e.g. userPoolProps). */\n public override props: OpenHiAuthServiceProps;\n\n public readonly userPoolKmsKey: IKey;\n public readonly preTokenGenerationLambda: IFunction;\n public readonly postAuthenticationLambda: IFunction;\n public readonly postConfirmationLambda: IFunction;\n public readonly userOnboardingWorkflow: UserOnboardingWorkflow;\n public readonly userPool: IUserPool;\n public readonly userPoolClient: IUserPoolClient;\n public readonly userPoolDomain: IUserPoolDomain;\n /**\n * Dedicated USER_PASSWORD_AUTH client for the seed-fixtures CLI.\n * Only created in non-prod environments (see\n * {@link createFixtureSeederClient}). `undefined` in prod.\n */\n public readonly fixtureSeederClient?: IUserPoolClient;\n\n /**\n * Cross-stack reference to the data store table. Cached so repeated\n * lookups share a single CDK construct id (\"dynamo-db-data-store\") in\n * this stack — a second `Table.fromTableName` call under the same scope\n * would collide.\n */\n private _dataStoreTable: ReturnType<\n typeof OpenHiDataService.dynamoDbDataStoreFromConstruct\n > | null = null;\n private _controlEventBus: IEventBus | null = null;\n\n constructor(ohEnv: OpenHiEnvironment, props: OpenHiAuthServiceProps = {}) {\n super(ohEnv, OpenHiAuthService.SERVICE_TYPE, props);\n this.props = props;\n\n this.userPoolKmsKey = this.createUserPoolKmsKey();\n this.preTokenGenerationLambda = this.createPreTokenGenerationLambda();\n this.postAuthenticationLambda = this.createPostAuthenticationLambda();\n this.postConfirmationLambda = this.createPostConfirmationLambda();\n this.userOnboardingWorkflow = this.createUserOnboardingWorkflow();\n this.userPool = this.createUserPool();\n this.grantPreTokenGenerationPermissions();\n this.grantPostAuthenticationPermissions();\n this.grantPostConfirmationPermissions();\n this.userPoolClient = this.createUserPoolClient();\n this.userPoolDomain = this.createUserPoolDomain();\n this.fixtureSeederClient = this.createFixtureSeederClient();\n }\n\n /**\n * Creates the KMS key for the Cognito User Pool and exports its ARN to SSM.\n * Look up via {@link OpenHiAuthService.userPoolKmsKeyFromConstruct}.\n * Override to customize.\n */\n protected createUserPoolKmsKey(): IKey {\n const key = new CognitoUserPoolKmsKey(this);\n new DiscoverableStringParameter(this, \"kms-key-param\", {\n ssmParamName: CognitoUserPoolKmsKey.SSM_PARAM_NAME,\n stringValue: key.keyArn,\n description:\n \"KMS key ARN for Cognito User Pool (e.g. custom sender); cross-stack reference\",\n });\n return key;\n }\n\n /**\n * Creates the Pre Token Generation Lambda (Cognito trigger). On every\n * sign-in and token refresh the Lambda resolves the User by Cognito `sub`\n * (GSI2) and injects `ohi_tid`, `ohi_wid`, `ohi_uid`, `ohi_uname` into\n * both the ID token and the access token (ADR 2026-03-17-01).\n */\n protected createPreTokenGenerationLambda(): IFunction {\n const construct = new PreTokenGenerationLambda(this, {\n dynamoTableName: this.dataStoreTable().tableName,\n });\n return construct.lambda;\n }\n\n /**\n * Creates the Post Authentication Lambda (Cognito trigger). Calls\n * AdminUserGlobalSignOut on every sign-in to enforce single-device-per-user\n * sessions per ADR 2026-03-17-01.\n */\n protected createPostAuthenticationLambda(): IFunction {\n const construct = new PostAuthenticationLambda(this);\n return construct.lambda;\n }\n\n /**\n * Creates the Post Confirmation Lambda (Cognito trigger). On sign-up\n * confirmation, publishes a control-plane workflow event; provisioning lives\n * behind EventBridge.\n */\n protected createPostConfirmationLambda(): IFunction {\n const construct = new PostConfirmationLambda(this, {\n controlEventBusName: this.controlEventBus().eventBusName,\n });\n return construct.lambda;\n }\n\n protected createUserOnboardingWorkflow(): UserOnboardingWorkflow {\n return new UserOnboardingWorkflow(this, {\n controlEventBus: this.controlEventBus(),\n dataStoreTable: this.dataStoreTable(),\n });\n }\n\n private dataStoreTable() {\n if (this._dataStoreTable === null) {\n this._dataStoreTable =\n OpenHiDataService.dynamoDbDataStoreFromConstruct(this);\n }\n return this._dataStoreTable;\n }\n\n private controlEventBus() {\n if (this._controlEventBus === null) {\n this._controlEventBus =\n OpenHiGlobalService.controlEventBusFromConstruct(this);\n }\n return this._controlEventBus;\n }\n\n /**\n * Creates the Cognito User Pool and exports its ID to SSM.\n * Look up via {@link OpenHiAuthService.userPoolFromConstruct}.\n * Override to customize.\n */\n protected createUserPool(): IUserPool {\n const userPool = new CognitoUserPool(this, {\n ...this.props.userPoolProps,\n customSenderKmsKey: this.userPoolKmsKey,\n });\n // Access-token-only claims require Pre Token Generation V2_0.\n userPool.addTrigger(\n UserPoolOperation.PRE_TOKEN_GENERATION_CONFIG,\n this.preTokenGenerationLambda,\n LambdaVersion.V2_0,\n );\n userPool.addTrigger(\n UserPoolOperation.POST_AUTHENTICATION,\n this.postAuthenticationLambda,\n );\n userPool.addTrigger(\n UserPoolOperation.POST_CONFIRMATION,\n this.postConfirmationLambda,\n );\n new DiscoverableStringParameter(this, \"user-pool-param\", {\n ssmParamName: CognitoUserPool.SSM_PARAM_NAME,\n stringValue: userPool.userPoolId,\n description:\n \"Cognito User Pool ID for this Auth stack; cross-stack reference\",\n });\n return userPool;\n }\n\n /**\n * Grants the Pre Token Generation Lambda read-only access on the data\n * store table and its GSIs. The Lambda only needs:\n * - `Query` on GSI2 to resolve a User by Cognito `sub`\n * - `GetItem` on the base table for direct User reads\n *\n * No write or scan access: a User missing `currentTenant`/`currentWorkspace`\n * falls into the absent-claims path; repair belongs in a separate backfill.\n */\n protected grantPreTokenGenerationPermissions(): void {\n const dataStoreTable = this.dataStoreTable();\n const dynamoActions = [\"dynamodb:GetItem\", \"dynamodb:Query\"] as const;\n dataStoreTable.grant(this.preTokenGenerationLambda, ...dynamoActions);\n this.preTokenGenerationLambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [...dynamoActions],\n resources: [`${dataStoreTable.tableArn}/index/*`],\n }),\n );\n }\n\n /**\n * Grants the Post Authentication Lambda permission to call\n * `cognito-idp:AdminUserGlobalSignOut`.\n *\n * Scoped via `Stack.of(this).formatArn` rather than `userPool.userPoolArn`\n * because the User Pool registers this Lambda as a Post Authentication\n * trigger, creating the cycle:\n * userPool → lambda (trigger ARN) → role policy → userPool ARN.\n * Using `formatArn` avoids referencing the User Pool resource directly\n * while still scoping to user pools in this account+region. The Lambda\n * is invoked only by Cognito with a Cognito-provided `event.userPoolId`,\n * so the runtime target is constrained by the trigger contract.\n */\n protected grantPostAuthenticationPermissions(): void {\n this.postAuthenticationLambda.addToRolePolicy(\n new PolicyStatement({\n actions: [\"cognito-idp:AdminUserGlobalSignOut\"],\n resources: [\n Stack.of(this).formatArn({\n service: \"cognito-idp\",\n resource: \"userpool\",\n resourceName: \"*\",\n }),\n ],\n }),\n );\n }\n\n /**\n * Grants the Post Confirmation Lambda publish-only access to the\n * control-plane event bus. Workflow Lambdas own DynamoDB writes.\n */\n protected grantPostConfirmationPermissions(): void {\n this.controlEventBus().grantPutEventsTo(this.postConfirmationLambda);\n }\n\n /**\n * Creates the User Pool Client and exports its ID to SSM (AUTH service type).\n * Look up via {@link OpenHiAuthService.userPoolClientFromConstruct}.\n * Override to customize.\n */\n protected createUserPoolClient(): IUserPoolClient {\n const client = new CognitoUserPoolClient(this, {\n userPool: this.userPool,\n });\n new DiscoverableStringParameter(this, \"user-pool-client-param\", {\n ssmParamName: CognitoUserPoolClient.SSM_PARAM_NAME,\n stringValue: client.userPoolClientId,\n description:\n \"Cognito User Pool Client ID for this Auth stack; cross-stack reference\",\n });\n return client;\n }\n\n /**\n * Creates the dedicated USER_PASSWORD_AUTH app client for the\n * `@openhi/seed-fixtures` CLI, **only** in non-prod environments.\n * Returns `undefined` when this stack is being deployed to a prod\n * stage so the prod auth stack carries no fixture-seeder code path.\n *\n * Operator post-deploy: create a `fixture-seeder` Cognito user with\n * a service password (manually via console or scripted with\n * `aws cognito-idp admin-create-user`); the CLI consumes those creds\n * via env vars to drive `InitiateAuth`.\n */\n protected createFixtureSeederClient(): IUserPoolClient | undefined {\n if (this.ohEnv.ohStage.stageType === OPEN_HI_STAGE.PROD) {\n return undefined;\n }\n const client = new CognitoFixtureSeederClient(this, {\n userPool: this.userPool,\n });\n new DiscoverableStringParameter(this, \"fixture-seeder-client-param\", {\n ssmParamName: CognitoFixtureSeederClient.SSM_PARAM_NAME,\n stringValue: client.userPoolClientId,\n description:\n \"Cognito User Pool Client ID for the OpenHI fixture-seeder CLI \" +\n \"(USER_PASSWORD_AUTH; non-prod only); cross-stack reference\",\n });\n return client;\n }\n\n /**\n * Creates the User Pool Domain (Cognito hosted UI) and exports domain name to SSM.\n * Look up via {@link OpenHiAuthService.userPoolDomainFromConstruct}.\n * Override to customize.\n */\n protected createUserPoolDomain(): IUserPoolDomain {\n const domain = new CognitoUserPoolDomain(this, {\n userPool: this.userPool,\n cognitoDomain: {\n domainPrefix: `auth-${this.branchHash}`,\n },\n });\n new DiscoverableStringParameter(this, \"user-pool-domain-param\", {\n ssmParamName: CognitoUserPoolDomain.SSM_PARAM_NAME,\n stringValue: domain.domainName,\n description:\n \"Cognito User Pool Domain (hosted UI) for this Auth stack; cross-stack reference\",\n });\n return domain;\n }\n}\n","import { OPEN_HI_STAGE } from \"@openhi/config\";\nimport { StreamViewType, ITable, Table } from \"aws-cdk-lib/aws-dynamodb\";\nimport { IEventBus } from \"aws-cdk-lib/aws-events\";\nimport * as kinesis from \"aws-cdk-lib/aws-kinesis\";\nimport { Construct } from \"constructs\";\nimport { OpenHiAuthService } from \"./open-hi-auth-service\";\nimport { OpenHiGlobalService } from \"./open-hi-global-service\";\nimport { OpenHiEnvironment } from \"../app/open-hi-environment\";\nimport {\n OpenHiService,\n OpenHiServiceProps,\n OpenHiServiceType,\n} from \"../app/open-hi-service\";\nimport { DataStoreHistoricalArchive } from \"../components/dynamodb/data-store-historical-archive\";\nimport {\n DynamoDbDataStore,\n getDynamoDbDataStoreTableName,\n} from \"../components/dynamodb/dynamo-db-data-store\";\nimport { DataStorePostgresReplica } from \"../components/postgres/data-store-postgres-replica\";\nimport { SeedDemoDataWorkflow } from \"../workflows/control-plane/seed-demo-data\";\nimport { SeedSystemDataWorkflow } from \"../workflows/control-plane/seed-system-data\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/services/open-hi-data-service.md\n */\n\nexport type OpenHiDataServiceProps = OpenHiServiceProps;\n\n/**\n * Data storage service stack: centralizes DynamoDB, S3, and other persistence\n * resources for OpenHI. Creates the single-table data store in a protected\n * method; subclasses may override to customize. EventBridge event buses\n * (data, ops, control) are owned by {@link OpenHiGlobalService} so they deploy\n * ahead of regional services.\n */\nexport class OpenHiDataService extends OpenHiService {\n static readonly SERVICE_TYPE = \"data\" as const satisfies OpenHiServiceType;\n\n /**\n * Returns the data store table by name. Use from other stacks (e.g. REST API Lambda) to obtain an ITable reference.\n */\n static dynamoDbDataStoreFromConstruct(\n scope: Construct,\n id = \"dynamo-db-data-store\",\n ): ITable {\n return Table.fromTableName(scope, id, getDynamoDbDataStoreTableName(scope));\n }\n\n get serviceType(): string {\n return OpenHiDataService.SERVICE_TYPE;\n }\n\n /** Override so this.props is typed with this service's options. */\n public override props: OpenHiDataServiceProps;\n\n /**\n * The single-table DynamoDB data store. Use {@link OpenHiDataService.dynamoDbDataStoreFromConstruct}\n * from other stacks to obtain an ITable reference by name.\n */\n public readonly dataStore: ITable;\n\n /**\n * Kinesis stream receiving DynamoDB item-level changes for the data store table.\n */\n public readonly dataStoreChangeStream: kinesis.IStream;\n\n /**\n * Historical archive pipeline (Kinesis → Firehose → S3) and data-event-bus\n * notifications for current FHIR resources (ADRs 2026-03-11-02, 2026-03-02-01).\n */\n public readonly dataStoreHistoricalArchive: DataStoreHistoricalArchive;\n\n /**\n * Postgres replication tier (ADR 2026-04-17-01, phase 1). A second consumer\n * on the change stream that projects current FHIR resources into a JSONB\n * `resources` table on Aurora Serverless v2. Phase 1 is replication-only;\n * the read path is not wired up yet.\n */\n public readonly dataStorePostgresReplica: DataStorePostgresReplica;\n\n /**\n * Deploy-triggered workflow that idempotently re-asserts the\n * platform-singleton control-plane records (today: the three canonical\n * Roles via `PLATFORM_ROLE_CONCEPTS`; future: additional system\n * data). Subscribes to `platform.deployment-completed.v1` on the\n * control event bus and dedups via the shared `WorkflowDedupTable`.\n */\n public readonly seedSystemDataWorkflow: SeedSystemDataWorkflow;\n\n /**\n * Deploy-triggered workflow that idempotently re-asserts the demo\n * data graph (placeholder + 3 demo Tenants + 5 Workspaces; per\n * dev-user Cognito users with their DynamoDB User records,\n * Memberships, and RoleAssignments). **Non-prod only** —\n * `undefined` on prod stages. The synth-time stage gate in\n * {@link createSeedDemoDataWorkflow} is the only guarantee\n * separating prod stacks from the workflow's IAM grants and rule\n * target; the construct itself never checks the stage.\n */\n public readonly seedDemoDataWorkflow?: SeedDemoDataWorkflow;\n\n /**\n * Cached control-event-bus lookup. `OpenHiGlobalService.controlEventBusFromConstruct`\n * registers a child `EventBus.fromEventBusName` construct with a\n * fixed id under the scope it is passed, so calling it twice on the\n * same `OpenHiDataService` instance collides. The cache mirrors the\n * `private controlEventBus()` pattern already used in\n * `OpenHiAuthService`. Use {@link controlEventBus} from this class\n * — never call the static lookup from inside `OpenHiDataService`.\n */\n private _controlEventBus: IEventBus | null = null;\n\n constructor(ohEnv: OpenHiEnvironment, props: OpenHiDataServiceProps = {}) {\n super(ohEnv, OpenHiDataService.SERVICE_TYPE, props);\n this.props = props;\n\n this.dataStoreChangeStream = new kinesis.Stream(\n this,\n \"data-store-change-stream\",\n {\n streamName: `openhi-dstore-cdc-${this.branchHash}`,\n streamMode: kinesis.StreamMode.ON_DEMAND,\n // CDK default for kinesis.Stream is RETAIN, which strands the stream\n // when a non-prod stack is destroyed. Use the service's policy so\n // non-prod tears down cleanly while prod retains.\n removalPolicy: this.removalPolicy,\n },\n );\n\n this.dataStore = this.createDataStore();\n\n this.dataStoreHistoricalArchive = new DataStoreHistoricalArchive(\n this,\n \"data-store-historical-archive\",\n {\n kinesisStream: this.dataStoreChangeStream,\n removalPolicy: this.removalPolicy,\n stackHash: this.stackHash,\n dataEventBus: OpenHiGlobalService.dataEventBusFromConstruct(this),\n },\n );\n\n this.dataStorePostgresReplica = new DataStorePostgresReplica(\n this,\n \"data-store-postgres-replica\",\n {\n kinesisStream: this.dataStoreChangeStream,\n removalPolicy: this.removalPolicy,\n stackHash: this.stackHash,\n branchHash: this.branchHash,\n },\n );\n\n this.seedSystemDataWorkflow = this.createSeedSystemDataWorkflow();\n this.seedDemoDataWorkflow = this.createSeedDemoDataWorkflow();\n }\n\n /**\n * Lazily looks up the control event bus exactly once per\n * `OpenHiDataService` instance and caches the reference. Every\n * workflow that consumes the bus must read it through this method\n * — see {@link _controlEventBus} for the underlying collision risk.\n */\n private controlEventBus(): IEventBus {\n if (this._controlEventBus === null) {\n this._controlEventBus =\n OpenHiGlobalService.controlEventBusFromConstruct(this);\n }\n return this._controlEventBus;\n }\n\n /**\n * Creates the seed-system-data workflow. Override to customize.\n */\n protected createSeedSystemDataWorkflow(): SeedSystemDataWorkflow {\n return new SeedSystemDataWorkflow(this, {\n controlEventBus: this.controlEventBus(),\n dataStoreTable: this.dataStore,\n });\n }\n\n /**\n * Creates the seed-demo-data workflow — but only on non-prod\n * stages. Returns `undefined` on prod so the workflow literally\n * does not exist in prod stacks. Override to customize.\n */\n protected createSeedDemoDataWorkflow(): SeedDemoDataWorkflow | undefined {\n if (this.ohEnv.ohStage.stageType === OPEN_HI_STAGE.PROD) {\n return undefined;\n }\n return new SeedDemoDataWorkflow(this, {\n controlEventBus: this.controlEventBus(),\n dataStoreTable: this.dataStore,\n userPool: OpenHiAuthService.userPoolFromConstruct(this),\n });\n }\n\n /**\n * Creates the single-table DynamoDB data store.\n * Override to customize.\n */\n protected createDataStore(): ITable {\n return new DynamoDbDataStore(this, \"dynamo-db-data-store\", {\n kinesisStream: this.dataStoreChangeStream,\n stream: StreamViewType.NEW_AND_OLD_IMAGES,\n });\n }\n}\n","import {\n Certificate,\n CertificateValidation,\n ICertificate,\n} from \"aws-cdk-lib/aws-certificatemanager\";\nimport { EventBus, IEventBus } from \"aws-cdk-lib/aws-events\";\nimport {\n HostedZone,\n HostedZoneAttributes,\n IHostedZone,\n} from \"aws-cdk-lib/aws-route53\";\nimport { StringParameter } from \"aws-cdk-lib/aws-ssm\";\nimport { Construct } from \"constructs\";\nimport { OpenHiEnvironment } from \"../app/open-hi-environment\";\nimport {\n OpenHiService,\n OpenHiServiceProps,\n OpenHiServiceType,\n} from \"../app/open-hi-service\";\nimport { RootWildcardCertificate } from \"../components/acm/root-wildcard-certificate\";\nimport { WorkflowDedupTable } from \"../components/dynamodb/workflow-dedup-table\";\nimport { ControlEventBus } from \"../components/event-bridge/control-event-bus\";\nimport { DataEventBus } from \"../components/event-bridge/data-event-bus\";\nimport { OpsEventBus } from \"../components/event-bridge/ops-event-bus\";\nimport { ChildHostedZone } from \"../components/route-53/child-hosted-zone\";\nimport { DiscoverableStringParameter } from \"../components/ssm\";\nimport { PlatformDeployBridge } from \"../workflows/control-plane/platform-deploy-bridge\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/services/open-hi-global-service.md\n */\n\nexport interface OpenHiGlobalServiceProps extends OpenHiServiceProps {}\n\n/**\n * Global Infrastructure stack: owns global DNS, certificates, and the\n * cross-region EventBridge buses (data, ops, control). Resources (root zone,\n * optional child zone, wildcard cert, data/ops/control buses) are created in\n * protected methods; subclasses may override to customize.\n */\nexport class OpenHiGlobalService extends OpenHiService {\n static readonly SERVICE_TYPE = \"global\" as const satisfies OpenHiServiceType;\n\n /**\n * Returns an IHostedZone from the given attributes (no SSM). Use when the zone is imported from config.\n */\n static rootHostedZoneFromConstruct(\n scope: Construct,\n props: HostedZoneAttributes,\n ): IHostedZone {\n return HostedZone.fromHostedZoneAttributes(scope, \"root-zone\", props);\n }\n\n /**\n * Returns an ICertificate by looking up the Global stack's wildcard cert ARN from SSM.\n */\n static rootWildcardCertificateFromConstruct(scope: Construct): ICertificate {\n const certificateArn = StringParameter.valueForStringParameter(\n scope,\n RootWildcardCertificate.ssmParameterName(),\n );\n return Certificate.fromCertificateArn(\n scope,\n \"wildcard-certificate\",\n certificateArn,\n );\n }\n\n /**\n * Returns an IHostedZone by looking up the child hosted zone ID from SSM. Defaults to GLOBAL service type.\n */\n static childHostedZoneFromConstruct(\n scope: Construct,\n props: { zoneName: string; serviceType?: OpenHiServiceType },\n ): IHostedZone {\n const hostedZoneId = DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: ChildHostedZone.SSM_PARAM_NAME,\n serviceType: props.serviceType ?? OpenHiGlobalService.SERVICE_TYPE,\n });\n return HostedZone.fromHostedZoneAttributes(scope, \"child-zone\", {\n hostedZoneId,\n zoneName: props.zoneName,\n });\n }\n\n /**\n * Returns the data event bus by name (deterministic per branch). Use from other stacks to obtain an IEventBus reference.\n */\n static dataEventBusFromConstruct(scope: Construct): IEventBus {\n return EventBus.fromEventBusName(\n scope,\n \"data-event-bus\",\n DataEventBus.getEventBusName(scope),\n );\n }\n\n /**\n * Returns the ops event bus by name (deterministic per branch). Use from other stacks to obtain an IEventBus reference.\n */\n static opsEventBusFromConstruct(scope: Construct): IEventBus {\n return EventBus.fromEventBusName(\n scope,\n \"ops-event-bus\",\n OpsEventBus.getEventBusName(scope),\n );\n }\n\n /**\n * Returns the control-plane event bus by name (deterministic per branch). Use from other stacks to obtain an IEventBus reference.\n */\n static controlEventBusFromConstruct(scope: Construct): IEventBus {\n return EventBus.fromEventBusName(\n scope,\n \"control-event-bus\",\n ControlEventBus.getEventBusName(scope),\n );\n }\n\n /**\n * Returns the workflow dedup table by name (deterministic per branch).\n * Use from other stacks to obtain an ITable reference. Consumer Lambdas\n * are typically wired via `WorkflowDedupTable.grantConsumer(fn, name)`\n * on the owning service's `workflowDedupTable` reference; the\n * `tableNameFromLookup` / `tableArnFromLookup` SSM helpers on the\n * construct cover cross-stack consumers that need only the name/ARN.\n */\n static workflowDedupTableNameFromLookup(scope: Construct): string {\n return WorkflowDedupTable.tableNameFromLookup(scope);\n }\n\n get serviceType(): string {\n return OpenHiGlobalService.SERVICE_TYPE;\n }\n\n /** Override so this.props is typed with this service's options. */\n public override props: OpenHiGlobalServiceProps;\n\n public readonly rootHostedZone: IHostedZone;\n public readonly childHostedZone?: IHostedZone;\n public readonly rootWildcardCertificate: ICertificate;\n\n /**\n * Event bus for data-related events (ingestion, transformation, storage).\n * Other stacks obtain it via {@link OpenHiGlobalService.dataEventBusFromConstruct}.\n */\n public readonly dataEventBus: IEventBus;\n\n /**\n * Event bus for operational events (monitoring, alerting, system health).\n * Other stacks obtain it via {@link OpenHiGlobalService.opsEventBusFromConstruct}.\n */\n public readonly opsEventBus: IEventBus;\n\n /**\n * Event bus for control-plane lifecycle and command events.\n * Other stacks obtain it via {@link OpenHiGlobalService.controlEventBusFromConstruct}.\n */\n public readonly controlEventBus: IEventBus;\n\n /**\n * Bridge that watches CloudFormation Stack Status Change events on the\n * default AWS bus and republishes terminal-success events for OpenHi-tagged\n * stacks onto {@link controlEventBus} as `platform.deployment-completed.v1`.\n */\n public readonly platformDeployBridge: PlatformDeployBridge;\n\n /**\n * Shared dedup table every retryable workflow consumer dedupes against\n * (TR-015). Singleton per deployment — provisioned here on the global\n * stack so consumer stacks reach it via SSM lookups, not props.\n */\n public readonly workflowDedupTable: WorkflowDedupTable;\n\n constructor(ohEnv: OpenHiEnvironment, props: OpenHiGlobalServiceProps = {}) {\n super(ohEnv, OpenHiGlobalService.SERVICE_TYPE, props);\n this.props = props;\n\n this.validateConfig(props);\n\n this.rootHostedZone = this.createRootHostedZone();\n this.childHostedZone = this.createChildHostedZone();\n this.rootWildcardCertificate = this.createRootWildcardCertificate();\n this.dataEventBus = this.createDataEventBus();\n this.opsEventBus = this.createOpsEventBus();\n this.controlEventBus = this.createControlEventBus();\n this.workflowDedupTable = this.createWorkflowDedupTable();\n this.platformDeployBridge = this.createPlatformDeployBridge();\n }\n\n /**\n * Validates that config required for the Global stack is present.\n */\n protected validateConfig(props: OpenHiGlobalServiceProps): void {\n const { config } = props;\n if (!config) {\n throw new Error(\"Config is required\");\n }\n if (!config.zoneName) {\n throw new Error(\"Zone name is required to import the root zone\");\n }\n if (!config.hostedZoneId) {\n throw new Error(\"Hosted zone ID is required to import the root zone\");\n }\n }\n\n /**\n * Creates the root hosted zone (imported via attributes from config).\n * Override to customize or create the zone.\n */\n protected createRootHostedZone(): IHostedZone {\n return OpenHiGlobalService.rootHostedZoneFromConstruct(this, {\n zoneName: this.config.zoneName!,\n hostedZoneId: this.config.hostedZoneId!,\n });\n }\n\n /**\n * Creates the optional child hosted zone (e.g. branch subdomain).\n * Override to create a child zone when config provides childHostedZoneAttributes.\n * If you create a ChildHostedZone, also create a DiscoverableStringParameter\n * with ChildHostedZone.SSM_PARAM_NAME and the zone's hostedZoneId.\n */\n protected createChildHostedZone(): IHostedZone | undefined {\n return undefined;\n }\n\n /**\n * Creates the root wildcard certificate. On main branch, creates a new cert\n * with DNS validation; otherwise imports from SSM.\n * Override to customize certificate creation.\n */\n protected createRootWildcardCertificate(): ICertificate {\n if (this.branchName === \"main\") {\n return new RootWildcardCertificate(this, {\n domainName: `*.${this.rootHostedZone.zoneName}`,\n subjectAlternativeNames: [this.rootHostedZone.zoneName],\n validation: CertificateValidation.fromDns(this.rootHostedZone),\n });\n }\n return OpenHiGlobalService.rootWildcardCertificateFromConstruct(this);\n }\n\n /**\n * Creates the data event bus.\n * Override to customize.\n */\n protected createDataEventBus(): IEventBus {\n return new DataEventBus(this);\n }\n\n /**\n * Creates the ops event bus.\n * Override to customize.\n */\n protected createOpsEventBus(): IEventBus {\n return new OpsEventBus(this);\n }\n\n /**\n * Creates the control-plane event bus.\n * Override to customize.\n */\n protected createControlEventBus(): IEventBus {\n return new ControlEventBus(this);\n }\n\n /**\n * Creates the platform deploy bridge that republishes CloudFormation\n * Stack Status Change events onto the control event bus.\n * Override to customize.\n */\n protected createPlatformDeployBridge(): PlatformDeployBridge {\n return new PlatformDeployBridge(this, {\n controlEventBus: this.controlEventBus,\n });\n }\n\n /**\n * Creates the shared workflow dedup table (TR-015 singleton).\n * Override to customize.\n */\n protected createWorkflowDedupTable(): WorkflowDedupTable {\n return new WorkflowDedupTable(this, \"workflow-dedup-table\");\n }\n}\n","/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/platform-deploy-bridge/index.md\n */\n\n/** EventBridge `source` for the AWS-native CloudFormation events the bridge listens to. */\nexport const CLOUDFORMATION_EVENT_SOURCE = \"aws.cloudformation\" as const;\n\n/** EventBridge `detail-type` for terminal stack status events. */\nexport const CLOUDFORMATION_STACK_STATUS_CHANGE_DETAIL_TYPE =\n \"CloudFormation Stack Status Change\" as const;\n\n/** Stack statuses the bridge republishes. Other statuses are pre-filtered at the rule. */\nexport const BRIDGED_STATUSES = [\"CREATE_COMPLETE\", \"UPDATE_COMPLETE\"] as const;\nexport type BridgedStatus = (typeof BRIDGED_STATUSES)[number];\n\n/** Env var the bridge handler reads to discover the control event bus name. */\nexport const CONTROL_EVENT_BUS_NAME_ENV_VAR = \"CONTROL_EVENT_BUS_NAME\";\n\n/**\n * Env var the bridge handler reads for the resolved\n * `openhi:repo-name`-shaped tag key. Resolved at synth time from the host\n * stack's `appName` + {@link OPENHI_TAG_SUFFIX_REPO_NAME}.\n */\nexport const OPENHI_REPO_TAG_KEY_ENV_VAR = \"OPENHI_REPO_TAG_KEY\";\n\n/**\n * Env var the bridge handler reads for the resolved `openhi:` tag prefix.\n * Resolved at synth time from the host stack's `appName`. Used to project\n * the relevant stack tags into the published envelope.\n */\nexport const OPENHI_TAG_KEY_PREFIX_ENV_VAR = \"OPENHI_TAG_KEY_PREFIX\";\n\n/** Free-form `actor.system` value per TR-016 § Decision points #1 (bootstrap-role pattern). */\nexport const PLATFORM_DEPLOY_BRIDGE_ACTOR_SYSTEM = \"platform-deploy-bridge\";\n\n/**\n * Subset of the CloudFormation Stack Status Change `detail` field the\n * bridge handler reads. AWS-side schema lives at\n * <https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/monitoring-cloudformation.html>.\n */\nexport interface CloudFormationStackStatusChangeDetail {\n readonly \"stack-id\": string;\n readonly \"logical-resource-id\"?: string;\n readonly \"physical-resource-id\"?: string;\n readonly \"status-details\": {\n readonly status: string;\n readonly \"status-reason\"?: string;\n };\n readonly \"resource-type\"?: string;\n readonly \"client-request-token\"?: string;\n}\n","import { IEventBus } from \"aws-cdk-lib/aws-events\";\nimport { Construct } from \"constructs\";\nimport { PlatformDeployBridgeLambda } from \"./platform-deploy-bridge-lambda\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/platform-deploy-bridge/index.md\n */\n\nexport interface PlatformDeployBridgeProps {\n /** Destination control event bus the bridge republishes onto. */\n readonly controlEventBus: IEventBus;\n}\n\n/**\n * Source-side reactor that watches CloudFormation Stack Status Change\n * events on the default AWS bus and republishes terminal-success events\n * (`CREATE_COMPLETE` / `UPDATE_COMPLETE`) for OpenHi-tagged stacks onto\n * the control event bus as `platform.deployment-completed.v1`.\n *\n * Implements row 4 of the workflow placement matrix\n * (codedrifters/openhi#953): ops-plane reactor → republishes to\n * control event bus.\n */\nexport class PlatformDeployBridge extends Construct {\n public readonly bridgeLambda: PlatformDeployBridgeLambda;\n\n constructor(scope: Construct, props: PlatformDeployBridgeProps) {\n super(scope, \"platform-deploy-bridge\");\n\n this.bridgeLambda = new PlatformDeployBridgeLambda(this, {\n controlEventBus: props.controlEventBus,\n });\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Duration, Stack } from \"aws-cdk-lib\";\nimport { IEventBus, Rule } from \"aws-cdk-lib/aws-events\";\nimport { LambdaFunction } from \"aws-cdk-lib/aws-events-targets\";\nimport { Effect, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\nimport {\n BRIDGED_STATUSES,\n CLOUDFORMATION_EVENT_SOURCE,\n CLOUDFORMATION_STACK_STATUS_CHANGE_DETAIL_TYPE,\n CONTROL_EVENT_BUS_NAME_ENV_VAR,\n OPENHI_REPO_TAG_KEY_ENV_VAR,\n OPENHI_TAG_KEY_PREFIX_ENV_VAR,\n} from \"./events\";\nimport {\n OPENHI_TAG_SUFFIX_REPO_NAME,\n OpenHiService,\n openHiTagKey,\n} from \"../../../app/open-hi-service\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/platform-deploy-bridge/index.md\n */\n\nconst HANDLER_NAME = \"platform-deploy-bridge.handler.js\";\n\n/**\n * Resolve handler entry so it works from src/ (tests) or lib/ (built).\n */\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n return path.join(dirname, \"..\", \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n}\n\nexport interface PlatformDeployBridgeLambdaProps {\n /** Destination control event bus the bridge republishes onto. */\n readonly controlEventBus: IEventBus;\n}\n\n/**\n * Lambda that bridges CloudFormation Stack Status Change events from the\n * default AWS bus into typed `platform.deployment-completed.v1` envelopes on\n * the OpenHI control event bus.\n *\n * Owns its EventBridge Rule (on the default AWS bus) and the IAM\n * permissions it needs — colocating routing + permissions with the\n * function they target.\n *\n * The EventBridge rule pre-filters by stack-id prefix so the rule (and\n * therefore the Lambda) only fires on the host stack's own branch deploys.\n * This prevents cross-branch leak when multiple branches are deployed into\n * the same account.\n */\nexport class PlatformDeployBridgeLambda extends Construct {\n public readonly lambda: NodejsFunction;\n public readonly rule: Rule;\n\n constructor(scope: Construct, props: PlatformDeployBridgeLambdaProps) {\n super(scope, \"platform-deploy-bridge-lambda\");\n\n // Resolve the host OpenHiService so we can pin the rule to this\n // branch's stacks and project the appName-aware tag key.\n const service = OpenHiService.of(this) as OpenHiService;\n const repoTagKey = openHiTagKey(\n service.appName,\n OPENHI_TAG_SUFFIX_REPO_NAME,\n );\n const tagKeyPrefix = `${service.appName}:`;\n\n // Derive the shared sibling-stack prefix from this stack's own\n // synthesized name. `service.branchHash` alone (e.g. `4e4512-`)\n // is wrong because CDK prepends the Stage hierarchy\n // (`<stage>-<environment>-…`) to every stack name, so the\n // CloudFormation events carry stack-ids like\n // `dev-primary-4e4512-data-…`. Stripping the known\n // `-<serviceId>-<account>-<region>` suffix off the bridge's own\n // stack name yields the prefix every sibling stack shares.\n const ownStackName = Stack.of(this).stackName;\n const ownSuffix = `-${service.serviceId}-${Stack.of(this).account}-${\n Stack.of(this).region\n }`;\n const sharedPrefix = ownStackName.endsWith(ownSuffix)\n ? ownStackName.slice(0, -ownSuffix.length)\n : service.branchHash;\n const stackIdPrefix = `arn:aws:cloudformation:${Stack.of(this).region}:${\n Stack.of(this).account\n }:stack/${sharedPrefix}-`;\n\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 256,\n timeout: Duration.seconds(30),\n environment: {\n [CONTROL_EVENT_BUS_NAME_ENV_VAR]: props.controlEventBus.eventBusName,\n [OPENHI_REPO_TAG_KEY_ENV_VAR]: repoTagKey,\n [OPENHI_TAG_KEY_PREFIX_ENV_VAR]: tagKeyPrefix,\n },\n });\n\n // Fetch stack tags so the handler can project them into the envelope.\n // Scope to stacks in the deploying account/region.\n this.lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\"cloudformation:DescribeStacks\"],\n resources: [\n `arn:aws:cloudformation:${Stack.of(this).region}:${\n Stack.of(this).account\n }:stack/*`,\n ],\n }),\n );\n\n // Republish the projected envelope onto the control event bus.\n props.controlEventBus.grantPutEventsTo(this.lambda);\n\n // Rule lives on the default AWS bus — that is where AWS publishes\n // its own CloudFormation Stack Status Change events. Omitting\n // `eventBus` defaults to the account's default bus.\n //\n // The `stack-id` prefix scopes the rule to this branch's own stacks\n // only (OpenHi stack names start with the deterministic per-branch\n // hash), so other branches' deploys never trigger this Lambda even\n // though every branch deploys its own bridge into the same account.\n this.rule = new Rule(this, \"rule\", {\n eventPattern: {\n source: [CLOUDFORMATION_EVENT_SOURCE],\n detailType: [CLOUDFORMATION_STACK_STATUS_CHANGE_DETAIL_TYPE],\n detail: {\n \"stack-id\": [{ prefix: stackIdPrefix }],\n \"status-details\": {\n status: [...BRIDGED_STATUSES],\n },\n },\n },\n targets: [\n new LambdaFunction(this.lambda, {\n retryAttempts: 2,\n maxEventAge: Duration.hours(2),\n }),\n ],\n });\n }\n}\n","import { PLATFORM_ROLE_CODE, type PlatformRoleCode } from \"@openhi/types\";\nimport { PlatformSystemDataSeededV1 } from \"@openhi/workflows\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-demo-data/events.md\n */\n\n/**\n * Stable logical name this workflow registers with the shared\n * `WorkflowDedupTable` (TR-015). Used in both the construct grant\n * (`workflowDedupTable.grantConsumer(lambda, SEED_DEMO_DATA_CONSUMER_NAME)`)\n * and the handler's runtime `recordIfAbsent` call — keep them aligned by\n * importing this constant in both places.\n */\nexport const SEED_DEMO_DATA_CONSUMER_NAME = \"seed-demo-data\";\n\n/** FHIR `Identifier.system` for the demo-scenario URN scheme. */\nexport const DEMO_URN_SYSTEM = \"urn:openhi:demo\";\n\n/**\n * FHIR `Identifier.system` for the canonical OpenHI resource URN\n * (ADR 2026-03-12-01). The value form is\n * `urn:ohi:<tenantId>:<workspaceId>:<resourceType>:<id>`; empty\n * workspaceId means the resource has tenant-wide scope (or is itself\n * the workspace identity, in the Workspace case).\n */\nexport const OPENHI_RESOURCE_URN_SYSTEM = \"http://openhi.org/\";\n\n/**\n * Period stamped on every demo Membership and RoleAssignment. Fixed\n * start so re-runs produce byte-identical bodies (decision #4); no\n * end so demo memberships are open-ended.\n */\nexport const DEMO_PERIOD = { start: \"2026-01-01T00:00:00Z\" } as const;\n\n/**\n * Sentinel `tenantId` used on every dev user's `system-admin`\n * RoleAssignment. A `system-admin` RA is platform-scoped (it spans\n * every tenant), but the RoleAssignment entity requires a tenantId on\n * its key for sharding — there is no real tenant to point at. The\n * `\"platform\"` literal is a reserved value that never matches a real\n * Tenant id and signals \"this RA scopes across all tenants\".\n *\n * Renaming this constant is a wire-format break — the IAM grant in\n * `seed-demo-data-lambda.ts` enumerates exact-match `LeadingKeys`\n * computed from this value, and the in-band records written under it\n * become unreachable if the sentinel changes.\n */\nexport const PLATFORM_SCOPE_TENANT_ID = \"platform\";\n\n/** Placeholder Tenant id seeded by the workflow as the dev-user `currentTenant`. */\nexport const PLACEHOLDER_TENANT_ID = \"placeholder-tenant-id\";\n\n/** Placeholder Workspace id seeded by the workflow as the dev-user `currentWorkspace`. */\nexport const PLACEHOLDER_WORKSPACE_ID = \"placeholder-workspace-id\";\n\n/**\n * Dev-user descriptor. Every entry produces:\n * - one Cognito user (idempotent create-then-skip),\n * - one DynamoDB User record (id = `dev-<email-local-part>`),\n * - four Memberships (placeholder + the three demo tenants),\n * - four `tenant-admin` RoleAssignments (one per tenant the user\n * belongs to),\n * - one `system-admin` RoleAssignment scoped to {@link PLATFORM_SCOPE_TENANT_ID}.\n */\nexport interface DemoDevUser {\n /** Stable DynamoDB User id. */\n readonly id: string;\n /** Email used as the Cognito `Username` and as the seed for the password algorithm. */\n readonly email: string;\n}\n\n/**\n * Hardcoded dev-user roster. Adding a new developer is a one-line\n * change here plus a re-deploy. The list intentionally lives in-source\n * (not behind an env-var seam) so the IAM grant in\n * `seed-demo-data-lambda.ts` can enumerate per-user PKs at synth time.\n */\nexport const DEV_USERS: ReadonlyArray<DemoDevUser> = [\n { id: \"dev-russell\", email: \"russell@codedrifters.com\" },\n { id: \"dev-cameron\", email: \"cameron@codedrifters.com\" },\n { id: \"dev-neelima\", email: \"neelima@codedrifters.com\" },\n { id: \"dev-garon\", email: \"garon@codedrifters.com\" },\n { id: \"dev-dave\", email: \"dave@codedrifters.com\" },\n { id: \"dev-drew\", email: \"drew@codedrifters.com\" },\n { id: \"dev-jessica\", email: \"jessica@codedrifters.com\" },\n { id: \"dev-jared\", email: \"jared@codedrifters.com\" },\n { id: \"dev-goddess\", email: \"goddess@codedrifters.com\" },\n];\n\n/**\n * A single workspace inside a demo tenant. The mixed tenant has two\n * workspaces (wound-care and primary-care sub-workspaces); the other\n * two tenants have one each.\n */\nexport interface DemoWorkspaceSpec {\n /** Stable id (DynamoDB record id; also drives the canonical OHI URN). */\n readonly id: string;\n /** FHIR `Workspace.name`. */\n readonly name: string;\n /**\n * Role suffix used in the demo URN value (`<scenario>:<roleSuffix>`).\n * Mirrors seed-fixtures' role suffix convention: `workspace` for\n * single-workspace tenants, `workspace-<sub>` for the mixed tenant.\n */\n readonly roleSuffix: string;\n}\n\n/**\n * One demo tenant + the workspaces it owns. Re-exported via {@link\n * DEMO_TENANT_SPECS} as the canonical list; iterate that constant in\n * the handler and the IAM-grant builder so the value sets agree.\n */\nexport interface DemoTenantSpec {\n /**\n * Scenario slug — `placeholder`, `demo-wound-care`, `demo-primary-care`,\n * `demo-mixed`. The placeholder tenant's slug is `placeholder`; the\n * three demo tenants mirror seed-fixtures' `fixture-*` slugs renamed\n * to `demo-*`.\n */\n readonly scenario: string;\n /** Stable id (DynamoDB record id; also drives the canonical OHI URN). */\n readonly tenantId: string;\n /** FHIR `Tenant.name`. */\n readonly tenantName: string;\n /** Workspaces owned by this tenant. */\n readonly workspaces: ReadonlyArray<DemoWorkspaceSpec>;\n}\n\n/**\n * The full demo-tenant graph. Four entries: the placeholder tenant the\n * JWT-claim fallback resolves to, plus the three v1 demo scenarios\n * (OPS-009 §\"v1 scenarios\"):\n *\n * 0. Placeholder tenant — dereferences the JWT-claim fallback in\n * `pre-token-generation.handler.ts` and acts as every dev user's\n * `currentTenant`/`currentWorkspace` so Post-Confirmation skips\n * default provisioning when a seeded User signs in.\n * 1. Single-workspace wound-care tenant.\n * 2. Single-workspace primary-care tenant.\n * 3. Two-workspace mixed tenant — exercises the cross-workspace\n * isolation flow that single-workspace tenants cannot.\n */\nexport const DEMO_TENANT_SPECS: ReadonlyArray<DemoTenantSpec> = [\n {\n scenario: \"placeholder\",\n tenantId: PLACEHOLDER_TENANT_ID,\n tenantName: \"OpenHI Placeholder Tenant\",\n workspaces: [\n {\n id: PLACEHOLDER_WORKSPACE_ID,\n name: \"OpenHI Placeholder Workspace\",\n roleSuffix: \"workspace\",\n },\n ],\n },\n {\n scenario: \"demo-wound-care\",\n tenantId: \"demo-wound-care-tenant\",\n tenantName: \"Cedarbrook Wound Healing Institute\",\n workspaces: [\n {\n id: \"demo-wound-care-workspace\",\n name: \"Cedarbrook Outpatient Wound Clinic\",\n roleSuffix: \"workspace\",\n },\n ],\n },\n {\n scenario: \"demo-primary-care\",\n tenantId: \"demo-primary-care-tenant\",\n tenantName: \"Maple Ridge Family Medicine\",\n workspaces: [\n {\n id: \"demo-primary-care-workspace\",\n name: \"Maple Ridge Main Street Office\",\n roleSuffix: \"workspace\",\n },\n ],\n },\n {\n scenario: \"demo-mixed\",\n tenantId: \"demo-mixed-tenant\",\n tenantName: \"Northbridge Health Network\",\n workspaces: [\n {\n id: \"demo-mixed-workspace-wound-care\",\n name: \"Northbridge Wound Care Center\",\n roleSuffix: \"workspace-wound-care\",\n },\n {\n id: \"demo-mixed-workspace-primary-care\",\n name: \"Northbridge Family Practice\",\n roleSuffix: \"workspace-primary-care\",\n },\n ],\n },\n];\n\n/** Stable Membership id derived from `(devUserId, tenantId)`. */\nexport const demoMembershipId = (devUserId: string, tenantId: string): string =>\n `demo-membership-${devUserId}-${tenantId}`;\n\n/**\n * Stable RoleAssignment id derived from `(devUserId, tenantId, roleCode)`.\n * Each (user, tenant, role) tuple maps to exactly one record — re-runs\n * upsert the same id.\n */\nexport const demoRoleAssignmentId = (\n devUserId: string,\n tenantId: string,\n roleCode: PlatformRoleCode,\n): string => `demo-roleassignment-${devUserId}-${tenantId}-${roleCode}`;\n\n/**\n * Demo-scenario FHIR `Identifier` entry — `urn:openhi:demo:<scenario>:<role>`.\n * Mirrors the `urn:openhi:fixture:<scenario>:<role>` pattern from\n * `@openhi/seed-fixtures/src/urn.ts`, renamed to the `demo` namespace.\n */\nexport const demoScenarioIdentifier = (\n scenario: string,\n roleSuffix: string,\n): { system: string; value: string } => ({\n system: DEMO_URN_SYSTEM,\n value: `${scenario}:${roleSuffix}`,\n});\n\n/**\n * Canonical OpenHI resource FHIR `Identifier` entry per ADR\n * 2026-03-12-01. `workspaceId` is empty for both Tenant resources and\n * Workspace resources — for a Tenant, the resource is tenant-scoped\n * with no workspace context; for a Workspace, the resource IS the\n * workspace identity rather than living inside one.\n */\nexport const openhiResourceIdentifier = (params: {\n tenantId: string;\n workspaceId: string;\n resourceType: string;\n id: string;\n}): { use: string; system: string; value: string } => ({\n use: \"unversioned\",\n system: OPENHI_RESOURCE_URN_SYSTEM,\n value: `urn:ohi:${params.tenantId}:${params.workspaceId}:${params.resourceType}:${params.id}`,\n});\n\n/**\n * Roles every dev user holds in every tenant they belong to. Per scope\n * decision Q3, every dev user is `tenant-admin` in every tenant — there\n * is no per-(user, tenant) variance to drive from.\n */\nexport const demoRolesForUserInTenant = (\n _user: DemoDevUser,\n _tenantId: string,\n): ReadonlyArray<PlatformRoleCode> => {\n void _user;\n void _tenantId;\n return [PLATFORM_ROLE_CODE.TENANT_ADMIN];\n};\n\n/**\n * DynamoDB single-table partition-key builders. The IAM grant in\n * `seed-demo-data-lambda.ts` uses these to enumerate exact-match\n * `dynamodb:LeadingKeys` values; the entity definitions in\n * `data/dynamo/entities/control/` own the canonical key templates.\n *\n * These builders MUST emit the keys ElectroDB actually writes — not\n * the entity definition's pretty template. None of the control-plane\n * entities sets `casing: \"none\"` on the base-table PK template, so\n * ElectroDB applies its default lowercase casing at runtime: the\n * entity's `ROLE#ID#${id}` becomes `role#id#<id>` on the wire. A\n * builder that returns the uppercase template form produces a\n * silently-broken IAM grant (every PutItem denied with \"no\n * identity-based policy allows\" because the request's leading-key\n * never matches a policy value).\n */\nexport const rolePartitionKey = (roleId: string): string => `role#id#${roleId}`;\n\nexport const demoTenantPartitionKey = (tenantId: string): string =>\n `tenant#id#${tenantId}`;\n\nexport const demoWorkspacePartitionKey = (\n tenantId: string,\n workspaceId: string,\n): string => `tid#${tenantId}#workspace#id#${workspaceId}`;\n\nexport const demoMembershipPartitionKey = (\n tenantId: string,\n membershipId: string,\n): string => `tid#${tenantId}#membership#id#${membershipId}`;\n\nexport const demoRoleAssignmentPartitionKey = (\n tenantId: string,\n roleAssignmentId: string,\n): string => `tid#${tenantId}#roleassignment#id#${roleAssignmentId}`;\n\n/** User entity PK template — `USER#ID#<id>` → `user#id#<id>` on the wire. */\nexport const demoUserPartitionKey = (userId: string): string =>\n `user#id#${userId}`;\n\n/**\n * Tenant + Workspace PKs the workflow writes on every fire: the 4\n * tenant PKs (placeholder + 3 demo) plus their workspaces (1 + 1 + 1 + 2 = 5).\n */\nexport const demoBasePartitionKeys = (): ReadonlyArray<string> => {\n const keys: string[] = [];\n for (const spec of DEMO_TENANT_SPECS) {\n keys.push(demoTenantPartitionKey(spec.tenantId));\n for (const workspace of spec.workspaces) {\n keys.push(demoWorkspacePartitionKey(spec.tenantId, workspace.id));\n }\n }\n return keys;\n};\n\n/**\n * Membership + RoleAssignment + User PKs the workflow writes per dev\n * user. Empty when `devUsers` is empty (used by tests). The list\n * mirrors the handler's iteration order so the IAM grant covers every\n * write the handler can make.\n *\n * Per dev user the function emits:\n * - one User PK,\n * - per tenant in {@link DEMO_TENANT_SPECS}: one Membership PK plus\n * one `tenant-admin` RoleAssignment PK,\n * - one platform-scoped `system-admin` RoleAssignment PK keyed by\n * {@link PLATFORM_SCOPE_TENANT_ID}.\n */\nexport const demoDevUserPartitionKeys = (\n devUsers: ReadonlyArray<DemoDevUser>,\n): ReadonlyArray<string> => {\n const keys: string[] = [];\n for (const user of devUsers) {\n keys.push(demoUserPartitionKey(user.id));\n for (const spec of DEMO_TENANT_SPECS) {\n keys.push(\n demoMembershipPartitionKey(\n spec.tenantId,\n demoMembershipId(user.id, spec.tenantId),\n ),\n );\n for (const roleCode of demoRolesForUserInTenant(user, spec.tenantId)) {\n keys.push(\n demoRoleAssignmentPartitionKey(\n spec.tenantId,\n demoRoleAssignmentId(user.id, spec.tenantId, roleCode),\n ),\n );\n }\n }\n // Platform-scoped system-admin RoleAssignment.\n keys.push(\n demoRoleAssignmentPartitionKey(\n PLATFORM_SCOPE_TENANT_ID,\n demoRoleAssignmentId(\n user.id,\n PLATFORM_SCOPE_TENANT_ID,\n PLATFORM_ROLE_CODE.SYSTEM_ADMIN,\n ),\n ),\n );\n }\n return keys;\n};\n\n/**\n * The trigger this workflow subscribes to on the control event bus.\n * The `seed-system-data` workflow publishes\n * `platform.system-data-seeded.v1` after it has finished re-asserting\n * the platform-singleton Role records; on every fire this workflow\n * re-asserts the demo-tenant graph.\n *\n * The indirection (subscribing to `system-data-seeded` rather than to\n * the raw `platform.deployment-completed.v1` event) enforces a strict\n * happens-before edge between the system-data seed and the demo-data\n * seed. Without it, both workflows would fire in parallel from the\n * same deploy event and seed-demo-data's pre-flight Role check would\n * race seed-system-data's puts — a race that EventBridge retries\n * resolve, but only after firing the failure alarm at least once on a\n * fresh environment's first deploy.\n *\n * Re-exported from `@openhi/workflows` for symmetry with the handler's\n * import — keep the construct + handler reading from the same registry\n * entry so a rename upstream surfaces at both call sites.\n */\nexport { PlatformSystemDataSeededV1 };\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { PLATFORM_ROLE_IDS } from \"@openhi/types\";\nimport { Duration, Stack } from \"aws-cdk-lib\";\nimport { IUserPool } from \"aws-cdk-lib/aws-cognito\";\nimport { ITable } from \"aws-cdk-lib/aws-dynamodb\";\nimport { IEventBus, Rule } from \"aws-cdk-lib/aws-events\";\nimport { LambdaFunction } from \"aws-cdk-lib/aws-events-targets\";\nimport { Effect, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\nimport {\n DEV_USERS,\n PlatformSystemDataSeededV1,\n demoBasePartitionKeys,\n demoDevUserPartitionKeys,\n rolePartitionKey,\n} from \"./events\";\nimport { SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR } from \"./seed-demo-data.handler\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-demo-data/seed-demo-data-lambda.md\n */\n\nconst HANDLER_NAME = \"seed-demo-data.handler.js\";\n\n/**\n * Resolve the bundled handler entry. Same dual-path lookup the\n * seed-system-data Lambda uses: src/ for tests (the file lives next\n * to this one) or lib/ for the compiled bundle.\n */\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n\n return path.join(dirname, \"..\", \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n}\n\nexport interface SeedDemoDataLambdaProps {\n /**\n * Data-store table the workflow upserts demo-data records into.\n * Wired via `DYNAMO_TABLE_NAME` env var; granted scoped read on the\n * Role PKs (pre-flight check) and scoped write on the enumerated\n * demo Tenant / Workspace / Membership / RoleAssignment / User PKs.\n */\n readonly dataStoreTable: ITable;\n\n /**\n * Control event bus that re-publishes\n * `platform.deployment-completed.v1` from the platform-deploy bridge.\n * The Rule mounts here.\n */\n readonly controlEventBus: IEventBus;\n\n /**\n * Cognito User Pool the workflow provisions dev users into. The\n * Lambda's IAM grant is scoped to this exact user-pool ARN — the\n * grant uses the user-pool ARN, **not** the wildcard formatArn\n * pattern used by `post-authentication-lambda` (that Lambda's\n * trigger-driven dependency cycle does not apply here, so the\n * tighter scope is safe).\n */\n readonly userPool: IUserPool;\n}\n\n/**\n * Lambda + EventBridge Rule pair for the seed-demo-data workflow.\n * Owns the routing (`source` / `detail-type` pattern), the scoped\n * DynamoDB grants, and the scoped Cognito Admin grant — co-locating\n * routing + permissions with the function they target. Wiring to the\n * workflow dedup table is the parent construct's job (it has the\n * singleton reference) and happens via `WorkflowDedupTable.grantConsumer`.\n *\n * Stage-gating is the parent's job too — this construct itself never\n * checks the stage. The CDK stage-router (`OpenHiDataService`)\n * decides whether to instantiate it at all on each stage.\n */\nexport class SeedDemoDataLambda extends Construct {\n public readonly lambda: NodejsFunction;\n public readonly rule: Rule;\n\n constructor(scope: Construct, props: SeedDemoDataLambdaProps) {\n super(scope, \"seed-demo-data-lambda\");\n\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 512,\n timeout: Duration.minutes(2),\n environment: {\n DYNAMO_TABLE_NAME: props.dataStoreTable.tableName,\n [SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR]: props.userPool.userPoolId,\n },\n });\n\n // Pre-flight Role check: read the three platform-singleton Role\n // PKs only. A regression that tried to read other records would\n // be rejected by IAM, not by application code.\n const roleReadKeys = Object.values(PLATFORM_ROLE_IDS).map(rolePartitionKey);\n this.lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\"dynamodb:GetItem\"],\n resources: [props.dataStoreTable.tableArn],\n conditions: {\n \"ForAllValues:StringEquals\": {\n \"dynamodb:LeadingKeys\": roleReadKeys,\n },\n },\n }),\n );\n\n // Write grant: the deterministic set of demo Tenant / Workspace /\n // Membership / RoleAssignment / User PKs the workflow writes.\n // {@link DEV_USERS} is a compile-time constant so the set is\n // fully enumerable at synth time.\n const writeKeys: string[] = [\n ...demoBasePartitionKeys(),\n ...demoDevUserPartitionKeys(DEV_USERS),\n ];\n this.lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\"dynamodb:PutItem\", \"dynamodb:UpdateItem\"],\n resources: [props.dataStoreTable.tableArn],\n conditions: {\n \"ForAllValues:StringEquals\": {\n \"dynamodb:LeadingKeys\": writeKeys,\n },\n },\n }),\n );\n\n // Cognito grant. The User Pool is imported by ARN from the auth\n // stack via SSM lookup, so reconstructing the ARN with\n // `Stack.of(this).formatArn` scopes the grant to this exact\n // account+region+pool without depending on the auth stack's\n // exported ARN.\n this.lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\n \"cognito-idp:AdminCreateUser\",\n \"cognito-idp:AdminGetUser\",\n \"cognito-idp:AdminSetUserPassword\",\n ],\n resources: [\n Stack.of(this).formatArn({\n service: \"cognito-idp\",\n resource: \"userpool\",\n resourceName: props.userPool.userPoolId,\n }),\n ],\n }),\n );\n\n this.rule = new Rule(this, \"rule\", {\n eventBus: props.controlEventBus,\n eventPattern: {\n source: [PlatformSystemDataSeededV1.source],\n detailType: [PlatformSystemDataSeededV1.detailType],\n },\n targets: [\n new LambdaFunction(this.lambda, {\n retryAttempts: 2,\n maxEventAge: Duration.hours(2),\n }),\n ],\n });\n }\n}\n","import { createHash } from \"node:crypto\";\nimport {\n AdminCreateUserCommand,\n AdminGetUserCommand,\n AdminSetUserPasswordCommand,\n CognitoIdentityProviderClient,\n UsernameExistsException,\n type AttributeType,\n} from \"@aws-sdk/client-cognito-identity-provider\";\nimport { DynamoDBClient } from \"@aws-sdk/client-dynamodb\";\nimport {\n PLATFORM_ROLE_CODE,\n PLATFORM_ROLE_CONCEPTS,\n PLATFORM_ROLE_IDS,\n extractSummary,\n type PlatformRoleCode,\n type FhirResourceLike,\n} from \"@openhi/types\";\nimport {\n parseWorkflowEvent,\n workflowDedupClient,\n type WorkflowDedupClient,\n} from \"@openhi/workflows\";\nimport type { EventBridgeEvent } from \"aws-lambda\";\nimport {\n DEMO_PERIOD,\n DEMO_TENANT_SPECS,\n DEV_USERS,\n PLACEHOLDER_TENANT_ID,\n PLACEHOLDER_WORKSPACE_ID,\n PLATFORM_SCOPE_TENANT_ID,\n PlatformSystemDataSeededV1,\n SEED_DEMO_DATA_CONSUMER_NAME,\n demoMembershipId,\n demoRoleAssignmentId,\n demoRolesForUserInTenant,\n demoScenarioIdentifier,\n openhiResourceIdentifier,\n type DemoDevUser,\n} from \"./events\";\nimport { getDynamoControlService } from \"../../../data/dynamo/dynamo-control-service\";\nimport { NotFoundError } from \"../../../data/errors\";\nimport type { OpenHiContext } from \"../../../data/openhi-context\";\nimport { createMembershipOperation } from \"../../../data/operations/control/membership/membership-create-operation\";\nimport { getRoleByIdOperation } from \"../../../data/operations/control/role/role-get-by-id-operation\";\nimport { createRoleAssignmentOperation } from \"../../../data/operations/control/roleassignment/roleassignment-create-operation\";\nimport { createTenantOperation } from \"../../../data/operations/control/tenant/tenant-create-operation\";\nimport { createWorkspaceOperation } from \"../../../data/operations/control/workspace/workspace-create-operation\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-demo-data/seed-demo-data-handler.md\n *\n * EventBridge workflow handler invoked once per platform-deploy event\n * on the control event bus. Pre-flight verifies that\n * `seed-system-data` has already seeded the canonical Role records,\n * then idempotently re-asserts the demo-data graph: the placeholder\n * Tenant + Workspace, the three demo tenants + their workspaces, and\n * for each entry in {@link DEV_USERS} a Cognito user, a DynamoDB User,\n * 4 Memberships, 4 `tenant-admin` RoleAssignments, plus 1\n * platform-scoped `system-admin` RoleAssignment.\n */\n\ntype SeedDemoDataEvent = EventBridgeEvent<\n \"platform.system-data-seeded.v1\",\n unknown\n>;\n\n/** Env var the lambda construct injects with the Cognito User Pool ID. */\nexport const SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR =\n \"SEED_DEMO_DATA_USER_POOL_ID\";\n\n/** Inputs the handler needs to provision Cognito users for every dev. */\nexport interface CognitoProvisioner {\n /**\n * Ensure a Cognito user exists for `email`. Returns the user's\n * `sub`. Implementations MUST be idempotent: a second invocation\n * for the same email returns the existing user's sub without\n * resetting its password or touching any attribute.\n */\n readonly ensureUser: (email: string) => Promise<string>;\n}\n\n/**\n * Dependency seam for tests. The factory mirrors the production\n * arrangement: a real DynamoDB client + the real `workflowDedupClient`\n * factory bound to the `OPENHI_WORKFLOW_DEDUP_TABLE_NAME` env var the\n * construct injects via `grantConsumer`.\n */\nexport interface SeedDemoDataDependencies {\n readonly dedupClient: WorkflowDedupClient;\n /**\n * Reads every id in `PLATFORM_ROLE_IDS`. Throws when any Role\n * record is missing — that means `seed-system-data` has not yet\n * run on this environment, and emitting demo RoleAssignments that\n * reference non-existent Roles would produce orphaned records.\n */\n readonly verifyRoles: () => Promise<void>;\n /**\n * Upserts every Tenant + Workspace in {@link DEMO_TENANT_SPECS},\n * plus per-dev-user Cognito users, DynamoDB User records,\n * Memberships, and RoleAssignments.\n */\n readonly seedDemoGraph: (params: {\n readonly baseContext: OpenHiContext;\n readonly devUsers: ReadonlyArray<DemoDevUser>;\n readonly cognito: CognitoProvisioner;\n }) => Promise<void>;\n /** Cognito provisioner threaded into `seedDemoGraph`. */\n readonly cognito: CognitoProvisioner;\n}\n\nconst errorMessage = (err: unknown): string => {\n if (err instanceof Error) {\n return err.message;\n }\n return String(err);\n};\n\n/**\n * Map a role code back to its canonical stable id from\n * `PLATFORM_ROLE_IDS`. Mirrors the lookup in\n * `seed-system-data.handler.ts` — the generator emits `_IDS` and\n * `_CONCEPTS` in lockstep, so a single SCREAMING_SNAKE key resolves\n * both projections.\n */\nconst idForRoleCode = (code: PlatformRoleCode): string => {\n for (const key of Object.keys(PLATFORM_ROLE_IDS) as Array<\n keyof typeof PLATFORM_ROLE_IDS\n >) {\n if (PLATFORM_ROLE_CONCEPTS[key].code === code) {\n return PLATFORM_ROLE_IDS[key];\n }\n }\n throw new Error(`No id mapping for role code \"${code}\".`);\n};\n\n/**\n * Pre-flight Role check. Iterates every id in\n * `PLATFORM_ROLE_IDS` and reads the record. If any read returns\n * not-found, this throws and `markFailed`'s the dedup row — the\n * replay tooling can retry once `seed-system-data` has populated the\n * Roles.\n */\nconst verifySystemRolesExist = async (): Promise<void> => {\n const probeContext: OpenHiContext = {\n tenantId: \"\",\n workspaceId: \"\",\n date: new Date(0).toISOString(),\n actorId: \"platform-deploy-bridge\",\n actorName: \"Platform Deploy Bridge\",\n actorType: \"internal-system\",\n source: \"step-function\",\n };\n for (const id of Object.values(PLATFORM_ROLE_IDS)) {\n try {\n await getRoleByIdOperation({ context: probeContext, id });\n } catch (err) {\n if (err instanceof NotFoundError) {\n throw new Error(\n `seed-demo-data pre-flight: control-plane Role \"${id}\" is missing. ` +\n \"Ensure seed-system-data has run on this environment before retrying.\",\n );\n }\n throw err;\n }\n }\n};\n\n/**\n * Build the resource body for one demo Tenant. Carries the two\n * identifiers per scope decision #6: the demo-scenario URN and the\n * canonical OpenHI resource URN (ADR 2026-03-12-01).\n */\nconst tenantResourceBody = (\n spec: (typeof DEMO_TENANT_SPECS)[number],\n): Record<string, unknown> => ({\n name: spec.tenantName,\n active: true,\n identifier: [\n demoScenarioIdentifier(spec.scenario, \"tenant\"),\n openhiResourceIdentifier({\n tenantId: spec.tenantId,\n workspaceId: \"\",\n resourceType: \"Tenant\",\n id: spec.tenantId,\n }),\n ],\n});\n\nconst workspaceResourceBody = (\n spec: (typeof DEMO_TENANT_SPECS)[number],\n workspace: (typeof DEMO_TENANT_SPECS)[number][\"workspaces\"][number],\n): Record<string, unknown> => ({\n name: workspace.name,\n active: true,\n identifier: [\n demoScenarioIdentifier(spec.scenario, workspace.roleSuffix),\n openhiResourceIdentifier({\n tenantId: spec.tenantId,\n workspaceId: \"\",\n resourceType: \"Workspace\",\n id: workspace.id,\n }),\n ],\n tenant: { reference: `Tenant/${spec.tenantId}`, type: \"Tenant\" },\n});\n\nconst membershipResourceBody = (\n spec: (typeof DEMO_TENANT_SPECS)[number],\n user: DemoDevUser,\n membershipId: string,\n): Record<string, unknown> => ({\n identifier: [\n demoScenarioIdentifier(spec.scenario, `membership-${user.id}`),\n openhiResourceIdentifier({\n tenantId: spec.tenantId,\n workspaceId: \"\",\n resourceType: \"Membership\",\n id: membershipId,\n }),\n ],\n user: { reference: `User/${user.id}`, type: \"User\" },\n tenant: { reference: `Tenant/${spec.tenantId}`, type: \"Tenant\" },\n period: DEMO_PERIOD,\n});\n\nconst roleAssignmentResourceBody = (\n scenario: string,\n tenantId: string,\n user: DemoDevUser,\n roleCode: PlatformRoleCode,\n roleAssignmentId: string,\n): Record<string, unknown> => ({\n identifier: [\n demoScenarioIdentifier(scenario, `roleassignment-${user.id}-${roleCode}`),\n openhiResourceIdentifier({\n tenantId,\n workspaceId: \"\",\n resourceType: \"RoleAssignment\",\n id: roleAssignmentId,\n }),\n ],\n user: { reference: `User/${user.id}`, type: \"User\" },\n role: { reference: `Role/${idForRoleCode(roleCode)}`, type: \"Role\" },\n tenant: { reference: `Tenant/${tenantId}`, type: \"Tenant\" },\n period: DEMO_PERIOD,\n});\n\n/** Build the FHIR User resource body for a seeded dev user. */\nconst userResourceBody = (\n user: DemoDevUser,\n cognitoSub: string,\n): Record<string, unknown> => ({\n resourceType: \"User\",\n id: user.id,\n name: [{ text: user.email }],\n status: \"active\",\n cognitoSub,\n currentTenant: { reference: `Tenant/${PLACEHOLDER_TENANT_ID}` },\n currentWorkspace: { reference: `Workspace/${PLACEHOLDER_WORKSPACE_ID}` },\n});\n\n/**\n * Idempotent User upsert. The User entity is non-tenant-isolated, so\n * the put is unconditional — the entity's PK is `USER#ID#<id>` and\n * supplying the same id re-asserts the same row.\n */\nconst upsertUser = async (\n context: OpenHiContext,\n user: DemoDevUser,\n cognitoSub: string,\n): Promise<void> => {\n const service = getDynamoControlService();\n const resource = userResourceBody(user, cognitoSub);\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n await service.entities.user\n .put({\n id: user.id,\n cognitoSub,\n resource: JSON.stringify(resource),\n summary,\n vid: \"1\",\n lastUpdated: context.date ?? new Date().toISOString(),\n })\n .go();\n};\n\n/**\n * Upsert the full demo-data graph. Walks each spec in\n * {@link DEMO_TENANT_SPECS}: Tenant → Workspaces. Then per dev user:\n * Cognito user → DynamoDB User → per-tenant Membership +\n * `tenant-admin` RoleAssignment → platform-scoped `system-admin`\n * RoleAssignment. Every put is keyed by a deterministic stable id so\n * re-runs after dedup-TTL expiry upsert the same records.\n *\n * Exported so the seeder test file can exercise it directly against\n * a mocked DynamoControlService; the production handler reaches it\n * through {@link SeedDemoDataDependencies.seedDemoGraph}.\n */\nexport const seedDemoGraph = async (params: {\n baseContext: OpenHiContext;\n devUsers: ReadonlyArray<DemoDevUser>;\n cognito: CognitoProvisioner;\n}): Promise<void> => {\n const { baseContext, devUsers, cognito } = params;\n\n // Phase 1: Tenants + Workspaces (no Memberships / RoleAssignments\n // yet — those need the per-user Cognito sub).\n for (const spec of DEMO_TENANT_SPECS) {\n const tenantContext: OpenHiContext = {\n ...baseContext,\n tenantId: spec.tenantId,\n };\n\n await createTenantOperation({\n context: tenantContext,\n body: { id: spec.tenantId, resource: tenantResourceBody(spec) },\n });\n\n for (const workspace of spec.workspaces) {\n await createWorkspaceOperation({\n context: tenantContext,\n body: {\n id: workspace.id,\n resource: workspaceResourceBody(spec, workspace),\n },\n });\n }\n }\n\n // Phase 2: per-dev-user records. The Cognito provisioner is the\n // only step that can fail in a way EventBridge can usefully retry —\n // it runs first so a failed Cognito call short-circuits before any\n // DynamoDB writes for that user.\n for (const user of devUsers) {\n const cognitoSub = await cognito.ensureUser(user.email);\n\n await upsertUser(baseContext, user, cognitoSub);\n\n for (const spec of DEMO_TENANT_SPECS) {\n const tenantContext: OpenHiContext = {\n ...baseContext,\n tenantId: spec.tenantId,\n };\n\n const membershipId = demoMembershipId(user.id, spec.tenantId);\n await createMembershipOperation({\n context: tenantContext,\n body: {\n id: membershipId,\n resource: membershipResourceBody(spec, user, membershipId),\n },\n });\n\n for (const roleCode of demoRolesForUserInTenant(user, spec.tenantId)) {\n const raId = demoRoleAssignmentId(user.id, spec.tenantId, roleCode);\n await createRoleAssignmentOperation({\n context: tenantContext,\n body: {\n id: raId,\n resource: roleAssignmentResourceBody(\n spec.scenario,\n spec.tenantId,\n user,\n roleCode,\n raId,\n ),\n },\n });\n }\n }\n\n // Platform-scoped system-admin RoleAssignment. The sentinel\n // tenantId is PLATFORM_SCOPE_TENANT_ID — there is no real Tenant\n // to point at, so the operation runs with an OpenHiContext whose\n // tenantId is the sentinel.\n const platformContext: OpenHiContext = {\n ...baseContext,\n tenantId: PLATFORM_SCOPE_TENANT_ID,\n };\n const platformRoleCode = PLATFORM_ROLE_CODE.SYSTEM_ADMIN;\n const platformRaId = demoRoleAssignmentId(\n user.id,\n PLATFORM_SCOPE_TENANT_ID,\n platformRoleCode,\n );\n await createRoleAssignmentOperation({\n context: platformContext,\n body: {\n id: platformRaId,\n resource: roleAssignmentResourceBody(\n \"platform\",\n PLATFORM_SCOPE_TENANT_ID,\n user,\n platformRoleCode,\n platformRaId,\n ),\n },\n });\n }\n};\n\n/**\n * Test-visible orchestrator. The production `handler` calls this with\n * real dependencies and the hardcoded {@link DEV_USERS} list; unit\n * tests inject fakes and pass the dev-user list directly.\n */\nexport const runSeedDemoData = async (\n event: SeedDemoDataEvent,\n deps: SeedDemoDataDependencies,\n devUsers: ReadonlyArray<DemoDevUser>,\n): Promise<void> => {\n const parsed = parseWorkflowEvent(event, PlatformSystemDataSeededV1);\n\n const recordResult = await deps.dedupClient.recordIfAbsent({\n consumerName: SEED_DEMO_DATA_CONSUMER_NAME,\n eventId: parsed.dedupKey.eventId,\n attempt: parsed.dedupKey.attempt,\n });\n if (!recordResult.recorded) {\n return;\n }\n\n const baseContext: OpenHiContext = {\n tenantId: \"\",\n workspaceId: \"\",\n date: parsed.envelope.occurredAt,\n actorId: \"platform-deploy-bridge\",\n actorName: \"Platform Deploy Bridge\",\n actorType: \"internal-system\",\n source: \"step-function\",\n };\n\n try {\n await deps.verifyRoles();\n await deps.seedDemoGraph({\n baseContext,\n devUsers,\n cognito: deps.cognito,\n });\n } catch (err) {\n // Mark the dedup row failed so the replay tooling (TR-016 follow-up)\n // can re-publish the originating event with a fresh attempt. The\n // re-throw keeps EventBridge's failure-detection path intact — the\n // event ends up in the DLQ + the workflow Lambda's failure\n // CloudWatch alarm fires.\n await deps.dedupClient.markFailed({\n consumerName: SEED_DEMO_DATA_CONSUMER_NAME,\n eventId: parsed.dedupKey.eventId,\n attempt: parsed.dedupKey.attempt,\n reason: errorMessage(err),\n });\n throw err;\n }\n};\n\n/**\n * Deterministic password derived from the user's email. Re-running\n * the algorithm with the same email reproduces the password, so devs\n * can recover their own credentials from the docs page without the\n * workflow ever surfacing them. The shape satisfies the default\n * Cognito password policy (≥8 chars, upper + lower + number + symbol).\n */\nexport const devPasswordForEmail = (email: string): string => {\n const digest = createHash(\"sha256\").update(email).digest();\n const base64url = digest\n .toString(\"base64\")\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/g, \"\");\n return `Dev-${base64url.slice(0, 20)}!1`;\n};\n\n/**\n * Production Cognito provisioner backed by the AWS SDK. Reads the\n * user-pool id from the env var the lambda construct injects.\n *\n * Idempotency contract:\n * - On first invocation, calls `AdminCreateUser` (with\n * `MessageAction: SUPPRESS` so no invitation email fires) then\n * `AdminSetUserPassword` (permanent). Returns the new user's sub.\n * - On subsequent invocations, `AdminCreateUser` throws\n * `UsernameExistsException`; the provisioner catches it, calls\n * `AdminGetUser` to read the existing user's sub, and returns\n * **without** touching the password or any attribute.\n */\nexport const productionCognitoProvisioner = (): CognitoProvisioner => {\n const client = new CognitoIdentityProviderClient({});\n const userPoolId = process.env[SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR];\n if (!userPoolId || userPoolId.trim() === \"\") {\n throw new Error(\n `${SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR} is not set; the seed-demo-data Lambda ` +\n \"cannot provision Cognito users without a user-pool id.\",\n );\n }\n const subFromAttributes = (\n attrs: ReadonlyArray<AttributeType> | undefined,\n ): string | undefined => {\n for (const attr of attrs ?? []) {\n if (attr.Name === \"sub\" && typeof attr.Value === \"string\") {\n return attr.Value;\n }\n }\n return undefined;\n };\n return {\n ensureUser: async (email: string): Promise<string> => {\n try {\n const created = await client.send(\n new AdminCreateUserCommand({\n UserPoolId: userPoolId,\n Username: email,\n MessageAction: \"SUPPRESS\",\n UserAttributes: [\n { Name: \"email\", Value: email },\n { Name: \"email_verified\", Value: \"true\" },\n ],\n }),\n );\n await client.send(\n new AdminSetUserPasswordCommand({\n UserPoolId: userPoolId,\n Username: email,\n Password: devPasswordForEmail(email),\n Permanent: true,\n }),\n );\n const sub = subFromAttributes(created.User?.Attributes);\n if (!sub) {\n throw new Error(\n `AdminCreateUser response for \"${email}\" did not carry a sub attribute.`,\n );\n }\n return sub;\n } catch (err) {\n if (err instanceof UsernameExistsException) {\n // User already exists — skip password reset entirely and\n // just read the existing sub.\n const got = await client.send(\n new AdminGetUserCommand({\n UserPoolId: userPoolId,\n Username: email,\n }),\n );\n const sub = subFromAttributes(got.UserAttributes);\n if (!sub) {\n throw new Error(\n `AdminGetUser response for \"${email}\" did not carry a sub attribute.`,\n );\n }\n return sub;\n }\n throw err;\n }\n },\n };\n};\n\nconst productionDependencies = (): SeedDemoDataDependencies => {\n const dynamodb = new DynamoDBClient({});\n const cognito = productionCognitoProvisioner();\n return {\n dedupClient: workflowDedupClient(dynamodb),\n verifyRoles: verifySystemRolesExist,\n seedDemoGraph,\n cognito,\n };\n};\n\nexport const handler = async (event: SeedDemoDataEvent): Promise<void> =>\n runSeedDemoData(event, productionDependencies(), DEV_USERS);\n","import { Service } from \"electrodb\";\nimport { defaultTableName, dynamoClient } from \"./dynamo-client\";\nimport { ConfigurationEntity } from \"./entities/control/configuration-entity\";\nimport { MembershipEntity } from \"./entities/control/membership-entity\";\nimport { RoleEntity } from \"./entities/control/role-entity\";\nimport { RoleAssignmentEntity } from \"./entities/control/roleassignment-entity\";\nimport { TenantEntity } from \"./entities/control/tenant-entity\";\nimport { UserEntity } from \"./entities/control/user-entity\";\nimport { WorkspaceEntity } from \"./entities/control/workspace-entity\";\n\n/**\n * Control-plane entities only (service \"control\"). Same table as data plane; use\n * DynamoDataService for data-plane entities.\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n */\n\nconst controlPlaneEntities = {\n configuration: ConfigurationEntity,\n membership: MembershipEntity,\n role: RoleEntity,\n roleAssignment: RoleAssignmentEntity,\n tenant: TenantEntity,\n user: UserEntity,\n workspace: WorkspaceEntity,\n};\n\nconst controlPlaneService = new Service(controlPlaneEntities, {\n table: defaultTableName,\n client: dynamoClient,\n});\n\n/**\n * Control-plane service: entities for configuration and control. Use with the\n * data store table (PK, SK, GSI1; UserEntity also uses GSI2).\n */\nexport const DynamoControlService = {\n entities: controlPlaneService.entities,\n};\n\nexport type DynamoControlServiceType = typeof DynamoControlService;\n\n/**\n * Returns the control-plane service. Table name is resolved from tableName (optional override),\n * then DYNAMO_TABLE_NAME, then \"jesttesttable\".\n */\nexport function getDynamoControlService(\n tableName?: string,\n): DynamoControlServiceType {\n const resolved = tableName ?? defaultTableName;\n const service = new Service(controlPlaneEntities, {\n table: resolved,\n client: dynamoClient,\n });\n return {\n entities: service.entities,\n };\n}\n","import { DynamoDBClient } from \"@aws-sdk/client-dynamodb\";\n\n/**\n * DynamoDB table name for the data store. Set via DYNAMO_TABLE_NAME at runtime\n * (e.g. from Lambda env); defaults for local/test.\n */\nexport const defaultTableName =\n process.env.DYNAMO_TABLE_NAME ?? \"jesttesttable\";\n\n/**\n * DynamoDB client. When MOCK_DYNAMODB_ENDPOINT is set (e.g. local DynamoDB or\n * jest-dynalite), uses that endpoint with no SSL and region \"local\".\n */\nexport const dynamoClient = new DynamoDBClient({\n ...(process.env.MOCK_DYNAMODB_ENDPOINT && {\n endpoint: process.env.MOCK_DYNAMODB_ENDPOINT,\n sslEnabled: false,\n region: \"local\",\n }),\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute } from \"./control-entity-common\";\n\n/**\n * Configuration data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Partially tenant-isolated, control plane. Cascade of scope\n * levels: resolution order user → workspace → tenant → baseline. Sentinels: tenantId \"BASELINE\" for\n * baseline tier; workspaceId/userId/roleId \"-\" for absent dimension.\n *\n * Key structure: PK = CONFIG#TID#<tenantId>#WID#<workspaceId>#UID#<userId>#RID#<roleId>,\n * SK = KEY#<key>#SK#<sk>. Uniqueness: one Configuration per (tenantId, workspaceId, userId, roleId, key).\n * Standard attributes and key-building conventions align with single-table design.\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Configuration entries in a tenant/workspace\n * across the four shards.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entities/configuration.md\n * @see sites/www-docs/content/architecture/control-plane/configuration.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Key-building conventions (keys built inside entity)\n */\nexport const ConfigurationEntity = new Entity({\n model: {\n entity: \"configuration\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key. \"CURRENT\" for current version; version history in S3. */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** Tenant scope. Use \"BASELINE\" when the config is baseline default (no tenant). */\n tenantId: {\n type: \"string\" as const,\n required: true,\n default: \"BASELINE\",\n },\n /** Workspace scope. Use \"-\" when absent. */\n workspaceId: {\n type: \"string\" as const,\n required: true,\n default: \"-\",\n },\n /** User scope. Use \"-\" when absent. */\n userId: {\n type: \"string\" as const,\n required: true,\n default: \"-\",\n },\n /** Role scope. Use \"-\" when absent. */\n roleId: {\n type: \"string\" as const,\n required: true,\n default: \"-\",\n },\n /** Config type (category), e.g. endpoints, branding, display. */\n key: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR Resource.id; logical id in URL and for the Configuration resource. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Payload as JSON string. JSON.stringify(resource) on write; JSON.parse(item.resource) on read. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, key, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). Tracks current version; S3 history key. */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** 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. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"tenantId\", \"workspaceId\", \"userId\", \"roleId\"],\n template:\n \"CONFIG#TID#${tenantId}#WID#${workspaceId}#UID#${userId}#RID#${roleId}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"key\", \"sk\"],\n template: \"KEY#${key}#SK#${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Configuration entries for a\n * (tenant, workspace) across the four shards. Use for \"list configs scoped to this tenant\"\n * (workspaceId = \"-\") or \"list configs scoped to this workspace\". Does not support\n * hierarchical resolution in one query; use base table GetItem in fallback order\n * (user → workspace → tenant → baseline) for that.\n * SK is `<key>#<id>` — Configuration's `key` is a required entity attribute (the\n * config category: endpoints, branding, display, …) and the natural sort/lookup\n * dimension. `casing: \"none\"` preserves the literal key value.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"tenantId\", \"workspaceId\", \"gsi1Shard\"],\n template:\n \"TID#${tenantId}#WID#${workspaceId}#RT#Configuration#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"key\", \"id\"],\n template: \"${key}#${id}\",\n },\n },\n },\n});\n","import { extractLabel } from \"@openhi/types\";\nimport { computeShard } from \"../../shard\";\n\n/**\n * Shared GSI1 shard attribute for control-plane entities.\n *\n * Control-plane entities (User, Tenant, Workspace, Membership, Role, RoleAssignment,\n * Configuration) use the same `TID#/WID#/RT#/SHARD#` PK shape on GSI1 as data-plane\n * FHIR resources per ADR-011. The shard index is derived deterministically from `id`\n * via `computeShard` so updates always land on the same shard. Stored as a string\n * because it appears as a literal segment in the GSI1 PK template; the underlying\n * value is 0..3.\n *\n * Not `required` because the value is derived via `watch`/`set`; ElectroDB's\n * required-field check runs before watch propagation, so callers must not fail\n * validation on a derived field.\n */\nexport const gsi1ShardAttribute = {\n type: \"string\" as const,\n watch: [\"id\"] as const,\n set: (_val?: string, item?: { id?: string }) => {\n if (typeof item?.id !== \"string\" || item.id.length === 0) {\n return undefined;\n }\n return String(computeShard(item.id));\n },\n};\n\n/**\n * Shared GSI1 sort-key attribute for control-plane entities.\n *\n * Derives the GSI1SK value at write time from the entity's `resource` JSON\n * string, applying the same label-extraction strategy as the data plane\n * (DR-004 / `@openhi/types` `extractLabel`). When the resource carries a\n * natural label (Tenant.name, Workspace.name, Configuration.key, …) the\n * sort key is `<normalizedLabel>#<id>` so list endpoints sort alphabetically\n * and `BEGINS_WITH` queries serve prefix searches. When no label is\n * extractable, falls back to `<entity.lastUpdated>#<id>` for stable\n * reverse-chronological ordering.\n *\n * Falls back gracefully on malformed `resource` payloads — JSON parse\n * failures and missing fields both route to the lastUpdated fallback so a\n * single bad write never blocks an entity put. The entity-level\n * `lastUpdated` is preferred over `resource.meta.lastUpdated` so the\n * fallback uses the authoritative timestamp the entity write supplies.\n *\n * Not `required` because the value is derived via `watch`/`set`.\n */\nexport const gsi1skAttribute = {\n type: \"string\" as const,\n watch: [\"resource\", \"lastUpdated\", \"id\"] as const,\n set: (\n _val?: string,\n item?: { resource?: string; lastUpdated?: string; id?: string },\n ) => {\n const id = typeof item?.id === \"string\" ? item.id : \"\";\n const lastUpdated =\n typeof item?.lastUpdated === \"string\" ? item.lastUpdated : \"\";\n const fallback = `${lastUpdated}#${id}`;\n\n if (typeof item?.resource !== \"string\" || item.resource.length === 0) {\n return fallback;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(item.resource);\n } catch {\n return fallback;\n }\n if (!parsed || typeof parsed !== \"object\") return fallback;\n const resourceType = (parsed as { resourceType?: unknown }).resourceType;\n if (typeof resourceType !== \"string\") return fallback;\n\n const label = extractLabel(parsed as Parameters<typeof extractLabel>[0]);\n return label !== undefined ? `${label}#${id}` : fallback;\n },\n};\n","/**\n * Shard selection for the data-plane single-table GSI1 partitioning per ADR-011.\n *\n * GSI1's partition key embeds a `SHARD#<n>` segment with `n = computeShard(id)`.\n * The hash is deterministic so updates to the same resource id always land on\n * the same shard (no cross-shard migration on update); reads fan out to all\n * shards in parallel and merge by SK.\n *\n * @see sites/www-docs/content/architecture/adr/ — ADR-011 (single-table DynamoDB)\n */\n\n/** Number of shards in the GSI1 partition key. Fixed at 4 in v1; raising it later is a backfill, not a schema migration. */\nexport const SHARD_COUNT = 4;\n\n/**\n * Returns a deterministic shard index in [0, SHARD_COUNT) for the given resource id.\n *\n * Implementation: 32-bit FNV-1a over the UTF-16 code units of the id, modulo SHARD_COUNT.\n * The function is pure and stable; the same id always returns the same shard.\n *\n * ESLint's `no-bitwise` rule is disabled inside this function because FNV-1a is\n * defined in terms of XOR and unsigned-right-shift — the bitwise ops are the\n * algorithm, not an accidental logical-operator confusion.\n */\nexport function computeShard(id: string): number {\n /* eslint-disable no-bitwise */\n let hash = 0x811c9dc5;\n for (let i = 0; i < id.length; i++) {\n hash ^= id.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0) % SHARD_COUNT;\n /* eslint-enable no-bitwise */\n}\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * Membership data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, control plane. Membership links a User\n * to a Tenant (and optionally a Workspace). One record per (tenantId, id).\n *\n * Key structure: PK = TID#<tenantId>#MEMBERSHIP#ID#<id>, SK = CURRENT.\n * Uniqueness: one Membership per (tenantId, id).\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Memberships in a tenant across the four\n * shards. Membership is tenant-scoped (not workspace-scoped), so the GSI1 PK uses `WID#-` as a\n * sentinel.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-13-02-control-plane-roles-and-user-tenant-workspace-linkage.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MembershipEntity = new Entity({\n model: {\n entity: \"membership\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** Tenant in which the user has membership (required). */\n tenantId: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR Resource.id; membership id. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full Membership resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n /**\n * Denormalized `linked-data-identity` Reference (e.g. `Practitioner/abc`).\n * Populated from the FHIR extension on the Membership resource at write\n * time so future GSIs can index data-plane identity lookups without\n * deserializing the full resource JSON. See ADR 2026-03-13-02 §6.\n */\n linkedDataIdentityRef: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = TID#<tenantId>#MEMBERSHIP#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"tenantId\", \"id\"],\n template: \"TID#${tenantId}#MEMBERSHIP#ID#${id}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Memberships for a tenant across the\n * four shards. Membership is tenant-scoped only, so `WID#-` is a sentinel.\n * SK is derived via `gsi1skAttribute` — uses the resource's natural label when\n * extractable, else `<lastUpdated>#<id>` (DR-004). `casing: \"none\"` preserves the\n * normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"tenantId\", \"gsi1Shard\"],\n template: \"TID#${tenantId}#WID#-#RT#Membership#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n },\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * Role data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Non-tenant-isolated, control plane. Role is a\n * platform-wide role catalog (e.g. tenant-admin, tenant-user, system-admin); not scoped by tenant.\n * RoleAssignment references Role to assign a role to a User in a Tenant/Workspace context.\n *\n * Key structure: PK = ROLE#ID#<id>, SK = CURRENT.\n * The ROLE# prefix prevents key collisions with other non-tenant-isolated entities (User, etc.)\n * sharing the same table (ADR 2026-03-11-01 — preferred pattern for all control plane entities).\n * Uniqueness: one Role per id.\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Roles across the four shards. Non-tenant-\n * isolated, so the PK uses `TID#-#WID#-` sentinels.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-13-02-control-plane-roles-and-user-tenant-workspace-linkage.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const RoleEntity = new Entity({\n model: {\n entity: \"role\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** FHIR Resource.id; role id. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full Role resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = ROLE#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"id\"],\n template: \"ROLE#ID#${id}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Roles across the four shards.\n * Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#Role#SHARD#<n>`.\n * SK is derived via `gsi1skAttribute` — uses the resource's natural label when\n * extractable, else `<lastUpdated>#<id>` (DR-004). `casing: \"none\"` preserves the\n * normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"gsi1Shard\"],\n template: \"TID#-#WID#-#RT#Role#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n },\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * RoleAssignment data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, control plane. RoleAssignment assigns\n * a Role to a User in a Tenant (and optionally Workspace) context.\n *\n * Key structure: PK = TID#<tenantId>#ROLEASSIGNMENT#ID#<id>, SK = CURRENT.\n * Uniqueness: one RoleAssignment per (tenantId, id).\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all RoleAssignments in a tenant across the four\n * shards. Tenant-scoped only (workspace context lives inside the resource), so the GSI1 PK uses\n * `WID#-` as a sentinel.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-13-02-control-plane-roles-and-user-tenant-workspace-linkage.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const RoleAssignmentEntity = new Entity({\n model: {\n entity: \"roleassignment\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** Tenant in which the role assignment applies (required). */\n tenantId: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR Resource.id; role assignment id. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full RoleAssignment resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = TID#<tenantId>#ROLEASSIGNMENT#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"tenantId\", \"id\"],\n template: \"TID#${tenantId}#ROLEASSIGNMENT#ID#${id}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all RoleAssignments for a tenant across the\n * four shards. Tenant-scoped only, so `WID#-` is a sentinel.\n * SK is derived via `gsi1skAttribute` — uses the resource's natural label when\n * extractable, else `<lastUpdated>#<id>` (DR-004). `casing: \"none\"` preserves the\n * normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"tenantId\", \"gsi1Shard\"],\n template: \"TID#${tenantId}#WID#-#RT#RoleAssignment#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n },\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * Tenant data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, control plane. Tenant IS the top scope;\n * the workspace dimension is not applicable and uses the sentinel `TENANT`. The tenant's own `id`\n * is stored as `tenantId` to drive the partition key.\n *\n * Key structure: PK = TENANT#ID#<tenantId>, SK = CURRENT.\n * Uniqueness: one Tenant per tenantId (id).\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Tenants across the four shards. Tenant has\n * no parent tenant or workspace, so the PK uses `TID#-#WID#-` sentinels.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-13-01-tenant-and-workspace-fhir-types-control-plane.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const TenantEntity = new Entity({\n model: {\n entity: \"tenant\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** The tenant's own id (= resource id). Drives the partition key. */\n tenantId: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR Resource.id; logical id in URL. Equals tenantId. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full Tenant resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = TENANT#ID#<tenantId>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"tenantId\"],\n template: \"TENANT#ID#${tenantId}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Tenants across the four shards.\n * Tenant lives at the platform tier (no parent tenant or workspace), so `TID#-#WID#-`\n * sentinels precede `RT#Tenant#SHARD#<n>`. SK is derived via `gsi1skAttribute` —\n * `<normalizedName>#<id>` when the resource carries a `name`, else `<lastUpdated>#<id>`\n * (DR-004). `casing: \"none\"` preserves the normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"gsi1Shard\"],\n template: \"TID#-#WID#-#RT#Tenant#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n },\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * User data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Non-tenant-isolated, control plane. User is a\n * platform-wide identity; association with tenants and workspaces is through Membership and\n * RoleAssignment, not the User entity's own key.\n *\n * Key structure: PK = USER#ID#<id>, SK = CURRENT.\n * The USER# prefix prevents key collisions with other non-tenant-isolated entities (Role, etc.)\n * sharing the same table (ADR 2026-03-11-01 — preferred pattern for all control plane entities).\n * Uniqueness: one User per id.\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Users across the four shards. Non-tenant-\n * isolated, so the PK uses `TID#-#WID#-` sentinels.\n * GSI2 — Cognito sub-lookup per ADR-011: resolves a UserEntity from a Cognito `sub` claim\n * (`USER#SUB#<cognitoSub>` PK, `CURRENT` SK). The `cognitoSub` attribute is populated by the\n * Post Confirmation Lambda (Epic #765 / #770); kept optional here until that write path lands.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-11-01-user-type-definition-fhir-and-data-layer.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const UserEntity = new Entity({\n model: {\n entity: \"user\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** FHIR Resource.id; platform user id (ohi_uid). */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full User resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Immutable Cognito-issued `sub` claim. Drives GSI2 (sub-lookup). Optional until the\n * Post Confirmation Lambda (#770) lands; required thereafter.\n */\n cognitoSub: {\n type: \"string\" as const,\n required: false,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = USER#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"id\"],\n template: \"USER#ID#${id}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Users across the four shards.\n * Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#User#SHARD#<n>`.\n * SK is derived via `gsi1skAttribute` — uses the resource's natural label when\n * extractable (string `name`/`title` via introspection), else `<lastUpdated>#<id>`\n * (DR-004). `casing: \"none\"` preserves the normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"gsi1Shard\"],\n template: \"TID#-#WID#-#RT#User#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n\n /**\n * GSI2 — Cognito sub-lookup per ADR-011: resolves the UserEntity from a Cognito `sub` claim.\n * `condition` skips the index when `cognitoSub` is missing so legacy items without a sub are\n * not indexed.\n */\n gsi2: {\n index: \"GSI2\",\n condition: (attrs: { cognitoSub?: string }) =>\n typeof attrs.cognitoSub === \"string\" && attrs.cognitoSub.length > 0,\n pk: {\n field: \"GSI2PK\",\n casing: \"none\" as const,\n composite: [\"cognitoSub\"],\n template: \"USER#SUB#${cognitoSub}\",\n },\n sk: {\n field: \"GSI2SK\",\n casing: \"none\" as const,\n composite: [],\n template: \"CURRENT\",\n },\n },\n },\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * Workspace data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, control plane. Each workspace belongs\n * to exactly one tenant; both tenantId and workspace id are in the partition key.\n *\n * Key structure: PK = TID#<tenantId>#WORKSPACE#ID#<id>, SK = CURRENT.\n * Uniqueness: one Workspace per (tenantId, id).\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Workspaces in a tenant across the four\n * shards. Workspace is itself the workspace identity, so the GSI1 PK uses `WID#-` as a sentinel.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-13-01-tenant-and-workspace-fhir-types-control-plane.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const WorkspaceEntity = new Entity({\n model: {\n entity: \"workspace\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** Tenant that contains this workspace (required). */\n tenantId: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR Resource.id; logical id in URL. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full Workspace resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = TID#<tenantId>#WORKSPACE#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"tenantId\", \"id\"],\n template: \"TID#${tenantId}#WORKSPACE#ID#${id}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Workspaces for a tenant across the\n * four shards. Workspace is itself the workspace identity, so `WID#-` is a sentinel.\n * SK is derived via `gsi1skAttribute` — `<normalizedName>#<id>` when the resource\n * carries a `name`, else `<lastUpdated>#<id>` (DR-004). `casing: \"none\"` preserves\n * the normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"tenantId\", \"gsi1Shard\"],\n template: \"TID#${tenantId}#WID#-#RT#Workspace#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n },\n});\n","import {\n assertLinkedDataIdentityCardinality,\n extractSummary,\n type Extension,\n type FhirResourceLike,\n LinkedDataIdentityCardinalityError,\n} from \"@openhi/types\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport { ValidationError } from \"../../../errors\";\nimport { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface MembershipCreateParams {\n context: OpenHiContext;\n body: { id?: string; resource?: Record<string, unknown> | string };\n tableName?: string;\n}\n\nexport interface MembershipCreateResult {\n id: string;\n resource: { resourceType: string; id: string; [key: string]: unknown };\n meta: { lastUpdated: string; versionId: string };\n}\n\nexport async function createMembershipOperation(\n params: MembershipCreateParams,\n): Promise<MembershipCreateResult> {\n const { context, body, tableName } = params;\n const service = getDynamoControlService(tableName);\n\n const id = body.id ?? `membership-${Date.now()}`;\n const parsedResource =\n typeof body.resource === \"string\"\n ? (JSON.parse(body.resource) as Record<string, unknown>)\n : (body.resource ?? {});\n\n const lastUpdated = context.date ?? new Date().toISOString();\n const vid = `1`;\n\n const resource = { resourceType: \"Membership\", id, ...parsedResource };\n\n let linkedDataIdentityRef: string | undefined;\n try {\n const ext = assertLinkedDataIdentityCardinality(\n resource as { extension?: Array<Extension> },\n );\n linkedDataIdentityRef = ext?.valueReference?.reference;\n } catch (e) {\n if (e instanceof LinkedDataIdentityCardinalityError) {\n throw new ValidationError(e.message, { cause: e });\n }\n throw e;\n }\n\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n await service.entities.membership\n .put({\n tenantId: context.tenantId,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n linkedDataIdentityRef,\n })\n .go();\n\n return {\n id,\n resource,\n meta: { lastUpdated, versionId: vid },\n };\n}\n","import { extractSummary, type FhirResourceLike } from \"@openhi/types\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface RoleAssignmentCreateParams {\n context: OpenHiContext;\n body: { id?: string; resource?: Record<string, unknown> | string };\n tableName?: string;\n}\n\nexport interface RoleAssignmentCreateResult {\n id: string;\n resource: { resourceType: string; id: string; [key: string]: unknown };\n meta: { lastUpdated: string; versionId: string };\n}\n\nexport async function createRoleAssignmentOperation(\n params: RoleAssignmentCreateParams,\n): Promise<RoleAssignmentCreateResult> {\n const { context, body, tableName } = params;\n const service = getDynamoControlService(tableName);\n\n const id = body.id ?? `roleassignment-${Date.now()}`;\n const parsedResource =\n typeof body.resource === \"string\"\n ? (JSON.parse(body.resource) as Record<string, unknown>)\n : (body.resource ?? {});\n\n const lastUpdated = context.date ?? new Date().toISOString();\n const vid = `1`;\n\n const resource = { resourceType: \"RoleAssignment\", id, ...parsedResource };\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n await service.entities.roleAssignment\n .put({\n tenantId: context.tenantId,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n })\n .go();\n\n return {\n id,\n resource,\n meta: { lastUpdated, versionId: vid },\n };\n}\n","import { extractSummary, type FhirResourceLike } from \"@openhi/types\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport type { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface CreateTenantParams {\n context: OpenHiContext;\n body: {\n id?: string;\n resource?: Record<string, unknown> | string;\n };\n tableName?: string;\n}\n\nexport interface CreateTenantResult {\n id: string;\n resource: Record<string, unknown>;\n meta: { lastUpdated: string; versionId: string };\n}\n\n/**\n * Creates a Tenant. Generates an id if not provided.\n */\nexport async function createTenantOperation(\n params: CreateTenantParams,\n): Promise<CreateTenantResult> {\n const { context, body, tableName } = params;\n const service = getDynamoControlService(tableName);\n\n const id = body.id ?? `tenant-${Date.now()}`;\n const lastUpdated = context.date;\n const vid =\n lastUpdated.replace(/[-:T.Z]/g, \"\").slice(0, 12) || Date.now().toString(36);\n\n const parsedResource =\n typeof body.resource === \"string\"\n ? (JSON.parse(body.resource) as Record<string, unknown>)\n : (body.resource ?? {});\n\n const resource = { resourceType: \"Tenant\", id, ...parsedResource };\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n await service.entities.tenant\n .put({\n tenantId: id,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n })\n .go();\n\n return { id, resource, meta: { lastUpdated, versionId: vid } };\n}\n","import { extractSummary, type FhirResourceLike } from \"@openhi/types\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport type { OpenHiContext } from \"../../../openhi-context\";\nimport { provisionOrganizationForWorkspaceOperation } from \"../../data/organization/organization-provision-for-workspace-operation\";\n\nexport interface CreateWorkspaceParams {\n context: OpenHiContext;\n body: {\n id?: string;\n resource?: Record<string, unknown> | string;\n };\n tableName?: string;\n}\n\nexport interface CreateWorkspaceResult {\n id: string;\n resource: Record<string, unknown>;\n meta: { lastUpdated: string; versionId: string };\n}\n\n/**\n * Creates a Workspace scoped to the context tenant. Generates an id if not provided.\n */\nexport async function createWorkspaceOperation(\n params: CreateWorkspaceParams,\n): Promise<CreateWorkspaceResult> {\n const { context, body, tableName } = params;\n const { tenantId } = context;\n const service = getDynamoControlService(tableName);\n\n const id = body.id ?? `workspace-${Date.now()}`;\n const lastUpdated = context.date;\n const vid =\n lastUpdated.replace(/[-:T.Z]/g, \"\").slice(0, 12) || Date.now().toString(36);\n\n const parsedResource =\n typeof body.resource === \"string\"\n ? (JSON.parse(body.resource) as Record<string, unknown>)\n : (body.resource ?? {});\n\n const resource = { resourceType: \"Workspace\", id, ...parsedResource };\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n await service.entities.workspace\n .put({\n tenantId,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n })\n .go();\n\n const workspaceName =\n typeof parsedResource.name === \"string\" ? parsedResource.name : undefined;\n await provisionOrganizationForWorkspaceOperation({\n context,\n workspaceId: id,\n workspaceName,\n tableName,\n });\n\n return { id, resource, meta: { lastUpdated, versionId: vid } };\n}\n","import { Service } from \"electrodb\";\nimport { defaultTableName, dynamoClient } from \"./dynamo-client\";\nimport { AccountEntity } from \"./entities/data/account-entity\";\nimport { ActivityDefinitionEntity } from \"./entities/data/activity-definition-entity\";\nimport { AdverseEventEntity } from \"./entities/data/adverse-event-entity\";\nimport { AllergyIntoleranceEntity } from \"./entities/data/allergy-intolerance-entity\";\nimport { AppointmentEntity } from \"./entities/data/appointment-entity\";\nimport { AppointmentResponseEntity } from \"./entities/data/appointment-response-entity\";\nimport { AuditEventEntity } from \"./entities/data/audit-event-entity\";\nimport { BasicEntity } from \"./entities/data/basic-entity\";\nimport { BiologicallyDerivedProductEntity } from \"./entities/data/biologically-derived-product-entity\";\nimport { BodyStructureEntity } from \"./entities/data/body-structure-entity\";\nimport { CapabilityStatementEntity } from \"./entities/data/capability-statement-entity\";\nimport { CarePlanEntity } from \"./entities/data/care-plan-entity\";\nimport { CareTeamEntity } from \"./entities/data/care-team-entity\";\nimport { CatalogEntryEntity } from \"./entities/data/catalog-entry-entity\";\nimport { ChargeItemDefinitionEntity } from \"./entities/data/charge-item-definition-entity\";\nimport { ChargeItemEntity } from \"./entities/data/charge-item-entity\";\nimport { ClaimEntity } from \"./entities/data/claim-entity\";\nimport { ClaimResponseEntity } from \"./entities/data/claim-response-entity\";\nimport { ClinicalImpressionEntity } from \"./entities/data/clinical-impression-entity\";\nimport { CodeSystemEntity } from \"./entities/data/code-system-entity\";\nimport { CommunicationEntity } from \"./entities/data/communication-entity\";\nimport { CommunicationRequestEntity } from \"./entities/data/communication-request-entity\";\nimport { CompartmentDefinitionEntity } from \"./entities/data/compartment-definition-entity\";\nimport { CompositionEntity } from \"./entities/data/composition-entity\";\nimport { ConceptMapEntity } from \"./entities/data/concept-map-entity\";\nimport { ConditionEntity } from \"./entities/data/condition-entity\";\nimport { ConsentEntity } from \"./entities/data/consent-entity\";\nimport { ContractEntity } from \"./entities/data/contract-entity\";\nimport { CoverageEligibilityRequestEntity } from \"./entities/data/coverage-eligibility-request-entity\";\nimport { CoverageEligibilityResponseEntity } from \"./entities/data/coverage-eligibility-response-entity\";\nimport { CoverageEntity } from \"./entities/data/coverage-entity\";\nimport { DetectedIssueEntity } from \"./entities/data/detected-issue-entity\";\nimport { DeviceDefinitionEntity } from \"./entities/data/device-definition-entity\";\nimport { DeviceEntity } from \"./entities/data/device-entity\";\nimport { DeviceMetricEntity } from \"./entities/data/device-metric-entity\";\nimport { DeviceRequestEntity } from \"./entities/data/device-request-entity\";\nimport { DeviceUseStatementEntity } from \"./entities/data/device-use-statement-entity\";\nimport { DiagnosticReportEntity } from \"./entities/data/diagnostic-report-entity\";\nimport { DocumentManifestEntity } from \"./entities/data/document-manifest-entity\";\nimport { DocumentReferenceEntity } from \"./entities/data/document-reference-entity\";\nimport { EffectEvidenceSynthesisEntity } from \"./entities/data/effect-evidence-synthesis-entity\";\nimport { EncounterEntity } from \"./entities/data/encounter-entity\";\nimport { EndpointEntity } from \"./entities/data/endpoint-entity\";\nimport { EnrollmentRequestEntity } from \"./entities/data/enrollment-request-entity\";\nimport { EnrollmentResponseEntity } from \"./entities/data/enrollment-response-entity\";\nimport { EpisodeOfCareEntity } from \"./entities/data/episode-of-care-entity\";\nimport { EventDefinitionEntity } from \"./entities/data/event-definition-entity\";\nimport { EvidenceEntity } from \"./entities/data/evidence-entity\";\nimport { EvidenceVariableEntity } from \"./entities/data/evidence-variable-entity\";\nimport { ExampleScenarioEntity } from \"./entities/data/example-scenario-entity\";\nimport { ExplanationOfBenefitEntity } from \"./entities/data/explanation-of-benefit-entity\";\nimport { FamilyMemberHistoryEntity } from \"./entities/data/family-member-history-entity\";\nimport { FlagEntity } from \"./entities/data/flag-entity\";\nimport { GoalEntity } from \"./entities/data/goal-entity\";\nimport { GraphDefinitionEntity } from \"./entities/data/graph-definition-entity\";\nimport { GroupEntity } from \"./entities/data/group-entity\";\nimport { GuidanceResponseEntity } from \"./entities/data/guidance-response-entity\";\nimport { HealthcareServiceEntity } from \"./entities/data/healthcare-service-entity\";\nimport { ImagingStudyEntity } from \"./entities/data/imaging-study-entity\";\nimport { ImmunizationEntity } from \"./entities/data/immunization-entity\";\nimport { ImmunizationEvaluationEntity } from \"./entities/data/immunization-evaluation-entity\";\nimport { ImmunizationRecommendationEntity } from \"./entities/data/immunization-recommendation-entity\";\nimport { ImplementationGuideEntity } from \"./entities/data/implementation-guide-entity\";\nimport { InsurancePlanEntity } from \"./entities/data/insurance-plan-entity\";\nimport { InvoiceEntity } from \"./entities/data/invoice-entity\";\nimport { LibraryEntity } from \"./entities/data/library-entity\";\nimport { LinkageEntity } from \"./entities/data/linkage-entity\";\nimport { ListEntity } from \"./entities/data/list-entity\";\nimport { LocationEntity } from \"./entities/data/location-entity\";\nimport { MeasureEntity } from \"./entities/data/measure-entity\";\nimport { MeasureReportEntity } from \"./entities/data/measure-report-entity\";\nimport { MediaEntity } from \"./entities/data/media-entity\";\nimport { MedicationAdministrationEntity } from \"./entities/data/medication-administration-entity\";\nimport { MedicationDispenseEntity } from \"./entities/data/medication-dispense-entity\";\nimport { MedicationEntity } from \"./entities/data/medication-entity\";\nimport { MedicationKnowledgeEntity } from \"./entities/data/medication-knowledge-entity\";\nimport { MedicationRequestEntity } from \"./entities/data/medication-request-entity\";\nimport { MedicationStatementEntity } from \"./entities/data/medication-statement-entity\";\nimport { MedicinalProductAuthorizationEntity } from \"./entities/data/medicinal-product-authorization-entity\";\nimport { MedicinalProductContraindicationEntity } from \"./entities/data/medicinal-product-contraindication-entity\";\nimport { MedicinalProductEntity } from \"./entities/data/medicinal-product-entity\";\nimport { MedicinalProductIndicationEntity } from \"./entities/data/medicinal-product-indication-entity\";\nimport { MedicinalProductIngredientEntity } from \"./entities/data/medicinal-product-ingredient-entity\";\nimport { MedicinalProductInteractionEntity } from \"./entities/data/medicinal-product-interaction-entity\";\nimport { MedicinalProductManufacturedEntity } from \"./entities/data/medicinal-product-manufactured-entity\";\nimport { MedicinalProductPackagedEntity } from \"./entities/data/medicinal-product-packaged-entity\";\nimport { MedicinalProductPharmaceuticalEntity } from \"./entities/data/medicinal-product-pharmaceutical-entity\";\nimport { MedicinalProductUndesirableEffectEntity } from \"./entities/data/medicinal-product-undesirable-effect-entity\";\nimport { MessageDefinitionEntity } from \"./entities/data/message-definition-entity\";\nimport { MessageHeaderEntity } from \"./entities/data/message-header-entity\";\nimport { MolecularSequenceEntity } from \"./entities/data/molecular-sequence-entity\";\nimport { NamingSystemEntity } from \"./entities/data/naming-system-entity\";\nimport { NutritionOrderEntity } from \"./entities/data/nutrition-order-entity\";\nimport { ObservationDefinitionEntity } from \"./entities/data/observation-definition-entity\";\nimport { ObservationEntity } from \"./entities/data/observation-entity\";\nimport { OperationDefinitionEntity } from \"./entities/data/operation-definition-entity\";\nimport { OrganizationAffiliationEntity } from \"./entities/data/organization-affiliation-entity\";\nimport { OrganizationEntity } from \"./entities/data/organization-entity\";\nimport { PatientEntity } from \"./entities/data/patient-entity\";\nimport { PaymentNoticeEntity } from \"./entities/data/payment-notice-entity\";\nimport { PaymentReconciliationEntity } from \"./entities/data/payment-reconciliation-entity\";\nimport { PersonEntity } from \"./entities/data/person-entity\";\nimport { PlanDefinitionEntity } from \"./entities/data/plan-definition-entity\";\nimport { PractitionerEntity } from \"./entities/data/practitioner-entity\";\nimport { PractitionerRoleEntity } from \"./entities/data/practitioner-role-entity\";\nimport { ProcedureEntity } from \"./entities/data/procedure-entity\";\nimport { ProvenanceEntity } from \"./entities/data/provenance-entity\";\nimport { QuestionnaireEntity } from \"./entities/data/questionnaire-entity\";\nimport { QuestionnaireResponseEntity } from \"./entities/data/questionnaire-response-entity\";\nimport { RelatedPersonEntity } from \"./entities/data/related-person-entity\";\nimport { RequestGroupEntity } from \"./entities/data/request-group-entity\";\nimport { ResearchDefinitionEntity } from \"./entities/data/research-definition-entity\";\nimport { ResearchElementDefinitionEntity } from \"./entities/data/research-element-definition-entity\";\nimport { ResearchStudyEntity } from \"./entities/data/research-study-entity\";\nimport { ResearchSubjectEntity } from \"./entities/data/research-subject-entity\";\nimport { RiskAssessmentEntity } from \"./entities/data/risk-assessment-entity\";\nimport { RiskEvidenceSynthesisEntity } from \"./entities/data/risk-evidence-synthesis-entity\";\nimport { ScheduleEntity } from \"./entities/data/schedule-entity\";\nimport { SearchParameterEntity } from \"./entities/data/search-parameter-entity\";\nimport { ServiceRequestEntity } from \"./entities/data/service-request-entity\";\nimport { SlotEntity } from \"./entities/data/slot-entity\";\nimport { SpecimenDefinitionEntity } from \"./entities/data/specimen-definition-entity\";\nimport { SpecimenEntity } from \"./entities/data/specimen-entity\";\nimport { StructureDefinitionEntity } from \"./entities/data/structure-definition-entity\";\nimport { StructureMapEntity } from \"./entities/data/structure-map-entity\";\nimport { SubscriptionEntity } from \"./entities/data/subscription-entity\";\nimport { SubstanceEntity } from \"./entities/data/substance-entity\";\nimport { SubstanceNucleicAcidEntity } from \"./entities/data/substance-nucleic-acid-entity\";\nimport { SubstancePolymerEntity } from \"./entities/data/substance-polymer-entity\";\nimport { SubstanceProteinEntity } from \"./entities/data/substance-protein-entity\";\nimport { SubstanceReferenceInformationEntity } from \"./entities/data/substance-reference-information-entity\";\nimport { SubstanceSourceMaterialEntity } from \"./entities/data/substance-source-material-entity\";\nimport { SubstanceSpecificationEntity } from \"./entities/data/substance-specification-entity\";\nimport { SupplyDeliveryEntity } from \"./entities/data/supply-delivery-entity\";\nimport { SupplyRequestEntity } from \"./entities/data/supply-request-entity\";\nimport { TaskEntity } from \"./entities/data/task-entity\";\nimport { TerminologyCapabilitiesEntity } from \"./entities/data/terminology-capabilities-entity\";\nimport { TestReportEntity } from \"./entities/data/test-report-entity\";\nimport { TestScriptEntity } from \"./entities/data/test-script-entity\";\nimport { ValueSetEntity } from \"./entities/data/value-set-entity\";\nimport { VerificationResultEntity } from \"./entities/data/verification-result-entity\";\nimport { VisionPrescriptionEntity } from \"./entities/data/vision-prescription-entity\";\n\n/**\n * Data-plane entities only (service \"data\"). Same table as control plane; use\n * DynamoControlService for control-plane entities.\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n */\n\nconst dataPlaneEntities = {\n account: AccountEntity,\n activitydefinition: ActivityDefinitionEntity,\n adverseevent: AdverseEventEntity,\n allergyintolerance: AllergyIntoleranceEntity,\n appointment: AppointmentEntity,\n appointmentresponse: AppointmentResponseEntity,\n auditevent: AuditEventEntity,\n basic: BasicEntity,\n biologicallyderivedproduct: BiologicallyDerivedProductEntity,\n bodystructure: BodyStructureEntity,\n capabilitystatement: CapabilityStatementEntity,\n careplan: CarePlanEntity,\n careteam: CareTeamEntity,\n catalogentry: CatalogEntryEntity,\n codesystem: CodeSystemEntity,\n chargeitem: ChargeItemEntity,\n chargeitemdefinition: ChargeItemDefinitionEntity,\n claim: ClaimEntity,\n claimresponse: ClaimResponseEntity,\n clinicalimpression: ClinicalImpressionEntity,\n communication: CommunicationEntity,\n communicationrequest: CommunicationRequestEntity,\n compartmentdefinition: CompartmentDefinitionEntity,\n composition: CompositionEntity,\n conceptmap: ConceptMapEntity,\n condition: ConditionEntity,\n consent: ConsentEntity,\n contract: ContractEntity,\n coverage: CoverageEntity,\n coverageeligibilityrequest: CoverageEligibilityRequestEntity,\n coverageeligibilityresponse: CoverageEligibilityResponseEntity,\n detectedissue: DetectedIssueEntity,\n device: DeviceEntity,\n devicedefinition: DeviceDefinitionEntity,\n devicemetric: DeviceMetricEntity,\n devicerequest: DeviceRequestEntity,\n deviceusestatement: DeviceUseStatementEntity,\n diagnosticreport: DiagnosticReportEntity,\n documentmanifest: DocumentManifestEntity,\n documentreference: DocumentReferenceEntity,\n effectevidencesynthesis: EffectEvidenceSynthesisEntity,\n encounter: EncounterEntity,\n examplescenario: ExampleScenarioEntity,\n endpoint: EndpointEntity,\n enrollmentrequest: EnrollmentRequestEntity,\n enrollmentresponse: EnrollmentResponseEntity,\n episodeofcare: EpisodeOfCareEntity,\n eventdefinition: EventDefinitionEntity,\n evidence: EvidenceEntity,\n evidencevariable: EvidenceVariableEntity,\n explanationofbenefit: ExplanationOfBenefitEntity,\n familymemberhistory: FamilyMemberHistoryEntity,\n flag: FlagEntity,\n goal: GoalEntity,\n graphdefinition: GraphDefinitionEntity,\n group: GroupEntity,\n guidanceresponse: GuidanceResponseEntity,\n healthcareservice: HealthcareServiceEntity,\n immunization: ImmunizationEntity,\n immunizationevaluation: ImmunizationEvaluationEntity,\n immunizationrecommendation: ImmunizationRecommendationEntity,\n imagingstudy: ImagingStudyEntity,\n implementationguide: ImplementationGuideEntity,\n insuranceplan: InsurancePlanEntity,\n invoice: InvoiceEntity,\n library: LibraryEntity,\n linkage: LinkageEntity,\n list: ListEntity,\n location: LocationEntity,\n medication: MedicationEntity,\n medicationadministration: MedicationAdministrationEntity,\n medicationdispense: MedicationDispenseEntity,\n medicationknowledge: MedicationKnowledgeEntity,\n medicationrequest: MedicationRequestEntity,\n medicationstatement: MedicationStatementEntity,\n medicinalproduct: MedicinalProductEntity,\n medicinalproductauthorization: MedicinalProductAuthorizationEntity,\n medicinalproductcontraindication: MedicinalProductContraindicationEntity,\n medicinalproductingredient: MedicinalProductIngredientEntity,\n medicinalproductindication: MedicinalProductIndicationEntity,\n medicinalproductinteraction: MedicinalProductInteractionEntity,\n medicinalproductmanufactured: MedicinalProductManufacturedEntity,\n medicinalproductpackaged: MedicinalProductPackagedEntity,\n medicinalproductpharmaceutical: MedicinalProductPharmaceuticalEntity,\n medicinalproductundesirableeffect: MedicinalProductUndesirableEffectEntity,\n media: MediaEntity,\n measure: MeasureEntity,\n measurereport: MeasureReportEntity,\n messagedefinition: MessageDefinitionEntity,\n messageheader: MessageHeaderEntity,\n molecularsequence: MolecularSequenceEntity,\n namingsystem: NamingSystemEntity,\n nutritionorder: NutritionOrderEntity,\n observation: ObservationEntity,\n observationdefinition: ObservationDefinitionEntity,\n operationdefinition: OperationDefinitionEntity,\n organization: OrganizationEntity,\n organizationaffiliation: OrganizationAffiliationEntity,\n patient: PatientEntity,\n paymentnotice: PaymentNoticeEntity,\n paymentreconciliation: PaymentReconciliationEntity,\n person: PersonEntity,\n plandefinition: PlanDefinitionEntity,\n practitioner: PractitionerEntity,\n practitionerrole: PractitionerRoleEntity,\n procedure: ProcedureEntity,\n provenance: ProvenanceEntity,\n questionnaire: QuestionnaireEntity,\n questionnaireresponse: QuestionnaireResponseEntity,\n requestgroup: RequestGroupEntity,\n relatedperson: RelatedPersonEntity,\n researchdefinition: ResearchDefinitionEntity,\n researchelementdefinition: ResearchElementDefinitionEntity,\n researchstudy: ResearchStudyEntity,\n researchsubject: ResearchSubjectEntity,\n riskassessment: RiskAssessmentEntity,\n riskevidencesynthesis: RiskEvidenceSynthesisEntity,\n schedule: ScheduleEntity,\n searchparameter: SearchParameterEntity,\n servicerequest: ServiceRequestEntity,\n specimen: SpecimenEntity,\n specimendefinition: SpecimenDefinitionEntity,\n structuredefinition: StructureDefinitionEntity,\n structuremap: StructureMapEntity,\n substance: SubstanceEntity,\n substancenucleicacid: SubstanceNucleicAcidEntity,\n substancepolymer: SubstancePolymerEntity,\n substanceprotein: SubstanceProteinEntity,\n substancereferenceinformation: SubstanceReferenceInformationEntity,\n substancespecification: SubstanceSpecificationEntity,\n substancesourcematerial: SubstanceSourceMaterialEntity,\n subscription: SubscriptionEntity,\n terminologycapabilities: TerminologyCapabilitiesEntity,\n testreport: TestReportEntity,\n testscript: TestScriptEntity,\n valueset: ValueSetEntity,\n supplydelivery: SupplyDeliveryEntity,\n supplyrequest: SupplyRequestEntity,\n slot: SlotEntity,\n task: TaskEntity,\n visionprescription: VisionPrescriptionEntity,\n verificationresult: VerificationResultEntity,\n};\n\nconst dataPlaneService = new Service(dataPlaneEntities, {\n table: defaultTableName,\n client: dynamoClient,\n});\n\n/**\n * Data-plane service: entities for the data store (FHIR and related). Both data-plane\n * and control-plane entities share the base table (PK, SK) and the unified GSI1 (sharded\n * resource-type list per ADR-011). Same physical table.\n */\nexport const DynamoDataService = {\n entities: dataPlaneService.entities,\n};\n\nexport type DynamoDataServiceType = typeof DynamoDataService;\n\n/**\n * Returns the data-plane service. Table name is resolved from tableName (optional override),\n * then DYNAMO_TABLE_NAME, then \"jesttesttable\".\n */\nexport function getDynamoDataService(\n tableName?: string,\n): DynamoDataServiceType {\n const resolved = tableName ?? defaultTableName;\n const service = new Service(dataPlaneEntities, {\n table: resolved,\n client: dynamoClient,\n });\n return {\n entities: service.entities,\n };\n}\n","import { Entity } from \"electrodb\";\nimport { computeShard } from \"../shard\";\n\n/**\n * Shared attributes for all data-plane FHIR entities (tenant-isolated, single-table store).\n * Used by all data-plane entities (Account, Patient, Encounter, Practitioner, etc.).\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md\n */\nexport const dataEntityAttributes = {\n /** Sort key. \"CURRENT\" for current version; version history in S3. */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n tenantId: {\n type: \"string\" as const,\n required: true,\n },\n workspaceId: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR Resource.id; logical id in URL and PK. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR resource as JSON string. JSON.stringify(resource) on write; JSON.parse(item.resource) on read. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection of the FHIR resource as a JSON string (uncompressed). Populated on every\n * write via `extractSummary(resource)` so GSI projections can surface list/lookup data without\n * reading the compressed `resource` blob. Kept uncompressed because the summary is small and\n * must be fast to retrieve without encode/decode overhead.\n *\n * @see sites/www-docs/content/architecture/adr/2026-04-17-02-fhir-summary-projection-for-gsi-access-patterns.md\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). Tracks current version; S3 history key. */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Shard index segment for the GSI1 partition key. Computed deterministically from `id`\n * via `computeShard` so updates always land on the same shard. Stored as a string because\n * it appears as a literal segment in the GSI1 PK template; the underlying value is 0..3.\n * Not `required` because the value is derived via `watch`/`set`; ElectroDB's required-field\n * check runs before watch propagation, so callers must not fail validation on a derived field.\n *\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md — GSI1 (sharded)\n */\n gsi1Shard: {\n type: \"string\" as const,\n watch: [\"id\"] as const,\n set: (_val?: string, item?: { id?: string }) => {\n if (typeof item?.id !== \"string\" || item.id.length === 0) {\n return undefined;\n }\n return String(computeShard(item.id));\n },\n },\n /**\n * GSI1 sort key. Written as the index's SK verbatim so list endpoints can\n * use `BEGINS_WITH` for prefix queries (e.g. `?name=Sm` against Patient).\n * Computed at write time via `extractSortKey(resource)` per DR-004:\n * - Labeled types (LABEL_PATHS): `<normalizedLabel>#<id>`\n * - Unlabeled types: `<ISO-8601 lastUpdated>#<id>`\n * The factory deliberately does not derive this from `lastUpdated`/`id`\n * — that would lock every type into the unlabeled fallback and defeat\n * label-based BEGINS_WITH on labeled types.\n *\n * @see openhi-planning DR-004\n */\n gsi1sk: {\n type: \"string\" as const,\n required: true,\n },\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n};\n\n/** Data-plane entity names (model.entity and key segment). Single source of truth for allowed values. Keys are UPPER_SNAKE for consistency. */\nexport const DATA_ENTITY_NAMES = {\n ACCOUNT: \"account\",\n ACTIVITY_DEFINITION: \"activitydefinition\",\n ADVERSE_EVENT: \"adverseevent\",\n ALLERGY_INTOLERANCE: \"allergyintolerance\",\n APPOINTMENT: \"appointment\",\n APPOINTMENT_RESPONSE: \"appointmentresponse\",\n AUDIT_EVENT: \"auditevent\",\n BASIC: \"basic\",\n BIOLOGICALLY_DERIVED_PRODUCT: \"biologicallyderivedproduct\",\n BODY_STRUCTURE: \"bodystructure\",\n CAPABILITY_STATEMENT: \"capabilitystatement\",\n CARE_PLAN: \"careplan\",\n CARE_TEAM: \"careteam\",\n CATALOG_ENTRY: \"catalogentry\",\n CODE_SYSTEM: \"codesystem\",\n COMPARTMENT_DEFINITION: \"compartmentdefinition\",\n CONCEPT_MAP: \"conceptmap\",\n CHARGE_ITEM: \"chargeitem\",\n CHARGE_ITEM_DEFINITION: \"chargeitemdefinition\",\n CLAIM: \"claim\",\n CLAIM_RESPONSE: \"claimresponse\",\n CLINICAL_IMPRESSION: \"clinicalimpression\",\n COMMUNICATION: \"communication\",\n COMMUNICATION_REQUEST: \"communicationrequest\",\n COMPOSITION: \"composition\",\n CONDITION: \"condition\",\n CONSENT: \"consent\",\n CONTRACT: \"contract\",\n COVERAGE: \"coverage\",\n COVERAGE_ELIGIBILITY_REQUEST: \"coverageeligibilityrequest\",\n COVERAGE_ELIGIBILITY_RESPONSE: \"coverageeligibilityresponse\",\n DETECTED_ISSUE: \"detectedissue\",\n DEVICE: \"device\",\n DEVICE_DEFINITION: \"devicedefinition\",\n DEVICE_METRIC: \"devicemetric\",\n DEVICE_REQUEST: \"devicerequest\",\n DEVICE_USE_STATEMENT: \"deviceusestatement\",\n DIAGNOSTIC_REPORT: \"diagnosticreport\",\n DOCUMENT_MANIFEST: \"documentmanifest\",\n DOCUMENT_REFERENCE: \"documentreference\",\n EFFECT_EVIDENCE_SYNTHESIS: \"effectevidencesynthesis\",\n ENCOUNTER: \"encounter\",\n ENDPOINT: \"endpoint\",\n ENROLLMENT_REQUEST: \"enrollmentrequest\",\n ENROLLMENT_RESPONSE: \"enrollmentresponse\",\n EPISODE_OF_CARE: \"episodeofcare\",\n EVENT_DEFINITION: \"eventdefinition\",\n EVIDENCE: \"evidence\",\n EVIDENCE_VARIABLE: \"evidencevariable\",\n EXAMPLE_SCENARIO: \"examplescenario\",\n EXPLANATION_OF_BENEFIT: \"explanationofbenefit\",\n FAMILY_MEMBER_HISTORY: \"familymemberhistory\",\n FLAG: \"flag\",\n GOAL: \"goal\",\n GRAPH_DEFINITION: \"graphdefinition\",\n GROUP: \"group\",\n GUIDANCE_RESPONSE: \"guidanceresponse\",\n HEALTHCARE_SERVICE: \"healthcareservice\",\n IMAGING_STUDY: \"imagingstudy\",\n IMPLEMENTATION_GUIDE: \"implementationguide\",\n IMMUNIZATION: \"immunization\",\n IMMUNIZATION_EVALUATION: \"immunizationevaluation\",\n IMMUNIZATION_RECOMMENDATION: \"immunizationrecommendation\",\n INSURANCE_PLAN: \"insuranceplan\",\n INVOICE: \"invoice\",\n LIBRARY: \"library\",\n LINKAGE: \"linkage\",\n LIST: \"list\",\n LOCATION: \"location\",\n MEASURE: \"measure\",\n MEASURE_REPORT: \"measurereport\",\n MEDIA: \"media\",\n MEDICATION: \"medication\",\n MEDICATION_ADMINISTRATION: \"medicationadministration\",\n MEDICATION_DISPENSE: \"medicationdispense\",\n MEDICATION_KNOWLEDGE: \"medicationknowledge\",\n MEDICATION_REQUEST: \"medicationrequest\",\n MEDICATION_STATEMENT: \"medicationstatement\",\n MEDICINAL_PRODUCT: \"medicinalproduct\",\n MEDICINAL_PRODUCT_AUTHORIZATION: \"medicinalproductauthorization\",\n MEDICINAL_PRODUCT_CONTRAINDICATION: \"medicinalproductcontraindication\",\n MEDICINAL_PRODUCT_INGREDIENT: \"medicinalproductingredient\",\n MEDICINAL_PRODUCT_INDICATION: \"medicinalproductindication\",\n MEDICINAL_PRODUCT_INTERACTION: \"medicinalproductinteraction\",\n MEDICINAL_PRODUCT_MANUFACTURED: \"medicinalproductmanufactured\",\n MEDICINAL_PRODUCT_PACKAGED: \"medicinalproductpackaged\",\n MEDICINAL_PRODUCT_PHARMACEUTICAL: \"medicinalproductpharmaceutical\",\n MEDICINAL_PRODUCT_UNDESIRABLE_EFFECT: \"medicinalproductundesirableeffect\",\n MESSAGE_DEFINITION: \"messagedefinition\",\n MESSAGE_HEADER: \"messageheader\",\n MOLECULAR_SEQUENCE: \"molecularsequence\",\n NAMING_SYSTEM: \"namingsystem\",\n NUTRITION_ORDER: \"nutritionorder\",\n OBSERVATION: \"observation\",\n OBSERVATION_DEFINITION: \"observationdefinition\",\n OPERATION_DEFINITION: \"operationdefinition\",\n ORGANIZATION: \"organization\",\n ORGANIZATION_AFFILIATION: \"organizationaffiliation\",\n PATIENT: \"patient\",\n PAYMENT_NOTICE: \"paymentnotice\",\n PAYMENT_RECONCILIATION: \"paymentreconciliation\",\n PERSON: \"person\",\n PLAN_DEFINITION: \"plandefinition\",\n PRACTITIONER: \"practitioner\",\n PRACTITIONER_ROLE: \"practitionerrole\",\n PROCEDURE: \"procedure\",\n PROVENANCE: \"provenance\",\n QUESTIONNAIRE: \"questionnaire\",\n QUESTIONNAIRE_RESPONSE: \"questionnaireresponse\",\n RELATED_PERSON: \"relatedperson\",\n REQUEST_GROUP: \"requestgroup\",\n RESEARCH_DEFINITION: \"researchdefinition\",\n RESEARCH_ELEMENT_DEFINITION: \"researchelementdefinition\",\n RESEARCH_STUDY: \"researchstudy\",\n RESEARCH_SUBJECT: \"researchsubject\",\n SEARCH_PARAMETER: \"searchparameter\",\n RISK_ASSESSMENT: \"riskassessment\",\n RISK_EVIDENCE_SYNTHESIS: \"riskevidencesynthesis\",\n SCHEDULE: \"schedule\",\n SERVICE_REQUEST: \"servicerequest\",\n SLOT: \"slot\",\n SPECIMEN: \"specimen\",\n SPECIMEN_DEFINITION: \"specimendefinition\",\n STRUCTURE_DEFINITION: \"structuredefinition\",\n STRUCTURE_MAP: \"structuremap\",\n SUBSTANCE: \"substance\",\n SUBSTANCE_NUCLEIC_ACID: \"substancenucleicacid\",\n SUBSTANCE_POLYMER: \"substancepolymer\",\n SUBSTANCE_PROTEIN: \"substanceprotein\",\n SUBSTANCE_REFERENCE_INFORMATION: \"substancereferenceinformation\",\n SUBSTANCE_SPECIFICATION: \"substancespecification\",\n SUBSTANCE_SOURCE_MATERIAL: \"substancesourcematerial\",\n SUBSCRIPTION: \"subscription\",\n TERMINOLOGY_CAPABILITIES: \"terminologycapabilities\",\n TEST_REPORT: \"testreport\",\n TEST_SCRIPT: \"testscript\",\n VALUE_SET: \"valueset\",\n SUPPLY_DELIVERY: \"supplydelivery\",\n SUPPLY_REQUEST: \"supplyrequest\",\n TASK: \"task\",\n VERIFICATION_RESULT: \"verificationresult\",\n VISION_PRESCRIPTION: \"visionprescription\",\n} as const;\n\n/** Data-plane entity name (model.entity and key segment). */\nexport type DataEntityName =\n (typeof DATA_ENTITY_NAMES)[keyof typeof DATA_ENTITY_NAMES];\n\n/**\n * Creates an ElectroDB Entity for a data-plane FHIR resource with the common model, attributes,\n * base-table index, and GSI1 (Unified Sharded List per ADR-011 / DR-004) for list-by-type-in-workspace.\n *\n * GSI1 PK shape: `TID#<tid>#WID#<wid>#RT#<resourceTypeLabel>#SHARD#<n>` where `n = computeShard(id)`\n * (4-way fixed sharding). GSI1 SK shape: `<lastUpdated>#<id>` — the unlabeled fallback per DR-004.\n * Labeled sort keys (e.g. `<normalizedLabel>#<id>` for Patient/Practitioner/etc.) ride on top of\n * this contract through the writer's `extractSortKey(resource)` integration; this factory wires the\n * index, attribute, and the deterministic shard derivation. PK template uses resourceTypeLabel as a\n * literal so each entity instance binds its own type segment.\n *\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md — GSI1\n * @see sites/www-docs/content/architecture/adr/ — ADR-011, DR-004\n */\nexport function createDataEntity(\n entity: DataEntityName,\n resourceTypeLabel: string,\n) {\n return new Entity({\n model: {\n entity,\n service: \"data\",\n version: \"01\",\n },\n attributes: dataEntityAttributes,\n indexes: {\n /** Base table: PK, SK (data store key names). PK is built from tenantId, workspaceId, id. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"tenantId\", \"workspaceId\", \"id\"],\n template: `TID#\\${tenantId}#WID#\\${workspaceId}#RT#${resourceTypeLabel}#ID#\\${id}`,\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n },\n },\n /**\n * GSI1 — Unified Sharded List: list all resources of this type in a workspace; reads fan\n * out across the four shards and merge by SK. SK is the writer-supplied `gsi1sk` verbatim\n * (per DR-004) so labeled types serve `BEGINS_WITH` prefix queries on the natural label.\n * `casing: \"none\"` is required on the SK because the writer (`extractSortKey`) already\n * applies DR-004 normalization — ElectroDB's default lowercasing would mangle the\n * ISO-8601 unlabeled fallback (`T`/`Z` → `t`/`z`).\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"tenantId\", \"workspaceId\", \"gsi1Shard\"],\n template: `TID#\\${tenantId}#WID#\\${workspaceId}#RT#${resourceTypeLabel}#SHARD#\\${gsi1Shard}`,\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: `\\${gsi1sk}`,\n },\n },\n },\n });\n}\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Account data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Account#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const AccountEntity = createDataEntity(\"account\", \"Account\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ActivityDefinition data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ActivityDefinition#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ActivityDefinitionEntity = createDataEntity(\n \"activitydefinition\",\n \"ActivityDefinition\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * AdverseEvent data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#AdverseEvent#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const AdverseEventEntity = createDataEntity(\n \"adverseevent\",\n \"AdverseEvent\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * AllergyIntolerance data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#AllergyIntolerance#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const AllergyIntoleranceEntity = createDataEntity(\n \"allergyintolerance\",\n \"AllergyIntolerance\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Appointment data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Appointment#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const AppointmentEntity = createDataEntity(\"appointment\", \"Appointment\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * AppointmentResponse data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#AppointmentResponse#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const AppointmentResponseEntity = createDataEntity(\n \"appointmentresponse\",\n \"AppointmentResponse\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * AuditEvent data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#AuditEvent#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const AuditEventEntity = createDataEntity(\"auditevent\", \"AuditEvent\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Basic data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Basic#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const BasicEntity = createDataEntity(\"basic\", \"Basic\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * BiologicallyDerivedProduct data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#BiologicallyDerivedProduct#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const BiologicallyDerivedProductEntity = createDataEntity(\n \"biologicallyderivedproduct\",\n \"BiologicallyDerivedProduct\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * BodyStructure data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#BodyStructure#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const BodyStructureEntity = createDataEntity(\n \"bodystructure\",\n \"BodyStructure\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * CapabilityStatement data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#CapabilityStatement#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const CapabilityStatementEntity = createDataEntity(\n \"capabilitystatement\",\n \"CapabilityStatement\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * CarePlan data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#CarePlan#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const CarePlanEntity = createDataEntity(\"careplan\", \"CarePlan\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * CareTeam data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#CareTeam#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const CareTeamEntity = createDataEntity(\"careteam\", \"CareTeam\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * CatalogEntry data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#CatalogEntry#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const CatalogEntryEntity = createDataEntity(\n \"catalogentry\",\n \"CatalogEntry\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ChargeItemDefinition data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ChargeItemDefinition#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ChargeItemDefinitionEntity = createDataEntity(\n \"chargeitemdefinition\",\n \"ChargeItemDefinition\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ChargeItem data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ChargeItem#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ChargeItemEntity = createDataEntity(\"chargeitem\", \"ChargeItem\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Claim data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Claim#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ClaimEntity = createDataEntity(\"claim\", \"Claim\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ClaimResponse data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ClaimResponse#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ClaimResponseEntity = createDataEntity(\n \"claimresponse\",\n \"ClaimResponse\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ClinicalImpression data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ClinicalImpression#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ClinicalImpressionEntity = createDataEntity(\n \"clinicalimpression\",\n \"ClinicalImpression\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * CodeSystem data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#CodeSystem#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const CodeSystemEntity = createDataEntity(\"codesystem\", \"CodeSystem\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Communication data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Communication#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const CommunicationEntity = createDataEntity(\n \"communication\",\n \"Communication\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * CommunicationRequest data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#CommunicationRequest#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const CommunicationRequestEntity = createDataEntity(\n \"communicationrequest\",\n \"CommunicationRequest\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * CompartmentDefinition data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#CompartmentDefinition#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const CompartmentDefinitionEntity = createDataEntity(\n \"compartmentdefinition\",\n \"CompartmentDefinition\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Composition data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Composition#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const CompositionEntity = createDataEntity(\"composition\", \"Composition\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ConceptMap data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ConceptMap#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ConceptMapEntity = createDataEntity(\"conceptmap\", \"ConceptMap\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Condition data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Condition#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ConditionEntity = createDataEntity(\"condition\", \"Condition\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Consent data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Consent#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ConsentEntity = createDataEntity(\"consent\", \"Consent\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Contract data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Contract#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ContractEntity = createDataEntity(\"contract\", \"Contract\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * CoverageEligibilityRequest data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#CoverageEligibilityRequest#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const CoverageEligibilityRequestEntity = createDataEntity(\n \"coverageeligibilityrequest\",\n \"CoverageEligibilityRequest\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * CoverageEligibilityResponse data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#CoverageEligibilityResponse#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const CoverageEligibilityResponseEntity = createDataEntity(\n \"coverageeligibilityresponse\",\n \"CoverageEligibilityResponse\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Coverage data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Coverage#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const CoverageEntity = createDataEntity(\"coverage\", \"Coverage\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * DetectedIssue data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#DetectedIssue#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const DetectedIssueEntity = createDataEntity(\n \"detectedissue\",\n \"DetectedIssue\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * DeviceDefinition data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#DeviceDefinition#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const DeviceDefinitionEntity = createDataEntity(\n \"devicedefinition\",\n \"DeviceDefinition\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Device data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Device#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const DeviceEntity = createDataEntity(\"device\", \"Device\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * DeviceMetric data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#DeviceMetric#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const DeviceMetricEntity = createDataEntity(\n \"devicemetric\",\n \"DeviceMetric\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * DeviceRequest data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#DeviceRequest#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const DeviceRequestEntity = createDataEntity(\n \"devicerequest\",\n \"DeviceRequest\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * DeviceUseStatement data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#DeviceUseStatement#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const DeviceUseStatementEntity = createDataEntity(\n \"deviceusestatement\",\n \"DeviceUseStatement\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * DiagnosticReport data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#DiagnosticReport#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const DiagnosticReportEntity = createDataEntity(\n \"diagnosticreport\",\n \"DiagnosticReport\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * DocumentManifest data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#DocumentManifest#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const DocumentManifestEntity = createDataEntity(\n \"documentmanifest\",\n \"DocumentManifest\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * DocumentReference data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#DocumentReference#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const DocumentReferenceEntity = createDataEntity(\n \"documentreference\",\n \"DocumentReference\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * EffectEvidenceSynthesis data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#EffectEvidenceSynthesis#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const EffectEvidenceSynthesisEntity = createDataEntity(\n \"effectevidencesynthesis\",\n \"EffectEvidenceSynthesis\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Encounter data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Encounter#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with Patient and other domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entities/encounter.md\n */\nexport const EncounterEntity = createDataEntity(\"encounter\", \"Encounter\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Endpoint data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Endpoint#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const EndpointEntity = createDataEntity(\"endpoint\", \"Endpoint\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * EnrollmentRequest data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#EnrollmentRequest#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const EnrollmentRequestEntity = createDataEntity(\n \"enrollmentrequest\",\n \"EnrollmentRequest\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * EnrollmentResponse data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#EnrollmentResponse#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const EnrollmentResponseEntity = createDataEntity(\n \"enrollmentresponse\",\n \"EnrollmentResponse\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * EpisodeOfCare data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#EpisodeOfCare#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const EpisodeOfCareEntity = createDataEntity(\n \"episodeofcare\",\n \"EpisodeOfCare\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * EventDefinition data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#EventDefinition#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const EventDefinitionEntity = createDataEntity(\n \"eventdefinition\",\n \"EventDefinition\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Evidence data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Evidence#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const EvidenceEntity = createDataEntity(\"evidence\", \"Evidence\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * EvidenceVariable data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#EvidenceVariable#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const EvidenceVariableEntity = createDataEntity(\n \"evidencevariable\",\n \"EvidenceVariable\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ExampleScenario data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ExampleScenario#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ExampleScenarioEntity = createDataEntity(\n \"examplescenario\",\n \"ExampleScenario\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ExplanationOfBenefit data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ExplanationOfBenefit#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ExplanationOfBenefitEntity = createDataEntity(\n \"explanationofbenefit\",\n \"ExplanationOfBenefit\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * FamilyMemberHistory data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#FamilyMemberHistory#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const FamilyMemberHistoryEntity = createDataEntity(\n \"familymemberhistory\",\n \"FamilyMemberHistory\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Flag data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Flag#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const FlagEntity = createDataEntity(\"flag\", \"Flag\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Goal data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Goal#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const GoalEntity = createDataEntity(\"goal\", \"Goal\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * GraphDefinition data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#GraphDefinition#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const GraphDefinitionEntity = createDataEntity(\n \"graphdefinition\",\n \"GraphDefinition\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Group data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Group#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const GroupEntity = createDataEntity(\"group\", \"Group\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * GuidanceResponse data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#GuidanceResponse#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const GuidanceResponseEntity = createDataEntity(\n \"guidanceresponse\",\n \"GuidanceResponse\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * HealthcareService data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#HealthcareService#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const HealthcareServiceEntity = createDataEntity(\n \"healthcareservice\",\n \"HealthcareService\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ImagingStudy data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ImagingStudy#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ImagingStudyEntity = createDataEntity(\n \"imagingstudy\",\n \"ImagingStudy\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Immunization data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Immunization#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ImmunizationEntity = createDataEntity(\n \"immunization\",\n \"Immunization\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ImmunizationEvaluation data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ImmunizationEvaluation#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ImmunizationEvaluationEntity = createDataEntity(\n \"immunizationevaluation\",\n \"ImmunizationEvaluation\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ImmunizationRecommendation data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ImmunizationRecommendation#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ImmunizationRecommendationEntity = createDataEntity(\n \"immunizationrecommendation\",\n \"ImmunizationRecommendation\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ImplementationGuide data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ImplementationGuide#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ImplementationGuideEntity = createDataEntity(\n \"implementationguide\",\n \"ImplementationGuide\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * InsurancePlan data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#InsurancePlan#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const InsurancePlanEntity = createDataEntity(\n \"insuranceplan\",\n \"InsurancePlan\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Invoice data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Invoice#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const InvoiceEntity = createDataEntity(\"invoice\", \"Invoice\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Library data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Library#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const LibraryEntity = createDataEntity(\"library\", \"Library\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Linkage data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Linkage#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const LinkageEntity = createDataEntity(\"linkage\", \"Linkage\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * List data-store entity based on FHIR R4 (single-table store).\n * Represents the FHIR List resource (curated list of items).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#List#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ListEntity = createDataEntity(\"list\", \"List\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Location data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Location#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const LocationEntity = createDataEntity(\"location\", \"Location\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Measure data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Measure#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MeasureEntity = createDataEntity(\"measure\", \"Measure\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MeasureReport data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MeasureReport#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MeasureReportEntity = createDataEntity(\n \"measurereport\",\n \"MeasureReport\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Media data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Media#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MediaEntity = createDataEntity(\"media\", \"Media\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MedicationAdministration data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MedicationAdministration#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MedicationAdministrationEntity = createDataEntity(\n \"medicationadministration\",\n \"MedicationAdministration\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MedicationDispense data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MedicationDispense#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MedicationDispenseEntity = createDataEntity(\n \"medicationdispense\",\n \"MedicationDispense\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Medication data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Medication#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MedicationEntity = createDataEntity(\"medication\", \"Medication\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MedicationKnowledge data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MedicationKnowledge#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MedicationKnowledgeEntity = createDataEntity(\n \"medicationknowledge\",\n \"MedicationKnowledge\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MedicationRequest data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MedicationRequest#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MedicationRequestEntity = createDataEntity(\n \"medicationrequest\",\n \"MedicationRequest\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MedicationStatement data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MedicationStatement#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MedicationStatementEntity = createDataEntity(\n \"medicationstatement\",\n \"MedicationStatement\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MedicinalProductAuthorization data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MedicinalProductAuthorization#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MedicinalProductAuthorizationEntity = createDataEntity(\n \"medicinalproductauthorization\",\n \"MedicinalProductAuthorization\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MedicinalProductContraindication data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MedicinalProductContraindication#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MedicinalProductContraindicationEntity = createDataEntity(\n \"medicinalproductcontraindication\",\n \"MedicinalProductContraindication\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MedicinalProduct data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MedicinalProduct#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MedicinalProductEntity = createDataEntity(\n \"medicinalproduct\",\n \"MedicinalProduct\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MedicinalProductIndication data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MedicinalProductIndication#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MedicinalProductIndicationEntity = createDataEntity(\n \"medicinalproductindication\",\n \"MedicinalProductIndication\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MedicinalProductIngredient data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MedicinalProductIngredient#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MedicinalProductIngredientEntity = createDataEntity(\n \"medicinalproductingredient\",\n \"MedicinalProductIngredient\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MedicinalProductInteraction data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MedicinalProductInteraction#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MedicinalProductInteractionEntity = createDataEntity(\n \"medicinalproductinteraction\",\n \"MedicinalProductInteraction\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MedicinalProductManufactured data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MedicinalProductManufactured#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MedicinalProductManufacturedEntity = createDataEntity(\n \"medicinalproductmanufactured\",\n \"MedicinalProductManufactured\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MedicinalProductPackaged data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MedicinalProductPackaged#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MedicinalProductPackagedEntity = createDataEntity(\n \"medicinalproductpackaged\",\n \"MedicinalProductPackaged\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MedicinalProductPharmaceutical data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MedicinalProductPharmaceutical#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MedicinalProductPharmaceuticalEntity = createDataEntity(\n \"medicinalproductpharmaceutical\",\n \"MedicinalProductPharmaceutical\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MedicinalProductUndesirableEffect data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MedicinalProductUndesirableEffect#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MedicinalProductUndesirableEffectEntity = createDataEntity(\n \"medicinalproductundesirableeffect\",\n \"MedicinalProductUndesirableEffect\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MessageDefinition data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MessageDefinition#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MessageDefinitionEntity = createDataEntity(\n \"messagedefinition\",\n \"MessageDefinition\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MessageHeader data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MessageHeader#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MessageHeaderEntity = createDataEntity(\n \"messageheader\",\n \"MessageHeader\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * MolecularSequence data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#MolecularSequence#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MolecularSequenceEntity = createDataEntity(\n \"molecularsequence\",\n \"MolecularSequence\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * NamingSystem data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#NamingSystem#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const NamingSystemEntity = createDataEntity(\n \"namingsystem\",\n \"NamingSystem\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * NutritionOrder data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#NutritionOrder#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const NutritionOrderEntity = createDataEntity(\n \"nutritionorder\",\n \"NutritionOrder\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ObservationDefinition data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ObservationDefinition#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ObservationDefinitionEntity = createDataEntity(\n \"observationdefinition\",\n \"ObservationDefinition\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Observation data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Observation#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ObservationEntity = createDataEntity(\"observation\", \"Observation\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * OperationDefinition data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#OperationDefinition#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const OperationDefinitionEntity = createDataEntity(\n \"operationdefinition\",\n \"OperationDefinition\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * OrganizationAffiliation data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#OrganizationAffiliation#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const OrganizationAffiliationEntity = createDataEntity(\n \"organizationaffiliation\",\n \"OrganizationAffiliation\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Organization data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Organization#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const OrganizationEntity = createDataEntity(\n \"organization\",\n \"Organization\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Patient data-store entity based on FHIR (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Patient#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) are documented in the Data-Store Entity Standards (FHIR).\n * Audit is in FHIR resource meta (meta.extension), not item attributes. See entity-standards.md.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entities/patient.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const PatientEntity = createDataEntity(\"patient\", \"Patient\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * PaymentNotice data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#PaymentNotice#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const PaymentNoticeEntity = createDataEntity(\n \"paymentnotice\",\n \"PaymentNotice\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * PaymentReconciliation data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#PaymentReconciliation#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const PaymentReconciliationEntity = createDataEntity(\n \"paymentreconciliation\",\n \"PaymentReconciliation\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Person data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Person#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const PersonEntity = createDataEntity(\"person\", \"Person\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * PlanDefinition data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#PlanDefinition#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const PlanDefinitionEntity = createDataEntity(\n \"plandefinition\",\n \"PlanDefinition\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Practitioner data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Practitioner#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with Patient, Encounter, and entity standards.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entities/practitioner.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const PractitionerEntity = createDataEntity(\n \"practitioner\",\n \"Practitioner\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * PractitionerRole data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#PractitionerRole#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const PractitionerRoleEntity = createDataEntity(\n \"practitionerrole\",\n \"PractitionerRole\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Procedure data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Procedure#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ProcedureEntity = createDataEntity(\"procedure\", \"Procedure\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Provenance data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Provenance#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ProvenanceEntity = createDataEntity(\"provenance\", \"Provenance\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Questionnaire data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Questionnaire#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const QuestionnaireEntity = createDataEntity(\n \"questionnaire\",\n \"Questionnaire\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * QuestionnaireResponse data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#QuestionnaireResponse#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const QuestionnaireResponseEntity = createDataEntity(\n \"questionnaireresponse\",\n \"QuestionnaireResponse\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * RelatedPerson data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#RelatedPerson#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const RelatedPersonEntity = createDataEntity(\n \"relatedperson\",\n \"RelatedPerson\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * RequestGroup data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#RequestGroup#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const RequestGroupEntity = createDataEntity(\n \"requestgroup\",\n \"RequestGroup\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ResearchDefinition data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ResearchDefinition#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ResearchDefinitionEntity = createDataEntity(\n \"researchdefinition\",\n \"ResearchDefinition\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ResearchElementDefinition data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ResearchElementDefinition#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ResearchElementDefinitionEntity = createDataEntity(\n \"researchelementdefinition\",\n \"ResearchElementDefinition\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ResearchStudy data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ResearchStudy#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ResearchStudyEntity = createDataEntity(\n \"researchstudy\",\n \"ResearchStudy\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ResearchSubject data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ResearchSubject#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ResearchSubjectEntity = createDataEntity(\n \"researchsubject\",\n \"ResearchSubject\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * RiskAssessment data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#RiskAssessment#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const RiskAssessmentEntity = createDataEntity(\n \"riskassessment\",\n \"RiskAssessment\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * RiskEvidenceSynthesis data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#RiskEvidenceSynthesis#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const RiskEvidenceSynthesisEntity = createDataEntity(\n \"riskevidencesynthesis\",\n \"RiskEvidenceSynthesis\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Schedule data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Schedule#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ScheduleEntity = createDataEntity(\"schedule\", \"Schedule\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * SearchParameter data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#SearchParameter#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const SearchParameterEntity = createDataEntity(\n \"searchparameter\",\n \"SearchParameter\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ServiceRequest data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ServiceRequest#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ServiceRequestEntity = createDataEntity(\n \"servicerequest\",\n \"ServiceRequest\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Slot data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Slot#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const SlotEntity = createDataEntity(\"slot\", \"Slot\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * SpecimenDefinition data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#SpecimenDefinition#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const SpecimenDefinitionEntity = createDataEntity(\n \"specimendefinition\",\n \"SpecimenDefinition\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Specimen data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Specimen#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const SpecimenEntity = createDataEntity(\"specimen\", \"Specimen\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * StructureDefinition data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#StructureDefinition#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const StructureDefinitionEntity = createDataEntity(\n \"structuredefinition\",\n \"StructureDefinition\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * StructureMap data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#StructureMap#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const StructureMapEntity = createDataEntity(\n \"structuremap\",\n \"StructureMap\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Subscription data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Subscription#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const SubscriptionEntity = createDataEntity(\n \"subscription\",\n \"Subscription\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Substance data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Substance#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const SubstanceEntity = createDataEntity(\"substance\", \"Substance\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * SubstanceNucleicAcid data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#SubstanceNucleicAcid#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const SubstanceNucleicAcidEntity = createDataEntity(\n \"substancenucleicacid\",\n \"SubstanceNucleicAcid\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * SubstancePolymer data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#SubstancePolymer#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const SubstancePolymerEntity = createDataEntity(\n \"substancepolymer\",\n \"SubstancePolymer\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * SubstanceProtein data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#SubstanceProtein#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const SubstanceProteinEntity = createDataEntity(\n \"substanceprotein\",\n \"SubstanceProtein\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * SubstanceReferenceInformation data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#SubstanceReferenceInformation#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const SubstanceReferenceInformationEntity = createDataEntity(\n \"substancereferenceinformation\",\n \"SubstanceReferenceInformation\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * SubstanceSourceMaterial data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#SubstanceSourceMaterial#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const SubstanceSourceMaterialEntity = createDataEntity(\n \"substancesourcematerial\",\n \"SubstanceSourceMaterial\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * SubstanceSpecification data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#SubstanceSpecification#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const SubstanceSpecificationEntity = createDataEntity(\n \"substancespecification\",\n \"SubstanceSpecification\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * SupplyDelivery data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#SupplyDelivery#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const SupplyDeliveryEntity = createDataEntity(\n \"supplydelivery\",\n \"SupplyDelivery\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * SupplyRequest data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#SupplyRequest#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const SupplyRequestEntity = createDataEntity(\n \"supplyrequest\",\n \"SupplyRequest\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * Task data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#Task#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const TaskEntity = createDataEntity(\"task\", \"Task\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * TerminologyCapabilities data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#TerminologyCapabilities#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const TerminologyCapabilitiesEntity = createDataEntity(\n \"terminologycapabilities\",\n \"TerminologyCapabilities\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * TestReport data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#TestReport#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const TestReportEntity = createDataEntity(\"testreport\", \"TestReport\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * TestScript data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#TestScript#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const TestScriptEntity = createDataEntity(\"testscript\", \"TestScript\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * ValueSet data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#ValueSet#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const ValueSetEntity = createDataEntity(\"valueset\", \"ValueSet\");\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * VerificationResult data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#VerificationResult#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const VerificationResultEntity = createDataEntity(\n \"verificationresult\",\n \"VerificationResult\",\n);\n","import { createDataEntity } from \"../data-entity-common\";\n\n/**\n * VisionPrescription data-store entity based on FHIR R4 (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, data plane. Strict isolation: tenantId and\n * workspaceId are required (no defaults, no sentinels). No crossover between tenants or workspaces.\n *\n * Key structure: PK = TID#<tenantId>#WID#<workspaceId>#RT#VisionPrescription#ID#<id>, SK = CURRENT.\n * Standard attributes (FHIR source and entity purpose) align with other data-plane domain resources.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Standard attributes (all FHIR domain resources)\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const VisionPrescriptionEntity = createDataEntity(\n \"visionprescription\",\n \"VisionPrescription\",\n);\n","import { extractSortKey, extractSummary } from \"@openhi/types\";\nimport type { Meta, FhirResourceLike } from \"@openhi/types\";\nimport { compressResource, decompressResource } from \"../../lib/compression\";\nimport { mergeAuditIntoMeta, type MetaWithExtensions } from \"../audit-meta\";\nimport { SHARD_COUNT } from \"../dynamo/shard\";\nimport { NotFoundError } from \"../errors\";\nimport type { OpenHiContext } from \"../openhi-context\";\n\n/**\n * Sort key for the current record version. Matches Dynamo record index SK default.\n * Use this in get/update/delete (and create where applicable) for data-plane entities.\n */\nexport const DATA_ENTITY_SK = \"CURRENT\" as const;\n\n/** Base params for data-entity operations: context and optional table override. */\nexport interface BaseDataEntityParams {\n context: OpenHiContext;\n /** Optional table name override; resolved by data service from DYNAMO_TABLE_NAME when omitted. */\n tableName?: string;\n}\n\n/** Params for get-by-id and delete (context + id + optional tableName). */\nexport interface GetByIdParams extends BaseDataEntityParams {\n id: string;\n}\n\n/**\n * Mode for list operations introduced by #853 to back FHIR `_summary` opt-ins.\n *\n * - `full` (default): GSI1 fan-out → BatchGet hydration → full FHIR resource per entry.\n * - `summary`: GSI1 fan-out only; the `summary` JSON projected onto GSI1 is parsed and used\n * as `resource` per entry. Skips BatchGet entirely — that's the cost win the FHIR spec\n * intends `_summary=true` to deliver.\n * - `count`: GSI1 fan-out only; entries are dropped, only `total` is populated. Routes\n * should pass `total` into `buildSearchsetBundle({ mode: \"count\", total })`.\n *\n * `_elements` is implemented at the route layer as `full` mode + post-hydration pruning,\n * since per-element pruning has to happen after decompression and is FHIR-spec-defined\n * (always retain root-level mandatories — see `prune-resource-by-elements.ts`).\n */\nexport type ListOperationMode = \"full\" | \"summary\" | \"count\";\n\n/** Params for list (context + optional tableName + optional mode for #853 `_summary`). */\nexport interface ListParams extends BaseDataEntityParams {\n /** Defaults to `\"full\"` (current behavior); routes pass other modes for `_summary` opt-ins. */\n mode?: ListOperationMode;\n}\n\n/** Result for create / get-by-id / update: single resource. */\nexport interface SingleResourceResult<T> {\n id: string;\n resource: T;\n}\n\n/** Entry shape for list results. */\nexport interface ListEntry<T> {\n id: string;\n resource: T;\n}\n\n/**\n * Result for list: entries array plus total count.\n *\n * - For `mode === \"full\"` and `mode === \"summary\"`, `total === entries.length`.\n * - For `mode === \"count\"`, `entries` is empty and `total` is the GSI1 fan-out count.\n *\n * Splitting `total` from `entries.length` lets count-mode routes report a true count\n * while skipping any per-entry work.\n */\nexport interface ListResult<T> {\n entries: Array<ListEntry<T>>;\n total: number;\n}\n\n/** Minimal entity shape for get (used by getDataEntityById). */\ninterface EntityWithGet {\n get(params: {\n tenantId: string;\n workspaceId: string;\n id: string;\n sk: string;\n }): { go(): Promise<{ data: { id: string; resource: string } | null }> };\n}\n\n/** Minimal entity shape for delete (used by deleteDataEntityById). */\ninterface EntityWithDelete {\n delete(params: {\n tenantId: string;\n workspaceId: string;\n id: string;\n sk: string;\n }): { go(): Promise<unknown> };\n}\n\n/**\n * Minimal entity shape for list via GSI1 + BatchGet hydration (used by listDataEntitiesByWorkspace).\n * GSI1 is sharded per ADR-011, so listing fans out to each shard and concatenates ids; the\n * `resource` attribute is NOT projected onto GSI1 (per the summary projection in\n * `dynamo-db-data-store.ts`), so the second phase BatchGets the base table for full items.\n *\n * GSI1 INCLUDE projection (per `dynamo-db-data-store.ts`) carries `summary`, `vid`, `lastUpdated`,\n * `createdDate`, `modifiedDate`, `createdById`, `modifiedById` alongside the key attributes.\n * `summary` is what `mode: \"summary\"` returns to the caller without hitting the base table.\n */\ninterface DataEntityWithListAndBatchGet {\n query: {\n gsi1(params: {\n tenantId: string;\n workspaceId: string;\n gsi1Shard: string;\n }): {\n go(): Promise<{\n data: Array<{\n id: string;\n summary?: string;\n vid?: string;\n lastUpdated?: string;\n createdDate?: string;\n modifiedDate?: string;\n createdById?: string;\n modifiedById?: string;\n }> | null;\n }>;\n };\n };\n get(\n keys: Array<{\n tenantId: string;\n workspaceId: string;\n id: string;\n sk: string;\n }>,\n ): {\n go(): Promise<{\n data: Array<{ id: string; resource: string }>;\n unprocessed: Array<{\n tenantId: string;\n workspaceId: string;\n id: string;\n sk: string;\n }>;\n }>;\n };\n}\n\n/** Minimal entity shape for put (used by createDataEntityRecord). */\ninterface EntityWithPut {\n put(attrs: {\n sk: string;\n tenantId: string;\n workspaceId: string;\n id: string;\n resource: string;\n summary: string;\n vid: string;\n lastUpdated: string;\n gsi1sk: string;\n }): { go(): Promise<unknown> };\n}\n\n/** Minimal entity shape for patch (used by updateDataEntityById). */\ninterface EntityWithPatch {\n get(params: {\n tenantId: string;\n workspaceId: string;\n id: string;\n sk: string;\n }): { go(): Promise<{ data: { id: string; resource: string } | null }> };\n patch(params: {\n tenantId: string;\n workspaceId: string;\n id: string;\n sk: string;\n }): {\n set(attrs: {\n resource: string;\n summary: string;\n lastUpdated: string;\n gsi1sk: string;\n }): {\n go(): Promise<unknown>;\n };\n };\n}\n\n/**\n * Get a single data-entity record by id. Decompresses and parses resource; throws NotFoundError if missing.\n * Use from get-by-id operations with the appropriate entity and resource type.\n */\nexport async function getDataEntityById<T>(\n entity: EntityWithGet,\n tenantId: string,\n workspaceId: string,\n id: string,\n resourceLabel: string,\n): Promise<SingleResourceResult<T>> {\n const result = await entity\n .get({\n tenantId,\n workspaceId,\n id,\n sk: DATA_ENTITY_SK,\n })\n .go();\n\n if (!result.data) {\n throw new NotFoundError(`${resourceLabel} ${id} not found`, {\n details: { id },\n });\n }\n\n const parsed = JSON.parse(decompressResource(result.data.resource)) as T & {\n id?: string;\n };\n return {\n id: result.data.id,\n resource: { ...parsed, id: result.data.id } as T,\n };\n}\n\n/**\n * Delete a data-entity record by id. Idempotent (no-op if not found).\n * Use from delete operations with the appropriate entity.\n */\nexport async function deleteDataEntityById(\n entity: EntityWithDelete,\n tenantId: string,\n workspaceId: string,\n id: string,\n): Promise<void> {\n await entity\n .delete({\n tenantId,\n workspaceId,\n id,\n sk: DATA_ENTITY_SK,\n })\n .go();\n}\n\n/** Maximum total attempts (initial + retries) when hydrating list ids via BatchGet. */\nconst BATCH_GET_MAX_ATTEMPTS = 3;\n/** Base backoff in milliseconds applied to BatchGet retries; doubles each attempt. */\nconst BATCH_GET_BASE_BACKOFF_MS = 50;\n\n/** Minimal entity shape for BatchGet hydration on the base table; chunks of 100 are handled by ElectroDB. */\ninterface EntityWithBatchGet<TKey, TItem> {\n get(keys: TKey[]): {\n go(): Promise<{ data: TItem[]; unprocessed: TKey[] }>;\n };\n}\n\n/**\n * BatchGet wrapper that retries `UnprocessedKeys` with exponential backoff. ElectroDB chunks the\n * input keys into groups of 100 internally, but does not retry unprocessed keys — DynamoDB can\n * return some keys unprocessed under throttling or partial failure, and the caller must reissue\n * them. Throws after `BATCH_GET_MAX_ATTEMPTS` if any keys remain unprocessed; intended for list\n * hydration (#854) where partial results would silently truncate the response.\n */\nexport async function batchGetWithRetry<TKey, TItem>(\n entity: EntityWithBatchGet<TKey, TItem>,\n keys: TKey[],\n): Promise<TItem[]> {\n if (keys.length === 0) return [];\n\n const collected: TItem[] = [];\n let pending = keys;\n let attempt = 0;\n\n while (pending.length > 0) {\n if (attempt > 0) {\n await new Promise((resolve) =>\n setTimeout(resolve, BATCH_GET_BASE_BACKOFF_MS * 2 ** (attempt - 1)),\n );\n }\n attempt++;\n const result = await entity.get(pending).go();\n collected.push(...result.data);\n const unprocessed = result.unprocessed ?? [];\n if (unprocessed.length === 0) break;\n if (attempt >= BATCH_GET_MAX_ATTEMPTS) {\n throw new Error(\n `BatchGet exhausted retries: ${unprocessed.length} key(s) still unprocessed after ${BATCH_GET_MAX_ATTEMPTS} attempt(s)`,\n );\n }\n pending = unprocessed;\n }\n\n return collected;\n}\n\n/** GSI1 index item shape — what a sharded `gsi1.query().go()` returns per row. */\nexport interface ShardedListIndexItem {\n id: string;\n summary?: string;\n}\n\n/** Hooks that adapt a generic mode-dispatching list to a specific entity's hydration and entry shape. */\nexport interface DispatchListModeHooks<TItem, TEntry> {\n /** Hydrate the base table for the given ids (typically `batchGetWithRetry(entity, keys)`). */\n hydrate: (orderedIds: string[]) => Promise<TItem[]>;\n /** Extract the canonical id from a hydrated item so it can be matched back to the GSI1 order. */\n getId: (item: TItem) => string;\n /** Build the result entry for `mode === \"full\"` from a hydrated base-table item. */\n buildEntry: (id: string, item: TItem) => TEntry;\n /** Build the result entry for `mode === \"summary\"` from the parsed GSI1 `summary` JSON. */\n buildSummaryEntry: (\n id: string,\n parsedSummary: Record<string, unknown>,\n ) => TEntry;\n}\n\n/**\n * Mode dispatcher shared by data-plane and control-plane list operations (#853).\n *\n * Given pre-fetched `shardResults` from a sharded GSI1 fan-out, returns either:\n * - `mode === \"count\"` — `{ entries: [], total }` where total is the sum of shard row counts.\n * - `mode === \"summary\"` — entries built from each shard row's parsed `summary` JSON; rows with\n * missing or unparseable `summary` are dropped.\n * - `mode === \"full\"` — calls `hydrate(orderedIds)` (typically a BatchGet) and builds entries\n * from hydrated items in per-shard GSI1 sort order; missing items are dropped.\n *\n * Lives here (alongside `listDataEntitiesByWorkspace`) because the same dispatch logic is needed\n * by every list operation that backs a FHIR list/search endpoint, including the seven\n * control-plane peers (User, Role, Tenant, Workspace, Membership, RoleAssignment, Configuration).\n */\nexport async function dispatchListMode<TItem, TEntry>(\n mode: ListOperationMode,\n shardResults: Array<{ data: Array<ShardedListIndexItem> | null }>,\n hooks: DispatchListModeHooks<TItem, TEntry>,\n): Promise<{ entries: TEntry[]; total: number }> {\n if (mode === \"count\") {\n let total = 0;\n for (const shardResult of shardResults) {\n total += (shardResult.data ?? []).length;\n }\n return { entries: [], total };\n }\n\n if (mode === \"summary\") {\n const entries: TEntry[] = [];\n for (const shardResult of shardResults) {\n for (const item of shardResult.data ?? []) {\n if (typeof item.summary !== \"string\") continue;\n let parsed: Record<string, unknown>;\n try {\n parsed = JSON.parse(item.summary) as Record<string, unknown>;\n } catch {\n continue;\n }\n entries.push(hooks.buildSummaryEntry(item.id, parsed));\n }\n }\n return { entries, total: entries.length };\n }\n\n const orderedIds: string[] = [];\n for (const shardResult of shardResults) {\n for (const item of shardResult.data ?? []) {\n orderedIds.push(item.id);\n }\n }\n\n if (orderedIds.length === 0) return { entries: [], total: 0 };\n\n const items = await hooks.hydrate(orderedIds);\n const byId = new Map(items.map((item) => [hooks.getId(item), item]));\n\n const entries: TEntry[] = [];\n for (const id of orderedIds) {\n const item = byId.get(id);\n if (!item) continue;\n entries.push(hooks.buildEntry(id, item));\n }\n\n return { entries, total: entries.length };\n}\n\n/**\n * List data-entity records in a workspace via GSI1.\n *\n * `mode` (default `\"full\"`) selects the read shape — see `dispatchListMode`. The data-plane\n * binding here adds the four-shard fan-out (per ADR-011) and the BatchGet hydration with\n * decompression for `mode === \"full\"`. K-way merge by `gsi1sk` is intentionally NOT done here\n * — full server-side natural sort lands with the FHIR list-endpoint plumbing that adds\n * pagination tokens.\n */\nexport async function listDataEntitiesByWorkspace<T>(\n entity: DataEntityWithListAndBatchGet,\n tenantId: string,\n workspaceId: string,\n mode: ListOperationMode = \"full\",\n): Promise<ListResult<T>> {\n const shardResults = await Promise.all(\n Array.from({ length: SHARD_COUNT }, (_, shard) =>\n entity.query\n .gsi1({ tenantId, workspaceId, gsi1Shard: String(shard) })\n .go(),\n ),\n );\n\n return dispatchListMode<{ id: string; resource: string }, ListEntry<T>>(\n mode,\n shardResults,\n {\n hydrate: (orderedIds) =>\n batchGetWithRetry(\n entity,\n orderedIds.map((id) => ({\n tenantId,\n workspaceId,\n id,\n sk: DATA_ENTITY_SK,\n })),\n ),\n getId: (item) => item.id,\n buildEntry: (id, item) => {\n const parsed = JSON.parse(decompressResource(item.resource)) as T & {\n id?: string;\n };\n return { id, resource: { ...parsed, id } as T };\n },\n buildSummaryEntry: (id, parsed) => ({\n id,\n resource: { ...parsed, id } as T,\n }),\n },\n );\n}\n\n/**\n * Create a data-entity record with put. Computes vid from lastUpdated (from resource meta or fallback).\n * Use from create operations (e.g. Practitioner, Encounter) that build the resource with audit in meta.\n */\nexport async function createDataEntityRecord<T>(\n entity: EntityWithPut,\n tenantId: string,\n workspaceId: string,\n id: string,\n resourceWithAudit: T & { meta?: { lastUpdated?: string } },\n fallbackDate: string,\n): Promise<SingleResourceResult<T>> {\n const lastUpdated =\n resourceWithAudit.meta?.lastUpdated ??\n fallbackDate ??\n new Date().toISOString();\n const vid =\n lastUpdated.replace(/[-:T.Z]/g, \"\").slice(0, 12) || Date.now().toString(36);\n\n const resourceLike = resourceWithAudit as unknown as FhirResourceLike;\n const summary = JSON.stringify(extractSummary(resourceLike));\n const gsi1sk = extractSortKey(resourceLike);\n\n await entity\n .put({\n sk: DATA_ENTITY_SK,\n tenantId,\n workspaceId,\n id,\n resource: compressResource(JSON.stringify(resourceWithAudit)),\n summary,\n vid,\n lastUpdated,\n gsi1sk,\n })\n .go();\n\n return {\n id,\n resource: resourceWithAudit as T,\n };\n}\n\n/**\n * Build an updated resource with audit in meta for use with updateDataEntityById.\n * Parses existing resource string for existing meta, merges body with id/resourceType/meta (versionId \"2\"),\n * then merges modified audit (modifiedDate, modifiedById, modifiedByName) into meta.\n * Use from update operations (Patient, Encounter, Practitioner) to avoid duplicating this logic.\n */\nexport function buildUpdatedResourceWithAudit<T extends { meta?: Meta }>(\n body: T,\n id: string,\n date: string,\n actorId: string,\n actorName: string,\n existingResourceStr: string,\n resourceType: string,\n): {\n resource: T & { id: string; meta: MetaWithExtensions };\n lastUpdated: string;\n} {\n const existingMeta: MetaWithExtensions | undefined = (\n JSON.parse(existingResourceStr) as { meta?: MetaWithExtensions }\n ).meta;\n\n const bodyWithMeta = body as T & { id?: string; meta?: Meta };\n const resourceWithVersion: T & { id: string; meta?: Meta } = {\n ...body,\n resourceType: resourceType as T[\"resourceType\"],\n id,\n meta: {\n ...(bodyWithMeta.meta ?? {}),\n lastUpdated: date,\n versionId: \"2\",\n },\n };\n\n const resourceWithAudit: T & { id: string; meta: MetaWithExtensions } = {\n ...resourceWithVersion,\n meta: mergeAuditIntoMeta(resourceWithVersion.meta ?? existingMeta, {\n modifiedDate: date,\n modifiedById: actorId,\n modifiedByName: actorName,\n }),\n };\n\n return {\n resource: resourceWithAudit,\n lastUpdated: date,\n };\n}\n\n/**\n * Update a data-entity record by id: get existing, throw if not found, then call builder with\n * decompressed existing resource string; builder returns { resource, lastUpdated }; then patch.\n * Use from update operations with the appropriate entity and resource type.\n */\nexport async function updateDataEntityById<T>(\n entity: EntityWithPatch,\n tenantId: string,\n workspaceId: string,\n id: string,\n resourceLabel: string,\n context: OpenHiContext,\n buildPatched: (existingResourceStr: string) => {\n resource: unknown;\n lastUpdated: string;\n },\n): Promise<SingleResourceResult<T>> {\n const existing = await entity\n .get({\n tenantId,\n workspaceId,\n id,\n sk: DATA_ENTITY_SK,\n })\n .go();\n\n if (!existing.data) {\n throw new NotFoundError(`${resourceLabel} ${id} not found`, {\n details: { id },\n });\n }\n\n const existingStr = decompressResource(existing.data.resource);\n const { resource, lastUpdated } = buildPatched(existingStr);\n\n const resourceLike = resource as FhirResourceLike;\n const summary = JSON.stringify(extractSummary(resourceLike));\n const gsi1sk = extractSortKey(resourceLike);\n\n await entity\n .patch({\n tenantId,\n workspaceId,\n id,\n sk: DATA_ENTITY_SK,\n })\n .set({\n resource: compressResource(JSON.stringify(resource)),\n summary,\n lastUpdated,\n gsi1sk,\n })\n .go();\n\n return {\n id,\n resource: resource as T,\n };\n}\n","import { gzipSync, gunzipSync } from \"node:zlib\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/lib/compression.md\n */\n\n/** Envelope format version. See ADR 2026-02-15-02 (data layer compression). */\nconst ENVELOPE_VERSION = 1;\n\n/**\n * Compression algorithm identifiers supported by the envelope (string values).\n * Only algos that Node.js supports out of the box (zlib): gzip, brotli, deflate.\n * \"none\" = uncompressed payload. zstd was considered in the ADR but requires native addon/WASM.\n */\nexport const COMPRESSION_ALGOS = {\n NONE: \"none\",\n GZIP: \"gzip\",\n BROTLI: \"brotli\",\n DEFLATE: \"deflate\",\n} as const;\n\n/** Algorithm value for envelope `algo`; only gzip and none are implemented today. */\nexport type CompressionAlgo =\n (typeof COMPRESSION_ALGOS)[keyof typeof COMPRESSION_ALGOS];\n\n/** Stored value is a JSON string of this envelope. */\ninterface CompressionEnvelope {\n v: number;\n algo: string;\n payload: string;\n}\n\nfunction isEnvelope(obj: unknown): obj is CompressionEnvelope {\n return (\n typeof obj === \"object\" &&\n obj !== null &&\n \"v\" in obj &&\n \"algo\" in obj &&\n \"payload\" in obj &&\n typeof (obj as CompressionEnvelope).payload === \"string\"\n );\n}\n\n/**\n * Compresses a JSON string (e.g. serialized FHIR resource) for storage in DynamoDB.\n * Uses a versioned envelope: { v, algo, payload } with gzip+base64 in payload.\n * Used by the data layer on write; see REST API docs (compression in data layer).\n * Optional compression: pass `{ algo: COMPRESSION_ALGOS.NONE }` to store in envelope without compressing.\n */\nexport function compressResource(\n jsonString: string,\n options?: { algo?: CompressionAlgo },\n): string {\n const algo = options?.algo ?? COMPRESSION_ALGOS.GZIP;\n if (algo === COMPRESSION_ALGOS.NONE) {\n const envelope: CompressionEnvelope = {\n v: ENVELOPE_VERSION,\n algo: COMPRESSION_ALGOS.NONE,\n payload: jsonString,\n };\n return JSON.stringify(envelope);\n }\n const buf = Buffer.from(jsonString, \"utf-8\");\n const payload = gzipSync(buf).toString(\"base64\");\n const envelope: CompressionEnvelope = {\n v: ENVELOPE_VERSION,\n algo: COMPRESSION_ALGOS.GZIP,\n payload,\n };\n return JSON.stringify(envelope);\n}\n\n/**\n * Decompresses a stored value: versioned envelope (v, algo, payload) or legacy gzip+base64 / raw.\n * If the value is not valid envelope JSON, falls back to legacy: try gzip magic on base64, else return as-is.\n */\nexport function decompressResource(compressedOrRaw: string): string {\n try {\n const parsed = JSON.parse(compressedOrRaw) as unknown;\n if (isEnvelope(parsed)) {\n if (parsed.algo === COMPRESSION_ALGOS.GZIP) {\n const buf = Buffer.from(parsed.payload, \"base64\");\n return gunzipSync(buf).toString(\"utf-8\");\n }\n if (parsed.algo === COMPRESSION_ALGOS.NONE) {\n return parsed.payload;\n }\n // Unknown algo: return payload as-is (safe fallback per ADR)\n return parsed.payload;\n }\n } catch {\n // Not valid envelope JSON — legacy path\n }\n\n // Legacy: pre-envelope gzip+base64 or raw\n try {\n const buf = Buffer.from(compressedOrRaw, \"base64\");\n if (buf.length >= 2 && buf[0] === 0x1f && buf[1] === 0x8b) {\n return gunzipSync(buf).toString(\"utf-8\");\n }\n } catch {\n // not base64 or gunzip failed\n }\n return compressedOrRaw;\n}\n","import { IUserPool } from \"aws-cdk-lib/aws-cognito\";\nimport { ITable } from \"aws-cdk-lib/aws-dynamodb\";\nimport { IEventBus } from \"aws-cdk-lib/aws-events\";\nimport { Construct } from \"constructs\";\nimport { SEED_DEMO_DATA_CONSUMER_NAME } from \"./events\";\nimport { SeedDemoDataLambda } from \"./seed-demo-data-lambda\";\nimport { WorkflowDedupTable } from \"../../../components/dynamodb/workflow-dedup-table\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-demo-data/seed-demo-data-workflow.md\n */\n\nexport interface SeedDemoDataWorkflowProps {\n /** Control event bus carrying `platform.system-data-seeded.v1`. */\n readonly controlEventBus: IEventBus;\n /** Data-store table the workflow upserts demo-data records into. */\n readonly dataStoreTable: ITable;\n /** Cognito User Pool the workflow provisions dev users into. */\n readonly userPool: IUserPool;\n}\n\n/**\n * Control-plane workflow that fires on every platform deploy and\n * idempotently re-asserts the demo-data graph: placeholder tenant +\n * workspace, 3 demo tenants + 4 workspaces, and per-dev-user Cognito\n * users with their DynamoDB User records, Memberships, and\n * RoleAssignments.\n *\n * Mounted on the data-service stack so the IAM grants against the\n * data-store table stay local. The control event bus and the workflow\n * dedup table reach in cross-stack via the SSM lookups\n * `OpenHiGlobalService.controlEventBusFromConstruct` and\n * `WorkflowDedupTable.grantConsumerFromLookup` respectively. The\n * Cognito User Pool similarly reaches in via\n * `OpenHiAuthService.userPoolFromConstruct`.\n *\n * Non-prod-only: the CDK stage-router (`OpenHiDataService`)\n * conditionally constructs this workflow only on non-prod stages.\n * The construct itself never checks the stage — its absence in prod\n * stacks is the gate.\n */\nexport class SeedDemoDataWorkflow extends Construct {\n public readonly seedDemoData: SeedDemoDataLambda;\n\n constructor(scope: Construct, props: SeedDemoDataWorkflowProps) {\n super(scope, \"seed-demo-data-workflow\");\n\n this.seedDemoData = new SeedDemoDataLambda(this, {\n controlEventBus: props.controlEventBus,\n dataStoreTable: props.dataStoreTable,\n userPool: props.userPool,\n });\n\n // Cross-stack grant resolves the dedup table's name + ARN via SSM\n // at synth time, so the data-service stack does not pick up a\n // CloudFormation export dependency on the global stack that owns\n // the dedup table.\n WorkflowDedupTable.grantConsumerFromLookup(\n this,\n this.seedDemoData.lambda,\n SEED_DEMO_DATA_CONSUMER_NAME,\n );\n }\n}\n","import {\n PlatformDeploymentCompletedV1,\n PlatformSystemDataSeededV1,\n} from \"@openhi/workflows\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-system-data/events.md\n */\n\n/**\n * Stable logical name this workflow registers with the shared\n * `WorkflowDedupTable` (TR-015). Used in both the construct grant\n * (`workflowDedupTable.grantConsumer(lambda, SEED_SYSTEM_DATA_CONSUMER_NAME)`)\n * and the handler's runtime `recordIfAbsent` call — keep them aligned by\n * importing this constant in both places.\n */\nexport const SEED_SYSTEM_DATA_CONSUMER_NAME = \"seed-system-data\";\n\n/**\n * Free-form `actor.system` value the handler stamps on the\n * `platform.system-data-seeded.v1` event it publishes when seeding\n * completes. Pinned here so the test can assert the wire value without\n * importing private handler internals.\n */\nexport const SEED_SYSTEM_DATA_ACTOR_SYSTEM = \"seed-system-data\";\n\n/**\n * Env var the Lambda construct injects with the control event bus\n * name. The handler reads it to build the publisher target when\n * emitting `platform.system-data-seeded.v1` after a successful seed.\n */\nexport const SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR = \"CONTROL_EVENT_BUS_NAME\";\n\n/**\n * The trigger this workflow subscribes to on the control event bus. The\n * `platform-deploy-bridge` workflow (#960) republishes terminal CloudFormation\n * stack-success events as `platform.deployment-completed.v1`; on every fire\n * this workflow re-asserts the platform-singleton control-plane records\n * (today: the three canonical Roles; future: additional system data).\n *\n * Re-exported from `@openhi/workflows` for symmetry with the handler's\n * import — keep the construct + handler reading from the same registry\n * entry so a rename upstream surfaces at both call sites.\n */\nexport { PlatformDeploymentCompletedV1, PlatformSystemDataSeededV1 };\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { PLATFORM_ROLE_IDS } from \"@openhi/types\";\nimport { Duration, Stack } from \"aws-cdk-lib\";\nimport { ITable } from \"aws-cdk-lib/aws-dynamodb\";\nimport { IEventBus, Rule } from \"aws-cdk-lib/aws-events\";\nimport { LambdaFunction } from \"aws-cdk-lib/aws-events-targets\";\nimport { Effect, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\nimport {\n PlatformDeploymentCompletedV1,\n SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR,\n} from \"./events\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-system-data/seed-system-data-lambda.md\n */\n\nconst HANDLER_NAME = \"seed-system-data.handler.js\";\n\n/**\n * Resolve the bundled handler entry. Same dual-path lookup the user-onboarding\n * Lambda uses: src/ for tests (the file lives next to this one) or lib/ for\n * the compiled bundle.\n */\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n\n return path.join(dirname, \"..\", \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n}\n\nexport interface SeedSystemDataLambdaProps {\n /**\n * Data-store table the workflow upserts platform-singleton control-plane\n * records into. Wired via `DYNAMO_TABLE_NAME` env var; granted scoped\n * write permission to the role records' partition keys only.\n */\n readonly dataStoreTable: ITable;\n\n /**\n * Control event bus that re-publishes\n * `platform.deployment-completed.v1` from the platform-deploy bridge.\n * The Rule mounts here.\n */\n readonly controlEventBus: IEventBus;\n}\n\n/**\n * Lambda + EventBridge Rule pair for the seed-system-data workflow. Owns\n * the routing (`source` / `detail-type` pattern) and the scoped data-store\n * grants — co-locating routing + permissions with the function they\n * target. Wiring to the workflow dedup table is the parent construct's\n * job (it has the singleton reference) and happens via\n * `WorkflowDedupTable.grantConsumer`.\n */\nexport class SeedSystemDataLambda extends Construct {\n public readonly lambda: NodejsFunction;\n public readonly rule: Rule;\n\n constructor(scope: Construct, props: SeedSystemDataLambdaProps) {\n super(scope, \"seed-system-data-lambda\");\n\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 512,\n timeout: Duration.minutes(1),\n environment: {\n DYNAMO_TABLE_NAME: props.dataStoreTable.tableName,\n [SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR]:\n props.controlEventBus.eventBusName,\n },\n });\n\n // Least-privilege grant: only the three known role PKs are\n // writable. A regression that tried to write a non-Role record\n // (or a Role with an unrecognized id) would be rejected by IAM,\n // not by application code. Updating the role-id set means\n // amending the generator's `ID_PREFIX_BY_VALUE_SET_URL` table\n // and regenerating — the role-id values flow through.\n //\n // The leading-keys values must match what ElectroDB *actually\n // writes*, not the entity definition's pretty template. The\n // base-table PK template `ROLE#ID#${id}` has no\n // `casing: \"none\"`, so ElectroDB applies its default casing\n // (lowercase) at runtime and the on-the-wire PK is\n // `role#id#<id>`. Authoring the policy in the uppercase\n // template form silently produces an IAM denial on every\n // PutItem the seeder attempts.\n const roleArns = Object.values(PLATFORM_ROLE_IDS).map(\n (id) => `role#id#${id}`,\n );\n this.lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\"dynamodb:PutItem\", \"dynamodb:UpdateItem\"],\n resources: [props.dataStoreTable.tableArn],\n conditions: {\n \"ForAllValues:StringEquals\": {\n \"dynamodb:LeadingKeys\": roleArns,\n },\n },\n }),\n );\n\n // Allow the handler to publish `platform.system-data-seeded.v1`\n // onto the control event bus after a successful seed. Downstream\n // consumers (`seed-demo-data`) subscribe to that event instead\n // of the raw deploy-completion event so the dependency is\n // enforced by a happens-before edge rather than by EventBridge\n // retry timing.\n props.controlEventBus.grantPutEventsTo(this.lambda);\n\n // Gate the rule on the host (data) stack's own completion event.\n // The data-store table is created by this stack, so seeding only\n // becomes safe once this stack reaches CREATE/UPDATE_COMPLETE.\n // Without this filter, sibling stacks (global, auth, rest-api,\n // graphql) completing before the data stack would trigger the\n // seeder against a not-yet-created table.\n //\n // The filter targets `detail.payload.stackName`: the outer\n // `detail` is EventBridge's envelope (which holds the whole\n // workflow envelope), and `payload` is the per-workflow payload\n // field on `WorkflowEvent` — i.e. the projection the bridge\n // produced from the CloudFormation Stack Status Change event.\n const hostStackName = Stack.of(this).stackName;\n\n this.rule = new Rule(this, \"rule\", {\n eventBus: props.controlEventBus,\n eventPattern: {\n source: [PlatformDeploymentCompletedV1.source],\n detailType: [PlatformDeploymentCompletedV1.detailType],\n detail: {\n payload: {\n stackName: [hostStackName],\n },\n },\n },\n targets: [\n new LambdaFunction(this.lambda, {\n retryAttempts: 2,\n maxEventAge: Duration.hours(2),\n }),\n ],\n });\n }\n}\n","import { ITable } from \"aws-cdk-lib/aws-dynamodb\";\nimport { IEventBus } from \"aws-cdk-lib/aws-events\";\nimport { Construct } from \"constructs\";\nimport { SEED_SYSTEM_DATA_CONSUMER_NAME } from \"./events\";\nimport { SeedSystemDataLambda } from \"./seed-system-data-lambda\";\nimport { WorkflowDedupTable } from \"../../../components/dynamodb/workflow-dedup-table\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-system-data/seed-system-data-workflow.md\n */\n\nexport interface SeedSystemDataWorkflowProps {\n /** Control event bus carrying `platform.deployment-completed.v1`. */\n readonly controlEventBus: IEventBus;\n /** Data-store table the workflow upserts platform-singleton records into. */\n readonly dataStoreTable: ITable;\n}\n\n/**\n * Control-plane workflow that fires on every platform deploy and\n * idempotently re-asserts the platform-singleton control-plane records\n * (today: the three canonical Roles; future: additional system data\n * slotted in as sibling steps under the same dedup record).\n *\n * Mounted on the data-service stack so the IAM grants against the\n * data-store table stay local. The control event bus and the\n * workflow dedup table reach in cross-stack via the SSM lookups\n * `OpenHiGlobalService.controlEventBusFromConstruct` and\n * `WorkflowDedupTable.grantConsumerFromLookup` respectively.\n */\nexport class SeedSystemDataWorkflow extends Construct {\n public readonly seedSystemData: SeedSystemDataLambda;\n\n constructor(scope: Construct, props: SeedSystemDataWorkflowProps) {\n super(scope, \"seed-system-data-workflow\");\n\n this.seedSystemData = new SeedSystemDataLambda(this, {\n controlEventBus: props.controlEventBus,\n dataStoreTable: props.dataStoreTable,\n });\n\n // Cross-stack grant resolves the dedup table's name + ARN via SSM\n // at synth time, so the data-service stack does not pick up a\n // CloudFormation export dependency on the global stack that owns\n // the dedup table.\n WorkflowDedupTable.grantConsumerFromLookup(\n this,\n this.seedSystemData.lambda,\n SEED_SYSTEM_DATA_CONSUMER_NAME,\n );\n }\n}\n","import type { PostConfirmationTriggerEvent } from \"aws-lambda\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/user-onboarding/events.md\n */\n\n// EventBridge routing values shared by the publisher and workflow rule.\nexport const USER_ONBOARDING_EVENT_SOURCE = \"openhi.control.user-onboarding\";\nexport const PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE =\n \"ProvisionDefaultWorkspaceRequested\";\n\n// Minimal workflow payload needed to provision and diagnose onboarding.\nexport interface ProvisionDefaultWorkspaceRequestedDetail {\n readonly cognitoSub: string;\n readonly userId?: string;\n readonly email?: string;\n readonly displayName?: string;\n readonly trigger: {\n readonly source: \"cognito.post-confirmation\";\n readonly triggerSource?: string;\n readonly userPoolId?: string;\n readonly userName?: string;\n readonly clientId?: string;\n };\n}\n\n// Convert Cognito's Post Confirmation trigger event into the workflow event.\nexport const buildProvisionDefaultWorkspaceRequestedDetail = (\n event: PostConfirmationTriggerEvent,\n): ProvisionDefaultWorkspaceRequestedDetail | undefined => {\n // Cognito sub is the required stable identity key for onboarding records.\n const attrs = event.request?.userAttributes ?? {};\n const cognitoSub = attrs.sub?.trim();\n if (!cognitoSub) {\n return undefined;\n }\n\n // Prefer email for display where available; fall back to Cognito metadata.\n const email = attrs.email?.trim();\n const displayName = email || event.userName || cognitoSub;\n\n // Include trigger metadata so failed or duplicate events can be traced.\n return {\n cognitoSub,\n ...(email ? { email } : {}),\n displayName,\n trigger: {\n source: \"cognito.post-confirmation\",\n triggerSource: event.triggerSource,\n userPoolId: event.userPoolId,\n userName: event.userName,\n clientId: event.callerContext?.clientId,\n },\n };\n};\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Duration } from \"aws-cdk-lib\";\nimport { ITable } from \"aws-cdk-lib/aws-dynamodb\";\nimport { IEventBus, Rule } from \"aws-cdk-lib/aws-events\";\nimport { LambdaFunction } from \"aws-cdk-lib/aws-events-targets\";\nimport { Effect, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\nimport {\n PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE,\n USER_ONBOARDING_EVENT_SOURCE,\n} from \"./events\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/user-onboarding/provision-default-workspace-lambda.md\n */\n\nconst HANDLER_NAME = \"provision-default-workspace.handler.js\";\n\n/**\n * Resolve Lambda entry so it works when running from src/ (tests) or from lib/ (built).\n */\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n\n return path.join(dirname, \"..\", \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n}\n\nexport interface ProvisionDefaultWorkspaceLambdaProps {\n /**\n * DynamoDB data store table. Used for the Lambda's `DYNAMO_TABLE_NAME`\n * env var and for granting the Lambda the writes + GSI queries it needs\n * to provision default control-plane resources.\n */\n readonly dataStoreTable: ITable;\n\n /**\n * Control-plane event bus that the EventBridge Rule listens on.\n */\n readonly controlEventBus: IEventBus;\n}\n\n/**\n * Lambda used by the user-onboarding workflow to create a user's default\n * Tenant, Workspace, Memberships, and RoleAssignment.\n *\n * Owns the EventBridge Rule that routes the default-workspace onboarding\n * event to itself, and the IAM permissions it needs on the data store\n * table — colocating routing + permissions with the function they target.\n */\nexport class ProvisionDefaultWorkspaceLambda extends Construct {\n public readonly lambda: NodejsFunction;\n public readonly rule: Rule;\n\n constructor(scope: Construct, props: ProvisionDefaultWorkspaceLambdaProps) {\n super(scope, \"provision-default-workspace-lambda\");\n\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 1024,\n environment: {\n DYNAMO_TABLE_NAME: props.dataStoreTable.tableName,\n },\n });\n\n // Grant table writes for default resources and User repair.\n props.dataStoreTable.grant(\n this.lambda,\n \"dynamodb:PutItem\",\n \"dynamodb:UpdateItem\",\n );\n\n // Table.grant(\"dynamodb:Query\") only covers the table itself, not its\n // GSIs, so an explicit policy is required to query GSI2 by cognitoSub.\n this.lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\"dynamodb:Query\"],\n resources: [`${props.dataStoreTable.tableArn}/index/*`],\n }),\n );\n\n // Route only the default-workspace onboarding event to this worker.\n this.rule = new Rule(this, \"rule\", {\n eventBus: props.controlEventBus,\n eventPattern: {\n source: [USER_ONBOARDING_EVENT_SOURCE],\n detailType: [PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE],\n },\n targets: [\n new LambdaFunction(this.lambda, {\n retryAttempts: 2,\n maxEventAge: Duration.hours(2),\n }),\n ],\n });\n }\n}\n","import { ITable } from \"aws-cdk-lib/aws-dynamodb\";\nimport { IEventBus } from \"aws-cdk-lib/aws-events\";\nimport { Construct } from \"constructs\";\nimport { ProvisionDefaultWorkspaceLambda } from \"./provision-default-workspace-lambda\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/user-onboarding/user-onboarding-workflow.md\n */\n\nexport interface UserOnboardingWorkflowProps {\n readonly controlEventBus: IEventBus;\n readonly dataStoreTable: ITable;\n}\n\n/**\n * Control-plane workflow for onboarding users after Cognito confirmation.\n */\nexport class UserOnboardingWorkflow extends Construct {\n public readonly provisionDefaultWorkspace: ProvisionDefaultWorkspaceLambda;\n\n constructor(scope: Construct, props: UserOnboardingWorkflowProps) {\n super(scope, \"user-onboarding-workflow\");\n\n this.provisionDefaultWorkspace = new ProvisionDefaultWorkspaceLambda(this, {\n dataStoreTable: props.dataStoreTable,\n controlEventBus: props.controlEventBus,\n });\n }\n}\n","import { OPEN_HI_STAGE } from \"@openhi/config\";\nimport {\n CorsHttpMethod,\n DomainName,\n HttpApi,\n HttpMethod,\n HttpNoneAuthorizer,\n HttpRoute,\n HttpRouteKey,\n IHttpApi,\n} from \"aws-cdk-lib/aws-apigatewayv2\";\nimport { HttpUserPoolAuthorizer } from \"aws-cdk-lib/aws-apigatewayv2-authorizers\";\nimport { HttpLambdaIntegration } from \"aws-cdk-lib/aws-apigatewayv2-integrations\";\nimport { ICertificate } from \"aws-cdk-lib/aws-certificatemanager\";\nimport { Effect, PolicyStatement } from \"aws-cdk-lib/aws-iam\";\nimport {\n ARecord,\n HostedZone,\n IHostedZone,\n RecordTarget,\n} from \"aws-cdk-lib/aws-route53\";\nimport { ApiGatewayv2DomainProperties } from \"aws-cdk-lib/aws-route53-targets\";\nimport { Duration } from \"aws-cdk-lib/core\";\nimport { Construct } from \"constructs\";\nimport { OpenHiAuthService } from \"./open-hi-auth-service\";\nimport { OpenHiDataService } from \"./open-hi-data-service\";\nimport { OpenHiGlobalService } from \"./open-hi-global-service\";\nimport { OpenHiEnvironment } from \"../app/open-hi-environment\";\nimport {\n OpenHiService,\n OpenHiServiceProps,\n OpenHiServiceType,\n} from \"../app/open-hi-service\";\nimport {\n RootHttpApi,\n RootHttpApiProps,\n} from \"../components/api-gateway/root-http-api\";\nimport {\n DataStorePostgresReplica,\n getPostgresReplicaSchemaName,\n} from \"../components/postgres/data-store-postgres-replica\";\nimport { DiscoverableStringParameter } from \"../components/ssm\";\nimport { CorsOptionsLambda } from \"../data/lambda/cors-options-lambda\";\nimport { RestApiLambda } from \"../data/lambda/rest-api-lambda\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/services/open-hi-rest-api-service.md\n */\n\nexport interface OpenHiRestApiServiceProps extends OpenHiServiceProps {\n /**\n * Optional props passed through to the RootHttpApi (API Gateway HTTP API) construct.\n * Use corsPreflight (CDK CorsPreflightOptions) for CORS; other HttpApiProps (e.g. description, disableExecuteApiEndpoint) apply as well.\n */\n readonly rootHttpApiProps?: RootHttpApiProps;\n}\n\n/**\n * SSM parameter name suffix for the REST API base URL.\n * Full parameter name is built via buildParameterName with serviceType REST_API.\n */\nexport const REST_API_BASE_URL_SSM_NAME = \"REST_API_BASE_URL\";\n\n/**\n * REST API service stack: HTTP API, custom domain, and Lambda; exports base URL via SSM.\n * Resources are created in protected methods; subclasses may override to customize.\n */\nexport class OpenHiRestApiService extends OpenHiService {\n static readonly SERVICE_TYPE =\n \"rest-api\" as const satisfies OpenHiServiceType;\n\n /**\n * Returns an IHttpApi by looking up the REST API stack's HTTP API ID from SSM.\n */\n static rootHttpApiFromConstruct(scope: Construct): IHttpApi {\n const httpApiId = DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: RootHttpApi.SSM_PARAM_NAME,\n serviceType: OpenHiRestApiService.SERVICE_TYPE,\n });\n return HttpApi.fromHttpApiAttributes(scope, \"http-api\", { httpApiId });\n }\n\n /**\n * Returns the REST API base URL (e.g. https://api.example.com) by looking it up from SSM.\n * Use in other stacks for E2E, scripts, or config.\n */\n static restApiBaseUrlFromConstruct(scope: Construct): string {\n return DiscoverableStringParameter.valueForLookupName(scope, {\n ssmParamName: REST_API_BASE_URL_SSM_NAME,\n serviceType: OpenHiRestApiService.SERVICE_TYPE,\n });\n }\n\n get serviceType(): string {\n return OpenHiRestApiService.SERVICE_TYPE;\n }\n\n /** Override so this.props is typed with this service's options (e.g. rootHttpApiProps). */\n public override props: OpenHiRestApiServiceProps;\n\n public readonly rootHttpApi: RootHttpApi;\n\n constructor(ohEnv: OpenHiEnvironment, props: OpenHiRestApiServiceProps = {}) {\n super(ohEnv, OpenHiRestApiService.SERVICE_TYPE, props);\n this.props = props;\n\n this.validateConfig(props);\n\n const hostedZone = this.createHostedZone();\n const certificate = this.createCertificate();\n const apiDomainName = this.createApiDomainNameString(hostedZone);\n this.createRestApiBaseUrlParameter(apiDomainName);\n const domainName = this.createDomainName(hostedZone, certificate);\n this.rootHttpApi = this.createRootHttpApi(domainName);\n this.createRestApiLambdaAndRoutes(hostedZone, domainName);\n }\n\n /**\n * Validates that config required for the REST API stack is present.\n */\n protected validateConfig(props: OpenHiRestApiServiceProps): void {\n const { config } = props;\n if (!config) {\n throw new Error(\"Config is required\");\n }\n if (!config.hostedZoneId) {\n throw new Error(\"Hosted zone ID is required\");\n }\n if (!config.zoneName) {\n throw new Error(\"Zone name is required\");\n }\n }\n\n /**\n * Creates the hosted zone reference (imported from config).\n * Override to customize.\n */\n protected createHostedZone(): IHostedZone {\n const { config } = this.props;\n return HostedZone.fromHostedZoneAttributes(this, \"root-zone\", {\n hostedZoneId: config!.hostedZoneId!,\n zoneName: config!.zoneName!,\n });\n }\n\n /**\n * Creates the wildcard certificate (imported from Global stack via SSM).\n * Override to customize.\n */\n protected createCertificate() {\n return OpenHiGlobalService.rootWildcardCertificateFromConstruct(this);\n }\n\n /**\n * Returns the API domain name string (e.g. api.example.com or api-{prefix}.example.com).\n * Override to customize.\n */\n protected createApiDomainNameString(hostedZone: IHostedZone): string {\n const apiPrefix =\n this.branchName === \"main\" ? `api` : `api-${this.childZonePrefix}`;\n return [apiPrefix, hostedZone.zoneName].join(\".\");\n }\n\n /**\n * Creates the SSM parameter for the REST API base URL.\n * Look up via {@link OpenHiRestApiService.restApiBaseUrlFromConstruct}.\n * Override to customize.\n */\n protected createRestApiBaseUrlParameter(apiDomainName: string): void {\n const restApiBaseUrl = `https://${apiDomainName}`;\n new DiscoverableStringParameter(this, \"rest-api-base-url-param\", {\n ssmParamName: REST_API_BASE_URL_SSM_NAME,\n stringValue: restApiBaseUrl,\n description: \"REST API base URL for this deployment (E2E, scripts)\",\n });\n }\n\n /**\n * Creates the API Gateway custom domain name resource.\n * Override to customize.\n */\n protected createDomainName(\n _hostedZone: IHostedZone,\n certificate: ICertificate,\n ): DomainName {\n const apiDomainName = this.createApiDomainNameString(_hostedZone);\n return new DomainName(this, \"domain\", {\n domainName: apiDomainName,\n certificate,\n });\n }\n\n /**\n * Creates the Lambda integration, HTTP routes, and API DNS record.\n * Override to customize. Uses {@link rootHttpApi} set by the constructor.\n */\n protected createRestApiLambdaAndRoutes(\n hostedZone: IHostedZone,\n domainName: DomainName,\n ): void {\n const dataStoreTable =\n OpenHiDataService.dynamoDbDataStoreFromConstruct(this);\n\n // Phase 2 of ADR 2026-04-17-01: REST API Lambda queries Postgres via the\n // RDS Data API. Cluster ARN, secret ARN, and database name come from SSM\n // (cross-stack discovery); the per-branch schema name is deterministic\n // from `branchHash` and computed locally to avoid an extra SSM lookup.\n const postgresClusterArn =\n DataStorePostgresReplica.clusterArnFromConstruct(this);\n const postgresSecretArn =\n DataStorePostgresReplica.secretArnFromConstruct(this);\n const postgresDatabase =\n DataStorePostgresReplica.databaseNameFromConstruct(this);\n const postgresSchema = getPostgresReplicaSchemaName(this.branchHash);\n\n const { lambda } = new RestApiLambda(this, {\n dynamoTableName: dataStoreTable.tableName,\n branchTagValue: this.branchName,\n httpApiTagValue: RootHttpApi.SSM_PARAM_NAME,\n postgresClusterArn,\n postgresSecretArn,\n postgresDatabase,\n postgresSchema,\n });\n\n // Allow the Lambda to issue Data API statements against this cluster and\n // read the cluster credentials secret. These are scoped to the specific\n // ARNs published by the data stack — no wildcards.\n lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\n \"rds-data:ExecuteStatement\",\n \"rds-data:BatchExecuteStatement\",\n ],\n resources: [postgresClusterArn],\n }),\n );\n lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\"secretsmanager:GetSecretValue\"],\n resources: [postgresSecretArn],\n }),\n );\n const dynamoActions = [\n \"dynamodb:GetItem\",\n \"dynamodb:Query\",\n \"dynamodb:BatchGetItem\",\n \"dynamodb:ConditionCheckItem\",\n \"dynamodb:DescribeTable\",\n \"dynamodb:BatchWriteItem\",\n \"dynamodb:PutItem\",\n \"dynamodb:UpdateItem\",\n \"dynamodb:DeleteItem\",\n ] as const;\n dataStoreTable.grant(lambda, ...dynamoActions);\n // Query (and other operations) on GSIs require the index resource ARN\n lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [...dynamoActions],\n resources: [`${dataStoreTable.tableArn}/index/*`],\n }),\n );\n // Temporary: broad SSM read for dynamic config (test only)\n lambda.addToRolePolicy(\n new PolicyStatement({\n effect: Effect.ALLOW,\n actions: [\n \"ssm:GetParameter\",\n \"ssm:GetParameters\",\n \"ssm:DescribeParameters\",\n ],\n resources: [\"*\"],\n }),\n );\n const integration = new HttpLambdaIntegration(\"lambda-integration\", lambda);\n const { lambda: optionsLambda } = new CorsOptionsLambda(this);\n const optionsIntegration = new HttpLambdaIntegration(\n \"options-integration\",\n optionsLambda,\n );\n const noAuth = new HttpNoneAuthorizer();\n // OPTIONS routes use dedicated low-memory Lambda so main REST API Lambda is not invoked for preflight (#694).\n new HttpRoute(this, \"options-route-root\", {\n httpApi: this.rootHttpApi,\n routeKey: HttpRouteKey.with(\"/\", HttpMethod.OPTIONS),\n integration: optionsIntegration,\n authorizer: noAuth,\n });\n new HttpRoute(this, \"options-route-proxy\", {\n httpApi: this.rootHttpApi,\n routeKey: HttpRouteKey.with(\"/{proxy+}\", HttpMethod.OPTIONS),\n integration: optionsIntegration,\n authorizer: noAuth,\n });\n new HttpRoute(this, \"proxy-route-root\", {\n httpApi: this.rootHttpApi,\n routeKey: HttpRouteKey.with(\"/\", HttpMethod.ANY),\n integration,\n });\n new HttpRoute(this, \"proxy-route\", {\n httpApi: this.rootHttpApi,\n routeKey: HttpRouteKey.with(\"/{proxy+}\", HttpMethod.ANY),\n integration,\n });\n const apiPrefix =\n this.branchName === \"main\" ? `api` : `api-${this.childZonePrefix}`;\n new ARecord(this, \"api-a-record\", {\n zone: hostedZone,\n recordName: apiPrefix,\n target: RecordTarget.fromAlias(\n new ApiGatewayv2DomainProperties(\n domainName.regionalDomainName,\n domainName.regionalHostedZoneId,\n ),\n ),\n });\n }\n\n /**\n * Creates the Root HTTP API with default domain mapping, Cognito JWT authorizer, and exports API ID to SSM.\n * Look up via {@link OpenHiRestApiService.rootHttpApiFromConstruct}.\n * Override to customize.\n */\n protected createRootHttpApi(domainName: DomainName): RootHttpApi {\n const userPool = OpenHiAuthService.userPoolFromConstruct(this);\n const userPoolClient = OpenHiAuthService.userPoolClientFromConstruct(this);\n // In non-prod, also accept tokens issued by the dedicated\n // fixture-seeder client (provisioned in the auth stack only when\n // stage !== prod). Adding it to the authorizer's accepted-clients\n // list lets the seed-fixtures CLI authenticate against the same\n // API the SPA uses, without weakening prod which never sees this\n // client at all.\n const userPoolClients = [userPoolClient];\n if (this.ohEnv.ohStage.stageType !== OPEN_HI_STAGE.PROD) {\n userPoolClients.push(\n OpenHiAuthService.fixtureSeederClientFromConstruct(this),\n );\n }\n const cognitoAuthorizer = new HttpUserPoolAuthorizer(\n \"cognito-authorizer\",\n userPool,\n { userPoolClients },\n );\n const { corsPreflight: cors, ...restRootHttpApiProps } =\n this.props.rootHttpApiProps ?? {};\n const corsPreflight =\n cors !== undefined\n ? {\n allowOrigins: cors.allowOrigins,\n allowMethods: cors.allowMethods ?? [\n CorsHttpMethod.GET,\n CorsHttpMethod.HEAD,\n CorsHttpMethod.POST,\n CorsHttpMethod.PUT,\n CorsHttpMethod.PATCH,\n CorsHttpMethod.DELETE,\n CorsHttpMethod.OPTIONS,\n ],\n allowHeaders: cors.allowHeaders ?? [\n \"Content-Type\",\n \"Authorization\",\n ],\n allowCredentials: cors.allowCredentials ?? true,\n maxAge: cors.maxAge ?? Duration.days(1),\n ...(cors.exposeHeaders !== undefined && {\n exposeHeaders: cors.exposeHeaders,\n }),\n }\n : undefined;\n const rootHttpApi = new RootHttpApi(this, {\n ...restRootHttpApiProps,\n ...(corsPreflight !== undefined && { corsPreflight }),\n defaultDomainMapping: {\n domainName,\n mappingKey: undefined,\n },\n defaultAuthorizer: cognitoAuthorizer,\n });\n new DiscoverableStringParameter(this, \"http-api-url-param\", {\n ssmParamName: RootHttpApi.SSM_PARAM_NAME,\n stringValue: rootHttpApi.httpApiId,\n description:\n \"API Gateway HTTP API ID for this REST API stack (cross-stack reference)\",\n });\n return rootHttpApi;\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\n\n/**\n * Dedicated Lambda for CORS preflight (OPTIONS) requests. Returns 204 so API Gateway\n * can add CORS headers from the API's corsPreflight config. Low memory footprint.\n * @see #694\n */\n\nconst HANDLER_NAME = \"cors-options-lambda.handler.js\";\n\n/**\n * Resolve Lambda entry so it works when running from src/ (tests) or from lib/ (built).\n */\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n\n const fromLib = path.join(dirname, \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n return fromLib;\n}\n\nexport class CorsOptionsLambda extends Construct {\n public readonly lambda: NodejsFunction;\n\n constructor(scope: Construct, id: string = \"cors-options-lambda\") {\n super(scope, id);\n\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 128,\n });\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { Runtime } from \"aws-cdk-lib/aws-lambda\";\nimport { NodejsFunction } from \"aws-cdk-lib/aws-lambda-nodejs\";\nimport { Construct } from \"constructs\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/data/lambda/rest-api-lambda.md\n */\n\nconst HANDLER_NAME = \"rest-api-lambda.handler.js\";\n\n/**\n * Resolve Lambda entry so it works when running from src/ (tests) or from lib/ (built).\n */\nfunction resolveHandlerEntry(dirname: string): string {\n const sameDir = path.join(dirname, HANDLER_NAME);\n if (fs.existsSync(sameDir)) {\n return sameDir;\n }\n\n const fromLib = path.join(dirname, \"..\", \"..\", \"..\", \"lib\", HANDLER_NAME);\n return fromLib;\n}\n\nexport interface RestApiLambdaProps {\n /**\n * DynamoDB table name for the data store. The Lambda receives it as the\n * environment variable DYNAMO_TABLE_NAME at runtime.\n */\n readonly dynamoTableName: string;\n\n /**\n * Branch name from the service. Passed as BRANCH_TAG_VALUE at runtime.\n */\n readonly branchTagValue: string;\n\n /**\n * SSM parameter name for the HTTP API (e.g. RootHttpApi.SSM_PARAM_NAME).\n * Passed as HTTP_API_TAG_VALUE at runtime.\n */\n readonly httpApiTagValue: string;\n\n /**\n * Aurora cluster ARN published by `DataStorePostgresReplica`. Passed as\n * `OPENHI_PG_CLUSTER_ARN` at runtime so the Lambda can target the cluster\n * via the RDS Data API (ADR 2026-04-17-01, phase 2).\n */\n readonly postgresClusterArn: string;\n\n /**\n * Secrets Manager ARN with the cluster credentials. Passed as\n * `OPENHI_PG_SECRET_ARN` at runtime; consumed by the RDS Data API client.\n */\n readonly postgresSecretArn: string;\n\n /**\n * Database name on the cluster. Passed as `OPENHI_PG_DATABASE` at runtime.\n */\n readonly postgresDatabase: string;\n\n /**\n * Per-branch schema name on the cluster (e.g. `b_<branchHash>`). Passed as\n * `OPENHI_PG_SCHEMA` at runtime.\n */\n readonly postgresSchema: string;\n}\n\nexport class RestApiLambda extends Construct {\n public readonly lambda: NodejsFunction;\n\n constructor(scope: Construct, props: RestApiLambdaProps) {\n super(scope, \"rest-api-lambda\");\n\n /**\n * Create a Lambda function\n */\n this.lambda = new NodejsFunction(this, \"handler\", {\n entry: resolveHandlerEntry(__dirname),\n runtime: Runtime.NODEJS_LATEST,\n memorySize: 1024,\n environment: {\n DYNAMO_TABLE_NAME: props.dynamoTableName,\n BRANCH_TAG_VALUE: props.branchTagValue,\n HTTP_API_TAG_VALUE: props.httpApiTagValue,\n OPENHI_PG_CLUSTER_ARN: props.postgresClusterArn,\n OPENHI_PG_SECRET_ARN: props.postgresSecretArn,\n OPENHI_PG_DATABASE: props.postgresDatabase,\n OPENHI_PG_SCHEMA: props.postgresSchema,\n },\n bundling: {\n minify: true,\n sourceMap: false,\n },\n });\n }\n}\n","import {\n AuthorizationType,\n IGraphqlApi,\n UserPoolDefaultAction,\n} from \"aws-cdk-lib/aws-appsync\";\nimport { Construct } from \"constructs\";\nimport { OpenHiAuthService } from \"./open-hi-auth-service\";\nimport { OpenHiEnvironment } from \"../app/open-hi-environment\";\nimport {\n OpenHiService,\n OpenHiServiceProps,\n OpenHiServiceType,\n} from \"../app/open-hi-service\";\nimport { RootGraphqlApi } from \"../components/app-sync/root-graphql-api\";\n\nexport interface OpenHiGraphqlServiceProps extends OpenHiServiceProps {}\n\n/**\n * GraphQL API service stack: creates the AppSync API via {@link RootGraphqlApi}\n * and exports its ID via SSM. Look up from other stacks via\n * {@link OpenHiGraphqlService.graphqlApiFromConstruct}.\n */\nexport class OpenHiGraphqlService extends OpenHiService {\n static readonly SERVICE_TYPE =\n \"graphql-api\" as const satisfies OpenHiServiceType;\n\n /**\n * Returns the GraphQL API by looking up the GraphQL stack's API ID from SSM.\n * Use from other stacks to obtain an IGraphqlApi reference.\n */\n static graphqlApiFromConstruct(scope: Construct): IGraphqlApi {\n return RootGraphqlApi.fromConstruct(scope);\n }\n\n get serviceType(): string {\n return OpenHiGraphqlService.SERVICE_TYPE;\n }\n\n /* Override so this.props is typed with this service's options */\n public override props: OpenHiGraphqlServiceProps;\n\n public readonly rootGraphqlApi: RootGraphqlApi;\n\n constructor(ohEnv: OpenHiEnvironment, props: OpenHiGraphqlServiceProps = {}) {\n super(ohEnv, OpenHiGraphqlService.SERVICE_TYPE, props);\n this.props = props;\n this.rootGraphqlApi = this.createRootGraphqlApi();\n }\n\n /** Creates the root GraphQL API with Cognito user pool. */\n protected createRootGraphqlApi(): RootGraphqlApi {\n const userPool = OpenHiAuthService.userPoolFromConstruct(this);\n return new RootGraphqlApi(this, {\n authorizationConfig: {\n defaultAuthorization: {\n authorizationType: AuthorizationType.USER_POOL,\n userPoolConfig: {\n userPool,\n defaultAction: UserPoolDefaultAction.ALLOW,\n },\n },\n },\n });\n }\n}\n"],"mappings":"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","names":["exports","exports","exports","exports","exports","exports","exports","exports","exports","exports","parseWorkflowEvent","exports","exports","workflowDedupClient","exports","exports","import_config","import_aws_cdk_lib","import_aws_cdk_lib","import_config","import_aws_cdk_lib","import_aws_cdk_lib","import_aws_ssm","import_aws_cdk_lib","import_aws_cognito","import_aws_cognito","import_aws_cognito","path","fs","import_node_fs","import_node_path","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_node_fs","import_node_path","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_node_fs","import_node_path","import_aws_cdk_lib","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_aws_cdk_lib","import_aws_dynamodb","import_constructs","import_aws_events","import_aws_events","import_node_fs","import_node_path","import_aws_cdk_lib","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_aws_cdk_lib","import_constructs","import_constructs","import_config","import_aws_cognito","import_aws_iam","import_aws_kms","import_core","import_config","import_aws_dynamodb","import_aws_certificatemanager","import_aws_events","import_aws_route53","import_aws_ssm","import_constructs","import_node_fs","import_node_path","import_aws_cdk_lib","import_aws_events","import_aws_iam","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_workflows","import_node_fs","import_node_path","import_types","import_aws_cdk_lib","import_aws_events","import_aws_events_targets","import_aws_iam","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","import_client_dynamodb","import_types","import_workflows","import_electrodb","import_types","import_electrodb","import_electrodb","import_electrodb","import_electrodb","import_electrodb","import_electrodb","import_types","import_types","import_types","import_types","import_electrodb","import_electrodb","import_types","HANDLER_NAME","resolveHandlerEntry","path","fs","import_constructs","import_workflows","import_node_fs","import_node_path","import_types","import_aws_cdk_lib","import_aws_events","import_aws_events_targets","import_aws_iam","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_constructs","import_node_fs","import_node_path","import_aws_cdk_lib","import_aws_events","import_aws_events_targets","import_aws_iam","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_constructs","import_config","import_aws_apigatewayv2","import_aws_iam","import_aws_route53","import_core","import_node_fs","import_node_path","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_node_fs","import_node_path","import_aws_lambda","import_aws_lambda_nodejs","import_constructs","HANDLER_NAME","resolveHandlerEntry","path","fs","import_aws_appsync"]}
|