@monad-inc/embed 0.1.0

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.
@@ -0,0 +1,271 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/connect/index.ts
21
+ var connect_exports = {};
22
+ __export(connect_exports, {
23
+ CLEANUP_FULL: () => CLEANUP_FULL,
24
+ CLEANUP_KEEP_OUTPUT: () => CLEANUP_KEEP_OUTPUT,
25
+ CLEANUP_PIPELINE_ONLY: () => CLEANUP_PIPELINE_ONLY,
26
+ PROTOCOL_SOURCE: () => PROTOCOL_SOURCE,
27
+ PROTOCOL_VERSION: () => PROTOCOL_VERSION,
28
+ buildDevNullPipeline: () => buildDevNullPipeline,
29
+ createConnectorFrame: () => createConnectorFrame,
30
+ deleteIntegration: () => deleteIntegration,
31
+ disableIntegration: () => disableIntegration,
32
+ enableIntegration: () => enableIntegration,
33
+ findIntegrationPipeline: () => findIntegrationPipeline,
34
+ setIntegrationEnabled: () => setIntegrationEnabled
35
+ });
36
+ module.exports = __toCommonJS(connect_exports);
37
+
38
+ // src/connect/protocol.ts
39
+ var PROTOCOL_SOURCE = "monad-embed";
40
+ var PROTOCOL_VERSION = 1;
41
+
42
+ // src/connect/lifecycle.ts
43
+ async function findIntegrationPipeline(opts) {
44
+ const { request, org, inputId } = opts;
45
+ const listed = await request(`/v2/${org}/pipelines/`);
46
+ const pipelines = Array.isArray(listed) ? listed : listed?.pipelines ?? listed?.data ?? [];
47
+ for (const summary of pipelines) {
48
+ if (!summary?.id) continue;
49
+ let detail;
50
+ try {
51
+ detail = await request(`/v2/${org}/pipelines/${summary.id}`);
52
+ } catch {
53
+ continue;
54
+ }
55
+ const pipeline = detail?.config ?? detail;
56
+ const nodes = pipeline?.nodes ?? [];
57
+ const inputNode = nodes.find((n) => n.component_type === "input" && n.component_id === inputId);
58
+ if (!inputNode) continue;
59
+ const outputNode = nodes.find((n) => n.component_type === "output");
60
+ return {
61
+ org,
62
+ pipelineId: summary.id,
63
+ enabled: Boolean(pipeline.enabled),
64
+ inputId,
65
+ outputId: outputNode?.component_id
66
+ };
67
+ }
68
+ return null;
69
+ }
70
+ async function setIntegrationEnabled(opts) {
71
+ const { request, org, pipelineId, enabled } = opts;
72
+ const detail = await request(`/v2/${org}/pipelines/${pipelineId}`);
73
+ const pipeline = detail?.config ?? detail;
74
+ await request(`/v2/${org}/pipelines/${pipelineId}`, {
75
+ method: "PATCH",
76
+ body: JSON.stringify({
77
+ name: pipeline.name,
78
+ description: pipeline.description ?? "",
79
+ enabled,
80
+ nodes: (pipeline.nodes ?? []).map((n) => ({
81
+ id: n.id,
82
+ slug: n.slug,
83
+ component_id: n.component_id,
84
+ component_type: n.component_type,
85
+ enabled: n.enabled ?? true
86
+ })),
87
+ edges: (pipeline.edges ?? []).map((e) => ({
88
+ name: e.name,
89
+ description: e.description ?? "",
90
+ from_node_instance_id: e.from_node_instance_id,
91
+ to_node_instance_id: e.to_node_instance_id,
92
+ disabled: e.disabled ?? false,
93
+ conditions: e.conditions
94
+ }))
95
+ })
96
+ });
97
+ }
98
+ function disableIntegration(opts) {
99
+ return setIntegrationEnabled({ ...opts, enabled: false });
100
+ }
101
+ function enableIntegration(opts) {
102
+ return setIntegrationEnabled({ ...opts, enabled: true });
103
+ }
104
+ var CLEANUP_FULL = { pipeline: true, input: true, output: true };
105
+ var CLEANUP_KEEP_OUTPUT = {
106
+ pipeline: true,
107
+ input: true,
108
+ output: false
109
+ };
110
+ var CLEANUP_PIPELINE_ONLY = {
111
+ pipeline: true,
112
+ input: false,
113
+ output: false
114
+ };
115
+ async function deleteIntegration(opts) {
116
+ const { request, org, pipelineId, inputId, outputId } = opts;
117
+ const policy = {
118
+ pipeline: true,
119
+ input: true,
120
+ output: true,
121
+ ...opts.cleanup
122
+ };
123
+ if (pipelineId && policy.pipeline) {
124
+ await request(`/v2/${org}/pipelines/${pipelineId}`, { method: "DELETE" });
125
+ }
126
+ if (inputId && policy.input) {
127
+ await request(`/v1/${org}/inputs/${inputId}`, { method: "DELETE" });
128
+ }
129
+ if (outputId && policy.output) {
130
+ await request(`/v1/${org}/outputs/${outputId}`, { method: "DELETE" });
131
+ }
132
+ }
133
+ async function buildDevNullPipeline(opts) {
134
+ const { request, org, inputId, inputName, pollAttempts = 15 } = opts;
135
+ const label = inputName || "Input";
136
+ const output = await request(`/v2/${org}/outputs`, {
137
+ method: "POST",
138
+ body: JSON.stringify({
139
+ output_type: "dev-null",
140
+ name: `${label} \u2192 /dev/null`,
141
+ description: "Auto-created sink for embed pipeline",
142
+ promise_id: "",
143
+ config: { settings: {}, secrets: {} }
144
+ })
145
+ });
146
+ const pipeline = await request(`/v2/${org}/pipelines/`, {
147
+ method: "POST",
148
+ body: JSON.stringify({
149
+ name: `${label} pipeline`,
150
+ description: "Auto-created when the input was configured via embed",
151
+ enabled: true,
152
+ nodes: [
153
+ { slug: "in", component_id: inputId, component_type: "input", enabled: true },
154
+ { slug: "out", component_id: output.id, component_type: "output", enabled: true }
155
+ ],
156
+ edges: [
157
+ {
158
+ from_node_instance_id: "in",
159
+ to_node_instance_id: "out",
160
+ description: "all records",
161
+ conditions: { operator: "always" }
162
+ }
163
+ ]
164
+ })
165
+ });
166
+ let status = "Pending";
167
+ for (let attempt = 0; attempt < pollAttempts; attempt++) {
168
+ const s = await request(`/v2/${org}/pipelines/${pipeline.id}/status`);
169
+ status = s?.status ?? "Unknown";
170
+ if (status === "Running" || status === "Erroring") break;
171
+ await new Promise((r) => setTimeout(r, 2e3));
172
+ }
173
+ return {
174
+ pipelineId: pipeline.id,
175
+ outputId: output.id,
176
+ status,
177
+ active: status === "Running"
178
+ };
179
+ }
180
+
181
+ // src/connect/index.ts
182
+ var DEFAULT_FRAME_ORIGIN = "https://app.monad.com/embed";
183
+ var DEFAULT_API_BASE = "https://app.monad.com/api";
184
+ function createConnectorFrame(opts) {
185
+ const container = typeof opts.container === "string" ? document.querySelector(opts.container) : opts.container;
186
+ if (!container) {
187
+ throw new Error("[monad-inc/connect] container element not found");
188
+ }
189
+ const frameInput = opts.frameOrigin ?? DEFAULT_FRAME_ORIGIN;
190
+ const apiBase = opts.apiBase ?? DEFAULT_API_BASE;
191
+ const frameUrl = new URL(frameInput);
192
+ const frameOrigin = frameUrl.origin;
193
+ const framePath = frameUrl.pathname.replace(/\/$/, "");
194
+ const iframe = document.createElement("iframe");
195
+ iframe.className = "monad-connect-frame";
196
+ iframe.title = "Monad connector configuration";
197
+ iframe.style.width = "100%";
198
+ iframe.style.border = "0";
199
+ iframe.style.display = "block";
200
+ iframe.allow = "clipboard-write";
201
+ iframe.src = `${frameOrigin}${framePath}/?parentOrigin=${encodeURIComponent(window.location.origin)}`;
202
+ let destroyed = false;
203
+ function postToFrame(msg) {
204
+ iframe.contentWindow?.postMessage(msg, frameOrigin);
205
+ }
206
+ function onMessage(event) {
207
+ if (destroyed) return;
208
+ if (event.origin !== frameOrigin) return;
209
+ if (event.source !== iframe.contentWindow) return;
210
+ const msg = event.data;
211
+ if (!msg || msg.source !== PROTOCOL_SOURCE) return;
212
+ switch (msg.type) {
213
+ case "ready":
214
+ postToFrame({
215
+ source: PROTOCOL_SOURCE,
216
+ type: "init",
217
+ version: PROTOCOL_VERSION,
218
+ sessionToken: opts.sessionToken,
219
+ apiBase,
220
+ organizationId: opts.organizationId,
221
+ typeId: opts.typeId,
222
+ kind: opts.kind,
223
+ displayName: opts.displayName,
224
+ existingId: opts.existingId,
225
+ synthetic: opts.synthetic,
226
+ appearance: opts.appearance,
227
+ stylesheets: opts.stylesheets
228
+ });
229
+ break;
230
+ case "resize": {
231
+ const h = Number.isFinite(msg.height) ? msg.height : 0;
232
+ iframe.style.height = `${Math.min(Math.max(h, 0), 2e4)}px`;
233
+ break;
234
+ }
235
+ case "saved":
236
+ opts.onSave?.(msg.connector);
237
+ break;
238
+ case "error":
239
+ opts.onError?.(msg.message);
240
+ break;
241
+ case "cancel":
242
+ opts.onCancel?.();
243
+ break;
244
+ }
245
+ }
246
+ window.addEventListener("message", onMessage);
247
+ container.appendChild(iframe);
248
+ return {
249
+ destroy() {
250
+ destroyed = true;
251
+ window.removeEventListener("message", onMessage);
252
+ iframe.remove();
253
+ }
254
+ };
255
+ }
256
+ // Annotate the CommonJS export names for ESM import in node:
257
+ 0 && (module.exports = {
258
+ CLEANUP_FULL,
259
+ CLEANUP_KEEP_OUTPUT,
260
+ CLEANUP_PIPELINE_ONLY,
261
+ PROTOCOL_SOURCE,
262
+ PROTOCOL_VERSION,
263
+ buildDevNullPipeline,
264
+ createConnectorFrame,
265
+ deleteIntegration,
266
+ disableIntegration,
267
+ enableIntegration,
268
+ findIntegrationPipeline,
269
+ setIntegrationEnabled
270
+ });
271
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/connect/index.ts","../../src/connect/protocol.ts","../../src/connect/lifecycle.ts"],"sourcesContent":["/**\n * @monad-inc/embed/connect — host-side SDK for embedding Monad's\n * connector-config UI as a secure iframe.\n *\n * The customer's page calls `createConnectorFrame()`; this injects an\n * <iframe> pointing at a Monad-hosted page (@monad-inc/embed-frame). The\n * connector form — and every secret the user types into it — runs\n * inside that iframe on Monad's origin. Same-origin policy means the\n * customer's own JavaScript can never read those secrets. The customer\n * gets back only a connector id (a safe reference), the same way a\n * Stripe integration only ever sees a `pm_…` token.\n *\n * The host is still responsible for one thing: minting a short-lived\n * embed session token from its backend (POST /v1/embed/sessions, with\n * the long-lived API key) and handing it to `createConnectorFrame`.\n * That token travels into the iframe over postMessage, never the URL.\n */\nimport {\n\tPROTOCOL_SOURCE,\n\tPROTOCOL_VERSION,\n\ttype Appearance,\n\ttype ComponentKind,\n\ttype FrameMessage,\n\ttype HostMessage\n} from './protocol';\n\nexport {\n\tPROTOCOL_SOURCE,\n\tPROTOCOL_VERSION,\n\ttype Appearance,\n\ttype ComponentKind,\n\ttype FrameMessage,\n\ttype HostMessage,\n\ttype InitMessage\n} from './protocol';\n\nexport {\n\tfindIntegrationPipeline,\n\tsetIntegrationEnabled,\n\tdisableIntegration,\n\tenableIntegration,\n\tdeleteIntegration,\n\tbuildDevNullPipeline,\n\tCLEANUP_FULL,\n\tCLEANUP_KEEP_OUTPUT,\n\tCLEANUP_PIPELINE_ONLY,\n\ttype MonadRequest,\n\ttype ResolvedIntegration,\n\ttype SetEnabledOptions,\n\ttype DeleteIntegrationOptions,\n\ttype CleanupPolicy,\n\ttype BuildDevNullPipelineOptions,\n\ttype BuiltPipeline\n} from './lifecycle';\n\nexport interface ConnectorFrameOptions {\n\t/** DOM node (or selector) the iframe is appended to. */\n\tcontainer: HTMLElement | string;\n\t/**\n\t * URL of the Monad embed-frame page. Defaults to\n\t * \"https://app.monad.com/embed\" (production). Override for staging\n\t * (\"https://app.monad.security/embed\") or local testing\n\t * (\"http://localhost:5173/embed\"). MUST be a different origin than\n\t * the host page — that cross-origin boundary is what isolates the\n\t * secrets. A path component is allowed (so the embed page can live\n\t * at a sub-route of the main app); the postMessage security checks\n\t * compare against the URL's origin, not its full href.\n\t */\n\tframeOrigin?: string;\n\t/**\n\t * Monad API base the iframe should call. Defaults to\n\t * \"https://app.monad.com/api\" (production). Override for non-prod\n\t * environments. Set this together with `frameOrigin` — mixing\n\t * environments will not work.\n\t */\n\tapiBase?: string;\n\t/** Connector type id, e.g. \"runzero-findings\". */\n\ttypeId: string;\n\tkind: ComponentKind;\n\t/** Short-lived embed session token (host backend mints it). */\n\tsessionToken: string;\n\t/**\n\t * Team org id the session is scoped to. The host backend already knows\n\t * this — it's the same value passed to `/v1/embed/sessions` when minting\n\t * the token. Forwarded into the iframe so it can build `/v1/<org>/*` and\n\t * `/v2/<org>/*` URLs.\n\t */\n\torganizationId: string;\n\t/** Display name shown in the frame header. */\n\tdisplayName?: string;\n\t/** Existing connector id — pass for edit mode, omit to create. */\n\texistingId?: string;\n\t/**\n\t * Whether the synthetic-data toggle is exposed in the form. Defaults\n\t * to `false` — production embed flows keep it hidden.\n\t */\n\tsynthetic?: boolean;\n\t/** Theming tokens applied as CSS variables inside the iframe. */\n\tappearance?: Appearance;\n\t/**\n\t * Stylesheet URLs to apply inside the iframe. All-or-nothing: if\n\t * any URL is provided, the iframe loads ONLY these and skips its\n\t * built-in Monad UI defaults; if omitted/empty, the iframe loads\n\t * the defaults and nothing else. URLs must be reachable from the\n\t * iframe — typically served by the host page's own origin.\n\t */\n\tstylesheets?: string[];\n\t/** Fired when the user saves successfully. */\n\tonSave?: (connector: { id: string; name?: string }) => void;\n\t/** Fired when the user cancels. */\n\tonCancel?: () => void;\n\t/** Fired on a save/test/load failure inside the iframe. */\n\tonError?: (message: string) => void;\n}\n\nexport interface ConnectorFrame {\n\t/** Remove the iframe and stop listening for its messages. */\n\tdestroy(): void;\n}\n\n/**\n * Production defaults. Customers integrating against prod don't pass\n * `frameOrigin`/`apiBase` at all; non-prod environments override both\n * together. Treat these as a public API contract — changing a hostname\n * is a breaking change for anyone on the default.\n */\nconst DEFAULT_FRAME_ORIGIN = 'https://app.monad.com/embed';\nconst DEFAULT_API_BASE = 'https://app.monad.com/api';\n\n/**\n * Inject the Monad connector iframe into `container` and wire the\n * postMessage channel. Returns a handle whose `destroy()` tears it all\n * down.\n *\n * Handshake:\n * 1. iframe loads, posts `ready`;\n * 2. host replies with `init` (session token, type, appearance);\n * 3. iframe mounts the form and streams `resize` as content grows;\n * 4. on save, iframe posts `saved {connector}`; on cancel, `cancel`.\n */\nexport function createConnectorFrame(opts: ConnectorFrameOptions): ConnectorFrame {\n\tconst container =\n\t\ttypeof opts.container === 'string'\n\t\t\t? document.querySelector<HTMLElement>(opts.container)\n\t\t\t: opts.container;\n\tif (!container) {\n\t\tthrow new Error('[monad-inc/connect] container element not found');\n\t}\n\n\tconst frameInput = opts.frameOrigin ?? DEFAULT_FRAME_ORIGIN;\n\tconst apiBase = opts.apiBase ?? DEFAULT_API_BASE;\n\n\t// Split the input URL into two pieces:\n\t// * `frameUrl` keeps the path (e.g. /embed) — used as iframe.src.\n\t// * `frameOrigin` is the bare origin — used for the postMessage\n\t// security checks below, which must compare against the value\n\t// the browser surfaces on MessageEvent.origin (always a bare\n\t// origin, never a path).\n\t// Previously these were collapsed into one `new URL(...).href`,\n\t// which silently broke any frameOrigin with a path — the equality\n\t// check `event.origin !== frameOrigin` would always fail and `init`\n\t// never made it back to the iframe.\n\tconst frameUrl = new URL(frameInput);\n\tconst frameOrigin = frameUrl.origin;\n\tconst framePath = frameUrl.pathname.replace(/\\/$/, '');\n\n\tconst iframe = document.createElement('iframe');\n\tiframe.className = 'monad-connect-frame';\n\tiframe.title = 'Monad connector configuration';\n\tiframe.style.width = '100%';\n\tiframe.style.border = '0';\n\tiframe.style.display = 'block';\n\t// The iframe needs its own scripts and same-origin access to the\n\t// Monad API; it must NOT get same-origin access to the host. A\n\t// cross-origin iframe already can't reach the host — no sandbox\n\t// attribute is needed for isolation, and adding one would block the\n\t// form's own scripts. Forms/popups stay enabled for OAuth-style\n\t// connectors that may open a window.\n\tiframe.allow = 'clipboard-write';\n\t// parentOrigin lets the iframe scope its postMessage target + sender\n\t// check. It is NOT a secret and NOT a capability — lying about it\n\t// only breaks the liar's own embed (see protocol.ts).\n\tiframe.src = `${frameOrigin}${framePath}/?parentOrigin=${encodeURIComponent(window.location.origin)}`;\n\n\tlet destroyed = false;\n\n\tfunction postToFrame(msg: HostMessage): void {\n\t\tiframe.contentWindow?.postMessage(msg, frameOrigin);\n\t}\n\n\tfunction onMessage(event: MessageEvent): void {\n\t\tif (destroyed) return;\n\t\t// --- security boundary: validate before trusting anything ---\n\t\tif (event.origin !== frameOrigin) return;\n\t\tif (event.source !== iframe.contentWindow) return;\n\t\tconst msg = event.data as FrameMessage | undefined;\n\t\tif (!msg || msg.source !== PROTOCOL_SOURCE) return;\n\n\t\tswitch (msg.type) {\n\t\t\tcase 'ready':\n\t\t\t\tpostToFrame({\n\t\t\t\t\tsource: PROTOCOL_SOURCE,\n\t\t\t\t\ttype: 'init',\n\t\t\t\t\tversion: PROTOCOL_VERSION,\n\t\t\t\t\tsessionToken: opts.sessionToken,\n\t\t\t\t\tapiBase,\n\t\t\t\t\torganizationId: opts.organizationId,\n\t\t\t\t\ttypeId: opts.typeId,\n\t\t\t\t\tkind: opts.kind,\n\t\t\t\t\tdisplayName: opts.displayName,\n\t\t\t\t\texistingId: opts.existingId,\n\t\t\t\t\tsynthetic: opts.synthetic,\n\t\t\t\t\tappearance: opts.appearance,\n\t\t\t\t\tstylesheets: opts.stylesheets\n\t\t\t\t});\n\t\t\t\tbreak;\n\t\t\tcase 'resize': {\n\t\t\t\t// Clamp to a sane range so a renderer bug in the iframe\n\t\t\t\t// can't blow up the host layout with a multi-million-px\n\t\t\t\t// height. The origin check above already gates who can\n\t\t\t\t// send this — clamping is defense in depth. Number.isFinite\n\t\t\t\t// guards against non-numeric heights that would otherwise\n\t\t\t\t// produce \"NaNpx\" and leave the iframe collapsed.\n\t\t\t\tconst h = Number.isFinite(msg.height) ? msg.height : 0;\n\t\t\t\tiframe.style.height = `${Math.min(Math.max(h, 0), 20000)}px`;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'saved':\n\t\t\t\topts.onSave?.(msg.connector);\n\t\t\t\tbreak;\n\t\t\tcase 'error':\n\t\t\t\topts.onError?.(msg.message);\n\t\t\t\tbreak;\n\t\t\tcase 'cancel':\n\t\t\t\topts.onCancel?.();\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\twindow.addEventListener('message', onMessage);\n\tcontainer.appendChild(iframe);\n\n\treturn {\n\t\tdestroy(): void {\n\t\t\tdestroyed = true;\n\t\t\twindow.removeEventListener('message', onMessage);\n\t\t\tiframe.remove();\n\t\t}\n\t};\n}\n","/**\n * postMessage contract between the host page (@monad-inc/embed/connect) and\n * the Monad-hosted iframe (@monad-inc/embed-frame).\n *\n * Why a protocol module: both sides must agree on message shapes, and a\n * drifting contract is a silent failure (a message just gets ignored).\n * @monad-inc/embed/connect owns it because it's the public-facing SDK; the\n * iframe app imports these types from here.\n *\n * Security model:\n * - Origin is the real boundary. Every listener validates\n * `event.origin` against an expected value before trusting a\n * message. The iframe knows its parent's origin (passed as a URL\n * param); the host knows the frame's origin (it chose it).\n * - `event.source` is also checked host-side so a sibling iframe on\n * the same origin can't impersonate ours.\n * - The `source: 'monad-embed'` tag is NOT security — it's just a\n * cheap filter so unrelated postMessage traffic (analytics SDKs,\n * devtools, wallet extensions) is ignored without parsing.\n * - The session token travels in the `init` message body, never in\n * the iframe URL, so it stays out of browser history and Referer.\n */\n\nexport const PROTOCOL_SOURCE = 'monad-embed' as const;\nexport const PROTOCOL_VERSION = 1 as const;\n\nexport type ComponentKind = 'input' | 'output';\n\n/**\n * Appearance tokens the host may pass to theme the iframe. The iframe\n * applies each as a `--monad-{token}` CSS variable. The host cannot\n * style across the iframe boundary directly (same-origin policy), so\n * this is the only theming channel — same idea as Stripe's `appearance`.\n */\nexport interface Appearance {\n\tcolorPrimary?: string;\n\tcolorText?: string;\n\tcolorBackground?: string;\n\tcolorBorder?: string;\n\tfontFamily?: string;\n\tborderRadius?: string;\n}\n\n/* ===== iframe -> host ===== */\n\nexport interface ReadyMessage {\n\tsource: typeof PROTOCOL_SOURCE;\n\ttype: 'ready';\n\tversion: number;\n}\n\nexport interface ResizeMessage {\n\tsource: typeof PROTOCOL_SOURCE;\n\ttype: 'resize';\n\t/** Content height in CSS pixels — the host sizes the <iframe> to this. */\n\theight: number;\n}\n\nexport interface SavedMessage {\n\tsource: typeof PROTOCOL_SOURCE;\n\ttype: 'saved';\n\t/** Safe reference to the created/updated connector — no secret material. */\n\tconnector: { id: string; name?: string };\n}\n\nexport interface ErrorMessage {\n\tsource: typeof PROTOCOL_SOURCE;\n\ttype: 'error';\n\tmessage: string;\n}\n\nexport interface CancelMessage {\n\tsource: typeof PROTOCOL_SOURCE;\n\ttype: 'cancel';\n}\n\nexport type FrameMessage =\n\t| ReadyMessage\n\t| ResizeMessage\n\t| SavedMessage\n\t| ErrorMessage\n\t| CancelMessage;\n\n/* ===== host -> iframe ===== */\n\nexport interface InitMessage {\n\tsource: typeof PROTOCOL_SOURCE;\n\ttype: 'init';\n\tversion: number;\n\t/** Short-lived embed session token minted by the host's backend. */\n\tsessionToken: string;\n\t/** Monad API base, e.g. \"https://api.monad.com\" or dev \"http://localhost/api\". */\n\tapiBase: string;\n\t/**\n\t * Team org id the session token is scoped to. The iframe uses this to\n\t * build `/v1/<org>/*` and `/v2/<org>/*` URLs. Must match the token's\n\t * `org` claim — server-side `org_access.Middleware` will 403 if not.\n\t */\n\torganizationId: string;\n\t/** Connector type id, e.g. \"runzero-findings\". */\n\ttypeId: string;\n\tkind: ComponentKind;\n\t/** Display name shown in the frame header. */\n\tdisplayName?: string;\n\t/** Existing connector id — present for edit mode, absent for create. */\n\texistingId?: string;\n\t/**\n\t * Whether the synthetic-data toggle is exposed in the form. Defaults\n\t * to `false` (toggle hidden). Production embed customers keep this\n\t * off — synthetic mode is a Monad-internal testing affordance.\n\t */\n\tsynthetic?: boolean;\n\t/** Theming tokens applied as CSS variables inside the iframe. */\n\tappearance?: Appearance;\n\t/**\n\t * Stylesheet URLs the host wants to apply to the iframe page. The\n\t * iframe loads these as `<link rel=\"stylesheet\">` tags before\n\t * rendering the form.\n\t *\n\t * All-or-nothing: if this array is present and non-empty, the\n\t * iframe loads ONLY these stylesheets and does NOT load its\n\t * built-in Monad UI defaults. If it's absent or empty, the iframe\n\t * loads the built-in defaults and nothing else. Mixing host CSS\n\t * with the defaults is intentionally not supported — partial\n\t * styling tends to produce confusing half-themed forms.\n\t *\n\t * URLs MUST be reachable from the iframe origin (typically by\n\t * being served from the host page's own origin, which the browser\n\t * permits for stylesheet `<link>` loads even cross-origin).\n\t */\n\tstylesheets?: string[];\n}\n\nexport type HostMessage = InitMessage;\n","/**\n * Connector-lifecycle helpers — the \"wiring harness\" a host (Okta, in\n * the demo's fiction) drops into its own backend to power Disable /\n * Enable / Delete buttons in its own UI.\n *\n * Unlike createConnectorFrame (browser-only), these are isomorphic:\n * they just sequence Monad's standard `/v2/{org}/*` + `/v1/{org}/*`\n * API calls. They run wherever the host holds its API key — almost\n * always its backend.\n *\n * The host supplies a `MonadRequest` thunk that performs an\n * authenticated request and returns parsed JSON. The harness owns the\n * non-obvious parts: which endpoint + verb each action maps to, the\n * delete ordering (pipeline before the input/output it references),\n * and resolving an input id to its pipeline without the host having to\n * store that mapping.\n */\n\n/**\n * Authenticated request against the Monad API, returning parsed JSON\n * (or undefined for empty responses). The host implements this with\n * its long-lived API key and base URL. Path is the full API path,\n * e.g. `/v2/{org}/pipelines/{id}`.\n */\nexport type MonadRequest = (path: string, init?: RequestInit) => Promise<any>;\n\n/* ===== resolve ===== */\n\n/**\n * Where an integration's resources live and their current state.\n * Returned by findIntegrationPipeline so the host can act on an\n * integration knowing only the input id.\n */\nexport interface ResolvedIntegration {\n\torg: string;\n\tpipelineId: string;\n\t/** Whether the pipeline is currently enabled. */\n\tenabled: boolean;\n\tinputId: string;\n\t/** Output id of the pipeline's destination node, if it has one. */\n\toutputId?: string;\n}\n\ninterface PipelineNode {\n\tcomponent_id?: string;\n\tcomponent_type?: string;\n}\n\n/**\n * Find the pipeline an input feeds. Lets the host act on an integration\n * (disable/enable/delete) knowing only the input id — no need to\n * persist an input→pipeline map of its own.\n *\n * The pipelines list is slim (no node wiring), so this fetches each\n * pipeline's detail and scans for the input node. Fine for the modest\n * pipeline counts a single tenant team has; a host managing many\n * pipelines per team should instead persist the `pipelineId`\n * buildDevNullPipeline returns and skip the lookup.\n *\n * Returns null when the input has no pipeline.\n */\nexport async function findIntegrationPipeline(opts: {\n\trequest: MonadRequest;\n\torg: string;\n\tinputId: string;\n}): Promise<ResolvedIntegration | null> {\n\tconst { request, org, inputId } = opts;\n\tconst listed = await request(`/v2/${org}/pipelines/`);\n\tconst pipelines: any[] = Array.isArray(listed)\n\t\t? listed\n\t\t: (listed?.pipelines ?? listed?.data ?? []);\n\n\tfor (const summary of pipelines) {\n\t\tif (!summary?.id) continue;\n\t\tlet detail: any;\n\t\ttry {\n\t\t\tdetail = await request(`/v2/${org}/pipelines/${summary.id}`);\n\t\t} catch {\n\t\t\tcontinue; // skip a pipeline we can't read rather than failing\n\t\t}\n\t\tconst pipeline = detail?.config ?? detail;\n\t\tconst nodes: PipelineNode[] = pipeline?.nodes ?? [];\n\t\tconst inputNode = nodes.find((n) => n.component_type === 'input' && n.component_id === inputId);\n\t\tif (!inputNode) continue;\n\t\tconst outputNode = nodes.find((n) => n.component_type === 'output');\n\t\treturn {\n\t\t\torg,\n\t\t\tpipelineId: summary.id,\n\t\t\tenabled: Boolean(pipeline.enabled),\n\t\t\tinputId,\n\t\t\toutputId: outputNode?.component_id\n\t\t};\n\t}\n\treturn null;\n}\n\n/* ===== enable / disable ===== */\n\nexport interface SetEnabledOptions {\n\trequest: MonadRequest;\n\torg: string;\n\tpipelineId: string;\n\t/** true → resume processing, false → stop (config is kept either way). */\n\tenabled: boolean;\n}\n\n/**\n * Enable or disable an integration's pipeline. Disabling stops data\n * flowing but destroys nothing — the input, output, and pipeline\n * config all survive, so a later enable resumes it as it was.\n *\n * The pipeline PATCH endpoint replaces the whole config rather than\n * accepting a partial, so this reads the current pipeline and sends it\n * back with only the `enabled` flag flipped.\n *\n * Race warning: this is GET-then-PATCH on the same resource, so a\n * concurrent edit between the two requests gets clobbered. Fine for\n * human-driven toggles. If you call this from automation, fan-out, or\n * a retry loop, ask the API team for a server-side `{ enabled }`\n * partial PATCH instead — the round-trip we do here is only a\n * workaround for the current full-config replace semantics.\n */\nexport async function setIntegrationEnabled(opts: SetEnabledOptions): Promise<void> {\n\tconst { request, org, pipelineId, enabled } = opts;\n\tconst detail = await request(`/v2/${org}/pipelines/${pipelineId}`);\n\tconst pipeline = detail?.config ?? detail;\n\tawait request(`/v2/${org}/pipelines/${pipelineId}`, {\n\t\tmethod: 'PATCH',\n\t\tbody: JSON.stringify({\n\t\t\tname: pipeline.name,\n\t\t\tdescription: pipeline.description ?? '',\n\t\t\tenabled,\n\t\t\tnodes: (pipeline.nodes ?? []).map((n: any) => ({\n\t\t\t\tid: n.id,\n\t\t\t\tslug: n.slug,\n\t\t\t\tcomponent_id: n.component_id,\n\t\t\t\tcomponent_type: n.component_type,\n\t\t\t\tenabled: n.enabled ?? true\n\t\t\t})),\n\t\t\tedges: (pipeline.edges ?? []).map((e: any) => ({\n\t\t\t\tname: e.name,\n\t\t\t\tdescription: e.description ?? '',\n\t\t\t\tfrom_node_instance_id: e.from_node_instance_id,\n\t\t\t\tto_node_instance_id: e.to_node_instance_id,\n\t\t\t\tdisabled: e.disabled ?? false,\n\t\t\t\tconditions: e.conditions\n\t\t\t}))\n\t\t})\n\t});\n}\n\n/** Convenience: disable the pipeline (stop without deleting). */\nexport function disableIntegration(opts: Omit<SetEnabledOptions, 'enabled'>): Promise<void> {\n\treturn setIntegrationEnabled({ ...opts, enabled: false });\n}\n\n/** Convenience: re-enable a disabled pipeline. */\nexport function enableIntegration(opts: Omit<SetEnabledOptions, 'enabled'>): Promise<void> {\n\treturn setIntegrationEnabled({ ...opts, enabled: true });\n}\n\n/* ===== delete ===== */\n\n/**\n * Which resources a delete removes. An embed integration is a pipeline\n * + an input + (in the dev/null flow) an output — but hosts provision\n * and own tenant resources differently. Some keep a shared output,\n * some keep the input so it can be re-wired later, some only ever tear\n * down the pipeline. Choose the policy that matches how you provision;\n * a flag left undefined defaults to `true` (delete it).\n *\n * Three ready-made policies are exported — CLEANUP_FULL,\n * CLEANUP_KEEP_OUTPUT, CLEANUP_PIPELINE_ONLY — or pass your own.\n */\nexport interface CleanupPolicy {\n\t/** Delete the pipeline. Default: true. */\n\tpipeline?: boolean;\n\t/** Delete the input connector. Default: true. */\n\tinput?: boolean;\n\t/** Delete the output connector. Default: true. */\n\toutput?: boolean;\n}\n\n/** Tear down everything — pipeline, input, and output. */\nexport const CLEANUP_FULL: CleanupPolicy = { pipeline: true, input: true, output: true };\n\n/** Remove the pipeline and input but keep the output (e.g. a shared sink). */\nexport const CLEANUP_KEEP_OUTPUT: CleanupPolicy = {\n\tpipeline: true,\n\tinput: true,\n\toutput: false\n};\n\n/** Remove only the pipeline; keep the input so it can be re-wired. */\nexport const CLEANUP_PIPELINE_ONLY: CleanupPolicy = {\n\tpipeline: true,\n\tinput: false,\n\toutput: false\n};\n\nexport interface DeleteIntegrationOptions {\n\trequest: MonadRequest;\n\torg: string;\n\t/** Pipeline to remove. Omit when the input has no pipeline. */\n\tpipelineId?: string;\n\t/** Input to remove. Omit to leave it. */\n\tinputId?: string;\n\t/** Output to remove (the auto-created dev/null sink). Omit to leave it. */\n\toutputId?: string;\n\t/**\n\t * What to actually delete. Defaults to everything supplied\n\t * (CLEANUP_FULL). Set a flag false to keep that resource even when\n\t * its id is provided.\n\t */\n\tcleanup?: CleanupPolicy;\n}\n\n/**\n * Delete an integration per a cleanup policy. Order matters: the\n * pipeline references the input and output, so it goes first. Only\n * resources whose id is supplied AND whose policy flag is true are\n * removed — so nothing the host wants to keep is touched, and nothing\n * it wants gone is left orphaned.\n */\nexport async function deleteIntegration(opts: DeleteIntegrationOptions): Promise<void> {\n\tconst { request, org, pipelineId, inputId, outputId } = opts;\n\tconst policy: Required<CleanupPolicy> = {\n\t\tpipeline: true,\n\t\tinput: true,\n\t\toutput: true,\n\t\t...opts.cleanup\n\t};\n\tif (pipelineId && policy.pipeline) {\n\t\tawait request(`/v2/${org}/pipelines/${pipelineId}`, { method: 'DELETE' });\n\t}\n\tif (inputId && policy.input) {\n\t\tawait request(`/v1/${org}/inputs/${inputId}`, { method: 'DELETE' });\n\t}\n\tif (outputId && policy.output) {\n\t\tawait request(`/v1/${org}/outputs/${outputId}`, { method: 'DELETE' });\n\t}\n}\n\n/* ===== build ===== */\n\nexport interface BuildDevNullPipelineOptions {\n\trequest: MonadRequest;\n\torg: string;\n\tinputId: string;\n\t/** Used to name the auto-created output and pipeline. */\n\tinputName: string;\n\t/** Status polls before giving up (default 15, ~2s apart). */\n\tpollAttempts?: number;\n}\n\nexport interface BuiltPipeline {\n\tpipelineId: string;\n\toutputId: string;\n\tstatus: string;\n\t/** True once the pipeline reports Running. */\n\tactive: boolean;\n}\n\n/**\n * Stand up a dev/null pipeline for a freshly-configured input: create a\n * dev/null output, create an enabled pipeline wiring input → output,\n * then poll until it reports Running. The POC's \"what happens after an\n * input is configured\" path, packaged as a reusable harness call.\n */\nexport async function buildDevNullPipeline(\n\topts: BuildDevNullPipelineOptions\n): Promise<BuiltPipeline> {\n\tconst { request, org, inputId, inputName, pollAttempts = 15 } = opts;\n\tconst label = inputName || 'Input';\n\n\tconst output = await request(`/v2/${org}/outputs`, {\n\t\tmethod: 'POST',\n\t\tbody: JSON.stringify({\n\t\t\toutput_type: 'dev-null',\n\t\t\tname: `${label} → /dev/null`,\n\t\t\tdescription: 'Auto-created sink for embed pipeline',\n\t\t\tpromise_id: '',\n\t\t\tconfig: { settings: {}, secrets: {} }\n\t\t})\n\t});\n\n\tconst pipeline = await request(`/v2/${org}/pipelines/`, {\n\t\tmethod: 'POST',\n\t\tbody: JSON.stringify({\n\t\t\tname: `${label} pipeline`,\n\t\t\tdescription: 'Auto-created when the input was configured via embed',\n\t\t\tenabled: true,\n\t\t\tnodes: [\n\t\t\t\t{ slug: 'in', component_id: inputId, component_type: 'input', enabled: true },\n\t\t\t\t{ slug: 'out', component_id: output.id, component_type: 'output', enabled: true }\n\t\t\t],\n\t\t\tedges: [\n\t\t\t\t{\n\t\t\t\t\tfrom_node_instance_id: 'in',\n\t\t\t\t\tto_node_instance_id: 'out',\n\t\t\t\t\tdescription: 'all records',\n\t\t\t\t\tconditions: { operator: 'always' }\n\t\t\t\t}\n\t\t\t]\n\t\t})\n\t});\n\n\tlet status = 'Pending';\n\tfor (let attempt = 0; attempt < pollAttempts; attempt++) {\n\t\tconst s = await request(`/v2/${org}/pipelines/${pipeline.id}/status`);\n\t\tstatus = s?.status ?? 'Unknown';\n\t\tif (status === 'Running' || status === 'Erroring') break;\n\t\tawait new Promise((r) => setTimeout(r, 2000));\n\t}\n\n\treturn {\n\t\tpipelineId: pipeline.id,\n\t\toutputId: output.id,\n\t\tstatus,\n\t\tactive: status === 'Running'\n\t};\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACuBO,IAAM,kBAAkB;AACxB,IAAM,mBAAmB;;;ACqChC,eAAsB,wBAAwB,MAIN;AACvC,QAAM,EAAE,SAAS,KAAK,QAAQ,IAAI;AAClC,QAAM,SAAS,MAAM,QAAQ,OAAO,GAAG,aAAa;AACpD,QAAM,YAAmB,MAAM,QAAQ,MAAM,IAC1C,SACC,QAAQ,aAAa,QAAQ,QAAQ,CAAC;AAE1C,aAAW,WAAW,WAAW;AAChC,QAAI,CAAC,SAAS,GAAI;AAClB,QAAI;AACJ,QAAI;AACH,eAAS,MAAM,QAAQ,OAAO,GAAG,cAAc,QAAQ,EAAE,EAAE;AAAA,IAC5D,QAAQ;AACP;AAAA,IACD;AACA,UAAM,WAAW,QAAQ,UAAU;AACnC,UAAM,QAAwB,UAAU,SAAS,CAAC;AAClD,UAAM,YAAY,MAAM,KAAK,CAAC,MAAM,EAAE,mBAAmB,WAAW,EAAE,iBAAiB,OAAO;AAC9F,QAAI,CAAC,UAAW;AAChB,UAAM,aAAa,MAAM,KAAK,CAAC,MAAM,EAAE,mBAAmB,QAAQ;AAClE,WAAO;AAAA,MACN;AAAA,MACA,YAAY,QAAQ;AAAA,MACpB,SAAS,QAAQ,SAAS,OAAO;AAAA,MACjC;AAAA,MACA,UAAU,YAAY;AAAA,IACvB;AAAA,EACD;AACA,SAAO;AACR;AA4BA,eAAsB,sBAAsB,MAAwC;AACnF,QAAM,EAAE,SAAS,KAAK,YAAY,QAAQ,IAAI;AAC9C,QAAM,SAAS,MAAM,QAAQ,OAAO,GAAG,cAAc,UAAU,EAAE;AACjE,QAAM,WAAW,QAAQ,UAAU;AACnC,QAAM,QAAQ,OAAO,GAAG,cAAc,UAAU,IAAI;AAAA,IACnD,QAAQ;AAAA,IACR,MAAM,KAAK,UAAU;AAAA,MACpB,MAAM,SAAS;AAAA,MACf,aAAa,SAAS,eAAe;AAAA,MACrC;AAAA,MACA,QAAQ,SAAS,SAAS,CAAC,GAAG,IAAI,CAAC,OAAY;AAAA,QAC9C,IAAI,EAAE;AAAA,QACN,MAAM,EAAE;AAAA,QACR,cAAc,EAAE;AAAA,QAChB,gBAAgB,EAAE;AAAA,QAClB,SAAS,EAAE,WAAW;AAAA,MACvB,EAAE;AAAA,MACF,QAAQ,SAAS,SAAS,CAAC,GAAG,IAAI,CAAC,OAAY;AAAA,QAC9C,MAAM,EAAE;AAAA,QACR,aAAa,EAAE,eAAe;AAAA,QAC9B,uBAAuB,EAAE;AAAA,QACzB,qBAAqB,EAAE;AAAA,QACvB,UAAU,EAAE,YAAY;AAAA,QACxB,YAAY,EAAE;AAAA,MACf,EAAE;AAAA,IACH,CAAC;AAAA,EACF,CAAC;AACF;AAGO,SAAS,mBAAmB,MAAyD;AAC3F,SAAO,sBAAsB,EAAE,GAAG,MAAM,SAAS,MAAM,CAAC;AACzD;AAGO,SAAS,kBAAkB,MAAyD;AAC1F,SAAO,sBAAsB,EAAE,GAAG,MAAM,SAAS,KAAK,CAAC;AACxD;AAyBO,IAAM,eAA8B,EAAE,UAAU,MAAM,OAAO,MAAM,QAAQ,KAAK;AAGhF,IAAM,sBAAqC;AAAA,EACjD,UAAU;AAAA,EACV,OAAO;AAAA,EACP,QAAQ;AACT;AAGO,IAAM,wBAAuC;AAAA,EACnD,UAAU;AAAA,EACV,OAAO;AAAA,EACP,QAAQ;AACT;AA0BA,eAAsB,kBAAkB,MAA+C;AACtF,QAAM,EAAE,SAAS,KAAK,YAAY,SAAS,SAAS,IAAI;AACxD,QAAM,SAAkC;AAAA,IACvC,UAAU;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,GAAG,KAAK;AAAA,EACT;AACA,MAAI,cAAc,OAAO,UAAU;AAClC,UAAM,QAAQ,OAAO,GAAG,cAAc,UAAU,IAAI,EAAE,QAAQ,SAAS,CAAC;AAAA,EACzE;AACA,MAAI,WAAW,OAAO,OAAO;AAC5B,UAAM,QAAQ,OAAO,GAAG,WAAW,OAAO,IAAI,EAAE,QAAQ,SAAS,CAAC;AAAA,EACnE;AACA,MAAI,YAAY,OAAO,QAAQ;AAC9B,UAAM,QAAQ,OAAO,GAAG,YAAY,QAAQ,IAAI,EAAE,QAAQ,SAAS,CAAC;AAAA,EACrE;AACD;AA4BA,eAAsB,qBACrB,MACyB;AACzB,QAAM,EAAE,SAAS,KAAK,SAAS,WAAW,eAAe,GAAG,IAAI;AAChE,QAAM,QAAQ,aAAa;AAE3B,QAAM,SAAS,MAAM,QAAQ,OAAO,GAAG,YAAY;AAAA,IAClD,QAAQ;AAAA,IACR,MAAM,KAAK,UAAU;AAAA,MACpB,aAAa;AAAA,MACb,MAAM,GAAG,KAAK;AAAA,MACd,aAAa;AAAA,MACb,YAAY;AAAA,MACZ,QAAQ,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC,EAAE;AAAA,IACrC,CAAC;AAAA,EACF,CAAC;AAED,QAAM,WAAW,MAAM,QAAQ,OAAO,GAAG,eAAe;AAAA,IACvD,QAAQ;AAAA,IACR,MAAM,KAAK,UAAU;AAAA,MACpB,MAAM,GAAG,KAAK;AAAA,MACd,aAAa;AAAA,MACb,SAAS;AAAA,MACT,OAAO;AAAA,QACN,EAAE,MAAM,MAAM,cAAc,SAAS,gBAAgB,SAAS,SAAS,KAAK;AAAA,QAC5E,EAAE,MAAM,OAAO,cAAc,OAAO,IAAI,gBAAgB,UAAU,SAAS,KAAK;AAAA,MACjF;AAAA,MACA,OAAO;AAAA,QACN;AAAA,UACC,uBAAuB;AAAA,UACvB,qBAAqB;AAAA,UACrB,aAAa;AAAA,UACb,YAAY,EAAE,UAAU,SAAS;AAAA,QAClC;AAAA,MACD;AAAA,IACD,CAAC;AAAA,EACF,CAAC;AAED,MAAI,SAAS;AACb,WAAS,UAAU,GAAG,UAAU,cAAc,WAAW;AACxD,UAAM,IAAI,MAAM,QAAQ,OAAO,GAAG,cAAc,SAAS,EAAE,SAAS;AACpE,aAAS,GAAG,UAAU;AACtB,QAAI,WAAW,aAAa,WAAW,WAAY;AACnD,UAAM,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,GAAI,CAAC;AAAA,EAC7C;AAEA,SAAO;AAAA,IACN,YAAY,SAAS;AAAA,IACrB,UAAU,OAAO;AAAA,IACjB;AAAA,IACA,QAAQ,WAAW;AAAA,EACpB;AACD;;;AFnMA,IAAM,uBAAuB;AAC7B,IAAM,mBAAmB;AAalB,SAAS,qBAAqB,MAA6C;AACjF,QAAM,YACL,OAAO,KAAK,cAAc,WACvB,SAAS,cAA2B,KAAK,SAAS,IAClD,KAAK;AACT,MAAI,CAAC,WAAW;AACf,UAAM,IAAI,MAAM,iDAAiD;AAAA,EAClE;AAEA,QAAM,aAAa,KAAK,eAAe;AACvC,QAAM,UAAU,KAAK,WAAW;AAYhC,QAAM,WAAW,IAAI,IAAI,UAAU;AACnC,QAAM,cAAc,SAAS;AAC7B,QAAM,YAAY,SAAS,SAAS,QAAQ,OAAO,EAAE;AAErD,QAAM,SAAS,SAAS,cAAc,QAAQ;AAC9C,SAAO,YAAY;AACnB,SAAO,QAAQ;AACf,SAAO,MAAM,QAAQ;AACrB,SAAO,MAAM,SAAS;AACtB,SAAO,MAAM,UAAU;AAOvB,SAAO,QAAQ;AAIf,SAAO,MAAM,GAAG,WAAW,GAAG,SAAS,kBAAkB,mBAAmB,OAAO,SAAS,MAAM,CAAC;AAEnG,MAAI,YAAY;AAEhB,WAAS,YAAY,KAAwB;AAC5C,WAAO,eAAe,YAAY,KAAK,WAAW;AAAA,EACnD;AAEA,WAAS,UAAU,OAA2B;AAC7C,QAAI,UAAW;AAEf,QAAI,MAAM,WAAW,YAAa;AAClC,QAAI,MAAM,WAAW,OAAO,cAAe;AAC3C,UAAM,MAAM,MAAM;AAClB,QAAI,CAAC,OAAO,IAAI,WAAW,gBAAiB;AAE5C,YAAQ,IAAI,MAAM;AAAA,MACjB,KAAK;AACJ,oBAAY;AAAA,UACX,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,SAAS;AAAA,UACT,cAAc,KAAK;AAAA,UACnB;AAAA,UACA,gBAAgB,KAAK;AAAA,UACrB,QAAQ,KAAK;AAAA,UACb,MAAM,KAAK;AAAA,UACX,aAAa,KAAK;AAAA,UAClB,YAAY,KAAK;AAAA,UACjB,WAAW,KAAK;AAAA,UAChB,YAAY,KAAK;AAAA,UACjB,aAAa,KAAK;AAAA,QACnB,CAAC;AACD;AAAA,MACD,KAAK,UAAU;AAOd,cAAM,IAAI,OAAO,SAAS,IAAI,MAAM,IAAI,IAAI,SAAS;AACrD,eAAO,MAAM,SAAS,GAAG,KAAK,IAAI,KAAK,IAAI,GAAG,CAAC,GAAG,GAAK,CAAC;AACxD;AAAA,MACD;AAAA,MACA,KAAK;AACJ,aAAK,SAAS,IAAI,SAAS;AAC3B;AAAA,MACD,KAAK;AACJ,aAAK,UAAU,IAAI,OAAO;AAC1B;AAAA,MACD,KAAK;AACJ,aAAK,WAAW;AAChB;AAAA,IACF;AAAA,EACD;AAEA,SAAO,iBAAiB,WAAW,SAAS;AAC5C,YAAU,YAAY,MAAM;AAE5B,SAAO;AAAA,IACN,UAAgB;AACf,kBAAY;AACZ,aAAO,oBAAoB,WAAW,SAAS;AAC/C,aAAO,OAAO;AAAA,IACf;AAAA,EACD;AACD;","names":[]}