@mastra/factory 0.16.0-alpha.6 → 0.16.0-alpha.8

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 (123) hide show
  1. package/README.md +4 -3
  2. package/dist/boards/work.d.ts.map +1 -1
  3. package/dist/boards/work.js +37 -3
  4. package/dist/boards/work.js.map +1 -1
  5. package/dist/factory.d.ts +6 -3
  6. package/dist/factory.d.ts.map +1 -1
  7. package/dist/factory.js +15 -6
  8. package/dist/factory.js.map +1 -1
  9. package/dist/integrations/base.d.ts +2 -0
  10. package/dist/integrations/base.d.ts.map +1 -1
  11. package/dist/integrations/incidentio/agent-tools.d.ts +49 -0
  12. package/dist/integrations/incidentio/agent-tools.d.ts.map +1 -0
  13. package/dist/integrations/incidentio/agent-tools.js +90 -0
  14. package/dist/integrations/incidentio/agent-tools.js.map +1 -0
  15. package/dist/integrations/incidentio/claim.d.ts +8 -0
  16. package/dist/integrations/incidentio/claim.d.ts.map +1 -0
  17. package/dist/integrations/incidentio/claim.js +14 -0
  18. package/dist/integrations/incidentio/claim.js.map +1 -0
  19. package/dist/integrations/incidentio/default-rules.d.ts +46 -0
  20. package/dist/integrations/incidentio/default-rules.d.ts.map +1 -0
  21. package/dist/integrations/incidentio/default-rules.js +66 -0
  22. package/dist/integrations/incidentio/default-rules.js.map +1 -0
  23. package/dist/integrations/incidentio/integration.d.ts +48 -2
  24. package/dist/integrations/incidentio/integration.d.ts.map +1 -1
  25. package/dist/integrations/incidentio/integration.js +88 -2
  26. package/dist/integrations/incidentio/integration.js.map +1 -1
  27. package/dist/integrations/incidentio/issue-reconciler.d.ts +2 -0
  28. package/dist/integrations/incidentio/issue-reconciler.d.ts.map +1 -1
  29. package/dist/integrations/incidentio/issue-reconciler.js +38 -2
  30. package/dist/integrations/incidentio/issue-reconciler.js.map +1 -1
  31. package/dist/integrations/incidentio/routes.d.ts +71 -0
  32. package/dist/integrations/incidentio/routes.d.ts.map +1 -0
  33. package/dist/integrations/incidentio/routes.js +245 -0
  34. package/dist/integrations/incidentio/routes.js.map +1 -0
  35. package/dist/integrations/incidentio/rules.d.ts +55 -0
  36. package/dist/integrations/incidentio/rules.d.ts.map +1 -0
  37. package/dist/integrations/incidentio/rules.js +175 -0
  38. package/dist/integrations/incidentio/rules.js.map +1 -0
  39. package/dist/integrations/jira/adf.d.ts +26 -0
  40. package/dist/integrations/jira/adf.d.ts.map +1 -0
  41. package/dist/integrations/jira/adf.js +73 -0
  42. package/dist/integrations/jira/adf.js.map +1 -0
  43. package/dist/integrations/jira/agent-tools.d.ts +45 -0
  44. package/dist/integrations/jira/agent-tools.d.ts.map +1 -0
  45. package/dist/integrations/jira/agent-tools.js +87 -0
  46. package/dist/integrations/jira/agent-tools.js.map +1 -0
  47. package/dist/integrations/jira/api.d.ts +157 -0
  48. package/dist/integrations/jira/api.d.ts.map +1 -0
  49. package/dist/integrations/jira/api.js +141 -0
  50. package/dist/integrations/jira/api.js.map +1 -0
  51. package/dist/integrations/jira/claim.d.ts +9 -0
  52. package/dist/integrations/jira/claim.d.ts.map +1 -0
  53. package/dist/integrations/jira/claim.js +15 -0
  54. package/dist/integrations/jira/claim.js.map +1 -0
  55. package/dist/integrations/jira/default-rules.d.ts +47 -0
  56. package/dist/integrations/jira/default-rules.d.ts.map +1 -0
  57. package/dist/integrations/jira/default-rules.js +67 -0
  58. package/dist/integrations/jira/default-rules.js.map +1 -0
  59. package/dist/integrations/jira/integration.d.ts +114 -0
  60. package/dist/integrations/jira/integration.d.ts.map +1 -0
  61. package/dist/integrations/jira/integration.js +401 -0
  62. package/dist/integrations/jira/integration.js.map +1 -0
  63. package/dist/integrations/jira/issue-reconciler.d.ts +10 -0
  64. package/dist/integrations/jira/issue-reconciler.d.ts.map +1 -0
  65. package/dist/integrations/jira/issue-reconciler.js +70 -0
  66. package/dist/integrations/jira/issue-reconciler.js.map +1 -0
  67. package/dist/integrations/jira/reconciliation-config.d.ts +3 -0
  68. package/dist/integrations/jira/reconciliation-config.d.ts.map +1 -0
  69. package/dist/integrations/jira/reconciliation-config.js +17 -0
  70. package/dist/integrations/jira/reconciliation-config.js.map +1 -0
  71. package/dist/integrations/jira/routes.d.ts +54 -0
  72. package/dist/integrations/jira/routes.d.ts.map +1 -0
  73. package/dist/integrations/jira/routes.js +260 -0
  74. package/dist/integrations/jira/routes.js.map +1 -0
  75. package/dist/integrations/jira/rules.d.ts +57 -0
  76. package/dist/integrations/jira/rules.d.ts.map +1 -0
  77. package/dist/integrations/jira/rules.js +175 -0
  78. package/dist/integrations/jira/rules.js.map +1 -0
  79. package/dist/integrations/platform/connect/routes.d.ts +39 -0
  80. package/dist/integrations/platform/connect/routes.d.ts.map +1 -0
  81. package/dist/integrations/platform/connect/routes.js +129 -0
  82. package/dist/integrations/platform/connect/routes.js.map +1 -0
  83. package/dist/integrations/platform/github/event-worker.js +1 -1
  84. package/dist/integrations/platform/github/integration.js +1 -1
  85. package/dist/integrations/platform/incidentio/integration.d.ts +61 -4
  86. package/dist/integrations/platform/incidentio/integration.d.ts.map +1 -1
  87. package/dist/integrations/platform/incidentio/integration.js +371 -22
  88. package/dist/integrations/platform/incidentio/integration.js.map +1 -1
  89. package/dist/integrations/platform/jira/agent-tools.d.ts +36 -0
  90. package/dist/integrations/platform/jira/agent-tools.d.ts.map +1 -0
  91. package/dist/integrations/platform/jira/agent-tools.js +85 -0
  92. package/dist/integrations/platform/jira/agent-tools.js.map +1 -0
  93. package/dist/integrations/platform/jira/integration.d.ts +65 -0
  94. package/dist/integrations/platform/jira/integration.d.ts.map +1 -0
  95. package/dist/integrations/platform/jira/integration.js +540 -0
  96. package/dist/integrations/platform/jira/integration.js.map +1 -0
  97. package/dist/integrations/platform/jira/routes.d.ts +55 -0
  98. package/dist/integrations/platform/jira/routes.d.ts.map +1 -0
  99. package/dist/integrations/platform/jira/routes.js +274 -0
  100. package/dist/integrations/platform/jira/routes.js.map +1 -0
  101. package/dist/routes/oauth.js +2 -5
  102. package/dist/routes/oauth.js.map +1 -1
  103. package/dist/routes/surface.d.ts.map +1 -1
  104. package/dist/routes/surface.js +24 -4
  105. package/dist/routes/surface.js.map +1 -1
  106. package/dist/rules/dispatcher.d.ts.map +1 -1
  107. package/dist/rules/dispatcher.js +1 -1
  108. package/dist/rules/dispatcher.js.map +1 -1
  109. package/dist/rules/index.d.ts +2 -2
  110. package/dist/rules/index.d.ts.map +1 -1
  111. package/dist/rules/index.js +2 -2
  112. package/dist/rules/types.d.ts +57 -3
  113. package/dist/rules/types.d.ts.map +1 -1
  114. package/dist/rules/types.js +9 -1
  115. package/dist/rules/types.js.map +1 -1
  116. package/dist/rules/validation.d.ts.map +1 -1
  117. package/dist/rules/validation.js +2 -0
  118. package/dist/rules/validation.js.map +1 -1
  119. package/dist/work-item-branch.d.ts +1 -1
  120. package/dist/work-item-branch.d.ts.map +1 -1
  121. package/dist/work-item-branch.js +4 -2
  122. package/dist/work-item-branch.js.map +1 -1
  123. package/package.json +7 -7
