@openhi/constructs 0.0.159 → 0.0.161

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/lib/{chunk-HQ67J7BP.mjs → chunk-5S6VFBLT.mjs} +12 -70
  2. package/lib/chunk-5S6VFBLT.mjs.map +1 -0
  3. package/lib/{chunk-MVQWAIMC.mjs → chunk-6BB4CRSS.mjs} +3 -312
  4. package/lib/chunk-6BB4CRSS.mjs.map +1 -0
  5. package/lib/{chunk-WPCBVDFZ.mjs → chunk-76UM2LQ5.mjs} +2 -2
  6. package/lib/chunk-7TRO2STL.mjs +4616 -0
  7. package/lib/chunk-7TRO2STL.mjs.map +1 -0
  8. package/lib/chunk-BUAYVN3C.mjs +87 -0
  9. package/lib/chunk-BUAYVN3C.mjs.map +1 -0
  10. package/lib/{chunk-23PUSHBV.mjs → chunk-D2Y6DDOC.mjs} +2 -2
  11. package/lib/chunk-DWSWCUZR.mjs +123 -0
  12. package/lib/chunk-DWSWCUZR.mjs.map +1 -0
  13. package/lib/{chunk-VZCPGQXA.mjs → chunk-EUIP2U5F.mjs} +69 -1
  14. package/lib/{chunk-VZCPGQXA.mjs.map → chunk-EUIP2U5F.mjs.map} +1 -1
  15. package/lib/chunk-GJTPXJKD.mjs +46 -0
  16. package/lib/chunk-GJTPXJKD.mjs.map +1 -0
  17. package/lib/chunk-I6LUPJUY.mjs +61 -0
  18. package/lib/chunk-I6LUPJUY.mjs.map +1 -0
  19. package/lib/{chunk-KR2Y2CVQ.mjs → chunk-KA3OMP3X.mjs} +2 -2
  20. package/lib/{chunk-ZM4GDHHC.mjs → chunk-KMEWULMX.mjs} +51 -3
  21. package/lib/chunk-KMEWULMX.mjs.map +1 -0
  22. package/lib/chunk-LKKLO66E.mjs +25 -0
  23. package/lib/chunk-LKKLO66E.mjs.map +1 -0
  24. package/lib/{chunk-CFJDATDK.mjs → chunk-MLFMW5IF.mjs} +43 -9
  25. package/lib/chunk-MLFMW5IF.mjs.map +1 -0
  26. package/lib/chunk-O5VQWB6U.mjs +315 -0
  27. package/lib/chunk-O5VQWB6U.mjs.map +1 -0
  28. package/lib/{chunk-7BQHLC7U.mjs → chunk-P3CTZWC2.mjs} +8 -40
  29. package/lib/chunk-P3CTZWC2.mjs.map +1 -0
  30. package/lib/chunk-P3NFCKTZ.mjs +502 -0
  31. package/lib/chunk-P3NFCKTZ.mjs.map +1 -0
  32. package/lib/{chunk-M7Y3BOQW.mjs → chunk-Q3MKITPY.mjs} +5 -5
  33. package/lib/chunk-Q64MOYJ7.mjs +218 -0
  34. package/lib/chunk-Q64MOYJ7.mjs.map +1 -0
  35. package/lib/chunk-RQKJNMX5.mjs +89 -0
  36. package/lib/chunk-RQKJNMX5.mjs.map +1 -0
  37. package/lib/{chunk-ZWSGM6PZ.mjs → chunk-SD7J3N3C.mjs} +2 -2
  38. package/lib/{chunk-7RZHFI77.mjs → chunk-VESULYQQ.mjs} +2 -2
  39. package/lib/{chunk-AOSEKL7U.mjs → chunk-WOTU36P3.mjs} +6 -103
  40. package/lib/chunk-WOTU36P3.mjs.map +1 -0
  41. package/lib/{chunk-X5E4YJGZ.mjs → chunk-YPTJJ35S.mjs} +2 -2
  42. package/lib/counter-apply-operation-DZM3MIDm.d.mts +63 -0
  43. package/lib/counter-apply-operation-DZM3MIDm.d.ts +63 -0
  44. package/lib/counter-maintenance.handler.d.mts +38 -0
  45. package/lib/counter-maintenance.handler.d.ts +38 -0
  46. package/lib/counter-maintenance.handler.js +2885 -0
  47. package/lib/counter-maintenance.handler.js.map +1 -0
  48. package/lib/counter-maintenance.handler.mjs +180 -0
  49. package/lib/counter-maintenance.handler.mjs.map +1 -0
  50. package/lib/counter-reconciliation.handler.d.mts +116 -0
  51. package/lib/counter-reconciliation.handler.d.ts +116 -0
  52. package/lib/counter-reconciliation.handler.js +3324 -0
  53. package/lib/counter-reconciliation.handler.js.map +1 -0
  54. package/lib/counter-reconciliation.handler.mjs +295 -0
  55. package/lib/counter-reconciliation.handler.mjs.map +1 -0
  56. package/lib/data-store-postgres-replication.handler.js +50 -2
  57. package/lib/data-store-postgres-replication.handler.js.map +1 -1
  58. package/lib/data-store-postgres-replication.handler.mjs +2 -2
  59. package/lib/delete-chunk.handler.js +118 -2
  60. package/lib/delete-chunk.handler.js.map +1 -1
  61. package/lib/delete-chunk.handler.mjs +3 -3
  62. package/lib/{events-DTgo2dcW.d.mts → events-TG654e7L.d.mts} +68 -19
  63. package/lib/{events-DTgo2dcW.d.ts → events-TG654e7L.d.ts} +68 -19
  64. package/lib/finalize.handler.js +50 -2
  65. package/lib/finalize.handler.js.map +1 -1
  66. package/lib/finalize.handler.mjs +4 -4
  67. package/lib/firehose-archive-transform.handler.js +50 -2
  68. package/lib/firehose-archive-transform.handler.js.map +1 -1
  69. package/lib/firehose-archive-transform.handler.mjs +2 -2
  70. package/lib/index.d.mts +1283 -4
  71. package/lib/index.d.ts +1389 -24
  72. package/lib/index.js +4113 -320
  73. package/lib/index.js.map +1 -1
  74. package/lib/index.mjs +602 -195
  75. package/lib/index.mjs.map +1 -1
  76. package/lib/list-chunks.handler.js +118 -2
  77. package/lib/list-chunks.handler.js.map +1 -1
  78. package/lib/list-chunks.handler.mjs +3 -3
  79. package/lib/platform-deploy-bridge.handler.js +50 -2
  80. package/lib/platform-deploy-bridge.handler.js.map +1 -1
  81. package/lib/platform-deploy-bridge.handler.mjs +1 -1
  82. package/lib/pre-token-generation.handler.js +68 -0
  83. package/lib/pre-token-generation.handler.js.map +1 -1
  84. package/lib/pre-token-generation.handler.mjs +9 -5
  85. package/lib/pre-token-generation.handler.mjs.map +1 -1
  86. package/lib/provision-default-workspace.handler.js +887 -4
  87. package/lib/provision-default-workspace.handler.js.map +1 -1
  88. package/lib/provision-default-workspace.handler.mjs +14 -9
  89. package/lib/provision-default-workspace.handler.mjs.map +1 -1
  90. package/lib/rename-finalize.handler.js +50 -2
  91. package/lib/rename-finalize.handler.js.map +1 -1
  92. package/lib/rename-finalize.handler.mjs +2 -2
  93. package/lib/rename-list-targets.handler.js +118 -2
  94. package/lib/rename-list-targets.handler.js.map +1 -1
  95. package/lib/rename-list-targets.handler.mjs +11 -9
  96. package/lib/rename-list-targets.handler.mjs.map +1 -1
  97. package/lib/rename-rewrite-chunk.handler.js +68 -0
  98. package/lib/rename-rewrite-chunk.handler.js.map +1 -1
  99. package/lib/rename-rewrite-chunk.handler.mjs +2 -2
  100. package/lib/rest-api-lambda.handler.js +1454 -251
  101. package/lib/rest-api-lambda.handler.js.map +1 -1
  102. package/lib/rest-api-lambda.handler.mjs +673 -821
  103. package/lib/rest-api-lambda.handler.mjs.map +1 -1
  104. package/lib/seed-demo-data.handler.d.mts +1 -1
  105. package/lib/seed-demo-data.handler.d.ts +1 -1
  106. package/lib/seed-demo-data.handler.js +4004 -201
  107. package/lib/seed-demo-data.handler.js.map +1 -1
  108. package/lib/seed-demo-data.handler.mjs +10 -7
  109. package/lib/seed-system-data.handler.js +118 -2
  110. package/lib/seed-system-data.handler.js.map +1 -1
  111. package/lib/seed-system-data.handler.mjs +5 -5
  112. package/package.json +1 -1
  113. package/lib/chunk-7BQHLC7U.mjs.map +0 -1
  114. package/lib/chunk-AOSEKL7U.mjs.map +0 -1
  115. package/lib/chunk-BQMJSDOD.mjs +0 -1136
  116. package/lib/chunk-BQMJSDOD.mjs.map +0 -1
  117. package/lib/chunk-CFJDATDK.mjs.map +0 -1
  118. package/lib/chunk-E6MCKJVS.mjs +0 -212
  119. package/lib/chunk-E6MCKJVS.mjs.map +0 -1
  120. package/lib/chunk-HQ67J7BP.mjs.map +0 -1
  121. package/lib/chunk-MVQWAIMC.mjs.map +0 -1
  122. package/lib/chunk-ZM4GDHHC.mjs.map +0 -1
  123. /package/lib/{chunk-WPCBVDFZ.mjs.map → chunk-76UM2LQ5.mjs.map} +0 -0
  124. /package/lib/{chunk-23PUSHBV.mjs.map → chunk-D2Y6DDOC.mjs.map} +0 -0
  125. /package/lib/{chunk-KR2Y2CVQ.mjs.map → chunk-KA3OMP3X.mjs.map} +0 -0
  126. /package/lib/{chunk-M7Y3BOQW.mjs.map → chunk-Q3MKITPY.mjs.map} +0 -0
  127. /package/lib/{chunk-ZWSGM6PZ.mjs.map → chunk-SD7J3N3C.mjs.map} +0 -0
  128. /package/lib/{chunk-7RZHFI77.mjs.map → chunk-VESULYQQ.mjs.map} +0 -0
  129. /package/lib/{chunk-X5E4YJGZ.mjs.map → chunk-YPTJJ35S.mjs.map} +0 -0
