@nwire/apollo 0.10.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alex Gefter / 200apps Ltd.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # @nwire/apollo
2
+
3
+ > Apollo Server (v4) interop — plug Nwire actions into a GraphQL schema as field resolvers; reuse a hand-authored schema you already maintain.
4
+
5
+ ## What it is
6
+
7
+ A thin adapter (~60 LOC of real code). `actionResolver(action)` wraps a Nwire `ActionDefinition` so it serves as a GraphQL field resolver. `nwireApolloContext({ runtime })` injects the runtime + a fresh envelope onto every request. `mountNwireOnApollo(...)` bundles the context factory with Nwire-aware error formatting so `defineError` errors surface with stable `extensions.code`.
8
+
9
+ Same migration story Express got: keep the schema and gateway you already have, stop hand-writing resolver bodies that just call services.
10
+
11
+ > A native GraphQL transport (schema generated from actions/queries automatically, subscriptions on top of `@nwire/bus`) is a separate roadmap item. **This package is for teams already running Apollo with a hand-authored schema.** Cross-link: [`@nwire/express`](../nwire-http-express/README.md).
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ pnpm add @nwire/apollo @apollo/server graphql
17
+ ```
18
+
19
+ `@apollo/server` (^4) and `graphql` (^16) are peer deps — bring whatever versions your Apollo setup already uses. Apollo v3 is EOL and unsupported.
20
+
21
+ ## Quickstart
22
+
23
+ ```ts
24
+ import { z } from "zod";
25
+ import { ApolloServer } from "@apollo/server";
26
+ import { startStandaloneServer } from "@apollo/server/standalone";
27
+ import gql from "graphql-tag";
28
+ import { defineAction, Runtime } from "@nwire/forge";
29
+ import { actionResolver, mountNwireOnApollo } from "@nwire/apollo";
30
+
31
+ const submitAnswer = defineAction({
32
+ name: "submissions.submit-answer",
33
+ schema: z.object({ questionId: z.string(), answer: z.string() }),
34
+ handler: async (input, ctx) => ({
35
+ id: "sub-1",
36
+ ...input,
37
+ student: ctx.envelope.userId,
38
+ }),
39
+ });
40
+
41
+ const runtime = new Runtime();
42
+ runtime.registerHandler(submitAnswer.handler!);
43
+
44
+ const typeDefs = gql`
45
+ input SubmitAnswerInput {
46
+ questionId: String!
47
+ answer: String!
48
+ }
49
+ type Submission {
50
+ id: ID!
51
+ questionId: String!
52
+ answer: String!
53
+ student: String
54
+ }
55
+ type Mutation {
56
+ submitAnswer(input: SubmitAnswerInput!): Submission!
57
+ }
58
+ type Query {
59
+ _: Boolean
60
+ }
61
+ `;
62
+
63
+ const apollo = new ApolloServer({
64
+ typeDefs,
65
+ resolvers: {
66
+ Mutation: { submitAnswer: actionResolver(submitAnswer) },
67
+ },
68
+ ...mountNwireOnApollo({ runtime }),
69
+ });
70
+
71
+ await startStandaloneServer(apollo, { listen: { port: 4000 } });
72
+ ```
73
+
74
+ `actionResolver` reads `args.input` by default (matches GraphQL convention for mutation inputs); customise via `extractInput` for query-style flat args. Dispatched actions return their handler value verbatim to the GraphQL response — emit events, no events, return a plain object, whatever the handler does.
75
+
76
+ ## API
77
+
78
+ - `actionResolver(action, options?)` — `ActionDefinition` → GraphQL field resolver. Options: `extractInput`, `extractEnvelope`, `resolveRuntime`.
79
+ - `nwireApolloContext({ runtime, extractEnvelope?, extractUser? })` — Apollo `context` factory that injects `runtime` + envelope per request.
80
+ - `mountNwireOnApollo({ runtime, ... })` — convenience bundle: returns `{ context, formatError }` you spread into `ApolloServer` constructor + `startStandaloneServer` options.
81
+ - `formatNwireError(formattedError, rawError)` — standalone `formatError` callback. Use directly if you compose your own error formatter.
82
+
83
+ ## Errors
84
+
85
+ When a handler throws a `defineError`-style value (e.g. `QuestionLocked`), GraphQL clients receive:
86
+
87
+ ```json
88
+ {
89
+ "errors": [
90
+ {
91
+ "message": "Question is locked for further submissions.",
92
+ "extensions": { "code": "QUESTION_LOCKED", "status": 423 }
93
+ }
94
+ ]
95
+ }
96
+ ```
97
+
98
+ Same stable `code` the REST transport emits — clients can branch on `extensions.code` and ignore wire format.
99
+
100
+ ## See also
101
+
102
+ - [`@nwire/express`](../nwire-http-express/README.md) — same architectural pattern for Express interop.
103
+ - [`docs/recipes/apollo-interop.md`](../../docs/recipes/apollo-interop.md) — full recipe with auth header extraction + per-resolver overrides.
@@ -0,0 +1,191 @@
1
+ /**
2
+ * `@nwire/apollo` — Apollo Server (v4) interop, adapter form.
3
+ *
4
+ * Three exports, each tiny:
5
+ *
6
+ * - `actionResolver(action, options?)` — wraps a Nwire `ActionDefinition`
7
+ * so it can be plugged into a GraphQL schema as a field resolver. The
8
+ * returned function reads input from `args`, builds an envelope from
9
+ * Apollo's per-request context (tenant / user — caller-controlled via
10
+ * `extractEnvelope`), dispatches via the runtime found on the GraphQL
11
+ * context, and returns the action result.
12
+ *
13
+ * - `nwireApolloContext({ runtime })` — Apollo `context` callback factory.
14
+ * Returns a function that injects `runtime` + a freshly-seeded envelope
15
+ * onto every request's GraphQL context. Resolvers built by
16
+ * `actionResolver` read both off this context; user-authored resolvers
17
+ * can use `ctx.runtime.dispatch(...)` directly.
18
+ *
19
+ * - `mountNwireOnApollo({ apollo, runtime })` — convenience constructor
20
+ * options builder. Returns the option bag (`context`, `formatError`)
21
+ * you spread into Apollo's HTTP integration (e.g.
22
+ * `startStandaloneServer(server, mountNwireOnApollo({...}))`). The
23
+ * `formatError` step recognises `NwireError` (from `defineError`) and
24
+ * surfaces its `code` in `extensions.code`, mapping to the stable error
25
+ * contract the rest of the framework already exposes over REST.
26
+ *
27
+ * The whole package is ~60 LOC of real code. Same architectural invariant
28
+ * as `@nwire/express`: with the Runtime as the universal dispatch
29
+ * joint, the adapter is plumbing — no shadow execution, no proxy, no
30
+ * translation layer. An action stays the source of truth; Apollo is just
31
+ * one more way to invoke it.
32
+ *
33
+ * ## Why an adapter, not a transport
34
+ *
35
+ * `@nwire/graphql` ships a native GraphQL transport — schema generated
36
+ * from actions/queries automatically, with subscriptions on top of
37
+ * `@nwire/bus`. That story is for greenfield Nwire services that pick
38
+ * GraphQL as their primary interface.
39
+ *
40
+ * This package is for the OTHER case: a team already running Apollo with
41
+ * a hand-authored schema. They want to keep that schema (and their gateway,
42
+ * persisted queries, federation, cache hints — everything) and just stop
43
+ * writing boilerplate resolvers. `actionResolver(submitAnswer)` is the
44
+ * one-liner that says "this GraphQL field is implemented by this Nwire
45
+ * action" — same migration story Express got.
46
+ */
47
+ import type { ActionDefinition, ForgeApp } from "@nwire/forge";
48
+ import { type MessageEnvelope } from "@nwire/envelope";
49
+ import type { ApolloServerOptions, BaseContext } from "@apollo/server";
50
+ import type { GraphQLFormattedError } from "graphql";
51
+ /**
52
+ * The minimal GraphQL context surface that Nwire-aware resolvers expect.
53
+ * `nwireApolloContext` produces exactly this shape; user-extended contexts
54
+ * should intersect it (`type MyCtx = NwireApolloContext & { …app fields }`).
55
+ */
56
+ export interface NwireApolloContext extends BaseContext {
57
+ /** The Nwire app to dispatch actions through. */
58
+ readonly app: ForgeApp;
59
+ /**
60
+ * The fresh envelope seeded for this GraphQL request. Resolvers can
61
+ * dispatch through `runtime` and the envelope will be derived as a
62
+ * child of this one, so the entire request stays correlated.
63
+ */
64
+ readonly envelope: MessageEnvelope;
65
+ /**
66
+ * Authenticated principal, if any. Free-form `unknown` here — the
67
+ * upstream integration (an Apollo plugin / @nwire/auth-better-auth
68
+ * verifier / a custom middleware) is responsible for typing it.
69
+ */
70
+ readonly user?: unknown;
71
+ }
72
+ /**
73
+ * Options for `actionResolver`. All optional.
74
+ *
75
+ * - `extractInput` — how to derive the action input from `args`. Default:
76
+ * pull `args.input` if present, else the whole `args` object. The
77
+ * action's zod schema is the source of truth either way (`runtime.
78
+ * dispatch` re-validates), so callers only need this to flatten
79
+ * non-standard arg shapes.
80
+ *
81
+ * - `extractEnvelope` — how to derive envelope overrides (tenant, userId,
82
+ * user) from the GraphQL context. Default: read `context.envelope` if
83
+ * present, else seed a fresh envelope from `context.user`. Override to
84
+ * plug in your own auth shape (`ctx.session.userId`, `ctx.tenant`, etc).
85
+ */
86
+ export interface ActionResolverOptions<TContext extends BaseContext = NwireApolloContext> {
87
+ readonly extractInput?: (args: Record<string, unknown>, context: TContext) => unknown;
88
+ readonly extractEnvelope?: (context: TContext) => MessageEnvelope;
89
+ /**
90
+ * How to find the app. Defaults to `context.app`. Provide this if
91
+ * your context shape names the app differently or pulls it from a
92
+ * DI container.
93
+ */
94
+ readonly resolveApp?: (context: TContext) => ForgeApp;
95
+ }
96
+ /**
97
+ * Wrap a Nwire `ActionDefinition` as a GraphQL field resolver.
98
+ *
99
+ * ```ts
100
+ * import { actionResolver, nwireApolloContext } from "@nwire/apollo"
101
+ *
102
+ * const resolvers = {
103
+ * Mutation: {
104
+ * submitAnswer: actionResolver(submitAnswer),
105
+ * },
106
+ * }
107
+ *
108
+ * const apollo = new ApolloServer({ typeDefs, resolvers })
109
+ * await startStandaloneServer(apollo, {
110
+ * context: nwireApolloContext({ runtime }),
111
+ * })
112
+ * ```
113
+ *
114
+ * The resolver signature returned matches Apollo's standard
115
+ * `(parent, args, context, info)` — it's a plain function. Errors thrown
116
+ * by the handler (including `defineError` values) propagate to Apollo as
117
+ * normal; pair with `mountNwireOnApollo`'s `formatError` to surface the
118
+ * stable error code in `extensions.code`.
119
+ */
120
+ export declare function actionResolver<TContext extends NwireApolloContext = NwireApolloContext>(action: ActionDefinition, options?: ActionResolverOptions<TContext>): (parent: unknown, args: Record<string, unknown>, context: TContext) => Promise<unknown>;
121
+ /**
122
+ * Build an Apollo `context` callback that injects `runtime` + a fresh
123
+ * envelope onto every request's GraphQL context.
124
+ *
125
+ * ```ts
126
+ * await startStandaloneServer(apollo, {
127
+ * context: nwireApolloContext({
128
+ * runtime,
129
+ * // Optional: derive tenant + user from the HTTP request headers.
130
+ * extractEnvelope: ({ req }) => seedEnvelope({
131
+ * tenant: req.headers["x-tenant-id"] as string | undefined,
132
+ * userId: req.headers["x-user-id"] as string | undefined,
133
+ * }),
134
+ * }),
135
+ * })
136
+ * ```
137
+ *
138
+ * Generic over the integration-specific arg shape Apollo passes to the
139
+ * context callback (e.g. `{ req, res }` for express middleware,
140
+ * `{ req }` for the standalone server). Default `unknown` keeps the
141
+ * type usable in any integration without a forced cast.
142
+ */
143
+ export interface NwireApolloContextOptions<TArgs = unknown> {
144
+ readonly app: ForgeApp;
145
+ /**
146
+ * Build a request-scoped envelope. The integration arg (`req`, headers,
147
+ * etc.) is available so caller can read auth headers. Defaults to a
148
+ * fresh seeded envelope with no tenant / user — fine for unauth dev
149
+ * workflows.
150
+ */
151
+ readonly extractEnvelope?: (args: TArgs) => MessageEnvelope;
152
+ /** Build an arbitrary `user` value alongside the envelope. */
153
+ readonly extractUser?: (args: TArgs) => unknown;
154
+ }
155
+ export declare function nwireApolloContext<TArgs = unknown>(options: NwireApolloContextOptions<TArgs>): (args: TArgs) => Promise<NwireApolloContext>;
156
+ /**
157
+ * Build the Apollo Server constructor options bundle that registers Nwire
158
+ * error formatting. Spread the result into `ApolloServerOptions` (or use
159
+ * the individual exports `formatNwireError` / `nwireApolloContext`
160
+ * directly).
161
+ *
162
+ * ```ts
163
+ * const apollo = new ApolloServer({
164
+ * typeDefs,
165
+ * resolvers,
166
+ * ...mountNwireOnApollo({ runtime }),
167
+ * })
168
+ * ```
169
+ *
170
+ * What it sets up:
171
+ * - `formatError` — recognises `NwireError` (`defineError` instances)
172
+ * and surfaces `code` in `extensions.code`, plus `status` and `tags`
173
+ * for clients that care. Non-Nwire errors pass through unchanged.
174
+ * - The matching `context` factory you'd pass to your HTTP integration
175
+ * is included as `context` for convenience; pull it out and pass to
176
+ * `startStandaloneServer` / `expressMiddleware` as appropriate.
177
+ */
178
+ export interface MountNwireOptions<TArgs = unknown> {
179
+ readonly app: ForgeApp;
180
+ readonly extractEnvelope?: (args: TArgs) => MessageEnvelope;
181
+ readonly extractUser?: (args: TArgs) => unknown;
182
+ }
183
+ export declare function mountNwireOnApollo<TArgs = unknown>(options: MountNwireOptions<TArgs>): Pick<ApolloServerOptions<NwireApolloContext>, "formatError"> & {
184
+ context: (args: TArgs) => Promise<NwireApolloContext>;
185
+ };
186
+ /**
187
+ * Apollo `formatError` callback that maps `NwireError` instances onto
188
+ * GraphQL error extensions. Exposed standalone so consumers using their
189
+ * own custom `formatError` can compose it.
190
+ */
191
+ export declare function formatNwireError(formattedError: GraphQLFormattedError, rawError: unknown): GraphQLFormattedError;
@@ -0,0 +1,148 @@
1
+ /**
2
+ * `@nwire/apollo` — Apollo Server (v4) interop, adapter form.
3
+ *
4
+ * Three exports, each tiny:
5
+ *
6
+ * - `actionResolver(action, options?)` — wraps a Nwire `ActionDefinition`
7
+ * so it can be plugged into a GraphQL schema as a field resolver. The
8
+ * returned function reads input from `args`, builds an envelope from
9
+ * Apollo's per-request context (tenant / user — caller-controlled via
10
+ * `extractEnvelope`), dispatches via the runtime found on the GraphQL
11
+ * context, and returns the action result.
12
+ *
13
+ * - `nwireApolloContext({ runtime })` — Apollo `context` callback factory.
14
+ * Returns a function that injects `runtime` + a freshly-seeded envelope
15
+ * onto every request's GraphQL context. Resolvers built by
16
+ * `actionResolver` read both off this context; user-authored resolvers
17
+ * can use `ctx.runtime.dispatch(...)` directly.
18
+ *
19
+ * - `mountNwireOnApollo({ apollo, runtime })` — convenience constructor
20
+ * options builder. Returns the option bag (`context`, `formatError`)
21
+ * you spread into Apollo's HTTP integration (e.g.
22
+ * `startStandaloneServer(server, mountNwireOnApollo({...}))`). The
23
+ * `formatError` step recognises `NwireError` (from `defineError`) and
24
+ * surfaces its `code` in `extensions.code`, mapping to the stable error
25
+ * contract the rest of the framework already exposes over REST.
26
+ *
27
+ * The whole package is ~60 LOC of real code. Same architectural invariant
28
+ * as `@nwire/express`: with the Runtime as the universal dispatch
29
+ * joint, the adapter is plumbing — no shadow execution, no proxy, no
30
+ * translation layer. An action stays the source of truth; Apollo is just
31
+ * one more way to invoke it.
32
+ *
33
+ * ## Why an adapter, not a transport
34
+ *
35
+ * `@nwire/graphql` ships a native GraphQL transport — schema generated
36
+ * from actions/queries automatically, with subscriptions on top of
37
+ * `@nwire/bus`. That story is for greenfield Nwire services that pick
38
+ * GraphQL as their primary interface.
39
+ *
40
+ * This package is for the OTHER case: a team already running Apollo with
41
+ * a hand-authored schema. They want to keep that schema (and their gateway,
42
+ * persisted queries, federation, cache hints — everything) and just stop
43
+ * writing boilerplate resolvers. `actionResolver(submitAnswer)` is the
44
+ * one-liner that says "this GraphQL field is implemented by this Nwire
45
+ * action" — same migration story Express got.
46
+ */
47
+ import { isNwireError } from "@nwire/forge";
48
+ import { seedEnvelope } from "@nwire/envelope";
49
+ /**
50
+ * Wrap a Nwire `ActionDefinition` as a GraphQL field resolver.
51
+ *
52
+ * ```ts
53
+ * import { actionResolver, nwireApolloContext } from "@nwire/apollo"
54
+ *
55
+ * const resolvers = {
56
+ * Mutation: {
57
+ * submitAnswer: actionResolver(submitAnswer),
58
+ * },
59
+ * }
60
+ *
61
+ * const apollo = new ApolloServer({ typeDefs, resolvers })
62
+ * await startStandaloneServer(apollo, {
63
+ * context: nwireApolloContext({ runtime }),
64
+ * })
65
+ * ```
66
+ *
67
+ * The resolver signature returned matches Apollo's standard
68
+ * `(parent, args, context, info)` — it's a plain function. Errors thrown
69
+ * by the handler (including `defineError` values) propagate to Apollo as
70
+ * normal; pair with `mountNwireOnApollo`'s `formatError` to surface the
71
+ * stable error code in `extensions.code`.
72
+ */
73
+ export function actionResolver(action, options = {}) {
74
+ const extractInput = options.extractInput ??
75
+ ((args) =>
76
+ // GraphQL convention: mutation inputs land under `input`. Fall back
77
+ // to the whole args object so query-style resolvers (where each arg
78
+ // is named separately) work without configuration.
79
+ "input" in args ? args.input : args);
80
+ const resolveApp = options.resolveApp ?? ((ctx) => ctx.app);
81
+ const extractEnvelope = options.extractEnvelope ??
82
+ ((ctx) =>
83
+ // If `nwireApolloContext` is used the envelope is already on the
84
+ // context; otherwise fall back to seeding one from the loose user
85
+ // field. Either way `runtime.dispatch` derives a child envelope for
86
+ // the actual action, so this just sets the root of the chain.
87
+ ctx.envelope ?? seedEnvelope({ user: ctx.user }));
88
+ return async (_parent, args, context) => {
89
+ const app = resolveApp(context);
90
+ const envelope = extractEnvelope(context);
91
+ const input = extractInput(args, context);
92
+ return app.dispatch(action, input, envelope);
93
+ };
94
+ }
95
+ export function nwireApolloContext(options) {
96
+ const { app, extractEnvelope, extractUser } = options;
97
+ return async (args) => {
98
+ const user = extractUser?.(args);
99
+ const envelope = extractEnvelope?.(args) ?? seedEnvelope(user !== undefined ? { user } : {});
100
+ return { app, envelope, user };
101
+ };
102
+ }
103
+ export function mountNwireOnApollo(options) {
104
+ return {
105
+ formatError: formatNwireError,
106
+ context: nwireApolloContext({
107
+ app: options.app,
108
+ extractEnvelope: options.extractEnvelope,
109
+ extractUser: options.extractUser,
110
+ }),
111
+ };
112
+ }
113
+ /**
114
+ * Apollo `formatError` callback that maps `NwireError` instances onto
115
+ * GraphQL error extensions. Exposed standalone so consumers using their
116
+ * own custom `formatError` can compose it.
117
+ */
118
+ export function formatNwireError(formattedError, rawError) {
119
+ // Apollo wraps the thrown error inside GraphQLError.originalError —
120
+ // we accept it on the raw error or one level deep.
121
+ const candidate = pickNwireError(rawError);
122
+ if (!candidate)
123
+ return formattedError;
124
+ return {
125
+ ...formattedError,
126
+ message: candidate.summary,
127
+ extensions: {
128
+ ...formattedError.extensions,
129
+ code: candidate.code,
130
+ status: candidate.status,
131
+ ...(candidate.tags ? { tags: candidate.tags } : {}),
132
+ },
133
+ };
134
+ }
135
+ /**
136
+ * Look at the value Apollo handed `formatError` and return a `NwireError`
137
+ * if there's one to find — either directly or as the wrapped
138
+ * `originalError` of a `GraphQLError`. Returns `undefined` otherwise so
139
+ * callers can fall through to default formatting.
140
+ */
141
+ function pickNwireError(raw) {
142
+ if (isNwireError(raw))
143
+ return raw;
144
+ const wrapped = raw?.originalError;
145
+ if (isNwireError(wrapped))
146
+ return wrapped;
147
+ return undefined;
148
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `@nwire/apollo` — Apollo Server (v4) interop adapter.
3
+ *
4
+ * Three exports cover the surface:
5
+ *
6
+ * - `actionResolver(action)` — wrap a Nwire ActionDefinition as a GraphQL
7
+ * field resolver. The action stays the source of truth; Apollo gets a
8
+ * thin resolver that dispatches through the runtime.
9
+ *
10
+ * - `nwireApolloContext({ runtime, ... })` — Apollo `context` factory
11
+ * that injects `runtime` + a fresh envelope onto every request.
12
+ *
13
+ * - `mountNwireOnApollo({ runtime, ... })` — convenience bundle: pairs
14
+ * the context factory with Nwire-aware error formatting so
15
+ * `defineError` values surface with stable `extensions.code`.
16
+ *
17
+ * See the README + `docs/recipes/apollo-interop.md` for the quickstart.
18
+ */
19
+ export { actionResolver, formatNwireError, mountNwireOnApollo, nwireApolloContext, type ActionResolverOptions, type MountNwireOptions, type NwireApolloContext, type NwireApolloContextOptions, } from "./apollo-interop.js";
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ /**
2
+ * `@nwire/apollo` — Apollo Server (v4) interop adapter.
3
+ *
4
+ * Three exports cover the surface:
5
+ *
6
+ * - `actionResolver(action)` — wrap a Nwire ActionDefinition as a GraphQL
7
+ * field resolver. The action stays the source of truth; Apollo gets a
8
+ * thin resolver that dispatches through the runtime.
9
+ *
10
+ * - `nwireApolloContext({ runtime, ... })` — Apollo `context` factory
11
+ * that injects `runtime` + a fresh envelope onto every request.
12
+ *
13
+ * - `mountNwireOnApollo({ runtime, ... })` — convenience bundle: pairs
14
+ * the context factory with Nwire-aware error formatting so
15
+ * `defineError` values surface with stable `extensions.code`.
16
+ *
17
+ * See the README + `docs/recipes/apollo-interop.md` for the quickstart.
18
+ */
19
+ export { actionResolver, formatNwireError, mountNwireOnApollo, nwireApolloContext, } from "./apollo-interop.js";
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@nwire/apollo",
3
+ "version": "0.10.0",
4
+ "description": "Nwire — Apollo Server (v4) interop adapter. actionResolver(action) plugs a Nwire ActionDefinition into a GraphQL schema as a field resolver; nwireApolloContext({ runtime }) adds runtime.dispatch + envelope to Apollo's per-request context; mountNwireOnApollo() registers Nwire-aware error mapping (defineError -> extensions.code). Opt-in, peer dep, ~60 LOC of real code.",
5
+ "keywords": [
6
+ "adapter",
7
+ "apollo",
8
+ "graphql",
9
+ "interop",
10
+ "nwire"
11
+ ],
12
+ "license": "MIT",
13
+ "files": [
14
+ "dist",
15
+ "README.md",
16
+ "LICENSE"
17
+ ],
18
+ "type": "module",
19
+ "main": "./dist/index.js",
20
+ "types": "./dist/index.d.ts",
21
+ "exports": {
22
+ ".": {
23
+ "import": "./dist/index.js",
24
+ "types": "./dist/index.d.ts"
25
+ }
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "dependencies": {
31
+ "@nwire/forge": "0.10.0",
32
+ "@nwire/envelope": "0.10.0"
33
+ },
34
+ "devDependencies": {
35
+ "@apollo/server": "^4.11.0",
36
+ "@types/node": "^22.19.9",
37
+ "graphql": "^16.9.0",
38
+ "graphql-tag": "^2.12.6",
39
+ "typescript": "^5.9.3",
40
+ "vitest": "^4.0.18",
41
+ "zod": "^4.0.0"
42
+ },
43
+ "peerDependencies": {
44
+ "@apollo/server": "^4.0.0",
45
+ "graphql": "^16.0.0"
46
+ },
47
+ "scripts": {
48
+ "build": "tsc && node ../../scripts/fix-dist-extensions.mjs dist",
49
+ "dev": "tsc --watch",
50
+ "typecheck": "tsc --noEmit"
51
+ }
52
+ }