@@ -0,0 +1,175 @@
1
+ import { assertFactoryDecisionTarget, validateFactoryRuleDecisions } from "../../rules/validation.js";
2
+ import { boardForWorkItem, isTerminalWorkItem } from "../../boards/semantics.js";
3
+ import "../../boards/index.js";
4
+ import { jiraClaimKey } from "./claim.js";
5
+ //#region src/integrations/jira/rules.ts
6
+ const RULE_TIMEOUT_MS = 5e3;
7
+ async function withRuleTimeout(promise) {
8
+ let timeout;
9
+ try {
10
+ return await Promise.race([promise, new Promise((_, reject) => {
11
+ timeout = setTimeout(() => reject(/* @__PURE__ */ new Error("FACTORY_RULE_TIMEOUT")), RULE_TIMEOUT_MS);
12
+ })]);
13
+ } finally {
14
+ if (timeout) clearTimeout(timeout);
15
+ }
16
+ }
17
+ var JiraRules = class {
18
+ options;
19
+ constructor(options) {
20
+ this.options = options;
21
+ }
22
+ async ingest(input) {
23
+ if (!await this.options.projects.get({
24
+ orgId: input.orgId,
25
+ id: input.factoryProjectId
26
+ })) return {
27
+ status: "missing",
28
+ ingested: 0
29
+ };
30
+ const items = await this.options.storage.list({
31
+ orgId: input.orgId,
32
+ factoryProjectId: input.factoryProjectId
33
+ });
34
+ const itemsBySourceKey = new Map(items.map((item) => [item.externalSource?.externalId, item]));
35
+ const itemsByClaimKey = new Map(items.filter((item) => item.claimKey).map((item) => [item.claimKey, item]));
36
+ const statuses = [];
37
+ for (const issue of input.issues) {
38
+ const relatedItem = itemsByClaimKey.get(jiraClaimKey(issue.id)) ?? itemsBySourceKey.get(issue.id);
39
+ if (!relatedItem && await this.#heldElsewhere(input, issue)) {
40
+ statuses.push("missing");
41
+ continue;
42
+ }
43
+ statuses.push(await this.#ingestIssue(input, issue, relatedItem));
44
+ }
45
+ if (statuses.some((status) => status === "committed")) return {
46
+ status: "committed",
47
+ ingested: statuses.length
48
+ };
49
+ if (statuses.some((status) => status === "replayed")) return {
50
+ status: "replayed",
51
+ ingested: statuses.length
52
+ };
53
+ return {
54
+ status: "missing",
55
+ ingested: statuses.length
56
+ };
57
+ }
58
+ async #heldElsewhere(input, issue) {
59
+ const heldLive = (row) => row.factoryProjectId !== input.factoryProjectId && !isTerminalWorkItem(this.options.boards, row);
60
+ const claimant = await this.options.storage.getByClaimKey({
61
+ orgId: input.orgId,
62
+ claimKey: jiraClaimKey(issue.id)
63
+ });
64
+ if (claimant && heldLive(claimant)) return true;
65
+ return (await this.options.storage.listBySource({
66
+ orgId: input.orgId,
67
+ source: {
68
+ integrationId: "jira",
69
+ type: "issue",
70
+ externalId: issue.id
71
+ }
72
+ })).some(heldLive);
73
+ }
74
+ async #ingestIssue(input, issue, relatedItem) {
75
+ const ingressId = `jira:${issue.id}:${issue.updatedAt}`;
76
+ const actor = {
77
+ type: "human",
78
+ id: input.userId
79
+ };
80
+ const isClosed = issue.stateType === "completed" || issue.stateType === "canceled";
81
+ if (isClosed && !relatedItem) return "missing";
82
+ const event = isClosed ? "issueClosed" : "issueObserved";
83
+ const boundBoardId = issue.sourceId ? input.intakeBoards?.[issue.sourceId] : void 0;
84
+ const boundBoard = boundBoardId ? this.options.boards.get(boundBoardId) : void 0;
85
+ const context = {
86
+ tenant: {
87
+ orgId: input.orgId,
88
+ projectId: input.factoryProjectId
89
+ },
90
+ actor,
91
+ ingress: {
92
+ type: "jira",
93
+ id: ingressId
94
+ },
95
+ cause: `jira.${event}`,
96
+ causalChain: [],
97
+ configVersion: this.options.configVersion,
98
+ ...relatedItem ? {
99
+ item: {
100
+ id: relatedItem.id,
101
+ source: "jira-issue",
102
+ sourceKey: relatedItem.externalSource?.externalId ?? null,
103
+ parentWorkItemId: relatedItem.parentWorkItemId,
104
+ title: relatedItem.title,
105
+ url: relatedItem.externalSource?.url ?? null,
106
+ stages: relatedItem.stages,
107
+ acceptedAt: relatedItem.acceptedAt,
108
+ metadata: relatedItem.metadata
109
+ },
110
+ board: boardForWorkItem(relatedItem),
111
+ itemRevision: relatedItem.revision
112
+ } : {},
113
+ ...boundBoard ? { intake: {
114
+ board: boundBoard.id,
115
+ initialPhase: boundBoard.initialPhase
116
+ } } : {},
117
+ event,
118
+ issue
119
+ };
120
+ const rule = this.options.jiraRules[context.event];
121
+ let decision;
122
+ let decisions = [];
123
+ let outcome = { status: "accepted" };
124
+ try {
125
+ decision = rule ? await withRuleTimeout(Promise.resolve(rule(Object.freeze(context)))) : void 0;
126
+ if (decision?.type === "reject") outcome = {
127
+ status: "rejected",
128
+ code: decision.code,
129
+ reason: decision.reason
130
+ };
131
+ else if (decision) decisions = validateFactoryRuleDecisions([decision]).map((entry) => {
132
+ assertFactoryDecisionTarget(entry, this.options.boards, relatedItem ? boardForWorkItem(relatedItem) : void 0);
133
+ return { ...entry };
134
+ });
135
+ } catch (error) {
136
+ const timedOut = error instanceof Error && error.message === "FACTORY_RULE_TIMEOUT";
137
+ outcome = {
138
+ status: "rejected",
139
+ code: timedOut ? "timeout" : "rule_error",
140
+ reason: timedOut ? "Factory rule evaluation timed out." : error instanceof Error ? error.message.slice(0, 2e3) : "Factory Jira rule failed."
141
+ };
142
+ }
143
+ return (await this.options.storage.commitRuleEvaluation({
144
+ orgId: input.orgId,
145
+ factoryProjectId: input.factoryProjectId,
146
+ workItemId: relatedItem?.id ?? null,
147
+ ingress: {
148
+ identity: ingressId,
149
+ triggerType: "jira.issueObserved"
150
+ },
151
+ configVersion: this.options.configVersion,
152
+ expectedRevision: relatedItem?.revision ?? null,
153
+ actor,
154
+ outcome,
155
+ decisions,
156
+ causalChain: [],
157
+ now: /* @__PURE__ */ new Date()
158
+ })).status;
159
+ }
160
+ };
161
+ function attachJiraRules(jira, context) {
162
+ if (!context.runtime) return void 0;
163
+ const rules = new JiraRules({
164
+ projects: context.storage.projects,
165
+ storage: context.runtime.workItems,
166
+ configVersion: context.runtime.configVersion,
167
+ boards: context.runtime.boards,
168
+ jiraRules: jira.rules
169
+ });
170
+ return (input) => rules.ingest(input);
171
+ }
172
+ //#endregion
173
+ export { JiraRules, attachJiraRules };
174
+
175
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","names":["#heldElsewhere","#ingestIssue"],"sources":["../../../src/integrations/jira/rules.ts"],"sourcesContent":["import { boardForWorkItem, isTerminalWorkItem } from '../../boards/index.js';\nimport type { BoardRegistry } from '../../boards/index.js';\nimport type { FactoryJiraRuleContext, FactoryRuleDecision } from '../../rules/types.js';\nimport { assertFactoryDecisionTarget, validateFactoryRuleDecisions } from '../../rules/validation.js';\nimport type { FactoryProjectsStorage } from '../../storage/domains/projects/base.js';\nimport type { WorkItemRow, WorkItemsStorage } from '../../storage/domains/work-items/base.js';\nimport type { IntegrationContext } from '../base.js';\nimport { jiraClaimKey } from './claim.js';\nimport type { JiraEventRules } from './default-rules.js';\n\nconst RULE_TIMEOUT_MS = 5_000;\n\nasync function withRuleTimeout<T>(promise: Promise<T>): Promise<T> {\n let timeout: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n promise,\n new Promise<never>((_, reject) => {\n timeout = setTimeout(() => reject(new Error('FACTORY_RULE_TIMEOUT')), RULE_TIMEOUT_MS);\n }),\n ]);\n } finally {\n if (timeout) clearTimeout(timeout);\n }\n}\n\nexport interface JiraIssueIngress {\n /** Stable issue reference — direct Jira id or the Platform-encoded issue reference. */\n id: string;\n identifier: string;\n title: string;\n url: string;\n state: string;\n stateType: string;\n priorityLabel: string;\n assignee: string | null;\n /** Display name of the Jira reporter, when available. */\n author: string | null;\n /** Jira project key the issue belongs to, when available. */\n project: string | null;\n /** Jira site host the issue was read from, when available. */\n site: string | null;\n labels: string[];\n createdAt: string;\n updatedAt: string;\n /** Jira source the issue was read from; resolves the bound board via `intakeBoards`. */\n sourceId?: string | null;\n}\n\nexport interface JiraRulesOptions {\n projects: Pick<FactoryProjectsStorage, 'get'>;\n storage: WorkItemsStorage;\n configVersion: string;\n boards: BoardRegistry;\n jiraRules: JiraEventRules;\n}\n\nexport interface JiraRulesIngress {\n orgId: string;\n userId: string;\n factoryProjectId: string;\n issues: JiraIssueIngress[];\n /** Board id per bound Jira source id, from the project's intake bindings. */\n intakeBoards?: Readonly<Record<string, string>>;\n}\n\ntype IngressStatus = 'committed' | 'replayed' | 'missing';\n\nexport class JiraRules {\n constructor(private readonly options: JiraRulesOptions) {}\n\n async ingest(input: JiraRulesIngress): Promise<{ status: IngressStatus; ingested: number }> {\n const project = await this.options.projects.get({ orgId: input.orgId, id: input.factoryProjectId });\n if (!project) return { status: 'missing', ingested: 0 };\n\n const items = await this.options.storage.list({ orgId: input.orgId, factoryProjectId: input.factoryProjectId });\n const itemsBySourceKey = new Map(items.map(item => [item.externalSource?.externalId, item]));\n const itemsByClaimKey = new Map(items.filter(item => item.claimKey).map(item => [item.claimKey, item]));\n const statuses: IngressStatus[] = [];\n for (const issue of input.issues) {\n // The claim finds the card even after a metadata drift; the raw issue\n // reference covers cards filed before claims existed.\n const relatedItem = itemsByClaimKey.get(jiraClaimKey(issue.id)) ?? itemsBySourceKey.get(issue.id);\n // One live card per Jira issue per org. The store's claim index is the\n // guarantee; this check spares the dispatcher a refused upsert.\n if (!relatedItem && (await this.#heldElsewhere(input, issue))) {\n statuses.push('missing');\n continue;\n }\n statuses.push(await this.#ingestIssue(input, issue, relatedItem));\n }\n if (statuses.some(status => status === 'committed')) return { status: 'committed', ingested: statuses.length };\n if (statuses.some(status => status === 'replayed')) return { status: 'replayed', ingested: statuses.length };\n return { status: 'missing', ingested: statuses.length };\n }\n\n async #heldElsewhere(input: JiraRulesIngress, issue: JiraIssueIngress): Promise<boolean> {\n const heldLive = (row: WorkItemRow) =>\n row.factoryProjectId !== input.factoryProjectId && !isTerminalWorkItem(this.options.boards, row);\n const claimant = await this.options.storage.getByClaimKey({\n orgId: input.orgId,\n claimKey: jiraClaimKey(issue.id),\n });\n if (claimant && heldLive(claimant)) return true;\n // Cards filed before claims existed carry only the issue reference.\n const legacy = await this.options.storage.listBySource({\n orgId: input.orgId,\n source: { integrationId: 'jira', type: 'issue', externalId: issue.id },\n });\n return legacy.some(heldLive);\n }\n\n async #ingestIssue(\n input: JiraRulesIngress,\n issue: JiraIssueIngress,\n relatedItem: WorkItemRow | undefined,\n ): Promise<IngressStatus> {\n const ingressId = `jira:${issue.id}:${issue.updatedAt}`;\n const actor = { type: 'human' as const, id: input.userId };\n\n // Closed issues without existing work items should not create new ones.\n const isClosed = issue.stateType === 'completed' || issue.stateType === 'canceled';\n if (isClosed && !relatedItem) {\n return 'missing';\n }\n\n const event = isClosed ? 'issueClosed' : 'issueObserved';\n const boundBoardId = issue.sourceId ? input.intakeBoards?.[issue.sourceId] : undefined;\n const boundBoard = boundBoardId ? this.options.boards.get(boundBoardId) : undefined;\n const context: FactoryJiraRuleContext = {\n tenant: { orgId: input.orgId, projectId: input.factoryProjectId },\n actor,\n ingress: { type: 'jira', id: ingressId },\n cause: `jira.${event}`,\n causalChain: [],\n configVersion: this.options.configVersion,\n ...(relatedItem\n ? {\n item: {\n id: relatedItem.id,\n source: 'jira-issue',\n sourceKey: relatedItem.externalSource?.externalId ?? null,\n parentWorkItemId: relatedItem.parentWorkItemId,\n title: relatedItem.title,\n url: relatedItem.externalSource?.url ?? null,\n stages: relatedItem.stages,\n acceptedAt: relatedItem.acceptedAt,\n metadata: relatedItem.metadata,\n },\n board: boardForWorkItem(relatedItem),\n itemRevision: relatedItem.revision,\n }\n : {}),\n ...(boundBoard ? { intake: { board: boundBoard.id, initialPhase: boundBoard.initialPhase } } : {}),\n event,\n issue,\n };\n\n const rule = this.options.jiraRules[context.event];\n let decision: FactoryRuleDecision | void;\n let decisions: Record<string, unknown>[] = [];\n let outcome: { status: 'accepted' | 'rejected'; code?: string; reason?: string } = { status: 'accepted' };\n try {\n decision = rule ? await withRuleTimeout(Promise.resolve(rule(Object.freeze(context)))) : undefined;\n if (decision?.type === 'reject') {\n outcome = { status: 'rejected', code: decision.code, reason: decision.reason };\n } else if (decision) {\n decisions = validateFactoryRuleDecisions([decision]).map(entry => {\n assertFactoryDecisionTarget(\n entry,\n this.options.boards,\n relatedItem ? boardForWorkItem(relatedItem) : undefined,\n );\n return { ...entry };\n });\n }\n } catch (error) {\n const timedOut = error instanceof Error && error.message === 'FACTORY_RULE_TIMEOUT';\n outcome = {\n status: 'rejected',\n code: timedOut ? 'timeout' : 'rule_error',\n reason: timedOut\n ? 'Factory rule evaluation timed out.'\n : error instanceof Error\n ? error.message.slice(0, 2_000)\n : 'Factory Jira rule failed.',\n };\n }\n\n const committed = await this.options.storage.commitRuleEvaluation({\n orgId: input.orgId,\n factoryProjectId: input.factoryProjectId,\n workItemId: relatedItem?.id ?? null,\n ingress: { identity: ingressId, triggerType: 'jira.issueObserved' },\n configVersion: this.options.configVersion,\n expectedRevision: relatedItem?.revision ?? null,\n actor,\n outcome,\n decisions,\n causalChain: [],\n now: new Date(),\n });\n return committed.status;\n }\n}\n\nexport function attachJiraRules(\n jira: { readonly rules: JiraEventRules },\n context: IntegrationContext,\n): ((input: JiraRulesIngress) => Promise<unknown>) | undefined {\n if (!context.runtime) return undefined;\n const rules = new JiraRules({\n projects: context.storage.projects,\n storage: context.runtime.workItems,\n configVersion: context.runtime.configVersion,\n boards: context.runtime.boards,\n jiraRules: jira.rules,\n });\n return input => rules.ingest(input);\n}\n"],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Mastra `apiRoutes` that let the Factory SPA connect and reconnect
3
+ * Platform-managed provider accounts without leaving Factory.
4
+ *
5
+ * The browser cannot talk to the integrations service itself — it has no
6
+ * Platform session — so these routes mint Nango connect/reconnect sessions
7
+ * server-side with the deploy's machine credentials
8
+ * (`MASTRA_PLATFORM_ACCESS_TOKEN`/`MASTRA_PLATFORM_SECRET_KEY`) and hand the
9
+ * short-lived session token back to the SPA, which drives the provider's
10
+ * OAuth popup headlessly via `@nangohq/frontend`. Gating mirrors the
11
+ * Platform's own rule for these providers: a signed-in organization member
12
+ * (org:write parity with Linear), not an organization admin.
13
+ */
14
+ import type { ApiRoute } from '@mastra/core/server';
15
+ import type { RouteAuth } from '../../../routes/route.js';
16
+ import { PlatformApiClient } from '../api-client.js';
17
+ export interface PlatformConnectProvider {
18
+ /** Platform integration id used to mint connect sessions. */
19
+ integrationId: string;
20
+ /** Connection `integrationId` variants that belong to this provider. */
21
+ connectionIntegrationIds: readonly string[];
22
+ }
23
+ /**
24
+ * Providers the Factory SPA may connect through these routes. Keys are the
25
+ * SPA-facing provider slugs; `integrationId` is the Platform catalog id used
26
+ * for new connect sessions. Provider-specific PRs extend this registry when
27
+ * their Factory integrations are available.
28
+ */
29
+ export declare const PLATFORM_CONNECT_PROVIDERS: Record<string, PlatformConnectProvider>;
30
+ export interface BuildPlatformConnectRoutesOptions {
31
+ auth: RouteAuth;
32
+ client: PlatformApiClient;
33
+ }
34
+ /**
35
+ * Build the `/web/integrations/platform/*` routes. Callers only mount these
36
+ * when Platform machine credentials are configured.
37
+ */
38
+ export declare function buildPlatformConnectRoutes(options: BuildPlatformConnectRoutesOptions): ApiRoute[];
39
+ //# sourceMappingURL=routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../../src/integrations/platform/connect/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAIpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAoB,MAAM,kBAAkB,CAAC;AASvE,MAAM,WAAW,uBAAuB;IACtC,6DAA6D;IAC7D,aAAa,EAAE,MAAM,CAAC;IACtB,wEAAwE;IACxE,wBAAwB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C;AAED;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,EAAE,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAG9E,CAAC;AAmBF,MAAM,WAAW,iCAAiC;IAChD,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,iBAAiB,CAAC;CAC3B;AA2CD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,iCAAiC,GAAG,QAAQ,EAAE,CAgFjG"}
@@ -0,0 +1,129 @@
1
+ import { PlatformApiError } from "../api-client.js";
2
+ import { registerApiRoute } from "@mastra/core/server";
3
+ //#region src/integrations/platform/connect/routes.ts
4
+ /** Erase a route handler's path-parameterized context to a plain `Context`. */
5
+ function loose(c) {
6
+ return c;
7
+ }
8
+ /**
9
+ * Providers the Factory SPA may connect through these routes. Keys are the
10
+ * SPA-facing provider slugs; `integrationId` is the Platform catalog id used
11
+ * for new connect sessions. Provider-specific PRs extend this registry when
12
+ * their Factory integrations are available.
13
+ */
14
+ const PLATFORM_CONNECT_PROVIDERS = {
15
+ jira: {
16
+ integrationId: "jira",
17
+ connectionIntegrationIds: ["jira"]
18
+ },
19
+ "incident-io": {
20
+ integrationId: "incident-io",
21
+ connectionIntegrationIds: ["incident-io"]
22
+ }
23
+ };
24
+ async function resolveOrgTenant(c, auth) {
25
+ if (!auth.enabled()) return { response: c.json({
26
+ error: "auth_disabled",
27
+ message: "Factory auth is not enabled."
28
+ }, 403) };
29
+ await auth.ensureUser(c);
30
+ const tenant = auth.tenant(c);
31
+ if (!tenant) return { response: c.json({ error: "unauthorized" }, 401) };
32
+ if (!tenant.orgId) return { response: c.json({
33
+ error: "organization_required",
34
+ message: "Provider connections require an organization. Personal accounts cannot connect providers."
35
+ }, 403) };
36
+ return { tenant: {
37
+ orgId: tenant.orgId,
38
+ userId: tenant.userId
39
+ } };
40
+ }
41
+ function providerFromParam(c) {
42
+ const provider = c.req.param("provider");
43
+ return provider ? PLATFORM_CONNECT_PROVIDERS[provider] ?? null : null;
44
+ }
45
+ function platformError(c, err) {
46
+ if (err instanceof PlatformApiError) {
47
+ if (err.status === 409) return c.json({
48
+ error: "session_pending",
49
+ message: err.message
50
+ }, 409);
51
+ if (err.status === 403) return c.json({
52
+ error: "platform_forbidden",
53
+ message: err.message
54
+ }, 403);
55
+ }
56
+ return c.json({
57
+ error: "platform_request_failed",
58
+ message: err instanceof Error ? err.message : String(err)
59
+ }, 502);
60
+ }
61
+ /**
62
+ * Build the `/web/integrations/platform/*` routes. Callers only mount these
63
+ * when Platform machine credentials are configured.
64
+ */
65
+ function buildPlatformConnectRoutes(options) {
66
+ const { auth, client } = options;
67
+ return [
68
+ registerApiRoute("/web/integrations/platform/:provider/connections", {
69
+ method: "GET",
70
+ requiresAuth: false,
71
+ handler: async (rawContext) => {
72
+ const c = loose(rawContext);
73
+ const provider = providerFromParam(c);
74
+ if (!provider) return c.json({ error: "unknown_provider" }, 404);
75
+ const resolved = await resolveOrgTenant(c, auth);
76
+ if ("response" in resolved) return resolved.response;
77
+ try {
78
+ const connections = (await client.request("GET", "/v2/connections")).connections.filter((connection) => provider.connectionIntegrationIds.includes(connection.integrationId));
79
+ return c.json({ connections });
80
+ } catch (err) {
81
+ return platformError(c, err);
82
+ }
83
+ }
84
+ }),
85
+ registerApiRoute("/web/integrations/platform/:provider/connect-session", {
86
+ method: "POST",
87
+ requiresAuth: false,
88
+ handler: async (rawContext) => {
89
+ const c = loose(rawContext);
90
+ const provider = providerFromParam(c);
91
+ if (!provider) return c.json({ error: "unknown_provider" }, 404);
92
+ const resolved = await resolveOrgTenant(c, auth);
93
+ if ("response" in resolved) return resolved.response;
94
+ try {
95
+ const session = await client.request("POST", `/v2/integrations/${encodeURIComponent(provider.integrationId)}/connect-sessions`, {});
96
+ c.header("Cache-Control", "no-store");
97
+ return c.json(session, 201);
98
+ } catch (err) {
99
+ return platformError(c, err);
100
+ }
101
+ }
102
+ }),
103
+ registerApiRoute("/web/integrations/platform/:provider/connections/:connectionId/reconnect-session", {
104
+ method: "POST",
105
+ requiresAuth: false,
106
+ handler: async (rawContext) => {
107
+ const c = loose(rawContext);
108
+ const provider = providerFromParam(c);
109
+ if (!provider) return c.json({ error: "unknown_provider" }, 404);
110
+ const resolved = await resolveOrgTenant(c, auth);
111
+ if ("response" in resolved) return resolved.response;
112
+ const connectionId = c.req.param("connectionId");
113
+ if (!connectionId) return c.json({ error: "connection_required" }, 400);
114
+ try {
115
+ if (!(await client.request("GET", "/v2/connections")).connections.find((candidate) => candidate.id === connectionId && provider.connectionIntegrationIds.includes(candidate.integrationId))) return c.json({ error: "connection_not_found" }, 404);
116
+ const session = await client.request("POST", `/v2/connections/${encodeURIComponent(connectionId)}/reconnect-session`, {});
117
+ c.header("Cache-Control", "no-store");
118
+ return c.json(session, 201);
119
+ } catch (err) {
120
+ return platformError(c, err);
121
+ }
122
+ }
123
+ })
124
+ ];
125
+ }
126
+ //#endregion
127
+ export { PLATFORM_CONNECT_PROVIDERS, buildPlatformConnectRoutes };
128
+
129
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","names":[],"sources":["../../../../src/integrations/platform/connect/routes.ts"],"sourcesContent":["/**\n * Mastra `apiRoutes` that let the Factory SPA connect and reconnect\n * Platform-managed provider accounts without leaving Factory.\n *\n * The browser cannot talk to the integrations service itself — it has no\n * Platform session — so these routes mint Nango connect/reconnect sessions\n * server-side with the deploy's machine credentials\n * (`MASTRA_PLATFORM_ACCESS_TOKEN`/`MASTRA_PLATFORM_SECRET_KEY`) and hand the\n * short-lived session token back to the SPA, which drives the provider's\n * OAuth popup headlessly via `@nangohq/frontend`. Gating mirrors the\n * Platform's own rule for these providers: a signed-in organization member\n * (org:write parity with Linear), not an organization admin.\n */\n\nimport type { ApiRoute } from '@mastra/core/server';\nimport { registerApiRoute } from '@mastra/core/server';\nimport type { Context } from 'hono';\n\nimport type { RouteAuth } from '../../../routes/route.js';\nimport { PlatformApiClient, PlatformApiError } from '../api-client.js';\n\ntype RouteContext = Context;\n\n/** Erase a route handler's path-parameterized context to a plain `Context`. */\nfunction loose(c: unknown): RouteContext {\n return c as RouteContext;\n}\n\nexport interface PlatformConnectProvider {\n /** Platform integration id used to mint connect sessions. */\n integrationId: string;\n /** Connection `integrationId` variants that belong to this provider. */\n connectionIntegrationIds: readonly string[];\n}\n\n/**\n * Providers the Factory SPA may connect through these routes. Keys are the\n * SPA-facing provider slugs; `integrationId` is the Platform catalog id used\n * for new connect sessions. Provider-specific PRs extend this registry when\n * their Factory integrations are available.\n */\nexport const PLATFORM_CONNECT_PROVIDERS: Record<string, PlatformConnectProvider> = {\n jira: { integrationId: 'jira', connectionIntegrationIds: ['jira'] },\n 'incident-io': { integrationId: 'incident-io', connectionIntegrationIds: ['incident-io'] },\n};\n\ninterface PlatformConnectionRow {\n id: string;\n integrationId: string;\n status: 'active' | 'needs_reauth';\n accountLabel: string | null;\n displayName?: string | null;\n connectedAt?: string;\n}\n\ninterface PlatformSessionResponse {\n connectionId: string;\n integrationId: string;\n connectUrl: string;\n sessionToken: string;\n expiresAt: string;\n}\n\nexport interface BuildPlatformConnectRoutesOptions {\n auth: RouteAuth;\n client: PlatformApiClient;\n}\n\nasync function resolveOrgTenant(\n c: RouteContext,\n auth: RouteAuth,\n): Promise<{ tenant: { orgId: string; userId: string } } | { response: Response }> {\n if (!auth.enabled()) {\n return { response: c.json({ error: 'auth_disabled', message: 'Factory auth is not enabled.' }, 403) };\n }\n await auth.ensureUser(c);\n const tenant = auth.tenant(c);\n if (!tenant) return { response: c.json({ error: 'unauthorized' }, 401) };\n if (!tenant.orgId) {\n return {\n response: c.json(\n {\n error: 'organization_required',\n message: 'Provider connections require an organization. Personal accounts cannot connect providers.',\n },\n 403,\n ),\n };\n }\n return { tenant: { orgId: tenant.orgId, userId: tenant.userId } };\n}\n\nfunction providerFromParam(c: RouteContext): PlatformConnectProvider | null {\n const provider = c.req.param('provider');\n return provider ? (PLATFORM_CONNECT_PROVIDERS[provider] ?? null) : null;\n}\n\nfunction platformError(c: RouteContext, err: unknown) {\n if (err instanceof PlatformApiError) {\n if (err.status === 409) {\n return c.json({ error: 'session_pending', message: err.message }, 409);\n }\n if (err.status === 403) {\n return c.json({ error: 'platform_forbidden', message: err.message }, 403);\n }\n }\n return c.json({ error: 'platform_request_failed', message: err instanceof Error ? err.message : String(err) }, 502);\n}\n\n/**\n * Build the `/web/integrations/platform/*` routes. Callers only mount these\n * when Platform machine credentials are configured.\n */\nexport function buildPlatformConnectRoutes(options: BuildPlatformConnectRoutesOptions): ApiRoute[] {\n const { auth, client } = options;\n\n return [\n registerApiRoute('/web/integrations/platform/:provider/connections', {\n method: 'GET',\n requiresAuth: false,\n handler: async rawContext => {\n const c = loose(rawContext);\n const provider = providerFromParam(c);\n if (!provider) return c.json({ error: 'unknown_provider' }, 404);\n const resolved = await resolveOrgTenant(c, auth);\n if ('response' in resolved) return resolved.response;\n try {\n const result = await client.request<{ connections: PlatformConnectionRow[] }>('GET', '/v2/connections');\n const connections = result.connections.filter(connection =>\n provider.connectionIntegrationIds.includes(connection.integrationId),\n );\n return c.json({ connections });\n } catch (err) {\n return platformError(c, err);\n }\n },\n }),\n registerApiRoute('/web/integrations/platform/:provider/connect-session', {\n method: 'POST',\n requiresAuth: false,\n handler: async rawContext => {\n const c = loose(rawContext);\n const provider = providerFromParam(c);\n if (!provider) return c.json({ error: 'unknown_provider' }, 404);\n const resolved = await resolveOrgTenant(c, auth);\n if ('response' in resolved) return resolved.response;\n try {\n const session = await client.request<PlatformSessionResponse>(\n 'POST',\n `/v2/integrations/${encodeURIComponent(provider.integrationId)}/connect-sessions`,\n {},\n );\n c.header('Cache-Control', 'no-store');\n return c.json(session, 201);\n } catch (err) {\n return platformError(c, err);\n }\n },\n }),\n registerApiRoute('/web/integrations/platform/:provider/connections/:connectionId/reconnect-session', {\n method: 'POST',\n requiresAuth: false,\n handler: async rawContext => {\n const c = loose(rawContext);\n const provider = providerFromParam(c);\n if (!provider) return c.json({ error: 'unknown_provider' }, 404);\n const resolved = await resolveOrgTenant(c, auth);\n if ('response' in resolved) return resolved.response;\n const connectionId = c.req.param('connectionId');\n if (!connectionId) return c.json({ error: 'connection_required' }, 400);\n try {\n // Confirm the connection belongs to this provider before minting so\n // one provider's page cannot reconnect a different provider's\n // connection through this route family.\n const listed = await client.request<{ connections: PlatformConnectionRow[] }>('GET', '/v2/connections');\n const connection = listed.connections.find(\n candidate =>\n candidate.id === connectionId && provider.connectionIntegrationIds.includes(candidate.integrationId),\n );\n if (!connection) return c.json({ error: 'connection_not_found' }, 404);\n const session = await client.request<PlatformSessionResponse>(\n 'POST',\n `/v2/connections/${encodeURIComponent(connectionId)}/reconnect-session`,\n {},\n );\n c.header('Cache-Control', 'no-store');\n return c.json(session, 201);\n } catch (err) {\n return platformError(c, err);\n }\n },\n }),\n ];\n}\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { listPullRequestSubscriptionsForWebhook, retirePullRequestSubscription } from "../../github/subscriptions.js";
2
- import { dispatchGithubWebhook, isFactoryAppSender, resolveAuthorizedBots } from "../../github/webhook.js";
3
2
  import { PlatformApiError } from "../api-client.js";
3
+ import { dispatchGithubWebhook, isFactoryAppSender, resolveAuthorizedBots } from "../../github/webhook.js";
4
4
  import { NoopLeaseProvider, isLeaseProvider } from "@mastra/core/events";
5
5
  import { randomUUID } from "crypto";
6
6
  import { MastraWorker } from "@mastra/core/worker";
@@ -1,4 +1,5 @@
1
1
  import { createGithubSubscriptionTools, parseCreatedPullRequest, subscribeCurrentSessionToPullRequest } from "../../github/session-subscriptions.js";
2
+ import { PlatformApiClient, PlatformApiError, logPlatformInfo, logPlatformWarn, platformApiClientConfigFromEnv } from "../api-client.js";
2
3
  import { GithubAppIdentity } from "../../github/app-identity.js";
3
4
  import { resolveGithubRules } from "../../github/default-rules.js";
4
5
  import { attachGithubReconciler, attachGithubRules } from "../../github/rules.js";
@@ -7,7 +8,6 @@ import { reconcileInterval, reconciliationEnabled } from "../../github/reconcili
7
8
  import { parseAuthorizedBotsEnv } from "../../github/webhook.js";
8
9
  import { buildGithubRoutes } from "../../github/routes.js";
9
10
  import { settleOrAbort } from "../../github/settle-or-abort.js";
10
- import { PlatformApiClient, PlatformApiError, logPlatformInfo, logPlatformWarn, platformApiClientConfigFromEnv } from "../api-client.js";
11
11
  import { PlatformGithubEventWorker } from "./event-worker.js";
12
12
  import { registerApiRoute } from "@mastra/core/server";
13
13
  //#region src/integrations/platform/github/integration.ts
@@ -1,17 +1,74 @@
1
+ import type { RequestContext } from '@mastra/core/request-context';
2
+ import type { ApiRoute } from '@mastra/core/server';
1
3
  import type { MastraWorker } from '@mastra/core/worker';
2
- import type { FactoryIntegration, IntegrationContext } from '../../base.js';
4
+ import type { Intake } from '../../../capabilities/intake.js';
5
+ import type { RouteAuth } from '../../../routes/route.js';
6
+ import type { IntakeStorage } from '../../../storage/domains/intake/base.js';
7
+ import type { FactoryProjectsStorage } from '../../../storage/domains/projects/base.js';
8
+ import type { FactoryIntegration, IntegrationContext, IntegrationTools } from '../../base.js';
9
+ import type { IncidentioEventRules, IncidentioRuleOverrides } from '../../incidentio/default-rules.js';
3
10
  import { type PlatformApiClientConfig } from '../api-client.js';
4
11
  export interface PlatformIncidentioIntegrationConfig {
5
12
  clientConfig?: PlatformApiClientConfig;
6
- connectionId?: string;
13
+ /** Per-event replacements for the default Factory incident.io rules. */
14
+ rules?: IncidentioRuleOverrides;
7
15
  }
16
+ interface PlatformIntegrationConnection {
17
+ id: string;
18
+ integrationId: string;
19
+ status: 'active' | 'needs_reauth';
20
+ accountLabel: string | null;
21
+ }
22
+ interface ScopedSourceId {
23
+ connectionId: string;
24
+ sourceId: string;
25
+ }
26
+ export declare function encodeScopedSourceId(connectionId: string, sourceId: string): string;
27
+ export declare function decodeScopedSourceId(value: string): ScopedSourceId | null;
8
28
  export declare class PlatformIncidentioIntegration implements FactoryIntegration {
9
29
  #private;
10
30
  readonly id = "incidentio";
11
- readonly intake: import("../../../capabilities/intake.js").Intake;
12
31
  constructor(config?: PlatformIncidentioIntegrationConfig);
32
+ /** Event rules driving automatic follow-up materialization and close handling. */
33
+ get rules(): IncidentioEventRules;
34
+ initialize({ projects, auth, intake, }: {
35
+ projects: FactoryProjectsStorage;
36
+ auth: RouteAuth;
37
+ intake: IntakeStorage;
38
+ }): void;
39
+ /** Cross-integration intake selection/binding domain — agent-tool authorization. */
40
+ get intakeStorage(): IntakeStorage;
41
+ /** Factory projects domain — maps a session's resourceId to its owning org. */
42
+ get projects(): FactoryProjectsStorage;
43
+ /** Whether the host runs with web auth enabled; every surface is inert without it. */
44
+ get authEnabled(): boolean;
45
+ /**
46
+ * Map a session's resourceId (the factory project id) to its owning org.
47
+ * Same caching semantics as the Jira integration: definitive misses are
48
+ * cached, transient database failures are not.
49
+ */
50
+ resolveOrgId(resourceId: string): Promise<string | null>;
51
+ /**
52
+ * Org-scoped agent tools: the read-only follow-up detail tool for sessions
53
+ * whose resource is a factory project with an active Platform-managed
54
+ * incident.io connection.
55
+ */
56
+ agentTools(args: {
57
+ requestContext: RequestContext;
58
+ }): Promise<IntegrationTools>;
59
+ listConnections(): Promise<PlatformIntegrationConnection[]>;
60
+ hasActiveConnections(): Promise<boolean>;
61
+ clearCaches(): void;
62
+ readonly intake: Intake;
13
63
  workers(ctx: IntegrationContext): MastraWorker[];
14
- routes(): [];
64
+ /**
65
+ * The integration's HTTP surface: `/web/incidentio/*` Mastra `apiRoutes`
66
+ * (status + follow-up listing/detail for Intake), aggregated across every
67
+ * discovered Platform connection. A board-scoped listing also feeds the
68
+ * incident.io event rules.
69
+ */
70
+ routes(ctx: IntegrationContext): ApiRoute[];
15
71
  diagnostics(): Record<string, unknown>;
16
72
  }
73
+ export {};
17
74
  //# sourceMappingURL=integration.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../../src/integrations/platform/incidentio/integration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAS5E,OAAO,EAAkC,KAAK,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAEhG,MAAM,WAAW,mCAAmC;IAClD,YAAY,CAAC,EAAE,uBAAuB,CAAC;IACvC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAID,qBAAa,6BAA8B,YAAW,kBAAkB;;IACtE,QAAQ,CAAC,EAAE,gBAAgB;IAC3B,QAAQ,CAAC,MAAM,mDAAC;gBAGJ,MAAM,GAAE,mCAAwC;IAsB5D,OAAO,CAAC,GAAG,EAAE,kBAAkB,GAAG,YAAY,EAAE;IAchD,MAAM,IAAI,EAAE;IAIZ,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAGvC"}
1
+ {"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../../src/integrations/platform/incidentio/integration.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGxD,OAAO,KAAK,EAIV,MAAM,EAUP,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AACxF,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAI9F,OAAO,KAAK,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AAcvG,OAAO,EAAqD,KAAK,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAEnH,MAAM,WAAW,mCAAmC;IAClD,YAAY,CAAC,EAAE,uBAAuB,CAAC;IACvC,wEAAwE;IACxE,KAAK,CAAC,EAAE,uBAAuB,CAAC;CACjC;AAaD,UAAU,6BAA6B;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,QAAQ,GAAG,cAAc,CAAC;IAClC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,UAAU,cAAc;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAcD,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEnF;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAYzE;AAoDD,qBAAa,6BAA8B,YAAW,kBAAkB;;IACtE,QAAQ,CAAC,EAAE,gBAAgB;gBAaf,MAAM,GAAE,mCAAwC;IAO5D,kFAAkF;IAClF,IAAI,KAAK,IAAI,oBAAoB,CAEhC;IAED,UAAU,CAAC,EACT,QAAQ,EACR,IAAI,EACJ,MAAM,GACP,EAAE;QACD,QAAQ,EAAE,sBAAsB,CAAC;QACjC,IAAI,EAAE,SAAS,CAAC;QAChB,MAAM,EAAE,aAAa,CAAC;KACvB,GAAG,IAAI;IAMR,oFAAoF;IACpF,IAAI,aAAa,IAAI,aAAa,CAKjC;IAED,+EAA+E;IAC/E,IAAI,QAAQ,IAAI,sBAAsB,CAKrC;IAED,sFAAsF;IACtF,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;;;OAIG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAqB9D;;;;OAIG;IACG,UAAU,CAAC,IAAI,EAAE;QAAE,cAAc,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAK/E,eAAe,IAAI,OAAO,CAAC,6BAA6B,EAAE,CAAC;IAS3D,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC;IAI9C,WAAW,IAAI,IAAI;IAiEnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAQrB;IA4JF,OAAO,CAAC,GAAG,EAAE,kBAAkB,GAAG,YAAY,EAAE;IAchD;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,QAAQ,EAAE;IAS3C,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAGvC"}