graphile-connection-filter 1.1.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.
Files changed (71) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +107 -0
  3. package/augmentations.d.ts +104 -0
  4. package/augmentations.js +11 -0
  5. package/esm/augmentations.d.ts +104 -0
  6. package/esm/augmentations.js +9 -0
  7. package/esm/index.d.ts +55 -0
  8. package/esm/index.js +56 -0
  9. package/esm/plugins/ConnectionFilterArgPlugin.d.ts +13 -0
  10. package/esm/plugins/ConnectionFilterArgPlugin.js +96 -0
  11. package/esm/plugins/ConnectionFilterAttributesPlugin.d.ts +14 -0
  12. package/esm/plugins/ConnectionFilterAttributesPlugin.js +79 -0
  13. package/esm/plugins/ConnectionFilterBackwardRelationsPlugin.d.ts +33 -0
  14. package/esm/plugins/ConnectionFilterBackwardRelationsPlugin.js +398 -0
  15. package/esm/plugins/ConnectionFilterComputedAttributesPlugin.d.ts +19 -0
  16. package/esm/plugins/ConnectionFilterComputedAttributesPlugin.js +133 -0
  17. package/esm/plugins/ConnectionFilterCustomOperatorsPlugin.d.ts +35 -0
  18. package/esm/plugins/ConnectionFilterCustomOperatorsPlugin.js +129 -0
  19. package/esm/plugins/ConnectionFilterForwardRelationsPlugin.d.ts +28 -0
  20. package/esm/plugins/ConnectionFilterForwardRelationsPlugin.js +168 -0
  21. package/esm/plugins/ConnectionFilterInflectionPlugin.d.ts +11 -0
  22. package/esm/plugins/ConnectionFilterInflectionPlugin.js +27 -0
  23. package/esm/plugins/ConnectionFilterLogicalOperatorsPlugin.d.ts +15 -0
  24. package/esm/plugins/ConnectionFilterLogicalOperatorsPlugin.js +86 -0
  25. package/esm/plugins/ConnectionFilterOperatorsPlugin.d.ts +21 -0
  26. package/esm/plugins/ConnectionFilterOperatorsPlugin.js +677 -0
  27. package/esm/plugins/ConnectionFilterTypesPlugin.d.ts +12 -0
  28. package/esm/plugins/ConnectionFilterTypesPlugin.js +225 -0
  29. package/esm/plugins/index.d.ts +11 -0
  30. package/esm/plugins/index.js +11 -0
  31. package/esm/plugins/operatorApply.d.ts +11 -0
  32. package/esm/plugins/operatorApply.js +70 -0
  33. package/esm/preset.d.ts +35 -0
  34. package/esm/preset.js +72 -0
  35. package/esm/types.d.ts +146 -0
  36. package/esm/types.js +4 -0
  37. package/esm/utils.d.ts +44 -0
  38. package/esm/utils.js +112 -0
  39. package/index.d.ts +55 -0
  40. package/index.js +77 -0
  41. package/package.json +58 -0
  42. package/plugins/ConnectionFilterArgPlugin.d.ts +13 -0
  43. package/plugins/ConnectionFilterArgPlugin.js +99 -0
  44. package/plugins/ConnectionFilterAttributesPlugin.d.ts +14 -0
  45. package/plugins/ConnectionFilterAttributesPlugin.js +82 -0
  46. package/plugins/ConnectionFilterBackwardRelationsPlugin.d.ts +33 -0
  47. package/plugins/ConnectionFilterBackwardRelationsPlugin.js +401 -0
  48. package/plugins/ConnectionFilterComputedAttributesPlugin.d.ts +19 -0
  49. package/plugins/ConnectionFilterComputedAttributesPlugin.js +136 -0
  50. package/plugins/ConnectionFilterCustomOperatorsPlugin.d.ts +35 -0
  51. package/plugins/ConnectionFilterCustomOperatorsPlugin.js +132 -0
  52. package/plugins/ConnectionFilterForwardRelationsPlugin.d.ts +28 -0
  53. package/plugins/ConnectionFilterForwardRelationsPlugin.js +171 -0
  54. package/plugins/ConnectionFilterInflectionPlugin.d.ts +11 -0
  55. package/plugins/ConnectionFilterInflectionPlugin.js +30 -0
  56. package/plugins/ConnectionFilterLogicalOperatorsPlugin.d.ts +15 -0
  57. package/plugins/ConnectionFilterLogicalOperatorsPlugin.js +89 -0
  58. package/plugins/ConnectionFilterOperatorsPlugin.d.ts +21 -0
  59. package/plugins/ConnectionFilterOperatorsPlugin.js +680 -0
  60. package/plugins/ConnectionFilterTypesPlugin.d.ts +12 -0
  61. package/plugins/ConnectionFilterTypesPlugin.js +228 -0
  62. package/plugins/index.d.ts +11 -0
  63. package/plugins/index.js +25 -0
  64. package/plugins/operatorApply.d.ts +11 -0
  65. package/plugins/operatorApply.js +73 -0
  66. package/preset.d.ts +35 -0
  67. package/preset.js +75 -0
  68. package/types.d.ts +146 -0
  69. package/types.js +7 -0
  70. package/utils.d.ts +44 -0
  71. package/utils.js +119 -0
