@selvajs/server 1.0.0-beta.9 → 1.0.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.
- package/README.md +18 -20
- package/dist/access/project-access-input.d.ts +13 -16
- package/dist/access/project-access-input.d.ts.map +1 -1
- package/dist/access/project-access-input.js +2 -2
- package/dist/access/project-access-input.js.map +1 -1
- package/dist/compute/idempotency.d.ts +18 -22
- package/dist/compute/idempotency.d.ts.map +1 -1
- package/dist/compute/idempotency.js +10 -14
- package/dist/compute/idempotency.js.map +1 -1
- package/dist/compute/index.d.ts.map +1 -1
- package/dist/compute/index.js +3 -5
- package/dist/compute/index.js.map +1 -1
- package/dist/compute/limits.d.ts +70 -86
- package/dist/compute/limits.d.ts.map +1 -1
- package/dist/compute/limits.js +27 -56
- package/dist/compute/limits.js.map +1 -1
- package/dist/compute/rate-limit.d.ts +41 -53
- package/dist/compute/rate-limit.d.ts.map +1 -1
- package/dist/compute/rate-limit.js +27 -35
- package/dist/compute/rate-limit.js.map +1 -1
- package/dist/compute/remote-definition.d.ts +8 -22
- package/dist/compute/remote-definition.d.ts.map +1 -1
- package/dist/compute/remote-definition.js +9 -19
- package/dist/compute/remote-definition.js.map +1 -1
- package/dist/compute/safe-url.d.ts +7 -5
- package/dist/compute/safe-url.d.ts.map +1 -1
- package/dist/compute/safe-url.js +26 -28
- package/dist/compute/safe-url.js.map +1 -1
- package/dist/definitions/definition-service.d.ts +13 -38
- package/dist/definitions/definition-service.d.ts.map +1 -1
- package/dist/definitions/definition-service.js +25 -53
- package/dist/definitions/definition-service.js.map +1 -1
- package/dist/definitions/index.d.ts +2 -2
- package/dist/definitions/index.d.ts.map +1 -1
- package/dist/definitions/index.js +1 -5
- package/dist/definitions/index.js.map +1 -1
- package/dist/definitions/load-for-render.d.ts +23 -20
- package/dist/definitions/load-for-render.d.ts.map +1 -1
- package/dist/definitions/load-for-render.js +55 -41
- package/dist/definitions/load-for-render.js.map +1 -1
- package/dist/definitions/schema-extraction.d.ts +36 -16
- package/dist/definitions/schema-extraction.d.ts.map +1 -1
- package/dist/definitions/schema-extraction.js +65 -28
- package/dist/definitions/schema-extraction.js.map +1 -1
- package/dist/errors/SentryErrorReporter.d.ts +2 -3
- package/dist/errors/SentryErrorReporter.d.ts.map +1 -1
- package/dist/errors/SentryErrorReporter.js +10 -14
- package/dist/errors/SentryErrorReporter.js.map +1 -1
- package/dist/http/body-size.d.ts +8 -11
- package/dist/http/body-size.d.ts.map +1 -1
- package/dist/http/body-size.js +8 -11
- package/dist/http/body-size.js.map +1 -1
- package/dist/http/redirect.d.ts +3 -7
- package/dist/http/redirect.d.ts.map +1 -1
- package/dist/http/redirect.js +8 -8
- package/dist/http/redirect.js.map +1 -1
- package/dist/http/route-classifier.d.ts +20 -24
- package/dist/http/route-classifier.d.ts.map +1 -1
- package/dist/http/route-classifier.js +6 -7
- package/dist/http/route-classifier.js.map +1 -1
- package/dist/http/security-headers.d.ts +13 -15
- package/dist/http/security-headers.d.ts.map +1 -1
- package/dist/http/security-headers.js +10 -12
- package/dist/http/security-headers.js.map +1 -1
- package/dist/logging/PinoLogger.d.ts +8 -14
- package/dist/logging/PinoLogger.d.ts.map +1 -1
- package/dist/logging/PinoLogger.js +29 -35
- package/dist/logging/PinoLogger.js.map +1 -1
- package/dist/logging/requestId.d.ts +20 -28
- package/dist/logging/requestId.d.ts.map +1 -1
- package/dist/logging/requestId.js +29 -37
- package/dist/logging/requestId.js.map +1 -1
- package/dist/ops/index.d.ts.map +1 -1
- package/dist/ops/index.js +2 -1
- package/dist/ops/index.js.map +1 -1
- package/dist/ops/semver.d.ts +22 -24
- package/dist/ops/semver.d.ts.map +1 -1
- package/dist/ops/semver.js +22 -25
- package/dist/ops/semver.js.map +1 -1
- package/dist/providers/create-selva-providers.d.ts +19 -27
- package/dist/providers/create-selva-providers.d.ts.map +1 -1
- package/dist/providers/create-selva-providers.js +28 -34
- package/dist/providers/create-selva-providers.js.map +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +2 -4
- package/dist/providers/index.js.map +1 -1
- package/dist/tokens/token-codec.d.ts +24 -27
- package/dist/tokens/token-codec.d.ts.map +1 -1
- package/dist/tokens/token-codec.js +18 -21
- package/dist/tokens/token-codec.js.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -9,12 +9,11 @@ single-flight, client and definition-byte caches. This package does not re-expor
|
|
|
9
9
|
not depend on it. What stays here is HTTP _request policy_, which is a different job from running a
|
|
10
10
|
solve.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
console-backed logger rather than failing.
|
|
12
|
+
Nothing here logs unless you wire it. Modules that emit diagnostics take an optional
|
|
13
|
+
`logger?: ILogger` defaulting to `NoopLogger`, so an embedder that wires nothing gets silence
|
|
14
|
+
rather than unsolicited stdout writes. `@selvajs/server/logging` provides a pino-backed
|
|
15
|
+
`createLogger`; `pino` is an **optional** peer dependency, and without it `createLogger` falls
|
|
16
|
+
back to a console-backed logger rather than failing.
|
|
18
17
|
|
|
19
18
|
## Install
|
|
20
19
|
|
|
@@ -25,20 +24,19 @@ pnpm add @selvajs/server
|
|
|
25
24
|
## Subpath exports
|
|
26
25
|
|
|
27
26
|
**There is no root export.** Import from a subpath — `import … from '@selvajs/server'` fails to
|
|
28
|
-
resolve, on purpose
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
| `@selvajs/server/
|
|
34
|
-
| `@selvajs/server/
|
|
35
|
-
| `@selvajs/server/
|
|
36
|
-
| `@selvajs/server/
|
|
37
|
-
| `@selvajs/server/
|
|
38
|
-
| `@selvajs/server/
|
|
39
|
-
| `@selvajs/server/
|
|
40
|
-
| `@selvajs/server/
|
|
41
|
-
| `@selvajs/server/ops` | Operational utilities |
|
|
27
|
+
resolve, on purpose, so it stays visible which slice a consumer depends on.
|
|
28
|
+
|
|
29
|
+
| Export | Contents |
|
|
30
|
+
| ----------------------------- | ------------------------------------------------------------------------------- |
|
|
31
|
+
| `@selvajs/server/compute` | Compute limits, rate limiting, idempotency, SSRF guard, remote-definition fetch |
|
|
32
|
+
| `@selvajs/server/definitions` | Definition service, compute-schema extraction, render loader |
|
|
33
|
+
| `@selvajs/server/providers` | Env-driven provider selection over a caller-supplied registry |
|
|
34
|
+
| `@selvajs/server/tokens` | HMAC codec for capability-URL tokens (share links, invites) |
|
|
35
|
+
| `@selvajs/server/errors` | Sentry-backed `IErrorReporter` |
|
|
36
|
+
| `@selvajs/server/logging` | pino-backed `ILogger` + request-id correlation |
|
|
37
|
+
| `@selvajs/server/http` | Security headers, body-size and redirect guards, route classification |
|
|
38
|
+
| `@selvajs/server/access` | Builds the input platform's project access rules consume |
|
|
39
|
+
| `@selvajs/server/ops` | Channel-aware semver comparison |
|
|
42
40
|
|
|
43
41
|
Requires a `@selvajs/platform` provider (e.g. `@selvajs/local-provider` or
|
|
44
42
|
`@selvajs/supabase-provider`) for data/storage/auth.
|
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import type { Project, ProjectAccessInput, RequestContext } from '@selvajs/platform';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* a consuming app doesn't reimplement (and mis-fetch) it:
|
|
3
|
+
* Marshals an app's providers into the one input shape platform's pure access
|
|
4
|
+
* rules (`canView`/`canEdit`/`canSolve`/`canManage`/…) read. What lives here is
|
|
5
|
+
* **which rows each visibility needs**, so a consuming app doesn't reimplement
|
|
6
|
+
* and mis-fetch it:
|
|
8
7
|
*
|
|
9
8
|
* - `platform` → grants (skipped entirely while the feature flag is off)
|
|
10
9
|
* - `private` → caller's project member row
|
|
11
10
|
* - `org` / `public` → caller's project member row (for canEdit/canManage) and
|
|
12
11
|
* org member row (for canView). Cross-org public skips the org row.
|
|
13
12
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
13
|
+
* Row lookups are injected as functions so lazily-initialized providers work
|
|
14
|
+
* unchanged, and flags are read per call so runtime flips behave like the app's
|
|
15
|
+
* own `flag()` reads.
|
|
17
16
|
*/
|
|
18
17
|
export interface ProjectAccessFlags {
|
|
19
18
|
/** `ALLOW_CROSS_ORG_PUBLIC` — public visibility spans orgs when true. */
|
|
@@ -30,17 +29,15 @@ export interface ProjectAccessInputDeps {
|
|
|
30
29
|
}
|
|
31
30
|
export interface ProjectAccessInputBuilder {
|
|
32
31
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
* already loaded a row (e.g. tests, batched listing pages).
|
|
32
|
+
* Fetches exactly the rows the rule will consult, based on
|
|
33
|
+
* `project.visibility`. Other fields default to safe values; `overrides` is
|
|
34
|
+
* for the rare caller that already loaded a row (tests, batched listings).
|
|
37
35
|
*/
|
|
38
36
|
buildProjectAccessInput(ctx: RequestContext, project: Project, overrides?: Partial<ProjectAccessInput>): Promise<ProjectAccessInput>;
|
|
39
37
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* `buildProjectAccessInput` to avoid an N+1 fetch.
|
|
38
|
+
* Same input, from caller-provided rows and no I/O. A listing page that has
|
|
39
|
+
* batch-loaded membership calls this per row instead of
|
|
40
|
+
* `buildProjectAccessInput`, which would fetch N+1 times.
|
|
44
41
|
*/
|
|
45
42
|
projectAccessInputFromRows(ctx: RequestContext, project: Project, rows: {
|
|
46
43
|
member?: ProjectAccessInput['member'];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-access-input.d.ts","sourceRoot":"","sources":["../../src/access/project-access-input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAErF
|
|
1
|
+
{"version":3,"file":"project-access-input.d.ts","sourceRoot":"","sources":["../../src/access/project-access-input.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAErF;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,kBAAkB;IAClC,yEAAyE;IACzE,mBAAmB,EAAE,OAAO,CAAC;IAC7B,0EAA0E;IAC1E,sBAAsB,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,sBAAsB;IACtC,gBAAgB,CACf,GAAG,EAAE,cAAc,EACnB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAC;IACzC,YAAY,CACX,GAAG,EAAE,cAAc,EACnB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,GACZ,OAAO,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC,CAAC;IAC5C,kBAAkB,CACjB,GAAG,EAAE,cAAc,EACnB,SAAS,EAAE,MAAM,GACf,OAAO,CAAC,kBAAkB,CAAC,gBAAgB,CAAC,CAAC,CAAC;IACjD,4EAA4E;IAC5E,KAAK,IAAI,kBAAkB,CAAC;CAC5B;AAED,MAAM,WAAW,yBAAyB;IACzC;;;;OAIG;IACH,uBAAuB,CACtB,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,OAAO,EAChB,SAAS,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GACrC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC/B;;;;OAIG;IACH,0BAA0B,CACzB,GAAG,EAAE,cAAc,EACnB,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE;QACL,MAAM,CAAC,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;QACtC,SAAS,CAAC,EAAE,kBAAkB,CAAC,WAAW,CAAC,CAAC;QAC5C,cAAc,CAAC,EAAE,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;KACtD,GACC,kBAAkB,CAAC;CACtB;AAED,wBAAgB,+BAA+B,CAC9C,IAAI,EAAE,sBAAsB,GAC1B,yBAAyB,CAoE3B"}
|
|
@@ -6,8 +6,8 @@ export function createProjectAccessInputBuilder(deps) {
|
|
|
6
6
|
let orgMember = null;
|
|
7
7
|
let platformGrants = [];
|
|
8
8
|
if (project.visibility === 'platform') {
|
|
9
|
-
//
|
|
10
|
-
//
|
|
9
|
+
// With the flag off every rule returns false before reading grants,
|
|
10
|
+
// so the lookup would be wasted I/O.
|
|
11
11
|
if (enablePlatformProjects) {
|
|
12
12
|
platformGrants = await deps.listPlatformGrants(ctx, project.id);
|
|
13
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-access-input.js","sourceRoot":"","sources":["../../src/access/project-access-input.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"project-access-input.js","sourceRoot":"","sources":["../../src/access/project-access-input.ts"],"names":[],"mappings":"AAuEA,MAAM,UAAU,+BAA+B,CAC9C,IAA4B;IAE5B,OAAO;QACN,KAAK,CAAC,uBAAuB,CAC5B,GAAmB,EACnB,OAAgB,EAChB,YAAyC,EAAE;YAE3C,MAAM,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YAErE,IAAI,MAAM,GAAiC,IAAI,CAAC;YAChD,IAAI,SAAS,GAAoC,IAAI,CAAC;YACtD,IAAI,cAAc,GAAyC,EAAE,CAAC;YAE9D,IAAI,OAAO,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;gBACvC,oEAAoE;gBACpE,qCAAqC;gBACrC,IAAI,sBAAsB,EAAE,CAAC;oBAC5B,cAAc,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;gBACjE,CAAC;YACF,CAAC;iBAAM,IAAI,OAAO,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC7C,MAAM,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;YACnE,CAAC;iBAAM,CAAC;gBACP,MAAM,aAAa,GAAG,OAAO,CAAC,UAAU,KAAK,QAAQ,IAAI,mBAAmB,CAAC;gBAC7E,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;oBACvC,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC;oBAClD,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC;iBACzF,CAAC,CAAC;YACJ,CAAC;YAED,OAAO;gBACN,cAAc,EAAE,GAAG,CAAC,cAAc;gBAClC,mBAAmB,EAAE,GAAG,CAAC,mBAAmB;gBAC5C,OAAO;gBACP,MAAM;gBACN,SAAS;gBACT,mBAAmB;gBACnB,sBAAsB;gBACtB,cAAc;gBACd,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,IAAI;gBACpC,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,GAAG,SAAS;aACZ,CAAC;QACH,CAAC;QAED,0BAA0B,CACzB,GAAmB,EACnB,OAAgB,EAChB,IAIC;YAED,MAAM,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YACrE,OAAO;gBACN,cAAc,EAAE,GAAG,CAAC,cAAc;gBAClC,mBAAmB,EAAE,GAAG,CAAC,mBAAmB;gBAC5C,OAAO;gBACP,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI;gBAC3B,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI;gBACjC,mBAAmB;gBACnB,sBAAsB;gBACtB,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,EAAE;gBACzC,WAAW,EAAE,GAAG,CAAC,WAAW,IAAI,IAAI;gBACpC,MAAM,EAAE,GAAG,CAAC,MAAM;aAClB,CAAC;QACH,CAAC;KACD,CAAC;AACH,CAAC"}
|
|
@@ -4,27 +4,25 @@
|
|
|
4
4
|
*
|
|
5
5
|
* **This absorbs client retries; it is not a result cache.** The TTL is short
|
|
6
6
|
* and the store is process-local, so a retry that lands on another instance
|
|
7
|
-
* (or after the TTL) re-solves
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* (or after the TTL) re-solves — a duplicate solve costs compute, not
|
|
8
|
+
* correctness. What it must never do is return one caller's result to another,
|
|
9
|
+
* hence the caller identity in the key.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
* usually retries
|
|
13
|
-
*
|
|
14
|
-
* first rather than starting a second solve. A reservation that rejects is
|
|
15
|
-
* dropped, so a failed solve is retryable rather than a cached error.
|
|
11
|
+
* The store holds a **promise**, reserved before the work starts, because a
|
|
12
|
+
* client that retries on timeout usually retries while the first solve is still
|
|
13
|
+
* running: the second request awaits the first instead of starting a second solve.
|
|
16
14
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* and give this module a lifecycle its callers don't have.
|
|
15
|
+
* Sweep and cap are both driven by `run` rather than a timer — a timer would keep
|
|
16
|
+
* the event loop alive and give this module a lifecycle its callers don't have.
|
|
20
17
|
*/
|
|
21
18
|
export interface IdempotencyStoreConfig {
|
|
22
19
|
/** How long a completed response stays replayable, in ms. */
|
|
23
20
|
ttlMs: number;
|
|
24
21
|
/**
|
|
25
|
-
* Max retained entries
|
|
26
|
-
*
|
|
27
|
-
*
|
|
22
|
+
* Max retained entries; once exceeded, the ones nearest expiry are evicted.
|
|
23
|
+
* Evicting only costs a re-solve on a retry, so size this above the plausible
|
|
24
|
+
* number of in-flight + recent keys. Defaults to
|
|
25
|
+
* {@link DEFAULT_IDEMPOTENCY_MAX_KEYS}.
|
|
28
26
|
*/
|
|
29
27
|
maxKeys?: number;
|
|
30
28
|
/**
|
|
@@ -42,22 +40,20 @@ export interface IdempotencyOutcome<T> {
|
|
|
42
40
|
}
|
|
43
41
|
export interface IdempotencyStore<T> {
|
|
44
42
|
/**
|
|
45
|
-
* Run `fn` under `key`, or join/replay the entry already stored there.
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* first run instead of starting its own. A rejection drops the reservation.
|
|
43
|
+
* Run `fn` under `key`, or join/replay the entry already stored there. The
|
|
44
|
+
* entry is reserved before `fn` starts, so a concurrent retry awaits the first
|
|
45
|
+
* run; a rejection drops the reservation.
|
|
49
46
|
*/
|
|
50
47
|
run(key: string, fn: () => Promise<T>): Promise<IdempotencyOutcome<T>>;
|
|
51
48
|
/** Test seam — wipes in-memory state. Production code never calls this. */
|
|
52
49
|
reset(): void;
|
|
53
|
-
/** Retained entry count, including
|
|
50
|
+
/** Retained entry count, including entries not yet swept. */
|
|
54
51
|
size(): number;
|
|
55
52
|
readonly config: Readonly<IdempotencyStoreConfig>;
|
|
56
53
|
}
|
|
57
54
|
/**
|
|
58
|
-
*
|
|
59
|
-
* the solve route stores a serialized
|
|
60
|
-
* because a `Response` body can only be read once.
|
|
55
|
+
* `T` is whatever the caller replays. It must not be a `Response` — a body can
|
|
56
|
+
* only be read once, so the solve route stores a serialized snapshot instead.
|
|
61
57
|
*/
|
|
62
58
|
export declare function createIdempotencyStore<T>(config: IdempotencyStoreConfig): IdempotencyStore<T>;
|
|
63
59
|
//# sourceMappingURL=idempotency.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../src/compute/idempotency.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"idempotency.d.ts","sourceRoot":"","sources":["../../src/compute/idempotency.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,sBAAsB;IACtC,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,sDAAsD;AACtD,eAAO,MAAM,4BAA4B,QAAS,CAAC;AAEnD,MAAM,WAAW,kBAAkB,CAAC,CAAC;IACpC,KAAK,EAAE,CAAC,CAAC;IACT,8EAA8E;IAC9E,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB,CAAC,CAAC;IAClC;;;;OAIG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,2EAA2E;IAC3E,KAAK,IAAI,IAAI,CAAC;IACd,6DAA6D;IAC7D,IAAI,IAAI,MAAM,CAAC;IACf,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,sBAAsB,CAAC,CAAC;CAClD;AAYD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,MAAM,EAAE,sBAAsB,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAwE7F"}
|
|
@@ -4,26 +4,22 @@
|
|
|
4
4
|
*
|
|
5
5
|
* **This absorbs client retries; it is not a result cache.** The TTL is short
|
|
6
6
|
* and the store is process-local, so a retry that lands on another instance
|
|
7
|
-
* (or after the TTL) re-solves
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* (or after the TTL) re-solves — a duplicate solve costs compute, not
|
|
8
|
+
* correctness. What it must never do is return one caller's result to another,
|
|
9
|
+
* hence the caller identity in the key.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
* usually retries
|
|
13
|
-
*
|
|
14
|
-
* first rather than starting a second solve. A reservation that rejects is
|
|
15
|
-
* dropped, so a failed solve is retryable rather than a cached error.
|
|
11
|
+
* The store holds a **promise**, reserved before the work starts, because a
|
|
12
|
+
* client that retries on timeout usually retries while the first solve is still
|
|
13
|
+
* running: the second request awaits the first instead of starting a second solve.
|
|
16
14
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
* and give this module a lifecycle its callers don't have.
|
|
15
|
+
* Sweep and cap are both driven by `run` rather than a timer — a timer would keep
|
|
16
|
+
* the event loop alive and give this module a lifecycle its callers don't have.
|
|
20
17
|
*/
|
|
21
18
|
/** Default {@link IdempotencyStoreConfig.maxKeys}. */
|
|
22
19
|
export const DEFAULT_IDEMPOTENCY_MAX_KEYS = 10_000;
|
|
23
20
|
/**
|
|
24
|
-
*
|
|
25
|
-
* the solve route stores a serialized
|
|
26
|
-
* because a `Response` body can only be read once.
|
|
21
|
+
* `T` is whatever the caller replays. It must not be a `Response` — a body can
|
|
22
|
+
* only be read once, so the solve route stores a serialized snapshot instead.
|
|
27
23
|
*/
|
|
28
24
|
export function createIdempotencyStore(config) {
|
|
29
25
|
const ttlMs = Math.max(0, Math.floor(config.ttlMs));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"idempotency.js","sourceRoot":"","sources":["../../src/compute/idempotency.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"idempotency.js","sourceRoot":"","sources":["../../src/compute/idempotency.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAWA,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EACR,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,wBAAwB,EACxB,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,eAAe,EACpB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACN,sBAAsB,EACtB,4BAA4B,EAC5B,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,kBAAkB,EACvB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAC;AAEzF,OAAO,EACN,6BAA6B,EAC7B,eAAe,EACf,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,MAAM,wBAAwB,CAAC"}
|
package/dist/compute/index.js
CHANGED
|
@@ -5,11 +5,9 @@
|
|
|
5
5
|
// from running one.
|
|
6
6
|
//
|
|
7
7
|
// **The solve core is not here.** Pipeline, L2 result cache, single-flight, and the client /
|
|
8
|
-
// definition-byte caches live in `@selvajs/solve/server
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
// borrowed — a surface that no longer described what the package does. `@selvajs/server` does not
|
|
12
|
-
// depend on `@selvajs/solve`.
|
|
8
|
+
// definition-byte caches live in `@selvajs/solve/server`. Do not re-export them through this
|
|
9
|
+
// barrel: `@selvajs/server` does not depend on `@selvajs/solve`, and a borrowed surface stops
|
|
10
|
+
// describing what this package does.
|
|
13
11
|
export { resolveComputeLimits, readPositiveInt, readNonNegativeInt, readBool } from './limits.js';
|
|
14
12
|
export { createComputeRateLimiter, DEFAULT_MAX_KEYS } from './rate-limit.js';
|
|
15
13
|
export { createIdempotencyStore, DEFAULT_IDEMPOTENCY_MAX_KEYS } from './idempotency.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,kGAAkG;AAClG,mGAAmG;AACnG,oBAAoB;AACpB,EAAE;AACF,6FAA6F;AAC7F,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compute/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,kGAAkG;AAClG,mGAAmG;AACnG,oBAAoB;AACpB,EAAE;AACF,6FAA6F;AAC7F,6FAA6F;AAC7F,8FAA8F;AAC9F,qCAAqC;AAErC,OAAO,EACN,oBAAoB,EACpB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EAGR,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,wBAAwB,EACxB,gBAAgB,EAIhB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACN,sBAAsB,EACtB,4BAA4B,EAI5B,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,yBAAyB,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAC;AAEzF,OAAO,EACN,6BAA6B,EAC7B,eAAe,EAGf,MAAM,wBAAwB,CAAC"}
|
package/dist/compute/limits.d.ts
CHANGED
|
@@ -1,88 +1,73 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Compute/solve server limits, resolved from an injected env map.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* fully-resolved `ComputeLimits` object. No ambient `$env` / `process.env`
|
|
8
|
-
* read happens here, so the module is importable from any runtime and testable
|
|
9
|
-
* without env plumbing.
|
|
10
|
-
*
|
|
11
|
-
* Every knob is env-overridable so deployments can tune without code changes;
|
|
12
|
-
* the defaults are sized for typical interactive use (sliders + small uploads).
|
|
13
|
-
* Centralizing them surfaces the tradeoffs (raise the rate cap → also consider
|
|
14
|
-
* the body cap) at one glance.
|
|
4
|
+
* Env comes in as a parameter — nothing ambient is read — so this is importable
|
|
5
|
+
* from any runtime and testable without env plumbing. Defaults are sized for
|
|
6
|
+
* typical interactive use (sliders + small uploads).
|
|
15
7
|
*
|
|
16
8
|
* Client-side counterparts (the in-flight compute throttle and slider debounce)
|
|
17
|
-
*
|
|
18
|
-
*
|
|
9
|
+
* are bundled into the client in `@selvajs/ui` and can't read env — they carry
|
|
10
|
+
* their own defaults.
|
|
19
11
|
*/
|
|
20
12
|
import { type ILogger } from '@selvajs/platform';
|
|
21
13
|
/** Env source: any string→string map. `$env/dynamic/private` satisfies this. */
|
|
22
14
|
export type EnvRecord = Record<string, string | undefined>;
|
|
23
|
-
/**
|
|
24
|
-
* Parse a positive integer env value. Returns `fallback` when absent, and warns
|
|
25
|
-
* + falls back when present but not a finite positive number.
|
|
26
|
-
*/
|
|
15
|
+
/** Parse a positive integer env value; absent, or present but not finite and positive, falls back. */
|
|
27
16
|
export declare function readPositiveInt(env: EnvRecord, name: string, fallback: number, logger?: ILogger): number;
|
|
28
|
-
/**
|
|
29
|
-
* Parse a non-negative integer env value — like {@link readPositiveInt} but `0`
|
|
30
|
-
* is a valid value (used by knobs where `0` means "disable", e.g. a cache
|
|
31
|
-
* budget). Returns `fallback` when absent, and warns + falls back when present
|
|
32
|
-
* but not a finite non-negative number.
|
|
33
|
-
*/
|
|
17
|
+
/** Like {@link readPositiveInt}, but `0` is valid — knobs where it means "disable". */
|
|
34
18
|
export declare function readNonNegativeInt(env: EnvRecord, name: string, fallback: number, logger?: ILogger): number;
|
|
35
19
|
/**
|
|
36
|
-
* Parse a boolean env flag. Accepts `true/1/yes/on`
|
|
37
|
-
*
|
|
20
|
+
* Parse a boolean env flag. Accepts `true/1/yes/on` and `false/0/no/off`
|
|
21
|
+
* (case-insensitive); any other spelling warns and falls back.
|
|
38
22
|
*/
|
|
39
23
|
export declare function readBool(env: EnvRecord, name: string, fallback: boolean, logger?: ILogger): boolean;
|
|
40
24
|
/** Fully-resolved server-side compute limits. */
|
|
41
25
|
export interface ComputeLimits {
|
|
42
26
|
/**
|
|
43
|
-
* Longest the app waits for one solve.
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
27
|
+
* Longest the app waits for one solve. The SolveScheduler (`@selvajs/compute`)
|
|
28
|
+
* propagates an AbortSignal into the upstream Compute call, so a timeout
|
|
29
|
+
* actually cancels the work. Also forwarded to the client, where it drives the
|
|
30
|
+
* in-browser AbortController.
|
|
47
31
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
32
|
+
* Bounds only the parts of the stack we own. A reverse proxy or serverless
|
|
33
|
+
* platform cap may shoot the request sooner; raising this past those caps
|
|
34
|
+
* produces 502s, not longer solves.
|
|
51
35
|
*/
|
|
52
36
|
solveDeadlineMs: number;
|
|
53
37
|
/** Fixed-window cap on /api/v1/compute: window length + max requests per window. */
|
|
54
38
|
rateLimitWindowMs: number;
|
|
55
39
|
rateLimitMaxRequests: number;
|
|
56
40
|
/**
|
|
57
|
-
* Largest .gh definition accepted on upload
|
|
58
|
-
*
|
|
59
|
-
* past the upload cap. Defaults to 50 MB to match Rhino.Compute's own
|
|
60
|
-
* `RHINO_COMPUTE_MAX_REQUEST_SIZE` default: a larger file 413s at compute
|
|
41
|
+
* Largest .gh definition accepted on upload. 50 MB matches Rhino.Compute's own
|
|
42
|
+
* `RHINO_COMPUTE_MAX_REQUEST_SIZE` default — a larger file 413s at compute
|
|
61
43
|
* regardless, so accepting it here only defers the failure.
|
|
62
44
|
*/
|
|
63
45
|
maxDefinitionFileSize: number;
|
|
46
|
+
/** Largest cover image accepted on upload. */
|
|
64
47
|
maxImageFileSize: number;
|
|
65
48
|
/**
|
|
66
|
-
* /api/v1/compute JSON *request* body cap (inputs + values, not the .gh).
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
* to ~200 MB on the wire, plus JSON envelope slack
|
|
71
|
-
* `BODY_SIZE_LIMIT=210M` shipped in `.env.example`.
|
|
49
|
+
* /api/v1/compute JSON *request* body cap (inputs + values, not the .gh). Must
|
|
50
|
+
* stay <= adapter-node's global `BODY_SIZE_LIMIT` or that backstop rejects
|
|
51
|
+
* first. 210 MB matches the `BODY_SIZE_LIMIT=210M` shipped in `.env.example`:
|
|
52
|
+
* a `file` widget embeds geometry as base64 in `values`, so the client's
|
|
53
|
+
* 150 MB raw file inflates to ~200 MB on the wire, plus JSON envelope slack.
|
|
72
54
|
*/
|
|
73
55
|
computeRequestMaxBytes: number;
|
|
74
56
|
/**
|
|
75
57
|
* /api/v1/compute JSON *response* cap. A `file`-typed output is base64-embedded
|
|
76
|
-
* in the response; guards V8's ~512 MB single-string wall (a
|
|
77
|
-
* `RangeError`) and browser-tab OOM
|
|
78
|
-
*
|
|
58
|
+
* in the response; this guards V8's ~512 MB single-string wall (a
|
|
59
|
+
* `JSON.stringify` `RangeError`) and browser-tab OOM, turning an opaque crash
|
|
60
|
+
* into a clear 413. The real fix is out-of-band streaming.
|
|
79
61
|
*
|
|
80
|
-
* 300 MB is
|
|
81
|
-
*
|
|
82
|
-
*
|
|
62
|
+
* 300 MB is deliberate, not a dev leftover — above any legitimate inline
|
|
63
|
+
* payload, below the V8 string wall. Unlike the request cap it is not bounded
|
|
64
|
+
* by `BODY_SIZE_LIMIT`, which only covers inbound bodies.
|
|
83
65
|
*/
|
|
84
66
|
computeResponseMaxBytes: number;
|
|
85
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* Hard cap on fetching a remote definition. Tracks `maxDefinitionFileSize` so a
|
|
69
|
+
* remote URL can't smuggle a file past the upload cap.
|
|
70
|
+
*/
|
|
86
71
|
remoteDefinitionMaxBytes: number;
|
|
87
72
|
/** Deadline on fetching a remote definition (slow-loris protection). */
|
|
88
73
|
remoteDefinitionFetchTimeoutMs: number;
|
|
@@ -90,17 +75,17 @@ export interface ComputeLimits {
|
|
|
90
75
|
* TTL for the in-process cache of .gh bytes fetched from a REMOTE URL.
|
|
91
76
|
*
|
|
92
77
|
* Only the remote path is TTL'd, and the name says so deliberately: the other
|
|
93
|
-
* definition cache is keyed on an immutable version id, where expiry
|
|
94
|
-
*
|
|
95
|
-
*
|
|
78
|
+
* definition cache is keyed on an immutable version id, where expiry could only
|
|
79
|
+
* ever throw away valid work. A URL's owner can swap the file underneath us, so
|
|
80
|
+
* that path needs a freshness bound and this is it.
|
|
96
81
|
*/
|
|
97
82
|
remoteDefinitionCacheTtlMs: number;
|
|
98
83
|
/**
|
|
99
84
|
* Reuse the server's cached definition via a pointer instead of re-uploading
|
|
100
85
|
* the binary each solve. SAFE only on a compute server that signals a
|
|
101
|
-
* stale-pointer miss (VektorNode fork
|
|
102
|
-
* returns an empty 200 would silently yield EMPTY geometry. Off
|
|
103
|
-
* / standard rhino.compute.
|
|
86
|
+
* stale-pointer miss (the VektorNode fork, or any fork that throws) — a server
|
|
87
|
+
* that returns an empty 200 instead would silently yield EMPTY geometry. Off
|
|
88
|
+
* for unknown / standard rhino.compute.
|
|
104
89
|
*/
|
|
105
90
|
computeReuseDefinitionCache: boolean;
|
|
106
91
|
/**
|
|
@@ -117,53 +102,52 @@ export interface ComputeLimits {
|
|
|
117
102
|
*/
|
|
118
103
|
computeCacheErroredSolves: boolean;
|
|
119
104
|
/**
|
|
120
|
-
* Backpressure — max solves allowed to WAIT in the per-server FIFO queue
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
* beats a hung request.
|
|
105
|
+
* Backpressure — max solves allowed to WAIT in the per-server FIFO queue. This
|
|
106
|
+
* excludes in-flight solves, which the scheduler caps at `maxConcurrent`,
|
|
107
|
+
* auto-detected from the compute server's active child count. A solve arriving
|
|
108
|
+
* at a full queue is rejected on the spot (scheduler `QUEUE_FULL` → HTTP 503 +
|
|
109
|
+
* `Retry-After`) instead of piling up. `0` = unbounded, the default.
|
|
110
|
+
*
|
|
111
|
+
* Size it to roughly 2–3× the compute server's child count once you know the
|
|
112
|
+
* Rhino pool's real capacity — rejecting fast beats a hung request.
|
|
129
113
|
*/
|
|
130
114
|
computeMaxQueueDepth: number;
|
|
131
115
|
/**
|
|
132
116
|
* Backpressure — longest (ms) a solve may sit QUEUED before it starts
|
|
133
|
-
* executing
|
|
134
|
-
* HTTP 503 + `Retry-After`) rather than burning compute on a stale request.
|
|
117
|
+
* executing. Still waiting past this, it's rejected (scheduler `QUEUE_TIMEOUT`
|
|
118
|
+
* → HTTP 503 + `Retry-After`) rather than burning compute on a stale request.
|
|
119
|
+
* `0` = no queue deadline, the default.
|
|
120
|
+
*
|
|
135
121
|
* Bounds tail latency: the scheduler's `timeoutMs` clock starts at execution,
|
|
136
|
-
* so without this a solve's total wait is unbounded and invisible.
|
|
137
|
-
*
|
|
122
|
+
* so without this a solve's total wait is unbounded and invisible. Tune it to
|
|
123
|
+
* ≈ `solveDeadlineMs`.
|
|
138
124
|
*/
|
|
139
125
|
computeQueueWaitMs: number;
|
|
140
126
|
/**
|
|
141
|
-
* **Definition cache** — total-byte budget
|
|
142
|
-
*
|
|
143
|
-
* `
|
|
144
|
-
*
|
|
145
|
-
* an entry count because definitions span KB→hundreds of MB. Env value is MB.
|
|
127
|
+
* **Definition cache** — total-byte budget for the in-process cache of .gh bytes
|
|
128
|
+
* keyed on immutable version id. A warm entry lets a solve skip `storage.get`;
|
|
129
|
+
* `0` disables it, so every solve re-reads storage. A byte LRU rather than an
|
|
130
|
+
* entry count because definitions span KB→hundreds of MB. Env value is MB.
|
|
146
131
|
*/
|
|
147
132
|
computeDefinitionCacheBytes: number;
|
|
148
133
|
/**
|
|
149
|
-
* **Solve cache** — total-byte budget
|
|
150
|
-
*
|
|
151
|
-
* Results
|
|
152
|
-
* entry
|
|
153
|
-
*
|
|
134
|
+
* **Solve cache** — total-byte budget for EACH warm client's in-process cache of
|
|
135
|
+
* solve results (the `fromCache` layer in front of Rhino.Compute). `0` disables
|
|
136
|
+
* it, so every solve goes to compute. Results span KB→100s of MB
|
|
137
|
+
* (`computeResponseMaxBytes` allows 300 MB), so an entry count alone can't bound
|
|
138
|
+
* heap; this adds a byte LRU on top. Env value is MB.
|
|
154
139
|
*
|
|
155
|
-
* **
|
|
156
|
-
* `maxWarmComputeServers` (default 16)
|
|
157
|
-
* ceiling
|
|
158
|
-
* which is why the default is comfortable
|
|
159
|
-
*
|
|
140
|
+
* **Per warm client, so the worst case multiplies.** Selva keeps up to
|
|
141
|
+
* `maxWarmComputeServers` (default 16) compute servers warm, making the real
|
|
142
|
+
* ceiling this × 16 — 4 GB at the default. Most deployments run 1–2 servers,
|
|
143
|
+
* which is why the default is comfortable; a fan-out across many servers on a
|
|
144
|
+
* small VPS is the case this knob exists for.
|
|
160
145
|
*/
|
|
161
146
|
computeSolveCacheBytes: number;
|
|
162
147
|
}
|
|
163
148
|
/**
|
|
164
|
-
* Resolve every server-side compute limit from an env map.
|
|
165
|
-
*
|
|
166
|
-
* this once at its composition root with `$env/dynamic/private`.
|
|
149
|
+
* Resolve every server-side compute limit from an env map. The app calls this
|
|
150
|
+
* once at its composition root with `$env/dynamic/private`.
|
|
167
151
|
*/
|
|
168
152
|
export declare function resolveComputeLimits(env: EnvRecord, logger?: ILogger): ComputeLimits;
|
|
169
153
|
//# sourceMappingURL=limits.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/compute/limits.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/compute/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAc,KAAK,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAI7D,gFAAgF;AAChF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAQ3D,sGAAsG;AACtG,wBAAgB,eAAe,CAC9B,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,OAAc,GACpB,MAAM,CAcR;AAED,uFAAuF;AACvF,wBAAgB,kBAAkB,CACjC,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,GAAE,OAAc,GACpB,MAAM,CAcR;AAoBD;;;GAGG;AACH,wBAAgB,QAAQ,CACvB,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,OAAO,EACjB,MAAM,GAAE,OAAc,GACpB,OAAO,CAaT;AAED,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC7B;;;;;;;;;OASG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,oFAAoF;IACpF,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAC9B,8CAA8C;IAC9C,gBAAgB,EAAE,MAAM,CAAC;IACzB;;;;;;OAMG;IACH,sBAAsB,EAAE,MAAM,CAAC;IAC/B;;;;;;;;;OASG;IACH,uBAAuB,EAAE,MAAM,CAAC;IAChC;;;OAGG;IACH,wBAAwB,EAAE,MAAM,CAAC;IACjC,wEAAwE;IACxE,8BAA8B,EAAE,MAAM,CAAC;IACvC;;;;;;;OAOG;IACH,0BAA0B,EAAE,MAAM,CAAC;IACnC;;;;;;OAMG;IACH,2BAA2B,EAAE,OAAO,CAAC;IACrC;;;;OAIG;IACH,uBAAuB,EAAE,OAAO,CAAC;IACjC;;;;;OAKG;IACH,yBAAyB,EAAE,OAAO,CAAC;IACnC;;;;;;;;;OASG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;;;;;;;OASG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,2BAA2B,EAAE,MAAM,CAAC;IACpC;;;;;;;;;;;;OAYG;IACH,sBAAsB,EAAE,MAAM,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,GAAE,OAAc,GAAG,aAAa,CAmD1F"}
|