@thallylabs/migrate 0.2.12 → 0.2.17

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
@@ -18,10 +18,18 @@ rendered application before falling back to DOM link inference.
18
18
  An `openapi` (or `asyncapi`) reference is resolved wherever Mintlify allows
19
19
  it — the top-level `api.openapi`, or on any individual
20
20
  tab/anchor/dropdown/product/version, e.g. `navigation.tabs[].openapi` — and
21
- binds to that exact tab, not only the first one found. Remote `http://` and
21
+ binds to that exact tab, not only the first one found. An `openapi` item inside
22
+ a tab `menu` (string or `{ source, directory }`) becomes its own API tab named
23
+ `<Tab>: <Item>`, so its endpoints appear in the sidebar; when it is the tab's
24
+ only content it binds to the tab itself. The item's `icon` and `hidden` carry
25
+ onto that tab, and a `directory` output folder is not used (a warning says so:
26
+ endpoint pages are generated under `/api/`). An item Thally cannot represent
27
+ produces a warning naming it. Specs that share a file name across folders are
28
+ kept as separate files in `openapi/`. Remote `http://` and
22
29
  `https://` specs are not downloaded during migration. The report names each
23
- remote URL and tells you to add its spec to `public/`, then set the API source
24
- in `docs.json` to that local path. Missing local specs also produce warnings
30
+ remote URL and tells you to add its spec to a project-root folder such as
31
+ `openapi/`, then set the API source in `docs.json` to that path. Missing local
32
+ specs also produce warnings
25
33
  instead of silently dropping that API tab's reference. AsyncAPI has no Thally
26
34
  renderer, so it always produces a clear warning naming the spec instead of
27
35
  disappearing.
@@ -39,12 +47,20 @@ version first, then pages not listed in navigation, and one warning counts and
39
47
  names (the first three of) the pages left out and suggests `--docs-dir` to
40
48
  migrate a smaller part of the repository. Scanning itself stops after 100,000
41
49
  files with its own warning. Individual assets over 25 MB, or beyond 500 MB in
42
- total, are not copied and are warned about one by one.
50
+ total, are not copied; one warning per reason gives the count, the first five
51
+ paths, and the next step (copy them into `public/` manually).
43
52
 
44
53
  Headings written with an explicit `{#custom-id}` become a preceding
45
54
  `<a id="custom-id"></a>` anchor and keep their text, so links to that id still
46
55
  work and the page still compiles as MDX.
47
56
 
57
+ Migrated specs are written to `openapi/`, never `public/`, because the host
58
+ serves `public/` as-is and would publish operations marked `x-excluded` or
59
+ `x-hidden`. Migrating into an existing site does not delete anything: an
60
+ older copy of a spec already in `public/` stays publicly downloadable. The
61
+ migration warns and lists such files, and `thally check` keeps flagging them;
62
+ delete them manually.
63
+
48
64
  Docusaurus sidebars are parsed as bounded, data-only JSON5—never executed—and
49
65
  preserve explicit categories, autogenerated slices, document IDs/slugs,
50
66
  positions, generated indexes, admonitions, tabs, and `static/` assets where
