@usebetterdev/tenant-core 0.1.0 → 0.2.0-beta.12

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.
package/README.md CHANGED
@@ -1,30 +1,22 @@
1
- # @better-tenant/core
1
+ # @usebetterdev/tenant-core
2
2
 
3
- Core library for request-scoped multi-tenancy with Postgres RLS. Provides tenant context (AsyncLocalStorage), resolver strategies, adapter contract, and tenant API. No database driver dependency — use with [@usebetterdev/tenant-drizzle](https://github.com/usebetter-dev/usebetter) or a custom adapter.
3
+ Core library for request-scoped multi-tenancy with Postgres RLS. Provides tenant context (AsyncLocalStorage), resolver strategies, adapter contract, and tenant API. No database driver dependency — use with the Drizzle adapter or a custom adapter.
4
4
 
5
5
  ## Install
6
6
 
7
7
  ```bash
8
- pnpm add @better-tenant/core
8
+ pnpm add @usebetterdev/tenant
9
9
  ```
10
10
 
11
11
  ## Quick start
12
12
 
13
13
  ```ts
14
- import { betterTenant, getContext, getDatabase } from "@better-tenant/core";
15
- import { drizzleAdapter } from "better-tenant/drizzle"; // or your adapter
14
+ import { betterTenant } from "@usebetterdev/tenant";
15
+ import { drizzleDatabase } from "@usebetterdev/tenant/drizzle"; // or prismaDatabase
16
16
 
17
- const adapter = drizzleAdapter(db);
18
17
  const tenant = betterTenant({
19
- adapter,
18
+ database: drizzleDatabase(db),
20
19
  tenantResolver: { header: "x-tenant-id" },
21
- tenantTables: ["projects", "tasks"],
22
- getTenantRepository: (database) => ({ create, update, list, delete }), // adapter provides this
23
- });
24
-
25
- // In middleware: resolve tenantId (e.g. tenant.resolveTenant(request)), then:
26
- await tenant.runWithTenantAndDatabase(tenantId, adapter, async () => {
27
- await next();
28
20
  });
29
21
 
30
22
  // In handlers:
@@ -36,15 +28,16 @@ const database = tenant.getDatabase(); // tenant-scoped DB handle (use only
36
28
 
37
29
  | API | Description |
38
30
  | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
39
- | `betterTenant(config)` | Create instance with adapter, resolver, optional `getTenantRepository`. |
31
+ | `betterTenant(config)` | Create instance with `database` provider and resolver. |
40
32
  | `getContext()` | Current `TenantContext \| undefined` (tenantId, tenant?, database?). |
41
- | `getDatabase()` | Tenant-scoped database handle, or `undefined` outside request scope. Use only this for tenant-scoped tables. |
33
+ | `tenant.getDatabase()` | Tenant-scoped database handle, or `undefined` outside request scope. Use only this for tenant-scoped tables. |
42
34
  | `runWithTenant(tenantId, fn)` | Run `fn` with context only (no DB). For tests or when adapter not used. |
43
- | `runWithTenantAndDatabase(tenantId, adapter, fn)` | Set context and run `fn` with adapters tenant-scoped DB. Call from middleware. |
44
- | `resolveTenant(request, config)` / `resolveTenantAsync` | Resolve tenant id from request (header, path, subdomain, jwt, custom). |
45
- | `runAs(tenantId, adapter, fn)` | Same as request flow; use for cron or per-tenant jobs. |
35
+ | `tenant.runWithTenantAndDatabase(tenantId, fn)` | Set context and run `fn` with adapter's tenant-scoped DB. Call from middleware. |
36
+ | `resolveTenant(request, config)` | Resolve tenant id from request (header, path, subdomain, jwt, custom). Returns `Promise<string \| undefined>`. |
37
+ | `tenant.runAs(tenantId, fn)` | Same as request flow; use for cron or per-tenant jobs. |
46
38
  | `runAsSystem(fn)` | Run with RLS bypass (adapter must implement `runAsSystem`). |
47
- | `tenant.api` | `createTenant`, `updateTenant`, `listTenants`, `deleteTenant` (when `getTenantRepository` is set). |
39
+ | `tenant.api` | `createTenant`, `updateTenant`, `listTenants`, `deleteTenant`. |
40
+ | `TenantRepository.getBySlug(slug)` | Look up a tenant by slug. Used internally for slug-to-UUID auto-resolution. Returns `Tenant \| null`. |
48
41
 
49
42
  ## Adapter contract
50
43
 
@@ -59,6 +52,26 @@ Types: `TenantScopedDatabase` (opaque handle), `SystemDatabase` (opaque handle f
59
52
 
60
53
  Resolution order: **header → path → subdomain → jwt → custom**. Configure `tenantResolver` with e.g. `header: "x-tenant-id"`, `path: "/t/:tenantId"`, `subdomain: true`, `jwt: { claim: "tenant_id" }`, or `custom: (req) => ...`.
61
54
 
55
+ ### Slug-to-UUID resolution
56
+
57
+ After the resolver extracts an identifier, it is automatically normalized to a UUID:
58
+
59
+ - **UUID** — passes through unchanged.
60
+ - **Slug** (e.g. `"acme"`) — looked up in the tenants table via `getBySlug`.
61
+ - **Custom transform** — if `resolveToId` is set, it is called instead of the above (takes full precedence).
62
+
63
+ This means subdomain resolution (`"acme"`) or header-based slugs automatically resolve to UUIDs.
64
+
65
+ ### `resolveToId`
66
+
67
+ Optional transform added to `TenantResolverConfig`:
68
+
69
+ ```ts
70
+ resolveToId?: (identifier: string) => string | Promise<string>;
71
+ ```
72
+
73
+ Use this to map custom domains, external IDs, or any non-slug identifier to a tenant UUID.
74
+
62
75
  ## Telemetry
63
76
 
64
77
  Anonymous telemetry is **on by default** to help improve the library. Data is sent to `https://telemetry.usebetter.dev` and includes runtime, framework, and redacted config (no PII or secrets). Telemetry is disabled in `NODE_ENV=test`.
package/dist/adapter.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { TenantAdapter, TenantScopedDatabase, RunWithTenantAndDatabaseOptions } from "./types.js";
1
+ import type { TenantAdapter, RunWithTenantAndDatabaseOptions } from "./types.js";
2
2
  /**
3
3
  * Returns the tenant-scoped database handle when inside a runWithTenantAndDatabase (or runAs) scope.
4
4
  *
@@ -6,7 +6,7 @@ import type { TenantAdapter, TenantScopedDatabase, RunWithTenantAndDatabaseOptio
6
6
  * Config requires an adapter when tenant-scoped DB is used; getDatabase() is then available inside
7
7
  * request handlers that ran via runWithTenantAndDatabase(tenantId, adapter, next).
8
8
  */
9
- export declare function getDatabase(): TenantScopedDatabase | undefined;
9
+ export declare function getDatabase(): unknown;
10
10
  /**
11
11
  * Sets AsyncLocalStorage context and runs fn with the adapter's tenant-scoped DB.
12
12
  *
@@ -20,5 +20,5 @@ export declare function getDatabase(): TenantScopedDatabase | undefined;
20
20
  * When options.loadTenant is true and options.getTenantRepository and adapter.runAsSystem are set,
21
21
  * loads the full Tenant row into context so getContext().tenant is available (or undefined if not found).
22
22
  */
23
- export declare function runWithTenantAndDatabase<T>(tenantId: string, adapter: TenantAdapter, fn: (database: TenantScopedDatabase) => Promise<T>, options?: RunWithTenantAndDatabaseOptions): Promise<T>;
23
+ export declare function runWithTenantAndDatabase<TDb, SDb, T>(tenantId: string, adapter: TenantAdapter<TDb, SDb>, fn: (database: TDb) => Promise<T>, options?: RunWithTenantAndDatabaseOptions<SDb>): Promise<T>;
24
24
  //# sourceMappingURL=adapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EAEb,oBAAoB,EACpB,+BAA+B,EAChC,MAAM,YAAY,CAAC;AAGpB;;;;;;GAMG;AACH,wBAAgB,WAAW,IAAI,oBAAoB,GAAG,SAAS,CAG9D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,wBAAwB,CAAC,CAAC,EAC9C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,EACtB,EAAE,EAAE,CAAC,QAAQ,EAAE,oBAAoB,KAAK,OAAO,CAAC,CAAC,CAAC,EAClD,OAAO,CAAC,EAAE,+BAA+B,GACxC,OAAO,CAAC,CAAC,CAAC,CAgBZ"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EAEb,+BAA+B,EAChC,MAAM,YAAY,CAAC;AAGpB;;;;;;GAMG;AACH,wBAAgB,WAAW,IAAI,OAAO,CAGrC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EACxD,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,EAChC,EAAE,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,EACjC,OAAO,CAAC,EAAE,+BAA+B,CAAC,GAAG,CAAC,GAC7C,OAAO,CAAC,CAAC,CAAC,CAiBZ"}
package/dist/api.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { TenantAdapter, Tenant, SystemDatabase, TenantScopedDatabase, TenantRepository } from "./types.js";
1
+ import type { TenantAdapter, Tenant, TenantRepository } from "./types.js";
2
2
  export interface CreateTenantData {
3
3
  name: string;
4
4
  slug: string;
@@ -13,8 +13,10 @@ export interface ListTenantsOptions {
13
13
  }
14
14
  /**
15
15
  * Tenant CRUD API. All methods run via adapter.runAsSystem and getTenantRepository.
16
+ * The runAsSystem check is deferred to call time so that betterTenant() can be
17
+ * constructed with adapters that don't implement runAsSystem (e.g. test mocks).
16
18
  */
17
- export declare function createTenantApi(adapter: TenantAdapter, getTenantRepository: (database: SystemDatabase) => TenantRepository): {
19
+ export declare function createTenantApi<TDb, SDb>(adapter: TenantAdapter<TDb, SDb>, getTenantRepository: (database: SDb) => TenantRepository): {
18
20
  createTenant(data: CreateTenantData): Promise<Tenant>;
19
21
  updateTenant(tenantId: string, data: UpdateTenantData): Promise<Tenant>;
20
22
  listTenants(options?: ListTenantsOptions): Promise<Tenant[]>;
@@ -24,10 +26,10 @@ export declare function createTenantApi(adapter: TenantAdapter, getTenantReposit
24
26
  * runAs(tenantId, fn): same as request flow — set context + run inside adapter.runWithTenant
25
27
  * so code can use getDatabase() and run tenant-scoped queries. Use for cron, per-tenant migrations.
26
28
  */
27
- export declare function runAs<T>(tenantId: string, adapter: TenantAdapter, fn: (database: TenantScopedDatabase) => Promise<T>): Promise<T>;
29
+ export declare function runAs<TDb, T>(tenantId: string, adapter: TenantAdapter<TDb>, fn: (database: TDb) => Promise<T>): Promise<T>;
28
30
  /**
29
31
  * runAsSystem(fn): run fn with a DB handle that has RLS bypass.
30
32
  * Optionally set context so getContext() reflects system mode.
31
33
  */
32
- export declare function runAsSystem<T>(adapter: TenantAdapter, fn: (database: SystemDatabase) => Promise<T>): Promise<T>;
34
+ export declare function runAsSystem<SDb, T>(adapter: TenantAdapter<unknown, SDb>, fn: (database: SDb) => Promise<T>): Promise<T>;
33
35
  //# sourceMappingURL=api.d.ts.map
package/dist/api.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,MAAM,EACN,cAAc,EACd,oBAAoB,EACpB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAOpB,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAwBD;;GAEG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,aAAa,EACtB,mBAAmB,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,gBAAgB;uBAMxC,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;2BAgB/C,MAAM,QACV,gBAAgB,GACrB,OAAO,CAAC,MAAM,CAAC;0BAYS,kBAAkB,GAAQ,OAAO,CAAC,MAAM,EAAE,CAAC;2BAWzC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;EAStD;AAED;;;GAGG;AACH,wBAAsB,KAAK,CAAC,CAAC,EAC3B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,EACtB,EAAE,EAAE,CAAC,QAAQ,EAAE,oBAAoB,KAAK,OAAO,CAAC,CAAC,CAAC,GACjD,OAAO,CAAC,CAAC,CAAC,CAEZ;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,CAAC,EACjC,OAAO,EAAE,aAAa,EACtB,EAAE,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,GAC3C,OAAO,CAAC,CAAC,CAAC,CAKZ"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAO1E,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAaD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,GAAG,EACtC,OAAO,EAAE,aAAa,CAAC,GAAG,EAAE,GAAG,CAAC,EAChC,mBAAmB,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,gBAAgB;uBAG7B,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;2BAiB/C,MAAM,QACV,gBAAgB,GACrB,OAAO,CAAC,MAAM,CAAC;0BAaS,kBAAkB,GAAQ,OAAO,CAAC,MAAM,EAAE,CAAC;2BAYzC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;EAUtD;AAED;;;GAGG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,CAAC,EAChC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,EAC3B,EAAE,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,GAChC,OAAO,CAAC,CAAC,CAAC,CAEZ;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,GAAG,EAAE,CAAC,EACtC,OAAO,EAAE,aAAa,CAAC,OAAO,EAAE,GAAG,CAAC,EACpC,EAAE,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,GAChC,OAAO,CAAC,CAAC,CAAC,CAGZ"}
@@ -1,8 +1,6 @@
1
- import type { BetterTenantConfig, Tenant, ResolvableRequest, SystemDatabase } from "./types.js";
2
- import { getContext, runWithTenant } from "./context.js";
3
- import { getDatabase, runWithTenantAndDatabase } from "./adapter.js";
1
+ import type { BetterTenantConfig, Tenant, ResolvableRequest, TenantContext } from "./types.js";
2
+ import { runWithTenant } from "./context.js";
4
3
  import { type HandleRequestOptions } from "./handle-request.js";
5
- import { runAs } from "./api.js";
6
4
  export interface TenantApi {
7
5
  createTenant(data: {
8
6
  name: string;
@@ -18,23 +16,20 @@ export interface TenantApi {
18
16
  }): Promise<Tenant[]>;
19
17
  deleteTenant(tenantId: string): Promise<void>;
20
18
  }
21
- export interface BetterTenantInstance {
22
- getContext: typeof getContext;
23
- getDatabase: typeof getDatabase;
19
+ export interface BetterTenantInstance<TDb = unknown, SDb = unknown> {
20
+ getContext: () => TenantContext | undefined;
21
+ getDatabase: () => TDb | undefined;
24
22
  runWithTenant: typeof runWithTenant;
25
- runWithTenantAndDatabase: typeof runWithTenantAndDatabase;
26
- runAs: typeof runAs;
27
- runAsSystem: <T>(fn: (database: SystemDatabase) => Promise<T>) => Promise<T>;
28
- resolveTenant: (request: ResolvableRequest) => string | undefined;
29
- resolveTenantAsync: (request: ResolvableRequest) => Promise<string | undefined>;
23
+ runAs: <T>(tenantId: string, fn: (database: TDb) => Promise<T>) => Promise<T>;
24
+ runAsSystem: <T>(fn: (database: SDb) => Promise<T>) => Promise<T>;
25
+ resolveTenant: (request: ResolvableRequest) => Promise<string | undefined>;
26
+ /** Human-readable descriptions of the configured resolution strategies. */
27
+ resolverStrategies: string[];
30
28
  handleRequest: <Req extends ResolvableRequest, Result>(request: Req, next: () => Promise<Result>, options?: Omit<HandleRequestOptions<Req, Result>, "resolveTenant" | "adapter">) => Promise<Result>;
31
- tenant: {
32
- api: TenantApi;
33
- };
29
+ api: TenantApi;
34
30
  }
35
31
  /**
36
- * Creates the Better Tenant instance. Config must include adapter and tenantResolver.
37
- * When getTenantRepository is provided, tenant.api is available for CRUD on the tenants table.
32
+ * Creates the Better Tenant instance. Config must include adapter, tenantResolver, and getTenantRepository.
38
33
  */
39
- export declare function betterTenant(config: BetterTenantConfig): BetterTenantInstance;
34
+ export declare function betterTenant<TDb, SDb>(config: BetterTenantConfig<TDb, SDb>): BetterTenantInstance<TDb, SDb>;
40
35
  //# sourceMappingURL=better-tenant.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"better-tenant.d.ts","sourceRoot":"","sources":["../src/better-tenant.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,MAAM,EACN,iBAAiB,EACjB,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,wBAAwB,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAEL,KAAK,EAEN,MAAM,UAAU,CAAC;AAGlB,MAAM,WAAW,SAAS;IACxB,YAAY,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpE,YAAY,CACV,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GACrC,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,WAAW,CAAC,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9E,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,WAAW,EAAE,OAAO,WAAW,CAAC;IAChC,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,wBAAwB,EAAE,OAAO,wBAAwB,CAAC;IAC1D,KAAK,EAAE,OAAO,KAAK,CAAC;IACpB,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAC7E,aAAa,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,MAAM,GAAG,SAAS,CAAC;IAClE,kBAAkB,EAAE,CAClB,OAAO,EAAE,iBAAiB,KACvB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACjC,aAAa,EAAE,CAAC,GAAG,SAAS,iBAAiB,EAAE,MAAM,EACnD,OAAO,EAAE,GAAG,EACZ,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EAC3B,OAAO,CAAC,EAAE,IAAI,CAAC,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,SAAS,CAAC,KAC3E,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,MAAM,EAAE;QAAE,GAAG,EAAE,SAAS,CAAA;KAAE,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,kBAAkB,GAAG,oBAAoB,CAmC7E"}
1
+ {"version":3,"file":"better-tenant.d.ts","sourceRoot":"","sources":["../src/better-tenant.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,MAAM,EAIN,iBAAiB,EACjB,aAAa,EAEd,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAc,aAAa,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,qBAAqB,CAAC;AAwC7B,MAAM,WAAW,SAAS;IACxB,YAAY,CAAC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpE,YAAY,CACV,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GACrC,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,WAAW,CAAC,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC9E,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,oBAAoB,CAAC,GAAG,GAAG,OAAO,EAAE,GAAG,GAAG,OAAO;IAChE,UAAU,EAAE,MAAM,aAAa,GAAG,SAAS,CAAC;IAC5C,WAAW,EAAE,MAAM,GAAG,GAAG,SAAS,CAAC;IACnC,aAAa,EAAE,OAAO,aAAa,CAAC;IACpC,KAAK,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAC9E,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAClE,aAAa,EAAE,CACb,OAAO,EAAE,iBAAiB,KACvB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACjC,2EAA2E;IAC3E,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,aAAa,EAAE,CAAC,GAAG,SAAS,iBAAiB,EAAE,MAAM,EACnD,OAAO,EAAE,GAAG,EACZ,IAAI,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,EAC3B,OAAO,CAAC,EAAE,IAAI,CACZ,oBAAoB,CAAC,GAAG,EAAE,MAAM,CAAC,EACjC,eAAe,GAAG,SAAS,CAC5B,KACE,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,GAAG,EAAE,SAAS,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,EACnC,MAAM,EAAE,kBAAkB,CAAC,GAAG,EAAE,GAAG,CAAC,GACnC,oBAAoB,CAAC,GAAG,EAAE,GAAG,CAAC,CAqDhC"}