@thallylabs/migrate 0.2.24 → 0.2.25

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/README.md CHANGED
@@ -171,6 +171,54 @@ from Fern MDX pages (`../../components/...`, resolved against the Fern
171
171
  project root) are migrated the same way Mintlify and Docusaurus components
172
172
  are — see "Mintlify compatibility" below.
173
173
 
174
+ ## Mintlify `sourceRef` repositories
175
+
176
+ A Mintlify navigation node `{"sourceRef": "owner/repo"}` mounts the docs of another
177
+ repository. Mintlify does not document where that mount lives, so it cannot be
178
+ derived from `docs.json`; supply it with a repeatable flag:
179
+
180
+ ```bash
181
+ create-thally-docs migrate https://github.com/OpenRouterTeam/docs my-docs \
182
+ --source-ref OpenRouterTeam/typescript-sdk=client-sdks/typescript
183
+ ```
184
+
185
+ Without a mapping the node is skipped with a warning that names the flag.
186
+
187
+ - The repository must be on github.com. Its docs root is the directory holding a
188
+ Mintlify `docs.json` (`docs/` first, then the repository root); without one the
189
+ mapping is skipped with a warning.
190
+ - Every `.md`/`.mdx` file under that root is imported (orphans included) through the
191
+ same pipeline as the main site, under `<mount-path>/...`. Assets are copied under
192
+ `public/<mount-path>/...`. The node is replaced by the referenced `docs.json`
193
+ navigation with page paths prefixed by the mount path; tabs become groups, and the
194
+ parent group keeps its label and icon.
195
+ - Root-absolute links inside those pages (`/models/x`, `/images/x.png`) are prefixed
196
+ with the mount path (sub-site semantics: Mintlify's behavior here is not
197
+ documented, so this is an assumption). Relative links work as-is.
198
+ - Pages, navigation, assets and the referenced repository's custom components are
199
+ imported. Its components are bundled with the main site's under `src/mdx/migrated/`
200
+ and registered in `src/mdx/custom-components.tsx`; their content-hashed names cannot
201
+ clash with the main site's. Branding, colors, redirects, OpenAPI specs and
202
+ access-restricted pages of the referenced repository are ignored (one warning says
203
+ so). Access-restricted pages and the files only they use are withheld from the site
204
+ and are not saved anywhere (the main site saves its own under `migration-quarantine/`;
205
+ a referenced repository's are discarded), so recover them from that repository if
206
+ needed.
207
+ - Safety limits per referenced repository, fixed constants: 5,000 files and 50 MB under
208
+ the docs root. Over a limit the whole repository is refused, never imported
209
+ partially. Symlinks and dot-directories are removed before import, and a docs
210
+ directory that resolves outside the clone is refused. A repository GitHub reports as
211
+ larger than 200 MB is refused before cloning (if the GitHub API is unreachable the
212
+ clone proceeds and the limits above still apply), its submodules are not fetched,
213
+ and a `docs.json` over 1 MB is ignored. `repo` must match `owner/repo`; the mount
214
+ path must be a relative lowercase slug path (no `..`, no leading slash), must not
215
+ start with a name the site itself uses (`api`, `admin`, `_next`, `public`,
216
+ `llms.txt`, `sitemap.xml`, ...), must not collide with an existing page or directory
217
+ of the main site, and mappings must not overlap.
218
+ - A mapping that matches no `sourceRef` in the navigation is reported and imports
219
+ nothing. `migration-report.json` lists each imported repository with its page count
220
+ under `sourceRefs`.
221
+
174
222
  ## Mintlify compatibility
175
223
 
176
224
  Repository imports preserve named/default local JSX and TSX components and
@@ -214,8 +262,8 @@ same-length nested fence is widened so the inner fence no longer closes it, and
214
262
  a bare placeholder tag in prose (`<Feature> requires a plan`) that would stop
215
263
  the page compiling is escaped as text rather than losing the page.
216
264
  The Mintlify `search-bar-entry` click trigger is mapped to Thally's search
217
- shortcut. Markdown mixed inside interactive JSX, computed imports, and
218
- namespace imports are reported for manual review; unsupported source is
265
+ shortcut. Markdown mixed inside interactive JSX and computed imports are
266
+ reported for manual review; unsupported source is
219
267
  preserved, not replaced with empty stubs. An external package's JSX usage is
220
268
  dropped and replaced with a safe fallback (with a warning); if that package's
221
269
  binding is instead referenced outside JSX (an expression, a prop, or an
@@ -237,6 +285,30 @@ Next) and copied into any extracted client module. A source site's own
237
285
  passes a page-authored function into a client component needs the client
238
286
  wrapper described above; unsupported callback shapes retain their content
239
287
  with a warning. An unconfirmed target is warned for review.
288
+ Other relative imports of a copied file are handled the same way: a namespace
289
+ import (`import * as X from './comp.jsx'`) and a dynamic `import('./comp.jsx')`
290
+ point at the copied module, and a relative side-effect import
291
+ (`import './styles.css'`) is removed with a warning, like one from an npm
292
+ package. A page is excluded only when the relative target is not copied (for
293
+ example a Next.js `./metadata` module, or an `export ... from` re-export), with
294
+ a single warning naming it. An MDX expression that names an identifier the page
295
+ never defines (`{missing}`) is removed with a warning, since it would throw at
296
+ render; define the name or escape the braces as `\{ \}` to keep the text.
297
+ A value import from a local snippet (`import { FEE, fee } from
298
+ '/snippets/c.mdx'`) is inlined when each name is a static value (string,
299
+ number, boolean, `null`, or an object/array of those) or a self-contained
300
+ function. Inlined functions may only use pure built-ins (`Math`, `JSON`,
301
+ `String`, `Number`, `Boolean`, `Array`, `Date`, `Intl`, `parseInt`,
302
+ `parseFloat`, `isNaN`, `isFinite`, `encodeURIComponent` and
303
+ `decodeURIComponent`) and may not reach
304
+ `Function`, constructors, `globalThis`, `fetch` or other globals; anything else
305
+ is not inlined. A name the snippet does not export, or one that cannot be
306
+ inlined, is bound to `undefined` (as Mintlify does for a missing export) with a
307
+ warning, while the other names of the same import are still inlined. A snippet
308
+ import that resolves outside the repository through a symlink is refused, and a
309
+ snippet whose frontmatter cannot be read is not inlined (left as a comment with
310
+ a warning). Mintlify `sourceRef` navigation nodes have no Thally equivalent and
311
+ are reported rather than migrated.
240
312
  File paths cannot escape the repository checkout; symlinks and oversized
241
313
  graphs are rejected. This is compatibility analysis, **not a code sandbox**:
242
314
  imported JavaScript executes when the developer builds or runs the resulting
package/dist/index.d.ts CHANGED
@@ -81,6 +81,8 @@ interface MigrationDocsConfig {
81
81
  };
82
82
  /** Local site-authored CSS projected from a source docs site, served from public/. */
83
83
  stylesheets?: Array<string>;
84
+ /** `false` hides the breadcrumb trail; Mintlify pages show only a group eyebrow. */
85
+ breadcrumbs?: boolean;
84
86
  tabs: Array<MigrationNavigationTab>;
85
87
  navigation?: {
86
88
  /** How sibling documentation collections are presented to readers. */
@@ -173,6 +175,8 @@ interface MigrationDocsConfig {
173
175
  playground?: {
174
176
  display: 'interactive' | 'simple' | 'none';
175
177
  };
178
+ /** `false` hides the visible "OpenAPI specification" line; Mintlify does not show it. */
179
+ specLink?: boolean;
176
180
  };
177
181
  /** Third-party analytics in Mintlify's `integrations` shape; validated again by the renderer. */
178
182
  integrations?: {
@@ -281,6 +285,12 @@ interface MigrationBundle {
281
285
  quarantinedFiles?: Array<RenderedMigrationFile>;
282
286
  /** Access-restricted pages the file limit dropped: neither published nor saved in quarantinedFiles. */
283
287
  droppedGatedPages?: number;
288
+ /** Mintlify `sourceRef` repositories imported under a mount path, with their page counts. */
289
+ sourceRefs?: Array<{
290
+ repo: string;
291
+ mountPath: string;
292
+ pages: number;
293
+ }>;
284
294
  docsConfig: MigrationDocsConfig;
285
295
  site?: {
286
296
  name?: string;
@@ -324,6 +334,51 @@ interface RenderedMigrationFile {
324
334
  content: string | Uint8Array;
325
335
  }
326
336
 
337
+ /**
338
+ * Mintlify `sourceRef` navigation nodes mount the docs of another repository
339
+ * (`{"sourceRef": "owner/repo"}`). The mount path is not derivable from any
340
+ * docs.json, so the caller supplies it with `--source-ref owner/repo=<path>`.
341
+ * The referenced repository is untrusted remote content: it is validated,
342
+ * size-capped, stripped of symlinks and dot-directories, then run through the
343
+ * same repository pipeline as the main site and prefixed with its mount path.
344
+ */
345
+
346
+ /** Per referenced repository: files and bytes under its docs root. Constants on purpose, not configurable. */
347
+ declare const SOURCE_REF_MAX_FILES = 5000;
348
+ declare const SOURCE_REF_MAX_BYTES = 50000000;
349
+ /** GitHub reports repository size in KB; a larger repository is refused before it is cloned. */
350
+ declare const SOURCE_REF_MAX_REPOSITORY_KB = 200000;
351
+ interface SourceRefMapping {
352
+ /** `owner/repo`, exactly as written in the source navigation. */
353
+ repo: string;
354
+ /** Relative slug path the referenced docs are mounted at, e.g. `client-sdks/typescript`. */
355
+ mountPath: string;
356
+ }
357
+ interface SourceRefImport extends SourceRefMapping {
358
+ pages: Array<MigrationPage>;
359
+ assets: Array<MigrationAsset>;
360
+ /** Navigation entries with every page id already prefixed by the mount path. */
361
+ navigation: Array<string | MigrationNavigationGroup>;
362
+ /** Custom component files (and registry) the mounted pages' `<MigratedXXXX/>` tags need; merged into the main bundle. */
363
+ componentFiles?: Array<RenderedMigrationFile>;
364
+ /** Why the import is empty (or partial), plus forwarded pipeline warnings. */
365
+ warnings: Array<MigrationWarning>;
366
+ }
367
+ /** Parse repeated `owner/repo=mount/path` values; throws on anything unsafe. */
368
+ declare function parseSourceRefFlags(values: ReadonlyArray<string>): Array<SourceRefMapping>;
369
+ /**
370
+ * Why a referenced repository is too large to clone, or null when it is small
371
+ * enough or its size is unknown (GitHub API unreachable, rate-limited or
372
+ * private): an unknown size proceeds, and the post-clone caps still apply.
373
+ */
374
+ declare function sourceRefOversizeReason(repo: string, fetcher?: MigrationFetcher): Promise<string | null>;
375
+ /**
376
+ * Import one cloned referenced repository as a sub-site under its mount path.
377
+ * `repositoryDir` is a disposable clone: symlinks and dot-directories under
378
+ * its docs root are deleted.
379
+ */
380
+ declare function importSourceRef(mapping: SourceRefMapping, repositoryDir: string): SourceRefImport;
381
+
327
382
  /**
328
383
  * Repository adapter for the shared migration engine. Traversal is bounded;
329
384
  * `scanFiles` follows a symbolic link only when its resolved real path stays
@@ -346,6 +401,8 @@ interface RepositoryMigrationOptions {
346
401
  sourceUrl: string;
347
402
  docsDir?: string;
348
403
  platform?: MigrationPlatform;
404
+ /** @internal Mount path of a sourceRef import; keeps its component files separate from any other copy of the same source. */
405
+ componentNamespace?: string;
349
406
  /** @internal Prefix used by additional Docusaurus docs-plugin instances. */
350
407
  docusaurusRoutePrefix?: string;
351
408
  /** @internal Prevent recursive discovery while importing one plugin root. */
@@ -360,6 +417,8 @@ interface RepositoryMigrationOptions {
360
417
  maxSourceFiles?: number;
361
418
  /** @internal Redirects are global config, read once, not per plugin instance. */
362
419
  docusaurusSkipRedirects?: boolean;
420
+ /** Mintlify `sourceRef` repositories already fetched and prepared by `importSourceRef`. */
421
+ sourceRefs?: ReadonlyArray<SourceRefImport>;
363
422
  }
364
423
  /** Parse and validate a public GitHub repository URL. */
365
424
  declare function parseGitHubRepositoryUrl(rawUrl: string): GitHubRepositorySource;
@@ -377,7 +436,11 @@ declare function gitmodulePaths(targetDir: string): Array<string>;
377
436
  * submodule-fetch-failure warning (this function otherwise returns exactly
378
437
  * as before, so existing callers are unaffected).
379
438
  */
380
- declare function cloneGitHubRepository(source: GitHubRepositorySource, targetDir: string, warnings?: Array<MigrationWarning>): Promise<void>;
439
+ declare function cloneGitHubRepository(source: GitHubRepositorySource, targetDir: string, warnings?: Array<MigrationWarning>,
440
+ /** Untrusted referenced repositories: never follow their `.gitmodules` to further remotes. */
441
+ options?: {
442
+ skipSubmodules?: boolean;
443
+ }): Promise<void>;
381
444
  /** Detect a supported repository docs platform from unambiguous config files. */
382
445
  declare function detectRepositoryPlatform(repositoryDir: string, docsDir?: string): MigrationPlatform;
383
446
  /** Pick the first conventional content root containing Markdown. */
@@ -406,11 +469,19 @@ interface MintlifyNavigationResult {
406
469
  pageReferences: Array<MintlifyPageReference>;
407
470
  /** Pages under a restricted container; withheld from `docsConfig` and `pageReferences`. */
408
471
  gatedReferences: Array<MintlifyGatedReference>;
472
+ /** `sourceRef` mounts under a restricted container; never resolved, so nothing of theirs is imported. */
473
+ gatedSourceRefs: Array<string>;
409
474
  warnings: Array<MigrationWarning>;
410
475
  }
411
476
  interface MintlifyProjectionOptions {
412
477
  /** Public URL migrations may expose page hrefs including the docs mount path. */
413
478
  pathPrefix?: string;
479
+ /**
480
+ * Resolves a `sourceRef` repository (`owner/repo`) to the navigation entries
481
+ * of its imported docs, already prefixed with their mount path. Returns null
482
+ * when the repository was not mapped with `--source-ref`.
483
+ */
484
+ resolveSourceRef?: (repo: string) => Array<string | MigrationNavigationGroup> | null;
414
485
  }
415
486
  /** Preserve Mintlify's implicit `/section` route for section landing pages. */
416
487
  declare function addMintlifyDirectoryRedirects(config: MigrationDocsConfig, pages: Array<MigrationPage>): MigrationDocsConfig;
@@ -589,4 +660,4 @@ declare function migrateUrl(options: UrlMigrationOptions): Promise<MigrationBund
589
660
  */
590
661
  declare function hydrateRemoteApiSpecs(bundle: MigrationBundle, fetcher?: MigrationFetcher): Promise<MigrationBundle>;
591
662
 
592
- export { type GitHubRepositorySource, type MigrationAsset, type MigrationBundle, type MigrationDocsConfig, type MigrationFetchRequest, type MigrationFetchResponse, type MigrationFetcher, type MigrationNavigationGroup, type MigrationNavigationTab, type MigrationPage, type MigrationPlatform, type MigrationWarning, type RenderedMigrationFile, type RepositoryMigrationOptions, type UrlMigrationOptions, addMintlifyDirectoryRedirects, buildNavigationFromPages, cloneGitHubRepository, defaultMigrationFetcher, detectRepositoryDocsDir, detectRepositoryPlatform, gitmodulePaths, hydrateRemoteApiSpecs, mergeMigrationConfig, migrateRepository, migrateUrl, normalizeMdx, parseGitHubRepositoryUrl, parseMarkdownPage, projectFernNavigation, projectMintlifyIntegrations, projectMintlifyNavigation, pruneMissingNavigationPages, readFernConfig, readMintlifyConfig, renderMigrationFiles, validateIntegrations, validateMigrationUrl };
663
+ export { type GitHubRepositorySource, type MigrationAsset, type MigrationBundle, type MigrationDocsConfig, type MigrationFetchRequest, type MigrationFetchResponse, type MigrationFetcher, type MigrationNavigationGroup, type MigrationNavigationTab, type MigrationPage, type MigrationPlatform, type MigrationWarning, type RenderedMigrationFile, type RepositoryMigrationOptions, SOURCE_REF_MAX_BYTES, SOURCE_REF_MAX_FILES, SOURCE_REF_MAX_REPOSITORY_KB, type SourceRefImport, type SourceRefMapping, type UrlMigrationOptions, addMintlifyDirectoryRedirects, buildNavigationFromPages, cloneGitHubRepository, defaultMigrationFetcher, detectRepositoryDocsDir, detectRepositoryPlatform, gitmodulePaths, hydrateRemoteApiSpecs, importSourceRef, mergeMigrationConfig, migrateRepository, migrateUrl, normalizeMdx, parseGitHubRepositoryUrl, parseMarkdownPage, parseSourceRefFlags, projectFernNavigation, projectMintlifyIntegrations, projectMintlifyNavigation, pruneMissingNavigationPages, readFernConfig, readMintlifyConfig, renderMigrationFiles, sourceRefOversizeReason, validateIntegrations, validateMigrationUrl };