@maydotinc/q-studio 0.1.0 → 0.1.1

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.
@@ -1,379 +1,6 @@
1
- import { Queue, RedisOptions } from 'bullmq';
2
- import { Hono } from 'hono';
3
-
4
- /**
5
- * Job status types matching BullMQ states
6
- */
7
- type JobStatus = "waiting" | "active" | "completed" | "failed" | "delayed" | "paused" | "unknown";
8
- /**
9
- * Group/folder of related BullMQ queues.
10
- */
11
- interface QueueGroup {
12
- name: string;
13
- queues: Queue[];
14
- }
15
- /**
16
- * Configuration options for Workbench
17
- */
18
- interface WorkbenchOptions {
19
- /** BullMQ Queue instances to display */
20
- queues?: Queue[];
21
- /** Named groups/folders of BullMQ Queue instances to display together */
22
- queueGroups?: QueueGroup[];
23
- /** Alias for queueGroups */
24
- groups?: QueueGroup[];
25
- /** Redis connection for auto-discovery of queues */
26
- redis?: string | RedisOptions;
27
- /** Basic auth credentials */
28
- auth?: {
29
- username: string;
30
- password: string;
31
- };
32
- /** Dashboard title */
33
- title?: string;
34
- /** Logo URL */
35
- logo?: string;
36
- /** Override base path detection */
37
- basePath?: string;
38
- /** Disable actions (retry, remove, promote) */
39
- readonly?: boolean;
40
- /** Fields from job.data to extract as filterable tags (e.g., ['teamId', 'userId']) */
41
- tags?: string[];
42
- }
43
- /**
44
- * Queue information for API responses
45
- */
46
- interface QueueInfo {
47
- name: string;
48
- group?: string;
49
- counts: {
50
- waiting: number;
51
- active: number;
52
- completed: number;
53
- failed: number;
54
- delayed: number;
55
- paused: number;
56
- };
57
- isPaused: boolean;
58
- }
59
- /**
60
- * Queue group information for API responses
61
- */
62
- interface QueueGroupInfo {
63
- name: string;
64
- queues: string[];
65
- }
66
- /**
67
- * Worker information from BullMQ
68
- */
69
- interface WorkerInfo {
70
- id: string;
71
- name: string;
72
- addr: string;
73
- age: number;
74
- idle: number;
75
- started: number;
76
- queueName: string;
77
- }
78
- /**
79
- * Extracted tag key-value pairs from job data
80
- */
81
- type JobTags = Record<string, string | number | boolean | null>;
82
- /**
83
- * Job information for API responses
84
- */
85
- interface JobInfo {
86
- id: string;
87
- name: string;
88
- data: unknown;
89
- opts: {
90
- attempts?: number;
91
- delay?: number;
92
- priority?: number;
93
- };
94
- progress: number | object;
95
- attemptsMade: number;
96
- processedOn?: number;
97
- finishedOn?: number;
98
- timestamp: number;
99
- failedReason?: string;
100
- stacktrace?: string[];
101
- returnvalue?: unknown;
102
- status: JobStatus;
103
- duration?: number;
104
- /** Extracted tag values from job.data based on configured tag fields */
105
- tags?: JobTags;
106
- /** Parent job info if this job is part of a flow */
107
- parent?: {
108
- id: string;
109
- queueName: string;
110
- };
111
- }
112
- /**
113
- * BullMQ log lines for a job.
114
- */
115
- interface JobLogsResponse {
116
- logs: string[];
117
- count: number;
118
- start: number;
119
- end: number;
120
- }
121
- /**
122
- * Overview stats for dashboard
123
- */
124
- interface OverviewStats {
125
- totalJobs: number;
126
- activeJobs: number;
127
- failedJobs: number;
128
- completedToday: number;
129
- avgDuration: number;
130
- queues: QueueInfo[];
131
- }
132
- /**
133
- * Paginated response wrapper
134
- */
135
- interface PaginatedResponse<T> {
136
- data: T[];
137
- total: number;
138
- cursor?: string;
139
- hasMore: boolean;
140
- }
141
- /**
142
- * Search result item
143
- */
144
- interface SearchResult {
145
- queue: string;
146
- job: JobInfo;
147
- }
148
- /**
149
- * Run item - job execution with queue context
150
- */
151
- interface RunInfo extends JobInfo {
152
- queueName: string;
153
- }
154
- /**
155
- * Lightweight run info for list view - only fields needed for table display
156
- * Excludes large fields like full job.data, opts, progress, etc.
157
- */
158
- interface RunInfoList {
159
- id: string;
160
- name: string;
161
- status: JobStatus;
162
- queueName: string;
163
- tags?: JobTags;
164
- processedOn?: number;
165
- timestamp: number;
166
- duration?: number;
167
- }
168
- /**
169
- * Scheduler info for repeatable jobs
170
- */
171
- interface SchedulerInfo {
172
- key: string;
173
- name: string;
174
- queueName: string;
175
- pattern?: string;
176
- every?: number;
177
- next?: number;
178
- endDate?: number;
179
- tz?: string;
180
- }
181
- /**
182
- * Delayed job info
183
- */
184
- interface DelayedJobInfo {
185
- id: string;
186
- name: string;
187
- queueName: string;
188
- delay: number;
189
- processAt: number;
190
- data: unknown;
191
- }
192
- /**
193
- * Test job request
194
- */
195
- interface TestJobRequest {
196
- queueName: string;
197
- jobName: string;
198
- data: unknown;
199
- opts?: {
200
- delay?: number;
201
- priority?: number;
202
- attempts?: number;
203
- };
204
- }
205
- /**
206
- * Sort direction
207
- */
208
- type SortDirection = "asc" | "desc";
209
- /**
210
- * Sort options for API requests
211
- */
212
- interface SortOptions {
213
- field: string;
214
- direction: SortDirection;
215
- }
216
- /**
217
- * Valid sort fields for runs/jobs
218
- */
219
- type RunSortField = "timestamp" | "name" | "status" | "duration" | "queueName";
220
- /**
221
- * Valid sort fields for repeatable schedulers
222
- */
223
- type RepeatableSortField = "name" | "queueName" | "pattern" | "next" | "tz";
224
- /**
225
- * Valid sort fields for delayed schedulers
226
- */
227
- type DelayedSortField = "name" | "queueName" | "processAt" | "delay";
228
- /**
229
- * Hourly bucket for metrics aggregation
230
- */
231
- interface HourlyBucket {
232
- /** Unix timestamp (start of hour) */
233
- hour: number;
234
- /** Number of completed jobs */
235
- completed: number;
236
- /** Number of failed jobs */
237
- failed: number;
238
- /** Average processing duration in ms */
239
- avgDuration: number;
240
- /** Average queue wait time in ms */
241
- avgWaitTime: number;
242
- }
243
- /**
244
- * Metrics for a single queue
245
- */
246
- interface QueueMetrics {
247
- queueName: string;
248
- buckets: HourlyBucket[];
249
- summary: {
250
- totalCompleted: number;
251
- totalFailed: number;
252
- /** Error rate as 0-1 */
253
- errorRate: number;
254
- /** Average processing duration in ms */
255
- avgDuration: number;
256
- /** Average queue wait time in ms */
257
- avgWaitTime: number;
258
- /** Average throughput per hour */
259
- throughputPerHour: number;
260
- };
261
- }
262
- /**
263
- * Slowest job entry
264
- */
265
- interface SlowestJob {
266
- name: string;
267
- queueName: string;
268
- duration: number;
269
- jobId: string;
270
- }
271
- /**
272
- * Most failing job type entry
273
- */
274
- interface FailingJobType {
275
- name: string;
276
- queueName: string;
277
- failCount: number;
278
- totalCount: number;
279
- errorRate: number;
280
- }
281
- /**
282
- * Complete metrics response
283
- */
284
- interface MetricsResponse {
285
- /** Metrics per queue */
286
- queues: QueueMetrics[];
287
- /** Aggregated metrics across all queues */
288
- aggregate: Omit<QueueMetrics, "queueName"> & {
289
- queueName: "all";
290
- };
291
- /** Top 10 slowest jobs */
292
- slowestJobs: SlowestJob[];
293
- /** Top 10 most failing job types */
294
- mostFailingTypes: FailingJobType[];
295
- /** Timestamp when metrics were computed */
296
- computedAt: number;
297
- }
298
- /**
299
- * A node in a flow tree representing a job and its children
300
- */
301
- interface FlowNode {
302
- job: JobInfo;
303
- queueName: string;
304
- children?: FlowNode[];
305
- }
306
- /**
307
- * Flow summary for list view
308
- */
309
- interface FlowSummary {
310
- /** Root job ID */
311
- id: string;
312
- /** Root job name */
313
- name: string;
314
- /** Queue containing root job */
315
- queueName: string;
316
- /** Root job status */
317
- status: JobStatus;
318
- /** Total number of jobs in flow */
319
- totalJobs: number;
320
- /** Number of completed jobs */
321
- completedJobs: number;
322
- /** Number of failed jobs */
323
- failedJobs: number;
324
- /** When flow was created */
325
- timestamp: number;
326
- /** Duration if completed */
327
- duration?: number;
328
- }
329
- /**
330
- * Request to create a test flow
331
- */
332
- interface CreateFlowRequest {
333
- name: string;
334
- queueName: string;
335
- data?: unknown;
336
- children: CreateFlowChildRequest[];
337
- }
338
- /**
339
- * Child job in a flow creation request
340
- */
341
- interface CreateFlowChildRequest {
342
- name: string;
343
- queueName: string;
344
- data?: unknown;
345
- children?: CreateFlowChildRequest[];
346
- }
347
- /**
348
- * Activity bucket for timeline
349
- */
350
- interface ActivityBucket {
351
- /** Unix timestamp (start of bucket) */
352
- time: number;
353
- /** Number of completed jobs */
354
- completed: number;
355
- /** Number of failed jobs */
356
- failed: number;
357
- }
358
- /**
359
- * Activity stats response for the 7-day timeline
360
- */
361
- interface ActivityStatsResponse {
362
- /** Activity buckets (4-hour intervals over 7 days) */
363
- buckets: ActivityBucket[];
364
- /** Start time of the first bucket */
365
- startTime: number;
366
- /** End time (now) */
367
- endTime: number;
368
- /** Size of each bucket in ms */
369
- bucketSize: number;
370
- /** Total completed in period */
371
- totalCompleted: number;
372
- /** Total failed in period */
373
- totalFailed: number;
374
- /** Timestamp when stats were computed */
375
- computedAt: number;
376
- }
1
+ import { Queue } from 'bullmq';
2
+ import { Q as QueueGroup, i as QueueGroupInfo, j as QueueInfo, O as OverviewStats, M as MetricsResponse, a as ActivityStatsResponse, g as JobStatus, r as SortOptions, P as PaginatedResponse, J as JobInfo, f as JobLogsResponse, o as SearchResult, m as RunInfoList, S as SchedulerInfo, D as DelayedJobInfo, T as TestJobRequest, e as FlowSummary, d as FlowNode, b as CreateFlowRequest, W as WorkbenchOptions } from './types-DhcUr9Xm.js';
3
+ export { A as ActivityBucket, C as CreateFlowChildRequest, c as DelayedSortField, F as FailingJobType, H as HourlyBucket, h as JobTags, k as QueueMetrics, R as RepeatableSortField, l as RunInfo, n as RunSortField, p as SlowestJob, q as SortDirection, s as WorkerInfo } from './types-DhcUr9Xm.js';
377
4
 
