@vzn/vx-migrate 0.0.640 → 0.0.641
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 +2 -2
- package/package.json +2 -2
- package/src/adopt.ts +15 -0
- package/src/migrate.ts +14 -8
- package/src/nx-cache/index.ts +0 -4
- package/src/turbo-cache/index.ts +1 -38
package/README.md
CHANGED
|
@@ -231,7 +231,7 @@ It is the one command a repo needs (`pnpx @vzn/vx-migrate` in a pnpm repo works
|
|
|
231
231
|
- In a Turbo or Nx repo, in a terminal, it asks which adoption you want: **native** (the default, a `vx.config.ts` per package) or **keep** (a workspace file declaring `turbo()` or `nx()`). `--native` / `--keep` answer it; with no terminal (CI, a pipe) it is native. Elsewhere both are refused. A repo already set up (a workspace file declaring neither, and project configs) is said to be, exit 0, unless `--force`.
|
|
232
232
|
- Native or keep, a remote cache the repo shows is declared: `turboCache()` for turbo.json's enabled `remoteCache`, a `turbo link`ed `.turbo/config.json` or a CI file (`.github/workflows/*`, `.gitlab-ci.yml`, `.circleci/config.yml`) setting `TURBO_TOKEN`; `nxCache()` for one setting `NX_SELF_HOSTED_REMOTE_CACHE_SERVER`. The report names the file that showed it. Nx Cloud (`nxCloudId`, `nxCloudAccessToken`, the `nx-cloud` runner) is named instead: vx does not speak its wire.
|
|
233
233
|
- With no `.vx/workspace.*` yet, it writes one declaring the plugins the repo calls for (`src/workspace-plugins.ts`): the remote cache above, the `@vzn/vx-lockfile` factory for the lockfile (`pnpm()`, `bun()`, `npm()`, `yarn()`), `scheduleHistoryPlugin()`, and `github()` when `.github/workflows` exists, from `@vzn/vx-ci`, each installed beside `@vzn/vx` at vx-migrate's own version. Keep adds them to the file it writes, or to one already in that shape; any other workspace file is the user's and left alone. Native over a file vx wrote (a keep adoption's) rewrites it: `turbo()` and `nx()` go, since the configs written replace them, and the other plugins stay.
|
|
234
|
-
- It installs what the written files import with the repo's own manager (`packageManager`, else the lockfile): `@vzn/vx`, the
|
|
234
|
+
- It installs what the written files import with the repo's own manager (`packageManager`, else the lockfile): `@vzn/vx`, every `@vzn/vx-*` package the workspace file imports (one an earlier init wrote included), and `@vzn/vx-migrate` for keep or when a written task runs its `nx-exec` or `nx-env` bin (`pnpm add -D -w`, `yarn add -D -W` on Yarn 1 and without `-W` on Yarn 2+, `bun add -d`, `npm install -D`). A package the root both lists and has installed at vx-migrate's own version is left alone; any other is installed at that version, and so is every other `@vzn/vx-*` plugin the root lists from the registry (one install moves them together, as `vx upgrade` does). Native removes a listed `@vzn/vx-migrate` when nothing written needs it (no `nx-exec` / `nx-env` task, no `turboCache()` or `nxCache()` left in the workspace file): `pnpm remove -w`, `yarn remove` (`-W` on Yarn 1), `bun remove`, `npm uninstall`. `--no-install` skips both. vx-migrate runs only with the `@vzn/vx` of its own release, which it installs: one of another release (a `bun x` install directory keeps the one it had) is refused, naming where it was loaded.
|
|
235
235
|
- Native, once the files are written, it ends with `vx lock`: `.vx/projects.lock.json` is written and the graph rules are checked, its line the report's `lock` row. A refusal prints above the report and the configs stay written (exit 0). `--no-install` skips it, since the plugins the workspace file declares are not installed yet, and `next` then starts with `vx lock`.
|
|
236
236
|
|
|
237
237
|
A Lerna repo is an Nx one: Lerna 6+ runs `lerna run` on Nx's task runner over the graph `nx graph` exports, nx.json or not, so a `lerna.json` with no nx.json and no turbo.json is mapped from that graph, and keep writes the workspace file declaring `nx()`. Lerna is the installed one, else the root manifest's range; with neither, the lerna.json is another tool's (lerna-lite reads it too, and runs no Nx). A root whose scripts never `lerna run` runs its tasks another way and publishes with Lerna (webdriverio: `run-s`, `pnpm -r`), so its scripts stay the source. Not where Lerna runs its own runner: `useNx: false`, or Lerna 5 without `useNx: true`; those map their scripts. Beside turbo.json, Turbo runs the tasks and Lerna only publishes: turbo.json is the source.
|
|
@@ -600,7 +600,7 @@ The Nx spec has no existence probe, so `has` (the `--dry` prediction; the prefet
|
|
|
600
600
|
- Three outages in a row (no connection, no answer within the deadline, or a `5xx` once the resends are spent) open Turbo's outage breaker: no request is sent for 30 s, each lookup is a miss at once, then one request probes and its answer closes or reopens it. A hit is judged when its body ends, so a server that sends headers and then stalls the body counts the same way. A hung server costs three deadlines, not one per task.
|
|
601
601
|
- A refused token (`401`/`403`) warns **once** and turns the layer off for the rest of the process (a `403` on an upload is a read-only token — Nx's spec, or turborepo-remote-cache's `READ_ONLY` and write-less JWTs: it turns off uploads alone, and reads go on) — including the requests already in flight when the refusal lands, which degrade in silence rather than repeating it (a six-project run printed five identical lines before 2026-09-20).
|
|
602
602
|
- Policy (`--cache-policy=remote:r`, `VX_CACHE_POLICY`, `cache.policy`) is enforced by core, which layers each plugin's remote over the local cache — a read-only token pairs naturally with `remote:r`.
|
|
603
|
-
-
|
|
603
|
+
- Core's policy alone decides reads and writes: the plugins read none of Turbo's or Nx's own switches (`TURBO_CACHE`, `TURBO_REMOTE_CACHE_READ_ONLY`, `NX_SKIP_REMOTE_CACHE`, `NX_DISABLE_REMOTE_CACHE`). A CI that keeps untrusted pull requests off the shared cache sets `cache.policy` or `--cache-policy=local:rw,remote:r` for them.
|
|
604
604
|
|
|
605
605
|
## Testing
|
|
606
606
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vzn/vx-migrate",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.641",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Adopt @vzn/vx from Turborepo, Nx or package.json scripts: run the repo unchanged, migrate it to vx.config.ts, and keep your Turbo or Nx remote cache",
|
|
6
6
|
"keywords": [
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"access": "public"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@vzn/vx": "^0.0.
|
|
43
|
+
"@vzn/vx": "^0.0.641"
|
|
44
44
|
},
|
|
45
45
|
"repository": {
|
|
46
46
|
"type": "git",
|
package/src/adopt.ts
CHANGED
|
@@ -175,6 +175,21 @@ export function listedPlugins(root: string): string[] {
|
|
|
175
175
|
return [...plugins].sort()
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
/**
|
|
179
|
+
* The `@vzn/vx*` packages a workspace file imports, parsed, not matched (a
|
|
180
|
+
* formatter's quotes hid a match). Each is installed with core: a file an
|
|
181
|
+
* earlier init wrote imports @vzn/vx-lockfile that package.json need not
|
|
182
|
+
* list, and the lock could not find it (M, 2026-10-16).
|
|
183
|
+
*/
|
|
184
|
+
export function importedPackages(text: string): string[] {
|
|
185
|
+
const out = new Set<string>()
|
|
186
|
+
for (const i of new Bun.Transpiler({ loader: 'ts' }).scanImports(text)) {
|
|
187
|
+
const m = /^(@vzn\/vx(?:-[^/]+)?)(?:\/|$)/.exec(i.path)
|
|
188
|
+
if (m !== null) out.add(m[1]!)
|
|
189
|
+
}
|
|
190
|
+
return [...out].sort()
|
|
191
|
+
}
|
|
192
|
+
|
|
178
193
|
/** Install `packages` (at `version`, given one) with the repo's manager; its output only on failure. */
|
|
179
194
|
export function install(
|
|
180
195
|
root: string,
|
package/src/migrate.ts
CHANGED
|
@@ -45,6 +45,7 @@ import { exportGraph, nxBin } from './nx/export-graph.js'
|
|
|
45
45
|
import { readNxJson } from './nx/nx-map.js'
|
|
46
46
|
import {
|
|
47
47
|
type AdoptionMode,
|
|
48
|
+
importedPackages,
|
|
48
49
|
install,
|
|
49
50
|
installArgv,
|
|
50
51
|
listedPackages,
|
|
@@ -358,11 +359,8 @@ export async function migrateCmd(args: readonly string[]): Promise<number> {
|
|
|
358
359
|
const finalWorkspace = workspaceText ?? readFileSync(path.join(root, existingWorkspace!), 'utf8')
|
|
359
360
|
// Imports parsed, not matched: a formatter's double quotes hid it, and
|
|
360
361
|
// the file still imported the package removed (owner, 2026-10-16).
|
|
361
|
-
const
|
|
362
|
-
|
|
363
|
-
new Bun.Transpiler({ loader: 'ts' })
|
|
364
|
-
.scanImports(finalWorkspace)
|
|
365
|
-
.some((i) => i.path === '@vzn/vx-migrate')
|
|
362
|
+
const imported = importedPackages(finalWorkspace)
|
|
363
|
+
const needsSelf = runsBins || imported.includes('@vzn/vx-migrate')
|
|
366
364
|
const cacheNote =
|
|
367
365
|
runner === 'vite-task'
|
|
368
366
|
? undefined
|
|
@@ -381,6 +379,7 @@ export async function migrateCmd(args: readonly string[]): Promise<number> {
|
|
|
381
379
|
'@vzn/vx',
|
|
382
380
|
...(needsSelf ? ['@vzn/vx-migrate'] : []),
|
|
383
381
|
...plugins.map((p) => p.pkg),
|
|
382
|
+
...imported,
|
|
384
383
|
]),
|
|
385
384
|
],
|
|
386
385
|
parsed,
|
|
@@ -544,7 +543,14 @@ async function keep(
|
|
|
544
543
|
const declares = before === undefined || extended !== null
|
|
545
544
|
const steps = await prepareRepo(
|
|
546
545
|
root,
|
|
547
|
-
[
|
|
546
|
+
[
|
|
547
|
+
...new Set([
|
|
548
|
+
'@vzn/vx',
|
|
549
|
+
'@vzn/vx-migrate',
|
|
550
|
+
...(declares ? all.map((p) => p.pkg) : []),
|
|
551
|
+
...importedPackages(text),
|
|
552
|
+
]),
|
|
553
|
+
],
|
|
548
554
|
args,
|
|
549
555
|
)
|
|
550
556
|
if (args.json)
|
|
@@ -633,8 +639,8 @@ function lockRow(lock: { code: number; said: string }): ui.FieldRow {
|
|
|
633
639
|
]
|
|
634
640
|
}
|
|
635
641
|
|
|
636
|
-
/** `.vx/.gitignore`'s text, as core writes it: the machine-local cache
|
|
637
|
-
const DOT_VX_GITIGNORE = '/cache\n'
|
|
642
|
+
/** `.vx/.gitignore`'s text, as core writes it: the machine-local cache and restore staging stay out of git. */
|
|
643
|
+
const DOT_VX_GITIGNORE = '/cache\n/tmp\n'
|
|
638
644
|
|
|
639
645
|
// npx and bunx name the package: their bare `vx` is a stranger's npm package.
|
|
640
646
|
const EXEC: Record<PackageManager, string> = {
|
package/src/nx-cache/index.ts
CHANGED
|
@@ -46,10 +46,6 @@ export function resolveNxCacheConfig(
|
|
|
46
46
|
options: NxCacheOptions,
|
|
47
47
|
env: Record<string, string | undefined> = Bun.env,
|
|
48
48
|
): NxCacheConfig | undefined {
|
|
49
|
-
// Nx's own off switches (`skipRemoteCache`): a CI that sets one for an
|
|
50
|
-
// untrusted pull request kept Nx off the shared cache and not vx.
|
|
51
|
-
if (env['NX_SKIP_REMOTE_CACHE'] === 'true' || env['NX_DISABLE_REMOTE_CACHE'] === 'true')
|
|
52
|
-
return undefined
|
|
53
49
|
const server = (options.server ?? env['NX_SELF_HOSTED_REMOTE_CACHE_SERVER'])?.replace(/\/+$/, '')
|
|
54
50
|
if (!server) return undefined
|
|
55
51
|
// The URL is printed in every refusal line, so a `user:pass@` in it would
|
package/src/turbo-cache/index.ts
CHANGED
|
@@ -393,8 +393,6 @@ export class TurboRemoteCache implements RemoteCacheLayer {
|
|
|
393
393
|
private readonly absent = new Set<string>()
|
|
394
394
|
private readonly key: Uint8Array | undefined
|
|
395
395
|
readonly endpoint: string
|
|
396
|
-
/** What `TURBO_REMOTE_CACHE_READ_ONLY` and friends leave this run (`turboRemoteAccess`). */
|
|
397
|
-
access?: { read: boolean; write: boolean }
|
|
398
396
|
constructor(
|
|
399
397
|
private readonly config: TurboCacheConfig,
|
|
400
398
|
private readonly fetchImpl: typeof fetch = fetch,
|
|
@@ -653,37 +651,6 @@ export class TurboRemoteCache implements RemoteCacheLayer {
|
|
|
653
651
|
}
|
|
654
652
|
}
|
|
655
653
|
|
|
656
|
-
/**
|
|
657
|
-
* What Turbo's own switches leave of the remote cache: `TURBO_CACHE`
|
|
658
|
-
* (`--cache-policy`'s syntax, where an omitted source is off) and
|
|
659
|
-
* `TURBO_REMOTE_CACHE_READ_ONLY`. A CI that sets either for an untrusted
|
|
660
|
-
* pull request kept Turbo from writing the shared cache and not vx. They
|
|
661
|
-
* only narrow what `--cache-policy` allows, never widen it.
|
|
662
|
-
*/
|
|
663
|
-
export function turboRemoteAccess(env: Record<string, string | undefined>): {
|
|
664
|
-
read: boolean
|
|
665
|
-
write: boolean
|
|
666
|
-
} {
|
|
667
|
-
let read = true
|
|
668
|
-
let write = true
|
|
669
|
-
const spec = env['TURBO_CACHE']
|
|
670
|
-
if (spec !== undefined && spec !== '') {
|
|
671
|
-
read = write = false
|
|
672
|
-
for (const part of spec.split(',')) {
|
|
673
|
-
const m = /^(local|remote):([rw]*)$/.exec(part.trim())
|
|
674
|
-
if (m === null)
|
|
675
|
-
throw new Error(
|
|
676
|
-
`vx/turbo-cache: TURBO_CACHE ${JSON.stringify(spec)} is not --cache-policy's syntax (e.g. local:rw,remote:r)`,
|
|
677
|
-
)
|
|
678
|
-
if (m[1] !== 'remote') continue
|
|
679
|
-
read = m[2]!.includes('r')
|
|
680
|
-
write = m[2]!.includes('w')
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
if (/^(1|true)$/i.test(env['TURBO_REMOTE_CACHE_READ_ONLY'] ?? '')) write = false
|
|
684
|
-
return { read, write }
|
|
685
|
-
}
|
|
686
|
-
|
|
687
654
|
/**
|
|
688
655
|
* The repo's `.turbo/config.json`, by Turbo's field names and their
|
|
689
656
|
* aliases (`apiurl`, `ApiUrl`, `APIURL`, …); `{}` when absent or unreadable.
|
|
@@ -757,14 +724,10 @@ export function turboCache(options: TurboCacheOptions = {}): VxPlugin {
|
|
|
757
724
|
localConfigOf(ctx.workspaceRoot),
|
|
758
725
|
)
|
|
759
726
|
if (config === undefined) return undefined
|
|
760
|
-
const access = turboRemoteAccess(Bun.env)
|
|
761
|
-
if (!access.read && !access.write) return undefined
|
|
762
727
|
// A signed download waits in a temp until its tag checks: in vx's
|
|
763
728
|
// cache directory, which the sandbox walls, not the shared temp dir a
|
|
764
729
|
// sandboxed task may read (L-45).
|
|
765
|
-
|
|
766
|
-
remote.access = access
|
|
767
|
-
return remote
|
|
730
|
+
return new TurboRemoteCache(config, fetch, ctx.cacheDir)
|
|
768
731
|
},
|
|
769
732
|
})
|
|
770
733
|
}
|