@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.
- package/README.md +100 -0
- package/index.cjs +385 -0
- package/index.cjs.map +1 -0
- package/index.d.cts +403 -0
- package/index.d.ts +403 -0
- package/index.js +372 -0
- package/index.js.map +1 -0
- package/package.json +14 -26
- package/dist/config.d.ts +0 -106
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -27
- package/dist/config.js.map +0 -1
- package/dist/errors.d.ts +0 -77
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -77
- package/dist/errors.js.map +0 -1
- package/dist/index.d.ts +0 -56
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -71
- package/dist/index.js.map +0 -1
- package/dist/memory-store.d.ts +0 -67
- package/dist/memory-store.d.ts.map +0 -1
- package/dist/memory-store.js +0 -104
- package/dist/memory-store.js.map +0 -1
- package/dist/persisted-queries-router.d.ts +0 -58
- package/dist/persisted-queries-router.d.ts.map +0 -1
- package/dist/persisted-queries-router.js +0 -277
- package/dist/persisted-queries-router.js.map +0 -1
- package/dist/store.d.ts +0 -49
- package/dist/store.d.ts.map +0 -1
- package/dist/store.js +0 -29
- package/dist/store.js.map +0 -1
- package/dist/utils.d.ts +0 -51
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -73
- package/dist/utils.js.map +0 -1
- package/src/config.ts +0 -122
- package/src/errors.ts +0 -107
- package/src/index.ts +0 -77
- package/src/memory-store.ts +0 -133
- package/src/persisted-queries-router.ts +0 -421
- package/src/store.ts +0 -54
- package/src/utils.ts +0 -111
|
@@ -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
|
package/dist/store.d.ts.map
DELETED
|
@@ -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
|
package/dist/utils.d.ts.map
DELETED
|
@@ -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
|
-
})
|