@glubean/graphql 0.1.7 → 0.2.2

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/contract/factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAwBH,SAAS,eAAe,CACtB,IAA4B,EAC5B,QAAgC;IAEhC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IACzC,MAAM,MAAM,GAA4B;QACtC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;QACf,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC;KACpB,CAAC;IACF,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAE,MAAqB,CAAC,CAAC,CAAC,SAAS,CAAC;AAC7E,CAAC;AAED,SAAS,oBAAoB,CAC3B,QAAsC,EACtC,IAAyB;IAEzB,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,UAAU,GAAG,CAAC,GAAG,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;IACpE,MAAM,gBAAgB,GAAG,eAAe,CAAC,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IAC/E,MAAM,aAAa,GAAG;QACpB,GAAG,CAAC,QAAQ,CAAC,OAAO,IAAI,EAAE,CAAC;QAC3B,GAAG,CAAC,IAAI,CAAC,cAAc,IAAI,EAAE,CAAC;KAC/B,CAAC;IACF,0EAA0E;IAC1E,0EAA0E;IAC1E,yEAAyE;IACzE,mBAAmB;IACnB,MAAM,UAAU,GAAG;QACjB,GAAG,IAAI;QACP,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM;QACtC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,QAAQ;QAC5C,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO;QACzC,IAAI,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;QACpD,UAAU,EAAE,gBAAgB;QAC5B,cAAc,EACZ,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS;KACpE,CAAC;IACF,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;QAClB,UAAgE,CAAC,QAAQ,GAAG;YAC3E,YAAY,EAAE,QAAQ,CAAC,KAAK;SAC7B,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAyB,EACzB,QAA2B;IAE3B,MAAM,OAAO,GAAG,CAKd,EAAU,EACV,IAA2C,EACwC,EAAE;QACrF,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC;YACrB,MAAM,IAAI,KAAK,CACb,qBAAqB,EAAE,6BAA6B;gBAClD,mFAAmF;gBACnF,uBAAuB,EAAE,WAAW,CACvC,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,oBAAoB,CAAC,QAAQ,EAAE,IAA2B,CAAC,CAAC;QAC3E,OAAO,QAAQ,CAAC,EAAE,EAAE,MAA+C,CAAC,CAAC;IACvE,CAAC,CAAC;IAED,OAAe,CAAC,IAAI,GAAG,CACtB,IAAY,EACZ,OAAgC,EAAE,EACV,EAAE;QAC1B,MAAM,UAAU,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;QACrE,MAAM,gBAAgB,GAAG,eAAe,CAAC,QAAQ,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;QAChF,MAAM,aAAa,GAAG;YACpB,GAAG,CAAC,QAAQ,EAAE,OAAO,IAAI,EAAE,CAAC;YAC5B,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;SACxB,CAAC;QACF,OAAO,oBAAoB,CAAC,QAAQ,EAAE;YACpC,GAAG,QAAQ;YACX,GAAG,IAAI;YACP,IAAI,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS;YACpD,UAAU,EAAE,gBAAgB;YAC5B,OAAO,EACL,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS;YACnE,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,OAAiC,CAAC;AAC3C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAyB;IACzD,OAAO,oBAAoB,CAAC,QAAQ,CAAmC,CAAC;AAC1E,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * GraphQL contract surface for @glubean/graphql.
3
+ *
4
+ * This module is **side-effect-free** — it only re-exports the adapter,
5
+ * factory, types, and the matcher collection so the manifest in
6
+ * `../index.ts` can reference them.
7
+ *
8
+ * Projects install the manifest via `installPlugin(graphqlPlugin)` (typically
9
+ * driven by `bootstrap()` loading `glubean.setup.ts`). There is no more
10
+ * "import the package and matchers auto-appear" path.
11
+ */
12
+ export { graphqlAdapter } from "./adapter.js";
13
+ export { createGraphqlFactory, createGraphqlRoot } from "./factory.js";
14
+ export { graphqlMatchers } from "./matchers.js";
15
+ export type { GraphqlContractCase, GraphqlContractDefaults, GraphqlContractExample, GraphqlContractExpect, GraphqlContractFactory, GraphqlContractMeta, GraphqlContractRoot, GraphqlContractSafeMeta, GraphqlContractSpec, GraphqlCaseResult, GraphqlErrorsExpect, GraphqlFlowCaseOutput, GraphqlPayloadSchemas, GraphqlSafeSchemas, GraphqlTypeDef, GraphqlTypeDefs, InferGraphqlVariables, InferGraphqlResponse, } from "./types.js";
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,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,15 @@
1
+ /**
2
+ * GraphQL contract surface for @glubean/graphql.
3
+ *
4
+ * This module is **side-effect-free** — it only re-exports the adapter,
5
+ * factory, types, and the matcher collection so the manifest in
6
+ * `../index.ts` can reference them.
7
+ *
8
+ * Projects install the manifest via `installPlugin(graphqlPlugin)` (typically
9
+ * driven by `bootstrap()` loading `glubean.setup.ts`). There is no more
10
+ * "import the package and matchers auto-appear" path.
11
+ */
12
+ export { graphqlAdapter } from "./adapter.js";
13
+ export { createGraphqlFactory, createGraphqlRoot } from "./factory.js";
14
+ export { graphqlMatchers } from "./matchers.js";
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contract/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * GraphQL custom matchers for `ctx.expect()`.
3
+ *
4
+ * Installed on `Expectation.prototype` when `installPlugin(graphqlPlugin)`
5
+ * runs (typically via `glubean.setup.ts` → `bootstrap()`). The
6
+ * `declare module "@glubean/sdk/expect"` block below publishes ambient
7
+ * types so `ctx.expect(res).toHaveGraphqlData(...)` is fully typed wherever
8
+ * `@glubean/graphql` is referenced, even if the actual runtime install
9
+ * happens elsewhere in the project.
10
+ *
11
+ * Matchers work on any object that carries GraphQL response shape, including:
12
+ * - `GraphQLResult<T>` from `@glubean/graphql` transport
13
+ * (`gql.query(...)` / `gql.mutate(...)`)
14
+ * - `GraphqlCaseResult<T>` from the contract adapter
15
+ * (verify callback / flow `out` lens input)
16
+ *
17
+ * Both expose:
18
+ * {
19
+ * data: T | null,
20
+ * errors?: GraphQLError[],
21
+ * extensions?: Record<string, unknown>,
22
+ * httpStatus: number,
23
+ * headers: Record<string, string | string[]>,
24
+ * rawBody: string | null,
25
+ * ...
26
+ * }
27
+ *
28
+ * The HTTP `toHaveStatus` built-in matcher reads `actual.status`, which the
29
+ * GraphQL envelope does NOT expose (it's `httpStatus` — a deliberate rename
30
+ * in CG-10 to avoid shadowing the native `Response.status` semantics). The
31
+ * `toHaveHttpStatus` matcher added here reads the envelope's `httpStatus`
32
+ * field so GraphQL users have a symmetric transport-level assertion.
33
+ */
34
+ import { type MatcherResult } from "@glubean/sdk/expect";
35
+ declare module "@glubean/sdk/expect" {
36
+ interface CustomMatchers<T> {
37
+ /**
38
+ * Assert the transport-level HTTP status on a `GraphQLResult` /
39
+ * `GraphqlCaseResult`. Reads `actual.httpStatus`.
40
+ *
41
+ * Use this (not `toHaveStatus`) for GraphQL responses — the envelope
42
+ * field is `httpStatus`, not `status`.
43
+ *
44
+ * @example ctx.expect(res).toHaveHttpStatus(200);
45
+ * @example ctx.expect(res).toHaveHttpStatus(401, "missing token");
46
+ */
47
+ toHaveHttpStatus(code: number, message?: string): Expectation<T>;
48
+ /**
49
+ * Partial-match the GraphQL `data` field (like `toMatchObject`).
50
+ *
51
+ * Fails if `data` is null or the subset is not contained in `data`.
52
+ *
53
+ * @example ctx.expect(res).toHaveGraphqlData({ user: { name: "Alice" } });
54
+ */
55
+ toHaveGraphqlData(subset: Record<string, unknown>, message?: string): Expectation<T>;
56
+ /**
57
+ * Assert the `errors` array is absent or empty (strict success).
58
+ *
59
+ * @example ctx.expect(res).toHaveGraphqlNoErrors();
60
+ */
61
+ toHaveGraphqlNoErrors(message?: string): Expectation<T>;
62
+ /**
63
+ * Assert at least one entry in `errors` has matching
64
+ * `extensions.code` (case-insensitive).
65
+ *
66
+ * @example ctx.expect(res).toHaveGraphqlErrorCode("UNAUTHENTICATED");
67
+ */
68
+ toHaveGraphqlErrorCode(code: string, message?: string): Expectation<T>;
69
+ /**
70
+ * Assert a key exists in `extensions` (server-side tracing / cost),
71
+ * optionally matching an exact value.
72
+ *
73
+ * @example ctx.expect(res).toHaveGraphqlExtension("tracing");
74
+ * @example ctx.expect(res).toHaveGraphqlExtension("version", "v2");
75
+ */
76
+ toHaveGraphqlExtension(key: string, value?: unknown, message?: string): Expectation<T>;
77
+ }
78
+ }
79
+ /**
80
+ * Register GraphQL matchers onto the shared `Expectation` prototype. Called
81
+ * from `./index.ts` during the same side-effect block that registers the
82
+ * contract adapter; users get matchers + adapter from a single
83
+ * `import "@glubean/graphql"`.
84
+ *
85
+ * Idempotent: swallows the "matcher already exists" error thrown by
86
+ * `Expectation.extend` on re-evaluation (duplicate imports, Vitest
87
+ * isolation boundaries, etc.).
88
+ *
89
+ * Note: `toHaveHttpStatus` is registered here even though it's
90
+ * conceptually transport-level (not GraphQL-specific) — users of
91
+ * `@glubean/graphql` are the ones who need it because the GraphQL
92
+ * envelope uses `httpStatus` instead of `status`. If a future package
93
+ * also exposes an `httpStatus` field, registration idempotency will
94
+ * catch the conflict at boot.
95
+ */
96
+ /**
97
+ * Collection of GraphQL custom matchers, keyed by matcher name.
98
+ *
99
+ * Consumed by the plugin manifest in `graphql/src/index.ts` as
100
+ * `manifest.matchers`. `installPlugin` drives the actual `Expectation.extend`
101
+ * call — plugin authors never need to touch it directly.
102
+ */
103
+ export declare const graphqlMatchers: {
104
+ readonly toHaveHttpStatus: (actual: unknown, ...args: unknown[]) => MatcherResult;
105
+ readonly toHaveGraphqlData: (actual: unknown, ...args: unknown[]) => MatcherResult;
106
+ readonly toHaveGraphqlNoErrors: (actual: unknown) => MatcherResult;
107
+ readonly toHaveGraphqlErrorCode: (actual: unknown, ...args: unknown[]) => MatcherResult;
108
+ readonly toHaveGraphqlExtension: (actual: unknown, ...args: unknown[]) => MatcherResult;
109
+ };
110
+ //# sourceMappingURL=matchers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matchers.d.ts","sourceRoot":"","sources":["../../src/contract/matchers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAO/E,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;;;;;;GAMG;AACH,eAAO,MAAM,eAAe;wCAhMlB,OAAO,WACN,OAAO,EAAE,KACjB,aAAa;yCAuBN,OAAO,WACN,OAAO,EAAE,KACjB,aAAa;6CAyCuB,OAAO,KAAG,aAAa;8CAsBpD,OAAO,WACN,OAAO,EAAE,KACjB,aAAa;8CA6BN,OAAO,WACN,OAAO,EAAE,KACjB,aAAa;CA2EN,CAAC"}
@@ -0,0 +1,244 @@
1
+ /**
2
+ * GraphQL custom matchers for `ctx.expect()`.
3
+ *
4
+ * Installed on `Expectation.prototype` when `installPlugin(graphqlPlugin)`
5
+ * runs (typically via `glubean.setup.ts` → `bootstrap()`). The
6
+ * `declare module "@glubean/sdk/expect"` block below publishes ambient
7
+ * types so `ctx.expect(res).toHaveGraphqlData(...)` is fully typed wherever
8
+ * `@glubean/graphql` is referenced, even if the actual runtime install
9
+ * happens elsewhere in the project.
10
+ *
11
+ * Matchers work on any object that carries GraphQL response shape, including:
12
+ * - `GraphQLResult<T>` from `@glubean/graphql` transport
13
+ * (`gql.query(...)` / `gql.mutate(...)`)
14
+ * - `GraphqlCaseResult<T>` from the contract adapter
15
+ * (verify callback / flow `out` lens input)
16
+ *
17
+ * Both expose:
18
+ * {
19
+ * data: T | null,
20
+ * errors?: GraphQLError[],
21
+ * extensions?: Record<string, unknown>,
22
+ * httpStatus: number,
23
+ * headers: Record<string, string | string[]>,
24
+ * rawBody: string | null,
25
+ * ...
26
+ * }
27
+ *
28
+ * The HTTP `toHaveStatus` built-in matcher reads `actual.status`, which the
29
+ * GraphQL envelope does NOT expose (it's `httpStatus` — a deliberate rename
30
+ * in CG-10 to avoid shadowing the native `Response.status` semantics). The
31
+ * `toHaveHttpStatus` matcher added here reads the envelope's `httpStatus`
32
+ * field so GraphQL users have a symmetric transport-level assertion.
33
+ */
34
+ import { inspect } from "@glubean/sdk/expect";
35
+ function readHttpStatus(actual) {
36
+ const s = actual?.httpStatus;
37
+ return typeof s === "number" ? s : undefined;
38
+ }
39
+ function readEnvelope(actual) {
40
+ if (!actual || typeof actual !== "object")
41
+ return undefined;
42
+ return actual;
43
+ }
44
+ /** Minimal partial-match helper — checks that every key/value in subset
45
+ * is deep-equal to the corresponding path in target. Keeps this file
46
+ * dependency-free from the sdk internal `matchesObject`. */
47
+ function matchesSubset(target, subset) {
48
+ for (const [k, expected] of Object.entries(subset)) {
49
+ const actual = target[k];
50
+ if (expected !== null &&
51
+ typeof expected === "object" &&
52
+ !Array.isArray(expected)) {
53
+ if (!actual || typeof actual !== "object" || Array.isArray(actual)) {
54
+ return false;
55
+ }
56
+ if (!matchesSubset(actual, expected)) {
57
+ return false;
58
+ }
59
+ }
60
+ else if (Array.isArray(expected)) {
61
+ if (!Array.isArray(actual) || actual.length !== expected.length) {
62
+ return false;
63
+ }
64
+ for (let i = 0; i < expected.length; i++) {
65
+ if (JSON.stringify(actual[i]) !== JSON.stringify(expected[i])) {
66
+ return false;
67
+ }
68
+ }
69
+ }
70
+ else if (actual !== expected) {
71
+ return false;
72
+ }
73
+ }
74
+ return true;
75
+ }
76
+ // =============================================================================
77
+ // Matcher implementations
78
+ // =============================================================================
79
+ const toHaveHttpStatus = (actual, ...args) => {
80
+ const code = args[0];
81
+ const actualCode = readHttpStatus(actual);
82
+ if (actualCode === undefined) {
83
+ return {
84
+ passed: false,
85
+ message: `to have HTTP status ${code} — actual has no \`.httpStatus\` (got ${inspect(actual)})`,
86
+ actual,
87
+ expected: code,
88
+ };
89
+ }
90
+ return {
91
+ passed: actualCode === code,
92
+ message: `to have HTTP status ${code}`,
93
+ actual: actualCode,
94
+ expected: code,
95
+ };
96
+ };
97
+ const toHaveGraphqlData = (actual, ...args) => {
98
+ const subset = args[0];
99
+ const env = readEnvelope(actual);
100
+ if (!env) {
101
+ return {
102
+ passed: false,
103
+ message: `to have GraphQL data matching ${inspect(subset)} — actual is not an envelope`,
104
+ actual,
105
+ expected: subset,
106
+ };
107
+ }
108
+ const data = env.data;
109
+ if (data == null) {
110
+ return {
111
+ passed: false,
112
+ message: `to have GraphQL data matching ${inspect(subset)} — \`.data\` is null`,
113
+ actual: data,
114
+ expected: subset,
115
+ };
116
+ }
117
+ if (typeof data !== "object" || Array.isArray(data)) {
118
+ return {
119
+ passed: false,
120
+ message: `to have GraphQL data matching ${inspect(subset)} — \`.data\` is not an object`,
121
+ actual: data,
122
+ expected: subset,
123
+ };
124
+ }
125
+ const passed = matchesSubset(data, subset);
126
+ return {
127
+ passed,
128
+ message: `to have GraphQL data matching ${inspect(subset)}`,
129
+ actual: data,
130
+ expected: subset,
131
+ };
132
+ };
133
+ const toHaveGraphqlNoErrors = (actual) => {
134
+ const env = readEnvelope(actual);
135
+ const errs = env?.errors;
136
+ const count = Array.isArray(errs) ? errs.length : 0;
137
+ const summary = count > 0
138
+ ? errs.map((e) => e.message).slice(0, 3).join("; ") +
139
+ (count > 3 ? ` (+${count - 3} more)` : "")
140
+ : "";
141
+ return {
142
+ passed: count === 0,
143
+ message: count > 0
144
+ ? `to have no GraphQL errors (got ${count}: ${summary})`
145
+ : `to have no GraphQL errors`,
146
+ actual: errs ?? [],
147
+ expected: [],
148
+ };
149
+ };
150
+ const toHaveGraphqlErrorCode = (actual, ...args) => {
151
+ const code = args[0];
152
+ const env = readEnvelope(actual);
153
+ const errs = env?.errors;
154
+ if (!Array.isArray(errs) || errs.length === 0) {
155
+ return {
156
+ passed: false,
157
+ message: `to have GraphQL error code ${inspect(code)} — no errors on envelope`,
158
+ actual: errs ?? [],
159
+ expected: { "extensions.code": code },
160
+ };
161
+ }
162
+ const lower = code.toUpperCase();
163
+ const match = errs.find((e) => {
164
+ const c = e.extensions?.code;
165
+ return typeof c === "string" && c.toUpperCase() === lower;
166
+ });
167
+ return {
168
+ passed: !!match,
169
+ message: `to have GraphQL error code ${inspect(code)}`,
170
+ actual: errs.map((e) => e.extensions?.code ?? null),
171
+ expected: code,
172
+ };
173
+ };
174
+ const toHaveGraphqlExtension = (actual, ...args) => {
175
+ const key = args[0];
176
+ const expectedValue = args[1];
177
+ const env = readEnvelope(actual);
178
+ const ext = env?.extensions;
179
+ if (!ext || typeof ext !== "object") {
180
+ return {
181
+ passed: false,
182
+ message: `to have GraphQL extension \`${key}\` — actual has no \`.extensions\``,
183
+ actual: ext,
184
+ expected: expectedValue !== undefined ? { [key]: expectedValue } : key,
185
+ };
186
+ }
187
+ if (!(key in ext)) {
188
+ return {
189
+ passed: false,
190
+ message: `to have GraphQL extension \`${key}\``,
191
+ actual: Object.keys(ext),
192
+ expected: expectedValue !== undefined ? { [key]: expectedValue } : key,
193
+ };
194
+ }
195
+ if (expectedValue === undefined) {
196
+ return {
197
+ passed: true,
198
+ message: `to have GraphQL extension \`${key}\``,
199
+ actual: ext[key],
200
+ expected: key,
201
+ };
202
+ }
203
+ return {
204
+ passed: JSON.stringify(ext[key]) === JSON.stringify(expectedValue),
205
+ message: `to have GraphQL extension \`${key}\` = ${inspect(expectedValue)}`,
206
+ actual: ext[key],
207
+ expected: expectedValue,
208
+ };
209
+ };
210
+ // =============================================================================
211
+ // Registration — side effect
212
+ // =============================================================================
213
+ /**
214
+ * Register GraphQL matchers onto the shared `Expectation` prototype. Called
215
+ * from `./index.ts` during the same side-effect block that registers the
216
+ * contract adapter; users get matchers + adapter from a single
217
+ * `import "@glubean/graphql"`.
218
+ *
219
+ * Idempotent: swallows the "matcher already exists" error thrown by
220
+ * `Expectation.extend` on re-evaluation (duplicate imports, Vitest
221
+ * isolation boundaries, etc.).
222
+ *
223
+ * Note: `toHaveHttpStatus` is registered here even though it's
224
+ * conceptually transport-level (not GraphQL-specific) — users of
225
+ * `@glubean/graphql` are the ones who need it because the GraphQL
226
+ * envelope uses `httpStatus` instead of `status`. If a future package
227
+ * also exposes an `httpStatus` field, registration idempotency will
228
+ * catch the conflict at boot.
229
+ */
230
+ /**
231
+ * Collection of GraphQL custom matchers, keyed by matcher name.
232
+ *
233
+ * Consumed by the plugin manifest in `graphql/src/index.ts` as
234
+ * `manifest.matchers`. `installPlugin` drives the actual `Expectation.extend`
235
+ * call — plugin authors never need to touch it directly.
236
+ */
237
+ export const graphqlMatchers = {
238
+ toHaveHttpStatus,
239
+ toHaveGraphqlData,
240
+ toHaveGraphqlNoErrors,
241
+ toHaveGraphqlErrorCode,
242
+ toHaveGraphqlExtension,
243
+ };
244
+ //# sourceMappingURL=matchers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matchers.js","sourceRoot":"","sources":["../../src/contract/matchers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAEH,OAAO,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;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,gBAAgB;IAChB,iBAAiB;IACjB,qBAAqB;IACrB,sBAAsB;IACtB,sBAAsB;CACd,CAAC"}