@effect-gql/persisted-queries 0.1.0 → 1.0.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.
@@ -1,277 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.makePersistedQueriesRouter = void 0;
4
- const platform_1 = require("@effect/platform");
5
- const effect_1 = require("effect");
6
- const graphql_1 = require("graphql");
7
- const server_1 = require("@effect-gql/core/server");
8
- const core_1 = require("@effect-gql/core");
9
- const store_1 = require("./store");
10
- const memory_store_1 = require("./memory-store");
11
- const errors_1 = require("./errors");
12
- const utils_1 = require("./utils");
13
- /**
14
- * Create a GraphQL router with Apollo Persisted Queries support.
15
- *
16
- * This creates a complete GraphQL router that includes:
17
- * - Apollo Persisted Queries (APQ) support
18
- * - GET request support for CDN caching
19
- * - All standard GraphQL router features (validation, execution, extensions)
20
- *
21
- * ## Apollo APQ Protocol
22
- *
23
- * 1. Client sends request with `extensions.persistedQuery.sha256Hash`
24
- * 2. If hash found in store, execute the stored query
25
- * 3. If hash NOT found and query provided (APQ mode), store it and execute
26
- * 4. If hash NOT found and NO query, return `PERSISTED_QUERY_NOT_FOUND`
27
- * 5. Client retries with both hash and query
28
- *
29
- * ## Modes
30
- *
31
- * - **APQ mode** (`mode: "apq"`): Automatic runtime registration.
32
- * Unknown queries trigger NOT_FOUND, prompting client retry with full query.
33
- *
34
- * - **Safelist mode** (`mode: "safelist"`): Pre-registered queries only.
35
- * Unknown queries return NOT_ALLOWED error. Use with `makeSafelistStore()`.
36
- *
37
- * @example APQ Mode (default)
38
- * ```typescript
39
- * import { makePersistedQueriesRouter } from "@effect-gql/persisted-queries"
40
- *
41
- * const router = makePersistedQueriesRouter(schema, serviceLayer, {
42
- * mode: "apq",
43
- * enableGet: true,
44
- * graphiql: { path: "/graphiql" },
45
- * })
46
- * ```
47
- *
48
- * @example Safelist Mode
49
- * ```typescript
50
- * import { makePersistedQueriesRouter, makeSafelistStore } from "@effect-gql/persisted-queries"
51
- *
52
- * const router = makePersistedQueriesRouter(schema, serviceLayer, {
53
- * mode: "safelist",
54
- * store: makeSafelistStore({
55
- * "abc123...": "query GetUser($id: ID!) { user(id: $id) { name } }",
56
- * }),
57
- * })
58
- * ```
59
- *
60
- * @param schema - The GraphQL schema
61
- * @param layer - Effect layer providing services required by resolvers
62
- * @param options - Router and persisted query configuration
63
- * @returns An HttpRouter with persisted query support
64
- */
65
- const makePersistedQueriesRouter = (schema, layer, options = {}) => {
66
- const mode = options.mode ?? "apq";
67
- const enableGet = options.enableGet ?? true;
68
- const validateHashOption = options.validateHash ?? true;
69
- const hashAlgorithm = options.hashAlgorithm ?? "sha256";
70
- // Core router config
71
- const resolvedConfig = (0, server_1.normalizeConfig)(options);
72
- const fieldComplexities = options.fieldComplexities ?? new Map();
73
- const extensions = options.extensions ?? [];
74
- const errorHandler = options.errorHandler ?? server_1.defaultErrorHandler;
75
- // Create the store layer (default to in-memory)
76
- const baseStoreLayer = options.store ?? (0, memory_store_1.makeMemoryStore)();
77
- /**
78
- * Resolve a persisted query from the store or register it.
79
- * Returns the resolved request body with the query filled in.
80
- */
81
- const resolvePersistedQuery = (body) => effect_1.Effect.gen(function* () {
82
- const persistedQuery = (0, utils_1.parsePersistedQueryExtension)(body.extensions);
83
- if (!persistedQuery) {
84
- // No persisted query extension - pass through unchanged
85
- return body;
86
- }
87
- // Validate version
88
- if (persistedQuery.version !== 1) {
89
- return yield* effect_1.Effect.fail(new errors_1.PersistedQueryVersionError({ version: persistedQuery.version }));
90
- }
91
- const hash = persistedQuery.sha256Hash;
92
- const store = yield* store_1.PersistedQueryStore;
93
- // Check if we have the query stored
94
- const storedQuery = yield* store.get(hash);
95
- if (effect_1.Option.isSome(storedQuery)) {
96
- // Query found - use it
97
- return {
98
- ...body,
99
- query: storedQuery.value,
100
- };
101
- }
102
- // Query not found in store
103
- if (!body.query) {
104
- // No query provided - client needs to send it
105
- return yield* effect_1.Effect.fail(new errors_1.PersistedQueryNotFoundError({ hash }));
106
- }
107
- // Query provided - check mode
108
- if (mode === "safelist") {
109
- // Safelist mode: reject unknown queries
110
- return yield* effect_1.Effect.fail(new errors_1.PersistedQueryNotAllowedError({ hash }));
111
- }
112
- // APQ mode: validate hash and store
113
- if (validateHashOption) {
114
- const computed = yield* (0, utils_1.computeHash)(body.query, hashAlgorithm);
115
- if (computed !== hash) {
116
- return yield* effect_1.Effect.fail(new errors_1.PersistedQueryHashMismatchError({
117
- providedHash: hash,
118
- computedHash: computed,
119
- }));
120
- }
121
- }
122
- // Store the query for future requests
123
- yield* store.set(hash, body.query);
124
- return body;
125
- });
126
- /**
127
- * Main GraphQL handler with APQ support
128
- */
129
- const createHandler = (parseBody) => effect_1.Effect.gen(function* () {
130
- // Parse request body
131
- const rawBody = yield* parseBody.pipe(effect_1.Effect.catchAll((error) => effect_1.Effect.succeed({
132
- _parseError: true,
133
- message: error.message,
134
- })));
135
- if ("_parseError" in rawBody) {
136
- return yield* platform_1.HttpServerResponse.json({ errors: [{ message: rawBody.message }] }, { status: 400 });
137
- }
138
- // Resolve persisted query
139
- const bodyResult = yield* resolvePersistedQuery(rawBody).pipe(effect_1.Effect.provide(baseStoreLayer), effect_1.Effect.either);
140
- if (bodyResult._tag === "Left") {
141
- // APQ error - return appropriate GraphQL error response
142
- const error = bodyResult.left;
143
- return yield* platform_1.HttpServerResponse.json({
144
- errors: [error.toGraphQLError()],
145
- });
146
- }
147
- const body = bodyResult.right;
148
- // Check if we have a query to execute
149
- if (!body.query) {
150
- return yield* platform_1.HttpServerResponse.json({ errors: [{ message: "No query provided" }] }, { status: 400 });
151
- }
152
- // Create the ExtensionsService for this request
153
- const extensionsService = yield* (0, core_1.makeExtensionsService)();
154
- // Get the runtime from the layer
155
- const runtime = yield* effect_1.Effect.runtime();
156
- // Phase 1: Parse
157
- let document;
158
- try {
159
- document = (0, graphql_1.parse)(body.query);
160
- }
161
- catch (parseError) {
162
- const extensionData = yield* extensionsService.get();
163
- return yield* platform_1.HttpServerResponse.json({
164
- errors: [{ message: String(parseError) }],
165
- extensions: Object.keys(extensionData).length > 0 ? extensionData : undefined,
166
- });
167
- }
168
- // Run onParse hooks
169
- yield* (0, core_1.runParseHooks)(extensions, body.query, document).pipe(effect_1.Effect.provideService(core_1.ExtensionsService, extensionsService));
170
- // Phase 2: Validate
171
- // Add NoSchemaIntrospectionCustomRule if introspection is disabled
172
- const validationRules = resolvedConfig.introspection
173
- ? undefined
174
- : [...graphql_1.specifiedRules, graphql_1.NoSchemaIntrospectionCustomRule];
175
- const validationErrors = (0, graphql_1.validate)(schema, document, validationRules);
176
- // Run onValidate hooks
177
- yield* (0, core_1.runValidateHooks)(extensions, document, validationErrors).pipe(effect_1.Effect.provideService(core_1.ExtensionsService, extensionsService));
178
- // If validation failed, return errors without executing
179
- if (validationErrors.length > 0) {
180
- const extensionData = yield* extensionsService.get();
181
- return yield* platform_1.HttpServerResponse.json({
182
- errors: validationErrors.map((e) => ({
183
- message: e.message,
184
- locations: e.locations,
185
- path: e.path,
186
- })),
187
- extensions: Object.keys(extensionData).length > 0 ? extensionData : undefined,
188
- }, { status: 400 });
189
- }
190
- // Validate query complexity if configured
191
- if (resolvedConfig.complexity) {
192
- yield* (0, server_1.validateComplexity)(body.query, body.operationName, body.variables, schema, fieldComplexities, resolvedConfig.complexity).pipe(effect_1.Effect.catchTag("ComplexityLimitExceededError", (error) => effect_1.Effect.fail(error)), effect_1.Effect.catchTag("ComplexityAnalysisError", (error) => effect_1.Effect.logWarning("Complexity analysis failed", error)));
193
- }
194
- // Phase 3: Execute
195
- const executionArgs = {
196
- source: body.query,
197
- document,
198
- variableValues: body.variables,
199
- operationName: body.operationName,
200
- schema,
201
- fieldComplexities,
202
- };
203
- // Run onExecuteStart hooks
204
- yield* (0, core_1.runExecuteStartHooks)(extensions, executionArgs).pipe(effect_1.Effect.provideService(core_1.ExtensionsService, extensionsService));
205
- // Execute GraphQL query
206
- const executeResult = yield* effect_1.Effect.try({
207
- try: () => (0, graphql_1.execute)({
208
- schema,
209
- document,
210
- variableValues: body.variables,
211
- operationName: body.operationName,
212
- contextValue: { runtime },
213
- }),
214
- catch: (error) => new Error(String(error)),
215
- });
216
- // Await result if it's a promise
217
- const resolvedResult = executeResult && typeof executeResult === "object" && "then" in executeResult
218
- ? yield* effect_1.Effect.promise(() => executeResult)
219
- : executeResult;
220
- // Run onExecuteEnd hooks
221
- yield* (0, core_1.runExecuteEndHooks)(extensions, resolvedResult).pipe(effect_1.Effect.provideService(core_1.ExtensionsService, extensionsService));
222
- // Merge extension data into result
223
- const extensionData = yield* extensionsService.get();
224
- const finalResult = Object.keys(extensionData).length > 0
225
- ? {
226
- ...resolvedResult,
227
- extensions: {
228
- ...resolvedResult.extensions,
229
- ...extensionData,
230
- },
231
- }
232
- : resolvedResult;
233
- return yield* platform_1.HttpServerResponse.json(finalResult);
234
- }).pipe(effect_1.Effect.provide(layer), effect_1.Effect.catchAll((error) => {
235
- if (error instanceof server_1.ComplexityLimitExceededError) {
236
- return platform_1.HttpServerResponse.json({
237
- errors: [
238
- {
239
- message: error.message,
240
- extensions: {
241
- code: "COMPLEXITY_LIMIT_EXCEEDED",
242
- limitType: error.limitType,
243
- limit: error.limit,
244
- actual: error.actual,
245
- },
246
- },
247
- ],
248
- }, { status: 400 }).pipe(effect_1.Effect.orDie);
249
- }
250
- return effect_1.Effect.fail(error);
251
- }), effect_1.Effect.catchAllCause(errorHandler));
252
- // POST handler
253
- const postHandler = createHandler(effect_1.Effect.gen(function* () {
254
- const request = yield* platform_1.HttpServerRequest.HttpServerRequest;
255
- return yield* request.json;
256
- }));
257
- // GET handler for CDN-cacheable persisted queries
258
- const getHandler = createHandler(effect_1.Effect.gen(function* () {
259
- const request = yield* platform_1.HttpServerRequest.HttpServerRequest;
260
- const url = new URL(request.url, "http://localhost");
261
- return yield* (0, utils_1.parseGetRequestBody)(url.searchParams);
262
- }));
263
- // Build router
264
- let router = platform_1.HttpRouter.empty.pipe(platform_1.HttpRouter.post(resolvedConfig.path, postHandler));
265
- // Add GET handler if enabled
266
- if (enableGet) {
267
- router = router.pipe(platform_1.HttpRouter.get(resolvedConfig.path, getHandler));
268
- }
269
- // Add GraphiQL route if enabled
270
- if (resolvedConfig.graphiql) {
271
- const { path, endpoint } = resolvedConfig.graphiql;
272
- router = router.pipe(platform_1.HttpRouter.get(path, platform_1.HttpServerResponse.html((0, server_1.graphiqlHtml)(endpoint))));
273
- }
274
- return router;
275
- };
276
- exports.makePersistedQueriesRouter = makePersistedQueriesRouter;
277
- //# sourceMappingURL=persisted-queries-router.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"persisted-queries-router.js","sourceRoot":"","sources":["../src/persisted-queries-router.ts"],"names":[],"mappings":"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"}
package/dist/store.d.ts DELETED
@@ -1,49 +0,0 @@
1
- import { Context, Effect, Option } from "effect";
2
- /**
3
- * Interface for persisted query storage.
4
- *
5
- * Implementations can be in-memory LRU cache, Redis, database, etc.
6
- * The interface uses Effect for consistency with the rest of the framework.
7
- */
8
- export interface PersistedQueryStore {
9
- /**
10
- * Get a query by its hash.
11
- * Returns Option.none() if not found.
12
- */
13
- readonly get: (hash: string) => Effect.Effect<Option.Option<string>>;
14
- /**
15
- * Store a query with its hash.
16
- * In safelist mode, this may be a no-op.
17
- */
18
- readonly set: (hash: string, query: string) => Effect.Effect<void>;
19
- /**
20
- * Check if a hash exists in the store without retrieving the query.
21
- * More efficient for large queries when you only need existence check.
22
- */
23
- readonly has: (hash: string) => Effect.Effect<boolean>;
24
- }
25
- /**
26
- * Service tag for the PersistedQueryStore.
27
- *
28
- * Use this tag to provide a store implementation via Effect's dependency injection.
29
- *
30
- * @example
31
- * ```typescript
32
- * import { PersistedQueryStore, makeMemoryStore } from "@effect-gql/persisted-queries"
33
- *
34
- * // Create a layer with the memory store
35
- * const storeLayer = makeMemoryStore({ maxSize: 1000 })
36
- *
37
- * // Use in an Effect
38
- * const program = Effect.gen(function* () {
39
- * const store = yield* PersistedQueryStore
40
- * yield* store.set("abc123", "query { hello }")
41
- * const query = yield* store.get("abc123")
42
- * // query: Option.some("query { hello }")
43
- * })
44
- *
45
- * Effect.runPromise(Effect.provide(program, storeLayer))
46
- * ```
47
- */
48
- export declare const PersistedQueryStore: Context.Tag<PersistedQueryStore, PersistedQueryStore>;
49
- //# sourceMappingURL=store.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAEhD;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAA;IAEpE;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAElE;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;CACvD;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,mBAAmB,uDAE/B,CAAA"}
package/dist/store.js DELETED
@@ -1,29 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PersistedQueryStore = void 0;
4
- const effect_1 = require("effect");
5
- /**
6
- * Service tag for the PersistedQueryStore.
7
- *
8
- * Use this tag to provide a store implementation via Effect's dependency injection.
9
- *
10
- * @example
11
- * ```typescript
12
- * import { PersistedQueryStore, makeMemoryStore } from "@effect-gql/persisted-queries"
13
- *
14
- * // Create a layer with the memory store
15
- * const storeLayer = makeMemoryStore({ maxSize: 1000 })
16
- *
17
- * // Use in an Effect
18
- * const program = Effect.gen(function* () {
19
- * const store = yield* PersistedQueryStore
20
- * yield* store.set("abc123", "query { hello }")
21
- * const query = yield* store.get("abc123")
22
- * // query: Option.some("query { hello }")
23
- * })
24
- *
25
- * Effect.runPromise(Effect.provide(program, storeLayer))
26
- * ```
27
- */
28
- exports.PersistedQueryStore = effect_1.Context.GenericTag("@effect-gql/persisted-queries/PersistedQueryStore");
29
- //# sourceMappingURL=store.js.map
package/dist/store.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":";;;AAAA,mCAAgD;AA4BhD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACU,QAAA,mBAAmB,GAAG,gBAAO,CAAC,UAAU,CACnD,mDAAmD,CACpD,CAAA"}
package/dist/utils.d.ts DELETED
@@ -1,51 +0,0 @@
1
- import { Effect } from "effect";
2
- import type { HashAlgorithm } from "./config";
3
- /**
4
- * Compute the hash of a query string.
5
- *
6
- * @param query - The GraphQL query string to hash
7
- * @param algorithm - Hash algorithm to use (default: sha256)
8
- * @returns Effect that resolves to the hex-encoded hash
9
- */
10
- export declare const computeHash: (query: string, algorithm?: HashAlgorithm) => Effect.Effect<string>;
11
- /**
12
- * Structure of the persisted query extension in GraphQL requests.
13
- * This follows the Apollo APQ protocol.
14
- */
15
- export interface PersistedQueryExtension {
16
- readonly version: number;
17
- readonly sha256Hash: string;
18
- }
19
- /**
20
- * Parse and validate the persisted query extension from request extensions.
21
- *
22
- * @param extensions - The extensions object from the GraphQL request
23
- * @returns The parsed persisted query extension, or null if not present/invalid
24
- */
25
- export declare const parsePersistedQueryExtension: (extensions: unknown) => PersistedQueryExtension | null;
26
- /**
27
- * GraphQL request body structure with optional persisted query extension.
28
- */
29
- export interface GraphQLRequestBody {
30
- query?: string;
31
- variables?: Record<string, unknown>;
32
- operationName?: string;
33
- extensions?: {
34
- persistedQuery?: PersistedQueryExtension;
35
- [key: string]: unknown;
36
- };
37
- }
38
- /**
39
- * Parse a GET request's query parameters into a GraphQL request body.
40
- *
41
- * Supports the following query parameters:
42
- * - `query`: The GraphQL query string (optional with persisted queries)
43
- * - `variables`: JSON-encoded variables object
44
- * - `operationName`: Name of the operation to execute
45
- * - `extensions`: JSON-encoded extensions object containing persistedQuery
46
- *
47
- * @param searchParams - URLSearchParams from the request URL
48
- * @returns Effect that resolves to the parsed request body or fails with parse error
49
- */
50
- export declare const parseGetRequestBody: (searchParams: URLSearchParams) => Effect.Effect<GraphQLRequestBody, Error>;
51
- //# sourceMappingURL=utils.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE/B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAE7C;;;;;;GAMG;AACH,eAAO,MAAM,WAAW,GACtB,OAAO,MAAM,EACb,YAAW,aAAwB,KAClC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAyE,CAAA;AAEhG;;;GAGG;AACH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAC5B;AAED;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,GACvC,YAAY,OAAO,KAClB,uBAAuB,GAAG,IAmB5B,CAAA;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACnC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,CAAC,EAAE;QACX,cAAc,CAAC,EAAE,uBAAuB,CAAA;QACxC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KACvB,CAAA;CACF;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,mBAAmB,GAC9B,cAAc,eAAe,KAC5B,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,KAAK,CA6BtC,CAAA"}
package/dist/utils.js DELETED
@@ -1,73 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseGetRequestBody = exports.parsePersistedQueryExtension = exports.computeHash = void 0;
4
- const effect_1 = require("effect");
5
- const crypto_1 = require("crypto");
6
- /**
7
- * Compute the hash of a query string.
8
- *
9
- * @param query - The GraphQL query string to hash
10
- * @param algorithm - Hash algorithm to use (default: sha256)
11
- * @returns Effect that resolves to the hex-encoded hash
12
- */
13
- const computeHash = (query, algorithm = "sha256") => effect_1.Effect.sync(() => (0, crypto_1.createHash)(algorithm).update(query).digest("hex"));
14
- exports.computeHash = computeHash;
15
- /**
16
- * Parse and validate the persisted query extension from request extensions.
17
- *
18
- * @param extensions - The extensions object from the GraphQL request
19
- * @returns The parsed persisted query extension, or null if not present/invalid
20
- */
21
- const parsePersistedQueryExtension = (extensions) => {
22
- if (typeof extensions !== "object" || extensions === null || !("persistedQuery" in extensions)) {
23
- return null;
24
- }
25
- const pq = extensions.persistedQuery;
26
- if (typeof pq !== "object" || pq === null || !("version" in pq) || !("sha256Hash" in pq)) {
27
- return null;
28
- }
29
- const version = pq.version;
30
- const sha256Hash = pq.sha256Hash;
31
- if (typeof version !== "number" || typeof sha256Hash !== "string") {
32
- return null;
33
- }
34
- return { version, sha256Hash };
35
- };
36
- exports.parsePersistedQueryExtension = parsePersistedQueryExtension;
37
- /**
38
- * Parse a GET request's query parameters into a GraphQL request body.
39
- *
40
- * Supports the following query parameters:
41
- * - `query`: The GraphQL query string (optional with persisted queries)
42
- * - `variables`: JSON-encoded variables object
43
- * - `operationName`: Name of the operation to execute
44
- * - `extensions`: JSON-encoded extensions object containing persistedQuery
45
- *
46
- * @param searchParams - URLSearchParams from the request URL
47
- * @returns Effect that resolves to the parsed request body or fails with parse error
48
- */
49
- const parseGetRequestBody = (searchParams) => effect_1.Effect.try({
50
- try: () => {
51
- const extensionsRaw = searchParams.get("extensions");
52
- const variablesRaw = searchParams.get("variables");
53
- const result = {};
54
- const query = searchParams.get("query");
55
- if (query) {
56
- result.query = query;
57
- }
58
- const operationName = searchParams.get("operationName");
59
- if (operationName) {
60
- result.operationName = operationName;
61
- }
62
- if (variablesRaw) {
63
- result.variables = JSON.parse(variablesRaw);
64
- }
65
- if (extensionsRaw) {
66
- result.extensions = JSON.parse(extensionsRaw);
67
- }
68
- return result;
69
- },
70
- catch: (e) => new Error(`Failed to parse query parameters: ${e}`),
71
- });
72
- exports.parseGetRequestBody = parseGetRequestBody;
73
- //# sourceMappingURL=utils.js.map
package/dist/utils.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;AAAA,mCAA+B;AAC/B,mCAAmC;AAGnC;;;;;;GAMG;AACI,MAAM,WAAW,GAAG,CACzB,KAAa,EACb,YAA2B,QAAQ,EACZ,EAAE,CAAC,eAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAA,mBAAU,EAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAA;AAHnF,QAAA,WAAW,eAGwE;AAWhG;;;;;GAKG;AACI,MAAM,4BAA4B,GAAG,CAC1C,UAAmB,EACa,EAAE;IAClC,IAAI,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,KAAK,IAAI,IAAI,CAAC,CAAC,gBAAgB,IAAI,UAAU,CAAC,EAAE,CAAC;QAC/F,OAAO,IAAI,CAAA;IACb,CAAC;IAED,MAAM,EAAE,GAAI,UAAsC,CAAC,cAAc,CAAA;IAEjE,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,IAAI,IAAI,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC,YAAY,IAAI,EAAE,CAAC,EAAE,CAAC;QACzF,OAAO,IAAI,CAAA;IACb,CAAC;IAED,MAAM,OAAO,GAAI,EAA8B,CAAC,OAAO,CAAA;IACvD,MAAM,UAAU,GAAI,EAA8B,CAAC,UAAU,CAAA;IAE7D,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;QAClE,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,CAAA;AAChC,CAAC,CAAA;AArBY,QAAA,4BAA4B,gCAqBxC;AAeD;;;;;;;;;;;GAWG;AACI,MAAM,mBAAmB,GAAG,CACjC,YAA6B,EACa,EAAE,CAC5C,eAAM,CAAC,GAAG,CAAC;IACT,GAAG,EAAE,GAAG,EAAE;QACR,MAAM,aAAa,GAAG,YAAY,CAAC,GAAG,CAAC,YAAY,CAAC,CAAA;QACpD,MAAM,YAAY,GAAG,YAAY,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QAElD,MAAM,MAAM,GAAuB,EAAE,CAAA;QAErC,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA;QACvC,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,KAAK,GAAG,KAAK,CAAA;QACtB,CAAC;QAED,MAAM,aAAa,GAAG,YAAY,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;QACvD,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,CAAC,aAAa,GAAG,aAAa,CAAA;QACtC,CAAC;QAED,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;QAC7C,CAAC;QAED,IAAI,aAAa,EAAE,CAAC;YAClB,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAA;QAC/C,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;IACD,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,KAAK,CAAC,qCAAqC,CAAC,EAAE,CAAC;CAClE,CAAC,CAAA;AA/BS,QAAA,mBAAmB,uBA+B5B"}
package/src/config.ts DELETED
@@ -1,122 +0,0 @@
1
- import { Config, Layer } from "effect"
2
- import type { PersistedQueryStore } from "./store"
3
- import type { MakeGraphQLRouterOptions } from "@effect-gql/core/server"
4
-
5
- /**
6
- * Operating mode for persisted queries.
7
- *
8
- * - `"apq"`: Automatic Persisted Queries - clients can register queries at runtime
9
- * - `"safelist"`: Only pre-registered queries are allowed (security mode)
10
- */
11
- export type PersistedQueryMode = "apq" | "safelist"
12
-
13
- /**
14
- * Hash algorithm used for query hashing.
15
- * Must match what clients use - Apollo clients use SHA-256.
16
- */
17
- export type HashAlgorithm = "sha256" | "sha512"
18
-
19
- /**
20
- * Configuration for the persisted queries feature.
21
- */
22
- export interface PersistedQueriesConfig {
23
- /**
24
- * Operating mode.
25
- *
26
- * - `"apq"`: Automatic Persisted Queries - clients can register queries at runtime.
27
- * Unknown hashes trigger PERSISTED_QUERY_NOT_FOUND, prompting clients to retry with query.
28
- *
29
- * - `"safelist"`: Only pre-registered queries are allowed.
30
- * Unknown hashes return PERSISTED_QUERY_NOT_ALLOWED error.
31
- * Use with `makeSafelistStore()` for production security.
32
- *
33
- * Default: `"apq"`
34
- */
35
- readonly mode?: PersistedQueryMode
36
-
37
- /**
38
- * Layer providing the PersistedQueryStore service.
39
- *
40
- * Defaults to in-memory LRU store with 1000 entries.
41
- * Use `makeMemoryStore()` for custom size or `makeSafelistStore()` for safelist mode.
42
- *
43
- * @example
44
- * ```typescript
45
- * // Custom memory store
46
- * store: makeMemoryStore({ maxSize: 5000 })
47
- *
48
- * // Safelist store
49
- * store: makeSafelistStore({ "hash1": "query {...}", "hash2": "query {...}" })
50
- * ```
51
- */
52
- readonly store?: Layer.Layer<PersistedQueryStore>
53
-
54
- /**
55
- * Whether to support GET requests with query parameters.
56
- *
57
- * When enabled, the router accepts:
58
- * ```
59
- * GET /graphql?extensions={"persistedQuery":{"version":1,"sha256Hash":"..."}}&variables={...}&operationName=...
60
- * ```
61
- *
62
- * This enables CDN caching since the same hash always maps to the same URL.
63
- *
64
- * Default: `true`
65
- */
66
- readonly enableGet?: boolean
67
-
68
- /**
69
- * Validate that the provided query matches its hash when storing.
70
- *
71
- * This prevents hash collision attacks where a malicious client could
72
- * register a different query under someone else's hash.
73
- *
74
- * Has a slight performance overhead for computing the hash.
75
- *
76
- * Default: `true`
77
- */
78
- readonly validateHash?: boolean
79
-
80
- /**
81
- * Hash algorithm to use for validation.
82
- * Must match what clients use - Apollo clients use SHA-256.
83
- *
84
- * Default: `"sha256"`
85
- */
86
- readonly hashAlgorithm?: HashAlgorithm
87
- }
88
-
89
- /**
90
- * Options for the persisted queries router.
91
- *
92
- * Extends the standard GraphQL router options with persisted query configuration.
93
- */
94
- export interface PersistedQueriesRouterOptions
95
- extends MakeGraphQLRouterOptions, PersistedQueriesConfig {}
96
-
97
- /**
98
- * Effect Config for loading persisted queries settings from environment variables.
99
- *
100
- * Environment variables:
101
- * - `PERSISTED_QUERIES_MODE`: `"apq"` | `"safelist"` (default: `"apq"`)
102
- * - `PERSISTED_QUERIES_ENABLE_GET`: boolean (default: `true`)
103
- * - `PERSISTED_QUERIES_VALIDATE_HASH`: boolean (default: `true`)
104
- *
105
- * Note: The store must still be provided programmatically.
106
- *
107
- * @example
108
- * ```typescript
109
- * import { PersistedQueriesConfigFromEnv } from "@effect-gql/persisted-queries"
110
- *
111
- * const config = yield* Config.unwrap(PersistedQueriesConfigFromEnv)
112
- * ```
113
- */
114
- export const PersistedQueriesConfigFromEnv: Config.Config<Omit<PersistedQueriesConfig, "store">> =
115
- Config.all({
116
- mode: Config.literal(
117
- "apq",
118
- "safelist"
119
- )("PERSISTED_QUERIES_MODE").pipe(Config.withDefault("apq" as const)),
120
- enableGet: Config.boolean("PERSISTED_QUERIES_ENABLE_GET").pipe(Config.withDefault(true)),
121
- validateHash: Config.boolean("PERSISTED_QUERIES_VALIDATE_HASH").pipe(Config.withDefault(true)),
122
- })