@openhi/constructs 0.0.159 → 0.0.161

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/lib/{chunk-HQ67J7BP.mjs → chunk-5S6VFBLT.mjs} +12 -70
  2. package/lib/chunk-5S6VFBLT.mjs.map +1 -0
  3. package/lib/{chunk-MVQWAIMC.mjs → chunk-6BB4CRSS.mjs} +3 -312
  4. package/lib/chunk-6BB4CRSS.mjs.map +1 -0
  5. package/lib/{chunk-WPCBVDFZ.mjs → chunk-76UM2LQ5.mjs} +2 -2
  6. package/lib/chunk-7TRO2STL.mjs +4616 -0
  7. package/lib/chunk-7TRO2STL.mjs.map +1 -0
  8. package/lib/chunk-BUAYVN3C.mjs +87 -0
  9. package/lib/chunk-BUAYVN3C.mjs.map +1 -0
  10. package/lib/{chunk-23PUSHBV.mjs → chunk-D2Y6DDOC.mjs} +2 -2
  11. package/lib/chunk-DWSWCUZR.mjs +123 -0
  12. package/lib/chunk-DWSWCUZR.mjs.map +1 -0
  13. package/lib/{chunk-VZCPGQXA.mjs → chunk-EUIP2U5F.mjs} +69 -1
  14. package/lib/{chunk-VZCPGQXA.mjs.map → chunk-EUIP2U5F.mjs.map} +1 -1
  15. package/lib/chunk-GJTPXJKD.mjs +46 -0
  16. package/lib/chunk-GJTPXJKD.mjs.map +1 -0
  17. package/lib/chunk-I6LUPJUY.mjs +61 -0
  18. package/lib/chunk-I6LUPJUY.mjs.map +1 -0
  19. package/lib/{chunk-KR2Y2CVQ.mjs → chunk-KA3OMP3X.mjs} +2 -2
  20. package/lib/{chunk-ZM4GDHHC.mjs → chunk-KMEWULMX.mjs} +51 -3
  21. package/lib/chunk-KMEWULMX.mjs.map +1 -0
  22. package/lib/chunk-LKKLO66E.mjs +25 -0
  23. package/lib/chunk-LKKLO66E.mjs.map +1 -0
  24. package/lib/{chunk-CFJDATDK.mjs → chunk-MLFMW5IF.mjs} +43 -9
  25. package/lib/chunk-MLFMW5IF.mjs.map +1 -0
  26. package/lib/chunk-O5VQWB6U.mjs +315 -0
  27. package/lib/chunk-O5VQWB6U.mjs.map +1 -0
  28. package/lib/{chunk-7BQHLC7U.mjs → chunk-P3CTZWC2.mjs} +8 -40
  29. package/lib/chunk-P3CTZWC2.mjs.map +1 -0
  30. package/lib/chunk-P3NFCKTZ.mjs +502 -0
  31. package/lib/chunk-P3NFCKTZ.mjs.map +1 -0
  32. package/lib/{chunk-M7Y3BOQW.mjs → chunk-Q3MKITPY.mjs} +5 -5
  33. package/lib/chunk-Q64MOYJ7.mjs +218 -0
  34. package/lib/chunk-Q64MOYJ7.mjs.map +1 -0
  35. package/lib/chunk-RQKJNMX5.mjs +89 -0
  36. package/lib/chunk-RQKJNMX5.mjs.map +1 -0
  37. package/lib/{chunk-ZWSGM6PZ.mjs → chunk-SD7J3N3C.mjs} +2 -2
  38. package/lib/{chunk-7RZHFI77.mjs → chunk-VESULYQQ.mjs} +2 -2
  39. package/lib/{chunk-AOSEKL7U.mjs → chunk-WOTU36P3.mjs} +6 -103
  40. package/lib/chunk-WOTU36P3.mjs.map +1 -0
  41. package/lib/{chunk-X5E4YJGZ.mjs → chunk-YPTJJ35S.mjs} +2 -2
  42. package/lib/counter-apply-operation-DZM3MIDm.d.mts +63 -0
  43. package/lib/counter-apply-operation-DZM3MIDm.d.ts +63 -0
  44. package/lib/counter-maintenance.handler.d.mts +38 -0
  45. package/lib/counter-maintenance.handler.d.ts +38 -0
  46. package/lib/counter-maintenance.handler.js +2885 -0
  47. package/lib/counter-maintenance.handler.js.map +1 -0
  48. package/lib/counter-maintenance.handler.mjs +180 -0
  49. package/lib/counter-maintenance.handler.mjs.map +1 -0
  50. package/lib/counter-reconciliation.handler.d.mts +116 -0
  51. package/lib/counter-reconciliation.handler.d.ts +116 -0
  52. package/lib/counter-reconciliation.handler.js +3324 -0
  53. package/lib/counter-reconciliation.handler.js.map +1 -0
  54. package/lib/counter-reconciliation.handler.mjs +295 -0
  55. package/lib/counter-reconciliation.handler.mjs.map +1 -0
  56. package/lib/data-store-postgres-replication.handler.js +50 -2
  57. package/lib/data-store-postgres-replication.handler.js.map +1 -1
  58. package/lib/data-store-postgres-replication.handler.mjs +2 -2
  59. package/lib/delete-chunk.handler.js +118 -2
  60. package/lib/delete-chunk.handler.js.map +1 -1
  61. package/lib/delete-chunk.handler.mjs +3 -3
  62. package/lib/{events-DTgo2dcW.d.mts → events-TG654e7L.d.mts} +68 -19
  63. package/lib/{events-DTgo2dcW.d.ts → events-TG654e7L.d.ts} +68 -19
  64. package/lib/finalize.handler.js +50 -2
  65. package/lib/finalize.handler.js.map +1 -1
  66. package/lib/finalize.handler.mjs +4 -4
  67. package/lib/firehose-archive-transform.handler.js +50 -2
  68. package/lib/firehose-archive-transform.handler.js.map +1 -1
  69. package/lib/firehose-archive-transform.handler.mjs +2 -2
  70. package/lib/index.d.mts +1283 -4
  71. package/lib/index.d.ts +1389 -24
  72. package/lib/index.js +4113 -320
  73. package/lib/index.js.map +1 -1
  74. package/lib/index.mjs +602 -195
  75. package/lib/index.mjs.map +1 -1
  76. package/lib/list-chunks.handler.js +118 -2
  77. package/lib/list-chunks.handler.js.map +1 -1
  78. package/lib/list-chunks.handler.mjs +3 -3
  79. package/lib/platform-deploy-bridge.handler.js +50 -2
  80. package/lib/platform-deploy-bridge.handler.js.map +1 -1
  81. package/lib/platform-deploy-bridge.handler.mjs +1 -1
  82. package/lib/pre-token-generation.handler.js +68 -0
  83. package/lib/pre-token-generation.handler.js.map +1 -1
  84. package/lib/pre-token-generation.handler.mjs +9 -5
  85. package/lib/pre-token-generation.handler.mjs.map +1 -1
  86. package/lib/provision-default-workspace.handler.js +887 -4
  87. package/lib/provision-default-workspace.handler.js.map +1 -1
  88. package/lib/provision-default-workspace.handler.mjs +14 -9
  89. package/lib/provision-default-workspace.handler.mjs.map +1 -1
  90. package/lib/rename-finalize.handler.js +50 -2
  91. package/lib/rename-finalize.handler.js.map +1 -1
  92. package/lib/rename-finalize.handler.mjs +2 -2
  93. package/lib/rename-list-targets.handler.js +118 -2
  94. package/lib/rename-list-targets.handler.js.map +1 -1
  95. package/lib/rename-list-targets.handler.mjs +11 -9
  96. package/lib/rename-list-targets.handler.mjs.map +1 -1
  97. package/lib/rename-rewrite-chunk.handler.js +68 -0
  98. package/lib/rename-rewrite-chunk.handler.js.map +1 -1
  99. package/lib/rename-rewrite-chunk.handler.mjs +2 -2
  100. package/lib/rest-api-lambda.handler.js +1454 -251
  101. package/lib/rest-api-lambda.handler.js.map +1 -1
  102. package/lib/rest-api-lambda.handler.mjs +673 -821
  103. package/lib/rest-api-lambda.handler.mjs.map +1 -1
  104. package/lib/seed-demo-data.handler.d.mts +1 -1
  105. package/lib/seed-demo-data.handler.d.ts +1 -1
  106. package/lib/seed-demo-data.handler.js +4004 -201
  107. package/lib/seed-demo-data.handler.js.map +1 -1
  108. package/lib/seed-demo-data.handler.mjs +10 -7
  109. package/lib/seed-system-data.handler.js +118 -2
  110. package/lib/seed-system-data.handler.js.map +1 -1
  111. package/lib/seed-system-data.handler.mjs +5 -5
  112. package/package.json +1 -1
  113. package/lib/chunk-7BQHLC7U.mjs.map +0 -1
  114. package/lib/chunk-AOSEKL7U.mjs.map +0 -1
  115. package/lib/chunk-BQMJSDOD.mjs +0 -1136
  116. package/lib/chunk-BQMJSDOD.mjs.map +0 -1
  117. package/lib/chunk-CFJDATDK.mjs.map +0 -1
  118. package/lib/chunk-E6MCKJVS.mjs +0 -212
  119. package/lib/chunk-E6MCKJVS.mjs.map +0 -1
  120. package/lib/chunk-HQ67J7BP.mjs.map +0 -1
  121. package/lib/chunk-MVQWAIMC.mjs.map +0 -1
  122. package/lib/chunk-ZM4GDHHC.mjs.map +0 -1
  123. /package/lib/{chunk-WPCBVDFZ.mjs.map → chunk-76UM2LQ5.mjs.map} +0 -0
  124. /package/lib/{chunk-23PUSHBV.mjs.map → chunk-D2Y6DDOC.mjs.map} +0 -0
  125. /package/lib/{chunk-KR2Y2CVQ.mjs.map → chunk-KA3OMP3X.mjs.map} +0 -0
  126. /package/lib/{chunk-M7Y3BOQW.mjs.map → chunk-Q3MKITPY.mjs.map} +0 -0
  127. /package/lib/{chunk-ZWSGM6PZ.mjs.map → chunk-SD7J3N3C.mjs.map} +0 -0
  128. /package/lib/{chunk-7RZHFI77.mjs.map → chunk-VESULYQQ.mjs.map} +0 -0
  129. /package/lib/{chunk-X5E4YJGZ.mjs.map → chunk-YPTJJ35S.mjs.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/workflows/control-plane/seed-demo-data/seed-demo-data.handler.ts","../src/workflows/control-plane/seed-demo-data/events.ts","../src/workflows/control-plane/seed-demo-data/data-plane-fixtures.ts"],"sourcesContent":["import {\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 GetParameterCommand,\n ParameterNotFound,\n SSMClient,\n} from \"@aws-sdk/client-ssm\";\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_DATA_PLANE_FIXTURES,\n type DemoWorkspaceDataPlaneFixtures,\n} from \"./data-plane-fixtures\";\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\";\nimport { createAccountOperation } from \"../../../data/operations/data/account/account-create-operation\";\nimport { createEncounterOperation } from \"../../../data/operations/data/encounter/encounter-create-operation\";\nimport { createObservationOperation } from \"../../../data/operations/data/observation/observation-create-operation\";\nimport { createPatientOperation } from \"../../../data/operations/data/patient/patient-create-operation\";\nimport { createPractitionerOperation } from \"../../../data/operations/data/practitioner/practitioner-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 * Structured per-item failure record collected during {@link seedDemoGraph}.\n * Every individual upsert in Phases 1-3 is wrapped so a single item's\n * failure (transient AWS error, write-time constraint violation, etc.)\n * does not skip the rest of its phase. After all phases run, the\n * collected failures are aggregate-thrown so EventBridge still routes\n * the workflow into its failure-detection path (DLQ + CloudWatch\n * alarm) and the outer `runSeedDemoData` records `markFailed` on the\n * dedup row.\n *\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-demo-data/index.md\n */\ntype SeedPhase = \"phase-1\" | \"phase-2\" | \"phase-3\";\n\ninterface SeedFailure {\n readonly phase: SeedPhase;\n /**\n * Short human label identifying where the failure happened\n * (e.g. tenant id, workspace id, user id). Used purely to enrich\n * the aggregate-throw message; no machine reads it.\n */\n readonly scope: string;\n readonly resourceType: string;\n readonly resourceId: string;\n readonly error: unknown;\n}\n\n/**\n * Wrap a single upsert call. On success, returns `true`. On failure,\n * pushes a structured {@link SeedFailure} onto `failures` and returns\n * `false` — the caller can use the boolean to decide whether to skip\n * downstream writes that strictly depend on this one. Most call sites\n * ignore the boolean (per-item isolation: the next item is attempted\n * regardless of this one's outcome).\n *\n * The wrapped function's return value is discarded; only the success/\n * failure signal is propagated. Callers that need the operation's\n * result (e.g. the `cognitoSub` from `cognito.ensureUser`) must hoist\n * the call out of `tryRun` and handle the failure inline.\n */\nconst tryRun = async (\n failures: Array<SeedFailure>,\n phase: SeedPhase,\n scope: string,\n resourceType: string,\n resourceId: string,\n fn: () => Promise<unknown>,\n): Promise<boolean> => {\n try {\n await fn();\n return true;\n } catch (err) {\n failures.push({ phase, scope, resourceType, resourceId, error: err });\n return false;\n }\n};\n\n/**\n * Format collected failures into a single Error suitable for the\n * outer `markFailed` reason and CloudWatch alarm payload. Lists every\n * failure as `<phase> <scope>/<resourceType>/<resourceId>: <message>`\n * joined by `; ` so a single CloudWatch line carries the full picture.\n */\nconst aggregateFailureError = (failures: ReadonlyArray<SeedFailure>): Error => {\n const summary = failures\n .map(\n (f) =>\n `${f.phase} ${f.scope}/${f.resourceType}/${f.resourceId}: ${errorMessage(\n f.error,\n )}`,\n )\n .join(\"; \");\n return new Error(\n `seed-demo-data: ${failures.length} item(s) failed across phases: ${summary}`,\n );\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 * Phase 3 helper: upsert one workspace's data-plane FHIR resources\n * (Patient/Practitioner/Observation/Encounter/Account). Parents are\n * written before children that reference them so direct `Resource/<id>`\n * references resolve on read paths that follow them — Patient first,\n * then Practitioner, then Observation/Encounter/Account which may\n * carry `subject: Patient/...` references.\n *\n * Every create operation's `body.id` is supplied from the fixture, so\n * re-fires after dedup-TTL expiry upsert the same record (ElectroDB's\n * `put` is unconditional — same composite key overwrites the prior\n * row).\n *\n * Each individual create is wrapped via {@link tryRun}: a failed\n * Patient does not skip the remaining Patients or the downstream\n * Practitioner/Observation/Encounter/Account writes in this workspace,\n * and a failure here does not propagate up to skip the next workspace\n * in the outer loop. Downstream resources that reference a failed\n * parent (`subject: Patient/<id>`) will likely fail too — that is\n * fine, they are attempted, the failures recorded, and the next\n * deploy upserts them once the parent lands.\n */\nconst seedWorkspaceDataPlane = async (\n baseContext: OpenHiContext,\n group: DemoWorkspaceDataPlaneFixtures,\n failures: Array<SeedFailure>,\n): Promise<void> => {\n const workspaceContext: OpenHiContext = {\n ...baseContext,\n tenantId: group.tenantId,\n workspaceId: group.workspaceId,\n };\n const scope = `${group.tenantId}/${group.workspaceId}`;\n for (const patient of group.patients) {\n await tryRun(failures, \"phase-3\", scope, \"Patient\", patient.id ?? \"\", () =>\n createPatientOperation({\n context: workspaceContext,\n body: patient,\n }),\n );\n }\n for (const practitioner of group.practitioners) {\n await tryRun(\n failures,\n \"phase-3\",\n scope,\n \"Practitioner\",\n practitioner.id ?? \"\",\n () =>\n createPractitionerOperation({\n context: workspaceContext,\n body: practitioner,\n }),\n );\n }\n for (const observation of group.observations) {\n await tryRun(\n failures,\n \"phase-3\",\n scope,\n \"Observation\",\n observation.id ?? \"\",\n () =>\n createObservationOperation({\n context: workspaceContext,\n body: observation,\n }),\n );\n }\n for (const encounter of group.encounters) {\n await tryRun(\n failures,\n \"phase-3\",\n scope,\n \"Encounter\",\n encounter.id ?? \"\",\n () =>\n createEncounterOperation({\n context: workspaceContext,\n body: encounter,\n }),\n );\n }\n for (const account of group.accounts) {\n await tryRun(failures, \"phase-3\", scope, \"Account\", account.id ?? \"\", () =>\n createAccountOperation({\n context: workspaceContext,\n body: account,\n }),\n );\n }\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. Finally walks {@link DEMO_DATA_PLANE_FIXTURES} and\n * upserts the OPS-009 v1 FHIR resource set (Patient, Practitioner,\n * Observation, Encounter, Account) into each demo workspace. Every\n * put is keyed by a deterministic stable id so re-runs after\n * dedup-TTL expiry upsert the same records.\n *\n * Self-healing-on-every-deploy contract: every individual upsert\n * across all three phases is wrapped via {@link tryRun}. A single\n * item's failure (transient AWS error, write-time constraint\n * violation, etc.) is collected into a per-call accumulator and\n * does not skip the remaining items in its phase. After all phases\n * run, collected failures are aggregate-thrown as a single error so\n * EventBridge still routes the workflow into its failure-detection\n * path (DLQ + CloudWatch alarm) and the outer `runSeedDemoData`\n * records `markFailed` on the dedup row. Because every put is keyed\n * by a deterministic stable id, the next deploy re-attempts every\n * item — failed items eventually heal, successful items overwrite\n * themselves with the same body.\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 const failures: Array<SeedFailure> = [];\n\n // Phase 1: Tenants + Workspaces (no Memberships / RoleAssignments\n // yet — those need the per-user Cognito sub).\n //\n // Each tenant and each workspace is wrapped individually. A failed\n // tenant create does NOT skip its workspaces — the tenant may\n // already exist from a prior deploy (the put is unconditional, so\n // a duplicate is a no-op; we treat all errors as recoverable here\n // and let the workspace writes proceed). A failed workspace does\n // not skip the next workspace under the same tenant or any\n // subsequent tenant.\n for (const spec of DEMO_TENANT_SPECS) {\n const tenantContext: OpenHiContext = {\n ...baseContext,\n tenantId: spec.tenantId,\n };\n\n await tryRun(\n failures,\n \"phase-1\",\n spec.tenantId,\n \"Tenant\",\n spec.tenantId,\n () =>\n createTenantOperation({\n context: tenantContext,\n body: { id: spec.tenantId, resource: tenantResourceBody(spec) },\n }),\n );\n\n for (const workspace of spec.workspaces) {\n await tryRun(\n failures,\n \"phase-1\",\n spec.tenantId,\n \"Workspace\",\n workspace.id,\n () =>\n createWorkspaceOperation({\n context: tenantContext,\n body: {\n id: workspace.id,\n resource: workspaceResourceBody(spec, workspace),\n },\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 because every subsequent write needs the\n // `cognitoSub` it returns. If `ensureUser` throws, skip the whole\n // user (we have no sub to write into the User row).\n //\n // For each individual call inside the per-user block we use\n // {@link tryRun} so a Membership failure in tenant 3 does not skip\n // the Membership + RAs in tenant 4 for the same user, and the\n // platform-scoped RA is attempted last regardless of any earlier\n // per-tenant failure.\n //\n // `upsertUser` is wrapped via tryRun BUT we do NOT short-circuit\n // the per-tenant writes on its failure. The User row carries\n // `cognitoSub` for JWT-claim resolution; the Memberships and\n // RoleAssignments only reference the User by stable id. A previous\n // deploy may have already written the User row with the correct\n // sub. Attempting the per-tenant writes lets the next deploy heal\n // any partial state.\n for (const user of devUsers) {\n let cognitoSub: string;\n try {\n cognitoSub = await cognito.ensureUser(user.email);\n } catch (err) {\n failures.push({\n phase: \"phase-2\",\n scope: user.id,\n resourceType: \"CognitoUser\",\n resourceId: user.email,\n error: err,\n });\n continue;\n }\n\n await tryRun(failures, \"phase-2\", user.id, \"User\", user.id, () =>\n upsertUser(baseContext, user, cognitoSub),\n );\n\n for (const spec of DEMO_TENANT_SPECS) {\n const tenantContext: OpenHiContext = {\n ...baseContext,\n tenantId: spec.tenantId,\n };\n const userScope = `${user.id}@${spec.tenantId}`;\n\n const membershipId = demoMembershipId(user.id, spec.tenantId);\n await tryRun(\n failures,\n \"phase-2\",\n userScope,\n \"Membership\",\n membershipId,\n () =>\n createMembershipOperation({\n context: tenantContext,\n body: {\n id: membershipId,\n resource: membershipResourceBody(spec, user, membershipId),\n },\n }),\n );\n\n for (const roleCode of demoRolesForUserInTenant(user, spec.tenantId)) {\n const raId = demoRoleAssignmentId(user.id, spec.tenantId, roleCode);\n await tryRun(\n failures,\n \"phase-2\",\n userScope,\n \"RoleAssignment\",\n raId,\n () =>\n 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\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. The write-time tenant-membership\n // constraint short-circuits for this sentinel so no Membership\n // prerequisite is required.\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 tryRun(\n failures,\n \"phase-2\",\n `${user.id}@${PLATFORM_SCOPE_TENANT_ID}`,\n \"RoleAssignment\",\n platformRaId,\n () =>\n 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 // Phase 3: data-plane FHIR resources (OPS-009 v1 set) per demo\n // workspace. Runs after the control-plane phases so the Tenants\n // and Workspaces those resources implicitly belong to already\n // exist by the time we write into them. The placeholder tenant\n // carries no data-plane fixtures.\n //\n // `seedWorkspaceDataPlane` already wraps each individual create\n // via tryRun; we wrap the helper call itself in an outer\n // try/catch as defence-in-depth so an unexpected throw from the\n // helper (programmer error, OOM, etc.) does not skip the next\n // workspace.\n for (const group of DEMO_DATA_PLANE_FIXTURES) {\n try {\n await seedWorkspaceDataPlane(baseContext, group, failures);\n } catch (err) {\n failures.push({\n phase: \"phase-3\",\n scope: `${group.tenantId}/${group.workspaceId}`,\n resourceType: \"Workspace\",\n resourceId: group.workspaceId,\n error: err,\n });\n }\n }\n\n if (failures.length > 0) {\n throw aggregateFailureError(failures);\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 * SSM parameter-path prefix that hosts every seeded dev user's\n * password. The full path for a single user is\n * `${SEED_USER_PASSWORD_PARAMETER_PREFIX}<email_safe>/password`\n * where `<email_safe>` is `<user>_at_<domain>` (see\n * {@link emailToSsmPath}). The trailing `/password` segment leaves\n * room for additional per-user parameters under the same email\n * prefix in future phases without renaming the existing entries.\n */\nexport const SEED_USER_PASSWORD_PARAMETER_PREFIX = \"/openhi/seed/users/\";\n\n/**\n * SSM Parameter Store path validation regex. Names must be 1-1011\n * characters, may include alphanumerics plus `_`, `.`, `-`, and\n * `/`. We enforce a stricter subset here — alphanumerics, hyphen,\n * underscore, and period only — because the path is constructed by\n * substituting `@` → `_at_` and any other special character would\n * indicate an email that should have been rejected upstream.\n */\nconst SSM_PATH_SEGMENT = /^[A-Za-z0-9_.-]+$/;\n\n/**\n * Map an email address to its canonical SSM parameter path. The\n * `@` separator is replaced with the literal sentinel `_at_` so\n * the resulting path is a single SSM-legal name. Throws when the\n * email is empty, missing exactly one `@`, or contains characters\n * that would produce an invalid SSM path.\n *\n * Example: `alice@codedrifters.com` →\n * `/openhi/seed/users/alice_at_codedrifters.com/password`.\n */\nexport const emailToSsmPath = (email: string): string => {\n if (typeof email !== \"string\" || email.length === 0) {\n throw new Error(\n `emailToSsmPath: email must be a non-empty string (received \"${String(email)}\").`,\n );\n }\n const atIdx = email.indexOf(\"@\");\n if (atIdx === -1 || atIdx !== email.lastIndexOf(\"@\")) {\n throw new Error(\n `emailToSsmPath: email \"${email}\" must contain exactly one \"@\" character.`,\n );\n }\n const localPart = email.slice(0, atIdx);\n const domainPart = email.slice(atIdx + 1);\n if (localPart.length === 0 || domainPart.length === 0) {\n throw new Error(\n `emailToSsmPath: email \"${email}\" must have a non-empty local-part and domain.`,\n );\n }\n if (!SSM_PATH_SEGMENT.test(localPart) || !SSM_PATH_SEGMENT.test(domainPart)) {\n throw new Error(\n `emailToSsmPath: email \"${email}\" contains characters that would produce ` +\n \"an invalid SSM parameter path (only A-Z, a-z, 0-9, '.', '-', and '_' are allowed).\",\n );\n }\n return `${SEED_USER_PASSWORD_PARAMETER_PREFIX}${localPart}_at_${domainPart}/password`;\n};\n\n/**\n * Module-scoped SSM client. Lambda containers reuse the same\n * `SSMClient` instance across invocations so the TLS connection\n * + credential-resolver state warms once per container. Mirrors\n * the `CognitoIdentityProviderClient` / `DynamoDBClient` reuse\n * pattern at the bottom of this file.\n */\nlet cachedSsmClient: SSMClient | undefined;\n\nconst getSsmClient = (): SSMClient => {\n if (!cachedSsmClient) {\n cachedSsmClient = new SSMClient({});\n }\n return cachedSsmClient;\n};\n\n/**\n * Test seam: reset the module-scoped SSM client cache. Unit tests\n * that swap in different mocks across `it` blocks call this in\n * `beforeEach` so each test gets a fresh client instance.\n */\nexport const __resetSsmClientForTests = (): void => {\n cachedSsmClient = undefined;\n};\n\n/**\n * Read a seeded dev user's password from SSM Parameter Store. The\n * parameter is expected to be a `SecureString` provisioned out of\n * band by an operator (see the runbook in phase 3 of #1249). On\n * `ParameterNotFound`, the error message includes the exact\n * expected path so the operator can copy-paste-fix.\n */\nexport const fetchSeedUserPassword = async (email: string): Promise<string> => {\n const path = emailToSsmPath(email);\n const client = getSsmClient();\n try {\n const result = await client.send(\n new GetParameterCommand({ Name: path, WithDecryption: true }),\n );\n const value = result.Parameter?.Value;\n if (typeof value !== \"string\" || value.length === 0) {\n throw new Error(\n `fetchSeedUserPassword: SSM parameter \"${path}\" returned an empty value.`,\n );\n }\n return value;\n } catch (err) {\n if (err instanceof ParameterNotFound) {\n throw new Error(\n `fetchSeedUserPassword: SSM parameter \"${path}\" not found. ` +\n `Provision a SecureString at \"${path}\" with the dev user's password before re-running seed-demo-data.`,\n );\n }\n throw err;\n }\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, from the SSM-sourced\n * value). 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 **also\n * re-applies the current SSM-sourced password** via\n * `AdminSetUserPassword`. This is the rotation seam: operators\n * update the SSM SecureString value and re-publish the seed\n * event to push a fresh password down to Cognito.\n *\n * The SSM password is fetched once per email up-front so the new-\n * user and rotation branches share the same value and at most one\n * `GetParameter` call lands per `ensureUser` invocation.\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 const setPassword = async (\n email: string,\n password: string,\n ): Promise<void> => {\n await client.send(\n new AdminSetUserPasswordCommand({\n UserPoolId: userPoolId,\n Username: email,\n Password: password,\n Permanent: true,\n }),\n );\n };\n return {\n ensureUser: async (email: string): Promise<string> => {\n // Fetch the SSM-sourced password up-front so both the\n // new-user path and the rotation path consume the same\n // value with a single `GetParameter` call.\n const password = await fetchSeedUserPassword(email);\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 setPassword(email, password);\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 — re-apply the current SSM-sourced\n // password so operator rotations propagate, then read the\n // existing sub. AdminSetUserPassword on an existing user\n // overwrites the credential without affecting the sub.\n await setPassword(email, password);\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 { PLATFORM_ROLE_CODE, type PlatformRoleCode } from \"@openhi/types\";\nimport { PlatformSystemDataSeededV1 } from \"@openhi/workflows\";\nimport { PLATFORM_SCOPE_TENANT_ID } from \"../../../data/operations/control/membership-constraints/platform-scope-tenant-id\";\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.\n *\n * The canonical definition lives in\n * `data/operations/control/membership-constraints/platform-scope-tenant-id.ts`\n * so the write-time tenant-membership constraint can short-circuit\n * the sentinel without the data layer importing from the workflow.\n * Re-exported here so existing imports that resolve through\n * `seed-demo-data/events` keep working.\n */\nexport { PLATFORM_SCOPE_TENANT_ID };\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 // Dedicated end-to-end test principal for admin-console Playwright\n // specs (issue #1275). Reuses the standard DEV_USERS plumbing\n // (Cognito user + DynamoDB User + per-tenant Memberships +\n // tenant-admin RAs + platform-scoped system-admin RA) and the\n // existing SSM-sourced password convention from #1249 — the\n // operator provisions the SecureString at\n // /openhi/seed/users/e2e-admin-console_at_codedrifters.com/password\n // out of band, and the seeded Cognito user picks it up via\n // AdminSetUserPassword on every seed run.\n { id: \"dev-e2e-admin-console\", email: \"e2e-admin-console@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 * `workspace` for single-workspace tenants, `workspace-<sub>` for the\n * 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 use `demo-*` slugs.\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 */\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 * 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 type {\n Account,\n Encounter,\n Observation,\n Patient,\n Practitioner,\n} from \"@openhi/types\";\nimport {\n DEMO_TENANT_SPECS,\n PLACEHOLDER_TENANT_ID,\n demoScenarioIdentifier,\n openhiResourceIdentifier,\n} from \"./events\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-demo-data/data-plane-fixtures.md\n *\n * Hand-authored FHIR data-plane fixture bodies the `seed-demo-data`\n * workflow upserts into the data store on every non-prod deploy.\n * Mirrors the OPS-009 v1 resource set: Patient, Practitioner,\n * Observation, Encounter, Account.\n *\n * Ids are deterministic — re-fires of the workflow upsert the same\n * records, satisfying the workflow's idempotency contract (no\n * duplicates) and letting the IAM grant in `seed-demo-data-lambda.ts`\n * enumerate exact-match `dynamodb:LeadingKeys` rather than a wildcard.\n *\n * The placeholder tenant carries no data-plane fixtures — only the\n * three real demo tenants (wound-care, primary-care, mixed) get\n * Patient/Practitioner/Observation/Encounter/Account records. The\n * placeholder tenant exists solely as a routing target for the\n * Cognito pre-token-generation fallback and never holds clinical\n * data.\n */\n\n/**\n * Logical group of FHIR resources owned by a single (tenant, workspace)\n * pair. The workflow walks `DEMO_DATA_PLANE_FIXTURES` and writes every\n * entry against the matching workspace's `OpenHiContext`.\n */\nexport interface DemoWorkspaceDataPlaneFixtures {\n readonly tenantId: string;\n readonly workspaceId: string;\n /**\n * Scenario slug used in the demo-URN identifier — mirrors the\n * `DemoTenantSpec.scenario` value for the parent tenant. For the\n * mixed tenant both workspaces share the `demo-mixed` scenario.\n */\n readonly scenario: string;\n readonly patients: ReadonlyArray<Patient>;\n readonly practitioners: ReadonlyArray<Practitioner>;\n readonly observations: ReadonlyArray<Observation>;\n readonly encounters: ReadonlyArray<Encounter>;\n readonly accounts: ReadonlyArray<Account>;\n}\n\n/**\n * Map a tenant scenario to a `(roleSuffix, tenantId, workspaceId)`\n * triple plus the demo + OHI identifiers. Keeps the per-resource\n * builders compact below — they call this once per body to stamp the\n * two-identifier pair that matches the control-plane pattern.\n */\nconst fixtureIdentifiers = (\n scenario: string,\n tenantId: string,\n workspaceId: string,\n resourceType: string,\n id: string,\n roleSuffix: string,\n) => [\n demoScenarioIdentifier(scenario, roleSuffix),\n openhiResourceIdentifier({\n tenantId,\n workspaceId,\n resourceType,\n id,\n }),\n];\n\nconst buildWoundCareFixtures = (\n scenario: string,\n tenantId: string,\n workspaceId: string,\n idPrefix: string,\n): DemoWorkspaceDataPlaneFixtures => ({\n tenantId,\n workspaceId,\n scenario,\n patients: [\n {\n resourceType: \"Patient\",\n id: `${idPrefix}-patient-1`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Patient\",\n `${idPrefix}-patient-1`,\n `patient-1`,\n ),\n active: true,\n name: [{ family: \"Carter\", given: [\"Eleanor\"], use: \"official\" }],\n gender: \"female\",\n birthDate: \"1952-04-18\",\n },\n {\n resourceType: \"Patient\",\n id: `${idPrefix}-patient-2`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Patient\",\n `${idPrefix}-patient-2`,\n `patient-2`,\n ),\n active: true,\n name: [{ family: \"Nguyen\", given: [\"Hao\"], use: \"official\" }],\n gender: \"male\",\n birthDate: \"1968-11-02\",\n },\n ],\n practitioners: [\n {\n resourceType: \"Practitioner\",\n id: `${idPrefix}-practitioner-1`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Practitioner\",\n `${idPrefix}-practitioner-1`,\n `practitioner-1`,\n ),\n active: true,\n name: [{ family: \"Reyes\", given: [\"Maria\"], prefix: [\"Dr.\"] }],\n gender: \"female\",\n },\n {\n resourceType: \"Practitioner\",\n id: `${idPrefix}-practitioner-2`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Practitioner\",\n `${idPrefix}-practitioner-2`,\n `practitioner-2`,\n ),\n active: true,\n name: [{ family: \"Okafor\", given: [\"Chinedu\"], prefix: [\"Dr.\"] }],\n gender: \"male\",\n },\n ],\n observations: [\n {\n resourceType: \"Observation\",\n id: `${idPrefix}-observation-1`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Observation\",\n `${idPrefix}-observation-1`,\n `observation-1`,\n ),\n status: \"final\",\n code: {\n coding: [\n {\n system: \"http://loinc.org\",\n code: \"39135-9\",\n display: \"Wound size\",\n },\n ],\n },\n subject: { reference: `Patient/${idPrefix}-patient-1` },\n valueString: \"3.2cm x 2.1cm\",\n },\n {\n resourceType: \"Observation\",\n id: `${idPrefix}-observation-2`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Observation\",\n `${idPrefix}-observation-2`,\n `observation-2`,\n ),\n status: \"final\",\n code: {\n coding: [\n {\n system: \"http://loinc.org\",\n code: \"72287-2\",\n display: \"Wound exudate amount\",\n },\n ],\n },\n subject: { reference: `Patient/${idPrefix}-patient-2` },\n valueString: \"moderate\",\n },\n ],\n encounters: [\n {\n resourceType: \"Encounter\",\n id: `${idPrefix}-encounter-1`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Encounter\",\n `${idPrefix}-encounter-1`,\n `encounter-1`,\n ),\n status: \"finished\",\n class: {\n system: \"http://terminology.hl7.org/CodeSystem/v3-ActCode\",\n code: \"AMB\",\n display: \"ambulatory\",\n },\n subject: { reference: `Patient/${idPrefix}-patient-1` },\n },\n {\n resourceType: \"Encounter\",\n id: `${idPrefix}-encounter-2`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Encounter\",\n `${idPrefix}-encounter-2`,\n `encounter-2`,\n ),\n status: \"finished\",\n class: {\n system: \"http://terminology.hl7.org/CodeSystem/v3-ActCode\",\n code: \"AMB\",\n display: \"ambulatory\",\n },\n subject: { reference: `Patient/${idPrefix}-patient-2` },\n },\n ],\n accounts: [\n {\n resourceType: \"Account\",\n id: `${idPrefix}-account-1`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Account\",\n `${idPrefix}-account-1`,\n `account-1`,\n ),\n status: \"active\",\n name: \"Wound-care self-pay account\",\n subject: [{ reference: `Patient/${idPrefix}-patient-1` }],\n },\n ],\n});\n\nconst buildPrimaryCareFixtures = (\n scenario: string,\n tenantId: string,\n workspaceId: string,\n idPrefix: string,\n): DemoWorkspaceDataPlaneFixtures => ({\n tenantId,\n workspaceId,\n scenario,\n patients: [\n {\n resourceType: \"Patient\",\n id: `${idPrefix}-patient-1`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Patient\",\n `${idPrefix}-patient-1`,\n `patient-1`,\n ),\n active: true,\n name: [{ family: \"Bennett\", given: [\"Sophia\"], use: \"official\" }],\n gender: \"female\",\n birthDate: \"1985-06-09\",\n },\n {\n resourceType: \"Patient\",\n id: `${idPrefix}-patient-2`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Patient\",\n `${idPrefix}-patient-2`,\n `patient-2`,\n ),\n active: true,\n name: [{ family: \"Patel\", given: [\"Arjun\"], use: \"official\" }],\n gender: \"male\",\n birthDate: \"1979-02-21\",\n },\n ],\n practitioners: [\n {\n resourceType: \"Practitioner\",\n id: `${idPrefix}-practitioner-1`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Practitioner\",\n `${idPrefix}-practitioner-1`,\n `practitioner-1`,\n ),\n active: true,\n name: [{ family: \"Lin\", given: [\"Wei\"], prefix: [\"Dr.\"] }],\n gender: \"female\",\n },\n {\n resourceType: \"Practitioner\",\n id: `${idPrefix}-practitioner-2`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Practitioner\",\n `${idPrefix}-practitioner-2`,\n `practitioner-2`,\n ),\n active: true,\n name: [{ family: \"Kowalski\", given: [\"Piotr\"], prefix: [\"Dr.\"] }],\n gender: \"male\",\n },\n ],\n observations: [\n {\n resourceType: \"Observation\",\n id: `${idPrefix}-observation-1`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Observation\",\n `${idPrefix}-observation-1`,\n `observation-1`,\n ),\n status: \"final\",\n code: {\n coding: [\n {\n system: \"http://loinc.org\",\n code: \"8480-6\",\n display: \"Systolic blood pressure\",\n },\n ],\n },\n subject: { reference: `Patient/${idPrefix}-patient-1` },\n valueQuantity: { value: 122, unit: \"mm[Hg]\" },\n },\n {\n resourceType: \"Observation\",\n id: `${idPrefix}-observation-2`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Observation\",\n `${idPrefix}-observation-2`,\n `observation-2`,\n ),\n status: \"final\",\n code: {\n coding: [\n {\n system: \"http://loinc.org\",\n code: \"8462-4\",\n display: \"Diastolic blood pressure\",\n },\n ],\n },\n subject: { reference: `Patient/${idPrefix}-patient-2` },\n valueQuantity: { value: 78, unit: \"mm[Hg]\" },\n },\n ],\n encounters: [\n {\n resourceType: \"Encounter\",\n id: `${idPrefix}-encounter-1`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Encounter\",\n `${idPrefix}-encounter-1`,\n `encounter-1`,\n ),\n status: \"finished\",\n class: {\n system: \"http://terminology.hl7.org/CodeSystem/v3-ActCode\",\n code: \"AMB\",\n display: \"ambulatory\",\n },\n subject: { reference: `Patient/${idPrefix}-patient-1` },\n },\n {\n resourceType: \"Encounter\",\n id: `${idPrefix}-encounter-2`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Encounter\",\n `${idPrefix}-encounter-2`,\n `encounter-2`,\n ),\n status: \"in-progress\",\n class: {\n system: \"http://terminology.hl7.org/CodeSystem/v3-ActCode\",\n code: \"AMB\",\n display: \"ambulatory\",\n },\n subject: { reference: `Patient/${idPrefix}-patient-2` },\n },\n ],\n accounts: [\n {\n resourceType: \"Account\",\n id: `${idPrefix}-account-1`,\n identifier: fixtureIdentifiers(\n scenario,\n tenantId,\n workspaceId,\n \"Account\",\n `${idPrefix}-account-1`,\n `account-1`,\n ),\n status: \"active\",\n name: \"Primary-care insurance account\",\n subject: [{ reference: `Patient/${idPrefix}-patient-1` }],\n },\n ],\n});\n\n/**\n * Per-workspace fixtures the data-plane phase writes on every fire.\n * The placeholder tenant carries no fixtures. The mixed tenant carries\n * one fixture group per workspace; the two single-workspace tenants\n * carry one each. Total: 4 fixture groups × ≈ 9 resources = ~36\n * data-plane records.\n *\n * Ids embed the tenant + workspace slug so they remain unambiguous\n * across the four workspaces (the FHIR resource id is the only thing\n * that survives into the partition key, so a duplicate id across\n * workspaces would still collide on read paths that scan-by-id).\n */\nexport const DEMO_DATA_PLANE_FIXTURES: ReadonlyArray<DemoWorkspaceDataPlaneFixtures> =\n [\n buildWoundCareFixtures(\n \"demo-wound-care\",\n \"demo-wound-care-tenant\",\n \"demo-wound-care-workspace\",\n \"demo-wound-care\",\n ),\n buildPrimaryCareFixtures(\n \"demo-primary-care\",\n \"demo-primary-care-tenant\",\n \"demo-primary-care-workspace\",\n \"demo-primary-care\",\n ),\n buildWoundCareFixtures(\n \"demo-mixed\",\n \"demo-mixed-tenant\",\n \"demo-mixed-workspace-wound-care\",\n \"demo-mixed-wound-care\",\n ),\n buildPrimaryCareFixtures(\n \"demo-mixed\",\n \"demo-mixed-tenant\",\n \"demo-mixed-workspace-primary-care\",\n \"demo-mixed-primary-care\",\n ),\n ];\n\n/**\n * Regression guard: the fixture groups must each be tied to a Tenant +\n * Workspace combination that exists in {@link DEMO_TENANT_SPECS}.\n * Imported `DEMO_TENANT_SPECS` and `PLACEHOLDER_TENANT_ID` keep the\n * guard self-contained — no test plumbing required.\n */\nconst _validateFixturesAgainstTenantSpecs = (): void => {\n for (const group of DEMO_DATA_PLANE_FIXTURES) {\n if (group.tenantId === PLACEHOLDER_TENANT_ID) {\n throw new Error(\n \"The placeholder tenant must not carry data-plane fixtures.\",\n );\n }\n const tenant = DEMO_TENANT_SPECS.find((s) => s.tenantId === group.tenantId);\n if (!tenant) {\n throw new Error(\n `Fixture references unknown tenantId \"${group.tenantId}\". ` +\n \"Add a matching entry to DEMO_TENANT_SPECS first.\",\n );\n }\n const workspace = tenant.workspaces.find(\n (ws) => ws.id === group.workspaceId,\n );\n if (!workspace) {\n throw new Error(\n `Fixture references unknown workspaceId \"${group.workspaceId}\" ` +\n `for tenant \"${group.tenantId}\".`,\n );\n }\n }\n};\n_validateFixturesAgainstTenantSpecs();\n"],"mappings":"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","names":["import_workflows","PLATFORM_ROLE_CODE","PLATFORM_ROLE_CODE"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/data/operations/control/membership-constraints/platform-scope-tenant-id.ts","../src/data/operations/control/membership/membership-create-operation.ts","../src/data/operations/control/denormalized-display-names.ts","../src/data/operations/control/membership-constraints/assert-workspace-in-tenant-operation.ts","../src/data/operations/control/roleassignment/roleassignment-create-operation.ts","../src/data/operations/control/membership-constraints/assert-user-has-tenant-membership-operation.ts","../src/data/operations/control/tenant/tenant-create-operation.ts","../src/data/operations/control/workspace/workspace-create-operation.ts","../src/data/operations/data/organization/organization-provision-for-workspace-operation.ts"],"sourcesContent":["/**\n * Sentinel `tenantId` used on platform-scoped `RoleAssignment` records.\n * A platform-scoped RA (e.g. `system-admin`) spans every tenant, but\n * the `RoleAssignment` entity requires a `tenantId` on its key for\n * sharding — there is no real tenant to point at. The `\"platform\"`\n * literal is a reserved value that never matches a real `Tenant.id`\n * and signals \"this RA scopes across all tenants\".\n *\n * The constant lives in the data layer so both the\n * {@link assertUserHasTenantMembershipOperation} write-time constraint\n * and the seed-demo-data workflow can read it without the constraint\n * helper having to reach back into the workflow package — which would\n * create a backwards data-layer-to-workflow dependency. The\n * `workflows/control-plane/seed-demo-data/events.ts` module re-exports\n * the constant under the same name for back-compat with existing\n * imports that point at the workflow's barrel.\n *\n * Renaming this constant is a wire-format break — the seed-demo-data\n * handler emits `RoleAssignment` records keyed on this value, and the\n * in-band records written under it become unreachable if the sentinel\n * changes.\n */\nexport const PLATFORM_SCOPE_TENANT_ID = \"platform\";\n","import {\n assertLinkedDataIdentityCardinality,\n extractSummary,\n type Extension,\n type FhirResourceLike,\n LinkedDataIdentityCardinalityError,\n} from \"@openhi/types\";\nimport {\n buildMembershipUserProjectionItem,\n extractReferenceSlug,\n} from \"./membership-user-projection\";\nimport { buildMembershipWorkspaceProjectionItem } from \"./membership-workspace-projection\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport { ValidationError } from \"../../../errors\";\nimport { OpenHiContext } from \"../../../openhi-context\";\nimport { extractDenormalizedReferenceDisplay } from \"../denormalized-display-names\";\nimport { assertWorkspaceInTenantOperation } from \"../membership-constraints/assert-workspace-in-tenant-operation\";\nimport {\n executeMultiWrite,\n type MultiWriteTriple,\n} from \"../multi-write-operation\";\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 // TR-024 denormalized display-name attributes. The authoritative\n // write-time source per TR-024 rule 2 is the canonical Tenant / User\n // record's `displayName`. Until the carrier-record-lookup pass lands\n // (#1010 follow-up), the foundational fallback (#1009) reads the\n // FHIR Reference.display values supplied on the resource so the\n // adjacency-list projection writer here can compose SKs from\n // top-level attributes.\n const resourceRecord = resource as Record<string, unknown>;\n const denormalizedTenantName = extractDenormalizedReferenceDisplay(\n resourceRecord,\n \"tenant\",\n );\n const denormalizedUserName = extractDenormalizedReferenceDisplay(\n resourceRecord,\n \"user\",\n );\n const denormalizedWorkspaceName = extractDenormalizedReferenceDisplay(\n resourceRecord,\n \"workspace\",\n );\n\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n // ADR-018 patterns #2 / #3 / #4 — user- and workspace-partition\n // projection rows. The projection's discriminating fields come from\n // the same FHIR Reference shape the canonical row reads, so the\n // canonical and projection writes always agree on the underlying\n // user / workspace identifiers. Missing identifiers (no user\n // reference at all) skip the user-projection; tenant-scoped\n // Memberships (no workspaceId) skip the workspace-projection — the\n // canonical row still lands either way.\n const userIdFromResource = extractReferenceSlug(resourceRecord, \"user\");\n const workspaceIdFromResource = extractReferenceSlug(\n resourceRecord,\n \"workspace\",\n );\n\n // ADR 2026-03-13-02 § 2 Consequences/Negative — when the resource\n // names a workspace, that workspace must belong to the referenced\n // tenant. Prefer the resource's `tenant.reference` (the issue's\n // \"referenced tenant\" wording) and fall back to the context tenant\n // when the body omits the reference. The check fires before any\n // write so the constraint is enforced atomically with respect to\n // the canonical row.\n if (workspaceIdFromResource !== undefined) {\n const tenantIdFromResource = extractReferenceSlug(resourceRecord, \"tenant\");\n const referencedTenantId = tenantIdFromResource ?? context.tenantId;\n await assertWorkspaceInTenantOperation({\n tenantId: referencedTenantId,\n workspaceId: workspaceIdFromResource,\n tableName,\n });\n }\n\n const userProjectionItem =\n userIdFromResource !== undefined\n ? buildMembershipUserProjectionItem({\n tenantId: context.tenantId,\n userId: userIdFromResource,\n workspaceId: workspaceIdFromResource,\n membershipId: id,\n summary,\n vid,\n lastUpdated,\n denormalizedTenantName,\n denormalizedUserName,\n denormalizedWorkspaceName,\n })\n : undefined;\n\n const workspaceProjectionItem =\n userIdFromResource !== undefined && workspaceIdFromResource !== undefined\n ? buildMembershipWorkspaceProjectionItem({\n tenantId: context.tenantId,\n workspaceId: workspaceIdFromResource,\n userId: userIdFromResource,\n membershipId: id,\n summary,\n vid,\n lastUpdated,\n denormalizedUserName,\n })\n : undefined;\n\n const canonicalItem = {\n tenantId: context.tenantId,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n linkedDataIdentityRef,\n denormalizedTenantName,\n denormalizedUserName,\n };\n\n const triples: Array<MultiWriteTriple> = [\n { entity: \"membership\", action: \"put\", item: canonicalItem },\n ];\n if (userProjectionItem) {\n triples.push({\n entity: \"membershipUserProjection\",\n action: \"put\",\n item: userProjectionItem as unknown as Record<string, unknown>,\n });\n }\n if (workspaceProjectionItem) {\n triples.push({\n entity: \"membershipWorkspaceProjection\",\n action: \"put\",\n item: workspaceProjectionItem as unknown as Record<string, unknown>,\n });\n }\n\n await executeMultiWrite({ service, triples });\n\n return {\n id,\n resource,\n meta: { lastUpdated, versionId: vid },\n };\n}\n","/**\n * Helpers that capture denormalized display-name attributes from an\n * incoming FHIR Reference on Membership and RoleAssignment resources.\n *\n * **Foundational implementation for TR-024.** ADR-018 § Implementation\n * Notes and TR-024 § Recommendation pin the authoritative write-time\n * source as the carrier entity's canonical `displayName` — i.e. the\n * operations layer reads the canonical Tenant / User / Role record\n * by id and copies its display name into the relationship's\n * `denormalized<CarrierEntity>Name` field on the same\n * `TransactWriteItems`. That contract is owned by the operations-layer\n * multi-write helper filed as #1010.\n *\n * This module is the **foundational fallback** that issue #1009 uses\n * before #1010 lands: it reads the display string a client already\n * supplies on the resource's `Reference.display` field. The fallback\n * keeps Membership / RoleAssignment write paths populating the new\n * top-level attributes today (so adjacency-list projection writers\n * downstream can rely on them) while letting #1010 own the canonical-\n * row lookup without re-plumbing the write paths.\n *\n * @see TR-024 — Denormalized display-name attributes\n * @see ADR-018 § Implementation Notes\n */\n\n/**\n * Returns the trimmed display string from `resource[fieldName].display`\n * when present and non-empty; otherwise returns `undefined`. Used by the\n * Membership and RoleAssignment create / update operations to populate\n * top-level `denormalized<CarrierEntity>Name` attributes from incoming\n * FHIR Reference fields.\n *\n * Field name maps to the FHIR field on the resource:\n * - `Membership.tenant` → `\"tenant\"`\n * - `Membership.user` → `\"user\"`\n * - `RoleAssignment.tenant` → `\"tenant\"`\n * - `RoleAssignment.user` → `\"user\"`\n * - `RoleAssignment.role` → `\"role\"`\n *\n * Guards against malformed payloads (non-object `field`, non-string\n * `display`, empty strings after trim) so a single bad write never\n * blocks an entity put — matching the same defensive posture\n * `gsi1skAttribute` takes for the `resource` JSON parse.\n */\nexport function extractDenormalizedReferenceDisplay(\n resource: Record<string, unknown>,\n fieldName: string,\n): string | undefined {\n const field = resource[fieldName];\n if (!field || typeof field !== \"object\") {\n return undefined;\n }\n const display = (field as { display?: unknown }).display;\n if (typeof display !== \"string\") {\n return undefined;\n }\n const trimmed = display.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n","import { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport { ConflictError } from \"../../../errors\";\n\n/** Inputs to {@link assertWorkspaceInTenantOperation}. */\nexport interface AssertWorkspaceInTenantParams {\n /** Tenant the workspace must belong to. */\n readonly tenantId: string;\n /** Workspace id that must exist under `tenantId`. */\n readonly workspaceId: string;\n /** Optional table-name override; resolved via env when omitted. */\n readonly tableName?: string;\n}\n\n/**\n * Throws {@link ConflictError} when no `Workspace` with id `workspaceId`\n * exists under `tenantId`.\n *\n * Implementation: a single base-table `get` against the Workspace entity\n * (`PK = TID#<tenantId>#WORKSPACE#ID#<workspaceId>, SK = CURRENT`). The\n * workspace's tenant is encoded into the PK by construction, so a hit\n * *is* the constraint check — there is no separate `managingOrganization`\n * comparison required.\n *\n * @see ADR 2026-03-13-02 § 2 — Consequences/Negative\n */\nexport async function assertWorkspaceInTenantOperation(\n params: AssertWorkspaceInTenantParams,\n): Promise<void> {\n const { tenantId, workspaceId, tableName } = params;\n const service = getDynamoControlService(tableName);\n\n const { data: item } = await service.entities.workspace\n .get({ tenantId, id: workspaceId, sk: \"CURRENT\" })\n .go();\n\n if (!item) {\n throw new ConflictError(\n `Workspace ${workspaceId} does not belong to tenant ${tenantId}; the workspace must be created in the referenced tenant before this resource can reference it.`,\n { details: { tenantId, workspaceId } },\n );\n }\n}\n","import { extractSummary, type FhirResourceLike } from \"@openhi/types\";\nimport {\n buildRoleAssignmentUserProjectionItem,\n extractReferenceSlug,\n} from \"./roleassignment-user-projection\";\nimport { buildRoleAssignmentWorkspaceProjectionItem } from \"./roleassignment-workspace-projection\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport { OpenHiContext } from \"../../../openhi-context\";\nimport { extractDenormalizedReferenceDisplay } from \"../denormalized-display-names\";\nimport { assertUserHasTenantMembershipOperation } from \"../membership-constraints/assert-user-has-tenant-membership-operation\";\nimport { assertWorkspaceInTenantOperation } from \"../membership-constraints/assert-workspace-in-tenant-operation\";\nimport {\n executeMultiWrite,\n type MultiWriteTriple,\n} from \"../multi-write-operation\";\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\n // TR-024 denormalized display-name attributes. The authoritative\n // write-time source per TR-024 rule 2 is the canonical Tenant / User /\n // Role record's `displayName`. Until the carrier-record-lookup pass\n // lands (#1010 follow-up), the foundational fallback (#1009) reads\n // the FHIR Reference.display values supplied on the resource so the\n // adjacency-list projection writer here can compose SKs from\n // top-level attributes.\n const resourceRecord = resource as Record<string, unknown>;\n const denormalizedTenantName = extractDenormalizedReferenceDisplay(\n resourceRecord,\n \"tenant\",\n );\n const denormalizedUserName = extractDenormalizedReferenceDisplay(\n resourceRecord,\n \"user\",\n );\n const denormalizedRoleName = extractDenormalizedReferenceDisplay(\n resourceRecord,\n \"role\",\n );\n\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n // ADR-018 patterns #5 / #9 — user- and workspace-partition projection\n // rows. The projection's discriminating fields come from the same\n // FHIR Reference shape the canonical row reads, so the canonical and\n // projection writes always agree on the underlying user / role /\n // workspace identifiers. Missing user or role references skip the\n // user-projection; tenant-scoped RoleAssignments (no workspaceId)\n // skip the workspace-projection — the canonical row still lands\n // either way.\n const userIdFromResource = extractReferenceSlug(resourceRecord, \"user\");\n const roleIdFromResource = extractReferenceSlug(resourceRecord, \"role\");\n const workspaceIdFromResource = extractReferenceSlug(\n resourceRecord,\n \"workspace\",\n );\n\n // ADR 2026-03-13-02 § 2 Consequences/Negative — a RoleAssignment may\n // only be created when the referenced user already holds a\n // tenant-level Membership in the referenced tenant, and (when\n // workspace-scoped) that workspace must belong to the same tenant.\n // Prefer the resource's `tenant.reference` (the issue's \"referenced\n // tenant\" wording) and fall back to the context tenant when the\n // body omits the reference. Constraints fire before any write so\n // the rejection is atomic with respect to the canonical row.\n if (userIdFromResource !== undefined) {\n const tenantIdFromResource = extractReferenceSlug(resourceRecord, \"tenant\");\n const referencedTenantId = tenantIdFromResource ?? context.tenantId;\n await assertUserHasTenantMembershipOperation({\n userId: userIdFromResource,\n tenantId: referencedTenantId,\n tableName,\n });\n if (workspaceIdFromResource !== undefined) {\n await assertWorkspaceInTenantOperation({\n tenantId: referencedTenantId,\n workspaceId: workspaceIdFromResource,\n tableName,\n });\n }\n }\n\n const userProjectionItem =\n userIdFromResource !== undefined && roleIdFromResource !== undefined\n ? buildRoleAssignmentUserProjectionItem({\n tenantId: context.tenantId,\n userId: userIdFromResource,\n workspaceId: workspaceIdFromResource,\n roleId: roleIdFromResource,\n roleAssignmentId: id,\n summary,\n vid,\n lastUpdated,\n denormalizedTenantName,\n denormalizedUserName,\n denormalizedRoleName,\n })\n : undefined;\n\n const workspaceProjectionItem =\n userIdFromResource !== undefined &&\n roleIdFromResource !== undefined &&\n workspaceIdFromResource !== undefined\n ? buildRoleAssignmentWorkspaceProjectionItem({\n tenantId: context.tenantId,\n workspaceId: workspaceIdFromResource,\n userId: userIdFromResource,\n roleId: roleIdFromResource,\n roleAssignmentId: id,\n summary,\n vid,\n lastUpdated,\n denormalizedUserName,\n denormalizedRoleName,\n })\n : undefined;\n\n const canonicalItem = {\n tenantId: context.tenantId,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n denormalizedTenantName,\n denormalizedUserName,\n denormalizedRoleName,\n };\n\n const triples: Array<MultiWriteTriple> = [\n { entity: \"roleAssignment\", action: \"put\", item: canonicalItem },\n ];\n if (userProjectionItem) {\n triples.push({\n entity: \"roleAssignmentUserProjection\",\n action: \"put\",\n item: userProjectionItem as unknown as Record<string, unknown>,\n });\n }\n if (workspaceProjectionItem) {\n triples.push({\n entity: \"roleAssignmentWorkspaceProjection\",\n action: \"put\",\n item: workspaceProjectionItem as unknown as Record<string, unknown>,\n });\n }\n\n await executeMultiWrite({ service, triples });\n\n return {\n id,\n resource,\n meta: { lastUpdated, versionId: vid },\n };\n}\n","import { PLATFORM_SCOPE_TENANT_ID } from \"./platform-scope-tenant-id\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport { ConflictError } from \"../../../errors\";\n\n/**\n * SK prefix for the tenant-level Membership sub-lane (ADR-018 pattern #3).\n *\n * Tenant-level Memberships project under the user partition with\n * `SK = MEMBERSHIP#TENANT#<normalizedTenantName>#TID#<tenantId>#<membershipId>`.\n * Because the `<tenantId>` segment appears *after* `<normalizedTenantName>`,\n * a `begins_with` filter cannot narrow to a single tenant — we read every\n * tenant-lane row for the user and filter on the projection-row's\n * `tenantId` attribute in memory.\n */\nconst TENANT_LANE_SK_PREFIX = \"MEMBERSHIP#TENANT#\";\n\n/** Inputs to {@link assertUserHasTenantMembershipOperation}. */\nexport interface AssertUserHasTenantMembershipParams {\n /** The user that must already be a member of `tenantId`. */\n readonly userId: string;\n /** The tenant the user must hold a tenant-level Membership in. */\n readonly tenantId: string;\n /** Optional table-name override; resolved via env when omitted. */\n readonly tableName?: string;\n}\n\n/**\n * Throws {@link ConflictError} when `userId` has no tenant-level\n * `Membership` in `tenantId`.\n *\n * Implementation: queries the ADR-018 user-partition projection\n * (`MembershipUserProjectionEntity`) under\n * `PK = USER#ID#<userId>, SK begins_with 'MEMBERSHIP#TENANT#'` — a single\n * strongly-consistent base-table query with no GSI hop — and filters the\n * returned rows for `tenantId` in memory.\n *\n * The filter cannot be expressed as an additional `begins_with` because\n * the projection SK encodes `<normalizedTenantName>` *before* `<tenantId>`\n * (`MEMBERSHIP#TENANT#<normalizedTenantName>#TID#<tenantId>#<id>`). The\n * scan cost is bounded by the user's tenant-membership fan-out, which is\n * small by construction.\n *\n * @see ADR 2026-03-13-02 § 2 — Consequences/Negative\n * @see ADR-018 § Access Pattern Coverage (pattern #3)\n */\nexport async function assertUserHasTenantMembershipOperation(\n params: AssertUserHasTenantMembershipParams,\n): Promise<void> {\n const { userId, tenantId, tableName } = params;\n\n // The platform-scope sentinel is not a real tenant — it is a\n // reserved `tenantId` value used on platform-scoped RoleAssignments\n // (e.g. `system-admin`). No `Membership` is ever created under it,\n // and a Membership there would have no semantic meaning. Skip the\n // tenant-membership constraint so platform-scoped RAs can be\n // created without requiring an impossible-to-create prerequisite\n // Membership row.\n if (tenantId === PLATFORM_SCOPE_TENANT_ID) {\n return;\n }\n\n const service = getDynamoControlService(tableName);\n\n const result = await service.entities.membershipUserProjection.query\n .record({ userId })\n .begins({ sk: TENANT_LANE_SK_PREFIX })\n .go();\n\n const matched = (result.data ?? []).some((row) => row.tenantId === tenantId);\n if (!matched) {\n throw new ConflictError(\n `User ${userId} has no tenant-level Membership in tenant ${tenantId}; a Membership must exist before a RoleAssignment can be created.`,\n { details: { userId, tenantId } },\n );\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 type { Organization, Reference } from \"@openhi/types\";\nimport { getDynamoDataService } from \"../../../dynamo/dynamo-data-service\";\nimport type { OpenHiContext } from \"../../../openhi-context\";\nimport {\n createDataEntityRecord,\n DATA_ENTITY_SK,\n type SingleResourceResult,\n} from \"../../data-operations-common\";\n\n/**\n * Provision the slim, id-share Organization that represents a Workspace on the data plane.\n *\n * - `Organization.id === workspaceId` (id-share with the Workspace).\n * - `name` is populated from the Workspace's name when provided.\n * - `partOf` is populated when the Workspace's Tenant has an Organization (id-share at the\n * tenant level: `Tenant.id === TenantOrganization.id`). Tenant-level provisioning is out of\n * scope for #1001 — the lookup returns `undefined` until Tenant Organizations exist.\n *\n * Idempotent: re-running with the same params overwrites the same PK with the same payload.\n *\n * @see https://github.com/codedrifters/openhi/issues/1001\n */\nexport interface ProvisionOrganizationForWorkspaceParams {\n context: OpenHiContext;\n /** Workspace id; the provisioned Organization's id equals this value. */\n workspaceId: string;\n /** Name to record on the Organization. Derived from the Workspace's `name` by the caller. */\n workspaceName?: string;\n /** Optional table name override; resolved by data service from DYNAMO_TABLE_NAME when omitted. */\n tableName?: string;\n}\n\nexport type ProvisionOrganizationForWorkspaceResult =\n SingleResourceResult<Organization>;\n\nexport async function provisionOrganizationForWorkspaceOperation(\n params: ProvisionOrganizationForWorkspaceParams,\n): Promise<ProvisionOrganizationForWorkspaceResult> {\n const { context, workspaceId, workspaceName, tableName } = params;\n const { tenantId, date } = context;\n const service = getDynamoDataService(tableName);\n\n const partOf = await resolveTenantOrganizationReference(service, tenantId);\n\n const resource: Organization = {\n resourceType: \"Organization\",\n id: workspaceId,\n ...(workspaceName !== undefined && workspaceName !== \"\"\n ? { name: workspaceName }\n : {}),\n ...(partOf !== undefined ? { partOf } : {}),\n meta: {\n lastUpdated: date,\n versionId: \"1\",\n },\n };\n\n return createDataEntityRecord<Organization>(\n service.entities.organization as Parameters<\n typeof createDataEntityRecord\n >[0],\n tenantId,\n workspaceId,\n workspaceId,\n resource,\n date,\n );\n}\n\n/**\n * Resolve the Tenant's Organization reference for `Organization.partOf`. Returns `undefined`\n * when the Tenant has no Organization yet (the default until Tenant Organization provisioning\n * is wired up in a follow-up).\n *\n * Lookup convention: the Tenant's Organization is stored at\n * `(tenantId, workspaceId=tenantId, id=tenantId)` — a self-scope key that mirrors the\n * Workspace id-share pattern one level up. This scoping convention will be ratified by the\n * Tenant Organization provisioning issue; the OrganizationEntity comment notes strict\n * isolation requires both `tenantId` and `workspaceId`, so the self-scope is the\n * simplest deterministic location.\n */\nasync function resolveTenantOrganizationReference(\n service: ReturnType<typeof getDynamoDataService>,\n tenantId: string,\n): Promise<Reference | undefined> {\n const result = await (\n service.entities.organization as {\n get(params: {\n tenantId: string;\n workspaceId: string;\n id: string;\n sk: string;\n }): { go(): Promise<{ data: unknown }> };\n }\n )\n .get({\n tenantId,\n workspaceId: tenantId,\n id: tenantId,\n sk: DATA_ENTITY_SK,\n })\n .go();\n\n if (result.data === null || result.data === undefined) {\n return undefined;\n }\n\n return { reference: `Organization/${tenantId}` };\n}\n"],"mappings":"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","names":["extractSummary","extractSummary","extractReferenceSlug","extractSummary","extractSummary","extractSummary","extractSummary"]}
