@openhi/constructs 0.0.105 → 0.0.106

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/lib/chunk-AGF3RAAZ.mjs +20 -0
  2. package/lib/chunk-AGF3RAAZ.mjs.map +1 -0
  3. package/lib/{chunk-BXEG7IOZ.mjs → chunk-AO3E22CS.mjs} +2 -2
  4. package/lib/{chunk-WNUH2WDZ.mjs → chunk-CHPEQRXU.mjs} +2 -2
  5. package/lib/chunk-JUNL76HF.mjs +428 -0
  6. package/lib/chunk-JUNL76HF.mjs.map +1 -0
  7. package/lib/chunk-L6UAP4KP.mjs +27 -0
  8. package/lib/chunk-L6UAP4KP.mjs.map +1 -0
  9. package/lib/{chunk-3QS3WKRC.mjs → chunk-LZOMFHX3.mjs} +9 -2
  10. package/lib/chunk-QMIOLLAS.mjs +531 -0
  11. package/lib/chunk-QMIOLLAS.mjs.map +1 -0
  12. package/lib/chunk-SYBADQXI.mjs +607 -0
  13. package/lib/chunk-SYBADQXI.mjs.map +1 -0
  14. package/lib/chunk-VXX4I3EF.mjs +19 -0
  15. package/lib/chunk-VXX4I3EF.mjs.map +1 -0
  16. package/lib/{chunk-36YCDLLA.mjs → chunk-VYDIGFIX.mjs} +75 -481
  17. package/lib/chunk-VYDIGFIX.mjs.map +1 -0
  18. package/lib/chunk-YU2HRNUP.mjs +33 -0
  19. package/lib/chunk-YU2HRNUP.mjs.map +1 -0
  20. package/lib/chunk-YZZDUJHI.mjs +37 -0
  21. package/lib/chunk-YZZDUJHI.mjs.map +1 -0
  22. package/lib/cors-options-lambda.handler.mjs +1 -1
  23. package/lib/data-store-postgres-replication.handler.mjs +1 -1
  24. package/lib/events-BfrkMoBD.d.mts +44 -0
  25. package/lib/events-BfrkMoBD.d.ts +44 -0
  26. package/lib/events-DGep6C7w.d.mts +207 -0
  27. package/lib/events-DGep6C7w.d.ts +207 -0
  28. package/lib/firehose-archive-transform.handler.mjs +1 -1
  29. package/lib/index.d.mts +417 -9
  30. package/lib/index.d.ts +663 -10
  31. package/lib/index.js +2400 -111
  32. package/lib/index.js.map +1 -1
  33. package/lib/index.mjs +781 -104
  34. package/lib/index.mjs.map +1 -1
  35. package/lib/openhi-context-CaBH8SFo.d.mts +39 -0
  36. package/lib/openhi-context-CaBH8SFo.d.ts +39 -0
  37. package/lib/platform-deploy-bridge.handler.d.mts +14 -0
  38. package/lib/platform-deploy-bridge.handler.d.ts +14 -0
  39. package/lib/platform-deploy-bridge.handler.js +762 -0
  40. package/lib/platform-deploy-bridge.handler.js.map +1 -0
  41. package/lib/platform-deploy-bridge.handler.mjs +134 -0
  42. package/lib/platform-deploy-bridge.handler.mjs.map +1 -0
  43. package/lib/post-authentication.handler.mjs +1 -1
  44. package/lib/post-confirmation.handler.mjs +1 -1
  45. package/lib/pre-token-generation.handler.js +76 -31
  46. package/lib/pre-token-generation.handler.js.map +1 -1
  47. package/lib/pre-token-generation.handler.mjs +5 -3
  48. package/lib/pre-token-generation.handler.mjs.map +1 -1
  49. package/lib/provision-default-workspace.handler.js +86 -41
  50. package/lib/provision-default-workspace.handler.js.map +1 -1
  51. package/lib/provision-default-workspace.handler.mjs +6 -4
  52. package/lib/provision-default-workspace.handler.mjs.map +1 -1
  53. package/lib/rest-api-lambda.handler.js +114 -59
  54. package/lib/rest-api-lambda.handler.js.map +1 -1
  55. package/lib/rest-api-lambda.handler.mjs +40 -61
  56. package/lib/rest-api-lambda.handler.mjs.map +1 -1
  57. package/lib/seed-demo-data.handler.d.mts +107 -0
  58. package/lib/seed-demo-data.handler.d.ts +107 -0
  59. package/lib/seed-demo-data.handler.js +2037 -0
  60. package/lib/seed-demo-data.handler.js.map +1 -0
  61. package/lib/seed-demo-data.handler.mjs +23 -0
  62. package/lib/seed-demo-data.handler.mjs.map +1 -0
  63. package/lib/seed-system-data.handler.d.mts +64 -0
  64. package/lib/seed-system-data.handler.d.ts +64 -0
  65. package/lib/seed-system-data.handler.js +1631 -0
  66. package/lib/seed-system-data.handler.js.map +1 -0
  67. package/lib/seed-system-data.handler.mjs +135 -0
  68. package/lib/seed-system-data.handler.mjs.map +1 -0
  69. package/package.json +4 -2
  70. package/lib/chunk-36YCDLLA.mjs.map +0 -1
  71. /package/lib/{chunk-BXEG7IOZ.mjs.map → chunk-AO3E22CS.mjs.map} +0 -0
  72. /package/lib/{chunk-WNUH2WDZ.mjs.map → chunk-CHPEQRXU.mjs.map} +0 -0
  73. /package/lib/{chunk-3QS3WKRC.mjs.map → chunk-LZOMFHX3.mjs.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../workflows/src/envelope-version.ts","../../workflows/src/envelope.ts","../../workflows/src/sources.ts","../../workflows/src/detail-types/registry.ts","../../workflows/src/detail-types/platform.ts","../../workflows/src/detail-types/index.ts","../../workflows/src/publisher.ts","../../workflows/src/consumer.ts","../../workflows/src/dedup/env.ts","../../workflows/src/dedup/workflow-dedup-client.ts","../../workflows/src/dedup/index.ts","../../workflows/src/index.ts","../src/workflows/control-plane/seed-demo-data/seed-demo-data.handler.ts","../src/workflows/control-plane/seed-demo-data/events.ts","../src/data/dynamo/dynamo-control-service.ts","../src/data/dynamo/dynamo-client.ts","../src/data/dynamo/entities/control/configuration-entity.ts","../src/data/dynamo/entities/control/control-entity-common.ts","../src/data/dynamo/shard.ts","../src/data/dynamo/entities/control/membership-entity.ts","../src/data/dynamo/entities/control/role-entity.ts","../src/data/dynamo/entities/control/roleassignment-entity.ts","../src/data/dynamo/entities/control/tenant-entity.ts","../src/data/dynamo/entities/control/user-entity.ts","../src/data/dynamo/entities/control/workspace-entity.ts","../src/data/errors/domain-errors.ts","../src/data/operations/control/membership/membership-create-operation.ts","../src/data/operations/control/role/role-get-by-id-operation.ts","../src/data/operations/control/roleassignment/roleassignment-create-operation.ts","../src/data/operations/control/tenant/tenant-create-operation.ts","../src/data/operations/control/workspace/workspace-create-operation.ts"],"sourcesContent":["/**\n * Envelope version pinned by the publisher SDK on every emitted envelope.\n *\n * Shape is `\"<major>.<minor>\"` per TR-016 §Open Items #3 (P1).\n * Additive optional fields bump the minor; breaking changes bump the\n * major and require a documented overlap window in the consumer SDK.\n */\nexport const ENVELOPE_VERSION = \"1.0\";\n\nconst ENVELOPE_VERSION_PATTERN = /^\\d+\\.\\d+$/;\n\n/**\n * Lowest envelope major version this SDK's consumer parser accepts.\n *\n * Bump this when a deprecated major reaches end-of-life and the\n * overlap window closes.\n */\nconst MIN_SUPPORTED_MAJOR = 1;\n\n/**\n * Highest envelope major version this SDK's consumer parser accepts.\n *\n * Bump this in lockstep with `ENVELOPE_VERSION` whenever a new major\n * ships; the consumer always supports its own publish major.\n */\nconst MAX_SUPPORTED_MAJOR = 1;\n\n/**\n * Return `true` when `version` is shaped `<major>.<minor>` and its\n * major lies within `[MIN_SUPPORTED_MAJOR, MAX_SUPPORTED_MAJOR]`.\n */\nexport function isSupportedEnvelopeVersion(version: string): boolean {\n if (!ENVELOPE_VERSION_PATTERN.test(version)) {\n return false;\n }\n const major = Number.parseInt(version.split(\".\")[0], 10);\n return major >= MIN_SUPPORTED_MAJOR && major <= MAX_SUPPORTED_MAJOR;\n}\n","import type { OhiJwtClaims } from \"@openhi/types\";\n\n/**\n * Discriminated-union actor field per ADR-016 §Decision #3.\n *\n * User-initiated workflows project the four ADR-014 JWT claims (with\n * `ohi_tid` / `ohi_wid` required — a workflow event without a tenant\n * + workspace context belongs to the `system` variant); bootstrap\n * workflows run before a User exists and carry the bootstrap-role\n * name instead. The `system` value is free-form pending the\n * onboarding-bootstrap IAM role TR (TR-016 §Open Items #4).\n */\nexport type WorkflowActor = WorkflowUserActor | WorkflowSystemActor;\n\n/** User-actor variant — required projection of the ADR-014 JWT claims. */\nexport interface WorkflowUserActor {\n readonly ohi_tid: string;\n readonly ohi_wid: string;\n readonly ohi_uid: string;\n readonly ohi_uname: string;\n}\n\n/** Bootstrap-actor variant — carries the bootstrap-role name. */\nexport interface WorkflowSystemActor {\n readonly system: string;\n}\n\n/**\n * Type guard for the user-actor variant.\n */\nexport function isWorkflowUserActor(\n actor: WorkflowActor,\n): actor is WorkflowUserActor {\n return (actor as WorkflowUserActor).ohi_uid !== undefined;\n}\n\n/**\n * Type guard for the system-actor variant.\n */\nexport function isWorkflowSystemActor(\n actor: WorkflowActor,\n): actor is WorkflowSystemActor {\n return (actor as WorkflowSystemActor).system !== undefined;\n}\n\n/**\n * Standard workflow event envelope per ADR-016 §Decision #3 / TR-016.\n *\n * The generic `TPayload` parameter narrows the per-workflow payload.\n *\n * Field naming note: the payload lives under `payload` (not `detail`)\n * deliberately. EventBridge's outer event already has its own `detail`\n * field that carries this whole envelope; nesting another `detail`\n * inside it produced double-`detail` paths in rule patterns\n * (`detail.detail.<x>`) that consistently bit consumers writing\n * filters. The unambiguous name is worth the rename.\n */\nexport interface WorkflowEvent<TPayload = Record<string, unknown>> {\n /** Per-event UUID. Dedup keys on `(eventId, attempt)`. */\n readonly eventId: string;\n /** 1-indexed delivery attempt. */\n readonly attempt: number;\n /** Originating cross-plane chain identifier. */\n readonly correlationId: string;\n /** Immediate predecessor event id, or null for a chain origin. */\n readonly causationId: string | null;\n /** Discriminated actor — JWT-claim projection or bootstrap-role marker. */\n readonly actor: WorkflowActor;\n /** ISO-8601 timestamp marking when the envelope was constructed. */\n readonly occurredAt: string;\n /** Semver-shaped envelope version (e.g. `\"1.0\"`). */\n readonly envelopeVersion: string;\n /** Per-workflow payload narrowed by the generic parameter. */\n readonly payload: TPayload;\n}\n\n/**\n * Promote a raw `OhiJwtClaims` projection to a fully-required\n * `WorkflowUserActor`.\n *\n * Throws `MissingActorContextError` when `ohi_tid` or `ohi_wid` is\n * absent — those claims are optional on the JWT but required on the\n * workflow user-actor.\n */\nexport function workflowUserActorFromClaims(\n claims: OhiJwtClaims,\n): WorkflowUserActor {\n if (claims.ohi_tid === undefined || claims.ohi_wid === undefined) {\n throw new MissingActorContextError(\n \"workflowUserActorFromClaims: ohi_tid and ohi_wid are required on the workflow user-actor; the caller's JWT is missing one or both. Use a system-actor for pre-provisioning bootstrap workflows.\",\n );\n }\n return {\n ohi_tid: claims.ohi_tid,\n ohi_wid: claims.ohi_wid,\n ohi_uid: claims.ohi_uid,\n ohi_uname: claims.ohi_uname,\n };\n}\n\n/** Thrown when JWT claims lack a field required by the workflow user-actor. */\nexport class MissingActorContextError extends Error {\n /** @param message - human-readable description of the missing claim. */\n constructor(message: string) {\n super(message);\n this.name = \"MissingActorContextError\";\n }\n}\n","/**\n * Per-bus `Source` constants for the three OpenHI EventBridge buses.\n *\n * Every workflow event carries one of these values in its EventBridge\n * `Source` field. Bus selection follows ADR-016 §Decision #1.\n *\n * @see https://github.com/codedrifters/openhi-planning/blob/main/docs/src/content/docs/requirements/architectural-decisions/ADR-016-workflow-boundary-strategy.md\n */\n\n/** Source for control-plane workflow events (Tenant / Workspace / User / Membership / Role / RoleAssignment / Configuration). */\nexport const OPENHI_CONTROL_SOURCE = \"openhi.control\" as const;\n\n/** Source for data-plane workflow events (committed writes to the FHIR data store). */\nexport const OPENHI_DATA_SOURCE = \"openhi.data\" as const;\n\n/** Source for ops-plane workflow events (platform telemetry, deployments, build events). */\nexport const OPENHI_OPS_SOURCE = \"openhi.ops\" as const;\n\n/**\n * Discriminated union over the three OpenHI EventBridge sources.\n *\n * A publisher that passes any other string for the EventBridge `Source`\n * field is rejected at compile time.\n */\nexport type OpenHiSource =\n | typeof OPENHI_CONTROL_SOURCE\n | typeof OPENHI_DATA_SOURCE\n | typeof OPENHI_OPS_SOURCE;\n\n/**\n * Default EventBridge bus name for each OpenHI source.\n *\n * Deployments may override per-source via\n * `PublisherOptions.busNameByPlane`.\n */\nexport const DEFAULT_BUS_NAME_BY_SOURCE: Record<OpenHiSource, string> = {\n [OPENHI_CONTROL_SOURCE]: \"openhi-control-event-bus\",\n [OPENHI_DATA_SOURCE]: \"openhi-data-event-bus\",\n [OPENHI_OPS_SOURCE]: \"openhi-ops-event-bus\",\n};\n","import type { OpenHiSource } from \"../sources\";\n\n/**\n * One entry in the workflow detail-type registry.\n *\n * Each registered detail-type binds a typed `detail` payload to:\n * - the EventBridge `Source` (and therefore the bus) it ships on,\n * - the `detail-type` string consumers subscribe to,\n * - per-event metadata (`crossPlane`, `dedupRequired`) read by\n * downstream tooling (AsyncAPI generator, placement matrix).\n *\n * The `_detail` property is a phantom marker type only — never\n * read, never written, never instantiated. It carries the\n * compile-time TDetail through the entry so callers of\n * `publishWorkflowEvent` / `parseWorkflowEvent` see the typed\n * payload at the call site.\n */\nexport interface WorkflowDetailTypeEntry<TDetail> {\n /** Versioned detail-type string, e.g. `tenant.onboarded.v1`. */\n readonly detailType: string;\n /** The bus this detail-type ships on (compile-time enforced). */\n readonly source: OpenHiSource;\n /** Phantom marker carrying the payload shape. */\n readonly _detail?: TDetail;\n /** Marks events that cross plane boundaries (placement-matrix metadata). */\n readonly crossPlane?: boolean;\n /** Marks events that retryable consumers MUST dedupe on. */\n readonly dedupRequired?: boolean;\n}\n\n/**\n * Declare a registry entry with type inference.\n *\n * Call sites:\n *\n * ```ts\n * export const TenantOnboardedV1 = defineDetailType<TenantOnboardedV1Detail>({\n * detailType: \"tenant.onboarded.v1\",\n * source: OPENHI_CONTROL_SOURCE,\n * dedupRequired: true,\n * });\n * ```\n *\n * Detail-type strings follow `<area>.<event>.<version>` (TR-016 §Open\n * Items #2). The conformance regex below is the platform-wide format.\n */\nexport function defineDetailType<TDetail>(\n entry: Omit<WorkflowDetailTypeEntry<TDetail>, \"_detail\">,\n): WorkflowDetailTypeEntry<TDetail> {\n if (!isWellFormedDetailType(entry.detailType)) {\n throw new InvalidDetailTypeRegistrationError(\n `Detail-type \"${entry.detailType}\" does not match the platform-wide format <area>.<event>.v<integer>. See TR-016 §Open Items #2.`,\n );\n }\n return entry;\n}\n\n/**\n * Pattern enforced on every registered detail-type:\n * `<area>.<event>.v<integer>` where each segment is lowercase\n * alphanumeric with optional dashes.\n *\n * Multi-level areas (e.g. `fhir.audit-event.recorded.v1`) are\n * intentionally not yet allowed; TR-016 §Open Items #2 defers that.\n */\nconst DETAIL_TYPE_PATTERN =\n /^[a-z0-9]+(?:-[a-z0-9]+)*\\.[a-z0-9]+(?:-[a-z0-9]+)*\\.v\\d+$/;\n\n/** Return `true` when `detailType` matches the platform-wide format. */\nexport function isWellFormedDetailType(detailType: string): boolean {\n return DETAIL_TYPE_PATTERN.test(detailType);\n}\n\n/** Thrown by `defineDetailType` when the supplied string violates the format. */\nexport class InvalidDetailTypeRegistrationError extends Error {\n /** @param message - human-readable description of the violation. */\n constructor(message: string) {\n super(message);\n this.name = \"InvalidDetailTypeRegistrationError\";\n }\n}\n","import { OPENHI_CONTROL_SOURCE } from \"../sources\";\nimport { defineDetailType } from \"./registry\";\n\n/**\n * `detail` payload for `platform.deployment-completed.v1`.\n *\n * Projected by the platform-deploy bridge from a CloudFormation\n * `Stack Status Change` event (`CREATE_COMPLETE` / `UPDATE_COMPLETE`)\n * on a tagged OpenHI platform stack. Downstream control-plane\n * workflows (e.g. seed-system-roles, seed-demo-data) subscribe\n * to this detail-type on the control event bus.\n */\nexport interface PlatformDeploymentCompletedV1Detail {\n /** CloudFormation stack name (`AWS::CloudFormation::Stack` `StackName`). */\n readonly stackName: string;\n /** Full CloudFormation stack ARN. */\n readonly stackId: string;\n /** AWS region the stack deployed into (e.g. `us-east-1`). */\n readonly region: string;\n /** 12-digit AWS account id the stack deployed into. */\n readonly accountId: string;\n /** Terminal stack status that triggered the bridge. */\n readonly status: \"CREATE_COMPLETE\" | \"UPDATE_COMPLETE\";\n /** Free-form reason text from CloudFormation; absent on most events. */\n readonly statusReason?: string;\n /**\n * Projected subset of stack tags. The bridge resolves tags via\n * `cloudformation:DescribeStacks` because the source EventBridge\n * event omits them.\n */\n readonly stackTags: ReadonlyArray<{\n readonly key: string;\n readonly value: string;\n }>;\n /** ISO-8601 timestamp from the source EventBridge `time` field. */\n readonly cloudformationEventTime: string;\n}\n\n/**\n * Registry entry for `platform.deployment-completed.v1`.\n *\n * Published on the control event bus (`OPENHI_CONTROL_SOURCE`) per\n * the workflow placement matrix (codedrifters/openhi#953 row 4):\n * the AWS-native source is the ops-plane default bus, but the bridge\n * republishes onto the control bus because the downstream consumers\n * are control-plane workflows.\n *\n * `dedupRequired: true` — at-least-once redelivery from EventBridge\n * means retryable consumers MUST dedupe on `(eventId, attempt)` via\n * `WorkflowDedupClient`.\n */\nexport const PlatformDeploymentCompletedV1 =\n defineDetailType<PlatformDeploymentCompletedV1Detail>({\n detailType: \"platform.deployment-completed.v1\",\n source: OPENHI_CONTROL_SOURCE,\n dedupRequired: true,\n });\n\n/**\n * `detail` payload for `platform.system-data-seeded.v1`.\n *\n * Published by the `seed-system-data` workflow after it has\n * idempotently re-asserted every platform-singleton control-plane\n * record (today: the three canonical Roles; future: additional system\n * data) on the back of a `platform.deployment-completed.v1` event.\n *\n * Downstream control-plane workflows that depend on the\n * platform-singleton records existing — `seed-demo-data`, for\n * example — subscribe to this detail-type instead of the raw\n * deploy-completion event so the dependency is enforced by a\n * happens-before edge rather than by EventBridge retry timing.\n */\nexport interface PlatformSystemDataSeededV1Detail {\n /**\n * EventBridge `eventId` of the originating\n * `platform.deployment-completed.v1` event that triggered the\n * system-data seeding. Propagated for correlation in logs and\n * downstream causation chains.\n */\n readonly sourceEventId: string;\n /**\n * Full CloudFormation stack ARN of the deploy that triggered the\n * system-data seeding. Mirrors the field on the originating\n * `PlatformDeploymentCompletedV1Detail`; downstream consumers can\n * filter by stack-id prefix without re-reading the source event.\n */\n readonly sourceStackId: string;\n /**\n * Number of platform-singleton records re-asserted on this run.\n * Useful for sanity checks and observability — divergence between\n * deploys signals either a generator-emitted catalog change or a\n * partial-failure recovery from the replay tooling.\n */\n readonly seededRecordCount: number;\n}\n\n/**\n * Registry entry for `platform.system-data-seeded.v1`.\n *\n * Published onto the control event bus (`OPENHI_CONTROL_SOURCE`).\n * `dedupRequired: true` — downstream consumers MUST dedup on\n * `(eventId, attempt)` via `WorkflowDedupClient`, same as every other\n * retryable consumer.\n */\nexport const PlatformSystemDataSeededV1 =\n defineDetailType<PlatformSystemDataSeededV1Detail>({\n detailType: \"platform.system-data-seeded.v1\",\n source: OPENHI_CONTROL_SOURCE,\n dedupRequired: true,\n });\n","export * from \"./platform\";\nexport * from \"./registry\";\n","import { randomUUID } from \"node:crypto\";\nimport {\n EventBridgeClient,\n PutEventsCommand,\n} from \"@aws-sdk/client-eventbridge\";\n\nimport type { WorkflowDetailTypeEntry } from \"./detail-types/registry\";\nimport type { WorkflowActor, WorkflowEvent } from \"./envelope\";\nimport { ENVELOPE_VERSION } from \"./envelope-version\";\nimport { DEFAULT_BUS_NAME_BY_SOURCE, type OpenHiSource } from \"./sources\";\n\n/**\n * Caller-supplied envelope context the publisher consumes.\n *\n * The actor is required on every publish — pre-provisioning bootstrap\n * workflows pass a `{ system: <role-name> }` actor.\n *\n * `correlationId` and `causationId` propagate from an upstream event\n * when this publisher is consuming-then-publishing; pass them through\n * verbatim from the inbound envelope's fields. Both are optional;\n * when omitted the publisher treats the publish as a chain origin\n * (`correlationId` = fresh UUID, `causationId` = null).\n */\nexport interface PublishContext {\n readonly actor: WorkflowActor;\n readonly correlationId?: string;\n readonly causationId?: string | null;\n}\n\n/**\n * Per-call output of a successful publish.\n */\nexport interface PublishResult {\n readonly eventId: string;\n}\n\n/**\n * Publisher overrides applied to every call against a single client.\n *\n * `eventIdGenerator`, `correlationIdGenerator`, and `now` are\n * test-only seams; production callers omit them and the publisher\n * uses `crypto.randomUUID()` and `new Date()`.\n */\nexport interface PublisherOptions {\n /** Override the default bus name for one or more sources. */\n readonly busNameByPlane?: Partial<Record<OpenHiSource, string>>;\n /** Test seam — supply a deterministic UUID generator for `eventId`. */\n readonly eventIdGenerator?: () => string;\n /** Test seam — supply a deterministic UUID generator for new `correlationId` values. */\n readonly correlationIdGenerator?: () => string;\n /** Test seam — supply a deterministic clock for `occurredAt`. */\n readonly now?: () => Date;\n}\n\n/**\n * Tree-shaped publisher client per ADR-016 Recommendation.\n *\n * The `publish` primitive accepts any registered detail-type and\n * returns a typed `PublishResult`. Downstream tree shaping\n * (`client.<bus>.<area>.<event>.publish(payload, ctx)`) is built from\n * the detail-type registry once entries are registered; until then,\n * callers invoke `client.publish(entry, payload, ctx)` directly.\n */\nexport interface WorkflowsClient {\n /**\n * Construct a workflow envelope around `payload` and publish it to\n * the EventBridge bus configured for `entry.source`.\n */\n publish<TPayload>(\n entry: WorkflowDetailTypeEntry<TPayload>,\n payload: TPayload,\n ctx: PublishContext,\n ): Promise<PublishResult>;\n}\n\n/**\n * Factory that returns a `WorkflowsClient` bound to a single\n * `EventBridgeClient`.\n */\nexport function workflowsClient(\n bridge: EventBridgeClient,\n options: PublisherOptions = {},\n): WorkflowsClient {\n return {\n publish: (entry, payload, ctx) =>\n publishWorkflowEvent(bridge, entry, payload, ctx, options),\n };\n}\n\n/**\n * Construct a workflow envelope and publish it via\n * `EventBridge.PutEvents`.\n *\n * Exposed as a stand-alone function for callers that prefer the\n * primitive over the `WorkflowsClient` indirection.\n */\nexport async function publishWorkflowEvent<TPayload>(\n bridge: EventBridgeClient,\n entry: WorkflowDetailTypeEntry<TPayload>,\n payload: TPayload,\n ctx: PublishContext,\n options: PublisherOptions = {},\n): Promise<PublishResult> {\n const eventIdGenerator = options.eventIdGenerator ?? (() => randomUUID());\n const correlationIdGenerator =\n options.correlationIdGenerator ?? (() => randomUUID());\n const now = options.now ?? (() => new Date());\n\n const envelope: WorkflowEvent<TPayload> = {\n eventId: eventIdGenerator(),\n attempt: 1,\n correlationId: ctx.correlationId ?? correlationIdGenerator(),\n causationId: ctx.causationId ?? null,\n actor: ctx.actor,\n occurredAt: now().toISOString(),\n envelopeVersion: ENVELOPE_VERSION,\n payload,\n };\n\n const busName =\n options.busNameByPlane?.[entry.source] ??\n DEFAULT_BUS_NAME_BY_SOURCE[entry.source];\n\n const result = await bridge.send(\n new PutEventsCommand({\n Entries: [\n {\n EventBusName: busName,\n Source: entry.source,\n DetailType: entry.detailType,\n Detail: JSON.stringify(envelope),\n },\n ],\n }),\n );\n\n if ((result.FailedEntryCount ?? 0) > 0) {\n const first = result.Entries?.[0];\n throw new WorkflowPublishError(\n `EventBridge rejected ${entry.detailType} publish on bus ${busName}: ${first?.ErrorCode ?? \"unknown\"} — ${first?.ErrorMessage ?? \"no error message\"}`,\n );\n }\n\n return { eventId: envelope.eventId };\n}\n\n/** Thrown when EventBridge rejects a `PutEvents` entry. */\nexport class WorkflowPublishError extends Error {\n /** @param message - human-readable description of the failed publish. */\n constructor(message: string) {\n super(message);\n this.name = \"WorkflowPublishError\";\n }\n}\n","import type { WorkflowDetailTypeEntry } from \"./detail-types/registry\";\nimport type { WorkflowEvent } from \"./envelope\";\nimport { isSupportedEnvelopeVersion } from \"./envelope-version\";\n\n/**\n * Structural shape of the EventBridge event objects this SDK's\n * consumer parses.\n *\n * Matches `@types/aws-lambda`'s `EventBridgeEvent<string, unknown>`\n * by structural compatibility without requiring callers to import\n * that types package — consumers may pass either an\n * `EventBridgeEvent` from `aws-lambda` or any record-shaped object\n * carrying the same keys.\n */\nexport interface EventBridgeEventLike {\n readonly source: string;\n readonly \"detail-type\": string;\n readonly detail: unknown;\n}\n\n/**\n * The `(eventId, attempt)` tuple every retryable consumer hands to\n * the `WorkflowDedupTable` client.\n */\nexport interface DedupKey {\n readonly eventId: string;\n readonly attempt: number;\n}\n\n/**\n * Output of `parseWorkflowEvent` — the validated envelope plus the\n * dedup tuple.\n */\nexport interface ParsedWorkflowEvent<TPayload> {\n readonly envelope: WorkflowEvent<TPayload>;\n readonly dedupKey: DedupKey;\n}\n\n/**\n * Parse an EventBridge event into a typed envelope and surface the\n * `(eventId, attempt)` tuple the dedup-table client consumes.\n *\n * Validates:\n * - `event.source` matches `expected.source`\n * - `event[\"detail-type\"]` matches `expected.detailType`\n * - the envelope's `envelopeVersion` is within the SDK's supported range\n * - every required envelope field is present and well-shaped\n */\nexport function parseWorkflowEvent<TPayload>(\n event: EventBridgeEventLike,\n expected: WorkflowDetailTypeEntry<TPayload>,\n): ParsedWorkflowEvent<TPayload> {\n if (event.source !== expected.source) {\n throw new InvalidWorkflowEventError(\n `EventBridge source \"${event.source}\" does not match expected detail-type's source \"${expected.source}\".`,\n );\n }\n\n if (event[\"detail-type\"] !== expected.detailType) {\n throw new InvalidWorkflowEventError(\n `EventBridge detail-type \"${event[\"detail-type\"]}\" does not match expected \"${expected.detailType}\".`,\n );\n }\n\n const candidate = asEnvelopeCandidate(event.detail);\n\n if (!isSupportedEnvelopeVersion(candidate.envelopeVersion)) {\n throw new UnsupportedEnvelopeVersionError(\n `Envelope version \"${candidate.envelopeVersion}\" is outside the SDK's supported range.`,\n );\n }\n\n const envelope: WorkflowEvent<TPayload> = {\n eventId: candidate.eventId,\n attempt: candidate.attempt,\n correlationId: candidate.correlationId,\n causationId: candidate.causationId,\n actor: candidate.actor,\n occurredAt: candidate.occurredAt,\n envelopeVersion: candidate.envelopeVersion,\n payload: candidate.payload as TPayload,\n };\n\n return {\n envelope,\n dedupKey: { eventId: envelope.eventId, attempt: envelope.attempt },\n };\n}\n\n/**\n * Validate that the EventBridge `detail` (which carries the workflow\n * envelope) has every required field with a plausible type. Returns a\n * typed `WorkflowEvent<unknown>` so the caller can narrow `payload`\n * once routing has succeeded.\n */\nfunction asEnvelopeCandidate(detail: unknown): WorkflowEvent<unknown> {\n if (detail === null || typeof detail !== \"object\") {\n throw new InvalidWorkflowEventError(\n \"EventBridge detail is not a non-null object.\",\n );\n }\n\n const obj = detail as Record<string, unknown>;\n\n assertString(obj, \"eventId\");\n assertPositiveInteger(obj, \"attempt\");\n assertString(obj, \"correlationId\");\n assertCausationId(obj);\n assertActor(obj);\n assertString(obj, \"occurredAt\");\n assertString(obj, \"envelopeVersion\");\n\n if (!(\"payload\" in obj)) {\n throw new InvalidWorkflowEventError(\n \"Envelope is missing required field: payload.\",\n );\n }\n\n return obj as unknown as WorkflowEvent<unknown>;\n}\n\nfunction assertString(\n obj: Record<string, unknown>,\n field: string,\n): asserts obj is Record<string, unknown> & Record<typeof field, string> {\n const value = obj[field];\n if (typeof value !== \"string\" || value.length === 0) {\n throw new InvalidWorkflowEventError(\n `Envelope field \"${field}\" must be a non-empty string.`,\n );\n }\n}\n\nfunction assertPositiveInteger(\n obj: Record<string, unknown>,\n field: string,\n): void {\n const value = obj[field];\n if (typeof value !== \"number\" || !Number.isInteger(value) || value < 1) {\n throw new InvalidWorkflowEventError(\n `Envelope field \"${field}\" must be a 1-indexed integer.`,\n );\n }\n}\n\nfunction assertCausationId(obj: Record<string, unknown>): void {\n if (!(\"causationId\" in obj)) {\n throw new InvalidWorkflowEventError(\n \"Envelope is missing required field: causationId.\",\n );\n }\n const value = obj.causationId;\n if (value !== null && (typeof value !== \"string\" || value.length === 0)) {\n throw new InvalidWorkflowEventError(\n 'Envelope field \"causationId\" must be a non-empty string or null.',\n );\n }\n}\n\nfunction assertActor(obj: Record<string, unknown>): void {\n const actor = obj.actor;\n if (actor === null || typeof actor !== \"object\") {\n throw new InvalidWorkflowEventError(\n 'Envelope field \"actor\" must be an object.',\n );\n }\n const actorObj = actor as Record<string, unknown>;\n const isUserActor =\n typeof actorObj.ohi_uid === \"string\" &&\n typeof actorObj.ohi_uname === \"string\" &&\n typeof actorObj.ohi_tid === \"string\" &&\n typeof actorObj.ohi_wid === \"string\";\n const isSystemActor = typeof actorObj.system === \"string\";\n if (!isUserActor && !isSystemActor) {\n throw new InvalidWorkflowEventError(\n 'Envelope field \"actor\" must be either a user-actor (ohi_tid, ohi_wid, ohi_uid, ohi_uname) or a system-actor ({ system: string }).',\n );\n }\n}\n\n/** Thrown when the event does not match the expected detail-type entry. */\nexport class InvalidWorkflowEventError extends Error {\n /** @param message - human-readable description of the validation failure. */\n constructor(message: string) {\n super(message);\n this.name = \"InvalidWorkflowEventError\";\n }\n}\n\n/** Thrown when the envelope version is outside the SDK's supported range. */\nexport class UnsupportedEnvelopeVersionError extends Error {\n /** @param message - human-readable description of the unsupported version. */\n constructor(message: string) {\n super(message);\n this.name = \"UnsupportedEnvelopeVersionError\";\n }\n}\n","/**\n * Environment-variable name the construct's `grantConsumer` integration\n * injects into a consumer Lambda; the runtime `WorkflowDedupClient`\n * reads it to discover the shared dedup table without a prop or import.\n *\n * The constant is the single cross-package contract between\n * `@openhi/constructs` (which emits the env var) and `@openhi/workflows`\n * (which consumes it). Renaming or removing it is a breaking change.\n */\nexport const WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR =\n \"OPENHI_WORKFLOW_DEDUP_TABLE_NAME\";\n\n/** Default TTL for dedup rows: 14 days, expressed in seconds (per TR-015). */\nexport const WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS = 14 * 24 * 60 * 60;\n\n/** Maximum length of a `consumerName` (per TR-015). */\nexport const WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH = 64;\n","import {\n ConditionalCheckFailedException,\n DynamoDBClient,\n PutItemCommand,\n UpdateItemCommand,\n} from \"@aws-sdk/client-dynamodb\";\n\nimport {\n WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS,\n WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH,\n WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR,\n} from \"./env\";\n\n/**\n * Inputs to `recordIfAbsent`.\n *\n * `eventId` and `attempt` are the dedup tuple every retryable\n * consumer derives from the standard envelope (see `parseWorkflowEvent`\n * and the `DedupKey` type); call sites typically spread the dedupKey\n * directly alongside the consumer name.\n */\nexport interface RecordIfAbsentInput {\n /** Stable logical name of the consumer. At most 64 chars; no whitespace. */\n readonly consumerName: string;\n /** Per-event UUID from the standard envelope. */\n readonly eventId: string;\n /** 1-indexed delivery attempt from the standard envelope. */\n readonly attempt: number;\n /** Override the 14-day default TTL. Must be a positive integer. */\n readonly ttlSeconds?: number;\n}\n\n/**\n * Result shape per TR-015. `recorded` is true on first delivery and\n * false on a duplicate; on a duplicate `alreadyProcessed` is also\n * true so callers can pattern-match without re-checking the boolean.\n */\nexport type RecordIfAbsentResult =\n | { readonly recorded: true }\n | { readonly recorded: false; readonly alreadyProcessed: true };\n\n/**\n * Inputs to `markFailed`.\n *\n * Updates the existing dedup row with `failed: true`, `failureReason`,\n * `failedAt` so the replay tooling (TR-016 follow-up) can re-publish\n * the originating event with a fresh `attempt`.\n */\nexport interface MarkFailedInput {\n /** Stable logical name of the consumer. */\n readonly consumerName: string;\n /** Per-event UUID. */\n readonly eventId: string;\n /** 1-indexed delivery attempt. */\n readonly attempt: number;\n /** Short string describing why the consumer gave up. */\n readonly reason: string;\n}\n\n/**\n * Runtime SDK every retryable workflow consumer calls before\n * performing its side-effect. See TR-015 for the contract.\n */\nexport interface WorkflowDedupClient {\n /**\n * Conditionally record a dedup token for the supplied consumer name\n * and dedup tuple. See `RecordIfAbsentResult` for the return shape.\n */\n recordIfAbsent(input: RecordIfAbsentInput): Promise<RecordIfAbsentResult>;\n /**\n * Mark the existing dedup row as permanently failed. Fire-and-forget\n * semantics for the caller; unexpected DynamoDB errors propagate.\n */\n markFailed(input: MarkFailedInput): Promise<void>;\n}\n\n/** Options shared by the factory and the standalone primitives. */\nexport interface WorkflowDedupClientOptions {\n /**\n * Table name. Defaults to `process.env[WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR]`\n * (populated by the `WorkflowDedupTable` construct's `grantConsumer`).\n */\n readonly tableName?: string;\n /** Override the 14-day default TTL for every `recordIfAbsent` call. */\n readonly defaultTtlSeconds?: number;\n /** Test seam — deterministic clock for `recordedAt` / `expiresAt`. */\n readonly now?: () => Date;\n}\n\n/** Factory that returns a `WorkflowDedupClient` bound to a single DynamoDB client. */\nexport function workflowDedupClient(\n dynamodb: DynamoDBClient,\n options: WorkflowDedupClientOptions = {},\n): WorkflowDedupClient {\n return {\n recordIfAbsent: (input) => recordIfAbsent(dynamodb, input, options),\n markFailed: (input) => markFailed(dynamodb, input, options),\n };\n}\n\n/**\n * Standalone primitive — exposed for callers that prefer it over the\n * `WorkflowDedupClient` indirection.\n */\nexport async function recordIfAbsent(\n dynamodb: DynamoDBClient,\n input: RecordIfAbsentInput,\n options: WorkflowDedupClientOptions = {},\n): Promise<RecordIfAbsentResult> {\n assertConsumerName(input.consumerName);\n assertPositiveInteger(input.attempt, \"attempt\");\n const ttlSeconds =\n input.ttlSeconds ??\n options.defaultTtlSeconds ??\n WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS;\n if (!Number.isInteger(ttlSeconds) || ttlSeconds <= 0) {\n throw new WorkflowDedupInvalidInputError(\n `ttlSeconds must be a positive integer; got ${ttlSeconds}.`,\n );\n }\n\n const tableName = resolveTableName(options.tableName);\n const now = (options.now ?? defaultNow)();\n const sk = encodeSortKey(input.eventId, input.attempt);\n const expiresAt = Math.floor(now.getTime() / 1000) + ttlSeconds;\n\n try {\n await dynamodb.send(\n new PutItemCommand({\n TableName: tableName,\n Item: {\n consumerName: { S: input.consumerName },\n sk: { S: sk },\n eventId: { S: input.eventId },\n attempt: { N: String(input.attempt) },\n recordedAt: { S: now.toISOString() },\n expiresAt: { N: String(expiresAt) },\n },\n ConditionExpression:\n \"attribute_not_exists(consumerName) AND attribute_not_exists(sk)\",\n }),\n );\n return { recorded: true };\n } catch (err) {\n if (err instanceof ConditionalCheckFailedException) {\n return { recorded: false, alreadyProcessed: true };\n }\n throw err;\n }\n}\n\n/** Standalone primitive — flips `failed: true` on an existing dedup row. */\nexport async function markFailed(\n dynamodb: DynamoDBClient,\n input: MarkFailedInput,\n options: WorkflowDedupClientOptions = {},\n): Promise<void> {\n assertConsumerName(input.consumerName);\n assertPositiveInteger(input.attempt, \"attempt\");\n if (input.reason.length === 0) {\n throw new WorkflowDedupInvalidInputError(\"reason must be non-empty.\");\n }\n\n const tableName = resolveTableName(options.tableName);\n const now = (options.now ?? defaultNow)();\n const sk = encodeSortKey(input.eventId, input.attempt);\n\n await dynamodb.send(\n new UpdateItemCommand({\n TableName: tableName,\n Key: {\n consumerName: { S: input.consumerName },\n sk: { S: sk },\n },\n UpdateExpression:\n \"SET #failed = :failed, #failureReason = :reason, #failedAt = :failedAt\",\n ExpressionAttributeNames: {\n \"#failed\": \"failed\",\n \"#failureReason\": \"failureReason\",\n \"#failedAt\": \"failedAt\",\n },\n ExpressionAttributeValues: {\n \":failed\": { BOOL: true },\n \":reason\": { S: input.reason },\n \":failedAt\": { S: now.toISOString() },\n },\n }),\n );\n}\n\n/** Compose the composite sort key per the TR-015 encoding. */\nexport function encodeSortKey(eventId: string, attempt: number): string {\n if (eventId.length === 0) {\n throw new WorkflowDedupInvalidInputError(\"eventId must be non-empty.\");\n }\n return `${eventId}#${attempt}`;\n}\n\nfunction resolveTableName(explicit?: string): string {\n const name = explicit ?? process.env[WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR];\n if (!name) {\n throw new WorkflowDedupTableNameMissingError(\n `Workflow dedup table name not set. Pass options.tableName or set ${WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR}.`,\n );\n }\n return name;\n}\n\nfunction assertConsumerName(consumerName: string): void {\n if (consumerName.length === 0) {\n throw new WorkflowDedupInvalidInputError(\"consumerName must be non-empty.\");\n }\n if (consumerName.length > WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH) {\n throw new WorkflowDedupInvalidInputError(\n `consumerName must be ≤${WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH} chars; got ${consumerName.length}.`,\n );\n }\n if (/\\s/.test(consumerName)) {\n throw new WorkflowDedupInvalidInputError(\n \"consumerName must not contain whitespace.\",\n );\n }\n}\n\nfunction assertPositiveInteger(value: number, field: string): void {\n if (!Number.isInteger(value) || value < 1) {\n throw new WorkflowDedupInvalidInputError(\n `${field} must be a 1-indexed integer; got ${value}.`,\n );\n }\n}\n\nfunction defaultNow(): Date {\n return new Date();\n}\n\n/** Thrown when the dedup table name cannot be resolved. */\nexport class WorkflowDedupTableNameMissingError extends Error {\n /** @param message - human-readable description. */\n constructor(message: string) {\n super(message);\n this.name = \"WorkflowDedupTableNameMissingError\";\n }\n}\n\n/** Thrown when an input violates a TR-015 invariant. */\nexport class WorkflowDedupInvalidInputError extends Error {\n /** @param message - human-readable description. */\n constructor(message: string) {\n super(message);\n this.name = \"WorkflowDedupInvalidInputError\";\n }\n}\n","export {\n WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS,\n WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH,\n WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR,\n} from \"./env\";\nexport {\n WorkflowDedupInvalidInputError,\n WorkflowDedupTableNameMissingError,\n encodeSortKey,\n markFailed,\n recordIfAbsent,\n workflowDedupClient,\n} from \"./workflow-dedup-client\";\nexport type {\n MarkFailedInput,\n RecordIfAbsentInput,\n RecordIfAbsentResult,\n WorkflowDedupClient,\n WorkflowDedupClientOptions,\n} from \"./workflow-dedup-client\";\n","export {\n ENVELOPE_VERSION,\n isSupportedEnvelopeVersion,\n} from \"./envelope-version\";\nexport {\n MissingActorContextError,\n isWorkflowSystemActor,\n isWorkflowUserActor,\n workflowUserActorFromClaims,\n} from \"./envelope\";\nexport type {\n WorkflowActor,\n WorkflowEvent,\n WorkflowSystemActor,\n WorkflowUserActor,\n} from \"./envelope\";\nexport {\n DEFAULT_BUS_NAME_BY_SOURCE,\n OPENHI_CONTROL_SOURCE,\n OPENHI_DATA_SOURCE,\n OPENHI_OPS_SOURCE,\n} from \"./sources\";\nexport type { OpenHiSource } from \"./sources\";\nexport {\n InvalidDetailTypeRegistrationError,\n PlatformDeploymentCompletedV1,\n PlatformSystemDataSeededV1,\n defineDetailType,\n isWellFormedDetailType,\n} from \"./detail-types\";\nexport type {\n PlatformDeploymentCompletedV1Detail,\n PlatformSystemDataSeededV1Detail,\n WorkflowDetailTypeEntry,\n} from \"./detail-types\";\nexport {\n WorkflowPublishError,\n publishWorkflowEvent,\n workflowsClient,\n} from \"./publisher\";\nexport type {\n PublishContext,\n PublishResult,\n PublisherOptions,\n WorkflowsClient,\n} from \"./publisher\";\nexport {\n InvalidWorkflowEventError,\n UnsupportedEnvelopeVersionError,\n parseWorkflowEvent,\n} from \"./consumer\";\nexport type {\n DedupKey,\n EventBridgeEventLike,\n ParsedWorkflowEvent,\n} from \"./consumer\";\nexport {\n WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS,\n WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH,\n WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR,\n WorkflowDedupInvalidInputError,\n WorkflowDedupTableNameMissingError,\n encodeSortKey,\n markFailed,\n recordIfAbsent,\n workflowDedupClient,\n} from \"./dedup\";\nexport type {\n MarkFailedInput,\n RecordIfAbsentInput,\n RecordIfAbsentResult,\n WorkflowDedupClient,\n WorkflowDedupClientOptions,\n} from \"./dedup\";\n","import { createHash } from \"node:crypto\";\nimport {\n AdminCreateUserCommand,\n AdminGetUserCommand,\n AdminSetUserPasswordCommand,\n CognitoIdentityProviderClient,\n UsernameExistsException,\n type AttributeType,\n} from \"@aws-sdk/client-cognito-identity-provider\";\nimport { DynamoDBClient } from \"@aws-sdk/client-dynamodb\";\nimport {\n CONTROL_PLANE_ROLE_CODE,\n CONTROL_PLANE_ROLE_CONCEPTS,\n CONTROL_PLANE_ROLE_IDS,\n extractSummary,\n type ControlPlaneRoleCode,\n type FhirResourceLike,\n} from \"@openhi/types\";\nimport {\n parseWorkflowEvent,\n workflowDedupClient,\n type WorkflowDedupClient,\n} from \"@openhi/workflows\";\nimport type { EventBridgeEvent } from \"aws-lambda\";\nimport {\n DEMO_PERIOD,\n DEMO_TENANT_SPECS,\n DEV_USERS,\n PLACEHOLDER_TENANT_ID,\n PLACEHOLDER_WORKSPACE_ID,\n PLATFORM_SCOPE_TENANT_ID,\n PlatformSystemDataSeededV1,\n SEED_DEMO_DATA_CONSUMER_NAME,\n demoMembershipId,\n demoRoleAssignmentId,\n demoRolesForUserInTenant,\n demoScenarioIdentifier,\n openhiResourceIdentifier,\n type DemoDevUser,\n} from \"./events\";\nimport { getDynamoControlService } from \"../../../data/dynamo/dynamo-control-service\";\nimport { NotFoundError } from \"../../../data/errors\";\nimport type { OpenHiContext } from \"../../../data/openhi-context\";\nimport { createMembershipOperation } from \"../../../data/operations/control/membership/membership-create-operation\";\nimport { getRoleByIdOperation } from \"../../../data/operations/control/role/role-get-by-id-operation\";\nimport { createRoleAssignmentOperation } from \"../../../data/operations/control/roleassignment/roleassignment-create-operation\";\nimport { createTenantOperation } from \"../../../data/operations/control/tenant/tenant-create-operation\";\nimport { createWorkspaceOperation } from \"../../../data/operations/control/workspace/workspace-create-operation\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-demo-data/seed-demo-data-handler.md\n *\n * EventBridge workflow handler invoked once per platform-deploy event\n * on the control event bus. Pre-flight verifies that\n * `seed-system-data` has already seeded the canonical Role records,\n * then idempotently re-asserts the demo-data graph: the placeholder\n * Tenant + Workspace, the three demo tenants + their workspaces, and\n * for each entry in {@link DEV_USERS} a Cognito user, a DynamoDB User,\n * 4 Memberships, 4 `tenant-admin` RoleAssignments, plus 1\n * platform-scoped `system-admin` RoleAssignment.\n */\n\ntype SeedDemoDataEvent = EventBridgeEvent<\n \"platform.system-data-seeded.v1\",\n unknown\n>;\n\n/** Env var the lambda construct injects with the Cognito User Pool ID. */\nexport const SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR =\n \"SEED_DEMO_DATA_USER_POOL_ID\";\n\n/** Inputs the handler needs to provision Cognito users for every dev. */\nexport interface CognitoProvisioner {\n /**\n * Ensure a Cognito user exists for `email`. Returns the user's\n * `sub`. Implementations MUST be idempotent: a second invocation\n * for the same email returns the existing user's sub without\n * resetting its password or touching any attribute.\n */\n readonly ensureUser: (email: string) => Promise<string>;\n}\n\n/**\n * Dependency seam for tests. The factory mirrors the production\n * arrangement: a real DynamoDB client + the real `workflowDedupClient`\n * factory bound to the `OPENHI_WORKFLOW_DEDUP_TABLE_NAME` env var the\n * construct injects via `grantConsumer`.\n */\nexport interface SeedDemoDataDependencies {\n readonly dedupClient: WorkflowDedupClient;\n /**\n * Reads every id in `CONTROL_PLANE_ROLE_IDS`. Throws when any Role\n * record is missing — that means `seed-system-data` has not yet\n * run on this environment, and emitting demo RoleAssignments that\n * reference non-existent Roles would produce orphaned records.\n */\n readonly verifyRoles: () => Promise<void>;\n /**\n * Upserts every Tenant + Workspace in {@link DEMO_TENANT_SPECS},\n * plus per-dev-user Cognito users, DynamoDB User records,\n * Memberships, and RoleAssignments.\n */\n readonly seedDemoGraph: (params: {\n readonly baseContext: OpenHiContext;\n readonly devUsers: ReadonlyArray<DemoDevUser>;\n readonly cognito: CognitoProvisioner;\n }) => Promise<void>;\n /** Cognito provisioner threaded into `seedDemoGraph`. */\n readonly cognito: CognitoProvisioner;\n}\n\nconst errorMessage = (err: unknown): string => {\n if (err instanceof Error) {\n return err.message;\n }\n return String(err);\n};\n\n/**\n * Map a role code back to its canonical stable id from\n * `CONTROL_PLANE_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: ControlPlaneRoleCode): string => {\n for (const key of Object.keys(CONTROL_PLANE_ROLE_IDS) as Array<\n keyof typeof CONTROL_PLANE_ROLE_IDS\n >) {\n if (CONTROL_PLANE_ROLE_CONCEPTS[key].code === code) {\n return CONTROL_PLANE_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 * `CONTROL_PLANE_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(CONTROL_PLANE_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: ControlPlaneRoleCode,\n roleAssignmentId: string,\n): Record<string, unknown> => ({\n identifier: [\n demoScenarioIdentifier(scenario, `roleassignment-${user.id}-${roleCode}`),\n openhiResourceIdentifier({\n tenantId,\n workspaceId: \"\",\n resourceType: \"RoleAssignment\",\n id: roleAssignmentId,\n }),\n ],\n user: { reference: `User/${user.id}`, type: \"User\" },\n role: { reference: `Role/${idForRoleCode(roleCode)}`, type: \"Role\" },\n tenant: { reference: `Tenant/${tenantId}`, type: \"Tenant\" },\n period: DEMO_PERIOD,\n});\n\n/** Build the FHIR User resource body for a seeded dev user. */\nconst userResourceBody = (\n user: DemoDevUser,\n cognitoSub: string,\n): Record<string, unknown> => ({\n resourceType: \"User\",\n id: user.id,\n name: [{ text: user.email }],\n status: \"active\",\n cognitoSub,\n currentTenant: { reference: `Tenant/${PLACEHOLDER_TENANT_ID}` },\n currentWorkspace: { reference: `Workspace/${PLACEHOLDER_WORKSPACE_ID}` },\n});\n\n/**\n * Idempotent User upsert. The User entity is non-tenant-isolated, so\n * the put is unconditional — the entity's PK is `USER#ID#<id>` and\n * supplying the same id re-asserts the same row.\n */\nconst upsertUser = async (\n context: OpenHiContext,\n user: DemoDevUser,\n cognitoSub: string,\n): Promise<void> => {\n const service = getDynamoControlService();\n const resource = userResourceBody(user, cognitoSub);\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n await service.entities.user\n .put({\n id: user.id,\n cognitoSub,\n resource: JSON.stringify(resource),\n summary,\n vid: \"1\",\n lastUpdated: context.date ?? new Date().toISOString(),\n })\n .go();\n};\n\n/**\n * Upsert the full demo-data graph. Walks each spec in\n * {@link DEMO_TENANT_SPECS}: Tenant → Workspaces. Then per dev user:\n * Cognito user → DynamoDB User → per-tenant Membership +\n * `tenant-admin` RoleAssignment → platform-scoped `system-admin`\n * RoleAssignment. Every put is keyed by a deterministic stable id so\n * re-runs after dedup-TTL expiry upsert the same records.\n *\n * Exported so the seeder test file can exercise it directly against\n * a mocked DynamoControlService; the production handler reaches it\n * through {@link SeedDemoDataDependencies.seedDemoGraph}.\n */\nexport const seedDemoGraph = async (params: {\n baseContext: OpenHiContext;\n devUsers: ReadonlyArray<DemoDevUser>;\n cognito: CognitoProvisioner;\n}): Promise<void> => {\n const { baseContext, devUsers, cognito } = params;\n\n // Phase 1: Tenants + Workspaces (no Memberships / RoleAssignments\n // yet — those need the per-user Cognito sub).\n for (const spec of DEMO_TENANT_SPECS) {\n const tenantContext: OpenHiContext = {\n ...baseContext,\n tenantId: spec.tenantId,\n };\n\n await createTenantOperation({\n context: tenantContext,\n body: { id: spec.tenantId, resource: tenantResourceBody(spec) },\n });\n\n for (const workspace of spec.workspaces) {\n await createWorkspaceOperation({\n context: tenantContext,\n body: {\n id: workspace.id,\n resource: workspaceResourceBody(spec, workspace),\n },\n });\n }\n }\n\n // Phase 2: per-dev-user records. The Cognito provisioner is the\n // only step that can fail in a way EventBridge can usefully retry —\n // it runs first so a failed Cognito call short-circuits before any\n // DynamoDB writes for that user.\n for (const user of devUsers) {\n const cognitoSub = await cognito.ensureUser(user.email);\n\n await upsertUser(baseContext, user, cognitoSub);\n\n for (const spec of DEMO_TENANT_SPECS) {\n const tenantContext: OpenHiContext = {\n ...baseContext,\n tenantId: spec.tenantId,\n };\n\n const membershipId = demoMembershipId(user.id, spec.tenantId);\n await createMembershipOperation({\n context: tenantContext,\n body: {\n id: membershipId,\n resource: membershipResourceBody(spec, user, membershipId),\n },\n });\n\n for (const roleCode of demoRolesForUserInTenant(user, spec.tenantId)) {\n const raId = demoRoleAssignmentId(user.id, spec.tenantId, roleCode);\n await createRoleAssignmentOperation({\n context: tenantContext,\n body: {\n id: raId,\n resource: roleAssignmentResourceBody(\n spec.scenario,\n spec.tenantId,\n user,\n roleCode,\n raId,\n ),\n },\n });\n }\n }\n\n // Platform-scoped system-admin RoleAssignment. The sentinel\n // tenantId is PLATFORM_SCOPE_TENANT_ID — there is no real Tenant\n // to point at, so the operation runs with an OpenHiContext whose\n // tenantId is the sentinel.\n const platformContext: OpenHiContext = {\n ...baseContext,\n tenantId: PLATFORM_SCOPE_TENANT_ID,\n };\n const platformRoleCode = CONTROL_PLANE_ROLE_CODE.SYSTEM_ADMIN;\n const platformRaId = demoRoleAssignmentId(\n user.id,\n PLATFORM_SCOPE_TENANT_ID,\n platformRoleCode,\n );\n await createRoleAssignmentOperation({\n context: platformContext,\n body: {\n id: platformRaId,\n resource: roleAssignmentResourceBody(\n \"platform\",\n PLATFORM_SCOPE_TENANT_ID,\n user,\n platformRoleCode,\n platformRaId,\n ),\n },\n });\n }\n};\n\n/**\n * Test-visible orchestrator. The production `handler` calls this with\n * real dependencies and the hardcoded {@link DEV_USERS} list; unit\n * tests inject fakes and pass the dev-user list directly.\n */\nexport const runSeedDemoData = async (\n event: SeedDemoDataEvent,\n deps: SeedDemoDataDependencies,\n devUsers: ReadonlyArray<DemoDevUser>,\n): Promise<void> => {\n const parsed = parseWorkflowEvent(event, PlatformSystemDataSeededV1);\n\n const recordResult = await deps.dedupClient.recordIfAbsent({\n consumerName: SEED_DEMO_DATA_CONSUMER_NAME,\n eventId: parsed.dedupKey.eventId,\n attempt: parsed.dedupKey.attempt,\n });\n if (!recordResult.recorded) {\n return;\n }\n\n const baseContext: OpenHiContext = {\n tenantId: \"\",\n workspaceId: \"\",\n date: parsed.envelope.occurredAt,\n actorId: \"platform-deploy-bridge\",\n actorName: \"Platform Deploy Bridge\",\n actorType: \"internal-system\",\n source: \"step-function\",\n };\n\n try {\n await deps.verifyRoles();\n await deps.seedDemoGraph({\n baseContext,\n devUsers,\n cognito: deps.cognito,\n });\n } catch (err) {\n // Mark the dedup row failed so the replay tooling (TR-016 follow-up)\n // can re-publish the originating event with a fresh attempt. The\n // re-throw keeps EventBridge's failure-detection path intact — the\n // event ends up in the DLQ + the workflow Lambda's failure\n // CloudWatch alarm fires.\n await deps.dedupClient.markFailed({\n consumerName: SEED_DEMO_DATA_CONSUMER_NAME,\n eventId: parsed.dedupKey.eventId,\n attempt: parsed.dedupKey.attempt,\n reason: errorMessage(err),\n });\n throw err;\n }\n};\n\n/**\n * Deterministic password derived from the user's email. Re-running\n * the algorithm with the same email reproduces the password, so devs\n * can recover their own credentials from the docs page without the\n * workflow ever surfacing them. The shape satisfies the default\n * Cognito password policy (≥8 chars, upper + lower + number + symbol).\n */\nexport const devPasswordForEmail = (email: string): string => {\n const digest = createHash(\"sha256\").update(email).digest();\n const base64url = digest\n .toString(\"base64\")\n .replace(/\\+/g, \"-\")\n .replace(/\\//g, \"_\")\n .replace(/=+$/g, \"\");\n return `Dev-${base64url.slice(0, 20)}!1`;\n};\n\n/**\n * Production Cognito provisioner backed by the AWS SDK. Reads the\n * user-pool id from the env var the lambda construct injects.\n *\n * Idempotency contract:\n * - On first invocation, calls `AdminCreateUser` (with\n * `MessageAction: SUPPRESS` so no invitation email fires) then\n * `AdminSetUserPassword` (permanent). Returns the new user's sub.\n * - On subsequent invocations, `AdminCreateUser` throws\n * `UsernameExistsException`; the provisioner catches it, calls\n * `AdminGetUser` to read the existing user's sub, and returns\n * **without** touching the password or any attribute.\n */\nexport const productionCognitoProvisioner = (): CognitoProvisioner => {\n const client = new CognitoIdentityProviderClient({});\n const userPoolId = process.env[SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR];\n if (!userPoolId || userPoolId.trim() === \"\") {\n throw new Error(\n `${SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR} is not set; the seed-demo-data Lambda ` +\n \"cannot provision Cognito users without a user-pool id.\",\n );\n }\n const subFromAttributes = (\n attrs: ReadonlyArray<AttributeType> | undefined,\n ): string | undefined => {\n for (const attr of attrs ?? []) {\n if (attr.Name === \"sub\" && typeof attr.Value === \"string\") {\n return attr.Value;\n }\n }\n return undefined;\n };\n return {\n ensureUser: async (email: string): Promise<string> => {\n try {\n const created = await client.send(\n new AdminCreateUserCommand({\n UserPoolId: userPoolId,\n Username: email,\n MessageAction: \"SUPPRESS\",\n UserAttributes: [\n { Name: \"email\", Value: email },\n { Name: \"email_verified\", Value: \"true\" },\n ],\n }),\n );\n await client.send(\n new AdminSetUserPasswordCommand({\n UserPoolId: userPoolId,\n Username: email,\n Password: devPasswordForEmail(email),\n Permanent: true,\n }),\n );\n const sub = subFromAttributes(created.User?.Attributes);\n if (!sub) {\n throw new Error(\n `AdminCreateUser response for \"${email}\" did not carry a sub attribute.`,\n );\n }\n return sub;\n } catch (err) {\n if (err instanceof UsernameExistsException) {\n // User already exists — skip password reset entirely and\n // just read the existing sub.\n const got = await client.send(\n new AdminGetUserCommand({\n UserPoolId: userPoolId,\n Username: email,\n }),\n );\n const sub = subFromAttributes(got.UserAttributes);\n if (!sub) {\n throw new Error(\n `AdminGetUser response for \"${email}\" did not carry a sub attribute.`,\n );\n }\n return sub;\n }\n throw err;\n }\n },\n };\n};\n\nconst productionDependencies = (): SeedDemoDataDependencies => {\n const dynamodb = new DynamoDBClient({});\n const cognito = productionCognitoProvisioner();\n return {\n dedupClient: workflowDedupClient(dynamodb),\n verifyRoles: verifySystemRolesExist,\n seedDemoGraph,\n cognito,\n };\n};\n\nexport const handler = async (event: SeedDemoDataEvent): Promise<void> =>\n runSeedDemoData(event, productionDependencies(), DEV_USERS);\n","import {\n CONTROL_PLANE_ROLE_CODE,\n type ControlPlaneRoleCode,\n} from \"@openhi/types\";\nimport { PlatformSystemDataSeededV1 } from \"@openhi/workflows\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-demo-data/events.md\n */\n\n/**\n * Stable logical name this workflow registers with the shared\n * `WorkflowDedupTable` (TR-015). Used in both the construct grant\n * (`workflowDedupTable.grantConsumer(lambda, SEED_DEMO_DATA_CONSUMER_NAME)`)\n * and the handler's runtime `recordIfAbsent` call — keep them aligned by\n * importing this constant in both places.\n */\nexport const SEED_DEMO_DATA_CONSUMER_NAME = \"seed-demo-data\";\n\n/** FHIR `Identifier.system` for the demo-scenario URN scheme. */\nexport const DEMO_URN_SYSTEM = \"urn:openhi:demo\";\n\n/**\n * FHIR `Identifier.system` for the canonical OpenHI resource URN\n * (ADR 2026-03-12-01). The value form is\n * `urn:ohi:<tenantId>:<workspaceId>:<resourceType>:<id>`; empty\n * workspaceId means the resource has tenant-wide scope (or is itself\n * the workspace identity, in the Workspace case).\n */\nexport const OPENHI_RESOURCE_URN_SYSTEM = \"http://openhi.org/\";\n\n/**\n * Period stamped on every demo Membership and RoleAssignment. Fixed\n * start so re-runs produce byte-identical bodies (decision #4); no\n * end so demo memberships are open-ended.\n */\nexport const DEMO_PERIOD = { start: \"2026-01-01T00:00:00Z\" } as const;\n\n/**\n * Sentinel `tenantId` used on every dev user's `system-admin`\n * RoleAssignment. A `system-admin` RA is platform-scoped (it spans\n * every tenant), but the RoleAssignment entity requires a tenantId on\n * its key for sharding — there is no real tenant to point at. The\n * `\"platform\"` literal is a reserved value that never matches a real\n * Tenant id and signals \"this RA scopes across all tenants\".\n *\n * Renaming this constant is a wire-format break — the IAM grant in\n * `seed-demo-data-lambda.ts` enumerates exact-match `LeadingKeys`\n * computed from this value, and the in-band records written under it\n * become unreachable if the sentinel changes.\n */\nexport const PLATFORM_SCOPE_TENANT_ID = \"platform\";\n\n/** Placeholder Tenant id seeded by the workflow as the dev-user `currentTenant`. */\nexport const PLACEHOLDER_TENANT_ID = \"placeholder-tenant-id\";\n\n/** Placeholder Workspace id seeded by the workflow as the dev-user `currentWorkspace`. */\nexport const PLACEHOLDER_WORKSPACE_ID = \"placeholder-workspace-id\";\n\n/**\n * Dev-user descriptor. Every entry produces:\n * - one Cognito user (idempotent create-then-skip),\n * - one DynamoDB User record (id = `dev-<email-local-part>`),\n * - four Memberships (placeholder + the three demo tenants),\n * - four `tenant-admin` RoleAssignments (one per tenant the user\n * belongs to),\n * - one `system-admin` RoleAssignment scoped to {@link PLATFORM_SCOPE_TENANT_ID}.\n */\nexport interface DemoDevUser {\n /** Stable DynamoDB User id. */\n readonly id: string;\n /** Email used as the Cognito `Username` and as the seed for the password algorithm. */\n readonly email: string;\n}\n\n/**\n * Hardcoded dev-user roster. Adding a new developer is a one-line\n * change here plus a re-deploy. The list intentionally lives in-source\n * (not behind an env-var seam) so the IAM grant in\n * `seed-demo-data-lambda.ts` can enumerate per-user PKs at synth time.\n */\nexport const DEV_USERS: ReadonlyArray<DemoDevUser> = [\n { id: \"dev-russell\", email: \"russell@codedrifters.com\" },\n { id: \"dev-cameron\", email: \"cameron@codedrifters.com\" },\n { id: \"dev-neelima\", email: \"neelima@codedrifters.com\" },\n { id: \"dev-garon\", email: \"garon@codedrifters.com\" },\n { id: \"dev-dave\", email: \"dave@codedrifters.com\" },\n { id: \"dev-drew\", email: \"drew@codedrifters.com\" },\n { id: \"dev-jessica\", email: \"jessica@codedrifters.com\" },\n { id: \"dev-jared\", email: \"jared@codedrifters.com\" },\n { id: \"dev-goddess\", email: \"goddess@codedrifters.com\" },\n];\n\n/**\n * A single workspace inside a demo tenant. The mixed tenant has two\n * workspaces (wound-care and primary-care sub-workspaces); the other\n * two tenants have one each.\n */\nexport interface DemoWorkspaceSpec {\n /** Stable id (DynamoDB record id; also drives the canonical OHI URN). */\n readonly id: string;\n /** FHIR `Workspace.name`. */\n readonly name: string;\n /**\n * Role suffix used in the demo URN value (`<scenario>:<roleSuffix>`).\n * Mirrors seed-fixtures' role suffix convention: `workspace` for\n * single-workspace tenants, `workspace-<sub>` for the mixed tenant.\n */\n readonly roleSuffix: string;\n}\n\n/**\n * One demo tenant + the workspaces it owns. Re-exported via {@link\n * DEMO_TENANT_SPECS} as the canonical list; iterate that constant in\n * the handler and the IAM-grant builder so the value sets agree.\n */\nexport interface DemoTenantSpec {\n /**\n * Scenario slug — `placeholder`, `demo-wound-care`, `demo-primary-care`,\n * `demo-mixed`. The placeholder tenant's slug is `placeholder`; the\n * three demo tenants mirror seed-fixtures' `fixture-*` slugs renamed\n * to `demo-*`.\n */\n readonly scenario: string;\n /** Stable id (DynamoDB record id; also drives the canonical OHI URN). */\n readonly tenantId: string;\n /** FHIR `Tenant.name`. */\n readonly tenantName: string;\n /** Workspaces owned by this tenant. */\n readonly workspaces: ReadonlyArray<DemoWorkspaceSpec>;\n}\n\n/**\n * The full demo-tenant graph. Four entries: the placeholder tenant the\n * JWT-claim fallback resolves to, plus the three v1 demo scenarios\n * (OPS-009 §\"v1 scenarios\"):\n *\n * 0. Placeholder tenant — dereferences the JWT-claim fallback in\n * `pre-token-generation.handler.ts` and acts as every dev user's\n * `currentTenant`/`currentWorkspace` so Post-Confirmation skips\n * default provisioning when a seeded User signs in.\n * 1. Single-workspace wound-care tenant.\n * 2. Single-workspace primary-care tenant.\n * 3. Two-workspace mixed tenant — exercises the cross-workspace\n * isolation flow that single-workspace tenants cannot.\n */\nexport const DEMO_TENANT_SPECS: ReadonlyArray<DemoTenantSpec> = [\n {\n scenario: \"placeholder\",\n tenantId: PLACEHOLDER_TENANT_ID,\n tenantName: \"OpenHI Placeholder Tenant\",\n workspaces: [\n {\n id: PLACEHOLDER_WORKSPACE_ID,\n name: \"OpenHI Placeholder Workspace\",\n roleSuffix: \"workspace\",\n },\n ],\n },\n {\n scenario: \"demo-wound-care\",\n tenantId: \"demo-wound-care-tenant\",\n tenantName: \"Cedarbrook Wound Healing Institute\",\n workspaces: [\n {\n id: \"demo-wound-care-workspace\",\n name: \"Cedarbrook Outpatient Wound Clinic\",\n roleSuffix: \"workspace\",\n },\n ],\n },\n {\n scenario: \"demo-primary-care\",\n tenantId: \"demo-primary-care-tenant\",\n tenantName: \"Maple Ridge Family Medicine\",\n workspaces: [\n {\n id: \"demo-primary-care-workspace\",\n name: \"Maple Ridge Main Street Office\",\n roleSuffix: \"workspace\",\n },\n ],\n },\n {\n scenario: \"demo-mixed\",\n tenantId: \"demo-mixed-tenant\",\n tenantName: \"Northbridge Health Network\",\n workspaces: [\n {\n id: \"demo-mixed-workspace-wound-care\",\n name: \"Northbridge Wound Care Center\",\n roleSuffix: \"workspace-wound-care\",\n },\n {\n id: \"demo-mixed-workspace-primary-care\",\n name: \"Northbridge Family Practice\",\n roleSuffix: \"workspace-primary-care\",\n },\n ],\n },\n];\n\n/** Stable Membership id derived from `(devUserId, tenantId)`. */\nexport const demoMembershipId = (devUserId: string, tenantId: string): string =>\n `demo-membership-${devUserId}-${tenantId}`;\n\n/**\n * Stable RoleAssignment id derived from `(devUserId, tenantId, roleCode)`.\n * Each (user, tenant, role) tuple maps to exactly one record — re-runs\n * upsert the same id.\n */\nexport const demoRoleAssignmentId = (\n devUserId: string,\n tenantId: string,\n roleCode: ControlPlaneRoleCode,\n): string => `demo-roleassignment-${devUserId}-${tenantId}-${roleCode}`;\n\n/**\n * Demo-scenario FHIR `Identifier` entry — `urn:openhi:demo:<scenario>:<role>`.\n * Mirrors the `urn:openhi:fixture:<scenario>:<role>` pattern from\n * `@openhi/seed-fixtures/src/urn.ts`, renamed to the `demo` namespace.\n */\nexport const demoScenarioIdentifier = (\n scenario: string,\n roleSuffix: string,\n): { system: string; value: string } => ({\n system: DEMO_URN_SYSTEM,\n value: `${scenario}:${roleSuffix}`,\n});\n\n/**\n * Canonical OpenHI resource FHIR `Identifier` entry per ADR\n * 2026-03-12-01. `workspaceId` is empty for both Tenant resources and\n * Workspace resources — for a Tenant, the resource is tenant-scoped\n * with no workspace context; for a Workspace, the resource IS the\n * workspace identity rather than living inside one.\n */\nexport const openhiResourceIdentifier = (params: {\n tenantId: string;\n workspaceId: string;\n resourceType: string;\n id: string;\n}): { use: string; system: string; value: string } => ({\n use: \"unversioned\",\n system: OPENHI_RESOURCE_URN_SYSTEM,\n value: `urn:ohi:${params.tenantId}:${params.workspaceId}:${params.resourceType}:${params.id}`,\n});\n\n/**\n * Roles every dev user holds in every tenant they belong to. Per scope\n * decision Q3, every dev user is `tenant-admin` in every tenant — there\n * is no per-(user, tenant) variance to drive from.\n */\nexport const demoRolesForUserInTenant = (\n _user: DemoDevUser,\n _tenantId: string,\n): ReadonlyArray<ControlPlaneRoleCode> => {\n void _user;\n void _tenantId;\n return [CONTROL_PLANE_ROLE_CODE.TENANT_ADMIN];\n};\n\n/**\n * DynamoDB single-table partition-key builders. The IAM grant in\n * `seed-demo-data-lambda.ts` uses these to enumerate exact-match\n * `dynamodb:LeadingKeys` values; the entity definitions in\n * `data/dynamo/entities/control/` own the canonical key templates.\n *\n * These builders MUST emit the keys ElectroDB actually writes — not\n * the entity definition's pretty template. None of the control-plane\n * entities sets `casing: \"none\"` on the base-table PK template, so\n * ElectroDB applies its default lowercase casing at runtime: the\n * entity's `ROLE#ID#${id}` becomes `role#id#<id>` on the wire. A\n * builder that returns the uppercase template form produces a\n * silently-broken IAM grant (every PutItem denied with \"no\n * identity-based policy allows\" because the request's leading-key\n * never matches a policy value).\n */\nexport const rolePartitionKey = (roleId: string): string => `role#id#${roleId}`;\n\nexport const demoTenantPartitionKey = (tenantId: string): string =>\n `tenant#id#${tenantId}`;\n\nexport const demoWorkspacePartitionKey = (\n tenantId: string,\n workspaceId: string,\n): string => `tid#${tenantId}#workspace#id#${workspaceId}`;\n\nexport const demoMembershipPartitionKey = (\n tenantId: string,\n membershipId: string,\n): string => `tid#${tenantId}#membership#id#${membershipId}`;\n\nexport const demoRoleAssignmentPartitionKey = (\n tenantId: string,\n roleAssignmentId: string,\n): string => `tid#${tenantId}#roleassignment#id#${roleAssignmentId}`;\n\n/** User entity PK template — `USER#ID#<id>` → `user#id#<id>` on the wire. */\nexport const demoUserPartitionKey = (userId: string): string =>\n `user#id#${userId}`;\n\n/**\n * Tenant + Workspace PKs the workflow writes on every fire: the 4\n * tenant PKs (placeholder + 3 demo) plus their workspaces (1 + 1 + 1 + 2 = 5).\n */\nexport const demoBasePartitionKeys = (): ReadonlyArray<string> => {\n const keys: string[] = [];\n for (const spec of DEMO_TENANT_SPECS) {\n keys.push(demoTenantPartitionKey(spec.tenantId));\n for (const workspace of spec.workspaces) {\n keys.push(demoWorkspacePartitionKey(spec.tenantId, workspace.id));\n }\n }\n return keys;\n};\n\n/**\n * Membership + RoleAssignment + User PKs the workflow writes per dev\n * user. Empty when `devUsers` is empty (used by tests). The list\n * mirrors the handler's iteration order so the IAM grant covers every\n * write the handler can make.\n *\n * Per dev user the function emits:\n * - one User PK,\n * - per tenant in {@link DEMO_TENANT_SPECS}: one Membership PK plus\n * one `tenant-admin` RoleAssignment PK,\n * - one platform-scoped `system-admin` RoleAssignment PK keyed by\n * {@link PLATFORM_SCOPE_TENANT_ID}.\n */\nexport const demoDevUserPartitionKeys = (\n devUsers: ReadonlyArray<DemoDevUser>,\n): ReadonlyArray<string> => {\n const keys: string[] = [];\n for (const user of devUsers) {\n keys.push(demoUserPartitionKey(user.id));\n for (const spec of DEMO_TENANT_SPECS) {\n keys.push(\n demoMembershipPartitionKey(\n spec.tenantId,\n demoMembershipId(user.id, spec.tenantId),\n ),\n );\n for (const roleCode of demoRolesForUserInTenant(user, spec.tenantId)) {\n keys.push(\n demoRoleAssignmentPartitionKey(\n spec.tenantId,\n demoRoleAssignmentId(user.id, spec.tenantId, roleCode),\n ),\n );\n }\n }\n // Platform-scoped system-admin RoleAssignment.\n keys.push(\n demoRoleAssignmentPartitionKey(\n PLATFORM_SCOPE_TENANT_ID,\n demoRoleAssignmentId(\n user.id,\n PLATFORM_SCOPE_TENANT_ID,\n CONTROL_PLANE_ROLE_CODE.SYSTEM_ADMIN,\n ),\n ),\n );\n }\n return keys;\n};\n\n/**\n * The trigger this workflow subscribes to on the control event bus.\n * The `seed-system-data` workflow publishes\n * `platform.system-data-seeded.v1` after it has finished re-asserting\n * the platform-singleton Role records; on every fire this workflow\n * re-asserts the demo-tenant graph.\n *\n * The indirection (subscribing to `system-data-seeded` rather than to\n * the raw `platform.deployment-completed.v1` event) enforces a strict\n * happens-before edge between the system-data seed and the demo-data\n * seed. Without it, both workflows would fire in parallel from the\n * same deploy event and seed-demo-data's pre-flight Role check would\n * race seed-system-data's puts — a race that EventBridge retries\n * resolve, but only after firing the failure alarm at least once on a\n * fresh environment's first deploy.\n *\n * Re-exported from `@openhi/workflows` for symmetry with the handler's\n * import — keep the construct + handler reading from the same registry\n * entry so a rename upstream surfaces at both call sites.\n */\nexport { PlatformSystemDataSeededV1 };\n","import { Service } from \"electrodb\";\nimport { defaultTableName, dynamoClient } from \"./dynamo-client\";\nimport { ConfigurationEntity } from \"./entities/control/configuration-entity\";\nimport { MembershipEntity } from \"./entities/control/membership-entity\";\nimport { RoleEntity } from \"./entities/control/role-entity\";\nimport { RoleAssignmentEntity } from \"./entities/control/roleassignment-entity\";\nimport { TenantEntity } from \"./entities/control/tenant-entity\";\nimport { UserEntity } from \"./entities/control/user-entity\";\nimport { WorkspaceEntity } from \"./entities/control/workspace-entity\";\n\n/**\n * Control-plane entities only (service \"control\"). Same table as data plane; use\n * DynamoDataService for data-plane entities.\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n */\n\nconst controlPlaneEntities = {\n configuration: ConfigurationEntity,\n membership: MembershipEntity,\n role: RoleEntity,\n roleAssignment: RoleAssignmentEntity,\n tenant: TenantEntity,\n user: UserEntity,\n workspace: WorkspaceEntity,\n};\n\nconst controlPlaneService = new Service(controlPlaneEntities, {\n table: defaultTableName,\n client: dynamoClient,\n});\n\n/**\n * Control-plane service: entities for configuration and control. Use with the\n * data store table (PK, SK, GSI1; UserEntity also uses GSI2).\n */\nexport const DynamoControlService = {\n entities: controlPlaneService.entities,\n};\n\nexport type DynamoControlServiceType = typeof DynamoControlService;\n\n/**\n * Returns the control-plane service. Table name is resolved from tableName (optional override),\n * then DYNAMO_TABLE_NAME, then \"jesttesttable\".\n */\nexport function getDynamoControlService(\n tableName?: string,\n): DynamoControlServiceType {\n const resolved = tableName ?? defaultTableName;\n const service = new Service(controlPlaneEntities, {\n table: resolved,\n client: dynamoClient,\n });\n return {\n entities: service.entities,\n };\n}\n","import { DynamoDBClient } from \"@aws-sdk/client-dynamodb\";\n\n/**\n * DynamoDB table name for the data store. Set via DYNAMO_TABLE_NAME at runtime\n * (e.g. from Lambda env); defaults for local/test.\n */\nexport const defaultTableName =\n process.env.DYNAMO_TABLE_NAME ?? \"jesttesttable\";\n\n/**\n * DynamoDB client. When MOCK_DYNAMODB_ENDPOINT is set (e.g. local DynamoDB or\n * jest-dynalite), uses that endpoint with no SSL and region \"local\".\n */\nexport const dynamoClient = new DynamoDBClient({\n ...(process.env.MOCK_DYNAMODB_ENDPOINT && {\n endpoint: process.env.MOCK_DYNAMODB_ENDPOINT,\n sslEnabled: false,\n region: \"local\",\n }),\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute } from \"./control-entity-common\";\n\n/**\n * Configuration data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Partially tenant-isolated, control plane. Cascade of scope\n * levels: resolution order user → workspace → tenant → baseline. Sentinels: tenantId \"BASELINE\" for\n * baseline tier; workspaceId/userId/roleId \"-\" for absent dimension.\n *\n * Key structure: PK = CONFIG#TID#<tenantId>#WID#<workspaceId>#UID#<userId>#RID#<roleId>,\n * SK = KEY#<key>#SK#<sk>. Uniqueness: one Configuration per (tenantId, workspaceId, userId, roleId, key).\n * Standard attributes and key-building conventions align with single-table design.\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Configuration entries in a tenant/workspace\n * across the four shards.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entities/configuration.md\n * @see sites/www-docs/content/architecture/control-plane/configuration.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/entity-standards.md — Key-building conventions (keys built inside entity)\n */\nexport const ConfigurationEntity = new Entity({\n model: {\n entity: \"configuration\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key. \"CURRENT\" for current version; version history in S3. */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** Tenant scope. Use \"BASELINE\" when the config is baseline default (no tenant). */\n tenantId: {\n type: \"string\" as const,\n required: true,\n default: \"BASELINE\",\n },\n /** Workspace scope. Use \"-\" when absent. */\n workspaceId: {\n type: \"string\" as const,\n required: true,\n default: \"-\",\n },\n /** User scope. Use \"-\" when absent. */\n userId: {\n type: \"string\" as const,\n required: true,\n default: \"-\",\n },\n /** Role scope. Use \"-\" when absent. */\n roleId: {\n type: \"string\" as const,\n required: true,\n default: \"-\",\n },\n /** Config type (category), e.g. endpoints, branding, display. */\n key: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR Resource.id; logical id in URL and for the Configuration resource. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Payload as JSON string. JSON.stringify(resource) on write; JSON.parse(item.resource) on read. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, key, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). Tracks current version; S3 history key. */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK, SK (data store key names). PK is built from tenantId, workspaceId, userId, roleId; SK is built from key and sk. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"tenantId\", \"workspaceId\", \"userId\", \"roleId\"],\n template:\n \"CONFIG#TID#${tenantId}#WID#${workspaceId}#UID#${userId}#RID#${roleId}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"key\", \"sk\"],\n template: \"KEY#${key}#SK#${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Configuration entries for a\n * (tenant, workspace) across the four shards. Use for \"list configs scoped to this tenant\"\n * (workspaceId = \"-\") or \"list configs scoped to this workspace\". Does not support\n * hierarchical resolution in one query; use base table GetItem in fallback order\n * (user → workspace → tenant → baseline) for that.\n * SK is `<key>#<id>` — Configuration's `key` is a required entity attribute (the\n * config category: endpoints, branding, display, …) and the natural sort/lookup\n * dimension. `casing: \"none\"` preserves the literal key value.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"tenantId\", \"workspaceId\", \"gsi1Shard\"],\n template:\n \"TID#${tenantId}#WID#${workspaceId}#RT#Configuration#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"key\", \"id\"],\n template: \"${key}#${id}\",\n },\n },\n },\n});\n","import { extractLabel } from \"@openhi/types\";\nimport { computeShard } from \"../../shard\";\n\n/**\n * Shared GSI1 shard attribute for control-plane entities.\n *\n * Control-plane entities (User, Tenant, Workspace, Membership, Role, RoleAssignment,\n * Configuration) use the same `TID#/WID#/RT#/SHARD#` PK shape on GSI1 as data-plane\n * FHIR resources per ADR-011. The shard index is derived deterministically from `id`\n * via `computeShard` so updates always land on the same shard. Stored as a string\n * because it appears as a literal segment in the GSI1 PK template; the underlying\n * value is 0..3.\n *\n * Not `required` because the value is derived via `watch`/`set`; ElectroDB's\n * required-field check runs before watch propagation, so callers must not fail\n * validation on a derived field.\n */\nexport const gsi1ShardAttribute = {\n type: \"string\" as const,\n watch: [\"id\"] as const,\n set: (_val?: string, item?: { id?: string }) => {\n if (typeof item?.id !== \"string\" || item.id.length === 0) {\n return undefined;\n }\n return String(computeShard(item.id));\n },\n};\n\n/**\n * Shared GSI1 sort-key attribute for control-plane entities.\n *\n * Derives the GSI1SK value at write time from the entity's `resource` JSON\n * string, applying the same label-extraction strategy as the data plane\n * (DR-004 / `@openhi/types` `extractLabel`). When the resource carries a\n * natural label (Tenant.name, Workspace.name, Configuration.key, …) the\n * sort key is `<normalizedLabel>#<id>` so list endpoints sort alphabetically\n * and `BEGINS_WITH` queries serve prefix searches. When no label is\n * extractable, falls back to `<entity.lastUpdated>#<id>` for stable\n * reverse-chronological ordering.\n *\n * Falls back gracefully on malformed `resource` payloads — JSON parse\n * failures and missing fields both route to the lastUpdated fallback so a\n * single bad write never blocks an entity put. The entity-level\n * `lastUpdated` is preferred over `resource.meta.lastUpdated` so the\n * fallback uses the authoritative timestamp the entity write supplies.\n *\n * Not `required` because the value is derived via `watch`/`set`.\n */\nexport const gsi1skAttribute = {\n type: \"string\" as const,\n watch: [\"resource\", \"lastUpdated\", \"id\"] as const,\n set: (\n _val?: string,\n item?: { resource?: string; lastUpdated?: string; id?: string },\n ) => {\n const id = typeof item?.id === \"string\" ? item.id : \"\";\n const lastUpdated =\n typeof item?.lastUpdated === \"string\" ? item.lastUpdated : \"\";\n const fallback = `${lastUpdated}#${id}`;\n\n if (typeof item?.resource !== \"string\" || item.resource.length === 0) {\n return fallback;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(item.resource);\n } catch {\n return fallback;\n }\n if (!parsed || typeof parsed !== \"object\") return fallback;\n const resourceType = (parsed as { resourceType?: unknown }).resourceType;\n if (typeof resourceType !== \"string\") return fallback;\n\n const label = extractLabel(parsed as Parameters<typeof extractLabel>[0]);\n return label !== undefined ? `${label}#${id}` : fallback;\n },\n};\n","/**\n * Shard selection for the data-plane single-table GSI1 partitioning per ADR-011.\n *\n * GSI1's partition key embeds a `SHARD#<n>` segment with `n = computeShard(id)`.\n * The hash is deterministic so updates to the same resource id always land on\n * the same shard (no cross-shard migration on update); reads fan out to all\n * shards in parallel and merge by SK.\n *\n * @see sites/www-docs/content/architecture/adr/ — ADR-011 (single-table DynamoDB)\n */\n\n/** Number of shards in the GSI1 partition key. Fixed at 4 in v1; raising it later is a backfill, not a schema migration. */\nexport const SHARD_COUNT = 4;\n\n/**\n * Returns a deterministic shard index in [0, SHARD_COUNT) for the given resource id.\n *\n * Implementation: 32-bit FNV-1a over the UTF-16 code units of the id, modulo SHARD_COUNT.\n * The function is pure and stable; the same id always returns the same shard.\n *\n * ESLint's `no-bitwise` rule is disabled inside this function because FNV-1a is\n * defined in terms of XOR and unsigned-right-shift — the bitwise ops are the\n * algorithm, not an accidental logical-operator confusion.\n */\nexport function computeShard(id: string): number {\n /* eslint-disable no-bitwise */\n let hash = 0x811c9dc5;\n for (let i = 0; i < id.length; i++) {\n hash ^= id.charCodeAt(i);\n hash = Math.imul(hash, 0x01000193);\n }\n return (hash >>> 0) % SHARD_COUNT;\n /* eslint-enable no-bitwise */\n}\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * Membership data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, control plane. Membership links a User\n * to a Tenant (and optionally a Workspace). One record per (tenantId, id).\n *\n * Key structure: PK = TID#<tenantId>#MEMBERSHIP#ID#<id>, SK = CURRENT.\n * Uniqueness: one Membership per (tenantId, id).\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Memberships in a tenant across the four\n * shards. Membership is tenant-scoped (not workspace-scoped), so the GSI1 PK uses `WID#-` as a\n * sentinel.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-13-02-control-plane-roles-and-user-tenant-workspace-linkage.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const MembershipEntity = new Entity({\n model: {\n entity: \"membership\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** Tenant in which the user has membership (required). */\n tenantId: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR Resource.id; membership id. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full Membership resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = TID#<tenantId>#MEMBERSHIP#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"tenantId\", \"id\"],\n template: \"TID#${tenantId}#MEMBERSHIP#ID#${id}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Memberships for a tenant across the\n * four shards. Membership is tenant-scoped only, so `WID#-` is a sentinel.\n * SK is derived via `gsi1skAttribute` — uses the resource's natural label when\n * extractable, else `<lastUpdated>#<id>` (DR-004). `casing: \"none\"` preserves the\n * normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"tenantId\", \"gsi1Shard\"],\n template: \"TID#${tenantId}#WID#-#RT#Membership#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n },\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * Role data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Non-tenant-isolated, control plane. Role is a\n * platform-wide role catalog (e.g. tenant-admin, tenant-user, system-admin); not scoped by tenant.\n * RoleAssignment references Role to assign a role to a User in a Tenant/Workspace context.\n *\n * Key structure: PK = ROLE#ID#<id>, SK = CURRENT.\n * The ROLE# prefix prevents key collisions with other non-tenant-isolated entities (User, etc.)\n * sharing the same table (ADR 2026-03-11-01 — preferred pattern for all control plane entities).\n * Uniqueness: one Role per id.\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Roles across the four shards. Non-tenant-\n * isolated, so the PK uses `TID#-#WID#-` sentinels.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-13-02-control-plane-roles-and-user-tenant-workspace-linkage.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const RoleEntity = new Entity({\n model: {\n entity: \"role\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** FHIR Resource.id; role id. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full Role resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = ROLE#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"id\"],\n template: \"ROLE#ID#${id}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Roles across the four shards.\n * Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#Role#SHARD#<n>`.\n * SK is derived via `gsi1skAttribute` — uses the resource's natural label when\n * extractable, else `<lastUpdated>#<id>` (DR-004). `casing: \"none\"` preserves the\n * normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"gsi1Shard\"],\n template: \"TID#-#WID#-#RT#Role#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n },\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * RoleAssignment data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, control plane. RoleAssignment assigns\n * a Role to a User in a Tenant (and optionally Workspace) context.\n *\n * Key structure: PK = TID#<tenantId>#ROLEASSIGNMENT#ID#<id>, SK = CURRENT.\n * Uniqueness: one RoleAssignment per (tenantId, id).\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all RoleAssignments in a tenant across the four\n * shards. Tenant-scoped only (workspace context lives inside the resource), so the GSI1 PK uses\n * `WID#-` as a sentinel.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-13-02-control-plane-roles-and-user-tenant-workspace-linkage.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const RoleAssignmentEntity = new Entity({\n model: {\n entity: \"roleassignment\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** Tenant in which the role assignment applies (required). */\n tenantId: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR Resource.id; role assignment id. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full RoleAssignment resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = TID#<tenantId>#ROLEASSIGNMENT#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"tenantId\", \"id\"],\n template: \"TID#${tenantId}#ROLEASSIGNMENT#ID#${id}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all RoleAssignments for a tenant across the\n * four shards. Tenant-scoped only, so `WID#-` is a sentinel.\n * SK is derived via `gsi1skAttribute` — uses the resource's natural label when\n * extractable, else `<lastUpdated>#<id>` (DR-004). `casing: \"none\"` preserves the\n * normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"tenantId\", \"gsi1Shard\"],\n template: \"TID#${tenantId}#WID#-#RT#RoleAssignment#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n },\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * Tenant data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, control plane. Tenant IS the top scope;\n * the workspace dimension is not applicable and uses the sentinel `TENANT`. The tenant's own `id`\n * is stored as `tenantId` to drive the partition key.\n *\n * Key structure: PK = TENANT#ID#<tenantId>, SK = CURRENT.\n * Uniqueness: one Tenant per tenantId (id).\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Tenants across the four shards. Tenant has\n * no parent tenant or workspace, so the PK uses `TID#-#WID#-` sentinels.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-13-01-tenant-and-workspace-fhir-types-control-plane.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const TenantEntity = new Entity({\n model: {\n entity: \"tenant\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** The tenant's own id (= resource id). Drives the partition key. */\n tenantId: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR Resource.id; logical id in URL. Equals tenantId. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full Tenant resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = TENANT#ID#<tenantId>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"tenantId\"],\n template: \"TENANT#ID#${tenantId}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Tenants across the four shards.\n * Tenant lives at the platform tier (no parent tenant or workspace), so `TID#-#WID#-`\n * sentinels precede `RT#Tenant#SHARD#<n>`. SK is derived via `gsi1skAttribute` —\n * `<normalizedName>#<id>` when the resource carries a `name`, else `<lastUpdated>#<id>`\n * (DR-004). `casing: \"none\"` preserves the normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"gsi1Shard\"],\n template: \"TID#-#WID#-#RT#Tenant#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n },\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * User data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Non-tenant-isolated, control plane. User is a\n * platform-wide identity; association with tenants and workspaces is through Membership and\n * RoleAssignment, not the User entity's own key.\n *\n * Key structure: PK = USER#ID#<id>, SK = CURRENT.\n * The USER# prefix prevents key collisions with other non-tenant-isolated entities (Role, etc.)\n * sharing the same table (ADR 2026-03-11-01 — preferred pattern for all control plane entities).\n * Uniqueness: one User per id.\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Users across the four shards. Non-tenant-\n * isolated, so the PK uses `TID#-#WID#-` sentinels.\n * GSI2 — Cognito sub-lookup per ADR-011: resolves a UserEntity from a Cognito `sub` claim\n * (`USER#SUB#<cognitoSub>` PK, `CURRENT` SK). The `cognitoSub` attribute is populated by the\n * Post Confirmation Lambda (Epic #765 / #770); kept optional here until that write path lands.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-11-01-user-type-definition-fhir-and-data-layer.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const UserEntity = new Entity({\n model: {\n entity: \"user\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** FHIR Resource.id; platform user id (ohi_uid). */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full User resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Immutable Cognito-issued `sub` claim. Drives GSI2 (sub-lookup). Optional until the\n * Post Confirmation Lambda (#770) lands; required thereafter.\n */\n cognitoSub: {\n type: \"string\" as const,\n required: false,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = USER#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"id\"],\n template: \"USER#ID#${id}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Users across the four shards.\n * Non-tenant-isolated, so `TID#-#WID#-` sentinels precede `RT#User#SHARD#<n>`.\n * SK is derived via `gsi1skAttribute` — uses the resource's natural label when\n * extractable (string `name`/`title` via introspection), else `<lastUpdated>#<id>`\n * (DR-004). `casing: \"none\"` preserves the normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"gsi1Shard\"],\n template: \"TID#-#WID#-#RT#User#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n\n /**\n * GSI2 — Cognito sub-lookup per ADR-011: resolves the UserEntity from a Cognito `sub` claim.\n * `condition` skips the index when `cognitoSub` is missing so legacy items without a sub are\n * not indexed.\n */\n gsi2: {\n index: \"GSI2\",\n condition: (attrs: { cognitoSub?: string }) =>\n typeof attrs.cognitoSub === \"string\" && attrs.cognitoSub.length > 0,\n pk: {\n field: \"GSI2PK\",\n casing: \"none\" as const,\n composite: [\"cognitoSub\"],\n template: \"USER#SUB#${cognitoSub}\",\n },\n sk: {\n field: \"GSI2SK\",\n casing: \"none\" as const,\n composite: [],\n template: \"CURRENT\",\n },\n },\n },\n});\n","import { Entity } from \"electrodb\";\nimport { gsi1ShardAttribute, gsi1skAttribute } from \"./control-entity-common\";\n\n/**\n * Workspace data-store entity (single-table store).\n *\n * **Classification (ADR 2026-03-03-01):** Tenant-isolated, control plane. Each workspace belongs\n * to exactly one tenant; both tenantId and workspace id are in the partition key.\n *\n * Key structure: PK = TID#<tenantId>#WORKSPACE#ID#<id>, SK = CURRENT.\n * Uniqueness: one Workspace per (tenantId, id).\n *\n * GSI1 — Unified Sharded List per ADR-011: lists all Workspaces in a tenant across the four\n * shards. Workspace is itself the workspace identity, so the GSI1 PK uses `WID#-` as a sentinel.\n *\n * @see sites/www-docs/content/architecture/adr/2026-03-03-01-tenant-isolated-vs-non-tenant-isolated-entities.md\n * @see sites/www-docs/content/architecture/adr/2026-03-13-01-tenant-and-workspace-fhir-types-control-plane.md\n * @see sites/www-docs/content/packages/@openhi/constructs/data/dynamo/single-table-design.md\n */\nexport const WorkspaceEntity = new Entity({\n model: {\n entity: \"workspace\",\n service: \"control\",\n version: \"01\",\n },\n attributes: {\n /** Sort key sentinel. Always \"CURRENT\". */\n sk: {\n type: \"string\" as const,\n required: true,\n default: \"CURRENT\",\n },\n /** Tenant that contains this workspace (required). */\n tenantId: {\n type: \"string\" as const,\n required: true,\n },\n /** FHIR Resource.id; logical id in URL. */\n id: {\n type: \"string\" as const,\n required: true,\n },\n /** Full Workspace resource serialized as JSON string. */\n resource: {\n type: \"string\" as const,\n required: true,\n },\n /**\n * Summary projection (key display fields as JSON string: id, displayName, status).\n * Populated on every write via extractSummary(resource); GSI1 INCLUDE surfaces it on lists.\n */\n summary: {\n type: \"string\" as const,\n required: true,\n },\n /** Version id (e.g. ULID). */\n vid: {\n type: \"string\" as const,\n required: true,\n },\n lastUpdated: {\n type: \"string\" as const,\n required: true,\n },\n gsi1Shard: gsi1ShardAttribute,\n /** Derived GSI1 sort key — name-based when extractable; else `<lastUpdated>#<id>`. */\n gsi1sk: gsi1skAttribute,\n deleted: {\n type: \"boolean\" as const,\n required: false,\n },\n bundleId: {\n type: \"string\" as const,\n required: false,\n },\n msgId: {\n type: \"string\" as const,\n required: false,\n },\n },\n indexes: {\n /** Base table: PK = TID#<tenantId>#WORKSPACE#ID#<id>, SK = CURRENT. Do not supply PK or SK from outside. */\n record: {\n pk: {\n field: \"PK\",\n composite: [\"tenantId\", \"id\"],\n template: \"TID#${tenantId}#WORKSPACE#ID#${id}\",\n },\n sk: {\n field: \"SK\",\n composite: [\"sk\"],\n template: \"${sk}\",\n },\n },\n\n /**\n * GSI1 — Unified Sharded List per ADR-011: list all Workspaces for a tenant across the\n * four shards. Workspace is itself the workspace identity, so `WID#-` is a sentinel.\n * SK is derived via `gsi1skAttribute` — `<normalizedName>#<id>` when the resource\n * carries a `name`, else `<lastUpdated>#<id>` (DR-004). `casing: \"none\"` preserves\n * the normalized label and ISO-8601 `T`/`Z`.\n */\n gsi1: {\n index: \"GSI1\",\n pk: {\n field: \"GSI1PK\",\n composite: [\"tenantId\", \"gsi1Shard\"],\n template: \"TID#${tenantId}#WID#-#RT#Workspace#SHARD#${gsi1Shard}\",\n },\n sk: {\n field: \"GSI1SK\",\n casing: \"none\" as const,\n composite: [\"gsi1sk\"],\n template: \"${gsi1sk}\",\n },\n },\n },\n});\n","/**\n * Typed domain errors for the data operations layer.\n * Data operations throw these; adapters (REST, GraphQL, Step Function) map them to\n * HTTP status, GraphQL errors, or workflow envelopes. No HTTP or OperationOutcome here.\n *\n * Full documentation (error types, HTTP mapping, FHIR OperationOutcome.issue.code alignment):\n * @see sites/www-docs/content/packages/@openhi/constructs/data/errors.md\n */\n\n/**\n * Base class for domain errors thrown by data operations.\n * Adapters use instanceof checks or domainErrorToHttpStatus() to map to transport.\n */\nexport class DomainError extends Error {\n /** Stable code for adapter mapping (e.g. \"NOT_FOUND\", \"VALIDATION\", \"CONFLICT\"). */\n readonly code: string;\n\n /** Optional details for validation messages or conflict context. */\n readonly details?: unknown;\n\n constructor(\n message: string,\n code: string,\n options?: { cause?: unknown; details?: unknown },\n ) {\n super(message, options);\n this.name = this.constructor.name;\n this.code = code;\n this.details = options?.details;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** Thrown when a requested resource or entity is not found (e.g. get by id returns nothing). */\nexport class NotFoundError extends DomainError {\n constructor(\n message: string,\n options?: { cause?: unknown; details?: unknown },\n ) {\n super(message, \"NOT_FOUND\", options);\n }\n}\n\n/** Thrown when input fails validation (e.g. missing required field, invalid format). */\nexport class ValidationError extends DomainError {\n constructor(\n message: string,\n options?: { cause?: unknown; details?: unknown },\n ) {\n super(message, \"VALIDATION\", options);\n }\n}\n\n/** Thrown when an operation conflicts with current state (e.g. duplicate key, version conflict). */\nexport class ConflictError extends DomainError {\n constructor(\n message: string,\n options?: { cause?: unknown; details?: unknown },\n ) {\n super(message, \"CONFLICT\", options);\n }\n}\n\n/**\n * Maps a thrown value to the HTTP status code that adapters should use for that domain error.\n * Returns null if the value is not a known domain error (adapter may use 500 or handle otherwise).\n * REST adapters use this to choose status; response body/OperationOutcome remain adapter responsibility.\n */\nexport function domainErrorToHttpStatus(err: unknown): number | null {\n if (err instanceof NotFoundError) return 404;\n if (err instanceof ValidationError) return 400;\n if (err instanceof ConflictError) return 409;\n return null;\n}\n","import { extractSummary, type FhirResourceLike } from \"@openhi/types\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface MembershipCreateParams {\n context: OpenHiContext;\n body: { id?: string; resource?: Record<string, unknown> | string };\n tableName?: string;\n}\n\nexport interface MembershipCreateResult {\n id: string;\n resource: { resourceType: string; id: string; [key: string]: unknown };\n meta: { lastUpdated: string; versionId: string };\n}\n\nexport async function createMembershipOperation(\n params: MembershipCreateParams,\n): Promise<MembershipCreateResult> {\n const { context, body, tableName } = params;\n const service = getDynamoControlService(tableName);\n\n const id = body.id ?? `membership-${Date.now()}`;\n const parsedResource =\n typeof body.resource === \"string\"\n ? (JSON.parse(body.resource) as Record<string, unknown>)\n : (body.resource ?? {});\n\n const lastUpdated = context.date ?? new Date().toISOString();\n const vid = `1`;\n\n const resource = { resourceType: \"Membership\", id, ...parsedResource };\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n await service.entities.membership\n .put({\n tenantId: context.tenantId,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n })\n .go();\n\n return {\n id,\n resource,\n meta: { lastUpdated, versionId: vid },\n };\n}\n","import { 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 { extractSummary, type FhirResourceLike } from \"@openhi/types\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface RoleAssignmentCreateParams {\n context: OpenHiContext;\n body: { id?: string; resource?: Record<string, unknown> | string };\n tableName?: string;\n}\n\nexport interface RoleAssignmentCreateResult {\n id: string;\n resource: { resourceType: string; id: string; [key: string]: unknown };\n meta: { lastUpdated: string; versionId: string };\n}\n\nexport async function createRoleAssignmentOperation(\n params: RoleAssignmentCreateParams,\n): Promise<RoleAssignmentCreateResult> {\n const { context, body, tableName } = params;\n const service = getDynamoControlService(tableName);\n\n const id = body.id ?? `roleassignment-${Date.now()}`;\n const parsedResource =\n typeof body.resource === \"string\"\n ? (JSON.parse(body.resource) as Record<string, unknown>)\n : (body.resource ?? {});\n\n const lastUpdated = context.date ?? new Date().toISOString();\n const vid = `1`;\n\n const resource = { resourceType: \"RoleAssignment\", id, ...parsedResource };\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n await service.entities.roleAssignment\n .put({\n tenantId: context.tenantId,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n })\n .go();\n\n return {\n id,\n resource,\n meta: { lastUpdated, versionId: vid },\n };\n}\n","import { extractSummary, type FhirResourceLike } from \"@openhi/types\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport type { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface CreateTenantParams {\n context: OpenHiContext;\n body: {\n id?: string;\n resource?: Record<string, unknown> | string;\n };\n tableName?: string;\n}\n\nexport interface CreateTenantResult {\n id: string;\n resource: Record<string, unknown>;\n meta: { lastUpdated: string; versionId: string };\n}\n\n/**\n * Creates a Tenant. Generates an id if not provided.\n */\nexport async function createTenantOperation(\n params: CreateTenantParams,\n): Promise<CreateTenantResult> {\n const { context, body, tableName } = params;\n const service = getDynamoControlService(tableName);\n\n const id = body.id ?? `tenant-${Date.now()}`;\n const lastUpdated = context.date;\n const vid =\n lastUpdated.replace(/[-:T.Z]/g, \"\").slice(0, 12) || Date.now().toString(36);\n\n const parsedResource =\n typeof body.resource === \"string\"\n ? (JSON.parse(body.resource) as Record<string, unknown>)\n : (body.resource ?? {});\n\n const resource = { resourceType: \"Tenant\", id, ...parsedResource };\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n await service.entities.tenant\n .put({\n tenantId: id,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n })\n .go();\n\n return { id, resource, meta: { lastUpdated, versionId: vid } };\n}\n","import { extractSummary, type FhirResourceLike } from \"@openhi/types\";\nimport { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport type { OpenHiContext } from \"../../../openhi-context\";\n\nexport interface CreateWorkspaceParams {\n context: OpenHiContext;\n body: {\n id?: string;\n resource?: Record<string, unknown> | string;\n };\n tableName?: string;\n}\n\nexport interface CreateWorkspaceResult {\n id: string;\n resource: Record<string, unknown>;\n meta: { lastUpdated: string; versionId: string };\n}\n\n/**\n * Creates a Workspace scoped to the context tenant. Generates an id if not provided.\n */\nexport async function createWorkspaceOperation(\n params: CreateWorkspaceParams,\n): Promise<CreateWorkspaceResult> {\n const { context, body, tableName } = params;\n const { tenantId } = context;\n const service = getDynamoControlService(tableName);\n\n const id = body.id ?? `workspace-${Date.now()}`;\n const lastUpdated = context.date;\n const vid =\n lastUpdated.replace(/[-:T.Z]/g, \"\").slice(0, 12) || Date.now().toString(36);\n\n const parsedResource =\n typeof body.resource === \"string\"\n ? (JSON.parse(body.resource) as Record<string, unknown>)\n : (body.resource ?? {});\n\n const resource = { resourceType: \"Workspace\", id, ...parsedResource };\n const summary = JSON.stringify(extractSummary(resource as FhirResourceLike));\n\n await service.entities.workspace\n .put({\n tenantId,\n id,\n resource: JSON.stringify(resource),\n summary,\n vid,\n lastUpdated,\n })\n .go();\n\n return { id, resource, meta: { lastUpdated, versionId: vid } };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,IAAAA,SAAA,6BAAA;AAxBa,IAAAA,SAAA,mBAAmB;AAEhC,QAAM,2BAA2B;AAQjC,QAAM,sBAAsB;AAQ5B,QAAM,sBAAsB;AAM5B,aAAgB,2BAA2B,SAAe;AACxD,UAAI,CAAC,yBAAyB,KAAK,OAAO,GAAG;AAC3C,eAAO;MACT;AACA,YAAM,QAAQ,OAAO,SAAS,QAAQ,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE;AACvD,aAAO,SAAS,uBAAuB,SAAS;IAClD;;;;;;;;;;ACPA,IAAAC,SAAA,sBAAA;AASA,IAAAA,SAAA,wBAAA;AA6CA,IAAAA,SAAA,8BAAA;AAtDA,aAAgB,oBACd,OAAoB;AAEpB,aAAQ,MAA4B,YAAY;IAClD;AAKA,aAAgB,sBACd,OAAoB;AAEpB,aAAQ,MAA8B,WAAW;IACnD;AAyCA,aAAgB,4BACd,QAAoB;AAEpB,UAAI,OAAO,YAAY,UAAa,OAAO,YAAY,QAAW;AAChE,cAAM,IAAI,yBACR,iMAAiM;MAErM;AACA,aAAO;QACL,SAAS,OAAO;QAChB,SAAS,OAAO;QAChB,SAAS,OAAO;QAChB,WAAW,OAAO;;IAEtB;AAGA,QAAa,2BAAb,cAA8C,MAAK;;MAEjD,YAAY,SAAe;AACzB,cAAM,OAAO;AACb,aAAK,OAAO;MACd;;AALF,IAAAA,SAAA,2BAAA;;;;;;;;;;AC3Fa,IAAAC,SAAA,wBAAwB;AAGxB,IAAAA,SAAA,qBAAqB;AAGrB,IAAAA,SAAA,oBAAoB;AAmBpB,IAAAA,SAAA,6BAA2D;MACtE,CAACA,SAAA,qBAAqB,GAAG;MACzB,CAACA,SAAA,kBAAkB,GAAG;MACtB,CAACA,SAAA,iBAAiB,GAAG;;;;;;;;;;;ACQvB,IAAAC,SAAA,mBAAA;AAuBA,IAAAA,SAAA,yBAAA;AAvBA,aAAgB,iBACd,OAAwD;AAExD,UAAI,CAAC,uBAAuB,MAAM,UAAU,GAAG;AAC7C,cAAM,IAAI,mCACR,gBAAgB,MAAM,UAAU,oGAAiG;MAErI;AACA,aAAO;IACT;AAUA,QAAM,sBACJ;AAGF,aAAgB,uBAAuB,YAAkB;AACvD,aAAO,oBAAoB,KAAK,UAAU;IAC5C;AAGA,QAAa,qCAAb,cAAwD,MAAK;;MAE3D,YAAY,SAAe;AACzB,cAAM,OAAO;AACb,aAAK,OAAO;MACd;;AALF,IAAAA,SAAA,qCAAA;;;;;;;;;;AC1EA,QAAA,YAAA;AACA,QAAA,aAAA;AAkDa,IAAAC,SAAA,iCACX,GAAA,WAAA,kBAAsD;MACpD,YAAY;MACZ,QAAQ,UAAA;MACR,eAAe;KAChB;AAgDU,IAAAA,SAAA,8BACX,GAAA,WAAA,kBAAmD;MACjD,YAAY;MACZ,QAAQ,UAAA;MACR,eAAe;KAChB;;;;;;;;;;;;;;;;;;;;;;;;;AC7GH,iBAAA,oBAAAC,QAAA;AACA,iBAAA,oBAAAA,QAAA;;;;;;;;;;AC8EA,IAAAC,SAAA,kBAAA;AAiBA,IAAAA,SAAA,uBAAA;AAhGA,QAAA,gBAAA,QAAA,QAAA;AACA,QAAA,uBAAA,QAAA,6BAAA;AAOA,QAAA,qBAAA;AACA,QAAA,YAAA;AAsEA,aAAgB,gBACd,QACA,UAA4B,CAAA,GAAE;AAE9B,aAAO;QACL,SAAS,CAAC,OAAO,SAAS,QACxB,qBAAqB,QAAQ,OAAO,SAAS,KAAK,OAAO;;IAE/D;AASO,mBAAe,qBACpB,QACA,OACA,SACA,KACA,UAA4B,CAAA,GAAE;AAE9B,YAAM,mBAAmB,QAAQ,qBAAqB,OAAM,GAAA,cAAA,YAAU;AACtE,YAAM,yBACJ,QAAQ,2BAA2B,OAAM,GAAA,cAAA,YAAU;AACrD,YAAM,MAAM,QAAQ,QAAQ,MAAM,oBAAI,KAAI;AAE1C,YAAM,WAAoC;QACxC,SAAS,iBAAgB;QACzB,SAAS;QACT,eAAe,IAAI,iBAAiB,uBAAsB;QAC1D,aAAa,IAAI,eAAe;QAChC,OAAO,IAAI;QACX,YAAY,IAAG,EAAG,YAAW;QAC7B,iBAAiB,mBAAA;QACjB;;AAGF,YAAM,UACJ,QAAQ,iBAAiB,MAAM,MAAM,KACrC,UAAA,2BAA2B,MAAM,MAAM;AAEzC,YAAM,SAAS,MAAM,OAAO,KAC1B,IAAI,qBAAA,iBAAiB;QACnB,SAAS;UACP;YACE,cAAc;YACd,QAAQ,MAAM;YACd,YAAY,MAAM;YAClB,QAAQ,KAAK,UAAU,QAAQ;;;OAGpC,CAAC;AAGJ,WAAK,OAAO,oBAAoB,KAAK,GAAG;AACtC,cAAM,QAAQ,OAAO,UAAU,CAAC;AAChC,cAAM,IAAI,qBACR,wBAAwB,MAAM,UAAU,mBAAmB,OAAO,KAAK,OAAO,aAAa,SAAS,WAAM,OAAO,gBAAgB,kBAAkB,EAAE;MAEzJ;AAEA,aAAO,EAAE,SAAS,SAAS,QAAO;IACpC;AAGA,QAAa,uBAAb,cAA0C,MAAK;;MAE7C,YAAY,SAAe;AACzB,cAAM,OAAO;AACb,aAAK,OAAO;MACd;;AALF,IAAAA,SAAA,uBAAA;;;;;;;;;;ACnGA,IAAAC,SAAA,qBAAAC;AA9CA,QAAA,qBAAA;AA8CA,aAAgBA,oBACd,OACA,UAA2C;AAE3C,UAAI,MAAM,WAAW,SAAS,QAAQ;AACpC,cAAM,IAAI,0BACR,uBAAuB,MAAM,MAAM,mDAAmD,SAAS,MAAM,IAAI;MAE7G;AAEA,UAAI,MAAM,aAAa,MAAM,SAAS,YAAY;AAChD,cAAM,IAAI,0BACR,4BAA4B,MAAM,aAAa,CAAC,8BAA8B,SAAS,UAAU,IAAI;MAEzG;AAEA,YAAM,YAAY,oBAAoB,MAAM,MAAM;AAElD,UAAI,EAAC,GAAA,mBAAA,4BAA2B,UAAU,eAAe,GAAG;AAC1D,cAAM,IAAI,gCACR,qBAAqB,UAAU,eAAe,yCAAyC;MAE3F;AAEA,YAAM,WAAoC;QACxC,SAAS,UAAU;QACnB,SAAS,UAAU;QACnB,eAAe,UAAU;QACzB,aAAa,UAAU;QACvB,OAAO,UAAU;QACjB,YAAY,UAAU;QACtB,iBAAiB,UAAU;QAC3B,SAAS,UAAU;;AAGrB,aAAO;QACL;QACA,UAAU,EAAE,SAAS,SAAS,SAAS,SAAS,SAAS,QAAO;;IAEpE;AAQA,aAAS,oBAAoB,QAAe;AAC1C,UAAI,WAAW,QAAQ,OAAO,WAAW,UAAU;AACjD,cAAM,IAAI,0BACR,8CAA8C;MAElD;AAEA,YAAM,MAAM;AAEZ,mBAAa,KAAK,SAAS;AAC3B,4BAAsB,KAAK,SAAS;AACpC,mBAAa,KAAK,eAAe;AACjC,wBAAkB,GAAG;AACrB,kBAAY,GAAG;AACf,mBAAa,KAAK,YAAY;AAC9B,mBAAa,KAAK,iBAAiB;AAEnC,UAAI,EAAE,aAAa,MAAM;AACvB,cAAM,IAAI,0BACR,8CAA8C;MAElD;AAEA,aAAO;IACT;AAEA,aAAS,aACP,KACA,OAAa;AAEb,YAAM,QAAQ,IAAI,KAAK;AACvB,UAAI,OAAO,UAAU,YAAY,MAAM,WAAW,GAAG;AACnD,cAAM,IAAI,0BACR,mBAAmB,KAAK,+BAA+B;MAE3D;IACF;AAEA,aAAS,sBACP,KACA,OAAa;AAEb,YAAM,QAAQ,IAAI,KAAK;AACvB,UAAI,OAAO,UAAU,YAAY,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,GAAG;AACtE,cAAM,IAAI,0BACR,mBAAmB,KAAK,gCAAgC;MAE5D;IACF;AAEA,aAAS,kBAAkB,KAA4B;AACrD,UAAI,EAAE,iBAAiB,MAAM;AAC3B,cAAM,IAAI,0BACR,kDAAkD;MAEtD;AACA,YAAM,QAAQ,IAAI;AAClB,UAAI,UAAU,SAAS,OAAO,UAAU,YAAY,MAAM,WAAW,IAAI;AACvE,cAAM,IAAI,0BACR,kEAAkE;MAEtE;IACF;AAEA,aAAS,YAAY,KAA4B;AAC/C,YAAM,QAAQ,IAAI;AAClB,UAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;AAC/C,cAAM,IAAI,0BACR,2CAA2C;MAE/C;AACA,YAAM,WAAW;AACjB,YAAM,cACJ,OAAO,SAAS,YAAY,YAC5B,OAAO,SAAS,cAAc,YAC9B,OAAO,SAAS,YAAY,YAC5B,OAAO,SAAS,YAAY;AAC9B,YAAM,gBAAgB,OAAO,SAAS,WAAW;AACjD,UAAI,CAAC,eAAe,CAAC,eAAe;AAClC,cAAM,IAAI,0BACR,mIAAmI;MAEvI;IACF;AAGA,QAAa,4BAAb,cAA+C,MAAK;;MAElD,YAAY,SAAe;AACzB,cAAM,OAAO;AACb,aAAK,OAAO;MACd;;AALF,IAAAD,SAAA,4BAAA;AASA,QAAa,kCAAb,cAAqD,MAAK;;MAExD,YAAY,SAAe;AACzB,cAAM,OAAO;AACb,aAAK,OAAO;MACd;;AALF,IAAAA,SAAA,kCAAA;;;;;;;;;;ACrLa,IAAAE,SAAA,oCACX;AAGW,IAAAA,SAAA,qCAAqC,KAAK,KAAK,KAAK;AAGpD,IAAAA,SAAA,0CAA0C;;;;;;;;;;AC0EvD,IAAAC,SAAA,sBAAAC;AAcA,IAAAD,SAAA,iBAAA;AAgDA,IAAAA,SAAA,aAAA;AAuCA,IAAAA,SAAA,gBAAA;AA/LA,QAAA,oBAAA,QAAA,0BAAA;AAOA,QAAA,QAAA;AAmFA,aAAgBC,qBACd,UACA,UAAsC,CAAA,GAAE;AAExC,aAAO;QACL,gBAAgB,CAAC,UAAU,eAAe,UAAU,OAAO,OAAO;QAClE,YAAY,CAAC,UAAU,WAAW,UAAU,OAAO,OAAO;;IAE9D;AAMO,mBAAe,eACpB,UACA,OACA,UAAsC,CAAA,GAAE;AAExC,yBAAmB,MAAM,YAAY;AACrC,4BAAsB,MAAM,SAAS,SAAS;AAC9C,YAAM,aACJ,MAAM,cACN,QAAQ,qBACR,MAAA;AACF,UAAI,CAAC,OAAO,UAAU,UAAU,KAAK,cAAc,GAAG;AACpD,cAAM,IAAI,+BACR,8CAA8C,UAAU,GAAG;MAE/D;AAEA,YAAM,YAAY,iBAAiB,QAAQ,SAAS;AACpD,YAAM,OAAO,QAAQ,OAAO,YAAW;AACvC,YAAM,KAAK,cAAc,MAAM,SAAS,MAAM,OAAO;AACrD,YAAM,YAAY,KAAK,MAAM,IAAI,QAAO,IAAK,GAAI,IAAI;AAErD,UAAI;AACF,cAAM,SAAS,KACb,IAAI,kBAAA,eAAe;UACjB,WAAW;UACX,MAAM;YACJ,cAAc,EAAE,GAAG,MAAM,aAAY;YACrC,IAAI,EAAE,GAAG,GAAE;YACX,SAAS,EAAE,GAAG,MAAM,QAAO;YAC3B,SAAS,EAAE,GAAG,OAAO,MAAM,OAAO,EAAC;YACnC,YAAY,EAAE,GAAG,IAAI,YAAW,EAAE;YAClC,WAAW,EAAE,GAAG,OAAO,SAAS,EAAC;;UAEnC,qBACE;SACH,CAAC;AAEJ,eAAO,EAAE,UAAU,KAAI;MACzB,SAAS,KAAK;AACZ,YAAI,eAAe,kBAAA,iCAAiC;AAClD,iBAAO,EAAE,UAAU,OAAO,kBAAkB,KAAI;QAClD;AACA,cAAM;MACR;IACF;AAGO,mBAAe,WACpB,UACA,OACA,UAAsC,CAAA,GAAE;AAExC,yBAAmB,MAAM,YAAY;AACrC,4BAAsB,MAAM,SAAS,SAAS;AAC9C,UAAI,MAAM,OAAO,WAAW,GAAG;AAC7B,cAAM,IAAI,+BAA+B,2BAA2B;MACtE;AAEA,YAAM,YAAY,iBAAiB,QAAQ,SAAS;AACpD,YAAM,OAAO,QAAQ,OAAO,YAAW;AACvC,YAAM,KAAK,cAAc,MAAM,SAAS,MAAM,OAAO;AAErD,YAAM,SAAS,KACb,IAAI,kBAAA,kBAAkB;QACpB,WAAW;QACX,KAAK;UACH,cAAc,EAAE,GAAG,MAAM,aAAY;UACrC,IAAI,EAAE,GAAG,GAAE;;QAEb,kBACE;QACF,0BAA0B;UACxB,WAAW;UACX,kBAAkB;UAClB,aAAa;;QAEf,2BAA2B;UACzB,WAAW,EAAE,MAAM,KAAI;UACvB,WAAW,EAAE,GAAG,MAAM,OAAM;UAC5B,aAAa,EAAE,GAAG,IAAI,YAAW,EAAE;;OAEtC,CAAC;IAEN;AAGA,aAAgB,cAAc,SAAiB,SAAe;AAC5D,UAAI,QAAQ,WAAW,GAAG;AACxB,cAAM,IAAI,+BAA+B,4BAA4B;MACvE;AACA,aAAO,GAAG,OAAO,IAAI,OAAO;IAC9B;AAEA,aAAS,iBAAiB,UAAiB;AACzC,YAAM,OAAO,YAAY,QAAQ,IAAI,MAAA,iCAAiC;AACtE,UAAI,CAAC,MAAM;AACT,cAAM,IAAI,mCACR,oEAAoE,MAAA,iCAAiC,GAAG;MAE5G;AACA,aAAO;IACT;AAEA,aAAS,mBAAmB,cAAoB;AAC9C,UAAI,aAAa,WAAW,GAAG;AAC7B,cAAM,IAAI,+BAA+B,iCAAiC;MAC5E;AACA,UAAI,aAAa,SAAS,MAAA,yCAAyC;AACjE,cAAM,IAAI,+BACR,8BAAyB,MAAA,uCAAuC,eAAe,aAAa,MAAM,GAAG;MAEzG;AACA,UAAI,KAAK,KAAK,YAAY,GAAG;AAC3B,cAAM,IAAI,+BACR,2CAA2C;MAE/C;IACF;AAEA,aAAS,sBAAsB,OAAe,OAAa;AACzD,UAAI,CAAC,OAAO,UAAU,KAAK,KAAK,QAAQ,GAAG;AACzC,cAAM,IAAI,+BACR,GAAG,KAAK,qCAAqC,KAAK,GAAG;MAEzD;IACF;AAEA,aAAS,aAAU;AACjB,aAAO,oBAAI,KAAI;IACjB;AAGA,QAAa,qCAAb,cAAwD,MAAK;;MAE3D,YAAY,SAAe;AACzB,cAAM,OAAO;AACb,aAAK,OAAO;MACd;;AALF,IAAAD,SAAA,qCAAA;AASA,QAAa,iCAAb,cAAoD,MAAK;;MAEvD,YAAY,SAAe;AACzB,cAAM,OAAO;AACb,aAAK,OAAO;MACd;;AALF,IAAAA,SAAA,iCAAA;;;;;;;;;;ACtPA,QAAA,QAAA;AACE,WAAA,eAAAE,UAAA,sCAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,MAAA;IAAkC,EAAA,CAAA;AAClC,WAAA,eAAAA,UAAA,2CAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,MAAA;IAAuC,EAAA,CAAA;AACvC,WAAA,eAAAA,UAAA,qCAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,MAAA;IAAiC,EAAA,CAAA;AAEnC,QAAA,0BAAA;AACE,WAAA,eAAAA,UAAA,kCAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,wBAAA;IAA8B,EAAA,CAAA;AAC9B,WAAA,eAAAA,UAAA,sCAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,wBAAA;IAAkC,EAAA,CAAA;AAClC,WAAA,eAAAA,UAAA,iBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,wBAAA;IAAa,EAAA,CAAA;AACb,WAAA,eAAAA,UAAA,cAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,wBAAA;IAAU,EAAA,CAAA;AACV,WAAA,eAAAA,UAAA,kBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,wBAAA;IAAc,EAAA,CAAA;AACd,WAAA,eAAAA,UAAA,uBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,wBAAA;IAAmB,EAAA,CAAA;;;;;;;;;;ACXrB,QAAA,qBAAA;AACE,WAAA,eAAAC,UAAA,oBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,mBAAA;IAAgB,EAAA,CAAA;AAChB,WAAA,eAAAA,UAAA,8BAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,mBAAA;IAA0B,EAAA,CAAA;AAE5B,QAAA,aAAA;AACE,WAAA,eAAAA,UAAA,4BAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,WAAA;IAAwB,EAAA,CAAA;AACxB,WAAA,eAAAA,UAAA,yBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,WAAA;IAAqB,EAAA,CAAA;AACrB,WAAA,eAAAA,UAAA,uBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,WAAA;IAAmB,EAAA,CAAA;AACnB,WAAA,eAAAA,UAAA,+BAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,WAAA;IAA2B,EAAA,CAAA;AAQ7B,QAAA,YAAA;AACE,WAAA,eAAAA,UAAA,8BAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,UAAA;IAA0B,EAAA,CAAA;AAC1B,WAAA,eAAAA,UAAA,yBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,UAAA;IAAqB,EAAA,CAAA;AACrB,WAAA,eAAAA,UAAA,sBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,UAAA;IAAkB,EAAA,CAAA;AAClB,WAAA,eAAAA,UAAA,qBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,UAAA;IAAiB,EAAA,CAAA;AAGnB,QAAA,iBAAA;AACE,WAAA,eAAAA,UAAA,sCAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,eAAA;IAAkC,EAAA,CAAA;AAClC,WAAA,eAAAA,UAAA,iCAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,eAAA;IAA6B,EAAA,CAAA;AAC7B,WAAA,eAAAA,UAAA,8BAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,eAAA;IAA0B,EAAA,CAAA;AAC1B,WAAA,eAAAA,UAAA,oBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,eAAA;IAAgB,EAAA,CAAA;AAChB,WAAA,eAAAA,UAAA,0BAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,eAAA;IAAsB,EAAA,CAAA;AAOxB,QAAA,cAAA;AACE,WAAA,eAAAA,UAAA,wBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,YAAA;IAAoB,EAAA,CAAA;AACpB,WAAA,eAAAA,UAAA,wBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,YAAA;IAAoB,EAAA,CAAA;AACpB,WAAA,eAAAA,UAAA,mBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,YAAA;IAAe,EAAA,CAAA;AAQjB,QAAA,aAAA;AACE,WAAA,eAAAA,UAAA,6BAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,WAAA;IAAyB,EAAA,CAAA;AACzB,WAAA,eAAAA,UAAA,mCAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,WAAA;IAA+B,EAAA,CAAA;AAC/B,WAAA,eAAAA,UAAA,sBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,WAAA;IAAkB,EAAA,CAAA;AAOpB,QAAA,UAAA;AACE,WAAA,eAAAA,UAAA,sCAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,QAAA;IAAkC,EAAA,CAAA;AAClC,WAAA,eAAAA,UAAA,2CAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,QAAA;IAAuC,EAAA,CAAA;AACvC,WAAA,eAAAA,UAAA,qCAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,QAAA;IAAiC,EAAA,CAAA;AACjC,WAAA,eAAAA,UAAA,kCAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,QAAA;IAA8B,EAAA,CAAA;AAC9B,WAAA,eAAAA,UAAA,sCAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,QAAA;IAAkC,EAAA,CAAA;AAClC,WAAA,eAAAA,UAAA,iBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,QAAA;IAAa,EAAA,CAAA;AACb,WAAA,eAAAA,UAAA,cAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,QAAA;IAAU,EAAA,CAAA;AACV,WAAA,eAAAA,UAAA,kBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,QAAA;IAAc,EAAA,CAAA;AACd,WAAA,eAAAA,UAAA,uBAAA,EAAA,YAAA,MAAA,KAAA,WAAA;AAAA,aAAA,QAAA;IAAmB,EAAA,CAAA;;;;;ACjErB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAA2B;AAC3B,8CAOO;AACP,IAAAC,0BAA+B;AAC/B,IAAAC,gBAOO;AACP,IAAAC,oBAIO;;;ACtBP,mBAGO;AACP,uBAA2C;AAapC,IAAM,+BAA+B;AAGrC,IAAM,kBAAkB;AASxB,IAAM,6BAA6B;AAOnC,IAAM,cAAc,EAAE,OAAO,uBAAuB;AAepD,IAAM,2BAA2B;AAGjC,IAAM,wBAAwB;AAG9B,IAAM,2BAA2B;AAwBjC,IAAM,YAAwC;AAAA,EACnD,EAAE,IAAI,eAAe,OAAO,2BAA2B;AAAA,EACvD,EAAE,IAAI,eAAe,OAAO,2BAA2B;AAAA,EACvD,EAAE,IAAI,eAAe,OAAO,2BAA2B;AAAA,EACvD,EAAE,IAAI,aAAa,OAAO,yBAAyB;AAAA,EACnD,EAAE,IAAI,YAAY,OAAO,wBAAwB;AAAA,EACjD,EAAE,IAAI,YAAY,OAAO,wBAAwB;AAAA,EACjD,EAAE,IAAI,eAAe,OAAO,2BAA2B;AAAA,EACvD,EAAE,IAAI,aAAa,OAAO,yBAAyB;AAAA,EACnD,EAAE,IAAI,eAAe,OAAO,2BAA2B;AACzD;AAuDO,IAAM,oBAAmD;AAAA,EAC9D;AAAA,IACE,UAAU;AAAA,IACV,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,MACV;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,YAAY;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,MACV;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,YAAY;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,MACV;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,YAAY;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,UAAU;AAAA,IACV,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,YAAY;AAAA,MACV;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,YAAY;AAAA,MACd;AAAA,MACA;AAAA,QACE,IAAI;AAAA,QACJ,MAAM;AAAA,QACN,YAAY;AAAA,MACd;AAAA,IACF;AAAA,EACF;AACF;AAGO,IAAM,mBAAmB,CAAC,WAAmB,aAClD,mBAAmB,SAAS,IAAI,QAAQ;AAOnC,IAAM,uBAAuB,CAClC,WACA,UACA,aACW,uBAAuB,SAAS,IAAI,QAAQ,IAAI,QAAQ;AAO9D,IAAM,yBAAyB,CACpC,UACA,gBACuC;AAAA,EACvC,QAAQ;AAAA,EACR,OAAO,GAAG,QAAQ,IAAI,UAAU;AAClC;AASO,IAAM,2BAA2B,CAAC,YAKc;AAAA,EACrD,KAAK;AAAA,EACL,QAAQ;AAAA,EACR,OAAO,WAAW,OAAO,QAAQ,IAAI,OAAO,WAAW,IAAI,OAAO,YAAY,IAAI,OAAO,EAAE;AAC7F;AAOO,IAAM,2BAA2B,CACtC,OACA,cACwC;AACxC,OAAK;AACL,OAAK;AACL,SAAO,CAAC,qCAAwB,YAAY;AAC9C;;;ACpQA,IAAAC,oBAAwB;;;ACAxB,6BAA+B;AAMxB,IAAM,mBACX,QAAQ,IAAI,qBAAqB;AAM5B,IAAM,eAAe,IAAI,sCAAe;AAAA,EAC7C,GAAI,QAAQ,IAAI,0BAA0B;AAAA,IACxC,UAAU,QAAQ,IAAI;AAAA,IACtB,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV;AACF,CAAC;;;ACnBD,uBAAuB;;;ACAvB,IAAAC,gBAA6B;;;ACYtB,IAAM,cAAc;AAYpB,SAAS,aAAa,IAAoB;AAE/C,MAAI,OAAO;AACX,WAAS,IAAI,GAAG,IAAI,GAAG,QAAQ,KAAK;AAClC,YAAQ,GAAG,WAAW,CAAC;AACvB,WAAO,KAAK,KAAK,MAAM,QAAU;AAAA,EACnC;AACA,UAAQ,SAAS,KAAK;AAExB;;;ADhBO,IAAM,qBAAqB;AAAA,EAChC,MAAM;AAAA,EACN,OAAO,CAAC,IAAI;AAAA,EACZ,KAAK,CAAC,MAAe,SAA2B;AAC9C,QAAI,OAAO,MAAM,OAAO,YAAY,KAAK,GAAG,WAAW,GAAG;AACxD,aAAO;AAAA,IACT;AACA,WAAO,OAAO,aAAa,KAAK,EAAE,CAAC;AAAA,EACrC;AACF;AAsBO,IAAM,kBAAkB;AAAA,EAC7B,MAAM;AAAA,EACN,OAAO,CAAC,YAAY,eAAe,IAAI;AAAA,EACvC,KAAK,CACH,MACA,SACG;AACH,UAAM,KAAK,OAAO,MAAM,OAAO,WAAW,KAAK,KAAK;AACpD,UAAM,cACJ,OAAO,MAAM,gBAAgB,WAAW,KAAK,cAAc;AAC7D,UAAM,WAAW,GAAG,WAAW,IAAI,EAAE;AAErC,QAAI,OAAO,MAAM,aAAa,YAAY,KAAK,SAAS,WAAW,GAAG;AACpE,aAAO;AAAA,IACT;AAEA,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,KAAK,QAAQ;AAAA,IACnC,QAAQ;AACN,aAAO;AAAA,IACT;AACA,QAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,UAAM,eAAgB,OAAsC;AAC5D,QAAI,OAAO,iBAAiB,SAAU,QAAO;AAE7C,UAAM,YAAQ,4BAAa,MAA4C;AACvE,WAAO,UAAU,SAAY,GAAG,KAAK,IAAI,EAAE,KAAK;AAAA,EAClD;AACF;;;ADtDO,IAAM,sBAAsB,IAAI,wBAAO;AAAA,EAC5C,OAAO;AAAA,IACL,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AAAA,EACA,YAAY;AAAA;AAAA,IAEV,IAAI;AAAA,MACF,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA;AAAA,IAEA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA;AAAA,IAEA,aAAa;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA;AAAA,IAEA,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA;AAAA,IAEA,QAAQ;AAAA,MACN,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA;AAAA,IAEA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,IAAI;AAAA,MACF,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,aAAa;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,WAAW;AAAA,IACX,SAAS;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,OAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,SAAS;AAAA;AAAA,IAEP,QAAQ;AAAA,MACN,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,YAAY,eAAe,UAAU,QAAQ;AAAA,QACzD,UACE;AAAA,MACJ;AAAA,MACA,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,OAAO,IAAI;AAAA,QACvB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAYA,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,YAAY,eAAe,WAAW;AAAA,QAClD,UACE;AAAA,MACJ;AAAA,MACA,IAAI;AAAA,QACF,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,WAAW,CAAC,OAAO,IAAI;AAAA,QACvB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AGpJD,IAAAC,oBAAuB;AAoBhB,IAAM,mBAAmB,IAAI,yBAAO;AAAA,EACzC,OAAO;AAAA,IACL,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AAAA,EACA,YAAY;AAAA;AAAA,IAEV,IAAI;AAAA,MACF,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA;AAAA,IAEA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,IAAI;AAAA,MACF,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,aAAa;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,WAAW;AAAA;AAAA,IAEX,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,OAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,SAAS;AAAA;AAAA,IAEP,QAAQ;AAAA,MACN,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,YAAY,IAAI;AAAA,QAC5B,UAAU;AAAA,MACZ;AAAA,MACA,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,IAAI;AAAA,QAChB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,YAAY,WAAW;AAAA,QACnC,UAAU;AAAA,MACZ;AAAA,MACA,IAAI;AAAA,QACF,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,WAAW,CAAC,QAAQ;AAAA,QACpB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACtHD,IAAAC,oBAAuB;AAsBhB,IAAM,aAAa,IAAI,yBAAO;AAAA,EACnC,OAAO;AAAA,IACL,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AAAA,EACA,YAAY;AAAA;AAAA,IAEV,IAAI;AAAA,MACF,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA;AAAA,IAEA,IAAI;AAAA,MACF,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,aAAa;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,WAAW;AAAA;AAAA,IAEX,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,OAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,SAAS;AAAA;AAAA,IAEP,QAAQ;AAAA,MACN,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,IAAI;AAAA,QAChB,UAAU;AAAA,MACZ;AAAA,MACA,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,IAAI;AAAA,QAChB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,WAAW;AAAA,QACvB,UAAU;AAAA,MACZ;AAAA,MACA,IAAI;AAAA,QACF,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,WAAW,CAAC,QAAQ;AAAA,QACpB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACnHD,IAAAC,oBAAuB;AAoBhB,IAAM,uBAAuB,IAAI,yBAAO;AAAA,EAC7C,OAAO;AAAA,IACL,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AAAA,EACA,YAAY;AAAA;AAAA,IAEV,IAAI;AAAA,MACF,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA;AAAA,IAEA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,IAAI;AAAA,MACF,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,aAAa;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,WAAW;AAAA;AAAA,IAEX,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,OAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,SAAS;AAAA;AAAA,IAEP,QAAQ;AAAA,MACN,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,YAAY,IAAI;AAAA,QAC5B,UAAU;AAAA,MACZ;AAAA,MACA,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,IAAI;AAAA,QAChB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,YAAY,WAAW;AAAA,QACnC,UAAU;AAAA,MACZ;AAAA,MACA,IAAI;AAAA,QACF,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,WAAW,CAAC,QAAQ;AAAA,QACpB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACtHD,IAAAC,oBAAuB;AAoBhB,IAAM,eAAe,IAAI,yBAAO;AAAA,EACrC,OAAO;AAAA,IACL,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AAAA,EACA,YAAY;AAAA;AAAA,IAEV,IAAI;AAAA,MACF,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA;AAAA,IAEA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,IAAI;AAAA,MACF,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,aAAa;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,WAAW;AAAA;AAAA,IAEX,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,OAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,SAAS;AAAA;AAAA,IAEP,QAAQ;AAAA,MACN,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,UAAU;AAAA,QACtB,UAAU;AAAA,MACZ;AAAA,MACA,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,IAAI;AAAA,QAChB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,WAAW;AAAA,QACvB,UAAU;AAAA,MACZ;AAAA,MACA,IAAI;AAAA,QACF,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,WAAW,CAAC,QAAQ;AAAA,QACpB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACtHD,IAAAC,oBAAuB;AAyBhB,IAAM,aAAa,IAAI,yBAAO;AAAA,EACnC,OAAO;AAAA,IACL,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AAAA,EACA,YAAY;AAAA;AAAA,IAEV,IAAI;AAAA,MACF,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA;AAAA,IAEA,IAAI;AAAA,MACF,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,YAAY;AAAA,MACV,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,aAAa;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,WAAW;AAAA;AAAA,IAEX,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,OAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,SAAS;AAAA;AAAA,IAEP,QAAQ;AAAA,MACN,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,IAAI;AAAA,QAChB,UAAU;AAAA,MACZ;AAAA,MACA,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,IAAI;AAAA,QAChB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,WAAW;AAAA,QACvB,UAAU;AAAA,MACZ;AAAA,MACA,IAAI;AAAA,QACF,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,WAAW,CAAC,QAAQ;AAAA,QACpB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,WAAW,CAAC,UACV,OAAO,MAAM,eAAe,YAAY,MAAM,WAAW,SAAS;AAAA,MACpE,IAAI;AAAA,QACF,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,WAAW,CAAC,YAAY;AAAA,QACxB,UAAU;AAAA,MACZ;AAAA,MACA,IAAI;AAAA,QACF,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,WAAW,CAAC;AAAA,QACZ,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;ACrJD,IAAAC,oBAAuB;AAmBhB,IAAM,kBAAkB,IAAI,yBAAO;AAAA,EACxC,OAAO;AAAA,IACL,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,SAAS;AAAA,EACX;AAAA,EACA,YAAY;AAAA;AAAA,IAEV,IAAI;AAAA,MACF,MAAM;AAAA,MACN,UAAU;AAAA,MACV,SAAS;AAAA,IACX;AAAA;AAAA,IAEA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,IAAI;AAAA,MACF,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA;AAAA;AAAA;AAAA,IAKA,SAAS;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA;AAAA,IAEA,KAAK;AAAA,MACH,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,aAAa;AAAA,MACX,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,WAAW;AAAA;AAAA,IAEX,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,IACA,OAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,IACZ;AAAA,EACF;AAAA,EACA,SAAS;AAAA;AAAA,IAEP,QAAQ;AAAA,MACN,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,YAAY,IAAI;AAAA,QAC5B,UAAU;AAAA,MACZ;AAAA,MACA,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,IAAI;AAAA,QAChB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,MAAM;AAAA,MACJ,OAAO;AAAA,MACP,IAAI;AAAA,QACF,OAAO;AAAA,QACP,WAAW,CAAC,YAAY,WAAW;AAAA,QACnC,UAAU;AAAA,MACZ;AAAA,MACA,IAAI;AAAA,QACF,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,WAAW,CAAC,QAAQ;AAAA,QACpB,UAAU;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;AVpGD,IAAM,uBAAuB;AAAA,EAC3B,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,gBAAgB;AAAA,EAChB,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,WAAW;AACb;AAEA,IAAM,sBAAsB,IAAI,0BAAQ,sBAAsB;AAAA,EAC5D,OAAO;AAAA,EACP,QAAQ;AACV,CAAC;AAMM,IAAM,uBAAuB;AAAA,EAClC,UAAU,oBAAoB;AAChC;AAQO,SAAS,wBACd,WAC0B;AAC1B,QAAM,WAAW,aAAa;AAC9B,QAAM,UAAU,IAAI,0BAAQ,sBAAsB;AAAA,IAChD,OAAO;AAAA,IACP,QAAQ;AAAA,EACV,CAAC;AACD,SAAO;AAAA,IACL,UAAU,QAAQ;AAAA,EACpB;AACF;;;AW5CO,IAAM,cAAN,cAA0B,MAAM;AAAA,EAOrC,YACE,SACA,MACA,SACA;AACA,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO,KAAK,YAAY;AAC7B,SAAK,OAAO;AACZ,SAAK,UAAU,SAAS;AACxB,WAAO,eAAe,MAAM,WAAW,SAAS;AAAA,EAClD;AACF;AAGO,IAAM,gBAAN,cAA4B,YAAY;AAAA,EAC7C,YACE,SACA,SACA;AACA,UAAM,SAAS,aAAa,OAAO;AAAA,EACrC;AACF;;;ACzCA,IAAAC,gBAAsD;AAgBtD,eAAsB,0BACpB,QACiC;AACjC,QAAM,EAAE,SAAS,MAAM,UAAU,IAAI;AACrC,QAAM,UAAU,wBAAwB,SAAS;AAEjD,QAAM,KAAK,KAAK,MAAM,cAAc,KAAK,IAAI,CAAC;AAC9C,QAAM,iBACJ,OAAO,KAAK,aAAa,WACpB,KAAK,MAAM,KAAK,QAAQ,IACxB,KAAK,YAAY,CAAC;AAEzB,QAAM,cAAc,QAAQ,SAAQ,oBAAI,KAAK,GAAE,YAAY;AAC3D,QAAM,MAAM;AAEZ,QAAM,WAAW,EAAE,cAAc,cAAc,IAAI,GAAG,eAAe;AACrE,QAAM,UAAU,KAAK,cAAU,8BAAe,QAA4B,CAAC;AAE3E,QAAM,QAAQ,SAAS,WACpB,IAAI;AAAA,IACH,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA,UAAU,KAAK,UAAU,QAAQ;AAAA,IACjC;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC,EACA,GAAG;AAEN,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,MAAM,EAAE,aAAa,WAAW,IAAI;AAAA,EACtC;AACF;;;ACnCA,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;;;AClCA,IAAAC,gBAAsD;AAgBtD,eAAsB,8BACpB,QACqC;AACrC,QAAM,EAAE,SAAS,MAAM,UAAU,IAAI;AACrC,QAAM,UAAU,wBAAwB,SAAS;AAEjD,QAAM,KAAK,KAAK,MAAM,kBAAkB,KAAK,IAAI,CAAC;AAClD,QAAM,iBACJ,OAAO,KAAK,aAAa,WACpB,KAAK,MAAM,KAAK,QAAQ,IACxB,KAAK,YAAY,CAAC;AAEzB,QAAM,cAAc,QAAQ,SAAQ,oBAAI,KAAK,GAAE,YAAY;AAC3D,QAAM,MAAM;AAEZ,QAAM,WAAW,EAAE,cAAc,kBAAkB,IAAI,GAAG,eAAe;AACzE,QAAM,UAAU,KAAK,cAAU,8BAAe,QAA4B,CAAC;AAE3E,QAAM,QAAQ,SAAS,eACpB,IAAI;AAAA,IACH,UAAU,QAAQ;AAAA,IAClB;AAAA,IACA,UAAU,KAAK,UAAU,QAAQ;AAAA,IACjC;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC,EACA,GAAG;AAEN,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,MAAM,EAAE,aAAa,WAAW,IAAI;AAAA,EACtC;AACF;;;AClDA,IAAAC,gBAAsD;AAsBtD,eAAsB,sBACpB,QAC6B;AAC7B,QAAM,EAAE,SAAS,MAAM,UAAU,IAAI;AACrC,QAAM,UAAU,wBAAwB,SAAS;AAEjD,QAAM,KAAK,KAAK,MAAM,UAAU,KAAK,IAAI,CAAC;AAC1C,QAAM,cAAc,QAAQ;AAC5B,QAAM,MACJ,YAAY,QAAQ,YAAY,EAAE,EAAE,MAAM,GAAG,EAAE,KAAK,KAAK,IAAI,EAAE,SAAS,EAAE;AAE5E,QAAM,iBACJ,OAAO,KAAK,aAAa,WACpB,KAAK,MAAM,KAAK,QAAQ,IACxB,KAAK,YAAY,CAAC;AAEzB,QAAM,WAAW,EAAE,cAAc,UAAU,IAAI,GAAG,eAAe;AACjE,QAAM,UAAU,KAAK,cAAU,8BAAe,QAA4B,CAAC;AAE3E,QAAM,QAAQ,SAAS,OACpB,IAAI;AAAA,IACH,UAAU;AAAA,IACV;AAAA,IACA,UAAU,KAAK,UAAU,QAAQ;AAAA,IACjC;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC,EACA,GAAG;AAEN,SAAO,EAAE,IAAI,UAAU,MAAM,EAAE,aAAa,WAAW,IAAI,EAAE;AAC/D;;;ACrDA,IAAAC,gBAAsD;AAsBtD,eAAsB,yBACpB,QACgC;AAChC,QAAM,EAAE,SAAS,MAAM,UAAU,IAAI;AACrC,QAAM,EAAE,SAAS,IAAI;AACrB,QAAM,UAAU,wBAAwB,SAAS;AAEjD,QAAM,KAAK,KAAK,MAAM,aAAa,KAAK,IAAI,CAAC;AAC7C,QAAM,cAAc,QAAQ;AAC5B,QAAM,MACJ,YAAY,QAAQ,YAAY,EAAE,EAAE,MAAM,GAAG,EAAE,KAAK,KAAK,IAAI,EAAE,SAAS,EAAE;AAE5E,QAAM,iBACJ,OAAO,KAAK,aAAa,WACpB,KAAK,MAAM,KAAK,QAAQ,IACxB,KAAK,YAAY,CAAC;AAEzB,QAAM,WAAW,EAAE,cAAc,aAAa,IAAI,GAAG,eAAe;AACpE,QAAM,UAAU,KAAK,cAAU,8BAAe,QAA4B,CAAC;AAE3E,QAAM,QAAQ,SAAS,UACpB,IAAI;AAAA,IACH;AAAA,IACA;AAAA,IACA,UAAU,KAAK,UAAU,QAAQ;AAAA,IACjC;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC,EACA,GAAG;AAEN,SAAO,EAAE,IAAI,UAAU,MAAM,EAAE,aAAa,WAAW,IAAI,EAAE;AAC/D;;;AlBcO,IAAM,sCACX;AA0CF,IAAM,eAAe,CAAC,QAAyB;AAC7C,MAAI,eAAe,OAAO;AACxB,WAAO,IAAI;AAAA,EACb;AACA,SAAO,OAAO,GAAG;AACnB;AASA,IAAM,gBAAgB,CAAC,SAAuC;AAC5D,aAAW,OAAO,OAAO,KAAK,oCAAsB,GAEjD;AACD,QAAI,0CAA4B,GAAG,EAAE,SAAS,MAAM;AAClD,aAAO,qCAAuB,GAAG;AAAA,IACnC;AAAA,EACF;AACA,QAAM,IAAI,MAAM,gCAAgC,IAAI,IAAI;AAC1D;AASA,IAAM,yBAAyB,YAA2B;AACxD,QAAM,eAA8B;AAAA,IAClC,UAAU;AAAA,IACV,aAAa;AAAA,IACb,OAAM,oBAAI,KAAK,CAAC,GAAE,YAAY;AAAA,IAC9B,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WAAW;AAAA,IACX,QAAQ;AAAA,EACV;AACA,aAAW,MAAM,OAAO,OAAO,oCAAsB,GAAG;AACtD,QAAI;AACF,YAAM,qBAAqB,EAAE,SAAS,cAAc,GAAG,CAAC;AAAA,IAC1D,SAAS,KAAK;AACZ,UAAI,eAAe,eAAe;AAChC,cAAM,IAAI;AAAA,UACR,kDAAkD,EAAE;AAAA,QAEtD;AAAA,MACF;AACA,YAAM;AAAA,IACR;AAAA,EACF;AACF;AAOA,IAAM,qBAAqB,CACzB,UAC6B;AAAA,EAC7B,MAAM,KAAK;AAAA,EACX,QAAQ;AAAA,EACR,YAAY;AAAA,IACV,uBAAuB,KAAK,UAAU,QAAQ;AAAA,IAC9C,yBAAyB;AAAA,MACvB,UAAU,KAAK;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,IAAI,KAAK;AAAA,IACX,CAAC;AAAA,EACH;AACF;AAEA,IAAM,wBAAwB,CAC5B,MACA,eAC6B;AAAA,EAC7B,MAAM,UAAU;AAAA,EAChB,QAAQ;AAAA,EACR,YAAY;AAAA,IACV,uBAAuB,KAAK,UAAU,UAAU,UAAU;AAAA,IAC1D,yBAAyB;AAAA,MACvB,UAAU,KAAK;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,IAAI,UAAU;AAAA,IAChB,CAAC;AAAA,EACH;AAAA,EACA,QAAQ,EAAE,WAAW,UAAU,KAAK,QAAQ,IAAI,MAAM,SAAS;AACjE;AAEA,IAAM,yBAAyB,CAC7B,MACA,MACA,kBAC6B;AAAA,EAC7B,YAAY;AAAA,IACV,uBAAuB,KAAK,UAAU,cAAc,KAAK,EAAE,EAAE;AAAA,IAC7D,yBAAyB;AAAA,MACvB,UAAU,KAAK;AAAA,MACf,aAAa;AAAA,MACb,cAAc;AAAA,MACd,IAAI;AAAA,IACN,CAAC;AAAA,EACH;AAAA,EACA,MAAM,EAAE,WAAW,QAAQ,KAAK,EAAE,IAAI,MAAM,OAAO;AAAA,EACnD,QAAQ,EAAE,WAAW,UAAU,KAAK,QAAQ,IAAI,MAAM,SAAS;AAAA,EAC/D,QAAQ;AACV;AAEA,IAAM,6BAA6B,CACjC,UACA,UACA,MACA,UACA,sBAC6B;AAAA,EAC7B,YAAY;AAAA,IACV,uBAAuB,UAAU,kBAAkB,KAAK,EAAE,IAAI,QAAQ,EAAE;AAAA,IACxE,yBAAyB;AAAA,MACvB;AAAA,MACA,aAAa;AAAA,MACb,cAAc;AAAA,MACd,IAAI;AAAA,IACN,CAAC;AAAA,EACH;AAAA,EACA,MAAM,EAAE,WAAW,QAAQ,KAAK,EAAE,IAAI,MAAM,OAAO;AAAA,EACnD,MAAM,EAAE,WAAW,QAAQ,cAAc,QAAQ,CAAC,IAAI,MAAM,OAAO;AAAA,EACnE,QAAQ,EAAE,WAAW,UAAU,QAAQ,IAAI,MAAM,SAAS;AAAA,EAC1D,QAAQ;AACV;AAGA,IAAM,mBAAmB,CACvB,MACA,gBAC6B;AAAA,EAC7B,cAAc;AAAA,EACd,IAAI,KAAK;AAAA,EACT,MAAM,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;AAAA,EAC3B,QAAQ;AAAA,EACR;AAAA,EACA,eAAe,EAAE,WAAW,UAAU,qBAAqB,GAAG;AAAA,EAC9D,kBAAkB,EAAE,WAAW,aAAa,wBAAwB,GAAG;AACzE;AAOA,IAAM,aAAa,OACjB,SACA,MACA,eACkB;AAClB,QAAM,UAAU,wBAAwB;AACxC,QAAM,WAAW,iBAAiB,MAAM,UAAU;AAClD,QAAM,UAAU,KAAK,cAAU,8BAAe,QAA4B,CAAC;AAC3E,QAAM,QAAQ,SAAS,KACpB,IAAI;AAAA,IACH,IAAI,KAAK;AAAA,IACT;AAAA,IACA,UAAU,KAAK,UAAU,QAAQ;AAAA,IACjC;AAAA,IACA,KAAK;AAAA,IACL,aAAa,QAAQ,SAAQ,oBAAI,KAAK,GAAE,YAAY;AAAA,EACtD,CAAC,EACA,GAAG;AACR;AAcO,IAAM,gBAAgB,OAAO,WAIf;AACnB,QAAM,EAAE,aAAa,UAAU,QAAQ,IAAI;AAI3C,aAAW,QAAQ,mBAAmB;AACpC,UAAM,gBAA+B;AAAA,MACnC,GAAG;AAAA,MACH,UAAU,KAAK;AAAA,IACjB;AAEA,UAAM,sBAAsB;AAAA,MAC1B,SAAS;AAAA,MACT,MAAM,EAAE,IAAI,KAAK,UAAU,UAAU,mBAAmB,IAAI,EAAE;AAAA,IAChE,CAAC;AAED,eAAW,aAAa,KAAK,YAAY;AACvC,YAAM,yBAAyB;AAAA,QAC7B,SAAS;AAAA,QACT,MAAM;AAAA,UACJ,IAAI,UAAU;AAAA,UACd,UAAU,sBAAsB,MAAM,SAAS;AAAA,QACjD;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAMA,aAAW,QAAQ,UAAU;AAC3B,UAAM,aAAa,MAAM,QAAQ,WAAW,KAAK,KAAK;AAEtD,UAAM,WAAW,aAAa,MAAM,UAAU;AAE9C,eAAW,QAAQ,mBAAmB;AACpC,YAAM,gBAA+B;AAAA,QACnC,GAAG;AAAA,QACH,UAAU,KAAK;AAAA,MACjB;AAEA,YAAM,eAAe,iBAAiB,KAAK,IAAI,KAAK,QAAQ;AAC5D,YAAM,0BAA0B;AAAA,QAC9B,SAAS;AAAA,QACT,MAAM;AAAA,UACJ,IAAI;AAAA,UACJ,UAAU,uBAAuB,MAAM,MAAM,YAAY;AAAA,QAC3D;AAAA,MACF,CAAC;AAED,iBAAW,YAAY,yBAAyB,MAAM,KAAK,QAAQ,GAAG;AACpE,cAAM,OAAO,qBAAqB,KAAK,IAAI,KAAK,UAAU,QAAQ;AAClE,cAAM,8BAA8B;AAAA,UAClC,SAAS;AAAA,UACT,MAAM;AAAA,YACJ,IAAI;AAAA,YACJ,UAAU;AAAA,cACR,KAAK;AAAA,cACL,KAAK;AAAA,cACL;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAMA,UAAM,kBAAiC;AAAA,MACrC,GAAG;AAAA,MACH,UAAU;AAAA,IACZ;AACA,UAAM,mBAAmB,sCAAwB;AACjD,UAAM,eAAe;AAAA,MACnB,KAAK;AAAA,MACL;AAAA,MACA;AAAA,IACF;AACA,UAAM,8BAA8B;AAAA,MAClC,SAAS;AAAA,MACT,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,UAAU;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAOO,IAAM,kBAAkB,OAC7B,OACA,MACA,aACkB;AAClB,QAAM,aAAS,sCAAmB,OAAO,2CAA0B;AAEnE,QAAM,eAAe,MAAM,KAAK,YAAY,eAAe;AAAA,IACzD,cAAc;AAAA,IACd,SAAS,OAAO,SAAS;AAAA,IACzB,SAAS,OAAO,SAAS;AAAA,EAC3B,CAAC;AACD,MAAI,CAAC,aAAa,UAAU;AAC1B;AAAA,EACF;AAEA,QAAM,cAA6B;AAAA,IACjC,UAAU;AAAA,IACV,aAAa;AAAA,IACb,MAAM,OAAO,SAAS;AAAA,IACtB,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WAAW;AAAA,IACX,QAAQ;AAAA,EACV;AAEA,MAAI;AACF,UAAM,KAAK,YAAY;AACvB,UAAM,KAAK,cAAc;AAAA,MACvB;AAAA,MACA;AAAA,MACA,SAAS,KAAK;AAAA,IAChB,CAAC;AAAA,EACH,SAAS,KAAK;AAMZ,UAAM,KAAK,YAAY,WAAW;AAAA,MAChC,cAAc;AAAA,MACd,SAAS,OAAO,SAAS;AAAA,MACzB,SAAS,OAAO,SAAS;AAAA,MACzB,QAAQ,aAAa,GAAG;AAAA,IAC1B,CAAC;AACD,UAAM;AAAA,EACR;AACF;AASO,IAAM,sBAAsB,CAAC,UAA0B;AAC5D,QAAM,aAAS,+BAAW,QAAQ,EAAE,OAAO,KAAK,EAAE,OAAO;AACzD,QAAM,YAAY,OACf,SAAS,QAAQ,EACjB,QAAQ,OAAO,GAAG,EAClB,QAAQ,OAAO,GAAG,EAClB,QAAQ,QAAQ,EAAE;AACrB,SAAO,OAAO,UAAU,MAAM,GAAG,EAAE,CAAC;AACtC;AAeO,IAAM,+BAA+B,MAA0B;AACpE,QAAM,SAAS,IAAI,sEAA8B,CAAC,CAAC;AACnD,QAAM,aAAa,QAAQ,IAAI,mCAAmC;AAClE,MAAI,CAAC,cAAc,WAAW,KAAK,MAAM,IAAI;AAC3C,UAAM,IAAI;AAAA,MACR,GAAG,mCAAmC;AAAA,IAExC;AAAA,EACF;AACA,QAAM,oBAAoB,CACxB,UACuB;AACvB,eAAW,QAAQ,SAAS,CAAC,GAAG;AAC9B,UAAI,KAAK,SAAS,SAAS,OAAO,KAAK,UAAU,UAAU;AACzD,eAAO,KAAK;AAAA,MACd;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,SAAO;AAAA,IACL,YAAY,OAAO,UAAmC;AACpD,UAAI;AACF,cAAM,UAAU,MAAM,OAAO;AAAA,UAC3B,IAAI,+DAAuB;AAAA,YACzB,YAAY;AAAA,YACZ,UAAU;AAAA,YACV,eAAe;AAAA,YACf,gBAAgB;AAAA,cACd,EAAE,MAAM,SAAS,OAAO,MAAM;AAAA,cAC9B,EAAE,MAAM,kBAAkB,OAAO,OAAO;AAAA,YAC1C;AAAA,UACF,CAAC;AAAA,QACH;AACA,cAAM,OAAO;AAAA,UACX,IAAI,oEAA4B;AAAA,YAC9B,YAAY;AAAA,YACZ,UAAU;AAAA,YACV,UAAU,oBAAoB,KAAK;AAAA,YACnC,WAAW;AAAA,UACb,CAAC;AAAA,QACH;AACA,cAAM,MAAM,kBAAkB,QAAQ,MAAM,UAAU;AACtD,YAAI,CAAC,KAAK;AACR,gBAAM,IAAI;AAAA,YACR,iCAAiC,KAAK;AAAA,UACxC;AAAA,QACF;AACA,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,YAAI,eAAe,iEAAyB;AAG1C,gBAAM,MAAM,MAAM,OAAO;AAAA,YACvB,IAAI,4DAAoB;AAAA,cACtB,YAAY;AAAA,cACZ,UAAU;AAAA,YACZ,CAAC;AAAA,UACH;AACA,gBAAM,MAAM,kBAAkB,IAAI,cAAc;AAChD,cAAI,CAAC,KAAK;AACR,kBAAM,IAAI;AAAA,cACR,8BAA8B,KAAK;AAAA,YACrC;AAAA,UACF;AACA,iBAAO;AAAA,QACT;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAM,yBAAyB,MAAgC;AAC7D,QAAM,WAAW,IAAI,uCAAe,CAAC,CAAC;AACtC,QAAM,UAAU,6BAA6B;AAC7C,SAAO;AAAA,IACL,iBAAa,uCAAoB,QAAQ;AAAA,IACzC,aAAa;AAAA,IACb;AAAA,IACA;AAAA,EACF;AACF;AAEO,IAAM,UAAU,OAAO,UAC5B,gBAAgB,OAAO,uBAAuB,GAAG,SAAS;","names":["exports","exports","exports","exports","exports","exports","exports","exports","parseWorkflowEvent","exports","exports","workflowDedupClient","exports","exports","import_client_dynamodb","import_types","import_workflows","import_electrodb","import_types","import_electrodb","import_electrodb","import_electrodb","import_electrodb","import_electrodb","import_electrodb","import_types","import_types","import_types","import_types"]}