@@ -229,6 +245,56 @@ YouTube-embed package (e.g. a custom `LiteYouTube` wrapping
229
245
  page that imports that package directly, instead of losing the embed to the
230
246
  generic unknown-component fallback.
231
247
 
248
+ ### Access-restricted pages
249
+
250
+ A gated Mintlify page must never migrate as public. A page is withheld when its
251
+ frontmatter sets a non-empty `groups` (list or string) or `public: false`
252
+ (also `"false"`, `no`, `off`, `0`; empty `groups` restricts nothing), or when it sits
253
+ under a navigation group or tab that sets `groups` (a list of strings) or
254
+ `public: false`. `public: true` alone is not gating. Withheld pages are not
255
+ written to `src/content` and are removed from navigation (emptied groups and
256
+ tabs included), whether or not navigation lists them. The original file is
257
+ written unchanged to `migration-quarantine/<original path>` at the project
258
+ root, which no runtime content reader, search index, or `llms` output walks;
259
+ `create-thally-docs` also adds it to the project's `.gitignore` (appended once,
260
+ existing entries kept) so it is never committed or pushed. A public page that
261
+ imports a withheld page as a component does not inline it (a `gated-page`
262
+ warning names the importer). Invalid frontmatter fails the run rather than
263
+ importing the page. Each page gets a
264
+ `gated-page` warning, plus a summary; `create-thally-docs` prints these last
265
+ under an "ACCESS-RESTRICTED CONTENT" heading and the report records them and a
266
+ `quarantined` count. Links from other pages to a withheld page are left as
267
+ authored and will fail `thally check`; images and other assets used only by
268
+ gated pages are still copied to `public/`. Mintlify's site-wide authentication mode lives in its
269
+ dashboard and cannot be read from a repository, so when any page sets
270
+ `public: true` (which implies the source site required sign-in for everything
271
+ else) one `gated-page` warning says Thally will publish every imported page.
272
+ Restricted-container `groups` and `public: false` are honored fail-closed even
273
+ though Mintlify documents only `"public": true` on a group.
274
+
275
+ ### Site-wide scripts, styles, fonts, and settings
276
+
277
+ Mintlify serves every `.css` and `.js` file in the content directory on every
278
+ page. Those files are copied to `public/` (tooling config such as
279
+ `*.config.js`, `tailwind.*`, `postcss.*`, plus `dist`/`build`/`coverage`
280
+ output, dot-directories, and `node_modules` are not) under the normal asset
281
+ size limits. Scripts are added to `docs.json` `customScripts` (URL-encoded),
282
+ except modules that pages import as components, which are copied as components
283
+ instead. Thally cannot
284
+ load a stylesheet from `docs.json`, so each `.css` is preserved with a warning
285
+ that it is not applied and likely targets Mintlify's DOM. Fonts named by
286
+ `fonts.source` (top level, `heading`, `body`) that are local `.woff`, `.woff2`,
287
+ `.ttf`, or `.otf` files are copied; Thally's `fonts` setting loads Google Fonts
288
+ by family only, so a warning says the file is not applied. Remote font URLs
289
+ are not downloaded, and unsafe or missing paths are warned about.
290
+
291
+ Legacy `mint.json` `topbarLinks` and `topbarCtaButton` (including
292
+ `type: github`) map to `navbar.links` and `navbar.primary`; `docs.json`
293
+ `navbar` fields win, with a warning. `appearance.default`, `appearance.strict`
294
+ (`showToggle: false`), and legacy `modeToggle.default` / `isHidden` map to
295
+ `appearance`; invalid values are skipped with a warning. Mintlify sources set
296
+ `markdown.enabled: true`, matching Mintlify's default Markdown mirrors.
297
+
232
298
  ## Branding
233
299
 
234
300
  A source site's logo, favicon, and theme accent color(s) are extracted where
@@ -245,18 +311,21 @@ dashboard's branding settings instead of leaving the loss silent.
245
311
 
246
312
  `cloneGitHubRepository` retries a transient network-class failure with
247
313
  backoff, and each underlying git process (the clone, and any submodule
248
- fetch) is killed and treated as a retryable failure if it stalls — a
249
- generous 10-minute default, configurable via
250
- `THALLY_MIGRATE_CLONE_TIMEOUT_MS` (milliseconds) for an unusually large
251
- repository on a slow link. The clone neutralizes the `filter.lfs.*`
314
+ fetch) is killed and treated as a retryable failure if it stalls, meaning
315
+ git prints no progress for 2 minutes (a large repository that is still
316
+ downloading is never cut off; an hour is the absolute ceiling for one git
317
+ process). The allowed silence is configurable via
318
+ `THALLY_MIGRATE_CLONE_TIMEOUT_MS` (milliseconds) for an unusually slow link.
319
+ The clone neutralizes the `filter.lfs.*`
252
320
  smudge/clean/process filter driver for that one process only (via
253
321
  `GIT_CONFIG_COUNT`/`GIT_CONFIG_KEY_n`/`GIT_CONFIG_VALUE_n`, never the user's
254
322
  global git config), so a repository that uses Git LFS still clones even when
255
323
  the host has no `git-lfs` binary installed — including a host where
256
324
  `filter.lfs.*` is still registered globally from a previous install,
257
325
  pointing at a command that no longer exists; an asset that's still a Git