@@ -1,212 +0,0 @@
1
- import {
2
- createDataEntityRecord,
3
- getDynamoDataService,
4
- mergeAuditIntoMeta
5
- } from "./chunk-MVQWAIMC.mjs";
6
- import {
7
- NotFoundError
8
- } from "./chunk-FYHBHHWK.mjs";
9
- import {
10
- getDynamoControlService
11
- } from "./chunk-VZCPGQXA.mjs";
12
-
13
- // src/data/operations/control/role/role-get-by-id-operation.ts
14
- async function getRoleByIdOperation(params) {
15
- const { id, tableName } = params;
16
- const service = getDynamoControlService(tableName);
17
- const response = await service.entities.role.get({ id, sk: "CURRENT" }).go();
18
- const item = response.data;
19
- if (!item) {
20
- throw new NotFoundError(`Role not found: ${id}`);
21
- }
22
- const parsedResource = JSON.parse(item.resource);
23
- return {
24
- id,
25
- resource: { resourceType: "Role", id, ...parsedResource }
26
- };
27
- }
28
-
29
- // src/data/operations/data/account/account-create-operation.ts
30
- import { ulid } from "ulid";
31
- async function createAccountOperation(params) {
32
- const { context, body, tableName } = params;
33
- const { tenantId, workspaceId, date, actorId, actorName } = context;
34
- const id = body.id ?? ulid();
35
- const meta = {
36
- ...body.meta ?? {},
37
- lastUpdated: date,
38
- versionId: "1"
39
- };
40
- const accountWithAudit = {
41
- ...body,
42
- resourceType: "Account",
43
- id,
44
- meta: mergeAuditIntoMeta(meta, {
45
- createdDate: date,
46
- createdById: actorId,
47
- createdByName: actorName,
48
- modifiedDate: date,
49
- modifiedById: actorId,
50
- modifiedByName: actorName
51
- })
52
- };
53
- const service = getDynamoDataService(tableName);
54
- return createDataEntityRecord(
55
- service.entities.account,
56
- tenantId,
57
- workspaceId,
58
- id,
59
- accountWithAudit,
60
- date
61
- );
62
- }
63
-
64
- // src/data/operations/data/encounter/encounter-create-operation.ts
65
- import { ulid as ulid2 } from "ulid";
66
- async function createEncounterOperation(params) {
67
- const { context, body, tableName } = params;
68
- const { tenantId, workspaceId, date, actorId, actorName } = context;
69
- const id = body.id ?? ulid2();
70
- const meta = {
71
- ...body.meta ?? {},
72
- lastUpdated: date,
73
- versionId: "1"
74
- };
75
- const encounterWithAudit = {
76
- ...body,
77
- resourceType: "Encounter",
78
- id,
79
- meta: mergeAuditIntoMeta(meta, {
80
- createdDate: date,
81
- createdById: actorId,
82
- createdByName: actorName,
83
- modifiedDate: date,
84
- modifiedById: actorId,
85
- modifiedByName: actorName
86
- })
87
- };
88
- const service = getDynamoDataService(tableName);
89
- return createDataEntityRecord(
90
- service.entities.encounter,
91
- tenantId,
92
- workspaceId,
93
- id,
94
- encounterWithAudit,
95
- date
96
- );
97
- }
98
-
99
- // src/data/operations/data/observation/observation-create-operation.ts
100
- import { ulid as ulid3 } from "ulid";
101
- async function createObservationOperation(params) {
102
- const { context, body, tableName } = params;
103
- const { tenantId, workspaceId, date, actorId, actorName } = context;
104
- const id = body.id ?? ulid3();
105
- const meta = {
106
- ...body.meta ?? {},
107
- lastUpdated: date,
108
- versionId: "1"
109
- };
110
- const resourceWithAudit = {
111
- ...body,
112
- resourceType: "Observation",
113
- id,
114
- meta: mergeAuditIntoMeta(meta, {
115
- createdDate: date,
116
- createdById: actorId,
117
- createdByName: actorName,
118
- modifiedDate: date,
119
- modifiedById: actorId,
120
- modifiedByName: actorName
121
- })
122
- };
123
- const service = getDynamoDataService(tableName);
124
- return createDataEntityRecord(
125
- service.entities.observation,
126
- tenantId,
127
- workspaceId,
128
- id,
129
- resourceWithAudit,
130
- date
131
- );
132
- }
133
-
134
- // src/data/operations/data/patient/patient-create-operation.ts
135
- import { ulid as ulid4 } from "ulid";
136
- async function createPatientOperation(params) {
137
- const { context, body, tableName } = params;
138
- const { tenantId, workspaceId, date, actorId, actorName } = context;
139
- const id = body.id ?? ulid4();
140
- const meta = {
141
- ...body.meta ?? {},
142
- lastUpdated: date,
143
- versionId: "1"
144
- };
145
- const patientWithAudit = {
146
- ...body,
147
- resourceType: "Patient",
148
- id,
149
- meta: mergeAuditIntoMeta(meta, {
150
- createdDate: date,
151
- createdById: actorId,
152
- createdByName: actorName,
153
- modifiedDate: date,
154
- modifiedById: actorId,
155
- modifiedByName: actorName
156
- })
157
- };
158
- const service = getDynamoDataService(tableName);
159
- return createDataEntityRecord(
160
- service.entities.patient,
161
- tenantId,
162
- workspaceId,
163
- id,
164
- patientWithAudit,
165
- date
166
- );
167
- }
168
-
169
- // src/data/operations/data/practitioner/practitioner-create-operation.ts
170
- import { ulid as ulid5 } from "ulid";
171
- async function createPractitionerOperation(params) {
172
- const { context, body, tableName } = params;
173
- const { tenantId, workspaceId, date, actorId, actorName } = context;
174
- const id = body.id ?? ulid5();
175
- const meta = {
176
- ...body.meta ?? {},
177
- lastUpdated: date,
178
- versionId: "1"
179
- };
180
- const practitionerWithAudit = {
181
- ...body,
182
- resourceType: "Practitioner",
183
- id,
184
- meta: mergeAuditIntoMeta(meta, {
185
- createdDate: date,
186
- createdById: actorId,
187
- createdByName: actorName,
188
- modifiedDate: date,
189
- modifiedById: actorId,
190
- modifiedByName: actorName
191
- })
192
- };
193
- const service = getDynamoDataService(tableName);
194
- return createDataEntityRecord(
195
- service.entities.practitioner,
196
- tenantId,
197
- workspaceId,
198
- id,
199
- practitionerWithAudit,
200
- date
201
- );
202
- }
203
-
204
- export {
205
- getRoleByIdOperation,
206
- createAccountOperation,
207
- createEncounterOperation,
208
- createObservationOperation,
209
- createPatientOperation,
210
- createPractitionerOperation
211
- };
212
- //# sourceMappingURL=chunk-E6MCKJVS.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/data/operations/control/role/role-get-by-id-operation.ts","../src/data/operations/data/account/account-create-operation.ts","../src/data/operations/data/encounter/encounter-create-operation.ts","../src/data/operations/data/observation/observation-create-operation.ts","../src/data/operations/data/patient/patient-create-operation.ts","../src/data/operations/data/practitioner/practitioner-create-operation.ts"],"sourcesContent":["import { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport { NotFoundError } from \"../../../errors\";\nimport { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface RoleGetByIdParams {\n context: OpenHiContext;\n id: string;\n tableName?: string;\n}\n\nexport interface RoleGetByIdResult {\n id: string;\n resource: { resourceType: string; id: string; [key: string]: unknown };\n}\n\nexport async function getRoleByIdOperation(\n params: RoleGetByIdParams,\n): Promise<RoleGetByIdResult> {\n const { id, tableName } = params;\n const service = getDynamoControlService(tableName);\n\n const response = await service.entities.role.get({ id, sk: \"CURRENT\" }).go();\n\n const item = response.data;\n if (!item) {\n throw new NotFoundError(`Role not found: ${id}`);\n }\n\n const parsedResource = JSON.parse(item.resource) as Record<string, unknown>;\n\n return {\n id,\n resource: { resourceType: \"Role\", id, ...parsedResource },\n };\n}\n","import type { Account, Meta } from \"@openhi/types\";\nimport { ulid } from \"ulid\";\nimport {\n mergeAuditIntoMeta,\n type MetaWithExtensions,\n} from \"../../../audit-meta\";\nimport { getDynamoDataService } from \"../../../dynamo/dynamo-data-service\";\nimport type { OpenHiContext } from \"../../../openhi-context\";\nimport {\n createDataEntityRecord,\n type SingleResourceResult,\n} from \"../../data-operations-common\";\n\n/**\n * Create an Account. Accepts FHIR-like body and OpenHI context; returns created resource.\n *\n * @see sites/www-docs/content/packages/@openhi/constructs/data/shared-data-layer-layout.md\n */\nexport interface CreateAccountParams {\n context: OpenHiContext;\n /** FHIR Account body (resourceType, id optional, meta optional). */\n body: Account;\n /** Optional table name override; resolved by data service from DYNAMO_TABLE_NAME when omitted. */\n tableName?: string;\n}\n\nexport type CreateAccountResult = SingleResourceResult<Account>;\n\n/**\n * Creates an Account. Builds put attrs with audit in meta, then ElectroDB put.\n * Throws on service/validation errors; adapters map to HTTP/GraphQL.\n */\nexport async function createAccountOperation(\n params: CreateAccountParams,\n): Promise<CreateAccountResult> {\n const { context, body, tableName } = params;\n const { tenantId, workspaceId, date, actorId, actorName } = context;\n const id = body.id ?? ulid();\n\n const meta: Meta = {\n ...(body.meta ?? {}),\n lastUpdated: date,\n versionId: \"1\",\n };\n\n const accountWithAudit: Account & {\n id: string;\n meta: MetaWithExtensions;\n } = {\n ...body,\n resourceType: \"Account\",\n id,\n meta: mergeAuditIntoMeta(meta, {\n createdDate: date,\n createdById: actorId,\n createdByName: actorName,\n modifiedDate: date,\n modifiedById: actorId,\n modifiedByName: actorName,\n }),\n };\n\n const service = getDynamoDataService(tableName);\n return createDataEntityRecord<Account>(\n service.entities.account as Parameters<typeof createDataEntityRecord>[0],\n tenantId,\n workspaceId,\n id,\n accountWithAudit,\n date,\n );\n}\n","import type { Encounter, Meta } from \"@openhi/types\";\nimport { ulid } from \"ulid\";\nimport {\n mergeAuditIntoMeta,\n type MetaWithExtensions,\n} from \"../../../audit-meta\";\nimport { getDynamoDataService } from \"../../../dynamo/dynamo-data-service\";\nimport type { OpenHiContext } from \"../../../openhi-context\";\nimport {\n createDataEntityRecord,\n type SingleResourceResult,\n} from \"../../data-operations-common\";\n\n/**\n * Create an Encounter. Accepts FHIR-like body and OpenHI context; returns created resource.\n *\n * @see sites/www-docs/content/packages/@openhi/constructs/data/shared-data-layer-layout.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entities/encounter.md\n */\nexport interface CreateEncounterParams {\n context: OpenHiContext;\n /** FHIR Encounter body (resourceType, id optional, meta optional). */\n body: Encounter;\n /** Optional table name override; resolved by data service from DYNAMO_TABLE_NAME when omitted. */\n tableName?: string;\n}\n\nexport type CreateEncounterResult = SingleResourceResult<Encounter>;\n\n/**\n * Creates an Encounter. Builds put attrs with audit in meta, then ElectroDB put.\n * Throws on service/validation errors; adapters map to HTTP/GraphQL.\n */\nexport async function createEncounterOperation(\n params: CreateEncounterParams,\n): Promise<CreateEncounterResult> {\n const { context, body, tableName } = params;\n const { tenantId, workspaceId, date, actorId, actorName } = context;\n const id = body.id ?? ulid();\n\n const meta: Meta = {\n ...(body.meta ?? {}),\n lastUpdated: date,\n versionId: \"1\",\n };\n\n const encounterWithAudit: Encounter & {\n id: string;\n meta: MetaWithExtensions;\n } = {\n ...body,\n resourceType: \"Encounter\",\n id,\n meta: mergeAuditIntoMeta(meta, {\n createdDate: date,\n createdById: actorId,\n createdByName: actorName,\n modifiedDate: date,\n modifiedById: actorId,\n modifiedByName: actorName,\n }),\n };\n\n const service = getDynamoDataService(tableName);\n return createDataEntityRecord<Encounter>(\n service.entities.encounter as Parameters<typeof createDataEntityRecord>[0],\n tenantId,\n workspaceId,\n id,\n encounterWithAudit,\n date,\n );\n}\n","import type { Observation, Meta } from \"@openhi/types\";\nimport { ulid } from \"ulid\";\nimport {\n mergeAuditIntoMeta,\n type MetaWithExtensions,\n} from \"../../../audit-meta\";\nimport { getDynamoDataService } from \"../../../dynamo/dynamo-data-service\";\nimport type { OpenHiContext } from \"../../../openhi-context\";\nimport {\n createDataEntityRecord,\n type SingleResourceResult,\n} from \"../../data-operations-common\";\n\n/**\n * Create a Observation. Accepts FHIR-like body and OpenHI context; returns created resource.\n *\n * @see sites/www-docs/content/packages/@openhi/constructs/data/shared-data-layer-layout.md\n */\nexport interface CreateObservationParams {\n context: OpenHiContext;\n /** FHIR Observation body (resourceType, id optional, meta optional). */\n body: Observation;\n /** Optional table name override; resolved by data service from DYNAMO_TABLE_NAME when omitted. */\n tableName?: string;\n}\n\nexport type CreateObservationResult = SingleResourceResult<Observation>;\n\n/**\n * Creates a Observation. Builds put attrs with audit in meta, then ElectroDB put.\n * Throws on service/validation errors; adapters map to HTTP/GraphQL.\n */\nexport async function createObservationOperation(\n params: CreateObservationParams,\n): Promise<CreateObservationResult> {\n const { context, body, tableName } = params;\n const { tenantId, workspaceId, date, actorId, actorName } = context;\n const id = body.id ?? ulid();\n\n const meta: Meta = {\n ...(body.meta ?? {}),\n lastUpdated: date,\n versionId: \"1\",\n };\n\n const resourceWithAudit: Observation & {\n id: string;\n meta: MetaWithExtensions;\n } = {\n ...body,\n resourceType: \"Observation\",\n id,\n meta: mergeAuditIntoMeta(meta, {\n createdDate: date,\n createdById: actorId,\n createdByName: actorName,\n modifiedDate: date,\n modifiedById: actorId,\n modifiedByName: actorName,\n }),\n };\n\n const service = getDynamoDataService(tableName);\n return createDataEntityRecord<Observation>(\n service.entities.observation as Parameters<\n typeof createDataEntityRecord\n >[0],\n tenantId,\n workspaceId,\n id,\n resourceWithAudit,\n date,\n );\n}\n","import type { Meta, Patient } from \"@openhi/types\";\nimport { ulid } from \"ulid\";\nimport {\n mergeAuditIntoMeta,\n type MetaWithExtensions,\n} from \"../../../audit-meta\";\nimport { getDynamoDataService } from \"../../../dynamo/dynamo-data-service\";\nimport type { OpenHiContext } from \"../../../openhi-context\";\nimport {\n createDataEntityRecord,\n type SingleResourceResult,\n} from \"../../data-operations-common\";\n\n/**\n * Create a Patient. Accepts FHIR-like body and OpenHI context; returns created resource.\n *\n * @see sites/www-docs/content/packages/@openhi/constructs/data/shared-data-layer-layout.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/operations/patient/patient-create.md\n */\nexport interface CreatePatientParams {\n context: OpenHiContext;\n /** FHIR Patient body (resourceType, id optional, meta optional). */\n body: Patient;\n /** Optional table name override; resolved by data service from DYNAMO_TABLE_NAME when omitted. */\n tableName?: string;\n}\n\nexport type CreatePatientResult = SingleResourceResult<Patient>;\n\n/**\n * Creates a Patient. Builds put attrs with audit in meta, then ElectroDB put.\n * Throws on service/validation errors; adapters map to HTTP/GraphQL.\n */\nexport async function createPatientOperation(\n params: CreatePatientParams,\n): Promise<CreatePatientResult> {\n const { context, body, tableName } = params;\n const { tenantId, workspaceId, date, actorId, actorName } = context;\n const id = body.id ?? ulid();\n\n const meta: Meta = {\n ...(body.meta ?? {}),\n lastUpdated: date,\n versionId: \"1\",\n };\n\n const patientWithAudit: Patient & {\n id: string;\n meta: MetaWithExtensions;\n } = {\n ...body,\n resourceType: \"Patient\",\n id,\n meta: mergeAuditIntoMeta(meta, {\n createdDate: date,\n createdById: actorId,\n createdByName: actorName,\n modifiedDate: date,\n modifiedById: actorId,\n modifiedByName: actorName,\n }),\n };\n\n const service = getDynamoDataService(tableName);\n return createDataEntityRecord<Patient>(\n service.entities.patient as Parameters<typeof createDataEntityRecord>[0],\n tenantId,\n workspaceId,\n id,\n patientWithAudit,\n date,\n );\n}\n","import type { Meta, Practitioner } from \"@openhi/types\";\nimport { ulid } from \"ulid\";\nimport {\n mergeAuditIntoMeta,\n type MetaWithExtensions,\n} from \"../../../audit-meta\";\nimport { getDynamoDataService } from \"../../../dynamo/dynamo-data-service\";\nimport type { OpenHiContext } from \"../../../openhi-context\";\nimport {\n createDataEntityRecord,\n type SingleResourceResult,\n} from \"../../data-operations-common\";\n\n/**\n * Create a Practitioner. Accepts FHIR-like body and OpenHI context; returns created resource.\n *\n * @see sites/www-docs/content/packages/@openhi/constructs/data/shared-data-layer-layout.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entities/practitioner.md\n */\nexport interface CreatePractitionerParams {\n context: OpenHiContext;\n /** FHIR Practitioner body (resourceType, id optional, meta optional). */\n body: Practitioner;\n /** Optional table name override; resolved by data service from DYNAMO_TABLE_NAME when omitted. */\n tableName?: string;\n}\n\nexport type CreatePractitionerResult = SingleResourceResult<Practitioner>;\n\n/**\n * Creates a Practitioner. Builds put attrs with audit in meta, then ElectroDB put.\n * Throws on service/validation errors; adapters map to HTTP/GraphQL.\n */\nexport async function createPractitionerOperation(\n params: CreatePractitionerParams,\n): Promise<CreatePractitionerResult> {\n const { context, body, tableName } = params;\n const { tenantId, workspaceId, date, actorId, actorName } = context;\n const id = body.id ?? ulid();\n\n const meta: Meta = {\n ...(body.meta ?? {}),\n lastUpdated: date,\n versionId: \"1\",\n };\n\n const practitionerWithAudit: Practitioner & {\n id: string;\n meta: MetaWithExtensions;\n } = {\n ...body,\n resourceType: \"Practitioner\",\n id,\n meta: mergeAuditIntoMeta(meta, {\n createdDate: date,\n createdById: actorId,\n createdByName: actorName,\n modifiedDate: date,\n modifiedById: actorId,\n modifiedByName: actorName,\n }),\n };\n\n const service = getDynamoDataService(tableName);\n return createDataEntityRecord<Practitioner>(\n service.entities.practitioner as Parameters<\n typeof createDataEntityRecord\n >[0],\n tenantId,\n workspaceId,\n id,\n practitionerWithAudit,\n date,\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,eAAsB,qBACpB,QAC4B;AAC5B,QAAM,EAAE,IAAI,UAAU,IAAI;AAC1B,QAAM,UAAU,wBAAwB,SAAS;AAEjD,QAAM,WAAW,MAAM,QAAQ,SAAS,KAAK,IAAI,EAAE,IAAI,IAAI,UAAU,CAAC,EAAE,GAAG;AAE3E,QAAM,OAAO,SAAS;AACtB,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,cAAc,mBAAmB,EAAE,EAAE;AAAA,EACjD;AAEA,QAAM,iBAAiB,KAAK,MAAM,KAAK,QAAQ;AAE/C,SAAO;AAAA,IACL;AAAA,IACA,UAAU,EAAE,cAAc,QAAQ,IAAI,GAAG,eAAe;AAAA,EAC1D;AACF;;;ACjCA,SAAS,YAAY;AA+BrB,eAAsB,uBACpB,QAC8B;AAC9B,QAAM,EAAE,SAAS,MAAM,UAAU,IAAI;AACrC,QAAM,EAAE,UAAU,aAAa,MAAM,SAAS,UAAU,IAAI;AAC5D,QAAM,KAAK,KAAK,MAAM,KAAK;AAE3B,QAAM,OAAa;AAAA,IACjB,GAAI,KAAK,QAAQ,CAAC;AAAA,IAClB,aAAa;AAAA,IACb,WAAW;AAAA,EACb;AAEA,QAAM,mBAGF;AAAA,IACF,GAAG;AAAA,IACH,cAAc;AAAA,IACd;AAAA,IACA,MAAM,mBAAmB,MAAM;AAAA,MAC7B,aAAa;AAAA,MACb,aAAa;AAAA,MACb,eAAe;AAAA,MACf,cAAc;AAAA,MACd,cAAc;AAAA,MACd,gBAAgB;AAAA,IAClB,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,qBAAqB,SAAS;AAC9C,SAAO;AAAA,IACL,QAAQ,SAAS;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACtEA,SAAS,QAAAA,aAAY;AAgCrB,eAAsB,yBACpB,QACgC;AAChC,QAAM,EAAE,SAAS,MAAM,UAAU,IAAI;AACrC,QAAM,EAAE,UAAU,aAAa,MAAM,SAAS,UAAU,IAAI;AAC5D,QAAM,KAAK,KAAK,MAAMC,MAAK;AAE3B,QAAM,OAAa;AAAA,IACjB,GAAI,KAAK,QAAQ,CAAC;AAAA,IAClB,aAAa;AAAA,IACb,WAAW;AAAA,EACb;AAEA,QAAM,qBAGF;AAAA,IACF,GAAG;AAAA,IACH,cAAc;AAAA,IACd;AAAA,IACA,MAAM,mBAAmB,MAAM;AAAA,MAC7B,aAAa;AAAA,MACb,aAAa;AAAA,MACb,eAAe;AAAA,MACf,cAAc;AAAA,MACd,cAAc;AAAA,MACd,gBAAgB;AAAA,IAClB,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,qBAAqB,SAAS;AAC9C,SAAO;AAAA,IACL,QAAQ,SAAS;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACvEA,SAAS,QAAAC,aAAY;AA+BrB,eAAsB,2BACpB,QACkC;AAClC,QAAM,EAAE,SAAS,MAAM,UAAU,IAAI;AACrC,QAAM,EAAE,UAAU,aAAa,MAAM,SAAS,UAAU,IAAI;AAC5D,QAAM,KAAK,KAAK,MAAMC,MAAK;AAE3B,QAAM,OAAa;AAAA,IACjB,GAAI,KAAK,QAAQ,CAAC;AAAA,IAClB,aAAa;AAAA,IACb,WAAW;AAAA,EACb;AAEA,QAAM,oBAGF;AAAA,IACF,GAAG;AAAA,IACH,cAAc;AAAA,IACd;AAAA,IACA,MAAM,mBAAmB,MAAM;AAAA,MAC7B,aAAa;AAAA,MACb,aAAa;AAAA,MACb,eAAe;AAAA,MACf,cAAc;AAAA,MACd,cAAc;AAAA,MACd,gBAAgB;AAAA,IAClB,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,qBAAqB,SAAS;AAC9C,SAAO;AAAA,IACL,QAAQ,SAAS;AAAA,IAGjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACxEA,SAAS,QAAAC,aAAY;AAgCrB,eAAsB,uBACpB,QAC8B;AAC9B,QAAM,EAAE,SAAS,MAAM,UAAU,IAAI;AACrC,QAAM,EAAE,UAAU,aAAa,MAAM,SAAS,UAAU,IAAI;AAC5D,QAAM,KAAK,KAAK,MAAMC,MAAK;AAE3B,QAAM,OAAa;AAAA,IACjB,GAAI,KAAK,QAAQ,CAAC;AAAA,IAClB,aAAa;AAAA,IACb,WAAW;AAAA,EACb;AAEA,QAAM,mBAGF;AAAA,IACF,GAAG;AAAA,IACH,cAAc;AAAA,IACd;AAAA,IACA,MAAM,mBAAmB,MAAM;AAAA,MAC7B,aAAa;AAAA,MACb,aAAa;AAAA,MACb,eAAe;AAAA,MACf,cAAc;AAAA,MACd,cAAc;AAAA,MACd,gBAAgB;AAAA,IAClB,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,qBAAqB,SAAS;AAC9C,SAAO;AAAA,IACL,QAAQ,SAAS;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACvEA,SAAS,QAAAC,aAAY;AAgCrB,eAAsB,4BACpB,QACmC;AACnC,QAAM,EAAE,SAAS,MAAM,UAAU,IAAI;AACrC,QAAM,EAAE,UAAU,aAAa,MAAM,SAAS,UAAU,IAAI;AAC5D,QAAM,KAAK,KAAK,MAAMC,MAAK;AAE3B,QAAM,OAAa;AAAA,IACjB,GAAI,KAAK,QAAQ,CAAC;AAAA,IAClB,aAAa;AAAA,IACb,WAAW;AAAA,EACb;AAEA,QAAM,wBAGF;AAAA,IACF,GAAG;AAAA,IACH,cAAc;AAAA,IACd;AAAA,IACA,MAAM,mBAAmB,MAAM;AAAA,MAC7B,aAAa;AAAA,MACb,aAAa;AAAA,MACb,eAAe;AAAA,MACf,cAAc;AAAA,MACd,cAAc;AAAA,MACd,gBAAgB;AAAA,IAClB,CAAC;AAAA,EACH;AAEA,QAAM,UAAU,qBAAqB,SAAS;AAC9C,SAAO;AAAA,IACL,QAAQ,SAAS;AAAA,IAGjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":["ulid","ulid","ulid","ulid","ulid","ulid","ulid","ulid"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/data/operations/control/membership/membership-user-projection.ts","../src/data/operations/control/membership/membership-workspace-projection.ts","../src/data/operations/control/roleassignment/roleassignment-user-projection.ts","../src/data/operations/control/roleassignment/roleassignment-workspace-projection.ts"],"sourcesContent":["/**\n * Membership user-projection composer.\n *\n * Owns the SK grammar for ADR-018 patterns #3 and #4 and assembles the\n * projection-row payloads consumed by the membership create / update /\n * delete operations. The {@link MembershipUserProjectionEntity} stores\n * the SK verbatim — the grammar lives here so the operations layer is\n * the single source of truth for projection-row shape (per\n * `.claude/rules/data-layer-layout.md`).\n *\n * SK grammar:\n *\n * - **Pattern #3** (tenant sub-lane, `workspaceId` absent):\n * `MEMBERSHIP#TENANT#<normalizedTenantName>#TID#<tenantId>#<membershipId>`\n * - **Pattern #4** (workspace sub-lane, `workspaceId` set):\n * `MEMBERSHIP#WORKSPACE#TID#<tenantId>#<normalizedWorkspaceName>#WID#<workspaceId>#<membershipId>`\n *\n * Both patterns share the user partition `PK = USER#ID#<userId>` so\n * `Query(PK = USER#ID#<userId>, SK begins_with 'MEMBERSHIP#')` returns\n * both lanes interleaved.\n *\n * @see ADR-018 § Access Pattern Coverage (patterns #3 and #4)\n * @see .state/adr-018-implementation-guide.md § 1 (SK grammar) and § 2 (attribute set)\n */\n\nimport { normalizeLabel } from \"@openhi/types\";\n\n/**\n * Sentinel rendered into the SK when the source display name is missing\n * or empty. Keeps the SK shape stable so a `begins_with` prefix query\n * still matches the row; the rename-cascade pipeline (TR-023) will\n * rewrite the SK once the carrier display name lands. Matches the\n * `gsi1skAttribute` defensive posture — a missing source field never\n * produces a malformed key.\n */\nconst MISSING_NAME_SENTINEL = \"-\";\n\n/** Inputs to compose a Membership user-projection row. */\nexport interface MembershipUserProjectionInput {\n readonly tenantId: string;\n readonly userId: string;\n readonly workspaceId?: string;\n readonly membershipId: string;\n readonly summary: string;\n readonly vid: string;\n readonly lastUpdated: string;\n readonly denormalizedTenantName?: string;\n readonly denormalizedUserName?: string;\n readonly denormalizedWorkspaceName?: string;\n}\n\n/** A projection-row payload ready for `multi-write` consumption. */\nexport interface MembershipUserProjectionItem {\n readonly userId: string;\n readonly sk: string;\n readonly tenantId: string;\n readonly workspaceId?: string;\n readonly membershipId: string;\n readonly summary: string;\n readonly vid: string;\n readonly lastUpdated: string;\n readonly denormalizedTenantName?: string;\n readonly denormalizedUserName?: string;\n readonly denormalizedWorkspaceName?: string;\n}\n\n/**\n * Compose the SK for ADR-018 pattern #3 (tenant sub-lane). The\n * `<normalizedTenantName>` segment sorts memberships alphabetically by\n * tenant name within the user's partition. Missing `denormalizedTenantName`\n * falls back to {@link MISSING_NAME_SENTINEL} so the SK shape stays valid\n * pre-rename-cascade.\n */\nexport function buildMembershipUserProjectionSkTenantLane(params: {\n readonly tenantId: string;\n readonly membershipId: string;\n readonly denormalizedTenantName?: string;\n}): string {\n const normalizedTenantName =\n typeof params.denormalizedTenantName === \"string\" &&\n params.denormalizedTenantName.length > 0\n ? normalizeLabel(params.denormalizedTenantName)\n : MISSING_NAME_SENTINEL;\n return `MEMBERSHIP#TENANT#${normalizedTenantName}#TID#${params.tenantId}#${params.membershipId}`;\n}\n\n/**\n * Compose the SK for ADR-018 pattern #4 (workspace sub-lane). `tenantId`\n * appears before `<normalizedWorkspaceName>` so a\n * `begins_with('MEMBERSHIP#WORKSPACE#TID#<tenantId>#')` query filters\n * by one tenant. Missing `denormalizedWorkspaceName` falls back to\n * {@link MISSING_NAME_SENTINEL}.\n */\nexport function buildMembershipUserProjectionSkWorkspaceLane(params: {\n readonly tenantId: string;\n readonly workspaceId: string;\n readonly membershipId: string;\n readonly denormalizedWorkspaceName?: string;\n}): string {\n const normalizedWorkspaceName =\n typeof params.denormalizedWorkspaceName === \"string\" &&\n params.denormalizedWorkspaceName.length > 0\n ? normalizeLabel(params.denormalizedWorkspaceName)\n : MISSING_NAME_SENTINEL;\n return `MEMBERSHIP#WORKSPACE#TID#${params.tenantId}#${normalizedWorkspaceName}#WID#${params.workspaceId}#${params.membershipId}`;\n}\n\n/**\n * Builds the projection item for the access lane implied by the input.\n * Pattern #3 when `workspaceId` is absent or empty; pattern #4 otherwise.\n * Returns `undefined` when `userId` is missing — a Membership without a\n * linked user cannot project onto the user partition.\n */\nexport function buildMembershipUserProjectionItem(\n input: MembershipUserProjectionInput,\n): MembershipUserProjectionItem | undefined {\n if (!input.userId || input.userId.length === 0) {\n return undefined;\n }\n const hasWorkspace =\n typeof input.workspaceId === \"string\" && input.workspaceId.length > 0;\n const sk = hasWorkspace\n ? buildMembershipUserProjectionSkWorkspaceLane({\n tenantId: input.tenantId,\n workspaceId: input.workspaceId as string,\n membershipId: input.membershipId,\n denormalizedWorkspaceName: input.denormalizedWorkspaceName,\n })\n : buildMembershipUserProjectionSkTenantLane({\n tenantId: input.tenantId,\n membershipId: input.membershipId,\n denormalizedTenantName: input.denormalizedTenantName,\n });\n return {\n userId: input.userId,\n sk,\n tenantId: input.tenantId,\n workspaceId: hasWorkspace ? input.workspaceId : undefined,\n membershipId: input.membershipId,\n summary: input.summary,\n vid: input.vid,\n lastUpdated: input.lastUpdated,\n denormalizedTenantName: input.denormalizedTenantName,\n denormalizedUserName: input.denormalizedUserName,\n denormalizedWorkspaceName: hasWorkspace\n ? input.denormalizedWorkspaceName\n : undefined,\n };\n}\n\n/**\n * Extracts a FHIR `Reference` slug — the segment after the final `/`.\n * Returns `undefined` when the reference is missing or malformed so\n * callers fall back gracefully (matches the defensive posture in\n * `extractRoleId` / `extractDenormalizedReferenceDisplay`).\n */\nexport function extractReferenceSlug(\n resource: Record<string, unknown>,\n fieldName: string,\n): string | undefined {\n const field = resource[fieldName];\n if (!field || typeof field !== \"object\") {\n return undefined;\n }\n const reference = (field as { reference?: unknown }).reference;\n if (typeof reference !== \"string\" || reference.length === 0) {\n return undefined;\n }\n const slash = reference.lastIndexOf(\"/\");\n const tail = slash >= 0 ? reference.slice(slash + 1) : reference;\n return tail.length > 0 ? tail : undefined;\n}\n","/**\n * Membership workspace-projection composer.\n *\n * Owns the SK grammar for ADR-018 pattern #2 and assembles the\n * projection-row payload consumed by the membership create / update /\n * delete operations. The {@link MembershipWorkspaceProjectionEntity}\n * stores the SK verbatim — the grammar lives here so the operations\n * layer is the single source of truth for projection-row shape (per\n * `.claude/rules/data-layer-layout.md`).\n *\n * SK grammar:\n *\n * - **Pattern #2** (users in a workspace, sorted by user name —\n * workspace-scoped Memberships only):\n * `MEMBERSHIP#<normalizedUserName>#USER#<userId>#<membershipId>`\n *\n * The projection co-locates with the canonical Workspace record under\n * `PK = TID#<tenantId>#WORKSPACE#ID#<workspaceId>` so\n * `Query(PK = TID#<tenantId>#WORKSPACE#ID#<workspaceId>, SK begins_with 'MEMBERSHIP#')`\n * returns workspace metadata + every member projection in one round\n * trip. Tenant-scoped Memberships (no `workspaceId`) skip this\n * projection entirely.\n *\n * @see ADR-018 § Access Pattern Coverage (pattern #2)\n * @see .state/adr-018-implementation-guide.md § 1 (SK grammar) and § 2 (attribute set)\n */\n\nimport { normalizeLabel } from \"@openhi/types\";\n\n/**\n * Sentinel rendered into the SK when the source display name is missing\n * or empty. Keeps the SK shape stable so a `begins_with` prefix query\n * still matches the row; the rename-cascade pipeline (TR-023) will\n * rewrite the SK once the carrier display name lands. Mirrors the\n * sibling user-projection composer's defensive posture — a missing\n * source field never produces a malformed key.\n */\nconst MISSING_NAME_SENTINEL = \"-\";\n\n/** Inputs to compose a Membership workspace-projection row. */\nexport interface MembershipWorkspaceProjectionInput {\n readonly tenantId: string;\n readonly workspaceId: string;\n readonly userId: string;\n readonly membershipId: string;\n readonly summary: string;\n readonly vid: string;\n readonly lastUpdated: string;\n readonly denormalizedUserName?: string;\n}\n\n/** A projection-row payload ready for `multi-write` consumption. */\nexport interface MembershipWorkspaceProjectionItem {\n readonly tenantId: string;\n readonly workspaceId: string;\n readonly sk: string;\n readonly userId: string;\n readonly membershipId: string;\n readonly summary: string;\n readonly vid: string;\n readonly lastUpdated: string;\n readonly denormalizedUserName?: string;\n}\n\n/**\n * Compose the SK for ADR-018 pattern #2. The `<normalizedUserName>`\n * segment sorts memberships alphabetically by user name within the\n * workspace partition. The trailing `USER#<userId>#<membershipId>`\n * disambiguates rows when two memberships share a normalized user name\n * (homonyms) and supports a per-user lookup via\n * `begins_with('MEMBERSHIP#<normalizedUserName>#USER#<userId>#')`.\n * Missing `denormalizedUserName` falls back to\n * {@link MISSING_NAME_SENTINEL} so the SK shape stays valid\n * pre-rename-cascade.\n */\nexport function buildMembershipWorkspaceProjectionSk(params: {\n readonly userId: string;\n readonly membershipId: string;\n readonly denormalizedUserName?: string;\n}): string {\n const normalizedUserName =\n typeof params.denormalizedUserName === \"string\" &&\n params.denormalizedUserName.length > 0\n ? normalizeLabel(params.denormalizedUserName)\n : MISSING_NAME_SENTINEL;\n return `MEMBERSHIP#${normalizedUserName}#USER#${params.userId}#${params.membershipId}`;\n}\n\n/**\n * Builds the projection item for a workspace-scoped Membership.\n * Returns `undefined` when `workspaceId` or `userId` is missing —\n * tenant-scoped Memberships (no workspaceId) skip the workspace\n * projection entirely, and a Membership without a linked user cannot\n * project onto the workspace partition.\n */\nexport function buildMembershipWorkspaceProjectionItem(\n input: MembershipWorkspaceProjectionInput,\n): MembershipWorkspaceProjectionItem | undefined {\n if (!input.workspaceId || input.workspaceId.length === 0) {\n return undefined;\n }\n if (!input.userId || input.userId.length === 0) {\n return undefined;\n }\n const sk = buildMembershipWorkspaceProjectionSk({\n userId: input.userId,\n membershipId: input.membershipId,\n denormalizedUserName: input.denormalizedUserName,\n });\n return {\n tenantId: input.tenantId,\n workspaceId: input.workspaceId,\n sk,\n userId: input.userId,\n membershipId: input.membershipId,\n summary: input.summary,\n vid: input.vid,\n lastUpdated: input.lastUpdated,\n denormalizedUserName: input.denormalizedUserName,\n };\n}\n","/**\n * RoleAssignment user-projection composer.\n *\n * Owns the SK grammar for ADR-018 pattern #5 and assembles the\n * projection-row payload consumed by the role-assignment create /\n * update / delete operations. The\n * {@link RoleAssignmentUserProjectionEntity} stores the SK verbatim —\n * the grammar lives here so the operations layer is the single source\n * of truth for projection-row shape (per\n * `.claude/rules/data-layer-layout.md`).\n *\n * SK grammar:\n *\n * - **tenant-level sub-lane** (`workspaceId` absent):\n * `ROLEASSIGNMENT#TENANT#<normalizedRoleName>#<roleId>#TID#<tenantId>#<roleAssignmentId>`\n * - **workspace-level sub-lane** (`workspaceId` set):\n * `ROLEASSIGNMENT#WORKSPACE#<normalizedRoleName>#<roleId>#TID#<tenantId>#WID#<workspaceId>#<roleAssignmentId>`\n *\n * Both sub-lanes share the user partition `PK = USER#ID#<userId>` so\n * `Query(PK = USER#ID#<userId>, SK begins_with 'ROLEASSIGNMENT#')`\n * returns both sub-lanes interleaved with TENANT preceding WORKSPACE\n * lexicographically.\n *\n * @see ADR-018 § Access Pattern Coverage (pattern #5)\n * @see .state/adr-018-implementation-guide.md § 1 (SK grammar) and § 2 (attribute set)\n */\n\nimport { normalizeLabel } from \"@openhi/types\";\n\n/**\n * Sentinel rendered into the SK when the source display name is missing\n * or empty. Keeps the SK shape stable so a `begins_with` prefix query\n * still matches the row; the rename-cascade pipeline (TR-023) will\n * rewrite the SK once the carrier display name lands. Matches the\n * defensive posture in `membership-user-projection` — a missing source\n * field never produces a malformed key.\n */\nconst MISSING_NAME_SENTINEL = \"-\";\n\n/** Inputs to compose a RoleAssignment user-projection row. */\nexport interface RoleAssignmentUserProjectionInput {\n readonly tenantId: string;\n readonly userId: string;\n readonly workspaceId?: string;\n readonly roleId: string;\n readonly roleAssignmentId: string;\n readonly summary: string;\n readonly vid: string;\n readonly lastUpdated: string;\n readonly denormalizedTenantName?: string;\n readonly denormalizedUserName?: string;\n readonly denormalizedRoleName?: string;\n}\n\n/** A projection-row payload ready for `multi-write` consumption. */\nexport interface RoleAssignmentUserProjectionItem {\n readonly userId: string;\n readonly sk: string;\n readonly tenantId: string;\n readonly workspaceId?: string;\n readonly roleId: string;\n readonly roleAssignmentId: string;\n readonly summary: string;\n readonly vid: string;\n readonly lastUpdated: string;\n readonly denormalizedTenantName?: string;\n readonly denormalizedUserName?: string;\n readonly denormalizedRoleName?: string;\n}\n\n/**\n * Compose the SK for ADR-018 pattern #5 — tenant-level sub-lane. The\n * `<normalizedRoleName>` segment sorts assignments alphabetically by\n * role name within the user's partition; `<roleId>` discriminates\n * rename-stable. Missing `denormalizedRoleName` falls back to\n * {@link MISSING_NAME_SENTINEL} so the SK shape stays valid\n * pre-rename-cascade.\n */\nexport function buildRoleAssignmentUserProjectionSkTenantLane(params: {\n readonly tenantId: string;\n readonly roleId: string;\n readonly roleAssignmentId: string;\n readonly denormalizedRoleName?: string;\n}): string {\n const normalizedRoleName =\n typeof params.denormalizedRoleName === \"string\" &&\n params.denormalizedRoleName.length > 0\n ? normalizeLabel(params.denormalizedRoleName)\n : MISSING_NAME_SENTINEL;\n return `ROLEASSIGNMENT#TENANT#${normalizedRoleName}#${params.roleId}#TID#${params.tenantId}#${params.roleAssignmentId}`;\n}\n\n/**\n * Compose the SK for ADR-018 pattern #5 — workspace-level sub-lane.\n * Same `<normalizedRoleName>#<roleId>` sort discriminator as the tenant\n * sub-lane; the trailing segments narrow the partition to a single\n * tenant + workspace. Missing `denormalizedRoleName` falls back to\n * {@link MISSING_NAME_SENTINEL}.\n */\nexport function buildRoleAssignmentUserProjectionSkWorkspaceLane(params: {\n readonly tenantId: string;\n readonly workspaceId: string;\n readonly roleId: string;\n readonly roleAssignmentId: string;\n readonly denormalizedRoleName?: string;\n}): string {\n const normalizedRoleName =\n typeof params.denormalizedRoleName === \"string\" &&\n params.denormalizedRoleName.length > 0\n ? normalizeLabel(params.denormalizedRoleName)\n : MISSING_NAME_SENTINEL;\n return `ROLEASSIGNMENT#WORKSPACE#${normalizedRoleName}#${params.roleId}#TID#${params.tenantId}#WID#${params.workspaceId}#${params.roleAssignmentId}`;\n}\n\n/**\n * Builds the projection item for the access lane implied by the input.\n * Tenant-level sub-lane when `workspaceId` is absent or empty;\n * workspace-level sub-lane otherwise. Returns `undefined` when `userId`\n * or `roleId` is missing — without either the projection cannot land\n * under a user partition or be sorted by role name.\n */\nexport function buildRoleAssignmentUserProjectionItem(\n input: RoleAssignmentUserProjectionInput,\n): RoleAssignmentUserProjectionItem | undefined {\n if (!input.userId || input.userId.length === 0) {\n return undefined;\n }\n if (!input.roleId || input.roleId.length === 0) {\n return undefined;\n }\n const hasWorkspace =\n typeof input.workspaceId === \"string\" && input.workspaceId.length > 0;\n const sk = hasWorkspace\n ? buildRoleAssignmentUserProjectionSkWorkspaceLane({\n tenantId: input.tenantId,\n workspaceId: input.workspaceId as string,\n roleId: input.roleId,\n roleAssignmentId: input.roleAssignmentId,\n denormalizedRoleName: input.denormalizedRoleName,\n })\n : buildRoleAssignmentUserProjectionSkTenantLane({\n tenantId: input.tenantId,\n roleId: input.roleId,\n roleAssignmentId: input.roleAssignmentId,\n denormalizedRoleName: input.denormalizedRoleName,\n });\n return {\n userId: input.userId,\n sk,\n tenantId: input.tenantId,\n workspaceId: hasWorkspace ? input.workspaceId : undefined,\n roleId: input.roleId,\n roleAssignmentId: input.roleAssignmentId,\n summary: input.summary,\n vid: input.vid,\n lastUpdated: input.lastUpdated,\n denormalizedTenantName: input.denormalizedTenantName,\n denormalizedUserName: input.denormalizedUserName,\n denormalizedRoleName: input.denormalizedRoleName,\n };\n}\n\n/**\n * Extracts a FHIR `Reference` slug — the segment after the final `/`.\n * Returns `undefined` when the reference is missing or malformed so\n * callers fall back gracefully (matches the defensive posture in\n * `extractRoleId` / `extractDenormalizedReferenceDisplay`).\n */\nexport function extractReferenceSlug(\n resource: Record<string, unknown>,\n fieldName: string,\n): string | undefined {\n const field = resource[fieldName];\n if (!field || typeof field !== \"object\") {\n return undefined;\n }\n const reference = (field as { reference?: unknown }).reference;\n if (typeof reference !== \"string\" || reference.length === 0) {\n return undefined;\n }\n const slash = reference.lastIndexOf(\"/\");\n const tail = slash >= 0 ? reference.slice(slash + 1) : reference;\n return tail.length > 0 ? tail : undefined;\n}\n","/**\n * RoleAssignment workspace-projection composer.\n *\n * Owns the SK grammar for ADR-018 pattern #9 and assembles the\n * projection-row payload consumed by the role-assignment create /\n * update / delete operations. The\n * {@link RoleAssignmentWorkspaceProjectionEntity} stores the SK\n * verbatim — the grammar lives here so the operations layer is the\n * single source of truth for projection-row shape (per\n * `.claude/rules/data-layer-layout.md`).\n *\n * SK grammar:\n *\n * - **Pattern #9** (users with a specific role in a workspace, sorted\n * by user name — workspace-scoped RoleAssignments only):\n * `ROLEASSIGNMENT#<roleId>#<normalizedUserName>#USER#<userId>#<roleAssignmentId>`\n *\n * The SK is **discriminator-first** on the raw `<roleId>` (mirroring\n * the canonical GSI1SK from pattern #8). Role id discriminates first so\n * a `begins_with('ROLEASSIGNMENT#<roleId>#')` filter returns every user\n * assigned to that role in the workspace, sorted alphabetically by\n * normalized user name. The trailing `USER#<userId>#<roleAssignmentId>`\n * disambiguates rows when two assignments share a normalized user name\n * (homonyms) and supports a per-user lookup via\n * `begins_with('ROLEASSIGNMENT#<roleId>#<normalizedUserName>#USER#<userId>#')`.\n *\n * The projection co-locates with the canonical Workspace record (and\n * the Membership workspace-projection rows from pattern #2) under\n * `PK = TID#<tenantId>#WORKSPACE#ID#<workspaceId>` so\n * `Query(PK = TID#<tenantId>#WORKSPACE#ID#<workspaceId>)` returns\n * workspace metadata + every member projection + every role-assignment\n * projection in one round trip. Tenant-scoped RoleAssignments (no\n * `workspaceId`) skip this projection entirely.\n *\n * **Rename-cascade interaction (TR-023, Phase 6).** The SK uses the\n * raw `<roleId>` (rename-stable) for the discriminator and\n * `<normalizedUserName>` for the secondary sort. A Role rename does\n * NOT rewrite this SK; a User rename DOES (cascaded by the rename\n * pipeline).\n *\n * @see ADR-018 § Access Pattern Coverage (pattern #9)\n * @see .state/adr-018-implementation-guide.md § 1 (SK grammar) and § 2 (attribute set)\n */\n\nimport { normalizeLabel } from \"@openhi/types\";\n\n/**\n * Sentinel rendered into the SK when the source user display name is\n * missing or empty. Keeps the SK shape stable so a `begins_with` prefix\n * query still matches the row; the rename-cascade pipeline (TR-023)\n * will rewrite the SK once the carrier display name lands. Mirrors the\n * sibling projection composers' defensive posture — a missing source\n * field never produces a malformed key.\n */\nconst MISSING_NAME_SENTINEL = \"-\";\n\n/** Inputs to compose a RoleAssignment workspace-projection row. */\nexport interface RoleAssignmentWorkspaceProjectionInput {\n readonly tenantId: string;\n readonly workspaceId: string;\n readonly userId: string;\n readonly roleId: string;\n readonly roleAssignmentId: string;\n readonly summary: string;\n readonly vid: string;\n readonly lastUpdated: string;\n readonly denormalizedUserName?: string;\n readonly denormalizedRoleName?: string;\n}\n\n/** A projection-row payload ready for `multi-write` consumption. */\nexport interface RoleAssignmentWorkspaceProjectionItem {\n readonly tenantId: string;\n readonly workspaceId: string;\n readonly sk: string;\n readonly userId: string;\n readonly roleId: string;\n readonly roleAssignmentId: string;\n readonly summary: string;\n readonly vid: string;\n readonly lastUpdated: string;\n readonly denormalizedUserName?: string;\n readonly denormalizedRoleName?: string;\n}\n\n/**\n * Compose the SK for ADR-018 pattern #9. The discriminator-first\n * `<roleId>` segment (raw, NOT normalized — matches the canonical\n * GSI1SK from pattern #8) lets `begins_with('ROLEASSIGNMENT#<roleId>#')`\n * filter one role. The `<normalizedUserName>` segment sorts assignments\n * alphabetically by user name within that role. The trailing\n * `USER#<userId>#<roleAssignmentId>` disambiguates homonyms and\n * supports a per-user lookup via\n * `begins_with('ROLEASSIGNMENT#<roleId>#<normalizedUserName>#USER#<userId>#')`.\n * Missing `denormalizedUserName` falls back to\n * {@link MISSING_NAME_SENTINEL} so the SK shape stays valid\n * pre-rename-cascade.\n */\nexport function buildRoleAssignmentWorkspaceProjectionSk(params: {\n readonly roleId: string;\n readonly userId: string;\n readonly roleAssignmentId: string;\n readonly denormalizedUserName?: string;\n}): string {\n const normalizedUserName =\n typeof params.denormalizedUserName === \"string\" &&\n params.denormalizedUserName.length > 0\n ? normalizeLabel(params.denormalizedUserName)\n : MISSING_NAME_SENTINEL;\n return `ROLEASSIGNMENT#${params.roleId}#${normalizedUserName}#USER#${params.userId}#${params.roleAssignmentId}`;\n}\n\n/**\n * Builds the projection item for a workspace-scoped RoleAssignment.\n * Returns `undefined` when `workspaceId`, `userId`, or `roleId` is\n * missing — tenant-scoped RoleAssignments (no workspaceId) skip the\n * workspace projection entirely; a RoleAssignment without a linked\n * user or role cannot project onto the workspace partition under the\n * pattern-#9 SK shape.\n */\nexport function buildRoleAssignmentWorkspaceProjectionItem(\n input: RoleAssignmentWorkspaceProjectionInput,\n): RoleAssignmentWorkspaceProjectionItem | undefined {\n if (!input.workspaceId || input.workspaceId.length === 0) {\n return undefined;\n }\n if (!input.userId || input.userId.length === 0) {\n return undefined;\n }\n if (!input.roleId || input.roleId.length === 0) {\n return undefined;\n }\n const sk = buildRoleAssignmentWorkspaceProjectionSk({\n roleId: input.roleId,\n userId: input.userId,\n roleAssignmentId: input.roleAssignmentId,\n denormalizedUserName: input.denormalizedUserName,\n });\n return {\n tenantId: input.tenantId,\n workspaceId: input.workspaceId,\n sk,\n userId: input.userId,\n roleId: input.roleId,\n roleAssignmentId: input.roleAssignmentId,\n summary: input.summary,\n vid: input.vid,\n lastUpdated: input.lastUpdated,\n denormalizedUserName: input.denormalizedUserName,\n denormalizedRoleName: input.denormalizedRoleName,\n };\n}\n"],"mappings":"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","names":["normalizeLabel","MISSING_NAME_SENTINEL","normalizeLabel","MISSING_NAME_SENTINEL","extractReferenceSlug","normalizeLabel","MISSING_NAME_SENTINEL"]}