@glubean/graphql 0.1.7 → 0.2.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 +433 -0
- package/dist/contract/adapter.d.ts +27 -0
- package/dist/contract/adapter.d.ts.map +1 -0
- package/dist/contract/adapter.js +543 -0
- package/dist/contract/adapter.js.map +1 -0
- package/dist/contract/factory.d.ts +34 -0
- package/dist/contract/factory.d.ts.map +1 -0
- package/dist/contract/factory.js +90 -0
- package/dist/contract/factory.js.map +1 -0
- package/dist/contract/index.d.ts +37 -0
- package/dist/contract/index.d.ts.map +1 -0
- package/dist/contract/index.js +55 -0
- package/dist/contract/index.js.map +1 -0
- package/dist/contract/matchers.d.ts +95 -0
- package/dist/contract/matchers.d.ts.map +1 -0
- package/dist/contract/matchers.js +246 -0
- package/dist/contract/matchers.js.map +1 -0
- package/dist/contract/types.d.ts +369 -0
- package/dist/contract/types.d.ts.map +1 -0
- package/dist/contract/types.js +41 -0
- package/dist/contract/types.js.map +1 -0
- package/dist/index.d.ts +24 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +28 -7
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* contract.graphql factory — scoped-defaults pattern.
|
|
3
|
+
*
|
|
4
|
+
* Root `contract.graphql` exposes only `.with(name, defaults)`. Calling
|
|
5
|
+
* `contract.graphql("id", spec)` directly is forbidden — client injection
|
|
6
|
+
* via scoped instances is the canonical authoring pattern (same as HTTP
|
|
7
|
+
* and gRPC).
|
|
8
|
+
*
|
|
9
|
+
* The factory wraps the generic dispatcher attached to `contract.graphql`
|
|
10
|
+
* by `contract.register("graphql", graphqlAdapter)`. Calls flow through:
|
|
11
|
+
* user code
|
|
12
|
+
* → scoped factory(id, spec)
|
|
13
|
+
* → merge instance defaults into spec
|
|
14
|
+
* → dispatcher(id, mergedSpec) [generic register() output]
|
|
15
|
+
* → adapter.project + per-case registerTest + Test[] with
|
|
16
|
+
* _projection/_spec
|
|
17
|
+
*/
|
|
18
|
+
function mergeExtensions(base, override) {
|
|
19
|
+
if (!base && !override)
|
|
20
|
+
return undefined;
|
|
21
|
+
const merged = {
|
|
22
|
+
...(base ?? {}),
|
|
23
|
+
...(override ?? {}),
|
|
24
|
+
};
|
|
25
|
+
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
26
|
+
}
|
|
27
|
+
function mergeGraphqlDefaults(defaults, spec) {
|
|
28
|
+
if (!defaults)
|
|
29
|
+
return spec;
|
|
30
|
+
const mergedTags = [...(defaults.tags ?? []), ...(spec.tags ?? [])];
|
|
31
|
+
const mergedExtensions = mergeExtensions(defaults.extensions, spec.extensions);
|
|
32
|
+
const mergedHeaders = {
|
|
33
|
+
...(defaults.headers ?? {}),
|
|
34
|
+
...(spec.defaultHeaders ?? {}),
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
...spec,
|
|
38
|
+
client: spec.client ?? defaults.client,
|
|
39
|
+
endpoint: spec.endpoint ?? defaults.endpoint,
|
|
40
|
+
feature: spec.feature ?? defaults.feature,
|
|
41
|
+
tags: mergedTags.length > 0 ? mergedTags : undefined,
|
|
42
|
+
extensions: mergedExtensions,
|
|
43
|
+
defaultHeaders: Object.keys(mergedHeaders).length > 0 ? mergedHeaders : undefined,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function applyInstanceMetadata(contract, instanceName) {
|
|
47
|
+
const proj = contract._projection;
|
|
48
|
+
proj.instanceName = instanceName;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Build a scoped GraphQL factory. `dispatch` is `contract.graphql` attached
|
|
52
|
+
* by the core's register() call — we wrap it to inject instance defaults.
|
|
53
|
+
*/
|
|
54
|
+
export function createGraphqlFactory(dispatch, defaults) {
|
|
55
|
+
const factory = (id, spec) => {
|
|
56
|
+
if (!defaults?._name) {
|
|
57
|
+
throw new Error(`contract.graphql("${id}", spec) is not supported. ` +
|
|
58
|
+
`Use contract.graphql.with("name", { client }) first to create a scoped instance, ` +
|
|
59
|
+
`then call instance("${id}", spec).`);
|
|
60
|
+
}
|
|
61
|
+
const merged = mergeGraphqlDefaults(defaults, spec);
|
|
62
|
+
const result = dispatch(id, merged);
|
|
63
|
+
applyInstanceMetadata(result, defaults._name);
|
|
64
|
+
return result;
|
|
65
|
+
};
|
|
66
|
+
factory.with = (name, more = {}) => {
|
|
67
|
+
const mergedTags = [...(defaults?.tags ?? []), ...(more.tags ?? [])];
|
|
68
|
+
const mergedExtensions = mergeExtensions(defaults?.extensions, more.extensions);
|
|
69
|
+
const mergedHeaders = {
|
|
70
|
+
...(defaults?.headers ?? {}),
|
|
71
|
+
...(more.headers ?? {}),
|
|
72
|
+
};
|
|
73
|
+
return createGraphqlFactory(dispatch, {
|
|
74
|
+
...defaults,
|
|
75
|
+
...more,
|
|
76
|
+
tags: mergedTags.length > 0 ? mergedTags : undefined,
|
|
77
|
+
extensions: mergedExtensions,
|
|
78
|
+
headers: Object.keys(mergedHeaders).length > 0 ? mergedHeaders : undefined,
|
|
79
|
+
_name: name,
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
return factory;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Root factory — `.with()` only, direct call throws.
|
|
86
|
+
*/
|
|
87
|
+
export function createGraphqlRoot(dispatch) {
|
|
88
|
+
return createGraphqlFactory(dispatch);
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/contract/factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL contract surface for @glubean/graphql 0.2.0.
|
|
3
|
+
*
|
|
4
|
+
* This module makes `@glubean/graphql` a single-package owner of both:
|
|
5
|
+
* - Transport / client layer (existing `../index.ts`, unchanged)
|
|
6
|
+
* - Contract adapter layer (this directory)
|
|
7
|
+
*
|
|
8
|
+
* Mirrors the same single-package design as `@glubean/grpc` 0.2.0.
|
|
9
|
+
* See `internal/00-product/positioning-v3.md` §12.0 resolved and
|
|
10
|
+
* `internal/40-discovery/proposals/contract-grpc-graphql-expansion.md` §6.1.
|
|
11
|
+
*
|
|
12
|
+
* Side-effect on import:
|
|
13
|
+
* 1. `contract.register("graphql", graphqlAdapter)` — registers dispatcher
|
|
14
|
+
* 2. Wrap dispatcher with `createGraphqlRoot` so
|
|
15
|
+
* `contract.graphql.with(name, defaults)` UX works
|
|
16
|
+
*
|
|
17
|
+
* After this module loads, users can:
|
|
18
|
+
*
|
|
19
|
+
* import "@glubean/graphql"; // side-effect: registers graphql contract adapter
|
|
20
|
+
* import { contract } from "@glubean/sdk";
|
|
21
|
+
*
|
|
22
|
+
* const api = contract.graphql.with("api", { client });
|
|
23
|
+
* export const getUser = api("get-user", {
|
|
24
|
+
* cases: {
|
|
25
|
+
* ok: {
|
|
26
|
+
* description: "success",
|
|
27
|
+
* query: `query GetUser($id: ID!) { user(id: $id) { name } }`,
|
|
28
|
+
* variables: { id: "1" },
|
|
29
|
+
* expect: { data: { user: { name: "Alice" } } },
|
|
30
|
+
* },
|
|
31
|
+
* },
|
|
32
|
+
* });
|
|
33
|
+
*/
|
|
34
|
+
export { graphqlAdapter } from "./adapter.js";
|
|
35
|
+
export { createGraphqlFactory, createGraphqlRoot } from "./factory.js";
|
|
36
|
+
export type { GraphqlContractCase, GraphqlContractDefaults, GraphqlContractExample, GraphqlContractExpect, GraphqlContractFactory, GraphqlContractMeta, GraphqlContractRoot, GraphqlContractSafeMeta, GraphqlContractSpec, GraphqlCaseResult, GraphqlErrorsExpect, GraphqlFlowCaseOutput, GraphqlPayloadSchemas, GraphqlSafeSchemas, GraphqlTypeDef, GraphqlTypeDefs, InferGraphqlVariables, InferGraphqlResponse, } from "./types.js";
|
|
37
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AA0BH,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACvE,YAAY,EACV,mBAAmB,EACnB,uBAAuB,EACvB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,mBAAmB,EACnB,uBAAuB,EACvB,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL contract surface for @glubean/graphql 0.2.0.
|
|
3
|
+
*
|
|
4
|
+
* This module makes `@glubean/graphql` a single-package owner of both:
|
|
5
|
+
* - Transport / client layer (existing `../index.ts`, unchanged)
|
|
6
|
+
* - Contract adapter layer (this directory)
|
|
7
|
+
*
|
|
8
|
+
* Mirrors the same single-package design as `@glubean/grpc` 0.2.0.
|
|
9
|
+
* See `internal/00-product/positioning-v3.md` §12.0 resolved and
|
|
10
|
+
* `internal/40-discovery/proposals/contract-grpc-graphql-expansion.md` §6.1.
|
|
11
|
+
*
|
|
12
|
+
* Side-effect on import:
|
|
13
|
+
* 1. `contract.register("graphql", graphqlAdapter)` — registers dispatcher
|
|
14
|
+
* 2. Wrap dispatcher with `createGraphqlRoot` so
|
|
15
|
+
* `contract.graphql.with(name, defaults)` UX works
|
|
16
|
+
*
|
|
17
|
+
* After this module loads, users can:
|
|
18
|
+
*
|
|
19
|
+
* import "@glubean/graphql"; // side-effect: registers graphql contract adapter
|
|
20
|
+
* import { contract } from "@glubean/sdk";
|
|
21
|
+
*
|
|
22
|
+
* const api = contract.graphql.with("api", { client });
|
|
23
|
+
* export const getUser = api("get-user", {
|
|
24
|
+
* cases: {
|
|
25
|
+
* ok: {
|
|
26
|
+
* description: "success",
|
|
27
|
+
* query: `query GetUser($id: ID!) { user(id: $id) { name } }`,
|
|
28
|
+
* variables: { id: "1" },
|
|
29
|
+
* expect: { data: { user: { name: "Alice" } } },
|
|
30
|
+
* },
|
|
31
|
+
* },
|
|
32
|
+
* });
|
|
33
|
+
*/
|
|
34
|
+
import { contract } from "@glubean/sdk";
|
|
35
|
+
import { graphqlAdapter } from "./adapter.js";
|
|
36
|
+
import { createGraphqlRoot } from "./factory.js";
|
|
37
|
+
import { registerGraphqlMatchers } from "./matchers.js";
|
|
38
|
+
// Step 1: register the adapter. After this, `contract.graphql` exists as the
|
|
39
|
+
// generic dispatcher attached by `contract.register()`.
|
|
40
|
+
contract.register("graphql", graphqlAdapter);
|
|
41
|
+
// Step 2: wrap dispatcher with the scoped-defaults factory so
|
|
42
|
+
// `contract.graphql.with(name, defaults)` UX works.
|
|
43
|
+
{
|
|
44
|
+
const dispatcher = contract.graphql;
|
|
45
|
+
contract.graphql = createGraphqlRoot(dispatcher);
|
|
46
|
+
}
|
|
47
|
+
// Step 3: register GraphQL custom matchers so
|
|
48
|
+
// `ctx.expect(res).toHaveGraphqlData({...})` / `.toHaveGraphqlNoErrors()` /
|
|
49
|
+
// `.toHaveHttpStatus(200)` / `.toHaveGraphqlErrorCode("UNAUTHENTICATED")`
|
|
50
|
+
// work out of the box for any `@glubean/graphql` user.
|
|
51
|
+
registerGraphqlMatchers();
|
|
52
|
+
// Re-exports for type consumers who import from the package directly.
|
|
53
|
+
export { graphqlAdapter } from "./adapter.js";
|
|
54
|
+
export { createGraphqlFactory, createGraphqlRoot } from "./factory.js";
|
|
55
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAGxD,6EAA6E;AAC7E,wDAAwD;AACxD,QAAQ,CAAC,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;AAE7C,8DAA8D;AAC9D,oDAAoD;AACpD,CAAC;IACC,MAAM,UAAU,GAAI,QAAgB,CAAC,OAAkD,CAAC;IACvF,QAAwD,CAAC,OAAO,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC;AACpG,CAAC;AAED,8CAA8C;AAC9C,4EAA4E;AAC5E,0EAA0E;AAC1E,uDAAuD;AACvD,uBAAuB,EAAE,CAAC;AAE1B,sEAAsE;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL custom matchers for `ctx.expect()`.
|
|
3
|
+
*
|
|
4
|
+
* Registered as a side effect of `import "@glubean/graphql"` (see
|
|
5
|
+
* `./index.ts`). No extra import or configure step required — the
|
|
6
|
+
* matchers become available on every `ctx.expect(res)` call and are
|
|
7
|
+
* fully typed via the `CustomMatchers<T>` declaration merging block
|
|
8
|
+
* below.
|
|
9
|
+
*
|
|
10
|
+
* Matchers work on any object that carries GraphQL response shape, including:
|
|
11
|
+
* - `GraphQLResult<T>` from `@glubean/graphql` transport
|
|
12
|
+
* (`gql.query(...)` / `gql.mutate(...)`)
|
|
13
|
+
* - `GraphqlCaseResult<T>` from the contract adapter
|
|
14
|
+
* (verify callback / flow `out` lens input)
|
|
15
|
+
*
|
|
16
|
+
* Both expose:
|
|
17
|
+
* {
|
|
18
|
+
* data: T | null,
|
|
19
|
+
* errors?: GraphQLError[],
|
|
20
|
+
* extensions?: Record<string, unknown>,
|
|
21
|
+
* httpStatus: number,
|
|
22
|
+
* headers: Record<string, string | string[]>,
|
|
23
|
+
* rawBody: string | null,
|
|
24
|
+
* ...
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* The HTTP `toHaveStatus` built-in matcher reads `actual.status`, which the
|
|
28
|
+
* GraphQL envelope does NOT expose (it's `httpStatus` — a deliberate rename
|
|
29
|
+
* in CG-10 to avoid shadowing the native `Response.status` semantics). The
|
|
30
|
+
* `toHaveHttpStatus` matcher added here reads the envelope's `httpStatus`
|
|
31
|
+
* field so GraphQL users have a symmetric transport-level assertion.
|
|
32
|
+
*/
|
|
33
|
+
declare module "@glubean/sdk/expect" {
|
|
34
|
+
interface CustomMatchers<T> {
|
|
35
|
+
/**
|
|
36
|
+
* Assert the transport-level HTTP status on a `GraphQLResult` /
|
|
37
|
+
* `GraphqlCaseResult`. Reads `actual.httpStatus`.
|
|
38
|
+
*
|
|
39
|
+
* Use this (not `toHaveStatus`) for GraphQL responses — the envelope
|
|
40
|
+
* field is `httpStatus`, not `status`.
|
|
41
|
+
*
|
|
42
|
+
* @example ctx.expect(res).toHaveHttpStatus(200);
|
|
43
|
+
* @example ctx.expect(res).toHaveHttpStatus(401, "missing token");
|
|
44
|
+
*/
|
|
45
|
+
toHaveHttpStatus(code: number, message?: string): Expectation<T>;
|
|
46
|
+
/**
|
|
47
|
+
* Partial-match the GraphQL `data` field (like `toMatchObject`).
|
|
48
|
+
*
|
|
49
|
+
* Fails if `data` is null or the subset is not contained in `data`.
|
|
50
|
+
*
|
|
51
|
+
* @example ctx.expect(res).toHaveGraphqlData({ user: { name: "Alice" } });
|
|
52
|
+
*/
|
|
53
|
+
toHaveGraphqlData(subset: Record<string, unknown>, message?: string): Expectation<T>;
|
|
54
|
+
/**
|
|
55
|
+
* Assert the `errors` array is absent or empty (strict success).
|
|
56
|
+
*
|
|
57
|
+
* @example ctx.expect(res).toHaveGraphqlNoErrors();
|
|
58
|
+
*/
|
|
59
|
+
toHaveGraphqlNoErrors(message?: string): Expectation<T>;
|
|
60
|
+
/**
|
|
61
|
+
* Assert at least one entry in `errors` has matching
|
|
62
|
+
* `extensions.code` (case-insensitive).
|
|
63
|
+
*
|
|
64
|
+
* @example ctx.expect(res).toHaveGraphqlErrorCode("UNAUTHENTICATED");
|
|
65
|
+
*/
|
|
66
|
+
toHaveGraphqlErrorCode(code: string, message?: string): Expectation<T>;
|
|
67
|
+
/**
|
|
68
|
+
* Assert a key exists in `extensions` (server-side tracing / cost),
|
|
69
|
+
* optionally matching an exact value.
|
|
70
|
+
*
|
|
71
|
+
* @example ctx.expect(res).toHaveGraphqlExtension("tracing");
|
|
72
|
+
* @example ctx.expect(res).toHaveGraphqlExtension("version", "v2");
|
|
73
|
+
*/
|
|
74
|
+
toHaveGraphqlExtension(key: string, value?: unknown, message?: string): Expectation<T>;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Register GraphQL matchers onto the shared `Expectation` prototype. Called
|
|
79
|
+
* from `./index.ts` during the same side-effect block that registers the
|
|
80
|
+
* contract adapter; users get matchers + adapter from a single
|
|
81
|
+
* `import "@glubean/graphql"`.
|
|
82
|
+
*
|
|
83
|
+
* Idempotent: swallows the "matcher already exists" error thrown by
|
|
84
|
+
* `Expectation.extend` on re-evaluation (duplicate imports, Vitest
|
|
85
|
+
* isolation boundaries, etc.).
|
|
86
|
+
*
|
|
87
|
+
* Note: `toHaveHttpStatus` is registered here even though it's
|
|
88
|
+
* conceptually transport-level (not GraphQL-specific) — users of
|
|
89
|
+
* `@glubean/graphql` are the ones who need it because the GraphQL
|
|
90
|
+
* envelope uses `httpStatus` instead of `status`. If a future package
|
|
91
|
+
* also exposes an `httpStatus` field, registration idempotency will
|
|
92
|
+
* catch the conflict at boot.
|
|
93
|
+
*/
|
|
94
|
+
export declare function registerGraphqlMatchers(): void;
|
|
95
|
+
//# sourceMappingURL=matchers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matchers.d.ts","sourceRoot":"","sources":["../../src/contract/matchers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AASH,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,cAAc,CAAC,CAAC;QACxB;;;;;;;;;WASG;QACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAEjE;;;;;;WAMG;QACH,iBAAiB,CACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,CAAC,EAAE,MAAM,GACf,WAAW,CAAC,CAAC,CAAC,CAAC;QAElB;;;;WAIG;QACH,qBAAqB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAExD;;;;;WAKG;QACH,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC;QAEvE;;;;;;WAMG;QACH,sBAAsB,CACpB,GAAG,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,OAAO,EACf,OAAO,CAAC,EAAE,MAAM,GACf,WAAW,CAAC,CAAC,CAAC,CAAC;KACnB;CACF;AAgPD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAe9C"}
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GraphQL custom matchers for `ctx.expect()`.
|
|
3
|
+
*
|
|
4
|
+
* Registered as a side effect of `import "@glubean/graphql"` (see
|
|
5
|
+
* `./index.ts`). No extra import or configure step required — the
|
|
6
|
+
* matchers become available on every `ctx.expect(res)` call and are
|
|
7
|
+
* fully typed via the `CustomMatchers<T>` declaration merging block
|
|
8
|
+
* below.
|
|
9
|
+
*
|
|
10
|
+
* Matchers work on any object that carries GraphQL response shape, including:
|
|
11
|
+
* - `GraphQLResult<T>` from `@glubean/graphql` transport
|
|
12
|
+
* (`gql.query(...)` / `gql.mutate(...)`)
|
|
13
|
+
* - `GraphqlCaseResult<T>` from the contract adapter
|
|
14
|
+
* (verify callback / flow `out` lens input)
|
|
15
|
+
*
|
|
16
|
+
* Both expose:
|
|
17
|
+
* {
|
|
18
|
+
* data: T | null,
|
|
19
|
+
* errors?: GraphQLError[],
|
|
20
|
+
* extensions?: Record<string, unknown>,
|
|
21
|
+
* httpStatus: number,
|
|
22
|
+
* headers: Record<string, string | string[]>,
|
|
23
|
+
* rawBody: string | null,
|
|
24
|
+
* ...
|
|
25
|
+
* }
|
|
26
|
+
*
|
|
27
|
+
* The HTTP `toHaveStatus` built-in matcher reads `actual.status`, which the
|
|
28
|
+
* GraphQL envelope does NOT expose (it's `httpStatus` — a deliberate rename
|
|
29
|
+
* in CG-10 to avoid shadowing the native `Response.status` semantics). The
|
|
30
|
+
* `toHaveHttpStatus` matcher added here reads the envelope's `httpStatus`
|
|
31
|
+
* field so GraphQL users have a symmetric transport-level assertion.
|
|
32
|
+
*/
|
|
33
|
+
import { Expectation, inspect } from "@glubean/sdk/expect";
|
|
34
|
+
function readHttpStatus(actual) {
|
|
35
|
+
const s = actual?.httpStatus;
|
|
36
|
+
return typeof s === "number" ? s : undefined;
|
|
37
|
+
}
|
|
38
|
+
function readEnvelope(actual) {
|
|
39
|
+
if (!actual || typeof actual !== "object")
|
|
40
|
+
return undefined;
|
|
41
|
+
return actual;
|
|
42
|
+
}
|
|
43
|
+
/** Minimal partial-match helper — checks that every key/value in subset
|
|
44
|
+
* is deep-equal to the corresponding path in target. Keeps this file
|
|
45
|
+
* dependency-free from the sdk internal `matchesObject`. */
|
|
46
|
+
function matchesSubset(target, subset) {
|
|
47
|
+
for (const [k, expected] of Object.entries(subset)) {
|
|
48
|
+
const actual = target[k];
|
|
49
|
+
if (expected !== null &&
|
|
50
|
+
typeof expected === "object" &&
|
|
51
|
+
!Array.isArray(expected)) {
|
|
52
|
+
if (!actual || typeof actual !== "object" || Array.isArray(actual)) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (!matchesSubset(actual, expected)) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else if (Array.isArray(expected)) {
|
|
60
|
+
if (!Array.isArray(actual) || actual.length !== expected.length) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
for (let i = 0; i < expected.length; i++) {
|
|
64
|
+
if (JSON.stringify(actual[i]) !== JSON.stringify(expected[i])) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else if (actual !== expected) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
// =============================================================================
|
|
76
|
+
// Matcher implementations
|
|
77
|
+
// =============================================================================
|
|
78
|
+
const toHaveHttpStatus = (actual, ...args) => {
|
|
79
|
+
const code = args[0];
|
|
80
|
+
const actualCode = readHttpStatus(actual);
|
|
81
|
+
if (actualCode === undefined) {
|
|
82
|
+
return {
|
|
83
|
+
passed: false,
|
|
84
|
+
message: `to have HTTP status ${code} — actual has no \`.httpStatus\` (got ${inspect(actual)})`,
|
|
85
|
+
actual,
|
|
86
|
+
expected: code,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
passed: actualCode === code,
|
|
91
|
+
message: `to have HTTP status ${code}`,
|
|
92
|
+
actual: actualCode,
|
|
93
|
+
expected: code,
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
const toHaveGraphqlData = (actual, ...args) => {
|
|
97
|
+
const subset = args[0];
|
|
98
|
+
const env = readEnvelope(actual);
|
|
99
|
+
if (!env) {
|
|
100
|
+
return {
|
|
101
|
+
passed: false,
|
|
102
|
+
message: `to have GraphQL data matching ${inspect(subset)} — actual is not an envelope`,
|
|
103
|
+
actual,
|
|
104
|
+
expected: subset,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const data = env.data;
|
|
108
|
+
if (data == null) {
|
|
109
|
+
return {
|
|
110
|
+
passed: false,
|
|
111
|
+
message: `to have GraphQL data matching ${inspect(subset)} — \`.data\` is null`,
|
|
112
|
+
actual: data,
|
|
113
|
+
expected: subset,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
if (typeof data !== "object" || Array.isArray(data)) {
|
|
117
|
+
return {
|
|
118
|
+
passed: false,
|
|
119
|
+
message: `to have GraphQL data matching ${inspect(subset)} — \`.data\` is not an object`,
|
|
120
|
+
actual: data,
|
|
121
|
+
expected: subset,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
const passed = matchesSubset(data, subset);
|
|
125
|
+
return {
|
|
126
|
+
passed,
|
|
127
|
+
message: `to have GraphQL data matching ${inspect(subset)}`,
|
|
128
|
+
actual: data,
|
|
129
|
+
expected: subset,
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
const toHaveGraphqlNoErrors = (actual) => {
|
|
133
|
+
const env = readEnvelope(actual);
|
|
134
|
+
const errs = env?.errors;
|
|
135
|
+
const count = Array.isArray(errs) ? errs.length : 0;
|
|
136
|
+
const summary = count > 0
|
|
137
|
+
? errs.map((e) => e.message).slice(0, 3).join("; ") +
|
|
138
|
+
(count > 3 ? ` (+${count - 3} more)` : "")
|
|
139
|
+
: "";
|
|
140
|
+
return {
|
|
141
|
+
passed: count === 0,
|
|
142
|
+
message: count > 0
|
|
143
|
+
? `to have no GraphQL errors (got ${count}: ${summary})`
|
|
144
|
+
: `to have no GraphQL errors`,
|
|
145
|
+
actual: errs ?? [],
|
|
146
|
+
expected: [],
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
const toHaveGraphqlErrorCode = (actual, ...args) => {
|
|
150
|
+
const code = args[0];
|
|
151
|
+
const env = readEnvelope(actual);
|
|
152
|
+
const errs = env?.errors;
|
|
153
|
+
if (!Array.isArray(errs) || errs.length === 0) {
|
|
154
|
+
return {
|
|
155
|
+
passed: false,
|
|
156
|
+
message: `to have GraphQL error code ${inspect(code)} — no errors on envelope`,
|
|
157
|
+
actual: errs ?? [],
|
|
158
|
+
expected: { "extensions.code": code },
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
const lower = code.toUpperCase();
|
|
162
|
+
const match = errs.find((e) => {
|
|
163
|
+
const c = e.extensions?.code;
|
|
164
|
+
return typeof c === "string" && c.toUpperCase() === lower;
|
|
165
|
+
});
|
|
166
|
+
return {
|
|
167
|
+
passed: !!match,
|
|
168
|
+
message: `to have GraphQL error code ${inspect(code)}`,
|
|
169
|
+
actual: errs.map((e) => e.extensions?.code ?? null),
|
|
170
|
+
expected: code,
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
const toHaveGraphqlExtension = (actual, ...args) => {
|
|
174
|
+
const key = args[0];
|
|
175
|
+
const expectedValue = args[1];
|
|
176
|
+
const env = readEnvelope(actual);
|
|
177
|
+
const ext = env?.extensions;
|
|
178
|
+
if (!ext || typeof ext !== "object") {
|
|
179
|
+
return {
|
|
180
|
+
passed: false,
|
|
181
|
+
message: `to have GraphQL extension \`${key}\` — actual has no \`.extensions\``,
|
|
182
|
+
actual: ext,
|
|
183
|
+
expected: expectedValue !== undefined ? { [key]: expectedValue } : key,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
if (!(key in ext)) {
|
|
187
|
+
return {
|
|
188
|
+
passed: false,
|
|
189
|
+
message: `to have GraphQL extension \`${key}\``,
|
|
190
|
+
actual: Object.keys(ext),
|
|
191
|
+
expected: expectedValue !== undefined ? { [key]: expectedValue } : key,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
if (expectedValue === undefined) {
|
|
195
|
+
return {
|
|
196
|
+
passed: true,
|
|
197
|
+
message: `to have GraphQL extension \`${key}\``,
|
|
198
|
+
actual: ext[key],
|
|
199
|
+
expected: key,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
return {
|
|
203
|
+
passed: JSON.stringify(ext[key]) === JSON.stringify(expectedValue),
|
|
204
|
+
message: `to have GraphQL extension \`${key}\` = ${inspect(expectedValue)}`,
|
|
205
|
+
actual: ext[key],
|
|
206
|
+
expected: expectedValue,
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
// =============================================================================
|
|
210
|
+
// Registration — side effect
|
|
211
|
+
// =============================================================================
|
|
212
|
+
/**
|
|
213
|
+
* Register GraphQL matchers onto the shared `Expectation` prototype. Called
|
|
214
|
+
* from `./index.ts` during the same side-effect block that registers the
|
|
215
|
+
* contract adapter; users get matchers + adapter from a single
|
|
216
|
+
* `import "@glubean/graphql"`.
|
|
217
|
+
*
|
|
218
|
+
* Idempotent: swallows the "matcher already exists" error thrown by
|
|
219
|
+
* `Expectation.extend` on re-evaluation (duplicate imports, Vitest
|
|
220
|
+
* isolation boundaries, etc.).
|
|
221
|
+
*
|
|
222
|
+
* Note: `toHaveHttpStatus` is registered here even though it's
|
|
223
|
+
* conceptually transport-level (not GraphQL-specific) — users of
|
|
224
|
+
* `@glubean/graphql` are the ones who need it because the GraphQL
|
|
225
|
+
* envelope uses `httpStatus` instead of `status`. If a future package
|
|
226
|
+
* also exposes an `httpStatus` field, registration idempotency will
|
|
227
|
+
* catch the conflict at boot.
|
|
228
|
+
*/
|
|
229
|
+
export function registerGraphqlMatchers() {
|
|
230
|
+
try {
|
|
231
|
+
Expectation.extend({
|
|
232
|
+
toHaveHttpStatus,
|
|
233
|
+
toHaveGraphqlData,
|
|
234
|
+
toHaveGraphqlNoErrors,
|
|
235
|
+
toHaveGraphqlErrorCode,
|
|
236
|
+
toHaveGraphqlExtension,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
catch (err) {
|
|
240
|
+
if (err instanceof Error && /already exists/.test(err.message)) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
throw err;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
//# sourceMappingURL=matchers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matchers.js","sourceRoot":"","sources":["../../src/contract/matchers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,WAAW,EAAE,OAAO,EAAsB,MAAM,qBAAqB,CAAC;AA6E/E,SAAS,cAAc,CAAC,MAAe;IACrC,MAAM,CAAC,GAAI,MAA6C,EAAE,UAAU,CAAC;IACrE,OAAO,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AAC/C,CAAC;AAED,SAAS,YAAY,CAAC,MAAe;IACnC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAC5D,OAAO,MAA8B,CAAC;AACxC,CAAC;AAED;;6DAE6D;AAC7D,SAAS,aAAa,CACpB,MAA+B,EAC/B,MAA+B;IAE/B,KAAK,MAAM,CAAC,CAAC,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;QACzB,IACE,QAAQ,KAAK,IAAI;YACjB,OAAO,QAAQ,KAAK,QAAQ;YAC5B,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EACxB,CAAC;YACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBACnE,OAAO,KAAK,CAAC;YACf,CAAC;YACD,IACE,CAAC,aAAa,CACZ,MAAiC,EACjC,QAAmC,CACpC,EACD,CAAC;gBACD,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;aAAM,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;YACnC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM,EAAE,CAAC;gBAChE,OAAO,KAAK,CAAC;YACf,CAAC;YACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACzC,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC9D,OAAO,KAAK,CAAC;gBACf,CAAC;YACH,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,gFAAgF;AAChF,0BAA0B;AAC1B,gFAAgF;AAEhF,MAAM,gBAAgB,GAAG,CACvB,MAAe,EACf,GAAG,IAAe,EACH,EAAE;IACjB,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAW,CAAC;IAC/B,MAAM,UAAU,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC;IAE1C,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO;YACL,MAAM,EAAE,KAAK;YACb,OAAO,EACL,uBAAuB,IAAI,yCAAyC,OAAO,CAAC,MAAM,CAAC,GAAG;YACxF,MAAM;YACN,QAAQ,EAAE,IAAI;SACf,CAAC;IACJ,CAAC;IAED,OAAO;QACL,MAAM,EAAE,UAAU,KAAK,IAAI;QAC3B,OAAO,EAAE,uBAAuB,IAAI,EAAE;QACtC,MAAM,EAAE,UAAU;QAClB,QAAQ,EAAE,IAAI;KACf,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,iBAAiB,GAAG,CACxB,MAAe,EACf,GAAG,IAAe,EACH,EAAE;IACjB,MAAM,MAAM,GAAG,IAAI,CAAC,CAAC,CAA4B,CAAC;IAClD,MAAM,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IAEjC,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO;YACL,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,iCAAiC,OAAO,CAAC,MAAM,CAAC,8BAA8B;YACvF,MAAM;YACN,QAAQ,EAAE,MAAM;SACjB,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;IACtB,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;QACjB,OAAO;YACL,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,iCAAiC,OAAO,CAAC,MAAM,CAAC,sBAAsB;YAC/E,MAAM,EAAE,IAAI;YACZ,QAAQ,EAAE,MAAM;SACjB,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;QACpD,OAAO;YACL,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,iCAAiC,OAAO,CAAC,MAAM,CAAC,+BAA+B;YACxF,MAAM,EAAE,IAAI;YACZ,QAAQ,EAAE,MAAM;SACjB,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,aAAa,CAAC,IAA+B,EAAE,MAAM,CAAC,CAAC;IACtE,OAAO;QACL,MAAM;QACN,OAAO,EAAE,iCAAiC,OAAO,CAAC,MAAM,CAAC,EAAE;QAC3D,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,MAAM;KACjB,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,qBAAqB,GAAG,CAAC,MAAe,EAAiB,EAAE;IAC/D,MAAM,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,GAAG,EAAE,MAAM,CAAC;IACzB,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,MAAM,OAAO,GACX,KAAK,GAAG,CAAC;QACP,CAAC,CAAC,IAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YAClD,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5C,CAAC,CAAC,EAAE,CAAC;IAET,OAAO;QACL,MAAM,EAAE,KAAK,KAAK,CAAC;QACnB,OAAO,EACL,KAAK,GAAG,CAAC;YACP,CAAC,CAAC,kCAAkC,KAAK,KAAK,OAAO,GAAG;YACxD,CAAC,CAAC,2BAA2B;QACjC,MAAM,EAAE,IAAI,IAAI,EAAE;QAClB,QAAQ,EAAE,EAAE;KACb,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,sBAAsB,GAAG,CAC7B,MAAe,EACf,GAAG,IAAe,EACH,EAAE;IACjB,MAAM,IAAI,GAAG,IAAI,CAAC,CAAC,CAAW,CAAC;IAC/B,MAAM,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,IAAI,GAAG,GAAG,EAAE,MAAM,CAAC;IAEzB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9C,OAAO;YACL,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,8BAA8B,OAAO,CAAC,IAAI,CAAC,0BAA0B;YAC9E,MAAM,EAAE,IAAI,IAAI,EAAE;YAClB,QAAQ,EAAE,EAAE,iBAAiB,EAAE,IAAI,EAAE;SACtC,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE;QAC5B,MAAM,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,IAAI,CAAC;QAC7B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,OAAO;QACL,MAAM,EAAE,CAAC,CAAC,KAAK;QACf,OAAO,EAAE,8BAA8B,OAAO,CAAC,IAAI,CAAC,EAAE;QACtD,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,IAAI,IAAI,IAAI,CAAC;QACnD,QAAQ,EAAE,IAAI;KACf,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,sBAAsB,GAAG,CAC7B,MAAe,EACf,GAAG,IAAe,EACH,EAAE;IACjB,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAW,CAAC;IAC9B,MAAM,aAAa,GAAG,IAAI,CAAC,CAAC,CAAY,CAAC;IACzC,MAAM,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,GAAG,GAAG,GAAG,EAAE,UAAU,CAAC;IAE5B,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACpC,OAAO;YACL,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,+BAA+B,GAAG,oCAAoC;YAC/E,MAAM,EAAE,GAAG;YACX,QAAQ,EAAE,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,GAAG;SACvE,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,EAAE,CAAC;QAClB,OAAO;YACL,MAAM,EAAE,KAAK;YACb,OAAO,EAAE,+BAA+B,GAAG,IAAI;YAC/C,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;YACxB,QAAQ,EAAE,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,GAAG;SACvE,CAAC;IACJ,CAAC;IAED,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,OAAO;YACL,MAAM,EAAE,IAAI;YACZ,OAAO,EAAE,+BAA+B,GAAG,IAAI;YAC/C,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC;YAChB,QAAQ,EAAE,GAAG;SACd,CAAC;IACJ,CAAC;IAED,OAAO;QACL,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC;QAClE,OAAO,EAAE,+BAA+B,GAAG,QAAQ,OAAO,CAAC,aAAa,CAAC,EAAE;QAC3E,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC;QAChB,QAAQ,EAAE,aAAa;KACxB,CAAC;AACJ,CAAC,CAAC;AAEF,gFAAgF;AAChF,6BAA6B;AAC7B,gFAAgF;AAEhF;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,uBAAuB;IACrC,IAAI,CAAC;QACH,WAAW,CAAC,MAAM,CAAC;YACjB,gBAAgB;YAChB,iBAAiB;YACjB,qBAAqB;YACrB,sBAAsB;YACtB,sBAAsB;SACvB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,KAAK,IAAI,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/D,OAAO;QACT,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC"}
|