@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/dist/errors.d.ts DELETED
@@ -1,77 +0,0 @@
1
- /**
2
- * GraphQL error format for APQ responses.
3
- * Compatible with Apollo Client's error handling.
4
- */
5
- export interface PersistedQueryGraphQLError {
6
- readonly message: string;
7
- readonly extensions: {
8
- readonly code: string;
9
- readonly [key: string]: unknown;
10
- };
11
- }
12
- declare const PersistedQueryNotFoundError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
13
- readonly _tag: "PersistedQueryNotFoundError";
14
- } & Readonly<A>;
15
- /**
16
- * Error returned when a persisted query hash is not found in the store
17
- * and no query body was provided.
18
- *
19
- * Apollo clients recognize this error and automatically retry with the full query.
20
- * This is the expected flow for Automatic Persisted Queries (APQ).
21
- */
22
- export declare class PersistedQueryNotFoundError extends PersistedQueryNotFoundError_base<{
23
- readonly hash: string;
24
- }> {
25
- /**
26
- * Convert to GraphQL error format compatible with Apollo protocol.
27
- */
28
- toGraphQLError(): PersistedQueryGraphQLError;
29
- }
30
- declare const PersistedQueryVersionError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
31
- readonly _tag: "PersistedQueryVersionError";
32
- } & Readonly<A>;
33
- /**
34
- * Error returned when the persisted query protocol version is not supported.
35
- *
36
- * Currently only version 1 is supported, which uses SHA-256 hashing.
37
- */
38
- export declare class PersistedQueryVersionError extends PersistedQueryVersionError_base<{
39
- readonly version: number;
40
- }> {
41
- toGraphQLError(): PersistedQueryGraphQLError;
42
- }
43
- declare const PersistedQueryHashMismatchError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
44
- readonly _tag: "PersistedQueryHashMismatchError";
45
- } & Readonly<A>;
46
- /**
47
- * Error returned when the provided query doesn't match its hash.
48
- *
49
- * This can indicate a client bug or a potential hash collision attack.
50
- * Hash validation is enabled by default and can be disabled if needed.
51
- */
52
- export declare class PersistedQueryHashMismatchError extends PersistedQueryHashMismatchError_base<{
53
- readonly providedHash: string;
54
- readonly computedHash: string;
55
- }> {
56
- toGraphQLError(): PersistedQueryGraphQLError;
57
- }
58
- declare const PersistedQueryNotAllowedError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
59
- readonly _tag: "PersistedQueryNotAllowedError";
60
- } & Readonly<A>;
61
- /**
62
- * Error returned when trying to execute a query that is not in the safelist.
63
- *
64
- * In safelist mode, only pre-registered queries are allowed.
65
- * This error is returned when a client tries to register a new query.
66
- */
67
- export declare class PersistedQueryNotAllowedError extends PersistedQueryNotAllowedError_base<{
68
- readonly hash: string;
69
- }> {
70
- toGraphQLError(): PersistedQueryGraphQLError;
71
- }
72
- /**
73
- * Union type of all APQ-related errors
74
- */
75
- export type PersistedQueryError = PersistedQueryNotFoundError | PersistedQueryVersionError | PersistedQueryHashMismatchError | PersistedQueryNotAllowedError;
76
- export {};
77
- //# sourceMappingURL=errors.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,EAAE;QACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;QACrB,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;KAChC,CAAA;CACF;;;;AAED;;;;;;GAMG;AACH,qBAAa,2BAA4B,SAAQ,iCAAgD;IAC/F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB,CAAC;IACA;;OAEG;IACH,cAAc,IAAI,0BAA0B;CAQ7C;;;;AAED;;;;GAIG;AACH,qBAAa,0BAA2B,SAAQ,gCAA+C;IAC7F,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CACzB,CAAC;IACA,cAAc,IAAI,0BAA0B;CAS7C;;;;AAED;;;;;GAKG;AACH,qBAAa,+BAAgC,SAAQ,qCAEnD;IACA,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAA;CAC9B,CAAC;IACA,cAAc,IAAI,0BAA0B;CAQ7C;;;;AAED;;;;;GAKG;AACH,qBAAa,6BAA8B,SAAQ,mCAEjD;IACA,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB,CAAC;IACA,cAAc,IAAI,0BAA0B;CAQ7C;AAED;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAC3B,2BAA2B,GAC3B,0BAA0B,GAC1B,+BAA+B,GAC/B,6BAA6B,CAAA"}
package/dist/errors.js DELETED
@@ -1,77 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PersistedQueryNotAllowedError = exports.PersistedQueryHashMismatchError = exports.PersistedQueryVersionError = exports.PersistedQueryNotFoundError = void 0;
4
- const effect_1 = require("effect");
5
- /**
6
- * Error returned when a persisted query hash is not found in the store
7
- * and no query body was provided.
8
- *
9
- * Apollo clients recognize this error and automatically retry with the full query.
10
- * This is the expected flow for Automatic Persisted Queries (APQ).
11
- */
12
- class PersistedQueryNotFoundError extends effect_1.Data.TaggedError("PersistedQueryNotFoundError") {
13
- /**
14
- * Convert to GraphQL error format compatible with Apollo protocol.
15
- */
16
- toGraphQLError() {
17
- return {
18
- message: "PersistedQueryNotFound",
19
- extensions: {
20
- code: "PERSISTED_QUERY_NOT_FOUND",
21
- },
22
- };
23
- }
24
- }
25
- exports.PersistedQueryNotFoundError = PersistedQueryNotFoundError;
26
- /**
27
- * Error returned when the persisted query protocol version is not supported.
28
- *
29
- * Currently only version 1 is supported, which uses SHA-256 hashing.
30
- */
31
- class PersistedQueryVersionError extends effect_1.Data.TaggedError("PersistedQueryVersionError") {
32
- toGraphQLError() {
33
- return {
34
- message: `Unsupported persisted query version: ${this.version}`,
35
- extensions: {
36
- code: "PERSISTED_QUERY_VERSION_NOT_SUPPORTED",
37
- version: this.version,
38
- },
39
- };
40
- }
41
- }
42
- exports.PersistedQueryVersionError = PersistedQueryVersionError;
43
- /**
44
- * Error returned when the provided query doesn't match its hash.
45
- *
46
- * This can indicate a client bug or a potential hash collision attack.
47
- * Hash validation is enabled by default and can be disabled if needed.
48
- */
49
- class PersistedQueryHashMismatchError extends effect_1.Data.TaggedError("PersistedQueryHashMismatchError") {
50
- toGraphQLError() {
51
- return {
52
- message: "Query hash does not match provided hash",
53
- extensions: {
54
- code: "PERSISTED_QUERY_HASH_MISMATCH",
55
- },
56
- };
57
- }
58
- }
59
- exports.PersistedQueryHashMismatchError = PersistedQueryHashMismatchError;
60
- /**
61
- * Error returned when trying to execute a query that is not in the safelist.
62
- *
63
- * In safelist mode, only pre-registered queries are allowed.
64
- * This error is returned when a client tries to register a new query.
65
- */
66
- class PersistedQueryNotAllowedError extends effect_1.Data.TaggedError("PersistedQueryNotAllowedError") {
67
- toGraphQLError() {
68
- return {
69
- message: "Query not in safelist",
70
- extensions: {
71
- code: "PERSISTED_QUERY_NOT_ALLOWED",
72
- },
73
- };
74
- }
75
- }
76
- exports.PersistedQueryNotAllowedError = PersistedQueryNotAllowedError;
77
- //# sourceMappingURL=errors.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;AAAA,mCAA6B;AAc7B;;;;;;GAMG;AACH,MAAa,2BAA4B,SAAQ,aAAI,CAAC,WAAW,CAAC,6BAA6B,CAE7F;IACA;;OAEG;IACH,cAAc;QACZ,OAAO;YACL,OAAO,EAAE,wBAAwB;YACjC,UAAU,EAAE;gBACV,IAAI,EAAE,2BAA2B;aAClC;SACF,CAAA;IACH,CAAC;CACF;AAdD,kEAcC;AAED;;;;GAIG;AACH,MAAa,0BAA2B,SAAQ,aAAI,CAAC,WAAW,CAAC,4BAA4B,CAE3F;IACA,cAAc;QACZ,OAAO;YACL,OAAO,EAAE,wCAAwC,IAAI,CAAC,OAAO,EAAE;YAC/D,UAAU,EAAE;gBACV,IAAI,EAAE,uCAAuC;gBAC7C,OAAO,EAAE,IAAI,CAAC,OAAO;aACtB;SACF,CAAA;IACH,CAAC;CACF;AAZD,gEAYC;AAED;;;;;GAKG;AACH,MAAa,+BAAgC,SAAQ,aAAI,CAAC,WAAW,CACnE,iCAAiC,CAIjC;IACA,cAAc;QACZ,OAAO;YACL,OAAO,EAAE,yCAAyC;YAClD,UAAU,EAAE;gBACV,IAAI,EAAE,+BAA+B;aACtC;SACF,CAAA;IACH,CAAC;CACF;AAdD,0EAcC;AAED;;;;;GAKG;AACH,MAAa,6BAA8B,SAAQ,aAAI,CAAC,WAAW,CACjE,+BAA+B,CAG/B;IACA,cAAc;QACZ,OAAO;YACL,OAAO,EAAE,uBAAuB;YAChC,UAAU,EAAE;gBACV,IAAI,EAAE,6BAA6B;aACpC;SACF,CAAA;IACH,CAAC;CACF;AAbD,sEAaC"}
package/dist/index.d.ts DELETED
@@ -1,56 +0,0 @@
1
- /**
2
- * @effect-gql/persisted-queries
3
- *
4
- * Apollo Persisted Queries support for Effect GraphQL.
5
- *
6
- * Supports both Automatic Persisted Queries (APQ) mode for runtime registration
7
- * and Safelist mode for pre-registered query allowlisting.
8
- *
9
- * ## Quick Start
10
- *
11
- * @example APQ Mode (runtime registration)
12
- * ```typescript
13
- * import { makePersistedQueriesRouter } from "@effect-gql/persisted-queries"
14
- *
15
- * const router = makePersistedQueriesRouter(schema, serviceLayer, {
16
- * mode: "apq",
17
- * enableGet: true, // Enable CDN caching
18
- * graphiql: { path: "/graphiql" },
19
- * })
20
- * ```
21
- *
22
- * @example Safelist Mode (pre-registered queries only)
23
- * ```typescript
24
- * import { makePersistedQueriesRouter, makeSafelistStore } from "@effect-gql/persisted-queries"
25
- *
26
- * const router = makePersistedQueriesRouter(schema, serviceLayer, {
27
- * mode: "safelist",
28
- * store: makeSafelistStore({
29
- * "ecf4edb46db40b5132295c0291d62fb65d6759a9eedfa4d5d612dd5ec54a6b38": "query GetUser($id: ID!) { user(id: $id) { name email } }",
30
- * "a1b2c3d4...": "query GetPosts { posts { title } }",
31
- * }),
32
- * })
33
- * ```
34
- *
35
- * @example Custom Store Size
36
- * ```typescript
37
- * import { makePersistedQueriesRouter, makeMemoryStore } from "@effect-gql/persisted-queries"
38
- *
39
- * const router = makePersistedQueriesRouter(schema, serviceLayer, {
40
- * store: makeMemoryStore({ maxSize: 5000 }),
41
- * })
42
- * ```
43
- *
44
- * @packageDocumentation
45
- */
46
- export { makePersistedQueriesRouter } from "./persisted-queries-router";
47
- export { PersistedQueryStore } from "./store";
48
- export type { PersistedQueryStore as PersistedQueryStoreInterface } from "./store";
49
- export { makeMemoryStore, makeSafelistStore } from "./memory-store";
50
- export type { MemoryStoreConfig } from "./memory-store";
51
- export type { PersistedQueriesConfig, PersistedQueriesRouterOptions, PersistedQueryMode, HashAlgorithm, } from "./config";
52
- export { PersistedQueriesConfigFromEnv } from "./config";
53
- export { PersistedQueryNotFoundError, PersistedQueryVersionError, PersistedQueryHashMismatchError, PersistedQueryNotAllowedError, type PersistedQueryError, type PersistedQueryGraphQLError, } from "./errors";
54
- export { computeHash, parsePersistedQueryExtension, parseGetRequestBody } from "./utils";
55
- export type { PersistedQueryExtension, GraphQLRequestBody } from "./utils";
56
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAGH,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAA;AAGvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC7C,YAAY,EAAE,mBAAmB,IAAI,4BAA4B,EAAE,MAAM,SAAS,CAAA;AAClF,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AACnE,YAAY,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAGvD,YAAY,EACV,sBAAsB,EACtB,6BAA6B,EAC7B,kBAAkB,EAClB,aAAa,GACd,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,6BAA6B,EAAE,MAAM,UAAU,CAAA;AAGxD,OAAO,EACL,2BAA2B,EAC3B,0BAA0B,EAC1B,+BAA+B,EAC/B,6BAA6B,EAC7B,KAAK,mBAAmB,EACxB,KAAK,0BAA0B,GAChC,MAAM,UAAU,CAAA;AAGjB,OAAO,EAAE,WAAW,EAAE,4BAA4B,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AACxF,YAAY,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA"}
package/dist/index.js DELETED
@@ -1,71 +0,0 @@
1
- "use strict";
2
- /**
3
- * @effect-gql/persisted-queries
4
- *
5
- * Apollo Persisted Queries support for Effect GraphQL.
6
- *
7
- * Supports both Automatic Persisted Queries (APQ) mode for runtime registration
8
- * and Safelist mode for pre-registered query allowlisting.
9
- *
10
- * ## Quick Start
11
- *
12
- * @example APQ Mode (runtime registration)
13
- * ```typescript
14
- * import { makePersistedQueriesRouter } from "@effect-gql/persisted-queries"
15
- *
16
- * const router = makePersistedQueriesRouter(schema, serviceLayer, {
17
- * mode: "apq",
18
- * enableGet: true, // Enable CDN caching
19
- * graphiql: { path: "/graphiql" },
20
- * })
21
- * ```
22
- *
23
- * @example Safelist Mode (pre-registered queries only)
24
- * ```typescript
25
- * import { makePersistedQueriesRouter, makeSafelistStore } from "@effect-gql/persisted-queries"
26
- *
27
- * const router = makePersistedQueriesRouter(schema, serviceLayer, {
28
- * mode: "safelist",
29
- * store: makeSafelistStore({
30
- * "ecf4edb46db40b5132295c0291d62fb65d6759a9eedfa4d5d612dd5ec54a6b38": "query GetUser($id: ID!) { user(id: $id) { name email } }",
31
- * "a1b2c3d4...": "query GetPosts { posts { title } }",
32
- * }),
33
- * })
34
- * ```
35
- *
36
- * @example Custom Store Size
37
- * ```typescript
38
- * import { makePersistedQueriesRouter, makeMemoryStore } from "@effect-gql/persisted-queries"
39
- *
40
- * const router = makePersistedQueriesRouter(schema, serviceLayer, {
41
- * store: makeMemoryStore({ maxSize: 5000 }),
42
- * })
43
- * ```
44
- *
45
- * @packageDocumentation
46
- */
47
- Object.defineProperty(exports, "__esModule", { value: true });
48
- exports.parseGetRequestBody = exports.parsePersistedQueryExtension = exports.computeHash = exports.PersistedQueryNotAllowedError = exports.PersistedQueryHashMismatchError = exports.PersistedQueryVersionError = exports.PersistedQueryNotFoundError = exports.PersistedQueriesConfigFromEnv = exports.makeSafelistStore = exports.makeMemoryStore = exports.PersistedQueryStore = exports.makePersistedQueriesRouter = void 0;
49
- // Router
50
- var persisted_queries_router_1 = require("./persisted-queries-router");
51
- Object.defineProperty(exports, "makePersistedQueriesRouter", { enumerable: true, get: function () { return persisted_queries_router_1.makePersistedQueriesRouter; } });
52
- // Store interface and implementations
53
- var store_1 = require("./store");
54
- Object.defineProperty(exports, "PersistedQueryStore", { enumerable: true, get: function () { return store_1.PersistedQueryStore; } });
55
- var memory_store_1 = require("./memory-store");
56
- Object.defineProperty(exports, "makeMemoryStore", { enumerable: true, get: function () { return memory_store_1.makeMemoryStore; } });
57
- Object.defineProperty(exports, "makeSafelistStore", { enumerable: true, get: function () { return memory_store_1.makeSafelistStore; } });
58
- var config_1 = require("./config");
59
- Object.defineProperty(exports, "PersistedQueriesConfigFromEnv", { enumerable: true, get: function () { return config_1.PersistedQueriesConfigFromEnv; } });
60
- // Errors
61
- var errors_1 = require("./errors");
62
- Object.defineProperty(exports, "PersistedQueryNotFoundError", { enumerable: true, get: function () { return errors_1.PersistedQueryNotFoundError; } });
63
- Object.defineProperty(exports, "PersistedQueryVersionError", { enumerable: true, get: function () { return errors_1.PersistedQueryVersionError; } });
64
- Object.defineProperty(exports, "PersistedQueryHashMismatchError", { enumerable: true, get: function () { return errors_1.PersistedQueryHashMismatchError; } });
65
- Object.defineProperty(exports, "PersistedQueryNotAllowedError", { enumerable: true, get: function () { return errors_1.PersistedQueryNotAllowedError; } });
66
- // Utilities
67
- var utils_1 = require("./utils");
68
- Object.defineProperty(exports, "computeHash", { enumerable: true, get: function () { return utils_1.computeHash; } });
69
- Object.defineProperty(exports, "parsePersistedQueryExtension", { enumerable: true, get: function () { return utils_1.parsePersistedQueryExtension; } });
70
- Object.defineProperty(exports, "parseGetRequestBody", { enumerable: true, get: function () { return utils_1.parseGetRequestBody; } });
71
- //# sourceMappingURL=index.js.map
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;;;AAEH,SAAS;AACT,uEAAuE;AAA9D,sIAAA,0BAA0B,OAAA;AAEnC,sCAAsC;AACtC,iCAA6C;AAApC,4GAAA,mBAAmB,OAAA;AAE5B,+CAAmE;AAA1D,+GAAA,eAAe,OAAA;AAAE,iHAAA,iBAAiB,OAAA;AAU3C,mCAAwD;AAA/C,uHAAA,6BAA6B,OAAA;AAEtC,SAAS;AACT,mCAOiB;AANf,qHAAA,2BAA2B,OAAA;AAC3B,oHAAA,0BAA0B,OAAA;AAC1B,yHAAA,+BAA+B,OAAA;AAC/B,uHAAA,6BAA6B,OAAA;AAK/B,YAAY;AACZ,iCAAwF;AAA/E,oGAAA,WAAW,OAAA;AAAE,qHAAA,4BAA4B,OAAA;AAAE,4GAAA,mBAAmB,OAAA"}
@@ -1,67 +0,0 @@
1
- import { Layer } from "effect";
2
- import { PersistedQueryStore } from "./store";
3
- /**
4
- * Configuration for the in-memory LRU store
5
- */
6
- export interface MemoryStoreConfig {
7
- /**
8
- * Maximum number of queries to cache.
9
- * When exceeded, least recently used queries are evicted.
10
- * Default: 1000
11
- */
12
- readonly maxSize?: number;
13
- }
14
- /**
15
- * Create an in-memory LRU (Least Recently Used) store for persisted queries.
16
- *
17
- * This implementation uses Map's natural insertion order for O(1) LRU operations:
18
- * - get: O(1) - delete and re-insert to move to end (most recently used)
19
- * - set: O(1) - insert at end, evict from front if needed
20
- * - eviction: O(1) - delete first entry (least recently used)
21
- *
22
- * This is the default store implementation suitable for single-instance servers.
23
- * For multi-instance deployments, consider using a shared store like Redis.
24
- *
25
- * @param config - Optional configuration for cache size
26
- * @returns A Layer providing the PersistedQueryStore service
27
- *
28
- * @example
29
- * ```typescript
30
- * import { makeMemoryStore, makePersistedQueriesRouter } from "@effect-gql/persisted-queries"
31
- *
32
- * // Default store with 1000 entry limit
33
- * const router1 = makePersistedQueriesRouter(schema, serviceLayer)
34
- *
35
- * // Custom store with larger cache
36
- * const router2 = makePersistedQueriesRouter(schema, serviceLayer, {
37
- * store: makeMemoryStore({ maxSize: 5000 })
38
- * })
39
- * ```
40
- */
41
- export declare const makeMemoryStore: (config?: MemoryStoreConfig) => Layer.Layer<PersistedQueryStore>;
42
- /**
43
- * Create a pre-populated safelist store.
44
- *
45
- * This store only allows queries that were provided at creation time.
46
- * Any attempt to store new queries is silently ignored.
47
- * Use this for production security where you want to allowlist specific operations.
48
- *
49
- * @param queries - Record mapping SHA-256 hashes to query strings
50
- * @returns A Layer providing the PersistedQueryStore service
51
- *
52
- * @example
53
- * ```typescript
54
- * import { makeSafelistStore, makePersistedQueriesRouter } from "@effect-gql/persisted-queries"
55
- *
56
- * // Pre-register allowed queries
57
- * const router = makePersistedQueriesRouter(schema, serviceLayer, {
58
- * mode: "safelist",
59
- * store: makeSafelistStore({
60
- * "ecf4edb46db40b5132295c0291d62fb65d6759a9eedfa4d5d612dd5ec54a6b38": "query GetUser($id: ID!) { user(id: $id) { name email } }",
61
- * "a1b2c3d4...": "query GetPosts { posts { title } }",
62
- * }),
63
- * })
64
- * ```
65
- */
66
- export declare const makeSafelistStore: (queries: Record<string, string>) => Layer.Layer<PersistedQueryStore>;
67
- //# sourceMappingURL=memory-store.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"memory-store.d.ts","sourceRoot":"","sources":["../src/memory-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,EAAU,MAAM,QAAQ,CAAA;AAC9C,OAAO,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAE7C;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,eAAe,GAC1B,SAAQ,iBAAsB,KAC7B,KAAK,CAAC,KAAK,CAAC,mBAAmB,CAgDjC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC9B,KAAK,CAAC,KAAK,CAAC,mBAAmB,CAY/B,CAAA"}
@@ -1,104 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.makeSafelistStore = exports.makeMemoryStore = void 0;
4
- const effect_1 = require("effect");
5
- const store_1 = require("./store");
6
- /**
7
- * Create an in-memory LRU (Least Recently Used) store for persisted queries.
8
- *
9
- * This implementation uses Map's natural insertion order for O(1) LRU operations:
10
- * - get: O(1) - delete and re-insert to move to end (most recently used)
11
- * - set: O(1) - insert at end, evict from front if needed
12
- * - eviction: O(1) - delete first entry (least recently used)
13
- *
14
- * This is the default store implementation suitable for single-instance servers.
15
- * For multi-instance deployments, consider using a shared store like Redis.
16
- *
17
- * @param config - Optional configuration for cache size
18
- * @returns A Layer providing the PersistedQueryStore service
19
- *
20
- * @example
21
- * ```typescript
22
- * import { makeMemoryStore, makePersistedQueriesRouter } from "@effect-gql/persisted-queries"
23
- *
24
- * // Default store with 1000 entry limit
25
- * const router1 = makePersistedQueriesRouter(schema, serviceLayer)
26
- *
27
- * // Custom store with larger cache
28
- * const router2 = makePersistedQueriesRouter(schema, serviceLayer, {
29
- * store: makeMemoryStore({ maxSize: 5000 })
30
- * })
31
- * ```
32
- */
33
- const makeMemoryStore = (config = {}) => {
34
- const maxSize = config.maxSize ?? 1000;
35
- // Map maintains insertion order - we use this for O(1) LRU
36
- // First entry = least recently used, last entry = most recently used
37
- const cache = new Map();
38
- // Move entry to end (most recently used) by deleting and re-inserting
39
- const touch = (hash, query) => {
40
- cache.delete(hash);
41
- cache.set(hash, query);
42
- };
43
- // Evict oldest entry (first in Map) if over capacity - O(1)
44
- const evictIfNeeded = () => {
45
- if (cache.size <= maxSize)
46
- return;
47
- // Map.keys().next() gives us the first (oldest) key in O(1)
48
- const oldestKey = cache.keys().next().value;
49
- if (oldestKey !== undefined) {
50
- cache.delete(oldestKey);
51
- }
52
- };
53
- return effect_1.Layer.succeed(store_1.PersistedQueryStore, store_1.PersistedQueryStore.of({
54
- get: (hash) => effect_1.Effect.sync(() => {
55
- const query = cache.get(hash);
56
- if (query === undefined) {
57
- return effect_1.Option.none();
58
- }
59
- // Move to end (most recently used)
60
- touch(hash, query);
61
- return effect_1.Option.some(query);
62
- }),
63
- set: (hash, query) => effect_1.Effect.sync(() => {
64
- // If key exists, delete first to ensure it moves to end
65
- cache.delete(hash);
66
- cache.set(hash, query);
67
- evictIfNeeded();
68
- }),
69
- has: (hash) => effect_1.Effect.sync(() => cache.has(hash)),
70
- }));
71
- };
72
- exports.makeMemoryStore = makeMemoryStore;
73
- /**
74
- * Create a pre-populated safelist store.
75
- *
76
- * This store only allows queries that were provided at creation time.
77
- * Any attempt to store new queries is silently ignored.
78
- * Use this for production security where you want to allowlist specific operations.
79
- *
80
- * @param queries - Record mapping SHA-256 hashes to query strings
81
- * @returns A Layer providing the PersistedQueryStore service
82
- *
83
- * @example
84
- * ```typescript
85
- * import { makeSafelistStore, makePersistedQueriesRouter } from "@effect-gql/persisted-queries"
86
- *
87
- * // Pre-register allowed queries
88
- * const router = makePersistedQueriesRouter(schema, serviceLayer, {
89
- * mode: "safelist",
90
- * store: makeSafelistStore({
91
- * "ecf4edb46db40b5132295c0291d62fb65d6759a9eedfa4d5d612dd5ec54a6b38": "query GetUser($id: ID!) { user(id: $id) { name email } }",
92
- * "a1b2c3d4...": "query GetPosts { posts { title } }",
93
- * }),
94
- * })
95
- * ```
96
- */
97
- const makeSafelistStore = (queries) => effect_1.Layer.succeed(store_1.PersistedQueryStore, store_1.PersistedQueryStore.of({
98
- get: (hash) => effect_1.Effect.succeed(queries[hash] !== undefined ? effect_1.Option.some(queries[hash]) : effect_1.Option.none()),
99
- // No-op for safelist mode - queries cannot be added at runtime
100
- set: () => effect_1.Effect.void,
101
- has: (hash) => effect_1.Effect.succeed(queries[hash] !== undefined),
102
- }));
103
- exports.makeSafelistStore = makeSafelistStore;
104
- //# sourceMappingURL=memory-store.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"memory-store.js","sourceRoot":"","sources":["../src/memory-store.ts"],"names":[],"mappings":";;;AAAA,mCAA8C;AAC9C,mCAA6C;AAc7C;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACI,MAAM,eAAe,GAAG,CAC7B,SAA4B,EAAE,EACI,EAAE;IACpC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,IAAI,CAAA;IAEtC,2DAA2D;IAC3D,qEAAqE;IACrE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAA;IAEvC,sEAAsE;IACtE,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,KAAa,EAAQ,EAAE;QAClD,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;QAClB,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IACxB,CAAC,CAAA;IAED,4DAA4D;IAC5D,MAAM,aAAa,GAAG,GAAS,EAAE;QAC/B,IAAI,KAAK,CAAC,IAAI,IAAI,OAAO;YAAE,OAAM;QACjC,4DAA4D;QAC5D,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAA;QAC3C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QACzB,CAAC;IACH,CAAC,CAAA;IAED,OAAO,cAAK,CAAC,OAAO,CAClB,2BAAmB,EACnB,2BAAmB,CAAC,EAAE,CAAC;QACrB,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CACZ,eAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACf,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YAC7B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,OAAO,eAAM,CAAC,IAAI,EAAU,CAAA;YAC9B,CAAC;YACD,mCAAmC;YACnC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;YAClB,OAAO,eAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QAC3B,CAAC,CAAC;QAEJ,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CACnB,eAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACf,wDAAwD;YACxD,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;YAClB,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;YACtB,aAAa,EAAE,CAAA;QACjB,CAAC,CAAC;QAEJ,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,eAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;KAClD,CAAC,CACH,CAAA;AACH,CAAC,CAAA;AAlDY,QAAA,eAAe,mBAkD3B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACI,MAAM,iBAAiB,GAAG,CAC/B,OAA+B,EACG,EAAE,CACpC,cAAK,CAAC,OAAO,CACX,2BAAmB,EACnB,2BAAmB,CAAC,EAAE,CAAC;IACrB,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CACZ,eAAM,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,eAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,eAAM,CAAC,IAAI,EAAE,CAAC;IAE1F,+DAA+D;IAC/D,GAAG,EAAE,GAAG,EAAE,CAAC,eAAM,CAAC,IAAI;IAEtB,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,eAAM,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC;CAC3D,CAAC,CACH,CAAA;AAdU,QAAA,iBAAiB,qBAc3B"}
@@ -1,58 +0,0 @@
1
- import { HttpRouter } from "@effect/platform";
2
- import { Layer } from "effect";
3
- import { GraphQLSchema } from "graphql";
4
- import type { PersistedQueriesRouterOptions } from "./config";
5
- /**
6
- * Create a GraphQL router with Apollo Persisted Queries support.
7
- *
8
- * This creates a complete GraphQL router that includes:
9
- * - Apollo Persisted Queries (APQ) support
10
- * - GET request support for CDN caching
11
- * - All standard GraphQL router features (validation, execution, extensions)
12
- *
13
- * ## Apollo APQ Protocol
14
- *
15
- * 1. Client sends request with `extensions.persistedQuery.sha256Hash`
16
- * 2. If hash found in store, execute the stored query
17
- * 3. If hash NOT found and query provided (APQ mode), store it and execute
18
- * 4. If hash NOT found and NO query, return `PERSISTED_QUERY_NOT_FOUND`
19
- * 5. Client retries with both hash and query
20
- *
21
- * ## Modes
22
- *
23
- * - **APQ mode** (`mode: "apq"`): Automatic runtime registration.
24
- * Unknown queries trigger NOT_FOUND, prompting client retry with full query.
25
- *
26
- * - **Safelist mode** (`mode: "safelist"`): Pre-registered queries only.
27
- * Unknown queries return NOT_ALLOWED error. Use with `makeSafelistStore()`.
28
- *
29
- * @example APQ Mode (default)
30
- * ```typescript
31
- * import { makePersistedQueriesRouter } from "@effect-gql/persisted-queries"
32
- *
33
- * const router = makePersistedQueriesRouter(schema, serviceLayer, {
34
- * mode: "apq",
35
- * enableGet: true,
36
- * graphiql: { path: "/graphiql" },
37
- * })
38
- * ```
39
- *
40
- * @example Safelist Mode
41
- * ```typescript
42
- * import { makePersistedQueriesRouter, makeSafelistStore } from "@effect-gql/persisted-queries"
43
- *
44
- * const router = makePersistedQueriesRouter(schema, serviceLayer, {
45
- * mode: "safelist",
46
- * store: makeSafelistStore({
47
- * "abc123...": "query GetUser($id: ID!) { user(id: $id) { name } }",
48
- * }),
49
- * })
50
- * ```
51
- *
52
- * @param schema - The GraphQL schema
53
- * @param layer - Effect layer providing services required by resolvers
54
- * @param options - Router and persisted query configuration
55
- * @returns An HttpRouter with persisted query support
56
- */
57
- export declare const makePersistedQueriesRouter: <R>(schema: GraphQLSchema, layer: Layer.Layer<R>, options?: PersistedQueriesRouterOptions) => HttpRouter.HttpRouter<never, never>;
58
- //# sourceMappingURL=persisted-queries-router.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"persisted-queries-router.d.ts","sourceRoot":"","sources":["../src/persisted-queries-router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAyC,MAAM,kBAAkB,CAAA;AACpF,OAAO,EAAU,KAAK,EAAU,MAAM,QAAQ,CAAA;AAC9C,OAAO,EACL,aAAa,EAOd,MAAM,SAAS,CAAA;AAsBhB,OAAO,KAAK,EAAE,6BAA6B,EAAqC,MAAM,UAAU,CAAA;AAchG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AACH,eAAO,MAAM,0BAA0B,GAAI,CAAC,EAC1C,QAAQ,aAAa,EACrB,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EACrB,UAAS,6BAAkC,KAC1C,UAAU,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CA8TpC,CAAA"}