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.
- package/LICENSE +23 -0
- package/README.md +107 -0
- package/augmentations.d.ts +104 -0
- package/augmentations.js +11 -0
- package/esm/augmentations.d.ts +104 -0
- package/esm/augmentations.js +9 -0
- package/esm/index.d.ts +55 -0
- package/esm/index.js +56 -0
- package/esm/plugins/ConnectionFilterArgPlugin.d.ts +13 -0
- package/esm/plugins/ConnectionFilterArgPlugin.js +96 -0
- package/esm/plugins/ConnectionFilterAttributesPlugin.d.ts +14 -0
- package/esm/plugins/ConnectionFilterAttributesPlugin.js +79 -0
- package/esm/plugins/ConnectionFilterBackwardRelationsPlugin.d.ts +33 -0
- package/esm/plugins/ConnectionFilterBackwardRelationsPlugin.js +398 -0
- package/esm/plugins/ConnectionFilterComputedAttributesPlugin.d.ts +19 -0
- package/esm/plugins/ConnectionFilterComputedAttributesPlugin.js +133 -0
- package/esm/plugins/ConnectionFilterCustomOperatorsPlugin.d.ts +35 -0
- package/esm/plugins/ConnectionFilterCustomOperatorsPlugin.js +129 -0
- package/esm/plugins/ConnectionFilterForwardRelationsPlugin.d.ts +28 -0
- package/esm/plugins/ConnectionFilterForwardRelationsPlugin.js +168 -0
- package/esm/plugins/ConnectionFilterInflectionPlugin.d.ts +11 -0
- package/esm/plugins/ConnectionFilterInflectionPlugin.js +27 -0
- package/esm/plugins/ConnectionFilterLogicalOperatorsPlugin.d.ts +15 -0
- package/esm/plugins/ConnectionFilterLogicalOperatorsPlugin.js +86 -0
- package/esm/plugins/ConnectionFilterOperatorsPlugin.d.ts +21 -0
- package/esm/plugins/ConnectionFilterOperatorsPlugin.js +677 -0
- package/esm/plugins/ConnectionFilterTypesPlugin.d.ts +12 -0
- package/esm/plugins/ConnectionFilterTypesPlugin.js +225 -0
- package/esm/plugins/index.d.ts +11 -0
- package/esm/plugins/index.js +11 -0
- package/esm/plugins/operatorApply.d.ts +11 -0
- package/esm/plugins/operatorApply.js +70 -0
- package/esm/preset.d.ts +35 -0
- package/esm/preset.js +72 -0
- package/esm/types.d.ts +146 -0
- package/esm/types.js +4 -0
- package/esm/utils.d.ts +44 -0
- package/esm/utils.js +112 -0
- package/index.d.ts +55 -0
- package/index.js +77 -0
- package/package.json +58 -0
- package/plugins/ConnectionFilterArgPlugin.d.ts +13 -0
- package/plugins/ConnectionFilterArgPlugin.js +99 -0
- package/plugins/ConnectionFilterAttributesPlugin.d.ts +14 -0
- package/plugins/ConnectionFilterAttributesPlugin.js +82 -0
- package/plugins/ConnectionFilterBackwardRelationsPlugin.d.ts +33 -0
- package/plugins/ConnectionFilterBackwardRelationsPlugin.js +401 -0
- package/plugins/ConnectionFilterComputedAttributesPlugin.d.ts +19 -0
- package/plugins/ConnectionFilterComputedAttributesPlugin.js +136 -0
- package/plugins/ConnectionFilterCustomOperatorsPlugin.d.ts +35 -0
- package/plugins/ConnectionFilterCustomOperatorsPlugin.js +132 -0
- package/plugins/ConnectionFilterForwardRelationsPlugin.d.ts +28 -0
- package/plugins/ConnectionFilterForwardRelationsPlugin.js +171 -0
- package/plugins/ConnectionFilterInflectionPlugin.d.ts +11 -0
- package/plugins/ConnectionFilterInflectionPlugin.js +30 -0
- package/plugins/ConnectionFilterLogicalOperatorsPlugin.d.ts +15 -0
- package/plugins/ConnectionFilterLogicalOperatorsPlugin.js +89 -0
- package/plugins/ConnectionFilterOperatorsPlugin.d.ts +21 -0
- package/plugins/ConnectionFilterOperatorsPlugin.js +680 -0
- package/plugins/ConnectionFilterTypesPlugin.d.ts +12 -0
- package/plugins/ConnectionFilterTypesPlugin.js +228 -0
- package/plugins/index.d.ts +11 -0
- package/plugins/index.js +25 -0
- package/plugins/operatorApply.d.ts +11 -0
- package/plugins/operatorApply.js +73 -0
- package/preset.d.ts +35 -0
- package/preset.js +75 -0
- package/types.d.ts +146 -0
- package/types.js +7 -0
- package/utils.d.ts +44 -0
- package/utils.js +119 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Dan Lynch <pyramation@gmail.com>
|
|
4
|
+
Copyright (c) 2025 Constructive <developers@constructive.io>
|
|
5
|
+
Copyright (c) 2020-present, Interweb, Inc.
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# graphile-connection-filter
|
|
2
|
+
|
|
3
|
+
A PostGraphile v5 native connection filter plugin for the Constructive monorepo.
|
|
4
|
+
|
|
5
|
+
Adds advanced filtering capabilities to connection and list fields, including:
|
|
6
|
+
|
|
7
|
+
- Per-table filter types (e.g. `UserFilter`)
|
|
8
|
+
- Per-scalar operator types (e.g. `StringFilter`, `IntFilter`)
|
|
9
|
+
- Standard operators: `equalTo`, `notEqualTo`, `isNull`, `in`, `notIn`, etc.
|
|
10
|
+
- Sort operators: `lessThan`, `greaterThan`, etc.
|
|
11
|
+
- Pattern matching: `includes`, `startsWith`, `endsWith`, `like` + case-insensitive variants
|
|
12
|
+
- Type-specific operators: JSONB, hstore, inet, array, range
|
|
13
|
+
- Logical operators: `and`, `or`, `not`
|
|
14
|
+
- Declarative custom operator API: `connectionFilterOperatorFactories` for satellite plugins
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { ConnectionFilterPreset } from 'graphile-connection-filter';
|
|
20
|
+
|
|
21
|
+
const preset: GraphileConfig.Preset = {
|
|
22
|
+
extends: [
|
|
23
|
+
ConnectionFilterPreset(),
|
|
24
|
+
],
|
|
25
|
+
};
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Custom Operators
|
|
29
|
+
|
|
30
|
+
Satellite plugins declare custom operators via `connectionFilterOperatorFactories` in their preset's schema options. Each factory is a function that receives the `build` object and returns an array of operator registrations:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
import type { ConnectionFilterOperatorFactory } from 'graphile-connection-filter';
|
|
34
|
+
|
|
35
|
+
const myOperatorFactory: ConnectionFilterOperatorFactory = (build) => [{
|
|
36
|
+
typeNames: 'MyType',
|
|
37
|
+
operatorName: 'myOperator',
|
|
38
|
+
spec: {
|
|
39
|
+
description: 'My custom operator',
|
|
40
|
+
resolve: (sqlIdentifier, sqlValue) => build.sql`${sqlIdentifier} OP ${sqlValue}`,
|
|
41
|
+
},
|
|
42
|
+
}];
|
|
43
|
+
|
|
44
|
+
const MyPreset: GraphileConfig.Preset = {
|
|
45
|
+
schema: {
|
|
46
|
+
connectionFilterOperatorFactories: [myOperatorFactory],
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Education and Tutorials
|
|
54
|
+
|
|
55
|
+
1. 🚀 [Quickstart: Getting Up and Running](https://constructive.io/learn/quickstart)
|
|
56
|
+
Get started with modular databases in minutes. Install prerequisites and deploy your first module.
|
|
57
|
+
|
|
58
|
+
2. 📦 [Modular PostgreSQL Development with Database Packages](https://constructive.io/learn/modular-postgres)
|
|
59
|
+
Learn to organize PostgreSQL projects with pgpm workspaces and reusable database modules.
|
|
60
|
+
|
|
61
|
+
3. ✏️ [Authoring Database Changes](https://constructive.io/learn/authoring-database-changes)
|
|
62
|
+
Master the workflow for adding, organizing, and managing database changes with pgpm.
|
|
63
|
+
|
|
64
|
+
4. 🧪 [End-to-End PostgreSQL Testing with TypeScript](https://constructive.io/learn/e2e-postgres-testing)
|
|
65
|
+
Master end-to-end PostgreSQL testing with ephemeral databases, RLS testing, and CI/CD automation.
|
|
66
|
+
|
|
67
|
+
5. ⚡ [Supabase Testing](https://constructive.io/learn/supabase)
|
|
68
|
+
Use TypeScript-first tools to test Supabase projects with realistic RLS, policies, and auth contexts.
|
|
69
|
+
|
|
70
|
+
6. 💧 [Drizzle ORM Testing](https://constructive.io/learn/drizzle-testing)
|
|
71
|
+
Run full-stack tests with Drizzle ORM, including database setup, teardown, and RLS enforcement.
|
|
72
|
+
|
|
73
|
+
7. 🔧 [Troubleshooting](https://constructive.io/learn/troubleshooting)
|
|
74
|
+
Common issues and solutions for pgpm, PostgreSQL, and testing.
|
|
75
|
+
|
|
76
|
+
## Related Constructive Tooling
|
|
77
|
+
|
|
78
|
+
### 📦 Package Management
|
|
79
|
+
|
|
80
|
+
* [pgpm](https://github.com/constructive-io/constructive/tree/main/pgpm/pgpm): **🖥️ PostgreSQL Package Manager** for modular Postgres development. Works with database workspaces, scaffolding, migrations, seeding, and installing database packages.
|
|
81
|
+
|
|
82
|
+
### 🧪 Testing
|
|
83
|
+
|
|
84
|
+
* [pgsql-test](https://github.com/constructive-io/constructive/tree/main/postgres/pgsql-test): **📊 Isolated testing environments** with per-test transaction rollbacks—ideal for integration tests, complex migrations, and RLS simulation.
|
|
85
|
+
* [pgsql-seed](https://github.com/constructive-io/constructive/tree/main/postgres/pgsql-seed): **🌱 PostgreSQL seeding utilities** for CSV, JSON, SQL data loading, and pgpm deployment.
|
|
86
|
+
* [supabase-test](https://github.com/constructive-io/constructive/tree/main/postgres/supabase-test): **🧪 Supabase-native test harness** preconfigured for the local Supabase stack—per-test rollbacks, JWT/role context helpers, and CI/GitHub Actions ready.
|
|
87
|
+
* [graphile-test](https://github.com/constructive-io/constructive/tree/main/graphile/graphile-test): **🔐 Authentication mocking** for Graphile-focused test helpers and emulating row-level security contexts.
|
|
88
|
+
* [pg-query-context](https://github.com/constructive-io/constructive/tree/main/postgres/pg-query-context): **🔒 Session context injection** to add session-local context (e.g., `SET LOCAL`) into queries—ideal for setting `role`, `jwt.claims`, and other session settings.
|
|
89
|
+
|
|
90
|
+
### 🧠 Parsing & AST
|
|
91
|
+
|
|
92
|
+
* [pgsql-parser](https://www.npmjs.com/package/pgsql-parser): **🔄 SQL conversion engine** that interprets and converts PostgreSQL syntax.
|
|
93
|
+
* [libpg-query-node](https://www.npmjs.com/package/libpg-query): **🌉 Node.js bindings** for `libpg_query`, converting SQL into parse trees.
|
|
94
|
+
* [pg-proto-parser](https://www.npmjs.com/package/pg-proto-parser): **📦 Protobuf parser** for parsing PostgreSQL Protocol Buffers definitions to generate TypeScript interfaces, utility functions, and JSON mappings for enums.
|
|
95
|
+
* [@pgsql/enums](https://www.npmjs.com/package/@pgsql/enums): **🏷️ TypeScript enums** for PostgreSQL AST for safe and ergonomic parsing logic.
|
|
96
|
+
* [@pgsql/types](https://www.npmjs.com/package/@pgsql/types): **📝 Type definitions** for PostgreSQL AST nodes in TypeScript.
|
|
97
|
+
* [@pgsql/utils](https://www.npmjs.com/package/@pgsql/utils): **🛠️ AST utilities** for constructing and transforming PostgreSQL syntax trees.
|
|
98
|
+
|
|
99
|
+
## Credits
|
|
100
|
+
|
|
101
|
+
**🛠 Built by the [Constructive](https://constructive.io) team — creators of modular Postgres tooling for secure, composable backends. If you like our work, contribute on [GitHub](https://github.com/constructive-io).**
|
|
102
|
+
|
|
103
|
+
## Disclaimer
|
|
104
|
+
|
|
105
|
+
AS DESCRIBED IN THE LICENSES, THE SOFTWARE IS PROVIDED "AS IS", AT YOUR OWN RISK, AND WITHOUT WARRANTIES OF ANY KIND.
|
|
106
|
+
|
|
107
|
+
No developer or entity involved in creating this software will be liable for any claims or damages whatsoever associated with your use, inability to use, or your interaction with other users of the code, including any direct, indirect, incidental, special, exemplary, punitive or consequential damages, or loss of profits, cryptocurrencies, tokens, or anything else of value.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript namespace augmentations for graphile-connection-filter.
|
|
3
|
+
*
|
|
4
|
+
* These extend the Graphile type system so that our custom inflection methods,
|
|
5
|
+
* build properties, scope properties, schema options, and behaviors are
|
|
6
|
+
* recognized by the TypeScript compiler.
|
|
7
|
+
*/
|
|
8
|
+
import 'graphile-build';
|
|
9
|
+
import 'graphile-build-pg';
|
|
10
|
+
import type { ConnectionFilterOperatorFactory, ConnectionFilterOperatorsDigest, PgConnectionFilterOperatorsScope } from './types';
|
|
11
|
+
declare global {
|
|
12
|
+
namespace GraphileBuild {
|
|
13
|
+
interface Inflection {
|
|
14
|
+
/** Filter type name for a table, e.g. "UserFilter" */
|
|
15
|
+
filterType(this: Inflection, typeName: string): string;
|
|
16
|
+
/** Filter field type name for a scalar, e.g. "StringFilter" */
|
|
17
|
+
filterFieldType(this: Inflection, typeName: string): string;
|
|
18
|
+
/** Filter field list type name for an array scalar, e.g. "StringListFilter" */
|
|
19
|
+
filterFieldListType(this: Inflection, typeName: string): string;
|
|
20
|
+
/** Forward relation field name (passthrough) */
|
|
21
|
+
filterSingleRelationFieldName(this: Inflection, fieldName: string): string;
|
|
22
|
+
/** Forward relation exists field name, e.g. "clientByClientIdExists" */
|
|
23
|
+
filterForwardRelationExistsFieldName(this: Inflection, relationFieldName: string): string;
|
|
24
|
+
/** Backward single relation field name (passthrough) */
|
|
25
|
+
filterSingleRelationByKeysBackwardsFieldName(this: Inflection, fieldName: string): string;
|
|
26
|
+
/** Backward single relation exists field name */
|
|
27
|
+
filterBackwardSingleRelationExistsFieldName(this: Inflection, relationFieldName: string): string;
|
|
28
|
+
/** Backward many relation field name (passthrough) */
|
|
29
|
+
filterManyRelationByKeysFieldName(this: Inflection, fieldName: string): string;
|
|
30
|
+
/** Backward many relation exists field name */
|
|
31
|
+
filterBackwardManyRelationExistsFieldName(this: Inflection, relationFieldName: string): string;
|
|
32
|
+
/** Many filter type name, e.g. "ClientToManyOrderFilter" */
|
|
33
|
+
filterManyType(this: Inflection, table: any, foreignTable: any): string;
|
|
34
|
+
}
|
|
35
|
+
interface Build {
|
|
36
|
+
/** Returns the operator digest for a given codec, or null if not filterable */
|
|
37
|
+
connectionFilterOperatorsDigest(codec: any): ConnectionFilterOperatorsDigest | null;
|
|
38
|
+
/** Escapes LIKE wildcard characters (% and _) */
|
|
39
|
+
escapeLikeWildcards(input: unknown): string;
|
|
40
|
+
/** Internal filter operator registry keyed by filter type name */
|
|
41
|
+
[key: symbol]: any;
|
|
42
|
+
}
|
|
43
|
+
interface ScopeInputObject {
|
|
44
|
+
/** True if this is a table-level connection filter type (e.g. UserFilter) */
|
|
45
|
+
isPgConnectionFilter?: boolean;
|
|
46
|
+
/** Operator type scope data (present on scalar filter types like StringFilter) */
|
|
47
|
+
pgConnectionFilterOperators?: PgConnectionFilterOperatorsScope;
|
|
48
|
+
/** Foreign table resource (used by many filter types) */
|
|
49
|
+
foreignTable?: any;
|
|
50
|
+
/** True if this is a many-relation filter type (e.g. ClientToManyOrderFilter) */
|
|
51
|
+
isPgConnectionFilterMany?: boolean;
|
|
52
|
+
}
|
|
53
|
+
interface ScopeInputObjectFieldsField {
|
|
54
|
+
/** True if this field is an attribute-based filter field */
|
|
55
|
+
isPgConnectionFilterField?: boolean;
|
|
56
|
+
/** True if this field is a filter operator (e.g. equalTo, lessThan) */
|
|
57
|
+
isPgConnectionFilterOperator?: boolean;
|
|
58
|
+
/** True if this field is a logical operator (and/or/not) */
|
|
59
|
+
isPgConnectionFilterOperatorLogical?: boolean;
|
|
60
|
+
/** True if this is a many-relation filter field */
|
|
61
|
+
isPgConnectionFilterManyField?: boolean;
|
|
62
|
+
}
|
|
63
|
+
interface BehaviorStrings {
|
|
64
|
+
filter: true;
|
|
65
|
+
filterProc: true;
|
|
66
|
+
'attribute:filterBy': true;
|
|
67
|
+
}
|
|
68
|
+
interface SchemaOptions {
|
|
69
|
+
connectionFilterArgumentName?: string;
|
|
70
|
+
connectionFilterArrays?: boolean;
|
|
71
|
+
connectionFilterLogicalOperators?: boolean;
|
|
72
|
+
connectionFilterAllowNullInput?: boolean;
|
|
73
|
+
connectionFilterAllowedFieldTypes?: string[];
|
|
74
|
+
connectionFilterAllowedOperators?: string[];
|
|
75
|
+
connectionFilterOperatorNames?: Record<string, string>;
|
|
76
|
+
connectionFilterSetofFunctions?: boolean;
|
|
77
|
+
connectionFilterComputedColumns?: boolean;
|
|
78
|
+
connectionFilterRelations?: boolean;
|
|
79
|
+
/** If true (default), relation filter fields are only added for FKs with supporting indexes.
|
|
80
|
+
* This prevents generating EXISTS subqueries that would cause sequential scans on large tables.
|
|
81
|
+
* Set to false to allow relation filters on all FKs regardless of index status. */
|
|
82
|
+
connectionFilterRelationsRequireIndex?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Declarative operator factories. Each factory receives the build object
|
|
85
|
+
* and returns operator registrations. Replaces addConnectionFilterOperator.
|
|
86
|
+
*/
|
|
87
|
+
connectionFilterOperatorFactories?: ConnectionFilterOperatorFactory[];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
namespace GraphileConfig {
|
|
91
|
+
interface Plugins {
|
|
92
|
+
ConnectionFilterInflectionPlugin: true;
|
|
93
|
+
ConnectionFilterTypesPlugin: true;
|
|
94
|
+
ConnectionFilterArgPlugin: true;
|
|
95
|
+
ConnectionFilterAttributesPlugin: true;
|
|
96
|
+
ConnectionFilterOperatorsPlugin: true;
|
|
97
|
+
ConnectionFilterCustomOperatorsPlugin: true;
|
|
98
|
+
ConnectionFilterLogicalOperatorsPlugin: true;
|
|
99
|
+
ConnectionFilterComputedAttributesPlugin: true;
|
|
100
|
+
ConnectionFilterForwardRelationsPlugin: true;
|
|
101
|
+
ConnectionFilterBackwardRelationsPlugin: true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
package/augmentations.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* TypeScript namespace augmentations for graphile-connection-filter.
|
|
4
|
+
*
|
|
5
|
+
* These extend the Graphile type system so that our custom inflection methods,
|
|
6
|
+
* build properties, scope properties, schema options, and behaviors are
|
|
7
|
+
* recognized by the TypeScript compiler.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
require("graphile-build");
|
|
11
|
+
require("graphile-build-pg");
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript namespace augmentations for graphile-connection-filter.
|
|
3
|
+
*
|
|
4
|
+
* These extend the Graphile type system so that our custom inflection methods,
|
|
5
|
+
* build properties, scope properties, schema options, and behaviors are
|
|
6
|
+
* recognized by the TypeScript compiler.
|
|
7
|
+
*/
|
|
8
|
+
import 'graphile-build';
|
|
9
|
+
import 'graphile-build-pg';
|
|
10
|
+
import type { ConnectionFilterOperatorFactory, ConnectionFilterOperatorsDigest, PgConnectionFilterOperatorsScope } from './types';
|
|
11
|
+
declare global {
|
|
12
|
+
namespace GraphileBuild {
|
|
13
|
+
interface Inflection {
|
|
14
|
+
/** Filter type name for a table, e.g. "UserFilter" */
|
|
15
|
+
filterType(this: Inflection, typeName: string): string;
|
|
16
|
+
/** Filter field type name for a scalar, e.g. "StringFilter" */
|
|
17
|
+
filterFieldType(this: Inflection, typeName: string): string;
|
|
18
|
+
/** Filter field list type name for an array scalar, e.g. "StringListFilter" */
|
|
19
|
+
filterFieldListType(this: Inflection, typeName: string): string;
|
|
20
|
+
/** Forward relation field name (passthrough) */
|
|
21
|
+
filterSingleRelationFieldName(this: Inflection, fieldName: string): string;
|
|
22
|
+
/** Forward relation exists field name, e.g. "clientByClientIdExists" */
|
|
23
|
+
filterForwardRelationExistsFieldName(this: Inflection, relationFieldName: string): string;
|
|
24
|
+
/** Backward single relation field name (passthrough) */
|
|
25
|
+
filterSingleRelationByKeysBackwardsFieldName(this: Inflection, fieldName: string): string;
|
|
26
|
+
/** Backward single relation exists field name */
|
|
27
|
+
filterBackwardSingleRelationExistsFieldName(this: Inflection, relationFieldName: string): string;
|
|
28
|
+
/** Backward many relation field name (passthrough) */
|
|
29
|
+
filterManyRelationByKeysFieldName(this: Inflection, fieldName: string): string;
|
|
30
|
+
/** Backward many relation exists field name */
|
|
31
|
+
filterBackwardManyRelationExistsFieldName(this: Inflection, relationFieldName: string): string;
|
|
32
|
+
/** Many filter type name, e.g. "ClientToManyOrderFilter" */
|
|
33
|
+
filterManyType(this: Inflection, table: any, foreignTable: any): string;
|
|
34
|
+
}
|
|
35
|
+
interface Build {
|
|
36
|
+
/** Returns the operator digest for a given codec, or null if not filterable */
|
|
37
|
+
connectionFilterOperatorsDigest(codec: any): ConnectionFilterOperatorsDigest | null;
|
|
38
|
+
/** Escapes LIKE wildcard characters (% and _) */
|
|
39
|
+
escapeLikeWildcards(input: unknown): string;
|
|
40
|
+
/** Internal filter operator registry keyed by filter type name */
|
|
41
|
+
[key: symbol]: any;
|
|
42
|
+
}
|
|
43
|
+
interface ScopeInputObject {
|
|
44
|
+
/** True if this is a table-level connection filter type (e.g. UserFilter) */
|
|
45
|
+
isPgConnectionFilter?: boolean;
|
|
46
|
+
/** Operator type scope data (present on scalar filter types like StringFilter) */
|
|
47
|
+
pgConnectionFilterOperators?: PgConnectionFilterOperatorsScope;
|
|
48
|
+
/** Foreign table resource (used by many filter types) */
|
|
49
|
+
foreignTable?: any;
|
|
50
|
+
/** True if this is a many-relation filter type (e.g. ClientToManyOrderFilter) */
|
|
51
|
+
isPgConnectionFilterMany?: boolean;
|
|
52
|
+
}
|
|
53
|
+
interface ScopeInputObjectFieldsField {
|
|
54
|
+
/** True if this field is an attribute-based filter field */
|
|
55
|
+
isPgConnectionFilterField?: boolean;
|
|
56
|
+
/** True if this field is a filter operator (e.g. equalTo, lessThan) */
|
|
57
|
+
isPgConnectionFilterOperator?: boolean;
|
|
58
|
+
/** True if this field is a logical operator (and/or/not) */
|
|
59
|
+
isPgConnectionFilterOperatorLogical?: boolean;
|
|
60
|
+
/** True if this is a many-relation filter field */
|
|
61
|
+
isPgConnectionFilterManyField?: boolean;
|
|
62
|
+
}
|
|
63
|
+
interface BehaviorStrings {
|
|
64
|
+
filter: true;
|
|
65
|
+
filterProc: true;
|
|
66
|
+
'attribute:filterBy': true;
|
|
67
|
+
}
|
|
68
|
+
interface SchemaOptions {
|
|
69
|
+
connectionFilterArgumentName?: string;
|
|
70
|
+
connectionFilterArrays?: boolean;
|
|
71
|
+
connectionFilterLogicalOperators?: boolean;
|
|
72
|
+
connectionFilterAllowNullInput?: boolean;
|
|
73
|
+
connectionFilterAllowedFieldTypes?: string[];
|
|
74
|
+
connectionFilterAllowedOperators?: string[];
|
|
75
|
+
connectionFilterOperatorNames?: Record<string, string>;
|
|
76
|
+
connectionFilterSetofFunctions?: boolean;
|
|
77
|
+
connectionFilterComputedColumns?: boolean;
|
|
78
|
+
connectionFilterRelations?: boolean;
|
|
79
|
+
/** If true (default), relation filter fields are only added for FKs with supporting indexes.
|
|
80
|
+
* This prevents generating EXISTS subqueries that would cause sequential scans on large tables.
|
|
81
|
+
* Set to false to allow relation filters on all FKs regardless of index status. */
|
|
82
|
+
connectionFilterRelationsRequireIndex?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Declarative operator factories. Each factory receives the build object
|
|
85
|
+
* and returns operator registrations. Replaces addConnectionFilterOperator.
|
|
86
|
+
*/
|
|
87
|
+
connectionFilterOperatorFactories?: ConnectionFilterOperatorFactory[];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
namespace GraphileConfig {
|
|
91
|
+
interface Plugins {
|
|
92
|
+
ConnectionFilterInflectionPlugin: true;
|
|
93
|
+
ConnectionFilterTypesPlugin: true;
|
|
94
|
+
ConnectionFilterArgPlugin: true;
|
|
95
|
+
ConnectionFilterAttributesPlugin: true;
|
|
96
|
+
ConnectionFilterOperatorsPlugin: true;
|
|
97
|
+
ConnectionFilterCustomOperatorsPlugin: true;
|
|
98
|
+
ConnectionFilterLogicalOperatorsPlugin: true;
|
|
99
|
+
ConnectionFilterComputedAttributesPlugin: true;
|
|
100
|
+
ConnectionFilterForwardRelationsPlugin: true;
|
|
101
|
+
ConnectionFilterBackwardRelationsPlugin: true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript namespace augmentations for graphile-connection-filter.
|
|
3
|
+
*
|
|
4
|
+
* These extend the Graphile type system so that our custom inflection methods,
|
|
5
|
+
* build properties, scope properties, schema options, and behaviors are
|
|
6
|
+
* recognized by the TypeScript compiler.
|
|
7
|
+
*/
|
|
8
|
+
import 'graphile-build';
|
|
9
|
+
import 'graphile-build-pg';
|
package/esm/index.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* graphile-connection-filter
|
|
3
|
+
*
|
|
4
|
+
* A PostGraphile v5 native connection filter plugin.
|
|
5
|
+
* Adds advanced filtering capabilities to connection and list fields.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { ConnectionFilterPreset } from 'graphile-connection-filter';
|
|
10
|
+
*
|
|
11
|
+
* const preset = {
|
|
12
|
+
* extends: [
|
|
13
|
+
* ConnectionFilterPreset(),
|
|
14
|
+
* ],
|
|
15
|
+
* };
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* For satellite plugins that need to register custom operators, declare them
|
|
19
|
+
* as factories in the preset's `connectionFilterOperatorFactories` option:
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import type { ConnectionFilterOperatorFactory } from 'graphile-connection-filter';
|
|
22
|
+
*
|
|
23
|
+
* const myOperatorFactory: ConnectionFilterOperatorFactory = (build) => [{
|
|
24
|
+
* typeNames: 'MyType',
|
|
25
|
+
* operatorName: 'myOperator',
|
|
26
|
+
* spec: {
|
|
27
|
+
* description: 'My custom operator',
|
|
28
|
+
* resolve: (sqlIdentifier, sqlValue) => build.sql`${sqlIdentifier} OP ${sqlValue}`,
|
|
29
|
+
* },
|
|
30
|
+
* }];
|
|
31
|
+
*
|
|
32
|
+
* export const MyPreset = {
|
|
33
|
+
* schema: {
|
|
34
|
+
* connectionFilterOperatorFactories: [myOperatorFactory],
|
|
35
|
+
* },
|
|
36
|
+
* plugins: [MyPlugin],
|
|
37
|
+
* };
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* For satellite plugins that need to access the query builder from a filter apply:
|
|
41
|
+
* ```typescript
|
|
42
|
+
* import { getQueryBuilder } from 'graphile-connection-filter';
|
|
43
|
+
* // In your filter field's apply callback:
|
|
44
|
+
* const qb = getQueryBuilder(build, $condition);
|
|
45
|
+
* if (qb) {
|
|
46
|
+
* const idx = qb.selectAndReturnIndex(sql`...`);
|
|
47
|
+
* qb.setMeta('key', { selectIndex: idx });
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export { ConnectionFilterPreset } from './preset';
|
|
52
|
+
export { ConnectionFilterInflectionPlugin, ConnectionFilterTypesPlugin, ConnectionFilterArgPlugin, ConnectionFilterAttributesPlugin, ConnectionFilterOperatorsPlugin, ConnectionFilterCustomOperatorsPlugin, ConnectionFilterLogicalOperatorsPlugin, ConnectionFilterComputedAttributesPlugin, ConnectionFilterForwardRelationsPlugin, ConnectionFilterBackwardRelationsPlugin, makeApplyFromOperatorSpec, } from './plugins';
|
|
53
|
+
export type { ConnectionFilterOperatorSpec, ConnectionFilterOperatorRegistration, ConnectionFilterOperatorFactory, ConnectionFilterOptions, ConnectionFilterOperatorsDigest, PgConnectionFilterOperatorsScope, } from './types';
|
|
54
|
+
export { $$filters } from './types';
|
|
55
|
+
export { isEmpty, makeAssertAllowed, getQueryBuilder, isComputedScalarAttributeResource, getComputedAttributeResources, } from './utils';
|
package/esm/index.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* graphile-connection-filter
|
|
3
|
+
*
|
|
4
|
+
* A PostGraphile v5 native connection filter plugin.
|
|
5
|
+
* Adds advanced filtering capabilities to connection and list fields.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { ConnectionFilterPreset } from 'graphile-connection-filter';
|
|
10
|
+
*
|
|
11
|
+
* const preset = {
|
|
12
|
+
* extends: [
|
|
13
|
+
* ConnectionFilterPreset(),
|
|
14
|
+
* ],
|
|
15
|
+
* };
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* For satellite plugins that need to register custom operators, declare them
|
|
19
|
+
* as factories in the preset's `connectionFilterOperatorFactories` option:
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import type { ConnectionFilterOperatorFactory } from 'graphile-connection-filter';
|
|
22
|
+
*
|
|
23
|
+
* const myOperatorFactory: ConnectionFilterOperatorFactory = (build) => [{
|
|
24
|
+
* typeNames: 'MyType',
|
|
25
|
+
* operatorName: 'myOperator',
|
|
26
|
+
* spec: {
|
|
27
|
+
* description: 'My custom operator',
|
|
28
|
+
* resolve: (sqlIdentifier, sqlValue) => build.sql`${sqlIdentifier} OP ${sqlValue}`,
|
|
29
|
+
* },
|
|
30
|
+
* }];
|
|
31
|
+
*
|
|
32
|
+
* export const MyPreset = {
|
|
33
|
+
* schema: {
|
|
34
|
+
* connectionFilterOperatorFactories: [myOperatorFactory],
|
|
35
|
+
* },
|
|
36
|
+
* plugins: [MyPlugin],
|
|
37
|
+
* };
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* For satellite plugins that need to access the query builder from a filter apply:
|
|
41
|
+
* ```typescript
|
|
42
|
+
* import { getQueryBuilder } from 'graphile-connection-filter';
|
|
43
|
+
* // In your filter field's apply callback:
|
|
44
|
+
* const qb = getQueryBuilder(build, $condition);
|
|
45
|
+
* if (qb) {
|
|
46
|
+
* const idx = qb.selectAndReturnIndex(sql`...`);
|
|
47
|
+
* qb.setMeta('key', { selectIndex: idx });
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export { ConnectionFilterPreset } from './preset';
|
|
52
|
+
// Re-export all plugins for granular use
|
|
53
|
+
export { ConnectionFilterInflectionPlugin, ConnectionFilterTypesPlugin, ConnectionFilterArgPlugin, ConnectionFilterAttributesPlugin, ConnectionFilterOperatorsPlugin, ConnectionFilterCustomOperatorsPlugin, ConnectionFilterLogicalOperatorsPlugin, ConnectionFilterComputedAttributesPlugin, ConnectionFilterForwardRelationsPlugin, ConnectionFilterBackwardRelationsPlugin, makeApplyFromOperatorSpec, } from './plugins';
|
|
54
|
+
export { $$filters } from './types';
|
|
55
|
+
// Re-export utilities
|
|
56
|
+
export { isEmpty, makeAssertAllowed, getQueryBuilder, isComputedScalarAttributeResource, getComputedAttributeResources, } from './utils';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import '../augmentations';
|
|
2
|
+
import type { GraphileConfig } from 'graphile-config';
|
|
3
|
+
/**
|
|
4
|
+
* ConnectionFilterArgPlugin
|
|
5
|
+
*
|
|
6
|
+
* Adds the filter argument (configurable name, default 'where') to connection
|
|
7
|
+
* and simple collection fields. Uses `applyPlan` to create a PgCondition that
|
|
8
|
+
* child filter fields can add WHERE clauses to.
|
|
9
|
+
*
|
|
10
|
+
* This runs before PgConnectionArgOrderByPlugin so that filters are applied
|
|
11
|
+
* before ordering (important for e.g. full-text search rank ordering).
|
|
12
|
+
*/
|
|
13
|
+
export declare const ConnectionFilterArgPlugin: GraphileConfig.Plugin;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import '../augmentations';
|
|
2
|
+
import { isEmpty } from '../utils';
|
|
3
|
+
const version = '1.0.0';
|
|
4
|
+
/**
|
|
5
|
+
* ConnectionFilterArgPlugin
|
|
6
|
+
*
|
|
7
|
+
* Adds the filter argument (configurable name, default 'where') to connection
|
|
8
|
+
* and simple collection fields. Uses `applyPlan` to create a PgCondition that
|
|
9
|
+
* child filter fields can add WHERE clauses to.
|
|
10
|
+
*
|
|
11
|
+
* This runs before PgConnectionArgOrderByPlugin so that filters are applied
|
|
12
|
+
* before ordering (important for e.g. full-text search rank ordering).
|
|
13
|
+
*/
|
|
14
|
+
export const ConnectionFilterArgPlugin = {
|
|
15
|
+
name: 'ConnectionFilterArgPlugin',
|
|
16
|
+
version,
|
|
17
|
+
description: 'Adds the filter argument to connection and list fields',
|
|
18
|
+
before: ['PgConnectionArgOrderByPlugin'],
|
|
19
|
+
schema: {
|
|
20
|
+
hooks: {
|
|
21
|
+
GraphQLObjectType_fields_field_args(args, build, context) {
|
|
22
|
+
const { extend, inflection, EXPORTABLE, dataplanPg: { PgCondition }, } = build;
|
|
23
|
+
const { scope: { isPgFieldConnection, isPgFieldSimpleCollection, pgFieldResource: resource, pgFieldCodec, fieldName, }, Self, } = context;
|
|
24
|
+
const shouldAddFilter = isPgFieldConnection || isPgFieldSimpleCollection;
|
|
25
|
+
if (!shouldAddFilter)
|
|
26
|
+
return args;
|
|
27
|
+
const codec = pgFieldCodec ?? resource?.codec;
|
|
28
|
+
if (!codec)
|
|
29
|
+
return args;
|
|
30
|
+
// Check behavior: procedures use "filterProc", tables use "filter"
|
|
31
|
+
const desiredBehavior = resource?.parameters
|
|
32
|
+
? 'filterProc'
|
|
33
|
+
: 'filter';
|
|
34
|
+
if (resource
|
|
35
|
+
? !build.behavior.pgResourceMatches(resource, desiredBehavior)
|
|
36
|
+
: !build.behavior.pgCodecMatches(codec, desiredBehavior)) {
|
|
37
|
+
return args;
|
|
38
|
+
}
|
|
39
|
+
const returnCodec = codec;
|
|
40
|
+
const nodeType = build.getGraphQLTypeByPgCodec(returnCodec, 'output');
|
|
41
|
+
if (!nodeType)
|
|
42
|
+
return args;
|
|
43
|
+
const nodeTypeName = nodeType.name;
|
|
44
|
+
const filterTypeName = inflection.filterType(nodeTypeName);
|
|
45
|
+
const FilterType = build.getTypeByName(filterTypeName);
|
|
46
|
+
if (!FilterType)
|
|
47
|
+
return args;
|
|
48
|
+
// For setof functions returning scalars, track the codec
|
|
49
|
+
const attributeCodec = resource?.parameters && !resource?.codec.attributes
|
|
50
|
+
? resource.codec
|
|
51
|
+
: null;
|
|
52
|
+
const argName = build.options.connectionFilterArgumentName || 'where';
|
|
53
|
+
return extend(args, {
|
|
54
|
+
[argName]: {
|
|
55
|
+
description: 'A filter to be used in determining which values should be returned by the collection.',
|
|
56
|
+
type: FilterType,
|
|
57
|
+
...(isPgFieldConnection
|
|
58
|
+
? {
|
|
59
|
+
applyPlan: EXPORTABLE((PgCondition, isEmpty, attributeCodec) => function (_, $connection, fieldArg) {
|
|
60
|
+
const $pgSelect = $connection.getSubplan();
|
|
61
|
+
fieldArg.apply($pgSelect, (queryBuilder, value) => {
|
|
62
|
+
// If filter is null/undefined or empty {}, treat as "no filter" — skip
|
|
63
|
+
if (value == null || isEmpty(value))
|
|
64
|
+
return;
|
|
65
|
+
const condition = new PgCondition(queryBuilder);
|
|
66
|
+
if (attributeCodec) {
|
|
67
|
+
condition.extensions.pgFilterAttribute = {
|
|
68
|
+
codec: attributeCodec,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return condition;
|
|
72
|
+
});
|
|
73
|
+
}, [PgCondition, isEmpty, attributeCodec]),
|
|
74
|
+
}
|
|
75
|
+
: {
|
|
76
|
+
applyPlan: EXPORTABLE((PgCondition, isEmpty, attributeCodec) => function (_, $pgSelect, fieldArg) {
|
|
77
|
+
fieldArg.apply($pgSelect, (queryBuilder, value) => {
|
|
78
|
+
// If filter is null/undefined or empty {}, treat as "no filter" — skip
|
|
79
|
+
if (value == null || isEmpty(value))
|
|
80
|
+
return;
|
|
81
|
+
const condition = new PgCondition(queryBuilder);
|
|
82
|
+
if (attributeCodec) {
|
|
83
|
+
condition.extensions.pgFilterAttribute = {
|
|
84
|
+
codec: attributeCodec,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return condition;
|
|
88
|
+
});
|
|
89
|
+
}, [PgCondition, isEmpty, attributeCodec]),
|
|
90
|
+
}),
|
|
91
|
+
},
|
|
92
|
+
}, `Adding connection filter '${argName}' arg to field '${fieldName}' of '${Self.name}'`);
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import '../augmentations';
|
|
2
|
+
import type { GraphileConfig } from 'graphile-config';
|
|
3
|
+
/**
|
|
4
|
+
* ConnectionFilterAttributesPlugin
|
|
5
|
+
*
|
|
6
|
+
* Adds per-column filter fields to the table filter types.
|
|
7
|
+
* For example, on `UserFilter`, adds fields like `name` (type: StringFilter),
|
|
8
|
+
* `age` (type: IntFilter), etc.
|
|
9
|
+
*
|
|
10
|
+
* Each field's `apply` function creates a new PgCondition with the
|
|
11
|
+
* `pgFilterAttribute` extension set, so downstream operator fields know
|
|
12
|
+
* which column they are operating on.
|
|
13
|
+
*/
|
|
14
|
+
export declare const ConnectionFilterAttributesPlugin: GraphileConfig.Plugin;
|