gtx-cli 2.5.48 → 2.5.49

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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # gtx-cli
2
2
 
3
+ ## 2.5.49
4
+
5
+ ### Patch Changes
6
+
7
+ - [#953](https://github.com/generaltranslation/gt/pull/953) [`4fca112`](https://github.com/generaltranslation/gt/commit/4fca1123d797883b8ad73a770ad177b5068a4707) Thanks [@fernando-aviles](https://github.com/fernando-aviles)! - Add handling for OpenAPI configurations via Mintlify's `docs.json`
8
+
3
9
  ## 2.5.48
4
10
 
5
11
  ### Patch Changes
@@ -1 +1 @@
1
- export declare const PACKAGE_VERSION = "2.5.48";
1
+ export declare const PACKAGE_VERSION = "2.5.49";
@@ -1,2 +1,2 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
- export const PACKAGE_VERSION = '2.5.48';
2
+ export const PACKAGE_VERSION = '2.5.49';
@@ -72,10 +72,194 @@ export default async function processOpenApi(settings, includeFiles) {
72
72
  }
73
73
  }
74
74
  }
75
+ const docsJsonTargets = collectDocsJsonTargets(settings, fileMapping, includeFiles);
76
+ for (const target of docsJsonTargets) {
77
+ if (!fs.existsSync(target.path))
78
+ continue;
79
+ const content = fs.readFileSync(target.path, 'utf8');
80
+ const updated = rewriteDocsJsonOpenApi(content, target.path, target.localeHint, specAnalyses, fileMappingAbs, fileMapping, warnings, configDir, settings.defaultLocale);
81
+ if (updated?.changed) {
82
+ await fs.promises.writeFile(target.path, updated.content, 'utf8');
83
+ }
84
+ }
75
85
  for (const message of warnings) {
76
86
  logger.warn(message);
77
87
  }
78
88
  }
