@openhi/constructs 0.0.106 → 0.0.107

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,8 +20,8 @@ var import_workflows2 = __toESM(require_lib());
20
20
  import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
21
21
  import { EventBridgeClient } from "@aws-sdk/client-eventbridge";
22
22
  import {
23
- CONTROL_PLANE_ROLE_CONCEPTS,
24
- CONTROL_PLANE_ROLE_IDS
23
+ PLATFORM_ROLE_CONCEPTS,
24
+ PLATFORM_ROLE_IDS
25
25
  } from "@openhi/types";
26
26
  var errorMessage = (err) => {
27
27
  if (err instanceof Error) {
@@ -30,15 +30,15 @@ var errorMessage = (err) => {
30
30
  return String(err);
31
31
  };
32
32
  var idForRoleCode = (code) => {
33
- for (const key of Object.keys(CONTROL_PLANE_ROLE_IDS)) {
34
- if (CONTROL_PLANE_ROLE_CONCEPTS[key].code === code) {
35
- return CONTROL_PLANE_ROLE_IDS[key];
33
+ for (const key of Object.keys(PLATFORM_ROLE_IDS)) {
34
+ if (PLATFORM_ROLE_CONCEPTS[key].code === code) {
35
+ return PLATFORM_ROLE_IDS[key];
36
36
  }
37
37
  }
38
38
  throw new Error(`No id mapping for role code "${code}".`);
39
39
  };
40
40
  var seedSystemRoles = async (context) => {
41
- for (const concept of Object.values(CONTROL_PLANE_ROLE_CONCEPTS)) {
41
+ for (const concept of Object.values(PLATFORM_ROLE_CONCEPTS)) {
42
42
  const id = idForRoleCode(concept.code);
43
43
  await createRoleOperation({
44
44
  context,
@@ -90,7 +90,7 @@ var runSeedSystemData = async (event, deps) => {
90
90
  payload: {
91
91
  sourceEventId: parsed.envelope.eventId,
92
92
  sourceStackId: sourcePayload.stackId,
93
- seededRecordCount: Object.keys(CONTROL_PLANE_ROLE_IDS).length
93
+ seededRecordCount: Object.keys(PLATFORM_ROLE_IDS).length
94
94
  },
95
95
  envelope: {
96
96
  correlationId: parsed.envelope.correlationId,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/workflows/control-plane/seed-system-data/seed-system-data.handler.ts"],"sourcesContent":["import { DynamoDBClient } from \"@aws-sdk/client-dynamodb\";\nimport { EventBridgeClient } from \"@aws-sdk/client-eventbridge\";\nimport {\n CONTROL_PLANE_ROLE_CONCEPTS,\n CONTROL_PLANE_ROLE_IDS,\n type ControlPlaneRoleCode,\n} from \"@openhi/types\";\nimport {\n OPENHI_CONTROL_SOURCE,\n parseWorkflowEvent,\n publishWorkflowEvent,\n workflowDedupClient,\n type PlatformDeploymentCompletedV1Detail,\n type PublishResult,\n type WorkflowDedupClient,\n} from \"@openhi/workflows\";\nimport type { EventBridgeEvent } from \"aws-lambda\";\nimport {\n PlatformDeploymentCompletedV1,\n PlatformSystemDataSeededV1,\n SEED_SYSTEM_DATA_ACTOR_SYSTEM,\n SEED_SYSTEM_DATA_CONSUMER_NAME,\n SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR,\n} from \"./events\";\nimport type { OpenHiContext } from \"../../../data/openhi-context\";\nimport { createRoleOperation } from \"../../../data/operations/control/role/role-create-operation\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-system-data/seed-system-data-handler.md\n *\n * EventBridge workflow handler invoked once per platform-deploy event on\n * the control event bus. Idempotently re-asserts the platform-singleton\n * control-plane records (today: the three canonical Roles; future:\n * additional system data slotted in as sibling steps under the same\n * dedup record).\n */\n\ntype SeedSystemDataEvent = EventBridgeEvent<\n \"platform.deployment-completed.v1\",\n unknown\n>;\n\n/**\n * Input shape the handler passes to the completion publisher. Mirrors\n * the wire shape of `PlatformSystemDataSeededV1Detail` plus the\n * envelope-correlation fields the publisher needs to chain the new\n * event onto the originating deploy event.\n */\nexport interface PublishSeededInput {\n /** Per-workflow payload — re-asserted record count + source event/stack ids. */\n readonly payload: {\n readonly sourceEventId: string;\n readonly sourceStackId: string;\n readonly seededRecordCount: number;\n };\n /**\n * Envelope context propagated from the originating\n * `platform.deployment-completed.v1` event so the chain is\n * observable in logs.\n */\n readonly envelope: {\n readonly correlationId: string;\n readonly causationId: string;\n };\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 SeedSystemDataDependencies {\n readonly dedupClient: WorkflowDedupClient;\n readonly seedRoles: (context: OpenHiContext) => Promise<void>;\n /**\n * Publishes the `platform.system-data-seeded.v1` completion event\n * onto the control event bus. Called after a successful seed and\n * before the handler returns — downstream consumers\n * (`seed-demo-data`) subscribe to this event instead of the raw\n * deploy-completion event so the dependency on system data is\n * enforced by a happens-before edge.\n */\n readonly publishSeeded: (input: PublishSeededInput) => Promise<PublishResult>;\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 * Lookup the stable record id for a given role code by walking the two\n * parallel generator-emitted constants. The `_IDS` / `_CONCEPTS` consts\n * are keyed by the same SCREAMING_SNAKE keys (the generator builds them\n * in lockstep), so a single key resolves 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 // Unreachable while `_IDS` and `_CONCEPTS` are emitted from the same\n // CodeSystem: every code in `_CONCEPTS` has a matching id in `_IDS`.\n throw new Error(`No id mapping for role code \"${code}\".`);\n};\n\n/**\n * Re-assert the three canonical control-plane Role records. Stable ids\n * from `CONTROL_PLANE_ROLE_IDS` make re-runs after dedup-TTL expiry\n * idempotent: every put with the same id+sk lands on the same primary\n * key and produces the same end state.\n */\nconst seedSystemRoles = async (context: OpenHiContext): Promise<void> => {\n for (const concept of Object.values(CONTROL_PLANE_ROLE_CONCEPTS)) {\n const id = idForRoleCode(concept.code);\n await createRoleOperation({\n context,\n body: {\n id,\n resource: {\n resourceType: \"Role\",\n code: concept.code,\n display: concept.display,\n active: true,\n },\n },\n });\n }\n};\n\n/**\n * Test-visible orchestrator. The production `handler` calls this with\n * real dependencies; unit tests inject fakes.\n */\nexport const runSeedSystemData = async (\n event: SeedSystemDataEvent,\n deps: SeedSystemDataDependencies,\n): Promise<void> => {\n const parsed = parseWorkflowEvent(event, PlatformDeploymentCompletedV1);\n\n // Dedup is the single circuit-breaker between EventBridge's\n // at-least-once redelivery and the DynamoDB writes below. If\n // `recordIfAbsent` returns `recorded: false`, a prior invocation of\n // this (eventId, attempt) tuple already ran to completion — there is\n // nothing left to do, so we exit without re-iterating the catalog.\n const recordResult = await deps.dedupClient.recordIfAbsent({\n consumerName: SEED_SYSTEM_DATA_CONSUMER_NAME,\n eventId: parsed.dedupKey.eventId,\n attempt: parsed.dedupKey.attempt,\n });\n if (!recordResult.recorded) {\n return;\n }\n\n // Role records are platform-wide singletons (per ADR 2026-03-13-02 §4)\n // and live under the sentinel tenant/workspace coordinates the\n // `role-entity` declares (`TID#-#WID#-`). We pass an internal-system\n // context whose tenant/workspace fields are empty strings; the role\n // create operation reads only `date` and audit metadata.\n const lastUpdated = parsed.envelope.occurredAt;\n const context: OpenHiContext = {\n tenantId: \"\",\n workspaceId: \"\",\n date: lastUpdated,\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.seedRoles(context);\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_SYSTEM_DATA_CONSUMER_NAME,\n eventId: parsed.dedupKey.eventId,\n attempt: parsed.dedupKey.attempt,\n reason: errorMessage(err),\n });\n throw err;\n }\n\n // Seeding succeeded — publish `platform.system-data-seeded.v1` so\n // downstream consumers that depend on the platform-singleton\n // records existing (today: `seed-demo-data`) are triggered by a\n // happens-before edge rather than by EventBridge retry timing.\n //\n // A publish failure here does NOT roll back the seed — the records\n // are already in the data store and idempotent on re-fire. We log\n // and re-throw so EventBridge retries the whole event; on retry,\n // dedup wins (no-op) and the publish path re-runs.\n const sourcePayload = parsed.envelope\n .payload as PlatformDeploymentCompletedV1Detail;\n await deps.publishSeeded({\n payload: {\n sourceEventId: parsed.envelope.eventId,\n sourceStackId: sourcePayload.stackId,\n seededRecordCount: Object.keys(CONTROL_PLANE_ROLE_IDS).length,\n },\n envelope: {\n correlationId: parsed.envelope.correlationId,\n causationId: parsed.envelope.eventId,\n },\n });\n};\n\nconst productionDependencies = (): SeedSystemDataDependencies => {\n const dynamodb = new DynamoDBClient({});\n const eventBridge = new EventBridgeClient({});\n return {\n dedupClient: workflowDedupClient(dynamodb),\n seedRoles: seedSystemRoles,\n publishSeeded: async (input) => {\n const controlBusName = process.env[SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR];\n if (!controlBusName) {\n throw new Error(\n `seed-system-data: ${SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR} env var is required to publish system-data-seeded events.`,\n );\n }\n return publishWorkflowEvent(\n eventBridge,\n PlatformSystemDataSeededV1,\n input.payload,\n {\n actor: { system: SEED_SYSTEM_DATA_ACTOR_SYSTEM },\n correlationId: input.envelope.correlationId,\n causationId: input.envelope.causationId,\n },\n {\n busNameByPlane: { [OPENHI_CONTROL_SOURCE]: controlBusName },\n },\n );\n },\n };\n};\n\nexport const handler = async (event: SeedSystemDataEvent): Promise<void> =>\n runSeedSystemData(event, productionDependencies());\n"],"mappings":";;;;;;;;;;;;;;;;;;AAOA,IAAAA,oBAQO;AAfP,SAAS,sBAAsB;AAC/B,SAAS,yBAAyB;AAClC;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AAgFP,IAAM,eAAe,CAAC,QAAyB;AAC7C,MAAI,eAAe,OAAO;AACxB,WAAO,IAAI;AAAA,EACb;AACA,SAAO,OAAO,GAAG;AACnB;AAQA,IAAM,gBAAgB,CAAC,SAAuC;AAC5D,aAAW,OAAO,OAAO,KAAK,sBAAsB,GAEjD;AACD,QAAI,4BAA4B,GAAG,EAAE,SAAS,MAAM;AAClD,aAAO,uBAAuB,GAAG;AAAA,IACnC;AAAA,EACF;AAGA,QAAM,IAAI,MAAM,gCAAgC,IAAI,IAAI;AAC1D;AAQA,IAAM,kBAAkB,OAAO,YAA0C;AACvE,aAAW,WAAW,OAAO,OAAO,2BAA2B,GAAG;AAChE,UAAM,KAAK,cAAc,QAAQ,IAAI;AACrC,UAAM,oBAAoB;AAAA,MACxB;AAAA,MACA,MAAM;AAAA,QACJ;AAAA,QACA,UAAU;AAAA,UACR,cAAc;AAAA,UACd,MAAM,QAAQ;AAAA,UACd,SAAS,QAAQ;AAAA,UACjB,QAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAMO,IAAM,oBAAoB,OAC/B,OACA,SACkB;AAClB,QAAM,aAAS,sCAAmB,OAAO,8CAA6B;AAOtE,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;AAOA,QAAM,cAAc,OAAO,SAAS;AACpC,QAAM,UAAyB;AAAA,IAC7B,UAAU;AAAA,IACV,aAAa;AAAA,IACb,MAAM;AAAA,IACN,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WAAW;AAAA,IACX,QAAQ;AAAA,EACV;AAEA,MAAI;AACF,UAAM,KAAK,UAAU,OAAO;AAAA,EAC9B,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;AAWA,QAAM,gBAAgB,OAAO,SAC1B;AACH,QAAM,KAAK,cAAc;AAAA,IACvB,SAAS;AAAA,MACP,eAAe,OAAO,SAAS;AAAA,MAC/B,eAAe,cAAc;AAAA,MAC7B,mBAAmB,OAAO,KAAK,sBAAsB,EAAE;AAAA,IACzD;AAAA,IACA,UAAU;AAAA,MACR,eAAe,OAAO,SAAS;AAAA,MAC/B,aAAa,OAAO,SAAS;AAAA,IAC/B;AAAA,EACF,CAAC;AACH;AAEA,IAAM,yBAAyB,MAAkC;AAC/D,QAAM,WAAW,IAAI,eAAe,CAAC,CAAC;AACtC,QAAM,cAAc,IAAI,kBAAkB,CAAC,CAAC;AAC5C,SAAO;AAAA,IACL,iBAAa,uCAAoB,QAAQ;AAAA,IACzC,WAAW;AAAA,IACX,eAAe,OAAO,UAAU;AAC9B,YAAM,iBAAiB,QAAQ,IAAI,oCAAoC;AACvE,UAAI,CAAC,gBAAgB;AACnB,cAAM,IAAI;AAAA,UACR,qBAAqB,oCAAoC;AAAA,QAC3D;AAAA,MACF;AACA,iBAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN;AAAA,UACE,OAAO,EAAE,QAAQ,8BAA8B;AAAA,UAC/C,eAAe,MAAM,SAAS;AAAA,UAC9B,aAAa,MAAM,SAAS;AAAA,QAC9B;AAAA,QACA;AAAA,UACE,gBAAgB,EAAE,CAAC,uCAAqB,GAAG,eAAe;AAAA,QAC5D;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,IAAM,UAAU,OAAO,UAC5B,kBAAkB,OAAO,uBAAuB,CAAC;","names":["import_workflows"]}
1
+ {"version":3,"sources":["../src/workflows/control-plane/seed-system-data/seed-system-data.handler.ts"],"sourcesContent":["import { DynamoDBClient } from \"@aws-sdk/client-dynamodb\";\nimport { EventBridgeClient } from \"@aws-sdk/client-eventbridge\";\nimport {\n PLATFORM_ROLE_CONCEPTS,\n PLATFORM_ROLE_IDS,\n type PlatformRoleCode,\n} from \"@openhi/types\";\nimport {\n OPENHI_CONTROL_SOURCE,\n parseWorkflowEvent,\n publishWorkflowEvent,\n workflowDedupClient,\n type PlatformDeploymentCompletedV1Detail,\n type PublishResult,\n type WorkflowDedupClient,\n} from \"@openhi/workflows\";\nimport type { EventBridgeEvent } from \"aws-lambda\";\nimport {\n PlatformDeploymentCompletedV1,\n PlatformSystemDataSeededV1,\n SEED_SYSTEM_DATA_ACTOR_SYSTEM,\n SEED_SYSTEM_DATA_CONSUMER_NAME,\n SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR,\n} from \"./events\";\nimport type { OpenHiContext } from \"../../../data/openhi-context\";\nimport { createRoleOperation } from \"../../../data/operations/control/role/role-create-operation\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/seed-system-data/seed-system-data-handler.md\n *\n * EventBridge workflow handler invoked once per platform-deploy event on\n * the control event bus. Idempotently re-asserts the platform-singleton\n * control-plane records (today: the three canonical Roles; future:\n * additional system data slotted in as sibling steps under the same\n * dedup record).\n */\n\ntype SeedSystemDataEvent = EventBridgeEvent<\n \"platform.deployment-completed.v1\",\n unknown\n>;\n\n/**\n * Input shape the handler passes to the completion publisher. Mirrors\n * the wire shape of `PlatformSystemDataSeededV1Detail` plus the\n * envelope-correlation fields the publisher needs to chain the new\n * event onto the originating deploy event.\n */\nexport interface PublishSeededInput {\n /** Per-workflow payload — re-asserted record count + source event/stack ids. */\n readonly payload: {\n readonly sourceEventId: string;\n readonly sourceStackId: string;\n readonly seededRecordCount: number;\n };\n /**\n * Envelope context propagated from the originating\n * `platform.deployment-completed.v1` event so the chain is\n * observable in logs.\n */\n readonly envelope: {\n readonly correlationId: string;\n readonly causationId: string;\n };\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 SeedSystemDataDependencies {\n readonly dedupClient: WorkflowDedupClient;\n readonly seedRoles: (context: OpenHiContext) => Promise<void>;\n /**\n * Publishes the `platform.system-data-seeded.v1` completion event\n * onto the control event bus. Called after a successful seed and\n * before the handler returns — downstream consumers\n * (`seed-demo-data`) subscribe to this event instead of the raw\n * deploy-completion event so the dependency on system data is\n * enforced by a happens-before edge.\n */\n readonly publishSeeded: (input: PublishSeededInput) => Promise<PublishResult>;\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 * Lookup the stable record id for a given role code by walking the two\n * parallel generator-emitted constants. The `_IDS` / `_CONCEPTS` consts\n * are keyed by the same SCREAMING_SNAKE keys (the generator builds them\n * in lockstep), so a single key resolves both projections.\n */\nconst idForRoleCode = (code: PlatformRoleCode): string => {\n for (const key of Object.keys(PLATFORM_ROLE_IDS) as Array<\n keyof typeof PLATFORM_ROLE_IDS\n >) {\n if (PLATFORM_ROLE_CONCEPTS[key].code === code) {\n return PLATFORM_ROLE_IDS[key];\n }\n }\n // Unreachable while `_IDS` and `_CONCEPTS` are emitted from the same\n // CodeSystem: every code in `_CONCEPTS` has a matching id in `_IDS`.\n throw new Error(`No id mapping for role code \"${code}\".`);\n};\n\n/**\n * Re-assert the three canonical control-plane Role records. Stable ids\n * from `PLATFORM_ROLE_IDS` make re-runs after dedup-TTL expiry\n * idempotent: every put with the same id+sk lands on the same primary\n * key and produces the same end state.\n */\nconst seedSystemRoles = async (context: OpenHiContext): Promise<void> => {\n for (const concept of Object.values(PLATFORM_ROLE_CONCEPTS)) {\n const id = idForRoleCode(concept.code);\n await createRoleOperation({\n context,\n body: {\n id,\n resource: {\n resourceType: \"Role\",\n code: concept.code,\n display: concept.display,\n active: true,\n },\n },\n });\n }\n};\n\n/**\n * Test-visible orchestrator. The production `handler` calls this with\n * real dependencies; unit tests inject fakes.\n */\nexport const runSeedSystemData = async (\n event: SeedSystemDataEvent,\n deps: SeedSystemDataDependencies,\n): Promise<void> => {\n const parsed = parseWorkflowEvent(event, PlatformDeploymentCompletedV1);\n\n // Dedup is the single circuit-breaker between EventBridge's\n // at-least-once redelivery and the DynamoDB writes below. If\n // `recordIfAbsent` returns `recorded: false`, a prior invocation of\n // this (eventId, attempt) tuple already ran to completion — there is\n // nothing left to do, so we exit without re-iterating the catalog.\n const recordResult = await deps.dedupClient.recordIfAbsent({\n consumerName: SEED_SYSTEM_DATA_CONSUMER_NAME,\n eventId: parsed.dedupKey.eventId,\n attempt: parsed.dedupKey.attempt,\n });\n if (!recordResult.recorded) {\n return;\n }\n\n // Role records are platform-wide singletons (per ADR 2026-03-13-02 §4)\n // and live under the sentinel tenant/workspace coordinates the\n // `role-entity` declares (`TID#-#WID#-`). We pass an internal-system\n // context whose tenant/workspace fields are empty strings; the role\n // create operation reads only `date` and audit metadata.\n const lastUpdated = parsed.envelope.occurredAt;\n const context: OpenHiContext = {\n tenantId: \"\",\n workspaceId: \"\",\n date: lastUpdated,\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.seedRoles(context);\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_SYSTEM_DATA_CONSUMER_NAME,\n eventId: parsed.dedupKey.eventId,\n attempt: parsed.dedupKey.attempt,\n reason: errorMessage(err),\n });\n throw err;\n }\n\n // Seeding succeeded — publish `platform.system-data-seeded.v1` so\n // downstream consumers that depend on the platform-singleton\n // records existing (today: `seed-demo-data`) are triggered by a\n // happens-before edge rather than by EventBridge retry timing.\n //\n // A publish failure here does NOT roll back the seed — the records\n // are already in the data store and idempotent on re-fire. We log\n // and re-throw so EventBridge retries the whole event; on retry,\n // dedup wins (no-op) and the publish path re-runs.\n const sourcePayload = parsed.envelope\n .payload as PlatformDeploymentCompletedV1Detail;\n await deps.publishSeeded({\n payload: {\n sourceEventId: parsed.envelope.eventId,\n sourceStackId: sourcePayload.stackId,\n seededRecordCount: Object.keys(PLATFORM_ROLE_IDS).length,\n },\n envelope: {\n correlationId: parsed.envelope.correlationId,\n causationId: parsed.envelope.eventId,\n },\n });\n};\n\nconst productionDependencies = (): SeedSystemDataDependencies => {\n const dynamodb = new DynamoDBClient({});\n const eventBridge = new EventBridgeClient({});\n return {\n dedupClient: workflowDedupClient(dynamodb),\n seedRoles: seedSystemRoles,\n publishSeeded: async (input) => {\n const controlBusName = process.env[SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR];\n if (!controlBusName) {\n throw new Error(\n `seed-system-data: ${SEED_SYSTEM_DATA_CONTROL_BUS_ENV_VAR} env var is required to publish system-data-seeded events.`,\n );\n }\n return publishWorkflowEvent(\n eventBridge,\n PlatformSystemDataSeededV1,\n input.payload,\n {\n actor: { system: SEED_SYSTEM_DATA_ACTOR_SYSTEM },\n correlationId: input.envelope.correlationId,\n causationId: input.envelope.causationId,\n },\n {\n busNameByPlane: { [OPENHI_CONTROL_SOURCE]: controlBusName },\n },\n );\n },\n };\n};\n\nexport const handler = async (event: SeedSystemDataEvent): Promise<void> =>\n runSeedSystemData(event, productionDependencies());\n"],"mappings":"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","names":["import_workflows"]}
package/package.json CHANGED
@@ -55,8 +55,8 @@
55
55
  "pg": "^8.20.0",
56
56
  "type-fest": "^4",
57
57
  "ulid": "^3.0.2",
58
- "@openhi/types": "0.0.0",
59
58
  "@openhi/config": "0.0.0",
59
+ "@openhi/types": "0.0.0",
60
60
  "@openhi/workflows": "0.0.0"
61
61
  },
62
62
  "devEngines": {
@@ -71,7 +71,7 @@
71
71
  "publishConfig": {
72
72
  "access": "public"
73
73
  },
74
- "version": "0.0.106",
74
+ "version": "0.0.107",
75
75
  "types": "lib/index.d.ts",
76
76
  "//": "~~ Generated by projen. To modify, edit .projenrc.js and run \"pnpm exec projen\".",
77
77
  "scripts": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/workflows/control-plane/seed-demo-data/seed-demo-data.handler.ts","../src/workflows/control-plane/seed-demo-data/events.ts"],"sourcesContent":["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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAkBA,IAAAA,oBAIO;AAtBP,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,sBAAsB;AAC/B;AAAA,EACE,2BAAAC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAGK;;;ACbP,uBAA2C;AAJ3C;AAAA,EACE;AAAA,OAEK;AAcA,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,wBAAwB,YAAY;AAC9C;AAkBO,IAAM,mBAAmB,CAAC,WAA2B,WAAW,MAAM;AAEtE,IAAM,yBAAyB,CAAC,aACrC,aAAa,QAAQ;AAEhB,IAAM,4BAA4B,CACvC,UACA,gBACW,OAAO,QAAQ,iBAAiB,WAAW;AAEjD,IAAM,6BAA6B,CACxC,UACA,iBACW,OAAO,QAAQ,kBAAkB,YAAY;AAEnD,IAAM,iCAAiC,CAC5C,UACA,qBACW,OAAO,QAAQ,sBAAsB,gBAAgB;AAG3D,IAAM,uBAAuB,CAAC,WACnC,WAAW,MAAM;AAMZ,IAAM,wBAAwB,MAA6B;AAChE,QAAM,OAAiB,CAAC;AACxB,aAAW,QAAQ,mBAAmB;AACpC,SAAK,KAAK,uBAAuB,KAAK,QAAQ,CAAC;AAC/C,eAAW,aAAa,KAAK,YAAY;AACvC,WAAK,KAAK,0BAA0B,KAAK,UAAU,UAAU,EAAE,CAAC;AAAA,IAClE;AAAA,EACF;AACA,SAAO;AACT;AAeO,IAAM,2BAA2B,CACtC,aAC0B;AAC1B,QAAM,OAAiB,CAAC;AACxB,aAAW,QAAQ,UAAU;AAC3B,SAAK,KAAK,qBAAqB,KAAK,EAAE,CAAC;AACvC,eAAW,QAAQ,mBAAmB;AACpC,WAAK;AAAA,QACH;AAAA,UACE,KAAK;AAAA,UACL,iBAAiB,KAAK,IAAI,KAAK,QAAQ;AAAA,QACzC;AAAA,MACF;AACA,iBAAW,YAAY,yBAAyB,MAAM,KAAK,QAAQ,GAAG;AACpE,aAAK;AAAA,UACH;AAAA,YACE,KAAK;AAAA,YACL,qBAAqB,KAAK,IAAI,KAAK,UAAU,QAAQ;AAAA,UACvD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,SAAK;AAAA,MACH;AAAA,QACE;AAAA,QACA;AAAA,UACE,KAAK;AAAA,UACL;AAAA,UACA,wBAAwB;AAAA,QAC1B;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;ADzSO,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,sBAAsB,GAEjD;AACD,QAAI,4BAA4B,GAAG,EAAE,SAAS,MAAM;AAClD,aAAO,uBAAuB,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,sBAAsB,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,UAAU,eAAe,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,mBAAmBC,yBAAwB;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,SAAS,WAAW,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,8BAA8B,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,uBAAuB;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,4BAA4B;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,yBAAyB;AAG1C,gBAAM,MAAM,MAAM,OAAO;AAAA,YACvB,IAAI,oBAAoB;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,eAAe,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":["import_workflows","CONTROL_PLANE_ROLE_CODE","CONTROL_PLANE_ROLE_CODE"]}