@devdogsuga/backstage 0.1.3 → 0.1.4
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 +45 -1
- package/dist/{catalog-BOBE4ee7.js → catalog-BuNLUu08.js} +145 -237
- package/dist/{cli-ZF4QgJro.js → cli-BI5Vn9sR.js} +16 -955
- package/dist/client-B7HhuTaA.js +429 -0
- package/dist/client-DzXQHd58.js +2 -0
- package/dist/commands-Dl3UlhBf.js +1118 -0
- package/dist/connection-D3XsOE3S.js +7 -0
- package/dist/launch.js +4 -2
- package/dist/peers-B0KDkI6a.js +229 -0
- package/dist/vault-C8LYJFOQ.js +563 -0
- package/package.json +3 -3
package/dist/launch.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { C as discoverRepoRoot, E as setCliName, S as RepoNotFoundError, T as cliName, _ as installFailureLog, d as hasYes, g as stripTierFlag, h as stripNoEnvFlag, m as setNoEnv, n as captureDevtoolsError, o as initDevtoolsTelemetry, p as isNonInteractive, s as lastSentryEventId } from "./telemetry-Bjoz29Hl.js";
|
|
2
|
-
import { c as setMenuEnvHook, d as formatCommand, f as
|
|
2
|
+
import { c as setMenuEnvHook, d as formatCommand, f as helpPath, n as bareGroupStartPath, t as catalog } from "./catalog-BuNLUu08.js";
|
|
3
|
+
import { i as loadEnvSession, r as loadEnvLoad } from "./peers-B0KDkI6a.js";
|
|
4
|
+
import { t as nonEmpty } from "./connection-D3XsOE3S.js";
|
|
3
5
|
import { c as setDryRun, o as isDryRun, s as resolveDryRun } from "./options-BTjOf5KP.js";
|
|
4
6
|
import { o as unwrap, r as errorMessage } from "./ui-CdKo8mLw.js";
|
|
5
7
|
import { confirm } from "@clack/prompts";
|
|
@@ -320,7 +322,7 @@ async function launchWith(argv, options) {
|
|
|
320
322
|
*/
|
|
321
323
|
async function launch(argv) {
|
|
322
324
|
await launchWith(argv, { dispatch: async (args) => {
|
|
323
|
-
const { main } = await import("./cli-
|
|
325
|
+
const { main } = await import("./cli-BI5Vn9sR.js");
|
|
324
326
|
await main(args);
|
|
325
327
|
} });
|
|
326
328
|
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { S as RepoNotFoundError, w as findRepoRoot } from "./telemetry-Bjoz29Hl.js";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
5
|
+
import { dirname, join } from "node:path";
|
|
6
|
+
//#region ../cli-core/src/repo/resolve.ts
|
|
7
|
+
/**
|
|
8
|
+
* Resolves an `@devdogsuga/*` package FROM the target repo, not from
|
|
9
|
+
* devtools' own (dlx-isolated) `node_modules`.
|
|
10
|
+
*
|
|
11
|
+
* Ported from the `devtools-dlx` prototype
|
|
12
|
+
* (`/home/sloan/scratchpad/devdogs/prototypes/devtools-dlx/FINDINGS.md`,
|
|
13
|
+
* experiments 2 and 4 — read that file for the two gotchas this module
|
|
14
|
+
* exists to avoid). The short version:
|
|
15
|
+
*
|
|
16
|
+
* - `require.resolve(\`${specifier}/package.json\`)` throws
|
|
17
|
+
* `ERR_PACKAGE_PATH_NOT_EXPORTED` on packages (like `@devdogsuga/env`,
|
|
18
|
+
* `@devdogsuga/open-graph`) whose `exports` map does not list
|
|
19
|
+
* `"./package.json"`, even though the file is on disk.
|
|
20
|
+
* - A resolved path cannot be assumed to contain a literal
|
|
21
|
+
* `node_modules/<specifier>` segment — pnpm workspace-linked packages
|
|
22
|
+
* resolve to their REALPATH (e.g. `packages/open-graph/dist/index.js`),
|
|
23
|
+
* which has no such segment at all.
|
|
24
|
+
* - `require.resolve` ignores whatever `conditions` a caller has set on
|
|
25
|
+
* `tsx`'s `register()` — condition-based resolution (the
|
|
26
|
+
* `devdogs-source` condition) has to be done by hand, reading the
|
|
27
|
+
* target package's own `exports["."]` map.
|
|
28
|
+
*
|
|
29
|
+
* Both problems are solved the same way: walk up from the resolved file's
|
|
30
|
+
* directory to the nearest `package.json` whose own `"name"` field matches
|
|
31
|
+
* the specifier. That works for a real installed dependency AND a
|
|
32
|
+
* workspace symlink, uniformly.
|
|
33
|
+
*/
|
|
34
|
+
/**
|
|
35
|
+
* The installable package name a (possibly subpath) specifier belongs to:
|
|
36
|
+
* `@devdogsuga/env/load` → `@devdogsuga/env`, `foo/bar` → `foo`. A repo's
|
|
37
|
+
* package.json declares the PACKAGE as a dependency, never a subpath, so
|
|
38
|
+
* `findDependent` has to search on this rather than the literal specifier a
|
|
39
|
+
* caller wants to import.
|
|
40
|
+
*/
|
|
41
|
+
function packageNameOf(specifier) {
|
|
42
|
+
const parts = specifier.split("/");
|
|
43
|
+
return specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Scans `apps/*` and `packages/*` under `repoRoot` for the first
|
|
47
|
+
* `package.json` whose `dependencies`/`devDependencies`/`peerDependencies`
|
|
48
|
+
* names `specifier`'s package (see `packageNameOf`). Returns its path
|
|
49
|
+
* relative to `repoRoot`, or `null` if nothing in the repo depends on it.
|
|
50
|
+
*
|
|
51
|
+
* Re-scans the filesystem on every call — fine for a short-lived CLI
|
|
52
|
+
* invocation calling this a handful of times; see FINDINGS item 5 if this
|
|
53
|
+
* is ever called in a hot loop.
|
|
54
|
+
*/
|
|
55
|
+
function findDependent(repoRoot, specifier) {
|
|
56
|
+
const packageName = packageNameOf(specifier);
|
|
57
|
+
for (const group of ["apps", "packages"]) {
|
|
58
|
+
const groupDir = join(repoRoot, group);
|
|
59
|
+
let entries;
|
|
60
|
+
try {
|
|
61
|
+
entries = readdirSync(groupDir);
|
|
62
|
+
} catch {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
for (const entry of entries) {
|
|
66
|
+
const pkgJsonPath = join(groupDir, entry, "package.json");
|
|
67
|
+
if (!existsSync(pkgJsonPath)) continue;
|
|
68
|
+
let pkg;
|
|
69
|
+
try {
|
|
70
|
+
pkg = JSON.parse(readFileSync(pkgJsonPath, "utf8"));
|
|
71
|
+
} catch {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (packageName in {
|
|
75
|
+
...pkg.dependencies,
|
|
76
|
+
...pkg.devDependencies,
|
|
77
|
+
...pkg.peerDependencies
|
|
78
|
+
}) return join(group, entry, "package.json");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
/** Walks up from `startDir` to the nearest `package.json` whose `"name"` matches `specifier`. */
|
|
84
|
+
function findOwningPackageJson(startDir, specifier) {
|
|
85
|
+
const packageName = packageNameOf(specifier);
|
|
86
|
+
let dir = startDir;
|
|
87
|
+
for (;;) {
|
|
88
|
+
const candidate = join(dir, "package.json");
|
|
89
|
+
if (existsSync(candidate)) try {
|
|
90
|
+
if (JSON.parse(readFileSync(candidate, "utf8")).name === packageName) return candidate;
|
|
91
|
+
} catch {}
|
|
92
|
+
const parent = dirname(dir);
|
|
93
|
+
if (parent === dir) throw new Error(`resolveFromRepo: could not find ${specifier}'s own package.json by walking up from ${startDir}`);
|
|
94
|
+
dir = parent;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Resolves `specifier` as `resolutionBase` (a workspace-relative
|
|
99
|
+
* `package.json` path known to depend on it, from `findDependent()`) would
|
|
100
|
+
* see it.
|
|
101
|
+
*
|
|
102
|
+
* `opts.condition`, when given, is NOT passed through to Node's resolver —
|
|
103
|
+
* `require.resolve` ignores tsx's `register({ conditions })` entirely (see
|
|
104
|
+
* this module's header). Instead the target package's own
|
|
105
|
+
* `exports["."]` map is read directly and `exports["."][condition]` is
|
|
106
|
+
* picked, falling back to `.default`.
|
|
107
|
+
*/
|
|
108
|
+
function resolveFromRepo(repoRoot, resolutionBase, specifier, opts) {
|
|
109
|
+
const baseFile = join(repoRoot, resolutionBase);
|
|
110
|
+
const require = createRequire(baseFile);
|
|
111
|
+
let resolvedPath;
|
|
112
|
+
if (opts?.condition) {
|
|
113
|
+
const defaultEntry = require.resolve(specifier);
|
|
114
|
+
const pkgJsonPath = findOwningPackageJson(dirname(defaultEntry), specifier);
|
|
115
|
+
const rootExport = JSON.parse(readFileSync(pkgJsonPath, "utf8")).exports?.["."];
|
|
116
|
+
const conditioned = rootExport && typeof rootExport === "object" ? rootExport[opts.condition] ?? rootExport.default : void 0;
|
|
117
|
+
if (typeof conditioned !== "string") throw new Error(`resolveFromRepo: ${specifier} has no "${opts.condition}" (or "default") export in its exports["."] map`);
|
|
118
|
+
resolvedPath = join(dirname(pkgJsonPath), conditioned);
|
|
119
|
+
} else resolvedPath = require.resolve(specifier);
|
|
120
|
+
const pkgJsonPath = findOwningPackageJson(dirname(resolvedPath), specifier);
|
|
121
|
+
const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf8"));
|
|
122
|
+
return {
|
|
123
|
+
resolvedPath,
|
|
124
|
+
version: pkg.version ?? "0.0.0",
|
|
125
|
+
pkgJsonPath
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
//#region ../cli-core/src/repo/peers.ts
|
|
130
|
+
/**
|
|
131
|
+
* Loads devtools' optional-peer `@devdogsuga/*` libraries — the ones
|
|
132
|
+
* published to npm and consumed by the TARGET repo, which devtools reads at
|
|
133
|
+
* runtime rather than bundling its own copy of (see this package's
|
|
134
|
+
* `package.json`: `peerDependencies` + `peerDependenciesMeta.optional`, and
|
|
135
|
+
* `FINDINGS.md` experiment 2).
|
|
136
|
+
*
|
|
137
|
+
* Every export here is memoized per module (one dynamic `import()` per
|
|
138
|
+
* process, not per call) — this is what makes the module-identity guarantee
|
|
139
|
+
* in FINDINGS experiment 3 hold: every devtools call site that needs
|
|
140
|
+
* `@devdogsuga/env`'s registry gets the exact same module instance the
|
|
141
|
+
* repo's own manifests populated via `declare()`/`define()`, because both
|
|
142
|
+
* resolve to the identical absolute file path and Node's ESM cache is keyed
|
|
143
|
+
* by resolved URL.
|
|
144
|
+
*/
|
|
145
|
+
const moduleCache = /* @__PURE__ */ new Map();
|
|
146
|
+
/** The file URL each peer resolved to through the target repo, set only on
|
|
147
|
+
* that path (not on either plain bare-specifier fallback). See
|
|
148
|
+
* `repoPeerUrl()`. */
|
|
149
|
+
const resolvedUrls = /* @__PURE__ */ new Map();
|
|
150
|
+
/**
|
|
151
|
+
* Dynamically imports `specifier` as the repo would resolve it, memoized so
|
|
152
|
+
* repeated calls in one process return the SAME module instance (required
|
|
153
|
+
* for the env-registry identity guarantee above).
|
|
154
|
+
*
|
|
155
|
+
* Falls back to a plain bare-specifier `import()` when devtools is not
|
|
156
|
+
* running inside a DevDogsUGA checkout at all (`findRepoRoot()` throws
|
|
157
|
+
* `RepoNotFoundError`). That is not a production affordance — under real
|
|
158
|
+
* `pnpm dlx` use, `specifier` is an optional peer devtools' own package
|
|
159
|
+
* never installs, so the fallback import fails with the ordinary
|
|
160
|
+
* `MODULE_NOT_FOUND` either way. It exists so devtools' OWN test suite
|
|
161
|
+
* (which has no target repo to discover, but does have every one of these
|
|
162
|
+
* peers as a real Backstage workspace package) exercises this code against
|
|
163
|
+
* the genuine package rather than needing every test to mock this module.
|
|
164
|
+
*/
|
|
165
|
+
function loadPeer(specifier) {
|
|
166
|
+
const cached = moduleCache.get(specifier);
|
|
167
|
+
if (cached) return cached;
|
|
168
|
+
const promise = (async () => {
|
|
169
|
+
if (process.env.DEVTOOLS_TEST_REPO_ROOT) return import(specifier);
|
|
170
|
+
let repoRoot;
|
|
171
|
+
try {
|
|
172
|
+
repoRoot = findRepoRoot();
|
|
173
|
+
} catch (err) {
|
|
174
|
+
if (err instanceof RepoNotFoundError) return import(specifier);
|
|
175
|
+
throw err;
|
|
176
|
+
}
|
|
177
|
+
const resolutionBase = findDependent(repoRoot, specifier);
|
|
178
|
+
if (!resolutionBase) throw new Error(`Nothing in this repo depends on ${specifier} — expected an app or package under apps/* or packages/* to declare it (dependencies/devDependencies/peerDependencies).`);
|
|
179
|
+
const { resolvedPath } = resolveFromRepo(repoRoot, resolutionBase, specifier);
|
|
180
|
+
const url = pathToFileURL(resolvedPath).href;
|
|
181
|
+
resolvedUrls.set(specifier, url);
|
|
182
|
+
return import(url);
|
|
183
|
+
})();
|
|
184
|
+
moduleCache.set(specifier, promise);
|
|
185
|
+
return promise;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* The file URL `specifier` was loaded from through the target repo, or
|
|
189
|
+
* undefined if it has not been loaded yet or came from a plain bare-specifier
|
|
190
|
+
* `import()` (the test-suite and not-in-a-repo fallbacks above).
|
|
191
|
+
*
|
|
192
|
+
* For code devtools imports that itself names a peer by bare specifier --
|
|
193
|
+
* today only devtools' own `env.ts` manifest. From an installed copy that
|
|
194
|
+
* specifier cannot resolve at all (an optional peer is never installed next
|
|
195
|
+
* to devtools under `pnpm dlx`), and even where it can, the only copy that
|
|
196
|
+
* keeps module identity is this one. `repo/peer-redirect.ts` uses it to
|
|
197
|
+
* point the import here.
|
|
198
|
+
*/
|
|
199
|
+
function repoPeerUrl(specifier) {
|
|
200
|
+
return resolvedUrls.get(specifier);
|
|
201
|
+
}
|
|
202
|
+
let resolvedEnvModule;
|
|
203
|
+
function loadEnv() {
|
|
204
|
+
return loadPeer("@devdogsuga/env").then((mod) => {
|
|
205
|
+
resolvedEnvModule = mod;
|
|
206
|
+
return mod;
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* The synchronous companion to `loadEnv()`, for the handful of call sites
|
|
211
|
+
* (`gh/environments.ts`'s `GITHUB_ENVIRONMENT_SPECS` getters) that cannot
|
|
212
|
+
* become async without changing their own callers' contract — they read a
|
|
213
|
+
* plain array off a getter, not a Promise. Safe ONLY after `loadEnv()` has
|
|
214
|
+
* resolved at least once; every caller of these getters already requires
|
|
215
|
+
* `env/discovery.ts`'s `loadRegistry()` (which calls `loadEnv()` itself) to
|
|
216
|
+
* have run first, via `assertRegistryLoaded()`'s own guard.
|
|
217
|
+
*/
|
|
218
|
+
function getEnvSync() {
|
|
219
|
+
if (!resolvedEnvModule) throw new Error("getEnvSync() called before loadEnv() ever resolved — call loadRegistry() (env/discovery.ts) first.");
|
|
220
|
+
return resolvedEnvModule;
|
|
221
|
+
}
|
|
222
|
+
function loadEnvLoad() {
|
|
223
|
+
return loadPeer("@devdogsuga/env/load");
|
|
224
|
+
}
|
|
225
|
+
function loadEnvSession() {
|
|
226
|
+
return loadPeer("@devdogsuga/env/session");
|
|
227
|
+
}
|
|
228
|
+
//#endregion
|
|
229
|
+
export { repoPeerUrl as a, loadEnvSession as i, loadEnv as n, loadEnvLoad as r, getEnvSync as t };
|