@znight/graphql 0.0.0 → 0.2.0

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 (62) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1063 -0
  3. package/dist/compiler.d.mts +93 -0
  4. package/dist/compiler.mjs +2832 -0
  5. package/dist/compiler.mjs.map +1 -0
  6. package/dist/index.d.mts +542 -0
  7. package/dist/index.mjs +128 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/dist/names-BlzDM5cF.mjs +111 -0
  10. package/dist/names-BlzDM5cF.mjs.map +1 -0
  11. package/dist/plugin-BdBxr5NQ.mjs +3222 -0
  12. package/dist/plugin-BdBxr5NQ.mjs.map +1 -0
  13. package/dist/testing.d.mts +101 -0
  14. package/dist/testing.mjs +306 -0
  15. package/dist/testing.mjs.map +1 -0
  16. package/dist/types-CgbV9LjD.d.mts +234 -0
  17. package/package.json +97 -1
  18. package/src/compiler/analyze.ts +608 -0
  19. package/src/compiler/builder.ts +1640 -0
  20. package/src/compiler/constants.ts +74 -0
  21. package/src/compiler/data.ts +73 -0
  22. package/src/compiler/defaults.ts +92 -0
  23. package/src/compiler/discriminators.ts +53 -0
  24. package/src/compiler/docs.ts +55 -0
  25. package/src/compiler/generate.ts +405 -0
  26. package/src/compiler/ids.ts +128 -0
  27. package/src/compiler/index.ts +93 -0
  28. package/src/compiler/locate.ts +44 -0
  29. package/src/compiler/model.ts +122 -0
  30. package/src/compiler/options.ts +54 -0
  31. package/src/compiler/scalars.ts +138 -0
  32. package/src/compiler/sdl-view.ts +59 -0
  33. package/src/context.ts +181 -0
  34. package/src/decisions.ts +169 -0
  35. package/src/decorators.ts +196 -0
  36. package/src/errors.ts +89 -0
  37. package/src/execution.ts +1064 -0
  38. package/src/format.ts +285 -0
  39. package/src/index.ts +84 -0
  40. package/src/limits.ts +177 -0
  41. package/src/loaders.ts +149 -0
  42. package/src/memory-pubsub.ts +133 -0
  43. package/src/metadata.ts +195 -0
  44. package/src/names.ts +47 -0
  45. package/src/options.ts +309 -0
  46. package/src/pipeline.ts +277 -0
  47. package/src/plugin.ts +557 -0
  48. package/src/pubsub.ts +36 -0
  49. package/src/scalars.ts +131 -0
  50. package/src/schema-hash.ts +18 -0
  51. package/src/schema.ts +557 -0
  52. package/src/sdl.ts +515 -0
  53. package/src/testing.ts +454 -0
  54. package/src/tokens.ts +22 -0
  55. package/src/transport/abort.ts +80 -0
  56. package/src/transport/accept.ts +155 -0
  57. package/src/transport/graphiql.ts +106 -0
  58. package/src/transport/http.ts +373 -0
  59. package/src/transport/sse.ts +45 -0
  60. package/src/type-ref.ts +89 -0
  61. package/src/types.ts +190 -0
  62. package/src/validate-args.ts +48 -0
