@capixjs/core 0.1.0-alpha.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.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +329 -0
  3. package/dist/capability.d.ts +165 -0
  4. package/dist/capability.d.ts.map +1 -0
  5. package/dist/capability.js +268 -0
  6. package/dist/capability.js.map +1 -0
  7. package/dist/context.d.ts +35 -0
  8. package/dist/context.d.ts.map +1 -0
  9. package/dist/context.js +28 -0
  10. package/dist/context.js.map +1 -0
  11. package/dist/enhancers.d.ts +92 -0
  12. package/dist/enhancers.d.ts.map +1 -0
  13. package/dist/enhancers.js +251 -0
  14. package/dist/enhancers.js.map +1 -0
  15. package/dist/errors.d.ts +53 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +74 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/event-bus.d.ts +56 -0
  20. package/dist/event-bus.d.ts.map +1 -0
  21. package/dist/event-bus.js +51 -0
  22. package/dist/event-bus.js.map +1 -0
  23. package/dist/execution-engine.d.ts +39 -0
  24. package/dist/execution-engine.d.ts.map +1 -0
  25. package/dist/execution-engine.js +210 -0
  26. package/dist/execution-engine.js.map +1 -0
  27. package/dist/guards.d.ts +78 -0
  28. package/dist/guards.d.ts.map +1 -0
  29. package/dist/guards.js +56 -0
  30. package/dist/guards.js.map +1 -0
  31. package/dist/index.d.ts +22 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +22 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/plugin.d.ts +28 -0
  36. package/dist/plugin.d.ts.map +1 -0
  37. package/dist/plugin.js +43 -0
  38. package/dist/plugin.js.map +1 -0
  39. package/dist/server.d.ts +70 -0
  40. package/dist/server.d.ts.map +1 -0
  41. package/dist/server.js +97 -0
  42. package/dist/server.js.map +1 -0
  43. package/dist/type-tests.d.ts +12 -0
  44. package/dist/type-tests.d.ts.map +1 -0
  45. package/dist/type-tests.js +175 -0
  46. package/dist/type-tests.js.map +1 -0
  47. package/package.json +47 -0
