graphile-i18n 1.1.1 → 1.2.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/esm/plugin.js CHANGED
@@ -1,147 +1,284 @@
1
- import { makeLanguageDataLoaderForTable } from './middleware';
2
- const defaultLanguageLoaderFactory = makeLanguageDataLoaderForTable();
3
- const escapeIdentifier = (str) => `"${str.replace(/"/g, '""')}"`;
4
- const hasI18nTag = (table) => Object.prototype.hasOwnProperty.call(table.tags, 'i18n');
5
- const getPrimaryKeyInfo = (table) => {
6
- const identifier = table.primaryKeyConstraint?.keyAttributes?.[0]?.name;
7
- const idType = table.primaryKeyConstraint?.keyAttributes?.[0]?.type?.name;
8
- return { identifier, idType };
9
- };
10
- export const LangPlugin = (builder, options) => {
11
- const { langPluginLanguageCodeColumn = 'lang_code', langPluginLanguageCodeGqlField = 'langCode', langPluginAllowedTypes = ['citext', 'text'], langPluginDefaultLanguages = ['en'] } = options;
12
- builder.hook('build', (build) => {
13
- const introspection = build.pgIntrospectionResultsByKind;
14
- const inflection = build.inflection;
15
- const tablesWithLanguageTables = introspection.class.filter(hasI18nTag);
16
- const tablesWithLanguageTablesIdInfo = tablesWithLanguageTables.reduce((memo, table) => {
17
- const keyInfo = getPrimaryKeyInfo(table);
18
- if (table.tags.i18n) {
19
- memo[table.tags.i18n] = keyInfo;
20
- }
21
- return memo;
22
- }, {});
23
- const languageVariationTables = tablesWithLanguageTables.map((table) => introspection.class.find((candidate) => candidate.name === table.tags.i18n &&
24
- candidate.namespaceName === table.namespaceName));
25
- const i18nTables = {};
26
- const tables = {};
27
- tablesWithLanguageTables.forEach((table) => {
28
- const i18nTableName = table.tags.i18n;
29
- i18nTables[i18nTableName] = {
30
- table: table.name,
31
- key: null,
32
- connection: inflection.connection(inflection.tableType(table)),
33
- attrs: {},
34
- fields: {},
35
- keyInfo: tablesWithLanguageTablesIdInfo[i18nTableName] ?? {}
36
- };
37
- tables[table.name] = i18nTableName;
38
- });
39
- languageVariationTables.forEach((table) => {
40
- if (!table)
41
- return;
42
- const foreignConstraintsThatMatter = table.constraints
43
- .filter((constraint) => constraint.type === 'f')
44
- .filter((constraint) => constraint.foreignClass.name === i18nTables[table.name].table);
45
- if (foreignConstraintsThatMatter.length !== 1) {
46
- return;
47
- }
48
- const foreignKeyConstraint = foreignConstraintsThatMatter[0];
49
- if (!foreignKeyConstraint || foreignKeyConstraint.keyAttributes.length !== 1) {
50
- return;
51
- }
52
- i18nTables[table.name].key = foreignKeyConstraint.keyAttributes[0].name;
53
- const { identifier } = i18nTables[table.name].keyInfo;
54
- if (!identifier)
55
- return;
56
- table.attributes.forEach((attr) => {
57
- if ([langPluginLanguageCodeColumn, identifier].includes(attr.name))
58
- return;
59
- if (langPluginAllowedTypes.includes(attr.type.name)) {
60
- i18nTables[table.name].fields[inflection.column(attr)] = {
61
- type: attr.type.name,
62
- attr: attr.name,
63
- isNotNull: attr.isNotNull,
64
- column: inflection.column(attr)
1
+ /**
2
+ * PostGraphile v5 i18n Plugin
3
+ *
4
+ * Discovers tables tagged with @i18n and adds a `localeStrings` field to the
5
+ * base type. The field resolves the best-matching translation row based on
6
+ * language codes provided in the GraphQL context, falling back to the base
7
+ * table's own values when no translation exists.
8
+ *
9
+ * Smart tag format:
10
+ * COMMENT ON TABLE app_public.posts IS E'@i18n posts_translations';
11
+ *
12
+ * The value of @i18n is the name of the translation table in the same schema.
13
+ * The translation table must have:
14
+ * - A FK column referencing the base table's PK
15
+ * - A lang_code column (configurable)
16
+ * - UNIQUE(fk_column, lang_code)
17
+ * - One or more text/citext columns matching the base table's columns
18
+ */
19
+ import 'graphile-build';
20
+ import 'graphile-build-pg';
21
+ import { TYPES } from '@dataplan/pg';
22
+ import { context as grafastContext, lambda, object } from 'grafast';
23
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
24
+ function hasI18nTag(codec) {
25
+ const tags = codec.extensions?.tags;
26
+ if (!tags)
27
+ return false;
28
+ const val = tags.i18n;
29
+ if (typeof val === 'string' && val.length > 0)
30
+ return val;
31
+ return false;
32
+ }
33
+ function resolvePgTypeName(codec) {
34
+ if (codec === TYPES.uuid)
35
+ return 'uuid';
36
+ if (codec === TYPES.int)
37
+ return 'int4';
38
+ if (codec === TYPES.bigint)
39
+ return 'int8';
40
+ if (codec === TYPES.text)
41
+ return 'text';
42
+ if (codec === TYPES.varchar)
43
+ return 'text';
44
+ return codec?.name ?? 'text';
45
+ }
46
+ function resolveAttrPgType(codec) {
47
+ if (codec === TYPES.text)
48
+ return 'text';
49
+ if (codec === TYPES.varchar)
50
+ return 'text';
51
+ if (codec?.name === 'citext')
52
+ return 'citext';
53
+ return codec?.name ?? 'text';
54
+ }
55
+ // ─── Plugin Factory ──────────────────────────────────────────────────────────
56
+ export function createI18nPlugin(options = {}) {
57
+ const { langCodeColumn = 'lang_code', langCodeGqlField = 'langCode', allowedTypes = ['text', 'citext'], defaultLanguages = ['en'], } = options;
58
+ // Closure-scoped state shared between init and field hooks
59
+ let i18nRegistry = {};
60
+ const localeTypeCache = {};
61
+ return {
62
+ name: 'I18nPlugin',
63
+ version: '1.0.0',
64
+ schema: {
65
+ hooks: {
66
+ init: {
67
+ callback(_, build) {
68
+ i18nRegistry = {};
69
+ for (const [, codec] of Object.entries(build.input.pgRegistry.pgCodecs)) {
70
+ const c = codec;
71
+ if (!c.attributes)
72
+ continue;
73
+ const translationTableName = hasI18nTag(c);
74
+ if (!translationTableName)
75
+ continue;
76
+ // Get schema name from the codec's pg extensions
77
+ let schemaName = c.extensions?.pg?.schemaName ?? 'public';
78
+ let pkColumn = null;
79
+ let pkType = 'text';
80
+ for (const [, resource] of Object.entries(build.input.pgRegistry.pgResources)) {
81
+ const r = resource;
82
+ if (r.codec === c) {
83
+ // Try multiple sources for schema name
84
+ const rSchema = r.extensions?.pg?.schemaName ?? r.schemaName;
85
+ if (rSchema)
86
+ schemaName = rSchema;
87
+ // Extract PK from the resource's uniques array
88
+ const uniques = r.uniques;
89
+ if (uniques) {
90
+ const pk = uniques.find((u) => u.isPrimary);
91
+ if (pk && pk.attributes.length === 1) {
92
+ pkColumn = pk.attributes[0];
93
+ const pkAttr = c.attributes[pkColumn];
94
+ if (pkAttr) {
95
+ pkType = resolvePgTypeName(pkAttr.codec);
96
+ }
97
+ }
98
+ }
99
+ break;
100
+ }
101
+ }
102
+ if (!pkColumn)
103
+ continue;
104
+ // Find the translation codec. The @i18n tag value is the SQL table name
105
+ // (e.g. 'posts_translations'), but PostGraphile inflects codec names
106
+ // to camelCase (e.g. 'postsTranslations'). Match via resource name.
107
+ let translationCodec = null;
108
+ for (const [, resource] of Object.entries(build.input.pgRegistry.pgResources)) {
109
+ const r = resource;
110
+ if (!r.codec?.attributes)
111
+ continue;
112
+ // Match by the resource's SQL name (which preserves snake_case)
113
+ const sqlName = r.codec?.extensions?.pg?.name ?? r.name;
114
+ if (sqlName === translationTableName) {
115
+ translationCodec = r.codec;
116
+ break;
117
+ }
118
+ }
119
+ // Fallback: try matching the inflected codec name directly
120
+ if (!translationCodec) {
121
+ const inflectedName = build.inflection.camelCase(translationTableName);
122
+ for (const [, tCodec] of Object.entries(build.input.pgRegistry.pgCodecs)) {
123
+ const tc = tCodec;
124
+ if (!tc.attributes)
125
+ continue;
126
+ if (tc.name === translationTableName || tc.name === inflectedName) {
127
+ translationCodec = tc;
128
+ break;
129
+ }
130
+ }
131
+ }
132
+ if (!translationCodec)
133
+ continue;
134
+ // Find FK column on translation table — convention first, then type match
135
+ let fkColumn = null;
136
+ const conventionalFk = `${c.name}_id`;
137
+ if (translationCodec.attributes[conventionalFk]) {
138
+ fkColumn = conventionalFk;
139
+ }
140
+ if (!fkColumn) {
141
+ // Fallback: find a column with the same type as the PK, excluding
142
+ // common non-FK columns (id, lang_code)
143
+ for (const [attrName, attr] of Object.entries(translationCodec.attributes)) {
144
+ if (attrName === 'id' || attrName === langCodeColumn)
145
+ continue;
146
+ const a = attr;
147
+ if (a.codec === c.attributes[pkColumn].codec) {
148
+ fkColumn = attrName;
149
+ break;
150
+ }
151
+ }
152
+ }
153
+ if (!fkColumn)
154
+ continue;
155
+ // Discover translatable fields
156
+ const fields = {};
157
+ for (const [attrName, attr] of Object.entries(translationCodec.attributes)) {
158
+ if (attrName === langCodeColumn || attrName === fkColumn)
159
+ continue;
160
+ if (attrName === 'id' || attrName === 'created_at' || attrName === 'updated_at')
161
+ continue;
162
+ const pgType = resolveAttrPgType(attr.codec);
163
+ if (!allowedTypes.includes(pgType))
164
+ continue;
165
+ const gqlName = build.inflection.camelCase(attrName);
166
+ fields[gqlName] = {
167
+ column: attrName,
168
+ type: pgType,
169
+ isNotNull: !!attr.notNull,
170
+ };
171
+ }
172
+ if (Object.keys(fields).length === 0)
173
+ continue;
174
+ i18nRegistry[c.name] = {
175
+ baseTable: c.name,
176
+ translationTable: translationTableName,
177
+ schemaName,
178
+ fkColumn,
179
+ pkColumn,
180
+ pkType,
181
+ fields,
182
+ };
183
+ }
184
+ return _;
185
+ },
186
+ },
187
+ GraphQLObjectType_fields(fields, build, context) {
188
+ const { graphql: { GraphQLString, GraphQLObjectType, GraphQLNonNull } } = build;
189
+ const { scope } = context;
190
+ if (!scope.pgCodec || !scope.isPgClassType)
191
+ return fields;
192
+ const codec = scope.pgCodec;
193
+ const info = i18nRegistry[codec.name];
194
+ if (!info)
195
+ return fields;
196
+ const localeFieldsConfig = {
197
+ [langCodeGqlField]: { type: GraphQLString },
65
198
  };
66
- i18nTables[table.name].attrs[attr.name] = {
67
- type: attr.type.name,
68
- attr: attr.name,
69
- column: inflection.column(attr)
70
- };
71
- }
72
- });
73
- });
74
- return build.extend(build, { i18n: { i18nTables, tables } });
75
- });
76
- builder.hook('GraphQLObjectType:fields', (fields, build, context) => {
77
- const { graphql: { GraphQLString, GraphQLObjectType, GraphQLNonNull }, i18n: { i18nTables, tables } } = build;
78
- const { scope: { pgIntrospection: table, isPgRowType }, fieldWithHooks } = context;
79
- if (!isPgRowType || !table || table.kind !== 'class') {
80
- return fields;
81
- }
82
- const variationsTableName = tables[table.name];
83
- if (!variationsTableName) {
84
- return fields;
85
- }
86
- const i18nTable = i18nTables[variationsTableName];
87
- const { identifier, idType } = i18nTable.keyInfo;
88
- if (!identifier || !idType) {
89
- return fields;
90
- }
91
- const { key, fields: i18nFields } = i18nTable;
92
- const localeFieldName = 'localeStrings';
93
- const localeFieldsConfig = Object.keys(i18nFields).reduce((memo, field) => {
94
- memo[field] = {
95
- type: i18nFields[field].isNotNull
96
- ? new GraphQLNonNull(GraphQLString)
97
- : GraphQLString,
98
- description: `Locale for ${field}`
99
- };
100
- return memo;
101
- }, {
102
- langCode: {
103
- type: GraphQLString
104
- }
105
- });
106
- const localeFieldsType = new GraphQLObjectType({
107
- name: `${context.Self.name}LocaleStrings`,
108
- description: `Locales for ${context.Self.name}`,
109
- fields: localeFieldsConfig
110
- });
111
- return build.extend(fields, {
112
- [localeFieldName]: fieldWithHooks(localeFieldName, (fieldContext) => {
113
- const { addDataGenerator } = fieldContext;
114
- addDataGenerator(() => ({
115
- pgQuery: (queryBuilder) => {
116
- queryBuilder.select(build.pgSql.fragment `${queryBuilder.getTableAlias()}.${build.pgSql.identifier(identifier)}`, identifier);
199
+ for (const [gqlName, field] of Object.entries(info.fields)) {
200
+ localeFieldsConfig[gqlName] = {
201
+ type: field.isNotNull ? new GraphQLNonNull(GraphQLString) : GraphQLString,
202
+ };
117
203
  }
118
- }));
119
- const coalescedFields = Object.keys(i18nFields).map((field) => {
120
- const columnName = i18nFields[field].attr;
121
- const escColumnName = escapeIdentifier(columnName);
122
- const escFieldName = escapeIdentifier(field);
123
- return `coalesce(v.${escColumnName}, b.${escColumnName}) as ${escFieldName}`;
124
- });
125
- const props = {
126
- table,
127
- coalescedFields,
128
- variationsTableName,
129
- key
130
- };
131
- return {
132
- description: `Locales for ${context.Self.name}`,
133
- type: new GraphQLNonNull(localeFieldsType),
134
- async resolve(source, _args, gqlContext) {
135
- const languageCodes = gqlContext.langCodes ?? langPluginDefaultLanguages;
136
- const getLoader = gqlContext.getLanguageDataLoader ??
137
- defaultLanguageLoaderFactory;
138
- const dataloader = getLoader(props, gqlContext.pgClient, languageCodes, identifier, idType, langPluginLanguageCodeColumn, langPluginLanguageCodeGqlField);
139
- return dataloader.load(source.id);
204
+ const localeTypeName = `${build.inflection.tableType(codec)}LocaleStrings`;
205
+ if (!localeTypeCache[localeTypeName]) {
206
+ localeTypeCache[localeTypeName] = new GraphQLObjectType({
207
+ name: localeTypeName,
208
+ fields: localeFieldsConfig,
209
+ });
140
210
  }
141
- };
142
- }, 'Adding the language code field from the lang plugin')
143
- });
144
- });
145
- };
146
- export { additionalGraphQLContextFromRequest, makeLanguageDataLoaderForTable } from './middleware';
147
- export default LangPlugin;
211
+ const localeType = localeTypeCache[localeTypeName];
212
+ const { schemaName, baseTable, translationTable, fkColumn, pkColumn, pkType, fields: i18nFields } = info;
213
+ const coalescedCols = Object.values(i18nFields)
214
+ .map(f => `coalesce(v."${f.column}", b."${f.column}") as "${f.column}"`)
215
+ .join(', ');
216
+ // Build the SQL query template
217
+ const sqlQuery = `SELECT v."${langCodeColumn}" AS "lang_code", ${coalescedCols}
218
+ FROM "${schemaName}"."${baseTable}" b
219
+ LEFT JOIN "${schemaName}"."${translationTable}" v
220
+ ON v."${fkColumn}" = b."${pkColumn}"
221
+ AND array_position($2::text[], v."${langCodeColumn}") IS NOT NULL
222
+ WHERE b."${pkColumn}" = $1::${pkType}
223
+ ORDER BY array_position($2::text[], v."${langCodeColumn}") ASC NULLS LAST
224
+ LIMIT 1`;
225
+ // Build column names list for mapping base values
226
+ const baseColNames = Object.entries(i18nFields).map(([gqlName, f]) => ({
227
+ gqlName,
228
+ column: f.column,
229
+ }));
230
+ return build.extend(fields, {
231
+ localeStrings: {
232
+ type: new GraphQLNonNull(localeType),
233
+ plan($parent) {
234
+ // Extract PK and all base translatable columns from the parent row
235
+ const $id = $parent.get(pkColumn);
236
+ const $baseCols = {};
237
+ for (const { column } of baseColNames) {
238
+ $baseCols[column] = $parent.get(column);
239
+ }
240
+ const $withPgClient = grafastContext().get('withPgClient');
241
+ const $langCodes = grafastContext().get('langCodes');
242
+ // Combine all inputs into a single step
243
+ const $input = object({
244
+ id: $id,
245
+ withPgClient: $withPgClient,
246
+ langCodes: $langCodes,
247
+ ...$baseCols,
248
+ });
249
+ return lambda($input, async (input) => {
250
+ const { id, withPgClient, langCodes: ctxLangCodes, ...baseCols } = input;
251
+ const langs = ctxLangCodes ?? defaultLanguages;
252
+ if (!withPgClient || !id) {
253
+ const result = { [langCodeGqlField]: null };
254
+ for (const { gqlName, column } of baseColNames) {
255
+ result[gqlName] = baseCols[column] ?? null;
256
+ }
257
+ return result;
258
+ }
259
+ const row = await withPgClient(null, async (client) => {
260
+ const { rows } = await client.query(sqlQuery, [id, langs]);
261
+ return rows[0] ?? null;
262
+ });
263
+ if (!row) {
264
+ const result = { [langCodeGqlField]: null };
265
+ for (const { gqlName, column } of baseColNames) {
266
+ result[gqlName] = baseCols[column] ?? null;
267
+ }
268
+ return result;
269
+ }
270
+ const result = { [langCodeGqlField]: row.lang_code };
271
+ for (const { gqlName, column } of baseColNames) {
272
+ result[gqlName] = row[column] ?? null;
273
+ }
274
+ return result;
275
+ });
276
+ },
277
+ },
278
+ }, 'Adding i18n localeStrings field');
279
+ },
280
+ },
281
+ },
282
+ };
283
+ }
284
+ export const I18nPlugin = createI18nPlugin();
@@ -0,0 +1,34 @@
1
+ /**
2
+ * PostGraphile v5 i18n Preset
3
+ *
4
+ * Convenience preset that bundles the i18n plugin with configurable options.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { I18nPreset } from 'graphile-i18n';
9
+ *
10
+ * const preset = {
11
+ * extends: [
12
+ * I18nPreset(),
13
+ * ],
14
+ * };
15
+ * ```
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { I18nPreset } from 'graphile-i18n';
20
+ *
21
+ * const preset = {
22
+ * extends: [
23
+ * I18nPreset({
24
+ * defaultLanguages: ['en', 'es'],
25
+ * langCodeColumn: 'lang_code',
26
+ * }),
27
+ * ],
28
+ * };
29
+ * ```
30
+ */
31
+ import type { GraphileConfig } from 'graphile-config';
32
+ import type { I18nPluginOptions } from './types';
33
+ export declare function I18nPreset(options?: I18nPluginOptions): GraphileConfig.Preset;
34
+ export default I18nPreset;
package/esm/preset.js ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * PostGraphile v5 i18n Preset
3
+ *
4
+ * Convenience preset that bundles the i18n plugin with configurable options.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { I18nPreset } from 'graphile-i18n';
9
+ *
10
+ * const preset = {
11
+ * extends: [
12
+ * I18nPreset(),
13
+ * ],
14
+ * };
15
+ * ```
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { I18nPreset } from 'graphile-i18n';
20
+ *
21
+ * const preset = {
22
+ * extends: [
23
+ * I18nPreset({
24
+ * defaultLanguages: ['en', 'es'],
25
+ * langCodeColumn: 'lang_code',
26
+ * }),
27
+ * ],
28
+ * };
29
+ * ```
30
+ */
31
+ import { createI18nPlugin } from './plugin';
32
+ export function I18nPreset(options = {}) {
33
+ return {
34
+ plugins: [createI18nPlugin(options)],
35
+ };
36
+ }
37
+ export default I18nPreset;
package/esm/types.d.ts ADDED
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Types for the graphile-i18n v5 plugin.
3
+ */
4
+ export interface I18nPluginOptions {
5
+ /**
6
+ * Column name on the translation table that stores the language code.
7
+ * @default 'lang_code'
8
+ */
9
+ langCodeColumn?: string;
10
+ /**
11
+ * GraphQL field name for the language code in the locale object.
12
+ * @default 'langCode'
13
+ */
14
+ langCodeGqlField?: string;
15
+ /**
16
+ * Column types eligible for translation overlay.
17
+ * @default ['text', 'citext']
18
+ */
19
+ allowedTypes?: string[];
20
+ /**
21
+ * Fallback language codes when no Accept-Language header is provided.
22
+ * @default ['en']
23
+ */
24
+ defaultLanguages?: string[];
25
+ }
26
+ export interface I18nTableInfo {
27
+ /** Base table name */
28
+ baseTable: string;
29
+ /** Translation table name */
30
+ translationTable: string;
31
+ /** Schema name */
32
+ schemaName: string;
33
+ /** FK column on the translation table referencing the base table PK */
34
+ fkColumn: string;
35
+ /** Base table PK column name */
36
+ pkColumn: string;
37
+ /** Base table PK PostgreSQL type */
38
+ pkType: string;
39
+ /** Translatable field mappings: { gqlFieldName: { column, type, isNotNull } } */
40
+ fields: Record<string, TranslatableField>;
41
+ }
42
+ export interface TranslatableField {
43
+ column: string;
44
+ type: string;
45
+ isNotNull: boolean;
46
+ }
package/esm/types.js ADDED
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Types for the graphile-i18n v5 plugin.
3
+ */
4
+ export {};
package/index.d.ts CHANGED
@@ -1,5 +1,37 @@
1
- import LangPlugin from './plugin';
2
- export { LangPlugin, type LangPluginOptions } from './plugin';
3
- export { additionalGraphQLContextFromRequest, makeLanguageDataLoaderForTable, type I18nGraphQLContext, type I18nRequestLike, type LanguageDataLoaderFactory } from './middleware';
4
- export { env } from './env';
5
- export default LangPlugin;
1
+ /**
2
+ * graphile-i18n PostGraphile v5 i18n Plugin
3
+ *
4
+ * Language-aware fields sourced from @i18n translation tables
5
+ * with Accept-Language negotiation and configurable fallback chains.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { I18nPreset } from 'graphile-i18n';
10
+ *
11
+ * const preset = {
12
+ * extends: [
13
+ * I18nPreset(),
14
+ * ],
15
+ * };
16
+ * ```
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import { I18nPreset } from 'graphile-i18n';
21
+ *
22
+ * const preset = {
23
+ * extends: [
24
+ * I18nPreset({
25
+ * defaultLanguages: ['en', 'es'],
26
+ * langCodeColumn: 'lang_code',
27
+ * allowedTypes: ['text', 'citext'],
28
+ * }),
29
+ * ],
30
+ * };
31
+ * ```
32
+ */
33
+ export { createI18nPlugin, I18nPlugin } from './plugin';
34
+ export { I18nPreset } from './preset';
35
+ export { makeI18nContext, additionalGraphQLContextFromRequest } from './middleware';
36
+ export type { I18nPluginOptions, I18nTableInfo, TranslatableField } from './types';
37
+ export type { I18nMiddlewareOptions } from './middleware';
package/index.js CHANGED
@@ -1,15 +1,46 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
2
+ /**
3
+ * graphile-i18n PostGraphile v5 i18n Plugin
4
+ *
5
+ * Language-aware fields sourced from @i18n translation tables
6
+ * with Accept-Language negotiation and configurable fallback chains.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { I18nPreset } from 'graphile-i18n';
11
+ *
12
+ * const preset = {
13
+ * extends: [
14
+ * I18nPreset(),
15
+ * ],
16
+ * };
17
+ * ```
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * import { I18nPreset } from 'graphile-i18n';
22
+ *
23
+ * const preset = {
24
+ * extends: [
25
+ * I18nPreset({
26
+ * defaultLanguages: ['en', 'es'],
27
+ * langCodeColumn: 'lang_code',
28
+ * allowedTypes: ['text', 'citext'],
29
+ * }),
30
+ * ],
31
+ * };
32
+ * ```
33
+ */
5
34
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.env = exports.makeLanguageDataLoaderForTable = exports.additionalGraphQLContextFromRequest = exports.LangPlugin = void 0;
7
- const plugin_1 = __importDefault(require("./plugin"));
8
- var plugin_2 = require("./plugin");
9
- Object.defineProperty(exports, "LangPlugin", { enumerable: true, get: function () { return plugin_2.LangPlugin; } });
35
+ exports.additionalGraphQLContextFromRequest = exports.makeI18nContext = exports.I18nPreset = exports.I18nPlugin = exports.createI18nPlugin = void 0;
36
+ // Plugin
37
+ var plugin_1 = require("./plugin");
38
+ Object.defineProperty(exports, "createI18nPlugin", { enumerable: true, get: function () { return plugin_1.createI18nPlugin; } });
39
+ Object.defineProperty(exports, "I18nPlugin", { enumerable: true, get: function () { return plugin_1.I18nPlugin; } });
40
+ // Preset
41
+ var preset_1 = require("./preset");
42
+ Object.defineProperty(exports, "I18nPreset", { enumerable: true, get: function () { return preset_1.I18nPreset; } });
43
+ // Middleware
10
44
  var middleware_1 = require("./middleware");
45
+ Object.defineProperty(exports, "makeI18nContext", { enumerable: true, get: function () { return middleware_1.makeI18nContext; } });
11
46
  Object.defineProperty(exports, "additionalGraphQLContextFromRequest", { enumerable: true, get: function () { return middleware_1.additionalGraphQLContextFromRequest; } });
12
- Object.defineProperty(exports, "makeLanguageDataLoaderForTable", { enumerable: true, get: function () { return middleware_1.makeLanguageDataLoaderForTable; } });
13
- var env_1 = require("./env");
14
- Object.defineProperty(exports, "env", { enumerable: true, get: function () { return env_1.env; } });
15
- exports.default = plugin_1.default;