graphile-settings 3.1.0 → 4.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.
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 +55 -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 +58 -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,215 @@
1
+ /**
2
+ * InflectorLoggerPlugin - Logs inflector calls during schema build for debugging.
3
+ *
4
+ * This plugin wraps key inflectors to log what fields are being generated and why.
5
+ * It passes through to the default behavior but logs the inputs and outputs.
6
+ *
7
+ * WHEN DO PLUGINS RUN?
8
+ * Plugins only run at BUILD TIME, not on every request. The schema is built once
9
+ * and cached. This means logging only happens during schema generation, not during
10
+ * query execution.
11
+ *
12
+ * SOURCE CODE REFERENCES:
13
+ *
14
+ * 1. PgRowByUniquePlugin - Creates root Query fields for unique constraints
15
+ * (e.g., `userById`, `userByEmail`, `postBySlug`)
16
+ * https://github.com/graphile/crystal/blob/924b2515c6bd30e5905ac1419a25244b40c8bb4d/graphile-build/graphile-build-pg/src/plugins/PgRowByUniquePlugin.ts#L42-L257
17
+ *
18
+ * 2. PgRelationsPlugin - Creates relationship fields on types for foreign keys
19
+ * - Forward relations: `post.author` (from posts.author_id -> users.id)
20
+ * - Backward relations: `user.posts` (from users.id <- posts.author_id)
21
+ * https://github.com/graphile/crystal/blob/924b2515c6bd30e5905ac1419a25244b40c8bb4d/graphile-build/graphile-build-pg/src/plugins/PgRelationsPlugin.ts#L167-L638
22
+ *
23
+ * 3. PgAttributesPlugin - Creates fields for table columns
24
+ * Also contains the `_attributeName` inflector that renames `id` to `rowId`
25
+ * https://github.com/graphile/crystal/blob/924b2515c6bd30e5905ac1419a25244b40c8bb4d/graphile-build/graphile-build-pg/src/plugins/PgAttributesPlugin.ts#L289-L298
26
+ *
27
+ * 4. PgTablesPlugin - Creates GraphQL types for tables
28
+ * Contains the `_schemaPrefix` inflector that adds schema prefixes
29
+ * https://github.com/graphile/crystal/blob/924b2515c6bd30e5905ac1419a25244b40c8bb4d/graphile-build/graphile-build-pg/src/plugins/PgTablesPlugin.ts#L261-L271
30
+ *
31
+ * USAGE:
32
+ * Add InflectorLoggerPlugin to your preset's plugins array.
33
+ * Set INFLECTOR_LOG=1 environment variable to enable logging.
34
+ */
35
+ const LOG_ENABLED = process.env.INFLECTOR_LOG === '1';
36
+ function log(category, message, details) {
37
+ if (!LOG_ENABLED)
38
+ return;
39
+ const detailsStr = details ? ` ${JSON.stringify(details)}` : '';
40
+ console.log(`[Inflector:${category}]${detailsStr} => ${message}`);
41
+ }
42
+ export const InflectorLoggerPlugin = {
43
+ name: 'InflectorLoggerPlugin',
44
+ version: '1.0.0',
45
+ description: 'Logs inflector calls during schema build for debugging',
46
+ inflection: {
47
+ replace: {
48
+ /**
49
+ * Logs when root Query fields are created for unique constraints.
50
+ * Source: PgRowByUniquePlugin
51
+ * https://github.com/graphile/crystal/blob/924b2515c6bd30e5905ac1419a25244b40c8bb4d/graphile-build/graphile-build-pg/src/plugins/PgRowByUniquePlugin.ts#L50-L63
52
+ */
53
+ rowByUnique(previous, _options, details) {
54
+ const result = previous(details);
55
+ const { unique, resource } = details;
56
+ log('rowByUnique', result, {
57
+ resource: resource.name,
58
+ uniqueAttributes: unique.attributes,
59
+ isPrimary: unique.isPrimary,
60
+ });
61
+ return result;
62
+ },
63
+ /**
64
+ * Logs when forward relation fields are created (e.g., post.author).
65
+ * Source: PgRelationsPlugin
66
+ * https://github.com/graphile/crystal/blob/924b2515c6bd30e5905ac1419a25244b40c8bb4d/graphile-build/graphile-build-pg/src/plugins/PgRelationsPlugin.ts#L228-L230
67
+ */
68
+ singleRelation(previous, _options, details) {
69
+ const result = previous(details);
70
+ const { codec, relationName, registry } = details;
71
+ const relation = registry.pgRelations[codec.name]?.[relationName];
72
+ log('singleRelation', result, {
73
+ fromType: codec.name,
74
+ relationName,
75
+ toType: relation?.remoteResource?.name,
76
+ localAttributes: relation?.localAttributes,
77
+ });
78
+ return result;
79
+ },
80
+ /**
81
+ * Logs when backward single relation fields are created.
82
+ * Source: PgRelationsPlugin
83
+ * https://github.com/graphile/crystal/blob/924b2515c6bd30e5905ac1419a25244b40c8bb4d/graphile-build/graphile-build-pg/src/plugins/PgRelationsPlugin.ts#L250-L252
84
+ */
85
+ singleRelationBackwards(previous, _options, details) {
86
+ const result = previous(details);
87
+ const { codec, relationName, registry } = details;
88
+ const relation = registry.pgRelations[codec.name]?.[relationName];
89
+ log('singleRelationBackwards', result, {
90
+ fromType: codec.name,
91
+ relationName,
92
+ toType: relation?.remoteResource?.name,
93
+ remoteAttributes: relation?.remoteAttributes,
94
+ });
95
+ return result;
96
+ },
97
+ /**
98
+ * Logs when many-relation fields are created (e.g., user.posts).
99
+ * Source: PgRelationsPlugin
100
+ * https://github.com/graphile/crystal/blob/924b2515c6bd30e5905ac1419a25244b40c8bb4d/graphile-build/graphile-build-pg/src/plugins/PgRelationsPlugin.ts#L268-L270
101
+ */
102
+ _manyRelation(previous, _options, details) {
103
+ const result = previous(details);
104
+ const { codec, relationName, registry } = details;
105
+ const relation = registry.pgRelations[codec.name]?.[relationName];
106
+ log('manyRelation', result, {
107
+ fromType: codec.name,
108
+ relationName,
109
+ toType: relation?.remoteResource?.name,
110
+ remoteAttributes: relation?.remoteAttributes,
111
+ });
112
+ return result;
113
+ },
114
+ /**
115
+ * Logs when connection fields are created for many relations.
116
+ * Source: PgRelationsPlugin
117
+ * https://github.com/graphile/crystal/blob/924b2515c6bd30e5905ac1419a25244b40c8bb4d/graphile-build/graphile-build-pg/src/plugins/PgRelationsPlugin.ts#L271-L279
118
+ */
119
+ manyRelationConnection(previous, _options, details) {
120
+ const result = previous(details);
121
+ const { codec, relationName } = details;
122
+ log('manyRelationConnection', result, {
123
+ fromType: codec.name,
124
+ relationName,
125
+ });
126
+ return result;
127
+ },
128
+ /**
129
+ * Logs when list fields are created for many relations.
130
+ * Source: PgRelationsPlugin
131
+ * https://github.com/graphile/crystal/blob/924b2515c6bd30e5905ac1419a25244b40c8bb4d/graphile-build/graphile-build-pg/src/plugins/PgRelationsPlugin.ts#L280-L288
132
+ */
133
+ manyRelationList(previous, _options, details) {
134
+ const result = previous(details);
135
+ const { codec, relationName } = details;
136
+ log('manyRelationList', result, {
137
+ fromType: codec.name,
138
+ relationName,
139
+ });
140
+ return result;
141
+ },
142
+ /**
143
+ * Logs when root connection fields are created (e.g., Query.users).
144
+ * Source: PgAllRowsPlugin
145
+ */
146
+ allRowsConnection(previous, _options, resource) {
147
+ const result = previous(resource);
148
+ log('allRowsConnection', result, {
149
+ resource: resource.name,
150
+ });
151
+ return result;
152
+ },
153
+ /**
154
+ * Logs when update mutation fields are created.
155
+ * Source: PgMutationUpdateDeletePlugin
156
+ */
157
+ updateByKeysField(previous, _options, details) {
158
+ const result = previous(details);
159
+ const { resource, unique } = details;
160
+ log('updateByKeysField', result, {
161
+ resource: resource.name,
162
+ uniqueAttributes: unique.attributes,
163
+ isPrimary: unique.isPrimary,
164
+ });
165
+ return result;
166
+ },
167
+ /**
168
+ * Logs when delete mutation fields are created.
169
+ * Source: PgMutationUpdateDeletePlugin
170
+ */
171
+ deleteByKeysField(previous, _options, details) {
172
+ const result = previous(details);
173
+ const { resource, unique } = details;
174
+ log('deleteByKeysField', result, {
175
+ resource: resource.name,
176
+ uniqueAttributes: unique.attributes,
177
+ isPrimary: unique.isPrimary,
178
+ });
179
+ return result;
180
+ },
181
+ /**
182
+ * Logs when attribute (column) fields are created.
183
+ * Source: PgAttributesPlugin
184
+ * https://github.com/graphile/crystal/blob/924b2515c6bd30e5905ac1419a25244b40c8bb4d/graphile-build/graphile-build-pg/src/plugins/PgAttributesPlugin.ts#L289-L298
185
+ */
186
+ attribute(previous, _options, details) {
187
+ const result = previous(details);
188
+ const { attributeName, codec } = details;
189
+ log('attribute', result, {
190
+ codec: codec.name,
191
+ attributeName,
192
+ });
193
+ return result;
194
+ },
195
+ /**
196
+ * Logs when table types are created.
197
+ * Source: PgTablesPlugin
198
+ */
199
+ tableType(previous, _options, codec) {
200
+ const result = previous(codec);
201
+ log('tableType', result, {
202
+ codec: codec.name,
203
+ });
204
+ return result;
205
+ },
206
+ },
207
+ },
208
+ };
209
+ /**
210
+ * Preset that includes the inflector logger plugin.
211
+ * Use this in your main preset's `extends` array.
212
+ */
213
+ export const InflectorLoggerPreset = {
214
+ plugins: [InflectorLoggerPlugin],
215
+ };
@@ -0,0 +1,62 @@
1
+ import type { GraphileConfig } from 'graphile-config';
2
+ /**
3
+ * Many-to-Many Preset with OPT-IN behavior (disabled by default).
4
+ *
5
+ * WHY THIS EXISTS:
6
+ * The default @graphile-contrib/pg-many-to-many plugin adds many-to-many connection
7
+ * fields to EVERY junction table automatically. This can bloat the API with fields
8
+ * that may never be used.
9
+ *
10
+ * OUR FIX:
11
+ * We override the default behavior to be OPT-IN instead of OPT-OUT:
12
+ * - By default, NO many-to-many fields are generated
13
+ * - To enable for a specific junction table, use: @behavior +manyToMany
14
+ *
15
+ * USAGE:
16
+ * To enable many-to-many for a specific junction table, add a smart comment:
17
+ *
18
+ * ```sql
19
+ * -- Enable many-to-many through this junction table
20
+ * COMMENT ON TABLE post_tags IS E'@behavior +manyToMany';
21
+ *
22
+ * -- Or enable on a specific constraint
23
+ * COMMENT ON CONSTRAINT post_tags_tag_id_fkey ON post_tags IS E'@behavior +manyToMany';
24
+ * ```
25
+ *
26
+ * SOURCE CODE REFERENCE:
27
+ * The many-to-many plugin uses the behavior system to control field generation:
28
+ * https://github.com/graphile-contrib/pg-many-to-many/blob/v2.0.0-rc.1/src/PgManyToManyRelationPlugin.ts#L478-L503
29
+ *
30
+ * The plugin defines these behaviors:
31
+ * - `manyToMany` - Controls whether many-to-many fields are generated
32
+ * - `connection` - Controls whether connection fields are generated
33
+ * - `list` - Controls whether list fields are generated
34
+ *
35
+ * By default, the plugin's entityBehavior for pgManyToMany includes:
36
+ * `["manyToMany", "connection", "list", behavior]`
37
+ *
38
+ * We override this to be `-manyToMany` by default, requiring explicit opt-in.
39
+ */
40
+ /**
41
+ * Plugin that makes many-to-many fields opt-in by default.
42
+ *
43
+ * This overrides the default behavior from @graphile-contrib/pg-many-to-many
44
+ * to require explicit `@behavior +manyToMany` smart tags.
45
+ */
46
+ export declare const ManyToManyOptInPlugin: GraphileConfig.Plugin;
47
+ /**
48
+ * Preset that includes the many-to-many plugin with opt-in behavior.
49
+ *
50
+ * Use this in your main preset's `extends` array:
51
+ * ```typescript
52
+ * const preset: GraphileConfig.Preset = {
53
+ * extends: [
54
+ * MinimalPreset,
55
+ * ManyToManyOptInPreset,
56
+ * // ... other presets
57
+ * ],
58
+ * };
59
+ * ```
60
+ */
61
+ export declare const ManyToManyOptInPreset: GraphileConfig.Preset;
62
+ export default ManyToManyOptInPreset;
@@ -0,0 +1,86 @@
1
+ import { PgManyToManyPreset } from '@graphile-contrib/pg-many-to-many';
2
+ /**
3
+ * Many-to-Many Preset with OPT-IN behavior (disabled by default).
4
+ *
5
+ * WHY THIS EXISTS:
6
+ * The default @graphile-contrib/pg-many-to-many plugin adds many-to-many connection
7
+ * fields to EVERY junction table automatically. This can bloat the API with fields
8
+ * that may never be used.
9
+ *
10
+ * OUR FIX:
11
+ * We override the default behavior to be OPT-IN instead of OPT-OUT:
12
+ * - By default, NO many-to-many fields are generated
13
+ * - To enable for a specific junction table, use: @behavior +manyToMany
14
+ *
15
+ * USAGE:
16
+ * To enable many-to-many for a specific junction table, add a smart comment:
17
+ *
18
+ * ```sql
19
+ * -- Enable many-to-many through this junction table
20
+ * COMMENT ON TABLE post_tags IS E'@behavior +manyToMany';
21
+ *
22
+ * -- Or enable on a specific constraint
23
+ * COMMENT ON CONSTRAINT post_tags_tag_id_fkey ON post_tags IS E'@behavior +manyToMany';
24
+ * ```
25
+ *
26
+ * SOURCE CODE REFERENCE:
27
+ * The many-to-many plugin uses the behavior system to control field generation:
28
+ * https://github.com/graphile-contrib/pg-many-to-many/blob/v2.0.0-rc.1/src/PgManyToManyRelationPlugin.ts#L478-L503
29
+ *
30
+ * The plugin defines these behaviors:
31
+ * - `manyToMany` - Controls whether many-to-many fields are generated
32
+ * - `connection` - Controls whether connection fields are generated
33
+ * - `list` - Controls whether list fields are generated
34
+ *
35
+ * By default, the plugin's entityBehavior for pgManyToMany includes:
36
+ * `["manyToMany", "connection", "list", behavior]`
37
+ *
38
+ * We override this to be `-manyToMany` by default, requiring explicit opt-in.
39
+ */
40
+ /**
41
+ * Plugin that makes many-to-many fields opt-in by default.
42
+ *
43
+ * This overrides the default behavior from @graphile-contrib/pg-many-to-many
44
+ * to require explicit `@behavior +manyToMany` smart tags.
45
+ */
46
+ export const ManyToManyOptInPlugin = {
47
+ name: 'ManyToManyOptInPlugin',
48
+ version: '1.0.0',
49
+ description: 'Makes many-to-many fields opt-in by default (disabled unless explicitly enabled)',
50
+ schema: {
51
+ entityBehavior: {
52
+ pgManyToMany: {
53
+ // Override the default behavior to be opt-out (disabled by default)
54
+ // The 'inferred' phase runs before 'override', so we use 'inferred'
55
+ // to set the default before any smart tags are processed.
56
+ inferred: {
57
+ provides: ['manyToManyOptIn'],
58
+ before: ['default'],
59
+ callback(behavior) {
60
+ // Default to disabled - require explicit @behavior +manyToMany
61
+ return ['-manyToMany', behavior];
62
+ },
63
+ },
64
+ },
65
+ },
66
+ },
67
+ };
68
+ /**
69
+ * Preset that includes the many-to-many plugin with opt-in behavior.
70
+ *
71
+ * Use this in your main preset's `extends` array:
72
+ * ```typescript
73
+ * const preset: GraphileConfig.Preset = {
74
+ * extends: [
75
+ * MinimalPreset,
76
+ * ManyToManyOptInPreset,
77
+ * // ... other presets
78
+ * ],
79
+ * };
80
+ * ```
81
+ */
82
+ export const ManyToManyOptInPreset = {
83
+ extends: [PgManyToManyPreset],
84
+ plugins: [ManyToManyOptInPlugin],
85
+ };
86
+ export default ManyToManyOptInPreset;
@@ -0,0 +1,4 @@
1
+ import type { TableMeta } from './types';
2
+ export declare let cachedTablesMeta: TableMeta[];
3
+ export declare function getCachedTablesMeta(): TableMeta[];
4
+ export declare function setCachedTablesMeta(tablesMeta: TableMeta[]): void;
@@ -0,0 +1,7 @@
1
+ export let cachedTablesMeta = [];
2
+ export function getCachedTablesMeta() {
3
+ return cachedTablesMeta;
4
+ }
5
+ export function setCachedTablesMeta(tablesMeta) {
6
+ cachedTablesMeta = tablesMeta;
7
+ }
@@ -0,0 +1,13 @@
1
+ import { type BuildContext } from './table-meta-context';
2
+ import type { ForeignKeyConstraintMeta, PgAttribute, PgCodec, PgRelation, PgUnique, PrimaryKeyConstraintMeta, UniqueConstraintMeta } from './types';
3
+ export declare function buildForeignKeyConstraint(constraintName: string, localCodec: PgCodec, localAttributes: Record<string, PgAttribute>, localAttributeNames: string[], remoteCodec: PgCodec | undefined, remoteAttributes: Record<string, PgAttribute>, remoteAttributeNames: string[], context: BuildContext): ForeignKeyConstraintMeta;
4
+ export declare function buildIndexes(codec: PgCodec, attributes: Record<string, PgAttribute>, uniques: PgUnique[], context: BuildContext): {
5
+ name: string;
6
+ isUnique: boolean;
7
+ isPrimary: boolean;
8
+ columns: string[];
9
+ fields: import("./types").FieldMeta[];
10
+ }[];
11
+ export declare function buildPrimaryKey(codec: PgCodec, attributes: Record<string, PgAttribute>, uniques: PgUnique[], context: BuildContext): PrimaryKeyConstraintMeta | null;
12
+ export declare function buildUniqueConstraints(codec: PgCodec, attributes: Record<string, PgAttribute>, uniques: PgUnique[], context: BuildContext): UniqueConstraintMeta[];
13
+ export declare function buildForeignKeyConstraints(codec: PgCodec, attributes: Record<string, PgAttribute>, relations: Record<string, PgRelation>, context: BuildContext): ForeignKeyConstraintMeta[];
@@ -0,0 +1,51 @@
1
+ import { buildFieldMeta } from './type-mappings';
2
+ import { buildFieldList } from './table-meta-context';
3
+ export function buildForeignKeyConstraint(constraintName, localCodec, localAttributes, localAttributeNames, remoteCodec, remoteAttributes, remoteAttributeNames, context) {
4
+ const referencedTable = remoteCodec?.name || 'unknown';
5
+ const referencedFields = remoteAttributeNames.map((attrName) => remoteCodec ? context.inflectAttr(attrName, remoteCodec) : attrName);
6
+ return {
7
+ name: constraintName,
8
+ fields: buildFieldList(localAttributeNames, localCodec, localAttributes, context),
9
+ referencedTable,
10
+ referencedFields,
11
+ refFields: remoteAttributeNames.map((attrName) => buildFieldMeta(remoteCodec ? context.inflectAttr(attrName, remoteCodec) : attrName, remoteAttributes[attrName], context.build)),
12
+ refTable: { name: referencedTable },
13
+ };
14
+ }
15
+ export function buildIndexes(codec, attributes, uniques, context) {
16
+ return uniques.map((unique) => ({
17
+ name: unique.tags?.name || `${codec.name}_${unique.attributes.join('_')}_idx`,
18
+ isUnique: true,
19
+ isPrimary: !!unique.isPrimary,
20
+ columns: unique.attributes.map((attrName) => context.inflectAttr(attrName, codec)),
21
+ fields: buildFieldList(unique.attributes, codec, attributes, context),
22
+ }));
23
+ }
24
+ export function buildPrimaryKey(codec, attributes, uniques, context) {
25
+ const primaryUnique = uniques.find((unique) => unique.isPrimary);
26
+ if (!primaryUnique)
27
+ return null;
28
+ return {
29
+ name: primaryUnique.tags?.name || `${codec.name}_pkey`,
30
+ fields: buildFieldList(primaryUnique.attributes, codec, attributes, context),
31
+ };
32
+ }
33
+ export function buildUniqueConstraints(codec, attributes, uniques, context) {
34
+ return uniques
35
+ .filter((unique) => !unique.isPrimary)
36
+ .map((unique) => ({
37
+ name: unique.tags?.name || `${codec.name}_${unique.attributes.join('_')}_key`,
38
+ fields: buildFieldList(unique.attributes, codec, attributes, context),
39
+ }));
40
+ }
41
+ export function buildForeignKeyConstraints(codec, attributes, relations, context) {
42
+ const constraints = [];
43
+ for (const [relationName, relation] of Object.entries(relations)) {
44
+ if (relation.isReferencee !== false)
45
+ continue;
46
+ const remoteCodec = relation.remoteResource?.codec;
47
+ const remoteAttributes = remoteCodec?.attributes || {};
48
+ constraints.push(buildForeignKeyConstraint(relationName, codec, attributes, relation.localAttributes || [], remoteCodec, remoteAttributes, relation.remoteAttributes || [], context));
49
+ }
50
+ return constraints;
51
+ }
@@ -0,0 +1,4 @@
1
+ import type { TableMeta } from './types';
2
+ type FieldMap = Record<string, unknown>;
3
+ export declare function extendQueryWithMetaField(fields: FieldMap, tablesMeta: TableMeta[]): FieldMap;
4
+ export {};
@@ -0,0 +1,201 @@
1
+ import { GraphQLBoolean, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLString, } from 'graphql';
2
+ function nn(type) {
3
+ return new GraphQLNonNull(type);
4
+ }
5
+ function nnList(type) {
6
+ return nn(new GraphQLList(nn(type)));
7
+ }
8
+ function createMetaSchemaType() {
9
+ const MetaTypeType = new GraphQLObjectType({
10
+ name: 'MetaType',
11
+ description: 'Information about a PostgreSQL type',
12
+ fields: () => ({
13
+ pgType: { type: nn(GraphQLString) },
14
+ gqlType: { type: nn(GraphQLString) },
15
+ isArray: { type: nn(GraphQLBoolean) },
16
+ isNotNull: { type: GraphQLBoolean },
17
+ hasDefault: { type: GraphQLBoolean },
18
+ }),
19
+ });
20
+ const MetaFieldType = new GraphQLObjectType({
21
+ name: 'MetaField',
22
+ description: 'Information about a table field/column',
23
+ fields: () => ({
24
+ name: { type: nn(GraphQLString) },
25
+ type: { type: nn(MetaTypeType) },
26
+ isNotNull: { type: nn(GraphQLBoolean) },
27
+ hasDefault: { type: nn(GraphQLBoolean) },
28
+ }),
29
+ });
30
+ const MetaIndexType = new GraphQLObjectType({
31
+ name: 'MetaIndex',
32
+ description: 'Information about a database index',
33
+ fields: () => ({
34
+ name: { type: nn(GraphQLString) },
35
+ isUnique: { type: nn(GraphQLBoolean) },
36
+ isPrimary: { type: nn(GraphQLBoolean) },
37
+ columns: { type: nnList(GraphQLString) },
38
+ fields: { type: new GraphQLList(nn(MetaFieldType)) },
39
+ }),
40
+ });
41
+ const MetaPrimaryKeyConstraintType = new GraphQLObjectType({
42
+ name: 'MetaPrimaryKeyConstraint',
43
+ description: 'Information about a primary key constraint',
44
+ fields: () => ({
45
+ name: { type: nn(GraphQLString) },
46
+ fields: { type: nnList(MetaFieldType) },
47
+ }),
48
+ });
49
+ const MetaUniqueConstraintType = new GraphQLObjectType({
50
+ name: 'MetaUniqueConstraint',
51
+ description: 'Information about a unique constraint',
52
+ fields: () => ({
53
+ name: { type: nn(GraphQLString) },
54
+ fields: { type: nnList(MetaFieldType) },
55
+ }),
56
+ });
57
+ const MetaRefTableType = new GraphQLObjectType({
58
+ name: 'MetaRefTable',
59
+ description: 'Reference to a related table',
60
+ fields: () => ({
61
+ name: { type: nn(GraphQLString) },
62
+ }),
63
+ });
64
+ const MetaForeignKeyConstraintType = new GraphQLObjectType({
65
+ name: 'MetaForeignKeyConstraint',
66
+ description: 'Information about a foreign key constraint',
67
+ fields: () => ({
68
+ name: { type: nn(GraphQLString) },
69
+ fields: { type: nnList(MetaFieldType) },
70
+ referencedTable: { type: nn(GraphQLString) },
71
+ referencedFields: { type: nnList(GraphQLString) },
72
+ refFields: { type: new GraphQLList(nn(MetaFieldType)) },
73
+ refTable: { type: MetaRefTableType },
74
+ }),
75
+ });
76
+ const MetaConstraintsType = new GraphQLObjectType({
77
+ name: 'MetaConstraints',
78
+ description: 'Table constraints',
79
+ fields: () => ({
80
+ primaryKey: { type: MetaPrimaryKeyConstraintType },
81
+ unique: { type: nnList(MetaUniqueConstraintType) },
82
+ foreignKey: { type: nnList(MetaForeignKeyConstraintType) },
83
+ }),
84
+ });
85
+ const MetaInflectionType = new GraphQLObjectType({
86
+ name: 'MetaInflection',
87
+ description: 'Table inflection names',
88
+ fields: () => ({
89
+ tableType: { type: nn(GraphQLString) },
90
+ allRows: { type: nn(GraphQLString) },
91
+ connection: { type: nn(GraphQLString) },
92
+ edge: { type: nn(GraphQLString) },
93
+ filterType: { type: GraphQLString },
94
+ orderByType: { type: nn(GraphQLString) },
95
+ conditionType: { type: nn(GraphQLString) },
96
+ patchType: { type: GraphQLString },
97
+ createInputType: { type: nn(GraphQLString) },
98
+ createPayloadType: { type: nn(GraphQLString) },
99
+ updatePayloadType: { type: GraphQLString },
100
+ deletePayloadType: { type: nn(GraphQLString) },
101
+ }),
102
+ });
103
+ const MetaQueryType = new GraphQLObjectType({
104
+ name: 'MetaQuery',
105
+ description: 'Table query/mutation names',
106
+ fields: () => ({
107
+ all: { type: nn(GraphQLString) },
108
+ one: { type: GraphQLString },
109
+ create: { type: GraphQLString },
110
+ update: { type: GraphQLString },
111
+ delete: { type: GraphQLString },
112
+ }),
113
+ });
114
+ const MetaBelongsToRelationType = new GraphQLObjectType({
115
+ name: 'MetaBelongsToRelation',
116
+ description: 'A belongs-to (forward FK) relation',
117
+ fields: () => ({
118
+ fieldName: { type: GraphQLString },
119
+ isUnique: { type: nn(GraphQLBoolean) },
120
+ type: { type: GraphQLString },
121
+ keys: { type: nnList(MetaFieldType) },
122
+ references: { type: nn(MetaRefTableType) },
123
+ }),
124
+ });
125
+ const MetaHasRelationType = new GraphQLObjectType({
126
+ name: 'MetaHasRelation',
127
+ description: 'A has-one or has-many (reverse FK) relation',
128
+ fields: () => ({
129
+ fieldName: { type: GraphQLString },
130
+ isUnique: { type: nn(GraphQLBoolean) },
131
+ type: { type: GraphQLString },
132
+ keys: { type: nnList(MetaFieldType) },
133
+ referencedBy: { type: nn(MetaRefTableType) },
134
+ }),
135
+ });
136
+ const MetaManyToManyRelationType = new GraphQLObjectType({
137
+ name: 'MetaManyToManyRelation',
138
+ description: 'A many-to-many relation via junction table',
139
+ fields: () => ({
140
+ fieldName: { type: GraphQLString },
141
+ type: { type: GraphQLString },
142
+ junctionTable: { type: nn(MetaRefTableType) },
143
+ junctionLeftConstraint: { type: nn(MetaForeignKeyConstraintType) },
144
+ junctionLeftKeyAttributes: { type: nnList(MetaFieldType) },
145
+ junctionRightConstraint: { type: nn(MetaForeignKeyConstraintType) },
146
+ junctionRightKeyAttributes: { type: nnList(MetaFieldType) },
147
+ leftKeyAttributes: { type: nnList(MetaFieldType) },
148
+ rightKeyAttributes: { type: nnList(MetaFieldType) },
149
+ rightTable: { type: nn(MetaRefTableType) },
150
+ }),
151
+ });
152
+ const MetaRelationsType = new GraphQLObjectType({
153
+ name: 'MetaRelations',
154
+ description: 'Table relations',
155
+ fields: () => ({
156
+ belongsTo: { type: nnList(MetaBelongsToRelationType) },
157
+ has: { type: nnList(MetaHasRelationType) },
158
+ hasOne: { type: nnList(MetaHasRelationType) },
159
+ hasMany: { type: nnList(MetaHasRelationType) },
160
+ manyToMany: { type: nnList(MetaManyToManyRelationType) },
161
+ }),
162
+ });
163
+ const MetaTableType = new GraphQLObjectType({
164
+ name: 'MetaTable',
165
+ description: 'Information about a database table',
166
+ fields: () => ({
167
+ name: { type: nn(GraphQLString) },
168
+ schemaName: { type: nn(GraphQLString) },
169
+ fields: { type: nnList(MetaFieldType) },
170
+ indexes: { type: nnList(MetaIndexType) },
171
+ constraints: { type: nn(MetaConstraintsType) },
172
+ foreignKeyConstraints: { type: nnList(MetaForeignKeyConstraintType) },
173
+ primaryKeyConstraints: { type: nnList(MetaPrimaryKeyConstraintType) },
174
+ uniqueConstraints: { type: nnList(MetaUniqueConstraintType) },
175
+ relations: { type: nn(MetaRelationsType) },
176
+ inflection: { type: nn(MetaInflectionType) },
177
+ query: { type: nn(MetaQueryType) },
178
+ }),
179
+ });
180
+ return new GraphQLObjectType({
181
+ name: 'MetaSchema',
182
+ description: 'Root meta schema type',
183
+ fields: () => ({
184
+ tables: { type: nnList(MetaTableType) },
185
+ }),
186
+ });
187
+ }
188
+ export function extendQueryWithMetaField(fields, tablesMeta) {
189
+ const metaSchemaType = createMetaSchemaType();
190
+ const metaField = {
191
+ type: metaSchemaType,
192
+ description: 'Metadata about the database schema, including tables, fields, indexes, and constraints. Useful for code generation tools.',
193
+ resolve() {
194
+ return { tables: tablesMeta };
195
+ },
196
+ };
197
+ return {
198
+ ...fields,
199
+ _meta: metaField,
200
+ };
201
+ }
@@ -0,0 +1,4 @@
1
+ import type { MetaInflection, PgCodec } from './types';
2
+ export declare function safeInflection<T>(fn: () => T, fallback: T): T;
3
+ export declare function createAttributeInflector(inflection: MetaInflection): (attrName: string, codec: PgCodec) => string;
4
+ export declare function fallbackTableType(codecName: string): string;
@@ -0,0 +1,20 @@
1
+ export function safeInflection(fn, fallback) {
2
+ try {
3
+ return fn() ?? fallback;
4
+ }
5
+ catch {
6
+ return fallback;
7
+ }
8
+ }
9
+ export function createAttributeInflector(inflection) {
10
+ return (attrName, codec) => {
11
+ const attributeName = safeInflection(() => inflection._attributeName?.({ attributeName: attrName, codec }), attrName);
12
+ return safeInflection(() => inflection.camelCase?.(attributeName), attributeName);
13
+ };
14
+ }
15
+ export function fallbackTableType(codecName) {
16
+ return codecName
17
+ .replace(/_/g, ' ')
18
+ .replace(/\b\w/g, (char) => char.toUpperCase())
19
+ .replace(/ /g, '');
20
+ }