89
+ function collectDocsJsonTargets(settings, fileMapping, includeFiles) {
90
+ const targets = [];
91
+ const seen = new Map();
92
+ const addTarget = (filePath, locale) => {
93
+ const canonicalPath = path.resolve(filePath);
94
+ if (includeFiles &&
95
+ !includeFiles.has(filePath) &&
96
+ !includeFiles.has(canonicalPath)) {
97
+ return;
98
+ }
99
+ const locales = seen.get(canonicalPath) ?? new Set();
100
+ if (locale)
101
+ locales.add(locale);
102
+ seen.set(canonicalPath, locales);
103
+ };
104
+ if (!includeFiles && settings.files?.resolvedPaths.json) {
105
+ for (const filePath of settings.files.resolvedPaths.json) {
106
+ addTarget(filePath, settings.defaultLocale);
107
+ }
108
+ }
109
+ for (const [locale, filesMap] of Object.entries(fileMapping)) {
110
+ for (const filePath of Object.values(filesMap)) {
111
+ if (!filePath.endsWith('.json'))
112
+ continue;
113
+ addTarget(filePath, locale);
114
+ }
115
+ }
116
+ for (const [filePath, locales] of seen.entries()) {
117
+ const localeHint = locales.size === 1 ? Array.from(locales)[0] : undefined;
118
+ targets.push({ path: filePath, localeHint });
119
+ }
120
+ return targets;
121
+ }
122
+ function isMintlifyDocsJson(filePath, json) {
123
+ if (!isRecord(json))
124
+ return false;
125
+ const schema = json.$schema;
126
+ if (typeof schema === 'string') {
127
+ return (schema.includes('mintlify.com/docs.json') ||
128
+ schema.includes('mintlify.com/mint.json'));
129
+ }
130
+ const base = path.basename(filePath);
131
+ return base === 'docs.json' || base === 'mint.json';
132
+ }
133
+ function rewriteDocsJsonOpenApi(content, filePath, localeHint, specs, fileMappingAbs, fileMappingRel, warnings, configDir, defaultLocale) {
134
+ let json;
135
+ try {
136
+ json = JSON.parse(content);
137
+ }
138
+ catch {
139
+ return null;
140
+ }
141
+ if (!isMintlifyDocsJson(filePath, json))
142
+ return null;
143
+ let changed = false;
144
+ const visitNode = (node, activeLocale) => {
145
+ if (Array.isArray(node)) {
146
+ node.forEach((item) => visitNode(item, activeLocale));
147
+ return;
148
+ }
149
+ if (!isRecord(node))
150
+ return;
151
+ let nextLocale = activeLocale;
152
+ if (typeof node.language === 'string') {
153
+ nextLocale = node.language;
154
+ }
155
+ if (isRecord(node.openapi) && Array.isArray(node.pages)) {
156
+ const locale = nextLocale || localeHint || defaultLocale;
157
+ const openapiConfig = node.openapi;
158
+ const sourceValue = openapiConfig.source;
159
+ if (typeof sourceValue === 'string') {
160
+ const localizedSource = localizeDocsJsonSpecPath(sourceValue, locale, filePath, specs, fileMappingAbs, fileMappingRel, warnings, configDir);
161
+ if (localizedSource && localizedSource !== sourceValue) {
162
+ openapiConfig.source = localizedSource;
163
+ changed = true;
164
+ }
165
+ }
166
+ const pages = node.pages;
167
+ for (let i = 0; i < pages.length; i += 1) {
168
+ const page = pages[i];
169
+ if (typeof page !== 'string')
170
+ continue;
171
+ const updated = stripLocaleFromOpenApiPage(page, locale);
172
+ if (updated !== page) {
173
+ pages[i] = updated;
174
+ changed = true;
175
+ }
176
+ }
177
+ }
178
+ for (const value of Object.values(node)) {
179
+ visitNode(value, nextLocale);
180
+ }
181
+ };
182
+ visitNode(json, undefined);
183
+ if (!changed)
184
+ return null;
185
+ return { changed, content: JSON.stringify(json, null, 2) };
186
+ }
187
+ function stripLocaleFromOpenApiPage(value, locale) {
188
+ const trimmed = value.trim();
189
+ const prefix = `${locale}/`;
190
+ if (!trimmed.startsWith(prefix))
191
+ return value;
192
+ const candidate = trimmed.slice(prefix.length);
193
+ const parsed = parseOpenApiValue(candidate);
194
+ if (!parsed)
195
+ return value;
196
+ return candidate;
197
+ }
198
+ function localizeDocsJsonSpecPath(source, locale, filePath, specs, fileMappingAbs, _fileMappingRel, warnings, configDir) {
199
+ const resolvedAbs = resolveDocsJsonSpecPath(source, filePath, configDir);
200
+ const matched = matchSpecBySource(source, resolvedAbs, specs, warnings);
201
+ if (!matched)
202
+ return source;
203
+ const localizedSpecPath = resolveLocalizedSpecPath(matched, locale, fileMappingAbs, configDir, source);
204
+ const localizedAbs = resolveDocsJsonSpecPath(localizedSpecPath, filePath, configDir);
205
+ if (!fs.existsSync(localizedAbs)) {
206
+ warnings.add(`OpenAPI source "${source}" localized for locale "${locale}" points to a missing file (${localizedAbs}). Keeping original source.`);
207
+ return source;
208
+ }
209
+ const rel = normalizeSlashes(path.relative(configDir, localizedAbs));
210
+ return formatSpecPathForDocsJson(rel, source);
211
+ }
212
+ function resolveDocsJsonSpecPath(source, filePath, configDir) {
213
+ if (source.startsWith('/')) {
214
+ return path.resolve(configDir, source.replace(/^\/+/, ''));
215
+ }
216
+ if (source.startsWith('./') || source.startsWith('../')) {
217
+ return path.resolve(path.dirname(filePath), source);
218
+ }
219
+ return path.resolve(configDir, source);
220
+ }
221
+ function matchSpecBySource(source, resolvedAbs, specs, warnings) {
222
+ const exact = specs.find((spec) => samePath(resolvedAbs, spec.absPath));
223
+ if (exact)
224
+ return exact;
225
+ const normalizedExplicit = normalizeSlashes(source).replace(/^\.?\/+/, '');
226
+ const explicitWithoutExt = stripExtension(normalizedExplicit);
227
+ const explicitBase = path.basename(normalizedExplicit);
228
+ const explicitBaseWithoutExt = stripExtension(explicitBase);
229
+ const matches = specs.filter((spec) => {
230
+ const configPath = normalizeSlashes(spec.configPath).replace(/^\.?\/+/, '');
231
+ const configBase = path.basename(configPath);
232
+ const configPathNoExt = stripExtension(configPath);
233
+ const configBaseNoExt = stripExtension(configBase);
234
+ return (configPath === normalizedExplicit ||
235
+ configPathNoExt === explicitWithoutExt ||
236
+ configBase === explicitBase ||
237
+ configBaseNoExt === explicitBaseWithoutExt);
238
+ });
239
+ if (matches.length === 1)
240
+ return matches[0];
241
+ if (matches.length > 1) {
242
+ warnings.add(`OpenAPI source "${source}" matches multiple specs (${matches
243
+ .map((m) => m.configPath)
244
+ .join(', ')}). Using the first configured match (${matches[0].configPath}).`);
245
+ return matches[0];
246
+ }
247
+ return null;
248
+ }
249
+ function formatSpecPathForDocsJson(relativePath, originalPathText) {
250
+ const normalized = normalizeSlashes(relativePath);
251
+ const base = normalized.replace(/^\.\//, '').replace(/\/+/g, '/');
252
+ if (originalPathText.startsWith('/')) {
253
+ return `/${base.replace(/^\/+/, '')}`;
254
+ }
255
+ if (originalPathText.startsWith('../')) {
256
+ return normalized;
257
+ }
258
+ if (originalPathText.startsWith('./')) {
259
+ return `./${base.replace(/^\/+/, '')}`;
260
+ }
261
+ return base.replace(/^\/+/, '');
262
+ }
79
263
  /**
80
264
  * Resolve configured OpenAPI files to absolute paths and collect the operations,
81
265
  * schemas, and webhooks they expose. Warns and skips when files are missing,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gtx-cli",
3
- "version": "2.5.48",
3
+ "version": "2.5.49",
4
4
  "main": "dist/index.js",
5
5
  "bin": "dist/main.js",
6
6
  "files": [