@@ -0,0 +1,35 @@
1
+ import '../augmentations';
2
+ import type { GraphileConfig } from 'graphile-config';
3
+ /**
4
+ * ConnectionFilterCustomOperatorsPlugin
5
+ *
6
+ * Processes declarative operator factories from the preset configuration.
7
+ * Satellite plugins (PostGIS filter, search, pg_trgm, etc.) declare their
8
+ * operators via `connectionFilterOperatorFactories` in their preset's schema
9
+ * options. This plugin processes all factories during its own init hook,
10
+ * populating the filter registry used at schema build time.
11
+ *
12
+ * This declarative approach replaces the previous imperative
13
+ * `build.addConnectionFilterOperator()` API, eliminating timing/ordering
14
+ * dependencies between plugins.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * // In a satellite plugin's preset:
19
+ * const MyPreset = {
20
+ * schema: {
21
+ * connectionFilterOperatorFactories: [
22
+ * (build) => [{
23
+ * typeNames: 'String',
24
+ * operatorName: 'myOp',
25
+ * spec: {
26
+ * description: 'My operator',
27
+ * resolve: (i, v) => build.sql`${i} OP ${v}`,
28
+ * },
29
+ * }],
30
+ * ],
31
+ * },
32
+ * };
33
+ * ```
34
+ */
35
+ export declare const ConnectionFilterCustomOperatorsPlugin: GraphileConfig.Plugin;
@@ -0,0 +1,129 @@
1
+ import '../augmentations';
2
+ import { $$filters } from '../types';
3
+ import { makeApplyFromOperatorSpec } from './operatorApply';
4
+ const version = '1.0.0';
5
+ /**
6
+ * ConnectionFilterCustomOperatorsPlugin
7
+ *
8
+ * Processes declarative operator factories from the preset configuration.
9
+ * Satellite plugins (PostGIS filter, search, pg_trgm, etc.) declare their
10
+ * operators via `connectionFilterOperatorFactories` in their preset's schema
11
+ * options. This plugin processes all factories during its own init hook,
12
+ * populating the filter registry used at schema build time.
13
+ *
14
+ * This declarative approach replaces the previous imperative
15
+ * `build.addConnectionFilterOperator()` API, eliminating timing/ordering
16
+ * dependencies between plugins.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * // In a satellite plugin's preset:
21
+ * const MyPreset = {
22
+ * schema: {
23
+ * connectionFilterOperatorFactories: [
24
+ * (build) => [{
25
+ * typeNames: 'String',
26
+ * operatorName: 'myOp',
27
+ * spec: {
28
+ * description: 'My operator',
29
+ * resolve: (i, v) => build.sql`${i} OP ${v}`,
30
+ * },
31
+ * }],
32
+ * ],
33
+ * },
34
+ * };
35
+ * ```
36
+ */
37
+ export const ConnectionFilterCustomOperatorsPlugin = {
38
+ name: 'ConnectionFilterCustomOperatorsPlugin',
39
+ version,
40
+ description: 'Processes declarative operator factories for custom filter operator registration',
41
+ schema: {
42
+ hooks: {
43
+ build(build) {
44
+ // Initialize the filter registry
45
+ build[$$filters] = new Map();
46
+ return build;
47
+ },
48
+ init(_, build) {
49
+ const { inflection } = build;
50
+ const factories = build.options.connectionFilterOperatorFactories;
51
+ if (!factories || !Array.isArray(factories) || factories.length === 0) {
52
+ return _;
53
+ }
54
+ // Process each factory: call it with build, register all returned operators
55
+ for (const factory of factories) {
56
+ const registrations = factory(build);
57
+ if (!registrations || !Array.isArray(registrations)) {
58
+ continue;
59
+ }
60
+ for (const registration of registrations) {
61
+ const { typeNames: typeNameOrNames, operatorName, spec } = registration;
62
+ const typeNames = Array.isArray(typeNameOrNames)
63
+ ? typeNameOrNames
64
+ : [typeNameOrNames];
65
+ for (const typeName of typeNames) {
66
+ const filterTypeName = inflection.filterFieldType(typeName);
67
+ let operatorSpecByFilterName = build[$$filters].get(filterTypeName);
68
+ if (!operatorSpecByFilterName) {
69
+ operatorSpecByFilterName = new Map();
70
+ build[$$filters].set(filterTypeName, operatorSpecByFilterName);
71
+ }
72
+ if (operatorSpecByFilterName.has(operatorName)) {
73
+ throw new Error(`Filter '${operatorName}' already registered on '${filterTypeName}'`);
74
+ }
75
+ operatorSpecByFilterName.set(operatorName, spec);
76
+ }
77
+ }
78
+ }
79
+ return _;
80
+ },
81
+ /**
82
+ * Applies custom operators to their respective filter types.
83
+ * When a type like "StringFilter" has custom operators registered,
84
+ * they are added as fields with apply functions.
85
+ */
86
+ GraphQLInputObjectType_fields(inFields, build, context) {
87
+ let fields = inFields;
88
+ const { scope: { pgConnectionFilterOperators }, Self, fieldWithHooks, } = context;
89
+ if (!pgConnectionFilterOperators) {
90
+ return fields;
91
+ }
92
+ const operatorSpecByFilterName = build[$$filters].get(Self.name);
93
+ if (!operatorSpecByFilterName) {
94
+ return fields;
95
+ }
96
+ const { inputTypeName } = pgConnectionFilterOperators;
97
+ const fieldInputType = build.getTypeByName(inputTypeName);
98
+ if (!fieldInputType) {
99
+ return fields;
100
+ }
101
+ for (const [filterName, spec] of operatorSpecByFilterName.entries()) {
102
+ const { description, resolveInputCodec, resolveType } = spec;
103
+ const firstCodec = pgConnectionFilterOperators.pgCodecs[0];
104
+ const inputCodec = resolveInputCodec
105
+ ? resolveInputCodec(firstCodec)
106
+ : firstCodec;
107
+ const codecGraphQLType = build.getGraphQLTypeByPgCodec(inputCodec, 'input');
108
+ if (!codecGraphQLType) {
109
+ continue;
110
+ }
111
+ const type = resolveType
112
+ ? resolveType(codecGraphQLType)
113
+ : codecGraphQLType;
114
+ fields = build.extend(fields, {
115
+ [filterName]: fieldWithHooks({
116
+ fieldName: filterName,
117
+ isPgConnectionFilterOperator: true,
118
+ }, {
119
+ description,
120
+ type,
121
+ apply: makeApplyFromOperatorSpec(build, Self.name, filterName, spec, type),
122
+ }),
123
+ }, '');
124
+ }
125
+ return fields;
126
+ },
127
+ },
128
+ },
129
+ };
@@ -0,0 +1,28 @@
1
+ import '../augmentations';
2
+ import type { GraphileConfig } from 'graphile-config';
3
+ /**
4
+ * ConnectionFilterForwardRelationsPlugin
5
+ *
6
+ * Adds forward relation filter fields to table filter types.
7
+ * A "forward" relation is one where the current table has a FK referencing another table.
8
+ *
9
+ * For example, if `orders` has `client_id` referencing `clients`,
10
+ * then `OrderFilter` gets a `clientByClientId` field of type `ClientFilter`,
11
+ * allowing queries like:
12
+ *
13
+ * ```graphql
14
+ * allOrders(filter: {
15
+ * clientByClientId: { name: { startsWith: "Acme" } }
16
+ * }) { ... }
17
+ * ```
18
+ *
19
+ * The SQL generated is an EXISTS subquery:
20
+ * ```sql
21
+ * WHERE EXISTS (
22
+ * SELECT 1 FROM clients
23
+ * WHERE clients.id = orders.client_id
24
+ * AND <nested filter conditions>
25
+ * )
26
+ * ```
27
+ */
28
+ export declare const ConnectionFilterForwardRelationsPlugin: GraphileConfig.Plugin;
@@ -0,0 +1,168 @@
1
+ import '../augmentations';
2
+ import { makeAssertAllowed } from '../utils';
3
+ const version = '1.0.0';
4
+ /**
5
+ * ConnectionFilterForwardRelationsPlugin
6
+ *
7
+ * Adds forward relation filter fields to table filter types.
8
+ * A "forward" relation is one where the current table has a FK referencing another table.
9
+ *
10
+ * For example, if `orders` has `client_id` referencing `clients`,
11
+ * then `OrderFilter` gets a `clientByClientId` field of type `ClientFilter`,
12
+ * allowing queries like:
13
+ *
14
+ * ```graphql
15
+ * allOrders(filter: {
16
+ * clientByClientId: { name: { startsWith: "Acme" } }
17
+ * }) { ... }
18
+ * ```
19
+ *
20
+ * The SQL generated is an EXISTS subquery:
21
+ * ```sql
22
+ * WHERE EXISTS (
23
+ * SELECT 1 FROM clients
24
+ * WHERE clients.id = orders.client_id
25
+ * AND <nested filter conditions>
26
+ * )
27
+ * ```
28
+ */
29
+ export const ConnectionFilterForwardRelationsPlugin = {
30
+ name: 'ConnectionFilterForwardRelationsPlugin',
31
+ version,
32
+ description: 'Adds forward relation filter fields to connection filter types',
33
+ inflection: {
34
+ add: {
35
+ filterForwardRelationExistsFieldName(_preset, relationFieldName) {
36
+ return `${relationFieldName}Exists`;
37
+ },
38
+ filterSingleRelationFieldName(_preset, fieldName) {
39
+ return fieldName;
40
+ },
41
+ },
42
+ },
43
+ schema: {
44
+ entityBehavior: {
45
+ pgCodecRelation: 'filterBy',
46
+ },
47
+ hooks: {
48
+ GraphQLInputObjectType_fields(inFields, build, context) {
49
+ let fields = inFields;
50
+ // Runtime check: only proceed if relation filters are enabled
51
+ if (!build.options.connectionFilterRelations) {
52
+ return fields;
53
+ }
54
+ const { extend, inflection, sql, graphql: { GraphQLBoolean }, EXPORTABLE, } = build;
55
+ const { fieldWithHooks, scope: { pgCodec, isPgConnectionFilter }, } = context;
56
+ if (!isPgConnectionFilter || !pgCodec || !pgCodec.attributes) {
57
+ return fields;
58
+ }
59
+ const assertAllowed = makeAssertAllowed(build);
60
+ const source = Object.values(build.input.pgRegistry.pgResources).find((s) => s.codec === pgCodec && !s.parameters);
61
+ if (!source)
62
+ return fields;
63
+ const relations = source.getRelations();
64
+ const forwardRelations = Object.entries(relations).filter(([_relationName, relation]) => !relation.isReferencee);
65
+ const requireIndex = build.options.connectionFilterRelationsRequireIndex !== false;
66
+ for (const [relationName, relation] of forwardRelations) {
67
+ const foreignTable = relation.remoteResource;
68
+ if (!build.behavior.pgCodecRelationMatches(relation, 'filterBy')) {
69
+ continue;
70
+ }
71
+ // Skip function-based sources
72
+ if (typeof foreignTable.from === 'function') {
73
+ continue;
74
+ }
75
+ // Skip unindexed relations when requireIndex is enabled.
76
+ // PgIndexBehaviorsPlugin sets relation.extensions.isIndexed = false
77
+ // on backward relations without supporting indexes. For forward
78
+ // relations this is less common (the referenced PK is always indexed)
79
+ // but we check it for consistency.
80
+ if (requireIndex && relation.extensions?.isIndexed === false) {
81
+ continue;
82
+ }
83
+ const fieldName = inflection.singleRelation({
84
+ registry: source.registry,
85
+ codec: source.codec,
86
+ relationName,
87
+ });
88
+ const filterFieldName = inflection.filterSingleRelationFieldName(fieldName);
89
+ const foreignTableTypeName = inflection.tableType(foreignTable.codec);
90
+ const foreignTableFilterTypeName = inflection.filterType(foreignTableTypeName);
91
+ const ForeignTableFilterType = build.getTypeByName(foreignTableFilterTypeName);
92
+ if (!ForeignTableFilterType)
93
+ continue;
94
+ const foreignTableExpression = foreignTable.from;
95
+ const localAttributes = relation.localAttributes;
96
+ const remoteAttributes = relation.remoteAttributes;
97
+ // Add the relation filter field (e.g. clientByClientId: ClientFilter)
98
+ fields = extend(fields, {
99
+ [filterFieldName]: fieldWithHooks({
100
+ fieldName: filterFieldName,
101
+ isPgConnectionFilterField: true,
102
+ }, () => ({
103
+ description: `Filter by the object\u2019s \`${fieldName}\` relation.`,
104
+ type: ForeignTableFilterType,
105
+ apply: EXPORTABLE((assertAllowed, foreignTable, foreignTableExpression, localAttributes, remoteAttributes, sql) => function ($where, value) {
106
+ assertAllowed(value, 'object');
107
+ if (value == null)
108
+ return;
109
+ const $subQuery = $where.existsPlan({
110
+ tableExpression: foreignTableExpression,
111
+ alias: foreignTable.name,
112
+ });
113
+ localAttributes.forEach((localAttribute, i) => {
114
+ const remoteAttribute = remoteAttributes[i];
115
+ $subQuery.where(sql `${$where.alias}.${sql.identifier(localAttribute)} = ${$subQuery.alias}.${sql.identifier(remoteAttribute)}`);
116
+ });
117
+ return $subQuery;
118
+ }, [
119
+ assertAllowed,
120
+ foreignTable,
121
+ foreignTableExpression,
122
+ localAttributes,
123
+ remoteAttributes,
124
+ sql,
125
+ ]),
126
+ })),
127
+ }, `Adding connection filter forward relation field from ${source.name} to ${foreignTable.name}`);
128
+ // Add an "exists" field for nullable FKs (e.g. clientByClientIdExists: Boolean)
129
+ const keyIsNullable = relation.localAttributes.some((col) => !source.codec.attributes[col]?.notNull);
130
+ if (keyIsNullable) {
131
+ const existsFieldName = inflection.filterForwardRelationExistsFieldName(fieldName);
132
+ fields = extend(fields, {
133
+ [existsFieldName]: fieldWithHooks({
134
+ fieldName: existsFieldName,
135
+ isPgConnectionFilterField: true,
136
+ }, () => ({
137
+ description: `A related \`${fieldName}\` exists.`,
138
+ type: GraphQLBoolean,
139
+ apply: EXPORTABLE((assertAllowed, foreignTable, foreignTableExpression, localAttributes, remoteAttributes, sql) => function ($where, value) {
140
+ assertAllowed(value, 'scalar');
141
+ if (value == null)
142
+ return;
143
+ const $subQuery = $where.existsPlan({
144
+ tableExpression: foreignTableExpression,
145
+ alias: foreignTable.name,
146
+ equals: value,
147
+ });
148
+ localAttributes.forEach((localAttribute, i) => {
149
+ const remoteAttribute = remoteAttributes[i];
150
+ $subQuery.where(sql `${$where.alias}.${sql.identifier(localAttribute)} = ${$subQuery.alias}.${sql.identifier(remoteAttribute)}`);
151
+ });
152
+ }, [
153
+ assertAllowed,
154
+ foreignTable,
155
+ foreignTableExpression,
156
+ localAttributes,
157
+ remoteAttributes,
158
+ sql,
159
+ ]),
160
+ })),
161
+ }, `Adding connection filter forward relation exists field for ${fieldName}`);
162
+ }
163
+ }
164
+ return fields;
165
+ },
166
+ },
167
+ },
168
+ };
@@ -0,0 +1,11 @@
1
+ import '../augmentations';
2
+ import type { GraphileConfig } from 'graphile-config';
3
+ /**
4
+ * ConnectionFilterInflectionPlugin
5
+ *
6
+ * Adds inflection methods for naming filter types:
7
+ * - filterType(typeName) -> e.g. "UserFilter"
8
+ * - filterFieldType(typeName) -> e.g. "StringFilter"
9
+ * - filterFieldListType(typeName) -> e.g. "StringListFilter"
10
+ */
11
+ export declare const ConnectionFilterInflectionPlugin: GraphileConfig.Plugin;
@@ -0,0 +1,27 @@
1
+ import '../augmentations';
2
+ /**
3
+ * ConnectionFilterInflectionPlugin
4
+ *
5
+ * Adds inflection methods for naming filter types:
6
+ * - filterType(typeName) -> e.g. "UserFilter"
7
+ * - filterFieldType(typeName) -> e.g. "StringFilter"
8
+ * - filterFieldListType(typeName) -> e.g. "StringListFilter"
9
+ */
10
+ export const ConnectionFilterInflectionPlugin = {
11
+ name: 'ConnectionFilterInflectionPlugin',
12
+ version: '1.0.0',
13
+ description: 'Adds inflection methods for connection filter type naming',
14
+ inflection: {
15
+ add: {
16
+ filterType(_preset, typeName) {
17
+ return `${typeName}Filter`;
18
+ },
19
+ filterFieldType(_preset, typeName) {
20
+ return `${typeName}Filter`;
21
+ },
22
+ filterFieldListType(_preset, typeName) {
23
+ return `${typeName}ListFilter`;
24
+ },
25
+ },
26
+ },
27
+ };
@@ -0,0 +1,15 @@
1
+ import '../augmentations';
2
+ import type { GraphileConfig } from 'graphile-config';
3
+ /**
4
+ * ConnectionFilterLogicalOperatorsPlugin
5
+ *
6
+ * Adds the `and`, `or`, and `not` logical operators to filter types.
7
+ *
8
+ * - `and`: [Filter!] - all conditions must match (uses $where.andPlan())
9
+ * - `or`: [Filter!] - any condition must match (uses $where.orPlan())
10
+ * - `not`: Filter - negates the condition (uses $where.notPlan())
11
+ *
12
+ * These are only added if the filter type has at least one other field
13
+ * (to avoid creating useless logical-only filter types).
14
+ */
15
+ export declare const ConnectionFilterLogicalOperatorsPlugin: GraphileConfig.Plugin;
@@ -0,0 +1,86 @@
1
+ import '../augmentations';
2
+ import { makeAssertAllowed } from '../utils';
3
+ const version = '1.0.0';
4
+ /**
5
+ * ConnectionFilterLogicalOperatorsPlugin
6
+ *
7
+ * Adds the `and`, `or`, and `not` logical operators to filter types.
8
+ *
9
+ * - `and`: [Filter!] - all conditions must match (uses $where.andPlan())
10
+ * - `or`: [Filter!] - any condition must match (uses $where.orPlan())
11
+ * - `not`: Filter - negates the condition (uses $where.notPlan())
12
+ *
13
+ * These are only added if the filter type has at least one other field
14
+ * (to avoid creating useless logical-only filter types).
15
+ */
16
+ export const ConnectionFilterLogicalOperatorsPlugin = {
17
+ name: 'ConnectionFilterLogicalOperatorsPlugin',
18
+ version,
19
+ description: 'Adds and/or/not logical operators to connection filter types',
20
+ schema: {
21
+ hooks: {
22
+ GraphQLInputObjectType_fields(fields, build, context) {
23
+ const { extend, graphql: { GraphQLList, GraphQLNonNull }, EXPORTABLE, } = build;
24
+ const { fieldWithHooks, scope: { isPgConnectionFilter }, Self, } = context;
25
+ if (!isPgConnectionFilter)
26
+ return fields;
27
+ // Check runtime option — allows toggling without removing the plugin
28
+ if (build.options.connectionFilterLogicalOperators === false) {
29
+ return fields;
30
+ }
31
+ // Don't add logical operators if there are no other fields
32
+ if (Object.keys(fields).length === 0) {
33
+ return fields;
34
+ }
35
+ const assertAllowed = makeAssertAllowed(build);
36
+ const logicalOperatorFields = {
37
+ and: fieldWithHooks({
38
+ fieldName: 'and',
39
+ isPgConnectionFilterOperatorLogical: true,
40
+ }, {
41
+ description: 'Checks for all expressions in this list.',
42
+ type: new GraphQLList(new GraphQLNonNull(Self)),
43
+ apply: EXPORTABLE((assertAllowed) => function ($where, value) {
44
+ assertAllowed(value, 'list');
45
+ if (value == null)
46
+ return;
47
+ const $and = $where.andPlan();
48
+ return $and;
49
+ }, [assertAllowed]),
50
+ }),
51
+ or: fieldWithHooks({
52
+ fieldName: 'or',
53
+ isPgConnectionFilterOperatorLogical: true,
54
+ }, {
55
+ description: 'Checks for any expressions in this list.',
56
+ type: new GraphQLList(new GraphQLNonNull(Self)),
57
+ apply: EXPORTABLE((assertAllowed) => function ($where, value) {
58
+ assertAllowed(value, 'list');
59
+ if (value == null)
60
+ return;
61
+ const $or = $where.orPlan();
62
+ // Each entry in the OR list should use AND internally
63
+ return () => $or.andPlan();
64
+ }, [assertAllowed]),
65
+ }),
66
+ not: fieldWithHooks({
67
+ fieldName: 'not',
68
+ isPgConnectionFilterOperatorLogical: true,
69
+ }, {
70
+ description: 'Negates the expression.',
71
+ type: Self,
72
+ apply: EXPORTABLE((assertAllowed) => function ($where, value) {
73
+ assertAllowed(value, 'object');
74
+ if (value == null)
75
+ return;
76
+ const $not = $where.notPlan();
77
+ const $and = $not.andPlan();
78
+ return $and;
79
+ }, [assertAllowed]),
80
+ }),
81
+ };
82
+ return extend(fields, logicalOperatorFields, '');
83
+ },
84
+ },
85
+ },
86
+ };
@@ -0,0 +1,21 @@
1
+ import '../augmentations';
2
+ import type { GraphileConfig } from 'graphile-config';
3
+ /**
4
+ * ConnectionFilterOperatorsPlugin
5
+ *
6
+ * Registers all built-in filter operators on the per-scalar operator types
7
+ * (e.g. StringFilter, IntFilter, DatetimeFilter, etc.).
8
+ *
9
+ * Operator categories:
10
+ * - Standard: isNull, equalTo, notEqualTo, distinctFrom, notDistinctFrom, in, notIn
11
+ * - Sort: lessThan, lessThanOrEqualTo, greaterThan, greaterThanOrEqualTo
12
+ * - Pattern matching (text-like): includes, startsWith, endsWith, like + insensitive variants
13
+ * - Hstore: contains, containsKey, containsAllKeys, containsAnyKeys, containedBy
14
+ * - JSONB: contains, containsKey, containsAllKeys, containsAnyKeys, containedBy
15
+ * - Inet: contains, containsOrEqualTo, containedBy, containedByOrEqualTo, containsOrContainedBy
16
+ * - Array: contains, containedBy, overlaps, anyEqualTo, anyNotEqualTo, any comparison operators
17
+ * - Range: contains, containsElement, containedBy, overlaps, strictlyLeftOf, strictlyRightOf, etc.
18
+ * - Enum: standard + sort operators
19
+ * - Case-insensitive variants of standard and sort operators
20
+ */
21
+ export declare const ConnectionFilterOperatorsPlugin: GraphileConfig.Plugin;