378
5
  /**
379
6
  * Manages queue operations for the Workbench dashboard
@@ -690,39 +317,6 @@ declare class WorkbenchCore {
690
317
  };
691
318
  }
692
319
 
693
- interface FetchHandlerResult {
694
- /**
695
- * Web-standard fetch handler. Accepts a `Request` and returns a `Response`.
696
- * Suitable for Elysia's `.mount(path, handler)`, Next.js route handlers,
697
- * Bun.serve, and any other web-standards-friendly runtime.
698
- */
699
- fetch: (req: Request) => Promise<Response>;
700
- /**
701
- * The underlying `WorkbenchCore` instance. Exposed so adapters can read
702
- * config, query state, or wire up custom auth strategies.
703
- */
704
- core: WorkbenchCore;
705
- }
706
- /**
707
- * Build a self-contained web-fetch handler for Workbench: API routes,
708
- * `/config`, static `/assets/:file`, an `index.html` catch-all with a
709
- * correct `<base href>`, CORS on `/api/*`, and optional Basic Auth on
710
- * everything.
711
- *
712
- * This is the engine shared by every fetch-native adapter (Elysia, Next.js).
713
- * Express and Fastify adapters use {@link buildRouteTable} directly instead.
714
- *
715
- * When `options.basePath` is set, the handler rewrites the incoming Request
716
- * URL to strip that prefix before routing. This makes the bridge work
717
- * uniformly for both fetch hosts:
718
- *
719
- * - `Elysia.mount()` already strips the prefix before calling us — the
720
- * strip below is a no-op in that case.
721
- * - Next.js App Router preserves the full path — the strip is what lets
722
- * our internal routes (`/api/*`, `/config`, …) match.
723
- */
724
- declare function createFetchHandler(options: WorkbenchOptions | Queue[]): FetchHandlerResult;
725
-
726
320
  /**
727
321
  * Framework-agnostic HTTP method.
728
322
  */