@@ -0,0 +1,101 @@
1
+ import { f as LoaderKey, m as LoaderValue, r as BatchLoader, t as BatchLoad } from "./types-CgbV9LjD.mjs";
2
+ import { GraphQLFormattedError } from "graphql";
3
+ import { MockFunction, TestApp } from "@znight/testing";
4
+ /** An operation for {@link executeOperation} and {@link subscribeOperation}. */
5
+ export interface OperationRequest {
6
+ readonly query: string;
7
+ readonly variables?: Readonly<Record<string, unknown>>;
8
+ readonly operationName?: string;
9
+ readonly extensions?: Readonly<Record<string, unknown>>;
10
+ /**
11
+ * Request headers (`x-api-key`, `accept`...), merged over the defaults (`content-type:
12
+ * application/json`, and `accept: application/graphql-response+json, application/json;q=0.9`
13
+ * or `text/event-stream`). Ignored without HTTP transport.
14
+ */
15
+ readonly headers?: Readonly<Record<string, string>>;
16
+ }
17
+ /** A GraphQL result: `data`, `errors` and `extensions` when present. */
18
+ export interface OperationResult<TData = Record<string, unknown>> {
19
+ readonly data?: TData | null;
20
+ readonly errors?: readonly GraphQLFormattedError[];
21
+ readonly extensions?: Readonly<Record<string, unknown>>;
22
+ }
23
+ /** What {@link executeOperation} resolves with. */
24
+ export interface OperationResponse<TData = Record<string, unknown>> extends OperationResult<TData> {
25
+ /**
26
+ * The HTTP status: 200 for an executed operation (field errors included), 400 for a request
27
+ * error (the default `accept` is `application/graphql-response+json`), 403 for an HTTP guard...
28
+ * Without HTTP transport, the status the endpoint would answer.
29
+ */
30
+ readonly status: number;
31
+ readonly headers: Readonly<Record<string, string | readonly string[] | undefined>>;
32
+ /** The raw body (an HTTP error body is not a GraphQL result: `data` and `errors` are then absent). */
33
+ readonly body: string;
34
+ }
35
+ /** Options of {@link OperationStream.next}. */
36
+ export interface OperationStreamWaitOptions {
37
+ /** Maximum wait for the next result (ms). Default 2 000. */
38
+ readonly timeoutMs?: number;
39
+ }
40
+ /**
41
+ * The results of {@link subscribeOperation}, one per `next` event. Ends after `complete` (or the
42
+ * single result of a query, or a request error). `return()` disconnects: the server sees a client
43
+ * abort (`ctx.signal`, then the source's `return()`).
44
+ */
45
+ export interface OperationStream<TData = Record<string, unknown>> extends AsyncIterableIterator<OperationResult<TData>>, AsyncDisposable {
46
+ /** HTTP status of the stream (200), or of a request error (400, 405, 503...). */
47
+ readonly status: number;
48
+ readonly headers: Readonly<Record<string, string | readonly string[] | undefined>>;
49
+ /** The next result. Rejects when none arrives within `timeoutMs` (2 000 ms by default). */
50
+ next(options?: OperationStreamWaitOptions): Promise<IteratorResult<OperationResult<TData>, undefined>>;
51
+ /** Ends the stream (disconnects). */
52
+ return(): Promise<IteratorResult<OperationResult<TData>, undefined>>;
53
+ }
54
+ /** The application `executeOperation()` and `subscribeOperation()` run against: a `TestApp`. */
55
+ export type GraphQLTestApp = Pick<TestApp, "app" | "inject" | "injectStream">;
56
+ /**
57
+ * Executes a query or a mutation through the whole HTTP pipeline (`app.inject()`: `POST` on the
58
+ * endpoint), or through `GraphQLPlugin` without HTTP transport.
59
+ *
60
+ * ```ts
61
+ * const res = await executeOperation(app, { query: '{ books { title } }', headers: { 'x-api-key': KEY } })
62
+ * expect(res.status).toBe(200)
63
+ * expect(res.errors).toBeUndefined()
64
+ * ```
65
+ */
66
+ export declare function executeOperation<TData = Record<string, unknown>>(app: GraphQLTestApp, request: OperationRequest): Promise<OperationResponse<TData>>;
67
+ /**
68
+ * Starts an operation as a stream: GraphQL over SSE in memory (`app.injectStream()`: `POST` with
69
+ * `accept: text/event-stream`), or `GraphQLPlugin.subscribe()` without HTTP transport (no HTTP
70
+ * guards then). Resolves once the subscription is established: its source has started (a resolver
71
+ * written as an async generator has subscribed to the PubSub, when it subscribes before its first
72
+ * `await`), so events published from then on
73
+ * are received, even before the first `next()`.
74
+ *
75
+ * ```ts
76
+ * await using events = await subscribeOperation(app, { query: 'subscription { bookAdded { title } }' })
77
+ * await executeOperation(app, { query: ADD_DUNE })
78
+ * expect((await events.next()).value).toEqual({ data: { bookAdded: { title: 'Dune' } } })
79
+ * ```
80
+ */
81
+ export declare function subscribeOperation<TData = Record<string, unknown>>(app: GraphQLTestApp, request: OperationRequest): Promise<OperationStream<TData>>;
82
+ /** Options of {@link fakeLoader}: what a key resolves to. */
83
+ export type FakeLoaderValues<K, V> = Readonly<Record<string, V | Error>> | ReadonlyMap<K, V | Error> | ((key: K) => V | Error | Promise<V | Error>);
84
+ /** A `Loader<L>` built by {@link fakeLoader}: `load` and `loadMany` are spies. */
85
+ export interface FakeLoader<K, V> extends BatchLoader<K, V> {
86
+ readonly load: MockFunction<(key: K) => Promise<V>> & ((key: K) => Promise<V>);
87
+ readonly loadMany: MockFunction<(keys: readonly K[]) => Promise<(V | Error)[]>> & ((keys: readonly K[]) => Promise<(V | Error)[]>);
88
+ }
89
+ /**
90
+ * A `Loader<L>` for unit tests of resolvers (with `TestBed`), answering from `values` (a record by
91
+ * key, a map, or a function of the key; an `Error` value fails that key). `load` and `loadMany` are
92
+ * spies; a missing key rejects.
93
+ *
94
+ * ```ts
95
+ * const authors = fakeLoader<AuthorsById>({ [herbert.id]: herbert })
96
+ * expect(await instance.author(dune, authors)).toBe(herbert)
97
+ * expect(authors.load).toHaveBeenCalledWith(herbert.id)
98
+ * ```
99
+ */
100
+ export declare function fakeLoader<L extends BatchLoad<unknown, unknown>>(values: FakeLoaderValues<LoaderKey<L>, LoaderValue<L>>): FakeLoader<LoaderKey<L>, LoaderValue<L>>;
101
+ //# sourceMappingURL=testing.d.mts.map
@@ -0,0 +1,306 @@
1
+ import { n as endpointOf, r as executorOf } from "./plugin-BdBxr5NQ.mjs";
2
+ import { parseSseStream } from "@znight/http";
3
+ import { resolveMockFn } from "@znight/testing";
4
+ // src/testing.ts
5
+ const DEFAULT_TIMEOUT_MS = 2e3;
6
+ const RESPONSE_ACCEPT = "application/graphql-response+json, application/json;q=0.9";
7
+ const DONE = Object.freeze({
8
+ done: true,
9
+ value: void 0
10
+ });
11
+ /**
12
+ * Executes a query or a mutation through the whole HTTP pipeline (`app.inject()`: `POST` on the
13
+ * endpoint), or through `GraphQLPlugin` without HTTP transport.
14
+ *
15
+ * ```ts
16
+ * const res = await executeOperation(app, { query: '{ books { title } }', headers: { 'x-api-key': KEY } })
17
+ * expect(res.status).toBe(200)
18
+ * expect(res.errors).toBeUndefined()
19
+ * ```
20
+ */
21
+ async function executeOperation(app, request) {
22
+ const plugin = graphqlPlugin(app, "executeOperation()");
23
+ const body = operationBody(request, "executeOperation()");
24
+ const endpoint = endpointOf(plugin);
25
+ if (endpoint === void 0) {
26
+ const response = await executorOf(plugin).execute(body);
27
+ return {
28
+ ...pick(response.result),
29
+ status: response.status,
30
+ headers: { ...response.headers },
31
+ body: JSON.stringify(response.result)
32
+ };
33
+ }
34
+ const response = await app.inject({
35
+ method: "POST",
36
+ url: endpoint,
37
+ headers: headers(request, RESPONSE_ACCEPT),
38
+ body: JSON.stringify(body)
39
+ });
40
+ return {
41
+ ...parseResult(response.body),
42
+ status: response.status,
43
+ headers: response.headers,
44
+ body: response.body
45
+ };
46
+ }
47
+ /**
48
+ * Starts an operation as a stream: GraphQL over SSE in memory (`app.injectStream()`: `POST` with
49
+ * `accept: text/event-stream`), or `GraphQLPlugin.subscribe()` without HTTP transport (no HTTP
50
+ * guards then). Resolves once the subscription is established: its source has started (a resolver
51
+ * written as an async generator has subscribed to the PubSub, when it subscribes before its first
52
+ * `await`), so events published from then on
53
+ * are received, even before the first `next()`.
54
+ *
55
+ * ```ts
56
+ * await using events = await subscribeOperation(app, { query: 'subscription { bookAdded { title } }' })
57
+ * await executeOperation(app, { query: ADD_DUNE })
58
+ * expect((await events.next()).value).toEqual({ data: { bookAdded: { title: 'Dune' } } })
59
+ * ```
60
+ */
61
+ async function subscribeOperation(app, request) {
62
+ const plugin = graphqlPlugin(app, "subscribeOperation()");
63
+ const body = operationBody(request, "subscribeOperation()");
64
+ const endpoint = endpointOf(plugin);
65
+ const label = describeOperation(body.query);
66
+ if (endpoint === void 0) {
67
+ const outcome = await executorOf(plugin).subscribe(body);
68
+ if ("status" in outcome) return new SingleResult(outcome.status, {}, pick(outcome.result));
69
+ return new Results(200, {}, directSource(outcome), label);
70
+ }
71
+ const response = await app.injectStream({
72
+ method: "POST",
73
+ url: endpoint,
74
+ headers: headers(request, "text/event-stream"),
75
+ body: JSON.stringify(body)
76
+ });
77
+ const type = response.headers["content-type"];
78
+ if (typeof type !== "string" || !type.startsWith("text/event-stream")) {
79
+ const text = await new Response(response.body).text();
80
+ return new SingleResult(response.status, response.headers, parseResult(text));
81
+ }
82
+ return new Results(response.status, response.headers, sseSource(parseSseStream(response.body)), label);
83
+ }
84
+ /**
85
+ * A `Loader<L>` for unit tests of resolvers (with `TestBed`), answering from `values` (a record by
86
+ * key, a map, or a function of the key; an `Error` value fails that key). `load` and `loadMany` are
87
+ * spies; a missing key rejects.
88
+ *
89
+ * ```ts
90
+ * const authors = fakeLoader<AuthorsById>({ [herbert.id]: herbert })
91
+ * expect(await instance.author(dune, authors)).toBe(herbert)
92
+ * expect(authors.load).toHaveBeenCalledWith(herbert.id)
93
+ * ```
94
+ */
95
+ function fakeLoader(values) {
96
+ const resolve = lookup(values);
97
+ const cache = /* @__PURE__ */ new Map();
98
+ const loadOne = (key) => {
99
+ const cached = cache.get(key);
100
+ if (cached !== void 0) return cached;
101
+ const loaded = Promise.resolve().then(() => resolve(key)).then((value) => value instanceof Error ? Promise.reject(value) : value);
102
+ cache.set(key, loaded);
103
+ return loaded;
104
+ };
105
+ const create = resolveMockFn().create;
106
+ return {
107
+ load: create((key) => loadOne(key)),
108
+ loadMany: create((keys) => Promise.all(keys.map((key) => loadOne(key).catch((error) => asError(error))))),
109
+ clear(key) {
110
+ cache.delete(key);
111
+ return this;
112
+ },
113
+ clearAll() {
114
+ cache.clear();
115
+ return this;
116
+ },
117
+ prime(key, value) {
118
+ if (!cache.has(key)) {
119
+ const primed = value instanceof Error ? Promise.reject(value) : Promise.resolve(value);
120
+ primed.catch(() => void 0);
121
+ cache.set(key, primed);
122
+ }
123
+ return this;
124
+ }
125
+ };
126
+ }
127
+ function graphqlPlugin(app, what) {
128
+ const plugin = app?.app?.getPlugin("graphql");
129
+ if (plugin === void 0) throw new TypeError(`${what} expects a TestApp whose plugins include GraphQLPlugin.configure()`);
130
+ return plugin;
131
+ }
132
+ function operationBody(request, what) {
133
+ if (request === null || typeof request !== "object" || typeof request.query !== "string") throw new TypeError(`${what} expects { query, variables?, operationName?, extensions?, headers? }`);
134
+ return {
135
+ query: request.query,
136
+ ...request.variables === void 0 ? {} : { variables: request.variables },
137
+ ...request.operationName === void 0 ? {} : { operationName: request.operationName },
138
+ ...request.extensions === void 0 ? {} : { extensions: request.extensions }
139
+ };
140
+ }
141
+ /** The request headers: the defaults, then the operation's (names lower-cased, so they replace the defaults). */
142
+ function headers(request, accept) {
143
+ const result = {
144
+ "content-type": "application/json",
145
+ accept
146
+ };
147
+ for (const [name, value] of Object.entries(request.headers ?? {})) result[name.toLowerCase()] = value;
148
+ return result;
149
+ }
150
+ function pick(result) {
151
+ return {
152
+ ..."data" in result ? { data: result.data } : {},
153
+ ...result.errors === void 0 ? {} : { errors: result.errors },
154
+ ...result.extensions === void 0 ? {} : { extensions: result.extensions }
155
+ };
156
+ }
157
+ /** The GraphQL result of a body; `{}` when it is not one (an HTTP error body, a page). */
158
+ function parseResult(body) {
159
+ let parsed;
160
+ try {
161
+ parsed = JSON.parse(body);
162
+ } catch {
163
+ return {};
164
+ }
165
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
166
+ const result = parsed;
167
+ if (!("data" in result) && !Array.isArray(result.errors)) return {};
168
+ return pick(result);
169
+ }
170
+ function describeOperation(query) {
171
+ const text = query.replace(/\s+/g, " ").trim();
172
+ return text.length > 60 ? `${text.slice(0, 60)}…` : text;
173
+ }
174
+ function asError(error) {
175
+ return error instanceof Error ? error : new Error(String(error));
176
+ }
177
+ function lookup(values) {
178
+ if (typeof values === "function") return values;
179
+ if (values instanceof Map) return (key) => values.has(key) ? values.get(key) : missing(key);
180
+ const record = values;
181
+ return (key) => Object.hasOwn(record, String(key)) ? record[String(key)] : missing(key);
182
+ }
183
+ function missing(key) {
184
+ return /* @__PURE__ */ new Error(`fakeLoader(): no value for the key ${JSON.stringify(key) ?? String(key)}`);
185
+ }
186
+ function directSource(stream) {
187
+ return {
188
+ next: async () => {
189
+ const step = await stream.next();
190
+ return step.done === true ? void 0 : step.value;
191
+ },
192
+ close: async () => {
193
+ await stream.return();
194
+ }
195
+ };
196
+ }
197
+ /** The `next` events of an event stream, until `complete` (comments, `retry` and other events skipped). */
198
+ function sseSource(items) {
199
+ return {
200
+ next: async () => {
201
+ for (;;) {
202
+ const step = await items.next();
203
+ if (step.done === true) return void 0;
204
+ const item = step.value;
205
+ if (item.type !== "event") continue;
206
+ if (item.event === "complete") return void 0;
207
+ if (item.event === "next") return JSON.parse(item.data);
208
+ }
209
+ },
210
+ close: async () => {
211
+ await items.return?.();
212
+ }
213
+ };
214
+ }
215
+ /** The stream of a subscription (or of a query sent as a stream). */
216
+ var Results = class {
217
+ status;
218
+ headers;
219
+ #source;
220
+ #label;
221
+ /** A read still pending after a timeout: the next call waits for it, so no result is lost. */
222
+ #pending;
223
+ #ended = false;
224
+ constructor(status, headers, source, label) {
225
+ this.status = status;
226
+ this.headers = headers;
227
+ this.#source = source;
228
+ this.#label = label;
229
+ }
230
+ async next(options = {}) {
231
+ if (this.#ended) return DONE;
232
+ const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
233
+ this.#pending ??= this.#source.next();
234
+ const pending = this.#pending;
235
+ let timer;
236
+ const timeout = new Promise((resolve) => {
237
+ timer = setTimeout(() => resolve(TIMEOUT), timeoutMs);
238
+ });
239
+ let result;
240
+ try {
241
+ result = await Promise.race([pending, timeout]);
242
+ } catch (error) {
243
+ this.#pending = void 0;
244
+ await this.return();
245
+ throw error;
246
+ } finally {
247
+ clearTimeout(timer);
248
+ }
249
+ if (result === TIMEOUT) throw new Error(`subscribeOperation(): no result within ${timeoutMs} ms for ${this.#label}`);
250
+ this.#pending = void 0;
251
+ if (result === void 0) {
252
+ await this.return();
253
+ return DONE;
254
+ }
255
+ return {
256
+ done: false,
257
+ value: pick(result)
258
+ };
259
+ }
260
+ async return() {
261
+ if (!this.#ended) {
262
+ this.#ended = true;
263
+ await this.#source.close();
264
+ }
265
+ return DONE;
266
+ }
267
+ [Symbol.asyncIterator]() {
268
+ return this;
269
+ }
270
+ [Symbol.asyncDispose]() {
271
+ return this.return().then(() => void 0);
272
+ }
273
+ };
274
+ const TIMEOUT = Symbol("timeout");
275
+ /** A request error (or a query's result without HTTP transport): one result, then the end. */
276
+ var SingleResult = class {
277
+ status;
278
+ headers;
279
+ #result;
280
+ constructor(status, headers, result) {
281
+ this.status = status;
282
+ this.headers = headers;
283
+ this.#result = result;
284
+ }
285
+ next() {
286
+ const result = this.#result;
287
+ this.#result = void 0;
288
+ return Promise.resolve(result === void 0 ? DONE : {
289
+ done: false,
290
+ value: result
291
+ });
292
+ }
293
+ return() {
294
+ this.#result = void 0;
295
+ return Promise.resolve(DONE);
296
+ }
297
+ [Symbol.asyncIterator]() {
298
+ return this;
299
+ }
300
+ [Symbol.asyncDispose]() {
301
+ return Promise.resolve();
302
+ }
303
+ };
304
+ export { executeOperation, fakeLoader, subscribeOperation };
305
+
306
+ //# sourceMappingURL=testing.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testing.mjs","names":[],"sources":["../src/testing.ts"],"sourcesContent":["/**\n * `@znight/graphql/testing`: test helpers for GraphQL applications (RFC 0005 §5.12).\n *\n * - `executeOperation()` sends an operation through the whole HTTP pipeline of a `TestApp`\n * (`app.inject()`: guards, hooks, every adapter) or, without HTTP transport, through\n * `GraphQLPlugin.execute()`;\n * - `subscribeOperation()` streams an operation over GraphQL over SSE in memory, or through\n * `GraphQLPlugin.subscribe()` without HTTP transport;\n * - `fakeLoader()` builds a `Loader<L>` whose `load` / `loadMany` are spies of the running test\n * framework (`bun:test`, Vitest, Jest, or the built-in spy of `@znight/testing`).\n *\n * Needs `@znight/testing` (an optional peer dependency of `@znight/graphql`).\n */\nimport type { Plugin } from '@znight/core'\nimport type { SseItem } from '@znight/http'\nimport type { MockFunction, TestApp } from '@znight/testing'\nimport type { FormattedExecutionResult, GraphQLFormattedError } from 'graphql'\nimport type { GraphQLRequest, GraphQLResultStream } from './execution.ts'\nimport type { BatchLoad, BatchLoader, LoaderKey, LoaderValue } from './types.ts'\n\nimport { parseSseStream } from '@znight/http'\nimport { resolveMockFn } from '@znight/testing'\n\nimport { endpointOf, executorOf, type GraphQLPlugin } from './plugin.ts'\n\n/** An operation for {@link executeOperation} and {@link subscribeOperation}. */\nexport interface OperationRequest {\n readonly query: string\n readonly variables?: Readonly<Record<string, unknown>>\n readonly operationName?: string\n readonly extensions?: Readonly<Record<string, unknown>>\n /**\n * Request headers (`x-api-key`, `accept`...), merged over the defaults (`content-type:\n * application/json`, and `accept: application/graphql-response+json, application/json;q=0.9`\n * or `text/event-stream`). Ignored without HTTP transport.\n */\n readonly headers?: Readonly<Record<string, string>>\n}\n\n/** A GraphQL result: `data`, `errors` and `extensions` when present. */\nexport interface OperationResult<TData = Record<string, unknown>> {\n readonly data?: TData | null\n readonly errors?: readonly GraphQLFormattedError[]\n readonly extensions?: Readonly<Record<string, unknown>>\n}\n\n/** What {@link executeOperation} resolves with. */\nexport interface OperationResponse<TData = Record<string, unknown>> extends OperationResult<TData> {\n /**\n * The HTTP status: 200 for an executed operation (field errors included), 400 for a request\n * error (the default `accept` is `application/graphql-response+json`), 403 for an HTTP guard...\n * Without HTTP transport, the status the endpoint would answer.\n */\n readonly status: number\n readonly headers: Readonly<Record<string, string | readonly string[] | undefined>>\n /** The raw body (an HTTP error body is not a GraphQL result: `data` and `errors` are then absent). */\n readonly body: string\n}\n\n/** Options of {@link OperationStream.next}. */\nexport interface OperationStreamWaitOptions {\n /** Maximum wait for the next result (ms). Default 2 000. */\n readonly timeoutMs?: number\n}\n\n/**\n * The results of {@link subscribeOperation}, one per `next` event. Ends after `complete` (or the\n * single result of a query, or a request error). `return()` disconnects: the server sees a client\n * abort (`ctx.signal`, then the source's `return()`).\n */\nexport interface OperationStream<TData = Record<string, unknown>>\n extends AsyncIterableIterator<OperationResult<TData>>,\n AsyncDisposable {\n /** HTTP status of the stream (200), or of a request error (400, 405, 503...). */\n readonly status: number\n readonly headers: Readonly<Record<string, string | readonly string[] | undefined>>\n /** The next result. Rejects when none arrives within `timeoutMs` (2 000 ms by default). */\n next(options?: OperationStreamWaitOptions): Promise<IteratorResult<OperationResult<TData>, undefined>>\n /** Ends the stream (disconnects). */\n return(): Promise<IteratorResult<OperationResult<TData>, undefined>>\n}\n\n/** The application `executeOperation()` and `subscribeOperation()` run against: a `TestApp`. */\nexport type GraphQLTestApp = Pick<TestApp, 'app' | 'inject' | 'injectStream'>\n\nconst DEFAULT_TIMEOUT_MS = 2_000\nconst RESPONSE_ACCEPT = 'application/graphql-response+json, application/json;q=0.9'\nconst DONE: IteratorResult<never, undefined> = Object.freeze({ done: true, value: undefined })\n\n/**\n * Executes a query or a mutation through the whole HTTP pipeline (`app.inject()`: `POST` on the\n * endpoint), or through `GraphQLPlugin` without HTTP transport.\n *\n * ```ts\n * const res = await executeOperation(app, { query: '{ books { title } }', headers: { 'x-api-key': KEY } })\n * expect(res.status).toBe(200)\n * expect(res.errors).toBeUndefined()\n * ```\n */\nexport async function executeOperation<TData = Record<string, unknown>>(\n app: GraphQLTestApp,\n request: OperationRequest,\n): Promise<OperationResponse<TData>> {\n const plugin = graphqlPlugin(app, 'executeOperation()')\n const body = operationBody(request, 'executeOperation()')\n const endpoint = endpointOf(plugin)\n if (endpoint === undefined) {\n const response = await executorOf(plugin).execute(body)\n return {\n ...pick<TData>(response.result),\n status: response.status,\n headers: { ...response.headers },\n body: JSON.stringify(response.result),\n }\n }\n const response = await app.inject({\n method: 'POST',\n url: endpoint,\n headers: headers(request, RESPONSE_ACCEPT),\n body: JSON.stringify(body),\n })\n return {\n ...parseResult<TData>(response.body),\n status: response.status,\n headers: response.headers,\n body: response.body,\n }\n}\n\n/**\n * Starts an operation as a stream: GraphQL over SSE in memory (`app.injectStream()`: `POST` with\n * `accept: text/event-stream`), or `GraphQLPlugin.subscribe()` without HTTP transport (no HTTP\n * guards then). Resolves once the subscription is established: its source has started (a resolver\n * written as an async generator has subscribed to the PubSub, when it subscribes before its first\n * `await`), so events published from then on\n * are received, even before the first `next()`.\n *\n * ```ts\n * await using events = await subscribeOperation(app, { query: 'subscription { bookAdded { title } }' })\n * await executeOperation(app, { query: ADD_DUNE })\n * expect((await events.next()).value).toEqual({ data: { bookAdded: { title: 'Dune' } } })\n * ```\n */\nexport async function subscribeOperation<TData = Record<string, unknown>>(\n app: GraphQLTestApp,\n request: OperationRequest,\n): Promise<OperationStream<TData>> {\n const plugin = graphqlPlugin(app, 'subscribeOperation()')\n const body = operationBody(request, 'subscribeOperation()')\n const endpoint = endpointOf(plugin)\n const label = describeOperation(body.query)\n if (endpoint === undefined) {\n const outcome = await executorOf(plugin).subscribe(body)\n if ('status' in outcome) return new SingleResult(outcome.status, {}, pick<TData>(outcome.result))\n return new Results<TData>(200, {}, directSource(outcome), label)\n }\n const response = await app.injectStream({\n method: 'POST',\n url: endpoint,\n headers: headers(request, 'text/event-stream'),\n body: JSON.stringify(body),\n })\n const type = response.headers['content-type']\n if (typeof type !== 'string' || !type.startsWith('text/event-stream')) {\n const text = await new Response(response.body).text()\n return new SingleResult(response.status, response.headers, parseResult<TData>(text))\n }\n return new Results<TData>(response.status, response.headers, sseSource(parseSseStream(response.body)), label)\n}\n\n/** Options of {@link fakeLoader}: what a key resolves to. */\nexport type FakeLoaderValues<K, V> =\n | Readonly<Record<string, V | Error>>\n | ReadonlyMap<K, V | Error>\n | ((key: K) => V | Error | Promise<V | Error>)\n\n/** A `Loader<L>` built by {@link fakeLoader}: `load` and `loadMany` are spies. */\nexport interface FakeLoader<K, V> extends BatchLoader<K, V> {\n readonly load: MockFunction<(key: K) => Promise<V>> & ((key: K) => Promise<V>)\n readonly loadMany: MockFunction<(keys: readonly K[]) => Promise<(V | Error)[]>> &\n ((keys: readonly K[]) => Promise<(V | Error)[]>)\n}\n\n/**\n * A `Loader<L>` for unit tests of resolvers (with `TestBed`), answering from `values` (a record by\n * key, a map, or a function of the key; an `Error` value fails that key). `load` and `loadMany` are\n * spies; a missing key rejects.\n *\n * ```ts\n * const authors = fakeLoader<AuthorsById>({ [herbert.id]: herbert })\n * expect(await instance.author(dune, authors)).toBe(herbert)\n * expect(authors.load).toHaveBeenCalledWith(herbert.id)\n * ```\n */\nexport function fakeLoader<L extends BatchLoad<unknown, unknown>>(\n values: FakeLoaderValues<LoaderKey<L>, LoaderValue<L>>,\n): FakeLoader<LoaderKey<L>, LoaderValue<L>> {\n type K = LoaderKey<L>\n type V = LoaderValue<L>\n const resolve = lookup<K, V>(values)\n const cache = new Map<unknown, Promise<V>>()\n const loadOne = (key: K): Promise<V> => {\n const cached = cache.get(key)\n if (cached !== undefined) return cached\n const loaded = Promise.resolve()\n .then(() => resolve(key))\n .then(value => (value instanceof Error ? Promise.reject(value) : value))\n cache.set(key, loaded)\n return loaded\n }\n const create = resolveMockFn().create\n const load = create((key: K) => loadOne(key)) as FakeLoader<K, V>['load']\n const loadMany = create((keys: readonly K[]) =>\n Promise.all(keys.map(key => loadOne(key).catch((error: unknown) => asError(error)))),\n ) as FakeLoader<K, V>['loadMany']\n const loader: FakeLoader<K, V> = {\n load,\n loadMany,\n clear(key: K) {\n cache.delete(key)\n return this\n },\n clearAll() {\n cache.clear()\n return this\n },\n prime(key: K, value: V | PromiseLike<V> | Error) {\n if (!cache.has(key)) {\n const primed = value instanceof Error ? Promise.reject(value) : Promise.resolve(value)\n primed.catch(() => undefined)\n cache.set(key, primed)\n }\n return this\n },\n }\n return loader\n}\n\n// ------------------------------------------------------------------------------------------------\n\nfunction graphqlPlugin(app: GraphQLTestApp, what: string): GraphQLPlugin {\n const plugin = app?.app?.getPlugin<Plugin>('graphql')\n if (plugin === undefined) {\n throw new TypeError(`${what} expects a TestApp whose plugins include GraphQLPlugin.configure()`)\n }\n return plugin as GraphQLPlugin\n}\n\nfunction operationBody(request: OperationRequest, what: string): GraphQLRequest {\n if (request === null || typeof request !== 'object' || typeof request.query !== 'string') {\n throw new TypeError(`${what} expects { query, variables?, operationName?, extensions?, headers? }`)\n }\n return {\n query: request.query,\n ...(request.variables === undefined ? {} : { variables: request.variables }),\n ...(request.operationName === undefined ? {} : { operationName: request.operationName }),\n ...(request.extensions === undefined ? {} : { extensions: request.extensions }),\n }\n}\n\n/** The request headers: the defaults, then the operation's (names lower-cased, so they replace the defaults). */\nfunction headers(request: OperationRequest, accept: string): Record<string, string> {\n const result: Record<string, string> = { 'content-type': 'application/json', accept }\n for (const [name, value] of Object.entries(request.headers ?? {})) result[name.toLowerCase()] = value\n return result\n}\n\nfunction pick<TData>(result: FormattedExecutionResult): OperationResult<TData> {\n return {\n ...('data' in result ? { data: result.data as TData | null } : {}),\n ...(result.errors === undefined ? {} : { errors: result.errors }),\n ...(result.extensions === undefined ? {} : { extensions: result.extensions }),\n }\n}\n\n/** The GraphQL result of a body; `{}` when it is not one (an HTTP error body, a page). */\nfunction parseResult<TData>(body: string): OperationResult<TData> {\n let parsed: unknown\n try {\n parsed = JSON.parse(body)\n } catch {\n return {}\n }\n if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) return {}\n const result = parsed as FormattedExecutionResult\n if (!('data' in result) && !Array.isArray(result.errors)) return {}\n return pick<TData>(result)\n}\n\nfunction describeOperation(query: string): string {\n const text = query.replace(/\\s+/g, ' ').trim()\n return text.length > 60 ? `${text.slice(0, 60)}…` : text\n}\n\nfunction asError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error))\n}\n\nfunction lookup<K, V>(values: FakeLoaderValues<K, V>): (key: K) => V | Error | Promise<V | Error> {\n if (typeof values === 'function') return values\n if (values instanceof Map) {\n return key => (values.has(key) ? (values.get(key) as V | Error) : missing(key))\n }\n const record = values as Readonly<Record<string, V | Error>>\n return key => (Object.hasOwn(record, String(key)) ? (record[String(key)] as V | Error) : missing(key))\n}\n\nfunction missing(key: unknown): Error {\n return new Error(`fakeLoader(): no value for the key ${JSON.stringify(key) ?? String(key)}`)\n}\n\n/** A source of results: `undefined` when it ended. */\ninterface ResultSource {\n next(): Promise<FormattedExecutionResult | undefined>\n close(): Promise<void>\n}\n\nfunction directSource(stream: GraphQLResultStream): ResultSource {\n return {\n next: async () => {\n const step = await stream.next()\n return step.done === true ? undefined : step.value\n },\n close: async () => {\n await stream.return()\n },\n }\n}\n\n/** The `next` events of an event stream, until `complete` (comments, `retry` and other events skipped). */\nfunction sseSource(items: AsyncIterableIterator<SseItem>): ResultSource {\n return {\n next: async () => {\n for (;;) {\n const step = await items.next()\n if (step.done === true) return undefined\n const item = step.value\n if (item.type !== 'event') continue\n if (item.event === 'complete') return undefined\n if (item.event === 'next') return JSON.parse(item.data) as FormattedExecutionResult\n }\n },\n close: async () => {\n await items.return?.()\n },\n }\n}\n\n/** The stream of a subscription (or of a query sent as a stream). */\nclass Results<TData> implements OperationStream<TData> {\n readonly status: number\n readonly headers: Readonly<Record<string, string | readonly string[] | undefined>>\n readonly #source: ResultSource\n readonly #label: string\n /** A read still pending after a timeout: the next call waits for it, so no result is lost. */\n #pending: Promise<FormattedExecutionResult | undefined> | undefined\n #ended = false\n\n constructor(\n status: number,\n headers: Readonly<Record<string, string | readonly string[] | undefined>>,\n source: ResultSource,\n label: string,\n ) {\n this.status = status\n this.headers = headers\n this.#source = source\n this.#label = label\n }\n\n async next(options: OperationStreamWaitOptions = {}): Promise<IteratorResult<OperationResult<TData>, undefined>> {\n if (this.#ended) return DONE\n const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n this.#pending ??= this.#source.next()\n const pending = this.#pending\n let timer: ReturnType<typeof setTimeout> | undefined\n const timeout = new Promise<typeof TIMEOUT>(resolve => {\n timer = setTimeout(() => resolve(TIMEOUT), timeoutMs)\n })\n let result: FormattedExecutionResult | undefined | typeof TIMEOUT\n try {\n result = await Promise.race([pending, timeout])\n } catch (error) {\n this.#pending = undefined\n await this.return()\n throw error\n } finally {\n clearTimeout(timer)\n }\n if (result === TIMEOUT) {\n throw new Error(`subscribeOperation(): no result within ${timeoutMs} ms for ${this.#label}`)\n }\n this.#pending = undefined\n if (result === undefined) {\n await this.return()\n return DONE\n }\n return { done: false, value: pick<TData>(result) }\n }\n\n async return(): Promise<IteratorResult<OperationResult<TData>, undefined>> {\n if (!this.#ended) {\n this.#ended = true\n await this.#source.close()\n }\n return DONE\n }\n\n [Symbol.asyncIterator](): OperationStream<TData> {\n return this\n }\n\n [Symbol.asyncDispose](): Promise<void> {\n return this.return().then(() => undefined)\n }\n}\n\nconst TIMEOUT: unique symbol = Symbol('timeout')\n\n/** A request error (or a query's result without HTTP transport): one result, then the end. */\nclass SingleResult<TData> implements OperationStream<TData> {\n readonly status: number\n readonly headers: Readonly<Record<string, string | readonly string[] | undefined>>\n #result: OperationResult<TData> | undefined\n\n constructor(\n status: number,\n headers: Readonly<Record<string, string | readonly string[] | undefined>>,\n result: OperationResult<TData>,\n ) {\n this.status = status\n this.headers = headers\n this.#result = result\n }\n\n next(): Promise<IteratorResult<OperationResult<TData>, undefined>> {\n const result = this.#result\n this.#result = undefined\n return Promise.resolve(result === undefined ? DONE : { done: false, value: result })\n }\n\n return(): Promise<IteratorResult<OperationResult<TData>, undefined>> {\n this.#result = undefined\n return Promise.resolve(DONE)\n }\n\n [Symbol.asyncIterator](): OperationStream<TData> {\n return this\n }\n\n [Symbol.asyncDispose](): Promise<void> {\n return Promise.resolve()\n }\n}\n"],"mappings":";;;;AAqFA,MAAM,qBAAqB;AAC3B,MAAM,kBAAkB;AACxB,MAAM,OAAyC,OAAO,OAAO;CAAE,MAAM;CAAM,OAAO,KAAA;AAAU,CAAC;;;;;;;;;;;AAY7F,eAAsB,iBACpB,KACA,SACmC;CACnC,MAAM,SAAS,cAAc,KAAK,oBAAoB;CACtD,MAAM,OAAO,cAAc,SAAS,oBAAoB;CACxD,MAAM,WAAW,WAAW,MAAM;CAClC,IAAI,aAAa,KAAA,GAAW;EAC1B,MAAM,WAAW,MAAM,WAAW,MAAM,CAAC,CAAC,QAAQ,IAAI;EACtD,OAAO;GACL,GAAG,KAAY,SAAS,MAAM;GAC9B,QAAQ,SAAS;GACjB,SAAS,EAAE,GAAG,SAAS,QAAQ;GAC/B,MAAM,KAAK,UAAU,SAAS,MAAM;EACtC;CACF;CACA,MAAM,WAAW,MAAM,IAAI,OAAO;EAChC,QAAQ;EACR,KAAK;EACL,SAAS,QAAQ,SAAS,eAAe;EACzC,MAAM,KAAK,UAAU,IAAI;CAC3B,CAAC;CACD,OAAO;EACL,GAAG,YAAmB,SAAS,IAAI;EACnC,QAAQ,SAAS;EACjB,SAAS,SAAS;EAClB,MAAM,SAAS;CACjB;AACF;;;;;;;;;;;;;;;AAgBA,eAAsB,mBACpB,KACA,SACiC;CACjC,MAAM,SAAS,cAAc,KAAK,sBAAsB;CACxD,MAAM,OAAO,cAAc,SAAS,sBAAsB;CAC1D,MAAM,WAAW,WAAW,MAAM;CAClC,MAAM,QAAQ,kBAAkB,KAAK,KAAK;CAC1C,IAAI,aAAa,KAAA,GAAW;EAC1B,MAAM,UAAU,MAAM,WAAW,MAAM,CAAC,CAAC,UAAU,IAAI;EACvD,IAAI,YAAY,SAAS,OAAO,IAAI,aAAa,QAAQ,QAAQ,CAAC,GAAG,KAAY,QAAQ,MAAM,CAAC;EAChG,OAAO,IAAI,QAAe,KAAK,CAAC,GAAG,aAAa,OAAO,GAAG,KAAK;CACjE;CACA,MAAM,WAAW,MAAM,IAAI,aAAa;EACtC,QAAQ;EACR,KAAK;EACL,SAAS,QAAQ,SAAS,mBAAmB;EAC7C,MAAM,KAAK,UAAU,IAAI;CAC3B,CAAC;CACD,MAAM,OAAO,SAAS,QAAQ;CAC9B,IAAI,OAAO,SAAS,YAAY,CAAC,KAAK,WAAW,mBAAmB,GAAG;EACrE,MAAM,OAAO,MAAM,IAAI,SAAS,SAAS,IAAI,CAAC,CAAC,KAAK;EACpD,OAAO,IAAI,aAAa,SAAS,QAAQ,SAAS,SAAS,YAAmB,IAAI,CAAC;CACrF;CACA,OAAO,IAAI,QAAe,SAAS,QAAQ,SAAS,SAAS,UAAU,eAAe,SAAS,IAAI,CAAC,GAAG,KAAK;AAC9G;;;;;;;;;;;;AA0BA,SAAgB,WACd,QAC0C;CAG1C,MAAM,UAAU,OAAa,MAAM;CACnC,MAAM,wBAAQ,IAAI,IAAyB;CAC3C,MAAM,WAAW,QAAuB;EACtC,MAAM,SAAS,MAAM,IAAI,GAAG;EAC5B,IAAI,WAAW,KAAA,GAAW,OAAO;EACjC,MAAM,SAAS,QAAQ,QAAQ,CAAC,CAC7B,WAAW,QAAQ,GAAG,CAAC,CAAC,CACxB,MAAK,UAAU,iBAAiB,QAAQ,QAAQ,OAAO,KAAK,IAAI,KAAM;EACzE,MAAM,IAAI,KAAK,MAAM;EACrB,OAAO;CACT;CACA,MAAM,SAAS,cAAc,CAAC,CAAC;CAyB/B,OAAO;EAnBL,MALW,QAAQ,QAAW,QAAQ,GAAG,CAKtC;EACH,UALe,QAAQ,SACvB,QAAQ,IAAI,KAAK,KAAI,QAAO,QAAQ,GAAG,CAAC,CAAC,OAAO,UAAmB,QAAQ,KAAK,CAAC,CAAC,CAAC,CAI5E;EACP,MAAM,KAAQ;GACZ,MAAM,OAAO,GAAG;GAChB,OAAO;EACT;EACA,WAAW;GACT,MAAM,MAAM;GACZ,OAAO;EACT;EACA,MAAM,KAAQ,OAAmC;GAC/C,IAAI,CAAC,MAAM,IAAI,GAAG,GAAG;IACnB,MAAM,SAAS,iBAAiB,QAAQ,QAAQ,OAAO,KAAK,IAAI,QAAQ,QAAQ,KAAK;IACrF,OAAO,YAAY,KAAA,CAAS;IAC5B,MAAM,IAAI,KAAK,MAAM;GACvB;GACA,OAAO;EACT;CAEU;AACd;AAIA,SAAS,cAAc,KAAqB,MAA6B;CACvE,MAAM,SAAS,KAAK,KAAK,UAAkB,SAAS;CACpD,IAAI,WAAW,KAAA,GACb,MAAM,IAAI,UAAU,GAAG,KAAK,mEAAmE;CAEjG,OAAO;AACT;AAEA,SAAS,cAAc,SAA2B,MAA8B;CAC9E,IAAI,YAAY,QAAQ,OAAO,YAAY,YAAY,OAAO,QAAQ,UAAU,UAC9E,MAAM,IAAI,UAAU,GAAG,KAAK,sEAAsE;CAEpG,OAAO;EACL,OAAO,QAAQ;EACf,GAAI,QAAQ,cAAc,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW,QAAQ,UAAU;EAC1E,GAAI,QAAQ,kBAAkB,KAAA,IAAY,CAAC,IAAI,EAAE,eAAe,QAAQ,cAAc;EACtF,GAAI,QAAQ,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,QAAQ,WAAW;CAC/E;AACF;;AAGA,SAAS,QAAQ,SAA2B,QAAwC;CAClF,MAAM,SAAiC;EAAE,gBAAgB;EAAoB;CAAO;CACpF,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,QAAQ,WAAW,CAAC,CAAC,GAAG,OAAO,KAAK,YAAY,KAAK;CAChG,OAAO;AACT;AAEA,SAAS,KAAY,QAA0D;CAC7E,OAAO;EACL,GAAI,UAAU,SAAS,EAAE,MAAM,OAAO,KAAqB,IAAI,CAAC;EAChE,GAAI,OAAO,WAAW,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ,OAAO,OAAO;EAC/D,GAAI,OAAO,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,YAAY,OAAO,WAAW;CAC7E;AACF;;AAGA,SAAS,YAAmB,MAAsC;CAChE,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAM,IAAI;CAC1B,QAAQ;EACN,OAAO,CAAC;CACV;CACA,IAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,MAAM,QAAQ,MAAM,GAAG,OAAO,CAAC;CACpF,MAAM,SAAS;CACf,IAAI,EAAE,UAAU,WAAW,CAAC,MAAM,QAAQ,OAAO,MAAM,GAAG,OAAO,CAAC;CAClE,OAAO,KAAY,MAAM;AAC3B;AAEA,SAAS,kBAAkB,OAAuB;CAChD,MAAM,OAAO,MAAM,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK;CAC7C,OAAO,KAAK,SAAS,KAAK,GAAG,KAAK,MAAM,GAAG,EAAE,EAAE,KAAK;AACtD;AAEA,SAAS,QAAQ,OAAuB;CACtC,OAAO,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AACjE;AAEA,SAAS,OAAa,QAA4E;CAChG,IAAI,OAAO,WAAW,YAAY,OAAO;CACzC,IAAI,kBAAkB,KACpB,QAAO,QAAQ,OAAO,IAAI,GAAG,IAAK,OAAO,IAAI,GAAG,IAAkB,QAAQ,GAAG;CAE/E,MAAM,SAAS;CACf,QAAO,QAAQ,OAAO,OAAO,QAAQ,OAAO,GAAG,CAAC,IAAK,OAAO,OAAO,GAAG,KAAmB,QAAQ,GAAG;AACtG;AAEA,SAAS,QAAQ,KAAqB;CACpC,uBAAO,IAAI,MAAM,sCAAsC,KAAK,UAAU,GAAG,KAAK,OAAO,GAAG,GAAG;AAC7F;AAQA,SAAS,aAAa,QAA2C;CAC/D,OAAO;EACL,MAAM,YAAY;GAChB,MAAM,OAAO,MAAM,OAAO,KAAK;GAC/B,OAAO,KAAK,SAAS,OAAO,KAAA,IAAY,KAAK;EAC/C;EACA,OAAO,YAAY;GACjB,MAAM,OAAO,OAAO;EACtB;CACF;AACF;;AAGA,SAAS,UAAU,OAAqD;CACtE,OAAO;EACL,MAAM,YAAY;GAChB,SAAS;IACP,MAAM,OAAO,MAAM,MAAM,KAAK;IAC9B,IAAI,KAAK,SAAS,MAAM,OAAO,KAAA;IAC/B,MAAM,OAAO,KAAK;IAClB,IAAI,KAAK,SAAS,SAAS;IAC3B,IAAI,KAAK,UAAU,YAAY,OAAO,KAAA;IACtC,IAAI,KAAK,UAAU,QAAQ,OAAO,KAAK,MAAM,KAAK,IAAI;GACxD;EACF;EACA,OAAO,YAAY;GACjB,MAAM,MAAM,SAAS;EACvB;CACF;AACF;;AAGA,IAAM,UAAN,MAAuD;CACrD;CACA;CACA;CACA;;CAEA;CACA,SAAS;CAET,YACE,QACA,SACA,QACA,OACA;EACA,KAAK,SAAS;EACd,KAAK,UAAU;EACf,KAAK,UAAU;EACf,KAAK,SAAS;CAChB;CAEA,MAAM,KAAK,UAAsC,CAAC,GAA+D;EAC/G,IAAI,KAAK,QAAQ,OAAO;EACxB,MAAM,YAAY,QAAQ,aAAa;EACvC,KAAK,aAAa,KAAK,QAAQ,KAAK;EACpC,MAAM,UAAU,KAAK;EACrB,IAAI;EACJ,MAAM,UAAU,IAAI,SAAwB,YAAW;GACrD,QAAQ,iBAAiB,QAAQ,OAAO,GAAG,SAAS;EACtD,CAAC;EACD,IAAI;EACJ,IAAI;GACF,SAAS,MAAM,QAAQ,KAAK,CAAC,SAAS,OAAO,CAAC;EAChD,SAAS,OAAO;GACd,KAAK,WAAW,KAAA;GAChB,MAAM,KAAK,OAAO;GAClB,MAAM;EACR,UAAU;GACR,aAAa,KAAK;EACpB;EACA,IAAI,WAAW,SACb,MAAM,IAAI,MAAM,0CAA0C,UAAU,UAAU,KAAK,QAAQ;EAE7F,KAAK,WAAW,KAAA;EAChB,IAAI,WAAW,KAAA,GAAW;GACxB,MAAM,KAAK,OAAO;GAClB,OAAO;EACT;EACA,OAAO;GAAE,MAAM;GAAO,OAAO,KAAY,MAAM;EAAE;CACnD;CAEA,MAAM,SAAqE;EACzE,IAAI,CAAC,KAAK,QAAQ;GAChB,KAAK,SAAS;GACd,MAAM,KAAK,QAAQ,MAAM;EAC3B;EACA,OAAO;CACT;CAEA,CAAC,OAAO,iBAAyC;EAC/C,OAAO;CACT;CAEA,CAAC,OAAO,gBAA+B;EACrC,OAAO,KAAK,OAAO,CAAC,CAAC,WAAW,KAAA,CAAS;CAC3C;AACF;AAEA,MAAM,UAAyB,OAAO,SAAS;;AAG/C,IAAM,eAAN,MAA4D;CAC1D;CACA;CACA;CAEA,YACE,QACA,SACA,QACA;EACA,KAAK,SAAS;EACd,KAAK,UAAU;EACf,KAAK,UAAU;CACjB;CAEA,OAAmE;EACjE,MAAM,SAAS,KAAK;EACpB,KAAK,UAAU,KAAA;EACf,OAAO,QAAQ,QAAQ,WAAW,KAAA,IAAY,OAAO;GAAE,MAAM;GAAO,OAAO;EAAO,CAAC;CACrF;CAEA,SAAqE;EACnE,KAAK,UAAU,KAAA;EACf,OAAO,QAAQ,QAAQ,IAAI;CAC7B;CAEA,CAAC,OAAO,iBAAyC;EAC/C,OAAO;CACT;CAEA,CAAC,OAAO,gBAA+B;EACrC,OAAO,QAAQ,QAAQ;CACzB;AACF"}