@@ -0,0 +1,46 @@
1
+ import {
2
+ batchGetWithRetry,
3
+ dispatchListMode
4
+ } from "./chunk-O5VQWB6U.mjs";
5
+ import {
6
+ SHARD_COUNT,
7
+ getDynamoControlService
8
+ } from "./chunk-EUIP2U5F.mjs";
9
+
10
+ // src/data/operations/control/membership/membership-list-operation.ts
11
+ var SK = "CURRENT";
12
+ async function listMembershipsOperation(params) {
13
+ const { context, tableName, mode = "full" } = params;
14
+ const tenantId = context.tenantId;
15
+ const service = getDynamoControlService(tableName);
16
+ const shardResults = await Promise.all(
17
+ Array.from(
18
+ { length: SHARD_COUNT },
19
+ (_, shard) => service.entities.membership.query.gsi1({ tenantId, gsi1Shard: String(shard) }).go()
20
+ )
21
+ );
22
+ return dispatchListMode(mode, shardResults, {
23
+ hydrate: (orderedIds) => batchGetWithRetry(
24
+ service.entities.membership,
25
+ orderedIds.map((id) => ({ tenantId, id, sk: SK }))
26
+ ),
27
+ getId: (item) => item.id,
28
+ buildEntry: (id, item) => ({
29
+ id,
30
+ resource: {
31
+ resourceType: "Membership",
32
+ id,
33
+ ...JSON.parse(item.resource)
34
+ }
35
+ }),
36
+ buildSummaryEntry: (id, parsed) => ({
37
+ id,
38
+ resource: { resourceType: "Membership", id, ...parsed }
39
+ })
40
+ });
41
+ }
42
+
43
+ export {
44
+ listMembershipsOperation
45
+ };
46
+ //# sourceMappingURL=chunk-GJTPXJKD.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/data/operations/control/membership/membership-list-operation.ts"],"sourcesContent":["import { getDynamoControlService } from \"../../../dynamo/dynamo-control-service\";\nimport { SHARD_COUNT } from \"../../../dynamo/shard\";\nimport { OpenHiContext } from \"../../../openhi-context\";\nimport {\n batchGetWithRetry,\n dispatchListMode,\n type ListOperationMode,\n} from \"../../data-operations-common\";\n\nconst SK = \"CURRENT\";\n\nexport interface MembershipListParams {\n context: OpenHiContext;\n tableName?: string;\n /** #853: defaults to `\"full\"`. `\"summary\"` skips BatchGet, `\"count\"` returns total only. */\n mode?: ListOperationMode;\n}\n\nexport interface MembershipListResult {\n entries: Array<{\n id: string;\n resource: { resourceType: string; id: string; [key: string]: unknown };\n }>;\n total: number;\n}\n\n/**\n * Lists Memberships for the context tenant via GSI1 (sharded). See `dispatchListMode` for\n * the mode contract (#853).\n */\nexport async function listMembershipsOperation(\n params: MembershipListParams,\n): Promise<MembershipListResult> {\n const { context, tableName, mode = \"full\" } = params;\n const tenantId = context.tenantId;\n const service = getDynamoControlService(tableName);\n\n const shardResults = await Promise.all(\n Array.from({ length: SHARD_COUNT }, (_, shard) =>\n service.entities.membership.query\n .gsi1({ tenantId, gsi1Shard: String(shard) })\n .go(),\n ),\n );\n\n return dispatchListMode<\n { id: string; resource: string },\n MembershipListResult[\"entries\"][number]\n >(mode, shardResults, {\n hydrate: (orderedIds) =>\n batchGetWithRetry(\n service.entities.membership,\n orderedIds.map((id) => ({ tenantId, id, sk: SK })),\n ) as Promise<Array<{ id: string; resource: string }>>,\n getId: (item) => item.id,\n buildEntry: (id, item) => ({\n id,\n resource: {\n resourceType: \"Membership\",\n id,\n ...(JSON.parse(item.resource) as Record<string, unknown>),\n },\n }),\n buildSummaryEntry: (id, parsed) => ({\n id,\n resource: { resourceType: \"Membership\", id, ...parsed },\n }),\n });\n}\n"],"mappings":";;;;;;;;;;AASA,IAAM,KAAK;AAqBX,eAAsB,yBACpB,QAC+B;AAC/B,QAAM,EAAE,SAAS,WAAW,OAAO,OAAO,IAAI;AAC9C,QAAM,WAAW,QAAQ;AACzB,QAAM,UAAU,wBAAwB,SAAS;AAEjD,QAAM,eAAe,MAAM,QAAQ;AAAA,IACjC,MAAM;AAAA,MAAK,EAAE,QAAQ,YAAY;AAAA,MAAG,CAAC,GAAG,UACtC,QAAQ,SAAS,WAAW,MACzB,KAAK,EAAE,UAAU,WAAW,OAAO,KAAK,EAAE,CAAC,EAC3C,GAAG;AAAA,IACR;AAAA,EACF;AAEA,SAAO,iBAGL,MAAM,cAAc;AAAA,IACpB,SAAS,CAAC,eACR;AAAA,MACE,QAAQ,SAAS;AAAA,MACjB,WAAW,IAAI,CAAC,QAAQ,EAAE,UAAU,IAAI,IAAI,GAAG,EAAE;AAAA,IACnD;AAAA,IACF,OAAO,CAAC,SAAS,KAAK;AAAA,IACtB,YAAY,CAAC,IAAI,UAAU;AAAA,MACzB;AAAA,MACA,UAAU;AAAA,QACR,cAAc;AAAA,QACd;AAAA,QACA,GAAI,KAAK,MAAM,KAAK,QAAQ;AAAA,MAC9B;AAAA,IACF;AAAA,IACA,mBAAmB,CAAC,IAAI,YAAY;AAAA,MAClC;AAAA,MACA,UAAU,EAAE,cAAc,cAAc,IAAI,GAAG,OAAO;AAAA,IACxD;AAAA,EACF,CAAC;AACH;","names":[]}
@@ -0,0 +1,61 @@
1
+ // src/data/operations/control/membership/membership-user-projection.ts
2
+ import { normalizeLabel } from "@openhi/types";
3
+ var MISSING_NAME_SENTINEL = "-";
4
+ function buildMembershipUserProjectionSkTenantLane(params) {
5
+ const normalizedTenantName = typeof params.denormalizedTenantName === "string" && params.denormalizedTenantName.length > 0 ? normalizeLabel(params.denormalizedTenantName) : MISSING_NAME_SENTINEL;
6
+ return `MEMBERSHIP#TENANT#${normalizedTenantName}#TID#${params.tenantId}#${params.membershipId}`;
7
+ }
8
+ function buildMembershipUserProjectionSkWorkspaceLane(params) {
9
+ const normalizedWorkspaceName = typeof params.denormalizedWorkspaceName === "string" && params.denormalizedWorkspaceName.length > 0 ? normalizeLabel(params.denormalizedWorkspaceName) : MISSING_NAME_SENTINEL;
10
+ return `MEMBERSHIP#WORKSPACE#TID#${params.tenantId}#${normalizedWorkspaceName}#WID#${params.workspaceId}#${params.membershipId}`;
11
+ }
12
+ function buildMembershipUserProjectionItem(input) {
13
+ if (!input.userId || input.userId.length === 0) {
14
+ return void 0;
15
+ }
16
+ const hasWorkspace = typeof input.workspaceId === "string" && input.workspaceId.length > 0;
17
+ const sk = hasWorkspace ? buildMembershipUserProjectionSkWorkspaceLane({
18
+ tenantId: input.tenantId,
19
+ workspaceId: input.workspaceId,
20
+ membershipId: input.membershipId,
21
+ denormalizedWorkspaceName: input.denormalizedWorkspaceName
22
+ }) : buildMembershipUserProjectionSkTenantLane({
23
+ tenantId: input.tenantId,
24
+ membershipId: input.membershipId,
25
+ denormalizedTenantName: input.denormalizedTenantName
26
+ });
27
+ return {
28
+ userId: input.userId,
29
+ sk,
30
+ tenantId: input.tenantId,
31
+ workspaceId: hasWorkspace ? input.workspaceId : void 0,
32
+ membershipId: input.membershipId,
33
+ summary: input.summary,
34
+ vid: input.vid,
35
+ lastUpdated: input.lastUpdated,
36
+ denormalizedTenantName: input.denormalizedTenantName,
37
+ denormalizedUserName: input.denormalizedUserName,
38
+ denormalizedWorkspaceName: hasWorkspace ? input.denormalizedWorkspaceName : void 0
39
+ };
40
+ }
41
+ function extractReferenceSlug(resource, fieldName) {
42
+ const field = resource[fieldName];
43
+ if (!field || typeof field !== "object") {
44
+ return void 0;
45
+ }
46
+ const reference = field.reference;
47
+ if (typeof reference !== "string" || reference.length === 0) {
48
+ return void 0;
49
+ }
50
+ const slash = reference.lastIndexOf("/");
51
+ const tail = slash >= 0 ? reference.slice(slash + 1) : reference;
52
+ return tail.length > 0 ? tail : void 0;
53
+ }
54
+
55
+ export {
56
+ buildMembershipUserProjectionSkTenantLane,
57
+ buildMembershipUserProjectionSkWorkspaceLane,
58
+ buildMembershipUserProjectionItem,
59
+ extractReferenceSlug
60
+ };
61
+ //# sourceMappingURL=chunk-I6LUPJUY.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/data/operations/control/membership/membership-user-projection.ts"],"sourcesContent":["/**\n * Membership user-projection composer.\n *\n * Owns the SK grammar for ADR-018 patterns #3 and #4 and assembles the\n * projection-row payloads consumed by the membership create / update /\n * delete operations. The {@link MembershipUserProjectionEntity} stores\n * the SK verbatim — the grammar lives here so the operations layer is\n * the single source of truth for projection-row shape (per\n * `.claude/rules/data-layer-layout.md`).\n *\n * SK grammar:\n *\n * - **Pattern #3** (tenant sub-lane, `workspaceId` absent):\n * `MEMBERSHIP#TENANT#<normalizedTenantName>#TID#<tenantId>#<membershipId>`\n * - **Pattern #4** (workspace sub-lane, `workspaceId` set):\n * `MEMBERSHIP#WORKSPACE#TID#<tenantId>#<normalizedWorkspaceName>#WID#<workspaceId>#<membershipId>`\n *\n * Both patterns share the user partition `PK = USER#ID#<userId>` so\n * `Query(PK = USER#ID#<userId>, SK begins_with 'MEMBERSHIP#')` returns\n * both lanes interleaved.\n *\n * @see ADR-018 § Access Pattern Coverage (patterns #3 and #4)\n * @see .state/adr-018-implementation-guide.md § 1 (SK grammar) and § 2 (attribute set)\n */\n\nimport { normalizeLabel } from \"@openhi/types\";\n\n/**\n * Sentinel rendered into the SK when the source display name is missing\n * or empty. Keeps the SK shape stable so a `begins_with` prefix query\n * still matches the row; the rename-cascade pipeline (TR-023) will\n * rewrite the SK once the carrier display name lands. Matches the\n * `gsi1skAttribute` defensive posture — a missing source field never\n * produces a malformed key.\n */\nconst MISSING_NAME_SENTINEL = \"-\";\n\n/** Inputs to compose a Membership user-projection row. */\nexport interface MembershipUserProjectionInput {\n readonly tenantId: string;\n readonly userId: string;\n readonly workspaceId?: string;\n readonly membershipId: string;\n readonly summary: string;\n readonly vid: string;\n readonly lastUpdated: string;\n readonly denormalizedTenantName?: string;\n readonly denormalizedUserName?: string;\n readonly denormalizedWorkspaceName?: string;\n}\n\n/** A projection-row payload ready for `multi-write` consumption. */\nexport interface MembershipUserProjectionItem {\n readonly userId: string;\n readonly sk: string;\n readonly tenantId: string;\n readonly workspaceId?: string;\n readonly membershipId: string;\n readonly summary: string;\n readonly vid: string;\n readonly lastUpdated: string;\n readonly denormalizedTenantName?: string;\n readonly denormalizedUserName?: string;\n readonly denormalizedWorkspaceName?: string;\n}\n\n/**\n * Compose the SK for ADR-018 pattern #3 (tenant sub-lane). The\n * `<normalizedTenantName>` segment sorts memberships alphabetically by\n * tenant name within the user's partition. Missing `denormalizedTenantName`\n * falls back to {@link MISSING_NAME_SENTINEL} so the SK shape stays valid\n * pre-rename-cascade.\n */\nexport function buildMembershipUserProjectionSkTenantLane(params: {\n readonly tenantId: string;\n readonly membershipId: string;\n readonly denormalizedTenantName?: string;\n}): string {\n const normalizedTenantName =\n typeof params.denormalizedTenantName === \"string\" &&\n params.denormalizedTenantName.length > 0\n ? normalizeLabel(params.denormalizedTenantName)\n : MISSING_NAME_SENTINEL;\n return `MEMBERSHIP#TENANT#${normalizedTenantName}#TID#${params.tenantId}#${params.membershipId}`;\n}\n\n/**\n * Compose the SK for ADR-018 pattern #4 (workspace sub-lane). `tenantId`\n * appears before `<normalizedWorkspaceName>` so a\n * `begins_with('MEMBERSHIP#WORKSPACE#TID#<tenantId>#')` query filters\n * by one tenant. Missing `denormalizedWorkspaceName` falls back to\n * {@link MISSING_NAME_SENTINEL}.\n */\nexport function buildMembershipUserProjectionSkWorkspaceLane(params: {\n readonly tenantId: string;\n readonly workspaceId: string;\n readonly membershipId: string;\n readonly denormalizedWorkspaceName?: string;\n}): string {\n const normalizedWorkspaceName =\n typeof params.denormalizedWorkspaceName === \"string\" &&\n params.denormalizedWorkspaceName.length > 0\n ? normalizeLabel(params.denormalizedWorkspaceName)\n : MISSING_NAME_SENTINEL;\n return `MEMBERSHIP#WORKSPACE#TID#${params.tenantId}#${normalizedWorkspaceName}#WID#${params.workspaceId}#${params.membershipId}`;\n}\n\n/**\n * Builds the projection item for the access lane implied by the input.\n * Pattern #3 when `workspaceId` is absent or empty; pattern #4 otherwise.\n * Returns `undefined` when `userId` is missing — a Membership without a\n * linked user cannot project onto the user partition.\n */\nexport function buildMembershipUserProjectionItem(\n input: MembershipUserProjectionInput,\n): MembershipUserProjectionItem | undefined {\n if (!input.userId || input.userId.length === 0) {\n return undefined;\n }\n const hasWorkspace =\n typeof input.workspaceId === \"string\" && input.workspaceId.length > 0;\n const sk = hasWorkspace\n ? buildMembershipUserProjectionSkWorkspaceLane({\n tenantId: input.tenantId,\n workspaceId: input.workspaceId as string,\n membershipId: input.membershipId,\n denormalizedWorkspaceName: input.denormalizedWorkspaceName,\n })\n : buildMembershipUserProjectionSkTenantLane({\n tenantId: input.tenantId,\n membershipId: input.membershipId,\n denormalizedTenantName: input.denormalizedTenantName,\n });\n return {\n userId: input.userId,\n sk,\n tenantId: input.tenantId,\n workspaceId: hasWorkspace ? input.workspaceId : undefined,\n membershipId: input.membershipId,\n summary: input.summary,\n vid: input.vid,\n lastUpdated: input.lastUpdated,\n denormalizedTenantName: input.denormalizedTenantName,\n denormalizedUserName: input.denormalizedUserName,\n denormalizedWorkspaceName: hasWorkspace\n ? input.denormalizedWorkspaceName\n : undefined,\n };\n}\n\n/**\n * Extracts a FHIR `Reference` slug — the segment after the final `/`.\n * Returns `undefined` when the reference is missing or malformed so\n * callers fall back gracefully (matches the defensive posture in\n * `extractRoleId` / `extractDenormalizedReferenceDisplay`).\n */\nexport function extractReferenceSlug(\n resource: Record<string, unknown>,\n fieldName: string,\n): string | undefined {\n const field = resource[fieldName];\n if (!field || typeof field !== \"object\") {\n return undefined;\n }\n const reference = (field as { reference?: unknown }).reference;\n if (typeof reference !== \"string\" || reference.length === 0) {\n return undefined;\n }\n const slash = reference.lastIndexOf(\"/\");\n const tail = slash >= 0 ? reference.slice(slash + 1) : reference;\n return tail.length > 0 ? tail : undefined;\n}\n"],"mappings":";AAyBA,SAAS,sBAAsB;AAU/B,IAAM,wBAAwB;AAsCvB,SAAS,0CAA0C,QAI/C;AACT,QAAM,uBACJ,OAAO,OAAO,2BAA2B,YACzC,OAAO,uBAAuB,SAAS,IACnC,eAAe,OAAO,sBAAsB,IAC5C;AACN,SAAO,qBAAqB,oBAAoB,QAAQ,OAAO,QAAQ,IAAI,OAAO,YAAY;AAChG;AASO,SAAS,6CAA6C,QAKlD;AACT,QAAM,0BACJ,OAAO,OAAO,8BAA8B,YAC5C,OAAO,0BAA0B,SAAS,IACtC,eAAe,OAAO,yBAAyB,IAC/C;AACN,SAAO,4BAA4B,OAAO,QAAQ,IAAI,uBAAuB,QAAQ,OAAO,WAAW,IAAI,OAAO,YAAY;AAChI;AAQO,SAAS,kCACd,OAC0C;AAC1C,MAAI,CAAC,MAAM,UAAU,MAAM,OAAO,WAAW,GAAG;AAC9C,WAAO;AAAA,EACT;AACA,QAAM,eACJ,OAAO,MAAM,gBAAgB,YAAY,MAAM,YAAY,SAAS;AACtE,QAAM,KAAK,eACP,6CAA6C;AAAA,IAC3C,UAAU,MAAM;AAAA,IAChB,aAAa,MAAM;AAAA,IACnB,cAAc,MAAM;AAAA,IACpB,2BAA2B,MAAM;AAAA,EACnC,CAAC,IACD,0CAA0C;AAAA,IACxC,UAAU,MAAM;AAAA,IAChB,cAAc,MAAM;AAAA,IACpB,wBAAwB,MAAM;AAAA,EAChC,CAAC;AACL,SAAO;AAAA,IACL,QAAQ,MAAM;AAAA,IACd;AAAA,IACA,UAAU,MAAM;AAAA,IAChB,aAAa,eAAe,MAAM,cAAc;AAAA,IAChD,cAAc,MAAM;AAAA,IACpB,SAAS,MAAM;AAAA,IACf,KAAK,MAAM;AAAA,IACX,aAAa,MAAM;AAAA,IACnB,wBAAwB,MAAM;AAAA,IAC9B,sBAAsB,MAAM;AAAA,IAC5B,2BAA2B,eACvB,MAAM,4BACN;AAAA,EACN;AACF;AAQO,SAAS,qBACd,UACA,WACoB;AACpB,QAAM,QAAQ,SAAS,SAAS;AAChC,MAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,WAAO;AAAA,EACT;AACA,QAAM,YAAa,MAAkC;AACrD,MAAI,OAAO,cAAc,YAAY,UAAU,WAAW,GAAG;AAC3D,WAAO;AAAA,EACT;AACA,QAAM,QAAQ,UAAU,YAAY,GAAG;AACvC,QAAM,OAAO,SAAS,IAAI,UAAU,MAAM,QAAQ,CAAC,IAAI;AACvD,SAAO,KAAK,SAAS,IAAI,OAAO;AAClC;","names":[]}
@@ -6,7 +6,7 @@ import {
6
6
  } from "./chunk-ZXPA6W3G.mjs";