@@ -768,16 +362,6 @@ interface RouteDef {
768
362
  */
769
363
  declare function buildRouteTable(core: WorkbenchCore): RouteDef[];
770
364
 
771
- /**
772
- * Create API routes for Workbench as a Hono app.
773
- *
774
- * Iterates the framework-agnostic `buildRouteTable(core)` and registers
775
- * each route on a fresh Hono instance. Adapters that don't speak Hono can
776
- * use `buildRouteTable` directly — see `@maydotinc/q-studio/express` and
777
- * `@maydotinc/q-studio-fastify`.
778
- */
779
- declare function createApiRoutes(core: WorkbenchCore): Hono;
780
-
781
365
  declare function computeBasePath(pathname: string): string;
782
366
  /**
783
367
  * Resolve the dashboard's base path, preferring an explicit override.
@@ -813,21 +397,6 @@ declare const BASIC_AUTH_CHALLENGE: {
813
397
  body: string;
814
398
  };
815
399
 
816
- /**
817
- * Build a fully-wired Hono app for Workbench:
818
- *
819
- * - `POST /api/*`, `GET /api/*` etc. — JSON API
820
- * - `GET /config` — UI bootstrap config
821
- * - `GET /assets/:file` — static asset reader
822
- * - `GET *` — `index.html` with `<base href>`
823
- * - CORS on `/api/*`
824
- * - Basic auth on everything when `core.requiresAuth()` is true
825
- *
826
- * Used directly by `@maydotinc/q-studio-hono` (returned as-is for `.route()`
827
- * mounting) and indirectly by `createFetchHandler` for non-Hono adapters.
828
- */
829
- declare function buildWorkbenchApp(core: WorkbenchCore): Hono;
830
-
831
400
  interface StaticAssetResult {
832
401
  status: 200 | 404;
833
402
  body: Buffer | null;
@@ -861,4 +430,4 @@ declare function renderIndexHtml(basePath: string, title: string): IndexHtmlResu
861
430
  */
862
431
  declare const UI_DIST_PATH: string;
863
432
 
864
- export { type ActivityBucket, type ActivityStatsResponse, BASIC_AUTH_CHALLENGE, type CreateFlowChildRequest, type CreateFlowRequest, type DelayedJobInfo, type DelayedSortField, type FailingJobType, type FetchHandlerResult, type FlowNode, type FlowSummary, type Handler, type HandlerInput, type HandlerResult, type HourlyBucket, type HttpMethod, type IndexHtmlResult, type JobInfo, type JobStatus, type JobTags, type MetricsResponse, type OverviewStats, type PaginatedResponse, type QueueGroup, type QueueGroupInfo, type QueueInfo, QueueManager, type QueueMetrics, type RepeatableSortField, type RouteDef, type RunInfo, type RunInfoList, type RunSortField, type SchedulerInfo, type SearchResult, type SlowestJob, type SortDirection, type SortOptions, type StaticAssetResult, type TestJobRequest, UI_DIST_PATH, WorkbenchCore, type WorkbenchOptions, type WorkerInfo, buildRouteTable, buildWorkbenchApp, checkBasicAuth, computeBasePath, createApiRoutes, createFetchHandler, renderIndexHtml, resolveBasePath, serveStaticAsset };
433
+ export { ActivityStatsResponse, BASIC_AUTH_CHALLENGE, CreateFlowRequest, DelayedJobInfo, FlowNode, FlowSummary, type Handler, type HandlerInput, type HandlerResult, type HttpMethod, type IndexHtmlResult, JobInfo, JobStatus, MetricsResponse, OverviewStats, PaginatedResponse, QueueGroup, QueueGroupInfo, QueueInfo, QueueManager, type RouteDef, RunInfoList, SchedulerInfo, SearchResult, SortOptions, type StaticAssetResult, TestJobRequest, UI_DIST_PATH, WorkbenchCore, WorkbenchOptions, buildRouteTable, checkBasicAuth, computeBasePath, renderIndexHtml, resolveBasePath, serveStaticAsset };
package/dist/core.js ADDED
@@ -0,0 +1,25 @@
1
+ import {
2
+ BASIC_AUTH_CHALLENGE,
3
+ QueueManager,
4
+ UI_DIST_PATH,
5
+ WorkbenchCore,
6
+ buildRouteTable,
7
+ checkBasicAuth,
8
+ computeBasePath,
9
+ renderIndexHtml,
10
+ resolveBasePath,
11
+ serveStaticAsset
12
+ } from "./chunk-L36RXNVW.js";
13
+ export {
14
+ BASIC_AUTH_CHALLENGE,
15
+ QueueManager,
16
+ UI_DIST_PATH,
17
+ WorkbenchCore,
18
+ buildRouteTable,
19
+ checkBasicAuth,
20
+ computeBasePath,
21
+ renderIndexHtml,
22
+ resolveBasePath,
23
+ serveStaticAsset
24
+ };
25
+ //# sourceMappingURL=core.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,7 @@
1
+ import { Queue } from 'bullmq';
2
+ import { W as WorkbenchOptions } from './types-DhcUr9Xm.js';
3
+ import { Router } from 'express';
4
+
5
+ declare function qStudio(options: WorkbenchOptions | Queue[]): Router;
6
+
7
+ export { WorkbenchOptions, qStudio };
@@ -1,13 +1,14 @@
1
- // src/index.ts
2
1
  import {
3
2
  BASIC_AUTH_CHALLENGE,
3
+ WorkbenchCore,
4
4
  buildRouteTable,
5
5
  checkBasicAuth,
6
6
  renderIndexHtml,
7
7
  resolveBasePath,
8
- serveStaticAsset,
9
- WorkbenchCore
10
- } from "@maydotinc/q-studio/core";
8
+ serveStaticAsset
9
+ } from "./chunk-L36RXNVW.js";
10
+
11
+ // src/index.ts
11
12
  import express from "express";
12
13
  function qStudio(options) {
13
14
  const core = new WorkbenchCore(options);
@@ -52,7 +53,7 @@ function qStudio(options) {
52
53
  router.get("/config", (_req, res) => {
53
54
  res.json(core.getConfig());
54
55
  });
55
- router.get("/favicon.ico", (req, res) => {
56
+ router.get("/favicon.ico", (_req, res) => {
56
57
  const asset = serveStaticAsset("favicon.ico");
57
58
  if (asset.status === 404 || !asset.body) {
58
59
  res.status(404).type("text/plain").send("Not found");
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["import {\n BASIC_AUTH_CHALLENGE,\n buildRouteTable,\n checkBasicAuth,\n renderIndexHtml,\n resolveBasePath,\n serveStaticAsset,\n WorkbenchCore,\n type WorkbenchOptions,\n} from \"./core\";\nimport type { Queue } from \"bullmq\";\nimport express, { type Router } from \"express\";\n\nexport function qStudio(options: WorkbenchOptions | Queue[]): Router {\n const core = new WorkbenchCore(options);\n const router = express.Router();\n\n if (core.requiresAuth()) {\n router.use((req, res, next) => {\n if (\n !checkBasicAuth(\n req.headers.authorization,\n core.options.auth!.username,\n core.options.auth!.password,\n )\n ) {\n res.set(BASIC_AUTH_CHALLENGE.headers);\n res.status(BASIC_AUTH_CHALLENGE.status).send(BASIC_AUTH_CHALLENGE.body);\n return;\n }\n next();\n });\n }\n\n router.use(\"/api\", (_req, res, next) => {\n res.set(\"Access-Control-Allow-Origin\", \"*\");\n res.set(\"Access-Control-Allow-Methods\", \"GET,HEAD,PUT,PATCH,POST,DELETE\");\n res.set(\"Access-Control-Allow-Headers\", \"*\");\n next();\n });\n\n router.use(\"/api\", express.json());\n\n for (const route of buildRouteTable(core)) {\n router[route.method](`/api${route.path}`, async (req, res) => {\n try {\n const result = await route.handler({\n params: req.params as Record<string, string>,\n query: req.query as Record<string, string | undefined>,\n body: req.body,\n });\n res.status(result.status).json(result.body);\n } catch (error) {\n res.status(500).json({\n error:\n error instanceof Error ? error.message : \"Internal server error\",\n });\n }\n });\n }\n\n router.get(\"/config\", (_req, res) => {\n res.json(core.getConfig());\n });\n\n router.get(\"/favicon.ico\", (_req, res) => {\n const asset = serveStaticAsset(\"favicon.ico\");\n if (asset.status === 404 || !asset.body) {\n res.status(404).type(\"text/plain\").send(\"Not found\");\n return;\n }\n\n res.status(200).type(asset.contentType).send(asset.body);\n });\n\n router.get(\"/assets/:file\", (req, res) => {\n const asset = serveStaticAsset(req.params.file as string);\n if (asset.status === 404 || !asset.body) {\n res.status(404).type(\"text/plain\").send(\"Not found\");\n return;\n }\n res.status(200).type(asset.contentType).send(asset.body);\n });\n\n router.use((req, res, next) => {\n if (req.method !== \"GET\") {\n next();\n return;\n }\n const pathname = (req.originalUrl ?? req.url).split(\"?\")[0] ?? \"/\";\n const basePath = resolveBasePath(core.options.basePath, pathname);\n const html = renderIndexHtml(basePath, core.options.title || \"Workbench\");\n res.status(200).type(\"text/html; charset=utf-8\").send(html.body);\n });\n\n return router;\n}\n\nexport type { WorkbenchOptions } from \"./core\";\n"],"mappings":";;;;;;;;;;;AAWA,OAAO,aAA8B;AAE9B,SAAS,QAAQ,SAA6C;AACnE,QAAM,OAAO,IAAI,cAAc,OAAO;AACtC,QAAM,SAAS,QAAQ,OAAO;AAE9B,MAAI,KAAK,aAAa,GAAG;AACvB,WAAO,IAAI,CAAC,KAAK,KAAK,SAAS;AAC7B,UACE,CAAC;AAAA,QACC,IAAI,QAAQ;AAAA,QACZ,KAAK,QAAQ,KAAM;AAAA,QACnB,KAAK,QAAQ,KAAM;AAAA,MACrB,GACA;AACA,YAAI,IAAI,qBAAqB,OAAO;AACpC,YAAI,OAAO,qBAAqB,MAAM,EAAE,KAAK,qBAAqB,IAAI;AACtE;AAAA,MACF;AACA,WAAK;AAAA,IACP,CAAC;AAAA,EACH;AAEA,SAAO,IAAI,QAAQ,CAAC,MAAM,KAAK,SAAS;AACtC,QAAI,IAAI,+BAA+B,GAAG;AAC1C,QAAI,IAAI,gCAAgC,gCAAgC;AACxE,QAAI,IAAI,gCAAgC,GAAG;AAC3C,SAAK;AAAA,EACP,CAAC;AAED,SAAO,IAAI,QAAQ,QAAQ,KAAK,CAAC;AAEjC,aAAW,SAAS,gBAAgB,IAAI,GAAG;AACzC,WAAO,MAAM,MAAM,EAAE,OAAO,MAAM,IAAI,IAAI,OAAO,KAAK,QAAQ;AAC5D,UAAI;AACF,cAAM,SAAS,MAAM,MAAM,QAAQ;AAAA,UACjC,QAAQ,IAAI;AAAA,UACZ,OAAO,IAAI;AAAA,UACX,MAAM,IAAI;AAAA,QACZ,CAAC;AACD,YAAI,OAAO,OAAO,MAAM,EAAE,KAAK,OAAO,IAAI;AAAA,MAC5C,SAAS,OAAO;AACd,YAAI,OAAO,GAAG,EAAE,KAAK;AAAA,UACnB,OACE,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QAC7C,CAAC;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO,IAAI,WAAW,CAAC,MAAM,QAAQ;AACnC,QAAI,KAAK,KAAK,UAAU,CAAC;AAAA,EAC3B,CAAC;AAED,SAAO,IAAI,gBAAgB,CAAC,MAAM,QAAQ;AACxC,UAAM,QAAQ,iBAAiB,aAAa;AAC5C,QAAI,MAAM,WAAW,OAAO,CAAC,MAAM,MAAM;AACvC,UAAI,OAAO,GAAG,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW;AACnD;AAAA,IACF;AAEA,QAAI,OAAO,GAAG,EAAE,KAAK,MAAM,WAAW,EAAE,KAAK,MAAM,IAAI;AAAA,EACzD,CAAC;AAED,SAAO,IAAI,iBAAiB,CAAC,KAAK,QAAQ;AACxC,UAAM,QAAQ,iBAAiB,IAAI,OAAO,IAAc;AACxD,QAAI,MAAM,WAAW,OAAO,CAAC,MAAM,MAAM;AACvC,UAAI,OAAO,GAAG,EAAE,KAAK,YAAY,EAAE,KAAK,WAAW;AACnD;AAAA,IACF;AACA,QAAI,OAAO,GAAG,EAAE,KAAK,MAAM,WAAW,EAAE,KAAK,MAAM,IAAI;AAAA,EACzD,CAAC;AAED,SAAO,IAAI,CAAC,KAAK,KAAK,SAAS;AAC7B,QAAI,IAAI,WAAW,OAAO;AACxB,WAAK;AACL;AAAA,IACF;AACA,UAAM,YAAY,IAAI,eAAe,IAAI,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK;AAC/D,UAAM,WAAW,gBAAgB,KAAK,QAAQ,UAAU,QAAQ;AAChE,UAAM,OAAO,gBAAgB,UAAU,KAAK,QAAQ,SAAS,WAAW;AACxE,QAAI,OAAO,GAAG,EAAE,KAAK,0BAA0B,EAAE,KAAK,KAAK,IAAI;AAAA,EACjE,CAAC;AAED,SAAO;AACT;","names":[]}