@@ -0,0 +1,268 @@
1
+ /**
2
+ * capability.ts — the core capability primitive
3
+ * Depends on: errors.ts, context.ts, guards.ts, zod
4
+ */
5
+ import { runInputGuards } from './guards.js';
6
+ const CAPABILITY_BRAND = Symbol.for('capix.Capability');
7
+ const QUERY_PREFIXES = ['get', 'find', 'fetch', 'read', 'list', 'search', 'filter', 'me', 'status', 'health', 'count', 'check'];
8
+ const MUTATION_PREFIXES = ['create', 'add', 'new'];
9
+ const UPDATE_PREFIXES = ['update', 'edit', 'patch', 'modify'];
10
+ const REPLACE_PREFIXES = ['replace', 'set', 'put'];
11
+ const DELETE_PREFIXES = ['delete', 'remove', 'destroy', 'cancel'];
12
+ /** Infers capability intent from its key name in the parent group. */
13
+ export function inferIntent(key) {
14
+ const lower = key.toLowerCase();
15
+ for (const p of QUERY_PREFIXES) {
16
+ if (lower.startsWith(p))
17
+ return 'query';
18
+ }
19
+ for (const p of MUTATION_PREFIXES) {
20
+ if (lower.startsWith(p))
21
+ return 'mutation';
22
+ }
23
+ for (const p of UPDATE_PREFIXES) {
24
+ if (lower.startsWith(p))
25
+ return 'update';
26
+ }
27
+ for (const p of REPLACE_PREFIXES) {
28
+ if (lower.startsWith(p))
29
+ return 'replace';
30
+ }
31
+ for (const p of DELETE_PREFIXES) {
32
+ if (lower.startsWith(p))
33
+ return 'delete';
34
+ }
35
+ return 'mutation';
36
+ }
37
+ function makeCapability(base) {
38
+ const cap = base;
39
+ const rawFn = base._fn;
40
+ // Attach methods as non-enumerable properties so spread in chaining doesn't copy them,
41
+ // and so enhancers returning { ...cap, resolve: wrappedFn } can set a plain enumerable
42
+ // resolve that enhance() picks up as the new _fn.
43
+ Object.defineProperties(base, {
44
+ // _resolverOnly: raw resolver, no guards. Used by the execution engine (guards already ran).
45
+ // Also called by enhancers to wrap the resolver without re-running guards.
46
+ _resolverOnly: {
47
+ value: rawFn,
48
+ writable: false,
49
+ enumerable: false,
50
+ configurable: false,
51
+ },
52
+ // resolve: runs all guards, then calls the raw resolver.
53
+ // Accepts BaseContext — safe for composition from any calling context.
54
+ // Non-async fast path when no guards: avoids extra microtask tick so fake-timer
55
+ // tests and hot-path callers see no overhead vs the old direct-fn assignment.
56
+ resolve: {
57
+ value: (input, ctx) => {
58
+ if (base.guards.length === 0 && base.inputGuards.length === 0) {
59
+ const r = rawFn(input, ctx);
60
+ return (r instanceof Promise ? r : Promise.resolve(r));
61
+ }
62
+ const run = async () => {
63
+ for (const guard of base.guards) {
64
+ const gr = guard(ctx);
65
+ if (gr !== undefined && gr !== null && typeof gr.then === 'function') {
66
+ await gr;
67
+ }
68
+ }
69
+ if (base.inputGuards.length > 0) {
70
+ await runInputGuards(base.inputGuards, input, ctx);
71
+ }
72
+ return rawFn(input, ctx);
73
+ };
74
+ return run();
75
+ },
76
+ writable: false,
77
+ enumerable: false,
78
+ configurable: false,
79
+ },
80
+ guard: {
81
+ value(g) {
82
+ return makeCapability({
83
+ ...base,
84
+ guards: [...base.guards, g],
85
+ });
86
+ },
87
+ writable: false,
88
+ enumerable: false,
89
+ configurable: false,
90
+ },
91
+ inputGuard: {
92
+ value(g) {
93
+ return makeCapability({
94
+ ...base,
95
+ inputGuards: [...base.inputGuards, g],
96
+ });
97
+ },
98
+ writable: false,
99
+ enumerable: false,
100
+ configurable: false,
101
+ },
102
+ enhance: {
103
+ value(e) {
104
+ const enhanced = e(cap);
105
+ // enhanced.resolve is the plain enumerable property set by the enhancer:
106
+ // (cap) => ({ ...cap, resolve: wrappedFn })
107
+ // This wrappedFn calls cap._resolverOnly internally, so guards don't double-run.
108
+ // We use it as the new raw resolver for the returned capability.
109
+ return makeCapability({
110
+ ...base,
111
+ _fn: enhanced.resolve,
112
+ });
113
+ },
114
+ writable: false,
115
+ enumerable: false,
116
+ configurable: false,
117
+ },
118
+ output: {
119
+ value(schema) {
120
+ return makeCapability({
121
+ ...base,
122
+ outputSchema: schema,
123
+ });
124
+ },
125
+ writable: false,
126
+ enumerable: false,
127
+ configurable: false,
128
+ },
129
+ });
130
+ return cap;
131
+ }
132
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
133
+ export function capability(...args) {
134
+ const [first, second, thirdArg] = args;
135
+ const isZodSchema = first !== null &&
136
+ typeof first === 'object' &&
137
+ '_def' in first;
138
+ if (!isZodSchema) {
139
+ // No-schema overloads: capability(resolver) | capability(resolver, intent)
140
+ const explicitIntent = typeof second === 'string' ? second : undefined;
141
+ return makeCapability({
142
+ _capix: true,
143
+ [CAPABILITY_BRAND]: true,
144
+ name: '(unnamed)',
145
+ _input: undefined,
146
+ _output: undefined,
147
+ _context: undefined,
148
+ inputSchema: null,
149
+ outputSchema: null,
150
+ guards: [],
151
+ inputGuards: [],
152
+ _intentExplicit: explicitIntent !== undefined,
153
+ intent: explicitIntent ?? 'mutation',
154
+ _skipValidation: false,
155
+ _fn: first,
156
+ });
157
+ }
158
+ // Schema overloads: capability(schema, resolver) | capability(schema, resolver, intent)
159
+ const thirdIntent = typeof thirdArg === 'string' ? thirdArg : undefined;
160
+ return makeCapability({
161
+ _capix: true,
162
+ [CAPABILITY_BRAND]: true,
163
+ name: '(unnamed)',
164
+ _input: undefined,
165
+ _output: undefined,
166
+ _context: undefined,
167
+ inputSchema: first,
168
+ outputSchema: null,
169
+ guards: [],
170
+ inputGuards: [],
171
+ _intentExplicit: thirdIntent !== undefined,
172
+ intent: thirdIntent ?? 'mutation',
173
+ _skipValidation: false,
174
+ _fn: second,
175
+ });
176
+ }
177
+ // ---------------------------------------------------------------------------
178
+ // isCapability
179
+ // ---------------------------------------------------------------------------
180
+ /** Returns true for values created by capability(). Plain objects with _capix: true return false. */
181
+ export function isCapability(value) {
182
+ return (typeof value === 'object' &&
183
+ value !== null &&
184
+ value[CAPABILITY_BRAND] === true);
185
+ }
186
+ /**
187
+ * Flattens a nested capability tree into a registry Map keyed by dot-path names.
188
+ *
189
+ * `createServer` calls this automatically — use it directly only when building
190
+ * custom transports or tooling that needs the registry before the server starts.
191
+ *
192
+ * Each capability is named from its path in the tree (`users.getUser`, `posts.create`).
193
+ * Capabilities with an empty `z.object({})` input schema have `_skipValidation` set so
194
+ * the execution engine bypasses Zod entirely for those routes.
195
+ *
196
+ * @throws {Error} If any key is not a camelCase identifier (letters and digits only,
197
+ * must start with a letter). Dashes, underscores, and leading digits are rejected.
198
+ */
199
+ const VALID_KEY = /^[a-zA-Z][a-zA-Z0-9]*$/;
200
+ export function compileRegistry(tree, prefix = '') {
201
+ const map = new Map();
202
+ for (const [key, value] of Object.entries(tree)) {
203
+ const path = prefix ? `${prefix}.${key}` : key;
204
+ if (!VALID_KEY.test(key)) {
205
+ throw new Error(`[capix] Invalid capability key: "${key}" at path "${path}". ` +
206
+ `Keys must be camelCase identifiers (letters and numbers only, ` +
207
+ `starting with a letter). Examples: getUser, listOrders, createPost.`);
208
+ }
209
+ if (isCapability(value)) {
210
+ // True when inputSchema is z.object({}) — execution engine skips safeParse entirely.
211
+ const skipVal = value.inputSchema !== null &&
212
+ 'shape' in value.inputSchema &&
213
+ typeof value.inputSchema.shape === 'object' &&
214
+ value.inputSchema.shape !== null &&
215
+ Object.keys(value.inputSchema.shape).length === 0;
216
+ // Create a named copy by building a fresh capability base with the correct name.
217
+ // _resolverOnly is the raw resolver (= base._fn on the original capability).
218
+ const base = {
219
+ _capix: true,
220
+ [CAPABILITY_BRAND]: true,
221
+ name: path,
222
+ _input: undefined,
223
+ _output: undefined,
224
+ _context: undefined,
225
+ inputSchema: value.inputSchema,
226
+ outputSchema: value.outputSchema,
227
+ guards: value.guards,
228
+ inputGuards: value.inputGuards,
229
+ intent: value.intent,
230
+ _intentExplicit: value._intentExplicit,
231
+ _skipValidation: skipVal,
232
+ // _resolverOnly === base._fn on the original capability (direct reference, no wrapping).
233
+ _fn: value._resolverOnly,
234
+ };
235
+ map.set(path, makeCapability(base));
236
+ }
237
+ else {
238
+ const nested = compileRegistry(value, path);
239
+ for (const [nestedPath, cap] of nested) {
240
+ map.set(nestedPath, cap);
241
+ }
242
+ }
243
+ }
244
+ return map;
245
+ }
246
+ // eslint-disable-next-line @typescript-eslint/no-namespace
247
+ (function (capability) {
248
+ /**
249
+ * Returns a `capability()` factory with TContext pre-bound.
250
+ * The resolver's `ctx` parameter is inferred as TContext without annotation,
251
+ * and guards typed for TContext are accepted without the `any` escape hatch.
252
+ *
253
+ * @example
254
+ * const appCap = capability.withContext<AppContext>();
255
+ * const getUser = appCap(
256
+ * z.object({ id: z.string() }),
257
+ * async (input, ctx) => { // ctx: AppContext — no annotation needed
258
+ * if (!ctx.user) throw Errors.Unauthorized();
259
+ * return db.users.find(input.id);
260
+ * },
261
+ * ).guard(mustBeAuthenticated);
262
+ */
263
+ function withContext() {
264
+ return capability;
265
+ }
266
+ capability.withContext = withContext;
267
+ })(capability || (capability = {}));
268
+ //# sourceMappingURL=capability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capability.js","sourceRoot":"","sources":["../src/capability.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * context.ts — request context types and builder
3
+ * No dependencies.
4
+ */
5
+ /** Raw request data passed to buildContext. */
6
+ export type RawRequest = {
7
+ readonly headers: Record<string, string | string[] | undefined>;
8
+ readonly method: string;
9
+ readonly url: string;
10
+ readonly signal: AbortSignal;
11
+ /**
12
+ * Raw request body bytes, populated by the transport when a body is present.
13
+ * Useful for HMAC webhook signature verification where re-serializing parsed
14
+ * JSON is not safe (key order and whitespace may differ).
15
+ * Undefined for GET/HEAD requests and empty-body requests.
16
+ */
17
+ readonly rawBody?: Buffer;
18
+ };
19
+ /** Minimum fields guaranteed on every context object. */
20
+ export type BaseContext = {
21
+ readonly requestId: string;
22
+ };
23
+ /** A function that builds the application context from a raw request. */
24
+ export type ContextBuilder<TContext extends BaseContext = BaseContext> = (req: RawRequest) => TContext | Promise<TContext>;
25
+ /**
26
+ * Pass-through function for type inference and readability.
27
+ * Use this to define your buildContext function with full type checking.
28
+ */
29
+ export declare function defineContext<TContext extends BaseContext>(builder: ContextBuilder<TContext>): ContextBuilder<TContext>;
30
+ /**
31
+ * Case-insensitive header lookup.
32
+ * Returns the first value if the header is an array.
33
+ */
34
+ export declare function getHeader(req: RawRequest, name: string): string | undefined;
35
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,+CAA+C;AAC/C,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IAChE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,yDAAyD;AACzD,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,yEAAyE;AACzE,MAAM,MAAM,cAAc,CAAC,QAAQ,SAAS,WAAW,GAAG,WAAW,IAAI,CACvE,GAAG,EAAE,UAAU,KACZ,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAElC;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,SAAS,WAAW,EACxD,OAAO,EAAE,cAAc,CAAC,QAAQ,CAAC,GAChC,cAAc,CAAC,QAAQ,CAAC,CAE1B;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAU3E"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * context.ts — request context types and builder
3
+ * No dependencies.
4
+ */
5
+ /**
6
+ * Pass-through function for type inference and readability.
7
+ * Use this to define your buildContext function with full type checking.
8
+ */
9
+ export function defineContext(builder) {
10
+ return builder;
11
+ }
12
+ /**
13
+ * Case-insensitive header lookup.
14
+ * Returns the first value if the header is an array.
15
+ */
16
+ export function getHeader(req, name) {
17
+ const lower = name.toLowerCase();
18
+ for (const key of Object.keys(req.headers)) {
19
+ if (key.toLowerCase() === lower) {
20
+ const val = req.headers[key];
21
+ if (val === undefined)
22
+ return undefined;
23
+ return Array.isArray(val) ? val[0] : val;
24
+ }
25
+ }
26
+ return undefined;
27
+ }
28
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA2BH;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,OAAiC;IAEjC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,GAAe,EAAE,IAAY;IACrD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3C,IAAI,GAAG,CAAC,WAAW,EAAE,KAAK,KAAK,EAAE,CAAC;YAChC,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC7B,IAAI,GAAG,KAAK,SAAS;gBAAE,OAAO,SAAS,CAAC;YACxC,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAC3C,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * enhancers.ts — built-in capability enhancers
3
+ * Depends on: capability.ts
4
+ */
5
+ import type { Enhancer } from './capability.js';
6
+ /** Pass-through for type inference. */
7
+ export declare function defineEnhancer(fn: Enhancer): Enhancer;
8
+ /** Logs capability name, duration, and outcome. Falls back to console if ctx has no logger. */
9
+ export declare const withLogging: Enhancer;
10
+ /** In-memory cache. Key = capabilityName:JSON(input). TTL in seconds. */
11
+ export declare function withCache(ttlSeconds: number): Enhancer;
12
+ /** Rejects if the resolver exceeds the given milliseconds. */
13
+ export declare function withTimeout(ms: number): Enhancer;
14
+ /** Retries on non-FrameworkError failures with exponential backoff. */
15
+ export declare function withRetry(maxAttempts: number, delayMs?: number): Enhancer;
16
+ export type RateLimitOptions = {
17
+ readonly limit: number;
18
+ readonly windowMs: number;
19
+ /**
20
+ * Derives the rate limit key from input and context.
21
+ *
22
+ * WARNING: Defaults to the capability name — a GLOBAL limit shared across all callers.
23
+ * For production use, always provide a keyFn to limit per-user or per-IP:
24
+ *
25
+ * @example Per-user rate limit
26
+ * withRateLimit({ limit: 100, windowMs: 60_000, keyFn: (_input, ctx) => (ctx as AppContext).user?.id ?? 'anon' })
27
+ *
28
+ * @example Per-IP rate limit
29
+ * withRateLimit({ limit: 10, windowMs: 60_000, keyFn: (_input, ctx) => (ctx as AppContext).ip ?? 'unknown' })
30
+ */
31
+ readonly keyFn?: (input: unknown, ctx: unknown) => string;
32
+ };
33
+ /** Sliding-window in-memory rate limiter. Throws 429 when limit exceeded. */
34
+ export declare function withRateLimit(options: RateLimitOptions): Enhancer;
35
+ export type RollbackFn = () => unknown;
36
+ /**
37
+ * Extends a context type with the `onRollback` method added by {@link withRollback}.
38
+ * Use this to type capabilities that need compensation actions.
39
+ *
40
+ * @example
41
+ * const cap = capability(schema, async (input, ctx: WithRollback<AppContext>) => {
42
+ * ctx.onRollback(() => cleanup());
43
+ * }).enhance(withRollback);
44
+ */
45
+ export type WithRollback<T> = T & {
46
+ readonly onRollback: (fn: RollbackFn) => void;
47
+ };
48
+ /**
49
+ * Adds explicit rollback support to a capability.
50
+ *
51
+ * Use ctx.onRollback(fn) to register compensation actions that run in reverse
52
+ * order if the resolver throws after one or more steps have already executed.
53
+ *
54
+ * This is NOT a database transaction. It does not provide atomicity,
55
+ * isolation, or durability. For real transactions, use your database's
56
+ * transaction API directly inside the resolver.
57
+ *
58
+ * Use this for: in-memory stores, multi-step operations where each step can
59
+ * be independently compensated (undone).
60
+ *
61
+ * @example
62
+ * export const checkout = cap(z.object({}), async (_, ctx) => {
63
+ * const order = ctx.db.orders.create({ ... });
64
+ * ctx.onRollback(() => ctx.db.orders.delete(order.id));
65
+ *
66
+ * ctx.db.inventory.decrement(item.id);
67
+ * ctx.onRollback(() => ctx.db.inventory.increment(item.id));
68
+ *
69
+ * return order;
70
+ * }).enhance(withRollback);
71
+ */
72
+ export declare const withRollback: Enhancer;
73
+ export interface MetricsCollector {
74
+ increment(name: string, tags?: Record<string, string>): void;
75
+ histogram(name: string, value: number, tags?: Record<string, string>): void;
76
+ }
77
+ /** Metrics collector that logs to console. */
78
+ export declare const consoleMetricsCollector: MetricsCollector;
79
+ /** Wraps a capability to emit duration and success/error metrics. */
80
+ export declare function withMetrics(collector: MetricsCollector): Enhancer;
81
+ export type CircuitBreakerOptions = {
82
+ readonly failureThreshold: number;
83
+ readonly successThreshold: number;
84
+ readonly timeoutMs: number;
85
+ };
86
+ /**
87
+ * Circuit breaker with closed/open/half-open states.
88
+ * State is closure-captured per capability application.
89
+ * FrameworkErrors do not count toward the failure threshold.
90
+ */
91
+ export declare function withCircuitBreaker(options: CircuitBreakerOptions): Enhancer;
92
+ //# sourceMappingURL=enhancers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enhancers.d.ts","sourceRoot":"","sources":["../src/enhancers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAiB,MAAM,iBAAiB,CAAC;AAI/D,uCAAuC;AACvC,wBAAgB,cAAc,CAAC,EAAE,EAAE,QAAQ,GAAG,QAAQ,CAErD;AAED,+FAA+F;AAC/F,eAAO,MAAM,WAAW,EAiBjB,QAAQ,CAAC;AAEhB,yEAAyE;AACzE,wBAAgB,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,CAgBtD;AAED,8DAA8D;AAC9D,wBAAgB,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,CAqBhD;AAED,uEAAuE;AACvE,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,SAAM,GAAG,QAAQ,CAoBtE;AAMD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,KAAK,MAAM,CAAC;CAC3D,CAAC;AAEF,6EAA6E;AAC7E,wBAAgB,aAAa,CAAC,OAAO,EAAE,gBAAgB,GAAG,QAAQ,CAgCjE;AAMD,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC;AAEvC;;;;;;;;GAQG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG;IAChC,QAAQ,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,UAAU,KAAK,IAAI,CAAC;CAC/C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,YAAY,EAsBlB,QAAQ,CAAC;AAMhB,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC7D,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;CAC7E;AAED,8CAA8C;AAC9C,eAAO,MAAM,uBAAuB,EAAE,gBAOrC,CAAC;AAEF,qEAAqE;AACrE,wBAAgB,WAAW,CAAC,SAAS,EAAE,gBAAgB,GAAG,QAAQ,CAkBjE;AAMD,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;AAMF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,qBAAqB,GAAG,QAAQ,CA+C3E"}