7
7
  import {
8
8
  require_lib
9
- } from "./chunk-ZM4GDHHC.mjs";
9
+ } from "./chunk-KMEWULMX.mjs";
10
10
  import {
11
11
  __toESM
12
12
  } from "./chunk-LZOMFHX3.mjs";
@@ -321,4 +321,4 @@ export {
321
321
  parseCurrentResourceKeys,
322
322
  handler
323
323
  };
324
- //# sourceMappingURL=chunk-KR2Y2CVQ.mjs.map
324
+ //# sourceMappingURL=chunk-KA3OMP3X.mjs.map
@@ -112,7 +112,7 @@ var require_control_plane = __commonJS({
112
112
  "../workflows/lib/detail-types/control-plane.js"(exports) {
113
113
  "use strict";
114
114
  Object.defineProperty(exports, "__esModule", { value: true });
115
- exports.ControlPlaneRenameFailedV1 = exports.ControlPlaneRenameCompleteV1 = exports.ControlPlaneRenameV1 = exports.RENAMABLE_ENTITY_TYPE = exports.ControlPlaneOwningDeleteFailedV1 = exports.ControlPlaneOwningDeleteCompleteV1 = exports.ControlPlaneOwningDeleteV1 = exports.OWNING_ENTITY_TYPE = void 0;
115
+ exports.ControlPlaneWorkspaceDeletedV1 = exports.ControlPlaneWorkspaceCreatedV1 = exports.ControlPlaneRoleAssignmentDeletedV1 = exports.ControlPlaneRoleAssignmentCreatedV1 = exports.ControlPlaneMembershipDeletedV1 = exports.ControlPlaneMembershipCreatedV1 = exports.ControlPlaneRenameFailedV1 = exports.ControlPlaneRenameCompleteV1 = exports.ControlPlaneRenameV1 = exports.RENAMABLE_ENTITY_TYPE = exports.ControlPlaneOwningDeleteFailedV1 = exports.ControlPlaneOwningDeleteCompleteV1 = exports.ControlPlaneOwningDeleteV1 = exports.OWNING_ENTITY_TYPE = void 0;
116
116
  var sources_1 = require_sources();
117
117
  var registry_1 = require_registry();
118
118
  exports.OWNING_ENTITY_TYPE = {
@@ -154,6 +154,36 @@ var require_control_plane = __commonJS({
154
154
  source: sources_1.OPENHI_OPS_SOURCE,
155
155
  dedupRequired: true
156
156
  });
157
+ exports.ControlPlaneMembershipCreatedV1 = (0, registry_1.defineDetailType)({
158
+ detailType: "control-plane.membership-created.v1",
159
+ source: sources_1.OPENHI_CONTROL_SOURCE,
160
+ dedupRequired: true
161
+ });
162
+ exports.ControlPlaneMembershipDeletedV1 = (0, registry_1.defineDetailType)({
163
+ detailType: "control-plane.membership-deleted.v1",
164
+ source: sources_1.OPENHI_CONTROL_SOURCE,
165
+ dedupRequired: true
166
+ });
167
+ exports.ControlPlaneRoleAssignmentCreatedV1 = (0, registry_1.defineDetailType)({
168
+ detailType: "control-plane.role-assignment-created.v1",
169
+ source: sources_1.OPENHI_CONTROL_SOURCE,
170
+ dedupRequired: true
171
+ });
172
+ exports.ControlPlaneRoleAssignmentDeletedV1 = (0, registry_1.defineDetailType)({
173
+ detailType: "control-plane.role-assignment-deleted.v1",
174
+ source: sources_1.OPENHI_CONTROL_SOURCE,
175
+ dedupRequired: true
176
+ });
177
+ exports.ControlPlaneWorkspaceCreatedV1 = (0, registry_1.defineDetailType)({
178
+ detailType: "control-plane.workspace-created.v1",
179
+ source: sources_1.OPENHI_CONTROL_SOURCE,
180
+ dedupRequired: true
181
+ });
182
+ exports.ControlPlaneWorkspaceDeletedV1 = (0, registry_1.defineDetailType)({
183
+ detailType: "control-plane.workspace-deleted.v1",
184
+ source: sources_1.OPENHI_CONTROL_SOURCE,
185
+ dedupRequired: true
186
+ });
157
187
  }
158
188
  });
159
189
 
@@ -551,7 +581,7 @@ var require_lib = __commonJS({
551
581
  "../workflows/lib/index.js"(exports) {
552
582
  "use strict";
553
583
  Object.defineProperty(exports, "__esModule", { value: true });
554
- exports.workflowDedupClient = exports.recordIfAbsent = exports.markFailed = exports.encodeSortKey = exports.WorkflowDedupTableNameMissingError = exports.WorkflowDedupInvalidInputError = exports.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR = exports.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH = exports.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS = exports.parseWorkflowEvent = exports.UnsupportedEnvelopeVersionError = exports.InvalidWorkflowEventError = exports.workflowsClient = exports.publishWorkflowEvent = exports.WorkflowPublishError = exports.isWellFormedDetailType = exports.defineDetailType = exports.RENAMABLE_ENTITY_TYPE = exports.PlatformSystemDataSeededV1 = exports.PlatformDeploymentCompletedV1 = exports.OWNING_ENTITY_TYPE = exports.InvalidDetailTypeRegistrationError = exports.ControlPlaneRenameV1 = exports.ControlPlaneRenameFailedV1 = exports.ControlPlaneRenameCompleteV1 = exports.ControlPlaneOwningDeleteV1 = exports.ControlPlaneOwningDeleteFailedV1 = exports.ControlPlaneOwningDeleteCompleteV1 = exports.OPENHI_OPS_SOURCE = exports.OPENHI_DATA_SOURCE = exports.OPENHI_CONTROL_SOURCE = exports.DEFAULT_BUS_NAME_BY_SOURCE = exports.workflowUserActorFromClaims = exports.isWorkflowUserActor = exports.isWorkflowSystemActor = exports.MissingActorContextError = exports.isSupportedEnvelopeVersion = exports.ENVELOPE_VERSION = void 0;
584
+ exports.workflowDedupClient = exports.recordIfAbsent = exports.markFailed = exports.encodeSortKey = exports.WorkflowDedupTableNameMissingError = exports.WorkflowDedupInvalidInputError = exports.WORKFLOW_DEDUP_TABLE_NAME_ENV_VAR = exports.WORKFLOW_DEDUP_MAX_CONSUMER_NAME_LENGTH = exports.WORKFLOW_DEDUP_DEFAULT_TTL_SECONDS = exports.parseWorkflowEvent = exports.UnsupportedEnvelopeVersionError = exports.InvalidWorkflowEventError = exports.workflowsClient = exports.publishWorkflowEvent = exports.WorkflowPublishError = exports.isWellFormedDetailType = exports.defineDetailType = exports.RENAMABLE_ENTITY_TYPE = exports.PlatformSystemDataSeededV1 = exports.PlatformDeploymentCompletedV1 = exports.OWNING_ENTITY_TYPE = exports.InvalidDetailTypeRegistrationError = exports.ControlPlaneWorkspaceDeletedV1 = exports.ControlPlaneWorkspaceCreatedV1 = exports.ControlPlaneRoleAssignmentDeletedV1 = exports.ControlPlaneRoleAssignmentCreatedV1 = exports.ControlPlaneRenameV1 = exports.ControlPlaneRenameFailedV1 = exports.ControlPlaneRenameCompleteV1 = exports.ControlPlaneOwningDeleteV1 = exports.ControlPlaneOwningDeleteFailedV1 = exports.ControlPlaneOwningDeleteCompleteV1 = exports.ControlPlaneMembershipDeletedV1 = exports.ControlPlaneMembershipCreatedV1 = exports.OPENHI_OPS_SOURCE = exports.OPENHI_DATA_SOURCE = exports.OPENHI_CONTROL_SOURCE = exports.DEFAULT_BUS_NAME_BY_SOURCE = exports.workflowUserActorFromClaims = exports.isWorkflowUserActor = exports.isWorkflowSystemActor = exports.MissingActorContextError = exports.isSupportedEnvelopeVersion = exports.ENVELOPE_VERSION = void 0;
555
585
  var envelope_version_1 = require_envelope_version();
556
586
  Object.defineProperty(exports, "ENVELOPE_VERSION", { enumerable: true, get: function() {
557
587
  return envelope_version_1.ENVELOPE_VERSION;
@@ -586,6 +616,12 @@ var require_lib = __commonJS({
586
616
  return sources_1.OPENHI_OPS_SOURCE;
587
617
  } });
588
618
  var detail_types_1 = require_detail_types();
619
+ Object.defineProperty(exports, "ControlPlaneMembershipCreatedV1", { enumerable: true, get: function() {
620
+ return detail_types_1.ControlPlaneMembershipCreatedV1;
621
+ } });
622
+ Object.defineProperty(exports, "ControlPlaneMembershipDeletedV1", { enumerable: true, get: function() {
623
+ return detail_types_1.ControlPlaneMembershipDeletedV1;
624
+ } });
589
625
  Object.defineProperty(exports, "ControlPlaneOwningDeleteCompleteV1", { enumerable: true, get: function() {
590
626
  return detail_types_1.ControlPlaneOwningDeleteCompleteV1;
591
627
  } });
@@ -604,6 +640,18 @@ var require_lib = __commonJS({
604
640
  Object.defineProperty(exports, "ControlPlaneRenameV1", { enumerable: true, get: function() {
605
641
  return detail_types_1.ControlPlaneRenameV1;
606
642
  } });
643
+ Object.defineProperty(exports, "ControlPlaneRoleAssignmentCreatedV1", { enumerable: true, get: function() {
644
+ return detail_types_1.ControlPlaneRoleAssignmentCreatedV1;
645
+ } });
646
+ Object.defineProperty(exports, "ControlPlaneRoleAssignmentDeletedV1", { enumerable: true, get: function() {
647
+ return detail_types_1.ControlPlaneRoleAssignmentDeletedV1;
648
+ } });
649
+ Object.defineProperty(exports, "ControlPlaneWorkspaceCreatedV1", { enumerable: true, get: function() {
650
+ return detail_types_1.ControlPlaneWorkspaceCreatedV1;
651
+ } });
652
+ Object.defineProperty(exports, "ControlPlaneWorkspaceDeletedV1", { enumerable: true, get: function() {
653
+ return detail_types_1.ControlPlaneWorkspaceDeletedV1;
654
+ } });
607
655
  Object.defineProperty(exports, "InvalidDetailTypeRegistrationError", { enumerable: true, get: function() {
608
656
  return detail_types_1.InvalidDetailTypeRegistrationError;
609
657
  } });
@@ -679,4 +727,4 @@ var require_lib = __commonJS({
679
727
  export {
680
728
  require_lib
681
729
  };
682
- //# sourceMappingURL=chunk-ZM4GDHHC.mjs.map
730
+ //# sourceMappingURL=chunk-KMEWULMX.mjs.map
@@ -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/control-plane.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"],"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 {\n OPENHI_CONTROL_SOURCE,\n OPENHI_DATA_SOURCE,\n OPENHI_OPS_SOURCE,\n} from \"../sources\";\nimport { defineDetailType } from \"./registry\";\n\n/**\n * Owning-entity types covered by the TR-022 hard-delete cascade.\n * The cascade pipeline today targets {@link OWNING_ENTITY_TYPE.Workspace}\n * and {@link OWNING_ENTITY_TYPE.User} — the two owning entities whose\n * deletion orphans adjacency-list projections under their partition.\n * Tenant hard-delete is intentionally out of scope (it cascades a much\n * wider graph and is handled outside this pipeline).\n */\nexport const OWNING_ENTITY_TYPE = {\n Workspace: \"Workspace\",\n User: \"User\",\n} as const;\n/** Union of the values of {@link OWNING_ENTITY_TYPE}. */\nexport type OwningEntityType =\n (typeof OWNING_ENTITY_TYPE)[keyof typeof OWNING_ENTITY_TYPE];\n\n/**\n * Payload (`detail.payload`) of `control-plane.owning-delete.v1` —\n * published on the `openhi.data` data event bus when the Firehose\n * transform Lambda observes a stream record showing\n * `lifecycleState: active → deleting` on a canonical Workspace or\n * User record. The owning-delete cascade state machine subscribes to\n * this detail-type.\n *\n * The full EventBridge `detail` carries this payload nested inside a\n * `WorkflowEvent<ControlPlaneOwningDeleteV1Detail>` envelope per\n * ADR-016 standard envelope shape — `eventId`, `attempt`,\n * `correlationId`, `causationId`, `actor`, `occurredAt` live on the\n * envelope, not on this payload.\n *\n * @see .state/adr-018-implementation-guide.md section 4 (TR-022 Hard-Delete-Cascade Contract)\n */\nexport interface ControlPlaneOwningDeleteV1Detail {\n readonly ownerType: OwningEntityType;\n readonly ownerId: string;\n /** Present for Workspace owners; absent for User (cross-tenant identity). */\n readonly tenantId?: string;\n}\n\n/**\n * Registry entry for `control-plane.owning-delete.v1`.\n *\n * The platform-wide detail-type format\n * (`<area>.<event>.v<integer>` enforced by `isWellFormedDetailType`)\n * does not allow the bare `ControlPlaneOwningDelete` shape the\n * ADR-018 implementation guide pseudocode uses; the registered string\n * is the format-compliant equivalent. The EventBridge rule pattern on\n * the cascade state machine matches this exact string.\n *\n * `dedupRequired: true` — the cascade state machine MUST dedupe on\n * `(eventId, attempt)` via `WorkflowDedupClient` so EventBridge retries\n * (which the data event bus delivers at-least-once) never start two\n * concurrent cascades on the same owning entity.\n */\nexport const ControlPlaneOwningDeleteV1 =\n defineDetailType<ControlPlaneOwningDeleteV1Detail>({\n detailType: \"control-plane.owning-delete.v1\",\n source: OPENHI_DATA_SOURCE,\n dedupRequired: true,\n });\n\n/**\n * Payload of `control-plane.owning-delete-complete.v1` — terminal\n * event published on the `openhi.ops` ops event bus when the cascade\n * state machine has successfully deleted every child projection and\n * the canonical owning record itself. Observability sinks subscribe\n * to this event to confirm cascade completion and emit metrics.\n *\n * @see .state/adr-018-implementation-guide.md section 4 (Terminal events on EventBridge)\n */\nexport interface ControlPlaneOwningDeleteCompleteV1Detail {\n readonly ownerType: OwningEntityType;\n readonly ownerId: string;\n readonly tenantId?: string;\n /** Number of `TransactWriteItems` chunks the cascade issued. */\n readonly chunkCount: number;\n /** Total number of projection rows removed by the cascade. */\n readonly projectionsRemoved: number;\n /** Wall-clock duration of the cascade, in milliseconds. */\n readonly durationMs: number;\n /** ISO-8601 UTC timestamp of cascade completion. */\n readonly completedAt: string;\n}\n\n/** Registry entry for `control-plane.owning-delete-complete.v1`. */\nexport const ControlPlaneOwningDeleteCompleteV1 =\n defineDetailType<ControlPlaneOwningDeleteCompleteV1Detail>({\n detailType: \"control-plane.owning-delete-complete.v1\",\n source: OPENHI_OPS_SOURCE,\n dedupRequired: true,\n });\n\n/**\n * Payload of `control-plane.owning-delete-failed.v1` — terminal event\n * published on the `openhi.ops` ops event bus when the cascade state\n * machine fails irrecoverably. The canonical owning record is left at\n * `lifecycleState = \"deleted-failed\"` for operator-driven recovery;\n * alerting subscribers fan out to oncall.\n *\n * @see .state/adr-018-implementation-guide.md section 4 (Terminal events on EventBridge)\n */\nexport interface ControlPlaneOwningDeleteFailedV1Detail {\n readonly ownerType: OwningEntityType;\n readonly ownerId: string;\n readonly tenantId?: string;\n /** Step Functions execution ARN — operators dereference for root cause. */\n readonly executionArn: string;\n readonly chunkCount: number;\n /** Last opaque cursor the state machine successfully processed, or null. */\n readonly lastProcessedCursor: string | null;\n /** Short failure cause string from the Step Functions Catch block. */\n readonly failureCause: string;\n readonly failedAt: string;\n}\n\n/** Registry entry for `control-plane.owning-delete-failed.v1`. */\nexport const ControlPlaneOwningDeleteFailedV1 =\n defineDetailType<ControlPlaneOwningDeleteFailedV1Detail>({\n detailType: \"control-plane.owning-delete-failed.v1\",\n source: OPENHI_OPS_SOURCE,\n dedupRequired: true,\n });\n\n/**\n * Renamable entity types covered by the TR-023 rename cascade.\n *\n * Per ADR-018 only the three carrier entities whose display name is\n * denormalized onto Membership / RoleAssignment projections trigger a\n * cascade — Tenant, User, Role. A Workspace rename is intentionally\n * **not** in scope: Workspace's display name is denormalized onto the\n * Membership user-projection workspace sub-lane, but TR-024 § Open Item\n * #4 defers a formal Workspace-rename cascade — the SK falls back to a\n * sentinel until a future TR commits the contract.\n */\nexport const RENAMABLE_ENTITY_TYPE = {\n Tenant: \"Tenant\",\n User: \"User\",\n Role: \"Role\",\n} as const;\n/** Union of the values of {@link RENAMABLE_ENTITY_TYPE}. */\nexport type RenamableEntityType =\n (typeof RENAMABLE_ENTITY_TYPE)[keyof typeof RENAMABLE_ENTITY_TYPE];\n\n/**\n * Payload (`detail.payload`) of `control-plane.rename.v1` — published\n * on the `openhi.data` data event bus when the Firehose transform\n * Lambda observes a stream record showing a display-name change on a\n * canonical Tenant, User, or Role record. The rename-cascade state\n * machine subscribes to this detail-type.\n *\n * The full EventBridge `detail` carries this payload nested inside a\n * `WorkflowEvent<ControlPlaneRenameV1Detail>` envelope per ADR-016\n * standard envelope shape — `eventId`, `attempt`, `correlationId`,\n * `causationId`, `actor`, `occurredAt` live on the envelope, not on\n * this payload.\n *\n * The platform-wide detail-type format\n * (`<area>.<event>.v<integer>` enforced by `isWellFormedDetailType`)\n * does not allow the bare `ControlPlaneRename` shape the ADR-018\n * implementation guide pseudocode uses; the registered string is the\n * format-compliant equivalent (mirroring the\n * `control-plane.owning-delete.v1` naming).\n *\n * @see .state/adr-018-implementation-guide.md section 5 (TR-023 Rename-Cascade Consumer Contract)\n */\nexport interface ControlPlaneRenameV1Detail {\n readonly entityType: RenamableEntityType;\n readonly entityId: string;\n /** Present for User and Role; absent for Tenant (Tenant is the partition root). */\n readonly tenantId?: string;\n readonly oldName: string;\n readonly newName: string;\n /** Pre-computed via `extractLabel` so consumers do not re-normalize. */\n readonly oldNormalizedName: string;\n readonly newNormalizedName: string;\n}\n\n/**\n * Registry entry for `control-plane.rename.v1`.\n *\n * `dedupRequired: true` — the cascade state machine MUST dedupe on\n * `(eventId, attempt)` via `WorkflowDedupClient` so EventBridge retries\n * (which the data event bus delivers at-least-once) never start two\n * concurrent cascades on the same rename.\n */\nexport const ControlPlaneRenameV1 =\n defineDetailType<ControlPlaneRenameV1Detail>({\n detailType: \"control-plane.rename.v1\",\n source: OPENHI_DATA_SOURCE,\n dedupRequired: true,\n });\n\n/**\n * Payload of `control-plane.rename-complete.v1` — terminal event\n * published on the `openhi.ops` ops event bus when the cascade state\n * machine has successfully rewritten every affected projection row.\n * UI clients subscribe so they can refresh stale list views.\n *\n * @see .state/adr-018-implementation-guide.md section 5\n */\nexport interface ControlPlaneRenameCompleteV1Detail {\n readonly entityType: RenamableEntityType;\n readonly entityId: string;\n readonly tenantId?: string;\n readonly newName: string;\n /** Number of `TransactWriteItems` chunks the cascade issued. */\n readonly chunkCount: number;\n /** Total number of projection rows rewritten by the cascade. */\n readonly itemsRewritten: number;\n /** Wall-clock duration of the cascade, in milliseconds. */\n readonly durationMs: number;\n /** ISO-8601 UTC timestamp of cascade completion. */\n readonly completedAt: string;\n}\n\n/** Registry entry for `control-plane.rename-complete.v1`. */\nexport const ControlPlaneRenameCompleteV1 =\n defineDetailType<ControlPlaneRenameCompleteV1Detail>({\n detailType: \"control-plane.rename-complete.v1\",\n source: OPENHI_OPS_SOURCE,\n dedupRequired: true,\n });\n\n/**\n * Payload of `control-plane.rename-failed.v1` — terminal event\n * published on the `openhi.ops` ops event bus when the cascade state\n * machine fails irrecoverably. Alerting subscribers fan out to oncall.\n *\n * @see .state/adr-018-implementation-guide.md section 5\n */\nexport interface ControlPlaneRenameFailedV1Detail {\n readonly entityType: RenamableEntityType;\n readonly entityId: string;\n readonly tenantId?: string;\n /** Step Functions execution ARN — operators dereference for root cause. */\n readonly executionArn: string;\n readonly chunkCount: number;\n /** Short failure cause string from the Step Functions Catch block. */\n readonly failureCause: string;\n readonly failedAt: string;\n}\n\n/** Registry entry for `control-plane.rename-failed.v1`. */\nexport const ControlPlaneRenameFailedV1 =\n defineDetailType<ControlPlaneRenameFailedV1Detail>({\n detailType: \"control-plane.rename-failed.v1\",\n source: OPENHI_OPS_SOURCE,\n dedupRequired: true,\n });\n\n/**\n * Payload (`detail.payload`) of `control-plane.membership-created.v1` /\n * `control-plane.membership-deleted.v1` — published on the `openhi.control`\n * control event bus by the Membership create / delete operations on their\n * success path. The ADR-028 counter-maintenance consumer subscribes to these\n * detail-types and applies atomic ADD increments / decrements to the\n * denormalized counters on the canonical Tenant / Workspace / User records.\n *\n * The scope discriminator is the same `workspaceId`-set/unset distinction\n * ADR-018 uses to choose the membership projection sub-lane:\n * - `workspaceId` absent → a **tenant-scoped** membership (user ↔ tenant):\n * drives `Tenant.usersInTenant` and `User.tenantsForUser`.\n * - `workspaceId` present → a **workspace-scoped** membership (user ↔ workspace):\n * drives `Workspace.usersInWorkspace` and `User.workspacesForUser`.\n *\n * Carries enough context (`tenantId`, `userId`, optional `workspaceId`,\n * `membershipId`) for the consumer to adjust the right counter without an\n * extra read.\n *\n * @see ADR-028 — Denormalized Control-Plane Membership Counters\n */\nexport interface ControlPlaneMembershipChangedV1Detail {\n readonly membershipId: string;\n readonly tenantId: string;\n /** User the membership links; absent only when the resource omitted the reference. */\n readonly userId?: string;\n /** Present for workspace-scoped memberships; absent for tenant-scoped. */\n readonly workspaceId?: string;\n}\n\n/**\n * Registry entry for `control-plane.membership-created.v1`.\n *\n * `dedupRequired: true` — the counter-maintenance consumer MUST dedupe on\n * `(eventId, attempt)` via `WorkflowDedupClient` because the control bus\n * delivers at-least-once and an atomic `ADD` is not idempotent; a replayed\n * event would otherwise double-count.\n */\nexport const ControlPlaneMembershipCreatedV1 =\n defineDetailType<ControlPlaneMembershipChangedV1Detail>({\n detailType: \"control-plane.membership-created.v1\",\n source: OPENHI_CONTROL_SOURCE,\n dedupRequired: true,\n });\n\n/** Registry entry for `control-plane.membership-deleted.v1`. */\nexport const ControlPlaneMembershipDeletedV1 =\n defineDetailType<ControlPlaneMembershipChangedV1Detail>({\n detailType: \"control-plane.membership-deleted.v1\",\n source: OPENHI_CONTROL_SOURCE,\n dedupRequired: true,\n });\n\n/**\n * Payload (`detail.payload`) of `control-plane.role-assignment-created.v1` /\n * `control-plane.role-assignment-deleted.v1` — published on the `openhi.control`\n * control event bus by the RoleAssignment create / delete operations on their\n * success path. The ADR-028 counter-maintenance consumer uses the role level /\n * type carried here to classify a workspace-scoped assignment as admin vs\n * normal and adjust `Workspace.adminUsersInWorkspace` /\n * `Workspace.normalUsersInWorkspace` without re-reading the Role record.\n *\n * Carries `tenantId`, `userId`, optional `workspaceId`, `roleId`, the\n * `roleAssignmentId`, and `roleLevel` (the ADR-019 organization-role code /\n * level extracted from the resource at publish time).\n *\n * @see ADR-028 — Denormalized Control-Plane Membership Counters\n * @see ADR-019 — Organization Role Vocabulary\n */\nexport interface ControlPlaneRoleAssignmentChangedV1Detail {\n readonly roleAssignmentId: string;\n readonly tenantId: string;\n readonly userId?: string;\n /** Present for workspace-scoped assignments; absent for tenant-level. */\n readonly workspaceId?: string;\n readonly roleId?: string;\n /**\n * The role level / type (ADR-019 organization-role code) the consumer uses\n * to classify the assignment as admin vs normal. Absent when the resource\n * carried no extractable role code.\n */\n readonly roleLevel?: string;\n}\n\n/** Registry entry for `control-plane.role-assignment-created.v1`. */\nexport const ControlPlaneRoleAssignmentCreatedV1 =\n defineDetailType<ControlPlaneRoleAssignmentChangedV1Detail>({\n detailType: \"control-plane.role-assignment-created.v1\",\n source: OPENHI_CONTROL_SOURCE,\n dedupRequired: true,\n });\n\n/** Registry entry for `control-plane.role-assignment-deleted.v1`. */\nexport const ControlPlaneRoleAssignmentDeletedV1 =\n defineDetailType<ControlPlaneRoleAssignmentChangedV1Detail>({\n detailType: \"control-plane.role-assignment-deleted.v1\",\n source: OPENHI_CONTROL_SOURCE,\n dedupRequired: true,\n });\n\n/**\n * Payload (`detail.payload`) of `control-plane.workspace-created.v1` /\n * `control-plane.workspace-deleted.v1` — published on the `openhi.control`\n * control event bus by the Workspace create / delete operations on their\n * success path. The ADR-028 counter-maintenance consumer subscribes to these\n * detail-types and applies an atomic ADD increment / decrement to\n * `Tenant.workspacesInTenant`.\n *\n * Workspace lifecycle is the one counter driver that the Membership /\n * RoleAssignment publish set (#1317) does not cover — `workspacesInTenant`\n * is driven by workspace creation / deletion, not by relationship changes.\n *\n * @see ADR-028 — Denormalized Control-Plane Membership Counters (Open Item #1)\n */\nexport interface ControlPlaneWorkspaceChangedV1Detail {\n readonly workspaceId: string;\n readonly tenantId: string;\n}\n\n/**\n * Registry entry for `control-plane.workspace-created.v1`.\n *\n * `dedupRequired: true` — the counter-maintenance consumer MUST dedupe on\n * `(eventId, attempt)` via `WorkflowDedupClient` because the control bus\n * delivers at-least-once and an atomic `ADD` is not idempotent; a replayed\n * event would otherwise double-count.\n */\nexport const ControlPlaneWorkspaceCreatedV1 =\n defineDetailType<ControlPlaneWorkspaceChangedV1Detail>({\n detailType: \"control-plane.workspace-created.v1\",\n source: OPENHI_CONTROL_SOURCE,\n dedupRequired: true,\n });\n\n/** Registry entry for `control-plane.workspace-deleted.v1`. */\nexport const ControlPlaneWorkspaceDeletedV1 =\n defineDetailType<ControlPlaneWorkspaceChangedV1Detail>({\n detailType: \"control-plane.workspace-deleted.v1\",\n source: OPENHI_CONTROL_SOURCE,\n dedupRequired: true,\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 \"./control-plane\";\nexport * 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 ControlPlaneMembershipCreatedV1,\n ControlPlaneMembershipDeletedV1,\n ControlPlaneOwningDeleteCompleteV1,\n ControlPlaneOwningDeleteFailedV1,\n ControlPlaneOwningDeleteV1,\n ControlPlaneRenameCompleteV1,\n ControlPlaneRenameFailedV1,\n ControlPlaneRenameV1,\n ControlPlaneRoleAssignmentCreatedV1,\n ControlPlaneRoleAssignmentDeletedV1,\n ControlPlaneWorkspaceCreatedV1,\n ControlPlaneWorkspaceDeletedV1,\n InvalidDetailTypeRegistrationError,\n OWNING_ENTITY_TYPE,\n PlatformDeploymentCompletedV1,\n PlatformSystemDataSeededV1,\n RENAMABLE_ENTITY_TYPE,\n defineDetailType,\n isWellFormedDetailType,\n} from \"./detail-types\";\nexport type {\n ControlPlaneMembershipChangedV1Detail,\n ControlPlaneOwningDeleteCompleteV1Detail,\n ControlPlaneOwningDeleteFailedV1Detail,\n ControlPlaneOwningDeleteV1Detail,\n ControlPlaneRenameCompleteV1Detail,\n ControlPlaneRenameFailedV1Detail,\n ControlPlaneRenameV1Detail,\n ControlPlaneRoleAssignmentChangedV1Detail,\n ControlPlaneWorkspaceChangedV1Detail,\n OwningEntityType,\n PlatformDeploymentCompletedV1Detail,\n PlatformSystemDataSeededV1Detail,\n RenamableEntityType,\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"],"mappings":"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","names":[]}
@@ -0,0 +1,25 @@
1
+ import {
2
+ require_lib
3
+ } from "./chunk-KMEWULMX.mjs";
4
+ import {
5
+ __toESM
6
+ } from "./chunk-LZOMFHX3.mjs";
7
+
8
+ // src/workflows/control-plane/counter-maintenance/events.ts
9
+ var import_workflows = __toESM(require_lib());
10
+ var COUNTER_MAINTENANCE_CONSUMER_NAME = "counter-maintenance";
11
+ var COUNTER_MAINTENANCE_DETAIL_TYPES = [
12
+ import_workflows.ControlPlaneMembershipCreatedV1.detailType,
13
+ import_workflows.ControlPlaneMembershipDeletedV1.detailType,
14
+ import_workflows.ControlPlaneRoleAssignmentCreatedV1.detailType,
15
+ import_workflows.ControlPlaneRoleAssignmentDeletedV1.detailType,
16
+ import_workflows.ControlPlaneWorkspaceCreatedV1.detailType,
17
+ import_workflows.ControlPlaneWorkspaceDeletedV1.detailType
18
+ ];
19
+
20
+ export {
21
+ import_workflows,
22
+ COUNTER_MAINTENANCE_CONSUMER_NAME,
23
+ COUNTER_MAINTENANCE_DETAIL_TYPES
24
+ };
25
+ //# sourceMappingURL=chunk-LKKLO66E.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/workflows/control-plane/counter-maintenance/events.ts"],"sourcesContent":["import {\n ControlPlaneMembershipCreatedV1,\n ControlPlaneMembershipDeletedV1,\n ControlPlaneRoleAssignmentCreatedV1,\n ControlPlaneRoleAssignmentDeletedV1,\n ControlPlaneWorkspaceCreatedV1,\n ControlPlaneWorkspaceDeletedV1,\n} from \"@openhi/workflows\";\n\n/**\n * @see sites/www-docs/content/packages/@openhi/constructs/workflows/control-plane/counter-maintenance/events.md\n *\n * Wiring constants for the ADR-028 counter-maintenance consumer: the\n * dedup consumer name and the set of `openhi.control` detail-types the\n * EventBridge rule routes to the Lambda.\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.grantConsumerFromLookup(... , COUNTER_MAINTENANCE_CONSUMER_NAME)`)\n * and the handler's runtime `recordIfAbsent` call — keep them aligned by\n * importing this constant in both places.\n */\nexport const COUNTER_MAINTENANCE_CONSUMER_NAME = \"counter-maintenance\";\n\n/**\n * The six `openhi.control` detail-types the counter-maintenance consumer\n * subscribes to. The EventBridge rule matches `source: [\"openhi.control\"]`\n * and this exact detail-type list; the handler dispatches on the\n * `detail-type` to choose the counter routing.\n */\nexport const COUNTER_MAINTENANCE_DETAIL_TYPES = [\n ControlPlaneMembershipCreatedV1.detailType,\n ControlPlaneMembershipDeletedV1.detailType,\n ControlPlaneRoleAssignmentCreatedV1.detailType,\n ControlPlaneRoleAssignmentDeletedV1.detailType,\n ControlPlaneWorkspaceCreatedV1.detailType,\n ControlPlaneWorkspaceDeletedV1.detailType,\n] as const;\n\n// Re-export the registry entries so the construct + handler read from\n// the same source — a rename upstream surfaces at both call sites.\nexport {\n ControlPlaneMembershipCreatedV1,\n ControlPlaneMembershipDeletedV1,\n ControlPlaneRoleAssignmentCreatedV1,\n ControlPlaneRoleAssignmentDeletedV1,\n ControlPlaneWorkspaceCreatedV1,\n ControlPlaneWorkspaceDeletedV1,\n};\n"],"mappings":";;;;;;;;AAAA,uBAOO;AAiBA,IAAM,oCAAoC;AAQ1C,IAAM,mCAAmC;AAAA,EAC9C,iDAAgC;AAAA,EAChC,iDAAgC;AAAA,EAChC,qDAAoC;AAAA,EACpC,qDAAoC;AAAA,EACpC,gDAA+B;AAAA,EAC/B,gDAA+B;AACjC;","names":[]}
@@ -1,26 +1,36 @@
1
1
  import {
2
- buildMembershipUserProjectionItem,
2
+ extractRoleLevel,
3
+ publishMembershipCreated,
4
+ publishRoleAssignmentCreated,
5
+ publishWorkspaceCreated
6
+ } from "./chunk-BUAYVN3C.mjs";
7
+ import {
3
8
  buildMembershipWorkspaceProjectionItem,
4
9
  buildRoleAssignmentUserProjectionItem,
5
10
  buildRoleAssignmentWorkspaceProjectionItem,
6
- extractReferenceSlug,
7
- extractReferenceSlug2
8
- } from "./chunk-HQ67J7BP.mjs";
11
+ extractReferenceSlug as extractReferenceSlug2
12
+ } from "./chunk-5S6VFBLT.mjs";
13
+ import {
14
+ buildMembershipUserProjectionItem,
15
+ extractReferenceSlug
16
+ } from "./chunk-I6LUPJUY.mjs";
9
17
  import {
10
18
  executeMultiWrite
11
19
  } from "./chunk-QJDHVMKT.mjs";
12
20
  import {
13
- DATA_ENTITY_SK,
14
- createDataEntityRecord,
15
21
  getDynamoDataService
16
- } from "./chunk-MVQWAIMC.mjs";
22
+ } from "./chunk-6BB4CRSS.mjs";
23
+ import {
24
+ DATA_ENTITY_SK,
25
+ createDataEntityRecord
26
+ } from "./chunk-O5VQWB6U.mjs";
17
27
  import {
18
28
  ConflictError,
19
29
  ValidationError
20
30
  } from "./chunk-FYHBHHWK.mjs";
21
31
  import {
22
32
  getDynamoControlService
23
- } from "./chunk-VZCPGQXA.mjs";
33
+ } from "./chunk-EUIP2U5F.mjs";
24
34
 
25
35
  // src/data/operations/control/membership-constraints/platform-scope-tenant-id.ts
26
36
  var PLATFORM_SCOPE_TENANT_ID = "platform";
@@ -159,6 +169,14 @@ async function createMembershipOperation(params) {
159
169
  });
160
170
  }
161
171
  await executeMultiWrite({ service, triples });
172
+ await publishMembershipCreated(context, {
173
+ membershipId: id,
174
+ tenantId: context.tenantId,
175
+ ...userIdFromResource !== void 0 && { userId: userIdFromResource },
176
+ ...workspaceIdFromResource !== void 0 && {
177
+ workspaceId: workspaceIdFromResource
178
+ }
179
+ });
162
180
  return {
163
181
  id,
164
182
  resource,
@@ -286,6 +304,18 @@ async function createRoleAssignmentOperation(params) {
286
304
  });
287
305
  }
288
306
  await executeMultiWrite({ service, triples });
307
+ await publishRoleAssignmentCreated(context, {
308
+ roleAssignmentId: id,
309
+ tenantId: context.tenantId,
310
+ ...userIdFromResource !== void 0 && { userId: userIdFromResource },
311
+ ...workspaceIdFromResource !== void 0 && {
312
+ workspaceId: workspaceIdFromResource
313
+ },
314
+ ...roleIdFromResource !== void 0 && { roleId: roleIdFromResource },
315
+ ...extractRoleLevel(resourceRecord) !== void 0 && {
316
+ roleLevel: extractRoleLevel(resourceRecord)
317
+ }
318
+ });
289
319
  return {
290
320
  id,
291
321
  resource,
@@ -382,6 +412,10 @@ async function createWorkspaceOperation(params) {
382
412
  workspaceName,
383
413
  tableName
384
414
  });
415
+ await publishWorkspaceCreated(context, {
416
+ workspaceId: id,
417
+ tenantId
418
+ });
385
419
  return { id, resource, meta: { lastUpdated, versionId: vid } };
386
420
  }
387
421
 
@@ -393,4 +427,4 @@ export {
393
427
  createTenantOperation,
394
428
  createWorkspaceOperation
395
429
  };
396
- //# sourceMappingURL=chunk-CFJDATDK.mjs.map
430
+ //# sourceMappingURL=chunk-MLFMW5IF.mjs.map