258
- LFS pointer file afterward (not its real content) is skipped with a warning
259
- instead of being copied as if it were the real file. Submodules are
326
+ LFS pointer file afterward (not its real content) is skipped, and one
327
+ warning lists those files, instead of copying them as if they were the real
328
+ files. Submodules are
260
329
  initialized one at a time, best-effort,
261
330
  after a successful clone — a submodule that can't be fetched (private,
262
331
  deleted, network trouble) is named in a warning instead of failing the
package/dist/index.d.ts CHANGED
@@ -113,6 +113,10 @@ interface MigrationDocsConfig {
113
113
  feedback?: {
114
114
  thumbsRating?: boolean;
115
115
  };
116
+ customScripts?: Array<{
117
+ src: string;
118
+ strategy?: 'beforeInteractive' | 'afterInteractive' | 'lazyOnload';
119
+ }>;
116
120
  ai?: {
117
121
  chat?: boolean;
118
122
  label?: string;
@@ -163,12 +167,16 @@ interface MigrationPage {
163
167
  frontmatterError?: string;
164
168
  }
165
169
  interface MigrationAsset {
166
- /** Path below `public`, always normalized and traversal-free. */
170
+ /** Path below `public` (or below the project root when `projectRelative`), always normalized and traversal-free. */
167
171
  path: string;
168
172
  content: Uint8Array;
173
+ /** Write at the project root instead of `public/`, so the host never serves it statically. */
174
+ projectRelative?: boolean;
169
175
  }
170
176
  interface MigrationWarning {
171
- code: 'collision' | 'invalid-page' | 'missing-page' | 'unsupported-config' | 'limit-reached' | 'fetch-failed' | 'skipped-file';
177
+ code: 'collision' | 'invalid-page' | 'missing-page' | 'unsupported-config' | 'limit-reached' | 'fetch-failed' | 'skipped-file'
178
+ /** Access-restricted source page withheld from the published site (or a site-wide auth risk). */
179
+ | 'gated-page';
172
180
  message: string;
173
181
  source?: string;
174
182
  }
@@ -182,9 +190,16 @@ interface MigrationBundle {
182
190
  remoteApiSpecs?: Array<{
183
191
  url: string;
184
192
  tabLabel?: string;
193
+ parentTab?: string;
194
+ icon?: string;
195
+ hidden?: boolean;
185
196
  }>;
186
197
  /** Customer-owned component source and registry; paths are repository-relative. */
187
198
  componentFiles?: Array<RenderedMigrationFile>;
199
+ /** Access-restricted source pages, written outside every published path. */
200
+ quarantinedFiles?: Array<RenderedMigrationFile>;
201
+ /** Access-restricted pages the file limit dropped: neither published nor saved in quarantinedFiles. */
202
+ droppedGatedPages?: number;
188
203
  docsConfig: MigrationDocsConfig;
189
204
  site?: {
190
205
  name?: string;
@@ -260,6 +275,8 @@ interface RepositoryMigrationOptions {
260
275
  docusaurusSidebarPath?: string;
261
276
  /** @internal Static assets are shared across docs-plugin instances. */
262
277
  docusaurusSkipAssets?: boolean;
278
+ /** @internal Test seam: lowers the 5,000-file budget so budget behaviour can be exercised with small fixtures. */
279
+ maxSourceFiles?: number;
263
280
  /** @internal Redirects are global config, read once, not per plugin instance. */
264
281
  docusaurusSkipRedirects?: boolean;
265
282
  }
@@ -298,9 +315,16 @@ interface MintlifyPageReference {
298
315
  navigationId: string;
299
316
  locale?: string;
300
317
  }
318
+ /** A page listed under an access-restricted navigation container. */
319
+ interface MintlifyGatedReference {
320
+ ref: string;
321
+ reason: string;
322
+ }
301
323
  interface MintlifyNavigationResult {
302
324
  docsConfig: MigrationDocsConfig;
303
325
  pageReferences: Array<MintlifyPageReference>;
326
+ /** Pages under a restricted container; withheld from `docsConfig` and `pageReferences`. */
327
+ gatedReferences: Array<MintlifyGatedReference>;
304
328
  warnings: Array<MigrationWarning>;
305
329
  }
306
330
  interface MintlifyProjectionOptions {