@@ -0,0 +1,23 @@
1
+ import {
2
+ SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR,
3
+ devPasswordForEmail,
4
+ handler,
5
+ productionCognitoProvisioner,
6
+ runSeedDemoData,
7
+ seedDemoGraph
8
+ } from "./chunk-QMIOLLAS.mjs";
9
+ import "./chunk-L6UAP4KP.mjs";
10
+ import "./chunk-SYBADQXI.mjs";
11
+ import "./chunk-AO3E22CS.mjs";
12
+ import "./chunk-YZZDUJHI.mjs";
13
+ import "./chunk-VYDIGFIX.mjs";
14
+ import "./chunk-LZOMFHX3.mjs";
15
+ export {
16
+ SEED_DEMO_DATA_USER_POOL_ID_ENV_VAR,
17
+ devPasswordForEmail,
18
+ handler,
19
+ productionCognitoProvisioner,
20
+ runSeedDemoData,
21
+ seedDemoGraph
22
+ };
23
+ //# sourceMappingURL=seed-demo-data.handler.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,64 @@
1
+ import { WorkflowDedupClient, PublishResult } from '@openhi/workflows';
2
+ import { EventBridgeEvent } from 'aws-lambda';
3
+ import { O as OpenHiContext } from './openhi-context-CaBH8SFo.mjs';
4
+
5
+ /**
6
+ * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-system-data/seed-system-data-handler.md
7
+ *
8
+ * EventBridge workflow handler invoked once per platform-deploy event on
9
+ * the control event bus. Idempotently re-asserts the platform-singleton
10
+ * control-plane records (today: the three canonical Roles; future:
11
+ * additional system data slotted in as sibling steps under the same
12
+ * dedup record).
13
+ */
14
+ type SeedSystemDataEvent = EventBridgeEvent<"platform.deployment-completed.v1", unknown>;
15
+ /**
16
+ * Input shape the handler passes to the completion publisher. Mirrors
17
+ * the wire shape of `PlatformSystemDataSeededV1Detail` plus the
18
+ * envelope-correlation fields the publisher needs to chain the new
19
+ * event onto the originating deploy event.
20
+ */
21
+ interface PublishSeededInput {
22
+ /** Per-workflow payload — re-asserted record count + source event/stack ids. */
23
+ readonly payload: {
24
+ readonly sourceEventId: string;
25
+ readonly sourceStackId: string;
26
+ readonly seededRecordCount: number;
27
+ };
28
+ /**
29
+ * Envelope context propagated from the originating
30
+ * `platform.deployment-completed.v1` event so the chain is
31
+ * observable in logs.
32
+ */
33
+ readonly envelope: {
34
+ readonly correlationId: string;
35
+ readonly causationId: string;
36
+ };
37
+ }
38
+ /**
39
+ * Dependency seam for tests. The factory mirrors the production
40
+ * arrangement: a real DynamoDB client + the real `workflowDedupClient`
41
+ * factory bound to the `OPENHI_WORKFLOW_DEDUP_TABLE_NAME` env var the
42
+ * construct injects via `grantConsumer`.
43
+ */
44
+ interface SeedSystemDataDependencies {
45
+ readonly dedupClient: WorkflowDedupClient;
46
+ readonly seedRoles: (context: OpenHiContext) => Promise<void>;
47
+ /**
48
+ * Publishes the `platform.system-data-seeded.v1` completion event
49
+ * onto the control event bus. Called after a successful seed and
50
+ * before the handler returns — downstream consumers
51
+ * (`seed-demo-data`) subscribe to this event instead of the raw
52
+ * deploy-completion event so the dependency on system data is
53
+ * enforced by a happens-before edge.
54
+ */
55
+ readonly publishSeeded: (input: PublishSeededInput) => Promise<PublishResult>;
56
+ }
57
+ /**
58
+ * Test-visible orchestrator. The production `handler` calls this with
59
+ * real dependencies; unit tests inject fakes.
60
+ */
61
+ declare const runSeedSystemData: (event: SeedSystemDataEvent, deps: SeedSystemDataDependencies) => Promise<void>;
62
+ declare const handler: (event: SeedSystemDataEvent) => Promise<void>;
63
+
64
+ export { type PublishSeededInput, type SeedSystemDataDependencies, handler, runSeedSystemData };
@@ -0,0 +1,64 @@
1
+ import { WorkflowDedupClient, PublishResult } from '@openhi/workflows';
2
+ import { EventBridgeEvent } from 'aws-lambda';
3
+ import { O as OpenHiContext } from './openhi-context-CaBH8SFo.js';
4
+
5
+ /**
6
+ * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-system-data/seed-system-data-handler.md
7
+ *
8
+ * EventBridge workflow handler invoked once per platform-deploy event on
9
+ * the control event bus. Idempotently re-asserts the platform-singleton
10
+ * control-plane records (today: the three canonical Roles; future:
11
+ * additional system data slotted in as sibling steps under the same
12
+ * dedup record).
13
+ */
14
+ type SeedSystemDataEvent = EventBridgeEvent<"platform.deployment-completed.v1", unknown>;
15
+ /**
16
+ * Input shape the handler passes to the completion publisher. Mirrors
17
+ * the wire shape of `PlatformSystemDataSeededV1Detail` plus the
18
+ * envelope-correlation fields the publisher needs to chain the new
19
+ * event onto the originating deploy event.
20
+ */
21
+ interface PublishSeededInput {
22
+ /** Per-workflow payload — re-asserted record count + source event/stack ids. */
23
+ readonly payload: {
24
+ readonly sourceEventId: string;
25
+ readonly sourceStackId: string;
26
+ readonly seededRecordCount: number;
27
+ };
28
+ /**
29
+ * Envelope context propagated from the originating
30
+ * `platform.deployment-completed.v1` event so the chain is
31
+ * observable in logs.
32
+ */
33
+ readonly envelope: {
34
+ readonly correlationId: string;
35
+ readonly causationId: string;
36
+ };
37
+ }
38
+ /**
39
+ * Dependency seam for tests. The factory mirrors the production
40
+ * arrangement: a real DynamoDB client + the real `workflowDedupClient`
41
+ * factory bound to the `OPENHI_WORKFLOW_DEDUP_TABLE_NAME` env var the
42
+ * construct injects via `grantConsumer`.
43
+ */
44
+ interface SeedSystemDataDependencies {
45
+ readonly dedupClient: WorkflowDedupClient;
46
+ readonly seedRoles: (context: OpenHiContext) => Promise<void>;
47
+ /**
48
+ * Publishes the `platform.system-data-seeded.v1` completion event
49
+ * onto the control event bus. Called after a successful seed and
50
+ * before the handler returns — downstream consumers
51
+ * (`seed-demo-data`) subscribe to this event instead of the raw
52
+ * deploy-completion event so the dependency on system data is
53
+ * enforced by a happens-before edge.
54
+ */
55
+ readonly publishSeeded: (input: PublishSeededInput) => Promise<PublishResult>;
56
+ }
57
+ /**
58
+ * Test-visible orchestrator. The production `handler` calls this with
59
+ * real dependencies; unit tests inject fakes.
60
+ */
61
+ declare const runSeedSystemData: (event: SeedSystemDataEvent, deps: SeedSystemDataDependencies) => Promise<void>;
62
+ declare const handler: (event: SeedSystemDataEvent) => Promise<void>;
63
+
64
+ export { type PublishSeededInput, type SeedSystemDataDependencies, handler, runSeedSystemData };