graphile-settings 3.1.1 → 4.0.1

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 (102) hide show
  1. package/README.md +170 -85
  2. package/esm/index.d.ts +37 -0
  3. package/esm/index.js +54 -103
  4. package/esm/plugins/conflict-detector.d.ts +7 -0
  5. package/esm/plugins/conflict-detector.js +67 -0
  6. package/esm/plugins/custom-inflector.d.ts +9 -0
  7. package/esm/plugins/custom-inflector.js +382 -0
  8. package/esm/plugins/enable-all-filter-columns.d.ts +60 -0
  9. package/esm/plugins/enable-all-filter-columns.js +85 -0
  10. package/esm/plugins/index.d.ts +19 -0
  11. package/esm/plugins/index.js +26 -0
  12. package/esm/plugins/inflector-logger.d.ts +7 -0
  13. package/esm/plugins/inflector-logger.js +215 -0
  14. package/esm/plugins/many-to-many-preset.d.ts +62 -0
  15. package/esm/plugins/many-to-many-preset.js +86 -0
  16. package/esm/plugins/meta-schema/cache.d.ts +4 -0
  17. package/esm/plugins/meta-schema/cache.js +7 -0
  18. package/esm/plugins/meta-schema/constraint-meta-builders.d.ts +13 -0
  19. package/esm/plugins/meta-schema/constraint-meta-builders.js +51 -0
  20. package/esm/plugins/meta-schema/graphql-meta-field.d.ts +4 -0
  21. package/esm/plugins/meta-schema/graphql-meta-field.js +201 -0
  22. package/esm/plugins/meta-schema/inflection-utils.d.ts +4 -0
  23. package/esm/plugins/meta-schema/inflection-utils.js +20 -0
  24. package/esm/plugins/meta-schema/name-meta-builders.d.ts +4 -0
  25. package/esm/plugins/meta-schema/name-meta-builders.js +38 -0
  26. package/esm/plugins/meta-schema/plugin.d.ts +2 -0
  27. package/esm/plugins/meta-schema/plugin.js +23 -0
  28. package/esm/plugins/meta-schema/relation-meta-builders.d.ts +8 -0
  29. package/esm/plugins/meta-schema/relation-meta-builders.js +115 -0
  30. package/esm/plugins/meta-schema/table-meta-builder.d.ts +2 -0
  31. package/esm/plugins/meta-schema/table-meta-builder.js +69 -0
  32. package/esm/plugins/meta-schema/table-meta-context.d.ts +13 -0
  33. package/esm/plugins/meta-schema/table-meta-context.js +11 -0
  34. package/esm/plugins/meta-schema/table-resource-utils.d.ts +12 -0
  35. package/esm/plugins/meta-schema/table-resource-utils.js +50 -0
  36. package/esm/plugins/meta-schema/type-mappings.d.ts +3 -0
  37. package/esm/plugins/meta-schema/type-mappings.js +75 -0
  38. package/esm/plugins/meta-schema/types.d.ts +206 -0
  39. package/esm/plugins/meta-schema/types.js +1 -0
  40. package/esm/plugins/meta-schema.d.ts +19 -0
  41. package/esm/plugins/meta-schema.js +20 -0
  42. package/esm/plugins/minimal-preset.d.ts +7 -0
  43. package/esm/plugins/minimal-preset.js +42 -0
  44. package/esm/plugins/pg-type-mappings.d.ts +41 -0
  45. package/esm/plugins/pg-type-mappings.js +122 -0
  46. package/esm/plugins/primary-key-only.d.ts +96 -0
  47. package/esm/plugins/primary-key-only.js +143 -0
  48. package/esm/presets/constructive-preset.d.ts +40 -0
  49. package/esm/presets/constructive-preset.js +137 -0
  50. package/esm/presets/index.d.ts +7 -0
  51. package/esm/presets/index.js +7 -0
  52. package/index.d.ts +37 -3
  53. package/index.js +56 -129
  54. package/package.json +16 -22
  55. package/plugins/conflict-detector.d.ts +7 -0
  56. package/plugins/conflict-detector.js +70 -0
  57. package/plugins/custom-inflector.d.ts +9 -0
  58. package/plugins/custom-inflector.js +385 -0
  59. package/plugins/enable-all-filter-columns.d.ts +60 -0
  60. package/plugins/enable-all-filter-columns.js +88 -0
  61. package/plugins/index.d.ts +19 -0
  62. package/plugins/index.js +56 -0
  63. package/plugins/inflector-logger.d.ts +7 -0
  64. package/plugins/inflector-logger.js +218 -0
  65. package/plugins/many-to-many-preset.d.ts +62 -0
  66. package/plugins/many-to-many-preset.js +89 -0
  67. package/plugins/meta-schema/cache.d.ts +4 -0
  68. package/plugins/meta-schema/cache.js +12 -0
  69. package/plugins/meta-schema/constraint-meta-builders.d.ts +13 -0
  70. package/plugins/meta-schema/constraint-meta-builders.js +58 -0
  71. package/plugins/meta-schema/graphql-meta-field.d.ts +4 -0
  72. package/plugins/meta-schema/graphql-meta-field.js +204 -0
  73. package/plugins/meta-schema/inflection-utils.d.ts +4 -0
  74. package/plugins/meta-schema/inflection-utils.js +25 -0
  75. package/plugins/meta-schema/name-meta-builders.d.ts +4 -0
  76. package/plugins/meta-schema/name-meta-builders.js +43 -0
  77. package/plugins/meta-schema/plugin.d.ts +2 -0
  78. package/plugins/meta-schema/plugin.js +26 -0
  79. package/plugins/meta-schema/relation-meta-builders.d.ts +8 -0
  80. package/plugins/meta-schema/relation-meta-builders.js +120 -0
  81. package/plugins/meta-schema/table-meta-builder.d.ts +2 -0
  82. package/plugins/meta-schema/table-meta-builder.js +72 -0
  83. package/plugins/meta-schema/table-meta-context.d.ts +13 -0
  84. package/plugins/meta-schema/table-meta-context.js +15 -0
  85. package/plugins/meta-schema/table-resource-utils.d.ts +12 -0
  86. package/plugins/meta-schema/table-resource-utils.js +60 -0
  87. package/plugins/meta-schema/type-mappings.d.ts +3 -0
  88. package/plugins/meta-schema/type-mappings.js +79 -0
  89. package/plugins/meta-schema/types.d.ts +206 -0
  90. package/plugins/meta-schema/types.js +2 -0
  91. package/plugins/meta-schema.d.ts +19 -0
  92. package/plugins/meta-schema.js +20 -0
  93. package/plugins/minimal-preset.d.ts +7 -0
  94. package/plugins/minimal-preset.js +45 -0
  95. package/plugins/pg-type-mappings.d.ts +41 -0
  96. package/plugins/pg-type-mappings.js +128 -0
  97. package/plugins/primary-key-only.d.ts +96 -0
  98. package/plugins/primary-key-only.js +147 -0
  99. package/presets/constructive-preset.d.ts +40 -0
  100. package/presets/constructive-preset.js +140 -0
  101. package/presets/index.d.ts +7 -0
  102. package/presets/index.js +11 -0
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NoUniqueLookupPreset = exports.PrimaryKeyOnlyPreset = exports.NoUniqueLookupPlugin = exports.PrimaryKeyOnlyPlugin = void 0;
4
+ exports.createUniqueLookupPlugin = createUniqueLookupPlugin;
5
+ /**
6
+ * PrimaryKeyOnlyPlugin - Disables non-primary-key unique constraint lookups for
7
+ * BOTH queries AND mutations.
8
+ *
9
+ * WHY THIS EXISTS:
10
+ * PostGraphile v5 creates fields for EVERY unique constraint on a table:
11
+ *
12
+ * QUERIES (PgRowByUniquePlugin):
13
+ * - `user(id)`, `userByEmail(email)`, `userByUsername(username)`
14
+ *
15
+ * MUTATIONS (PgMutationUpdateDeletePlugin):
16
+ * - `updateUser`, `updateUserByEmail`, `updateUserByUsername`
17
+ * - `deleteUser`, `deleteUserByEmail`, `deleteUserByUsername`
18
+ *
19
+ * For code generation (React Query, etc.), this creates unnecessary complexity.
20
+ * The same operations can be done using the primary key lookup or filters.
21
+ * Standardizing on primary keys reduces the API surface and generated code.
22
+ *
23
+ * SOURCE CODE REFERENCES:
24
+ *
25
+ * 1. Query fields (PgRowByUniquePlugin):
26
+ * https://github.com/graphile/crystal/blob/924b2515c6bd30e5905ac1419a25244b40c8bb4d/graphile-build/graphile-build-pg/src/plugins/PgRowByUniquePlugin.ts#L42-L257
27
+ *
28
+ * The behavior check for queries:
29
+ * ```typescript
30
+ * const fieldBehaviorScope = "query:resource:single";
31
+ * if (!build.behavior.pgResourceUniqueMatches([resource, unique], fieldBehaviorScope)) {
32
+ * return memo; // Skip this field
33
+ * }
34
+ * ```
35
+ *
36
+ * 2. Mutation fields (PgMutationUpdateDeletePlugin):
37
+ * https://github.com/graphile/crystal/blob/924b2515c6bd30e5905ac1419a25244b40c8bb4d/graphile-build/graphile-build-pg/src/plugins/PgMutationUpdateDeletePlugin.ts
38
+ *
39
+ * The behavior check for mutations:
40
+ * ```typescript
41
+ * const constraintMode = `constraint:${mode}`; // "constraint:resource:update" or "constraint:resource:delete"
42
+ * ...resource.uniques.filter((unique) => {
43
+ * return build.behavior.pgResourceUniqueMatches([resource, unique], constraintMode);
44
+ * })
45
+ * ```
46
+ *
47
+ * OUR FIX:
48
+ * We use the behavior system's OVERRIDE phase (not inferred) to disable these behaviors.
49
+ * The override phase runs AFTER the behavior multiplication/preferences system processes
50
+ * behaviors, giving us the final say on what's enabled/disabled.
51
+ *
52
+ * Behaviors we control:
53
+ * - `-single` - Disables query lookups (userByEmail, etc.)
54
+ * - `-constraint:resource:update` - Disables updateByX mutations
55
+ * - `-constraint:resource:delete` - Disables deleteByX mutations
56
+ *
57
+ * CONFIGURATION OPTIONS:
58
+ *
59
+ * 1. `disableAllUniqueLookups: false` (default - PrimaryKeyOnlyPreset):
60
+ * - Primary key: query lookup + mutations enabled
61
+ * - Non-primary-key: everything disabled
62
+ * Result: `user(id)`, `updateUser`, `deleteUser` only
63
+ *
64
+ * 2. `disableAllUniqueLookups: true` (NoUniqueLookupPreset):
65
+ * - Primary key: query lookup DISABLED, mutations ENABLED
66
+ * - Non-primary-key: everything disabled
67
+ * Result: No query lookups (use filters), but `updateUser`, `deleteUser` still work
68
+ */
69
+ /**
70
+ * Creates a plugin that controls unique constraint lookup behavior.
71
+ *
72
+ * @param options - Configuration options
73
+ * @param options.disableAllUniqueLookups - If true, disables ALL unique lookups including primary keys
74
+ */
75
+ function createUniqueLookupPlugin(options = {}) {
76
+ const { disableAllUniqueLookups = false } = options;
77
+ return {
78
+ name: 'UniqueLookupPlugin',
79
+ version: '1.0.0',
80
+ description: disableAllUniqueLookups
81
+ ? 'Disables all unique constraint lookups (use filters instead)'
82
+ : 'Disables non-primary-key unique constraint lookups to reduce API surface',
83
+ schema: {
84
+ entityBehavior: {
85
+ pgResourceUnique: {
86
+ // Use 'override' phase instead of 'inferred' - override runs AFTER
87
+ // the behavior multiplication/preferences system processes behaviors,
88
+ // so it has the final say on what behaviors are enabled/disabled.
89
+ override: {
90
+ provides: ['uniqueLookupControl'],
91
+ callback(behavior, [_resource, unique]) {
92
+ if (disableAllUniqueLookups) {
93
+ // Disable ALL unique QUERY lookups - users must use filters
94
+ // But KEEP primary key mutations (updateX, deleteX)
95
+ if (unique.isPrimary) {
96
+ // Primary key: only disable query lookups, keep mutations
97
+ return [behavior, '-single'];
98
+ }
99
+ // Non-primary-key: disable everything (queries and mutations)
100
+ return [
101
+ behavior,
102
+ '-single',
103
+ '-constraint:resource:update',
104
+ '-constraint:resource:delete',
105
+ ];
106
+ }
107
+ // Only allow primary key lookups (both queries and mutations)
108
+ if (!unique.isPrimary) {
109
+ // Disable non-primary-key unique constraint lookups for queries and mutations
110
+ return [
111
+ behavior,
112
+ '-single',
113
+ '-constraint:resource:update',
114
+ '-constraint:resource:delete',
115
+ ];
116
+ }
117
+ return behavior;
118
+ },
119
+ },
120
+ },
121
+ },
122
+ },
123
+ };
124
+ }
125
+ // Default plugin instance (primary key only)
126
+ exports.PrimaryKeyOnlyPlugin = createUniqueLookupPlugin({
127
+ disableAllUniqueLookups: false,
128
+ });
129
+ // Plugin that disables ALL unique lookups
130
+ exports.NoUniqueLookupPlugin = createUniqueLookupPlugin({
131
+ disableAllUniqueLookups: true,
132
+ });
133
+ /**
134
+ * Preset that keeps only primary key lookups.
135
+ * Use this in your main preset's `extends` array.
136
+ */
137
+ exports.PrimaryKeyOnlyPreset = {
138
+ plugins: [exports.PrimaryKeyOnlyPlugin],
139
+ };
140
+ /**
141
+ * Preset that disables ALL unique lookups (including primary keys).
142
+ * Users must use collection queries with filters instead.
143
+ * Use this in your main preset's `extends` array.
144
+ */
145
+ exports.NoUniqueLookupPreset = {
146
+ plugins: [exports.NoUniqueLookupPlugin],
147
+ };
@@ -0,0 +1,40 @@
1
+ import type { GraphileConfig } from 'graphile-config';
2
+ /**
3
+ * Constructive PostGraphile v5 Preset
4
+ *
5
+ * This is the main preset that combines all our custom plugins and configurations.
6
+ * It provides a clean, opinionated GraphQL API built from PostgreSQL.
7
+ *
8
+ * FEATURES:
9
+ * - No Node/Relay features (keeps `id` as `id`, no global object identification)
10
+ * - Custom inflection using inflekt library
11
+ * - Conflict detection for multi-schema setups
12
+ * - Inflector logging for debugging (enable with INFLECTOR_LOG=1)
13
+ * - Primary key only lookups (no *ByEmail, *ByUsername, etc.)
14
+ * - Connection filter plugin with all columns filterable
15
+ * - Many-to-many relationships (opt-in via @behavior +manyToMany)
16
+ * - Meta schema plugin (_meta query for introspection of tables, fields, indexes)
17
+ * - PG type mappings (maps custom types like email, url to GraphQL scalars)
18
+ *
19
+ * DISABLED PLUGINS:
20
+ * - PgConnectionArgFilterBackwardRelationsPlugin (relation filters bloat the API)
21
+ * - PgConnectionArgFilterForwardRelationsPlugin (relation filters bloat the API)
22
+ *
23
+ * USAGE:
24
+ * ```typescript
25
+ * import { ConstructivePreset } from 'graphile-settings/presets';
26
+ * import { makePgService } from 'postgraphile/adaptors/pg';
27
+ *
28
+ * const preset: GraphileConfig.Preset = {
29
+ * extends: [ConstructivePreset],
30
+ * pgServices: [
31
+ * makePgService({
32
+ * connectionString: DATABASE_URL,
33
+ * schemas: ['public'],
34
+ * }),
35
+ * ],
36
+ * };
37
+ * ```
38
+ */
39
+ export declare const ConstructivePreset: GraphileConfig.Preset;
40
+ export default ConstructivePreset;
@@ -0,0 +1,140 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConstructivePreset = void 0;
4
+ const postgraphile_plugin_connection_filter_1 = require("postgraphile-plugin-connection-filter");
5
+ const minimal_preset_1 = require("../plugins/minimal-preset");
6
+ const custom_inflector_1 = require("../plugins/custom-inflector");
7
+ const conflict_detector_1 = require("../plugins/conflict-detector");
8
+ const inflector_logger_1 = require("../plugins/inflector-logger");
9
+ const primary_key_only_1 = require("../plugins/primary-key-only");
10
+ const enable_all_filter_columns_1 = require("../plugins/enable-all-filter-columns");
11
+ const many_to_many_preset_1 = require("../plugins/many-to-many-preset");
12
+ const meta_schema_1 = require("../plugins/meta-schema");
13
+ const graphile_search_plugin_1 = require("graphile-search-plugin");
14
+ const pg_type_mappings_1 = require("../plugins/pg-type-mappings");
15
+ /**
16
+ * Constructive PostGraphile v5 Preset
17
+ *
18
+ * This is the main preset that combines all our custom plugins and configurations.
19
+ * It provides a clean, opinionated GraphQL API built from PostgreSQL.
20
+ *
21
+ * FEATURES:
22
+ * - No Node/Relay features (keeps `id` as `id`, no global object identification)
23
+ * - Custom inflection using inflekt library
24
+ * - Conflict detection for multi-schema setups
25
+ * - Inflector logging for debugging (enable with INFLECTOR_LOG=1)
26
+ * - Primary key only lookups (no *ByEmail, *ByUsername, etc.)
27
+ * - Connection filter plugin with all columns filterable
28
+ * - Many-to-many relationships (opt-in via @behavior +manyToMany)
29
+ * - Meta schema plugin (_meta query for introspection of tables, fields, indexes)
30
+ * - PG type mappings (maps custom types like email, url to GraphQL scalars)
31
+ *
32
+ * DISABLED PLUGINS:
33
+ * - PgConnectionArgFilterBackwardRelationsPlugin (relation filters bloat the API)
34
+ * - PgConnectionArgFilterForwardRelationsPlugin (relation filters bloat the API)
35
+ *
36
+ * USAGE:
37
+ * ```typescript
38
+ * import { ConstructivePreset } from 'graphile-settings/presets';
39
+ * import { makePgService } from 'postgraphile/adaptors/pg';
40
+ *
41
+ * const preset: GraphileConfig.Preset = {
42
+ * extends: [ConstructivePreset],
43
+ * pgServices: [
44
+ * makePgService({
45
+ * connectionString: DATABASE_URL,
46
+ * schemas: ['public'],
47
+ * }),
48
+ * ],
49
+ * };
50
+ * ```
51
+ */
52
+ exports.ConstructivePreset = {
53
+ extends: [
54
+ minimal_preset_1.MinimalPreset,
55
+ conflict_detector_1.ConflictDetectorPreset,
56
+ custom_inflector_1.InflektPreset,
57
+ inflector_logger_1.InflectorLoggerPreset,
58
+ primary_key_only_1.NoUniqueLookupPreset,
59
+ postgraphile_plugin_connection_filter_1.PostGraphileConnectionFilterPreset,
60
+ enable_all_filter_columns_1.EnableAllFilterColumnsPreset,
61
+ many_to_many_preset_1.ManyToManyOptInPreset,
62
+ meta_schema_1.MetaSchemaPreset,
63
+ (0, graphile_search_plugin_1.PgSearchPreset)({ pgSearchPrefix: 'fullText' }),
64
+ pg_type_mappings_1.PgTypeMappingsPreset,
65
+ ],
66
+ /**
67
+ * Disable relation filter plugins from postgraphile-plugin-connection-filter.
68
+ *
69
+ * WHY THIS EXISTS:
70
+ * The connection filter plugin includes PgConnectionArgFilterBackwardRelationsPlugin and
71
+ * PgConnectionArgFilterForwardRelationsPlugin which add relation filter fields like
72
+ * `apiExtensions`, `apiExtensionsExist`, `database`, `domains`, etc. to every filter type.
73
+ *
74
+ * The `connectionFilterRelations: false` schema option does NOT work - it's defined in the
75
+ * plugin's TypeScript types but the actual code always includes the plugins regardless.
76
+ * See: https://github.com/graphile-contrib/postgraphile-plugin-connection-filter/blob/master/src/index.ts
77
+ * The comments `//if (connectionFilterRelations)` are just comments, not actual conditional logic.
78
+ *
79
+ * The entityBehavior approach (setting `pgCodecRelation: '-filterBy'`) also doesn't work
80
+ * because the behavior system doesn't properly negate the plugin's default `filterBy` behavior.
81
+ *
82
+ * OUR FIX:
83
+ * We use `disablePlugins` to directly disable the two relation filter plugins.
84
+ * This is the most reliable way to prevent relation filter fields from being generated.
85
+ */
86
+ disablePlugins: [
87
+ 'PgConnectionArgFilterBackwardRelationsPlugin',
88
+ 'PgConnectionArgFilterForwardRelationsPlugin',
89
+ ],
90
+ /**
91
+ * Connection Filter Plugin Configuration
92
+ *
93
+ * These options control what fields appear in the `filter` argument on connections.
94
+ * We disable relation filters to keep the API surface clean and match our v4 behavior.
95
+ *
96
+ * NOTE: By default, PostGraphile v5 only allows filtering on INDEXED columns.
97
+ * We override this with EnableAllFilterColumnsPreset to allow filtering on ALL columns.
98
+ * This gives developers flexibility but requires monitoring for slow queries on
99
+ * non-indexed columns. See the plugin documentation for performance considerations.
100
+ *
101
+ * NOTE: Relation filtering is disabled via `disablePlugins` above.
102
+ *
103
+ * Documentation: https://github.com/graphile-contrib/postgraphile-plugin-connection-filter
104
+ */
105
+ schema: {
106
+ /**
107
+ * connectionFilterRelations: false
108
+ * This option is defined in the plugin's types but does NOT actually work.
109
+ * The relation filter plugins are disabled via `disablePlugins` above.
110
+ * We keep this option set to false for documentation purposes.
111
+ */
112
+ connectionFilterRelations: false,
113
+ /**
114
+ * connectionFilterComputedColumns: false
115
+ * Disables filtering on computed columns (functions that return a value for a row).
116
+ * Computed columns can be expensive to filter on since they may not be indexed.
117
+ * To selectively enable, use `@filterable` smart tag on specific functions.
118
+ */
119
+ connectionFilterComputedColumns: false,
120
+ /**
121
+ * connectionFilterSetofFunctions: false
122
+ * Disables filtering on functions that return `setof` (multiple rows).
123
+ * These can be expensive operations. To selectively enable, use `@filterable` smart tag.
124
+ */
125
+ connectionFilterSetofFunctions: false,
126
+ /**
127
+ * connectionFilterLogicalOperators: true (default)
128
+ * Keeps `and`, `or`, `not` operators for combining filter conditions.
129
+ * Example: filter: { or: [{ name: { eq: "foo" } }, { name: { eq: "bar" } }] }
130
+ */
131
+ connectionFilterLogicalOperators: true,
132
+ /**
133
+ * connectionFilterArrays: true (default)
134
+ * Allows filtering on PostgreSQL array columns.
135
+ * Example: filter: { tags: { contains: ["important"] } }
136
+ */
137
+ connectionFilterArrays: true,
138
+ },
139
+ };
140
+ exports.default = exports.ConstructivePreset;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * PostGraphile v5 Presets
3
+ *
4
+ * This module exports pre-configured presets that combine multiple plugins
5
+ * for common use cases.
6
+ */
7
+ export { ConstructivePreset } from './constructive-preset';
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ /**
3
+ * PostGraphile v5 Presets
4
+ *
5
+ * This module exports pre-configured presets that combine multiple plugins
6
+ * for common use cases.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.ConstructivePreset = void 0;
10
+ var constructive_preset_1 = require("./constructive-preset");
11
+ Object.defineProperty(exports, "ConstructivePreset", { enumerable: true, get: function () { return constructive_preset_1.ConstructivePreset; } });