@bakers-build/ui 0.0.0-stage → 0.1.0-next.42
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/LICENSE +21 -0
- package/README.md +4 -2
- package/dist/src/bun-runtime.d.ts +84 -0
- package/dist/src/bun-runtime.js +149 -0
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.js +10 -0
- package/dist/src/preset.d.ts +2 -0
- package/dist/src/preset.js +8 -0
- package/dist/src/route.d.ts +119 -0
- package/dist/src/route.js +271 -0
- package/dist/src/ui-home.d.ts +69 -0
- package/dist/src/ui-home.js +67 -0
- package/dist/src/ui.d.ts +155 -0
- package/dist/src/ui.js +550 -0
- package/package.json +33 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 keytec GmbH
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @bakers-build/ui
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
bakers.build project-first cockpit: the `bakers ui` command plugin (renderer resolved dynamically).
|
|
4
|
+
|
|
5
|
+
Part of the bakers.build CLI. Install the meta package `@bakers-build/bakers` to get the `bakers` CLI with all addons. Source: https://git.key-tec.de/bakers-build/bakers-build (bakers-cli/).
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* BB-326 — `@opentui/core` reaches its Zig core through FFI, which Node does
|
|
3
|
+
* not expose before 26.1 (and then only behind `--experimental-ffi`). So the
|
|
4
|
+
* cockpit runs under the `bun` that `@bakers-build/addon-bakers-ui` installs.
|
|
5
|
+
*
|
|
6
|
+
* `process.versions.bun` is the loop guard: it is defined under Bun and nowhere
|
|
7
|
+
* else, so a re-exec can never recurse and no sentinel variable is introduced.
|
|
8
|
+
* `--print-config` deliberately stays on Node — it is what a human runs when the
|
|
9
|
+
* cockpit will not start, and a diagnostic that needs the thing it diagnoses is
|
|
10
|
+
* worth nothing.
|
|
11
|
+
*
|
|
12
|
+
* `injectedRenderer` is the same argument one step further: the runtime is
|
|
13
|
+
* needed by the DYNAMICALLY-resolved `@bakers-build/addon-bakers-ui`, so a caller that
|
|
14
|
+
* supplies its own `runUi` never reaches OpenTUI and must not be re-execed.
|
|
15
|
+
*/
|
|
16
|
+
export declare function shouldReExecUnderBun(input: {
|
|
17
|
+
bunVersion?: string | undefined;
|
|
18
|
+
noBun?: string | undefined;
|
|
19
|
+
printConfig: boolean;
|
|
20
|
+
injectedRenderer?: boolean | undefined;
|
|
21
|
+
}): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Resolve the `bun` executable from the renderer addon's own base — the same
|
|
24
|
+
* dynamic seam `loadBbUiModule` resolves the renderer through, so the one
|
|
25
|
+
* dynamic edge in this package stays one edge.
|
|
26
|
+
*
|
|
27
|
+
* The manifest is read with a plain `readFileSync` rather than through the
|
|
28
|
+
* resolver: `createRequire(...).resolve()` gives an absolute path, and reading a
|
|
29
|
+
* known path needs no module system. `bun`'s manifest is resolved by name
|
|
30
|
+
* (`bun/package.json`) because the launcher's location is the thing we are
|
|
31
|
+
* looking up; its `bin` entry is a relative path from the manifest's directory.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveBunBinary(bases: string[]): string | undefined;
|
|
34
|
+
export type LibcFamily = 'glibc' | 'musl' | 'unknown';
|
|
35
|
+
/**
|
|
36
|
+
* BB-326 (A1) — which C library is this host running?
|
|
37
|
+
*
|
|
38
|
+
* Two evidence sources, cheapest first, both injected so the answer never
|
|
39
|
+
* depends on the machine the test runs on:
|
|
40
|
+
*
|
|
41
|
+
* - `/proc/self/maps` (~0.1 ms) names the interpreter the kernel ACTUALLY
|
|
42
|
+
* mapped into this process. That is the strongest signal available: a glibc
|
|
43
|
+
* box with a musl-compat package installed has `/lib/ld-musl-x86_64.so.1` on
|
|
44
|
+
* disk but never maps it, so the `existsSync` shortcut would misread it.
|
|
45
|
+
* - the node report's `header.glibcVersionRuntime` (~4.6 ms) — present on
|
|
46
|
+
* glibc, absent on musl. It needs no filesystem, which is why it is the
|
|
47
|
+
* fallback for a container that hides `/proc`. Verified on all four
|
|
48
|
+
* combinations: node+glibc `"2.43"`, bun+glibc `"2.43"`, node+musl absent,
|
|
49
|
+
* bun+musl absent (node:22-alpine).
|
|
50
|
+
*
|
|
51
|
+
* Non-Linux returns `unknown` and NOT `musl`: the report header omits
|
|
52
|
+
* `glibcVersionRuntime` on macOS and Windows too, so reading its absence as
|
|
53
|
+
* musl would mislabel every mac. musl is a Linux question.
|
|
54
|
+
*
|
|
55
|
+
* Inconclusive is `unknown`, and `unknown` changes nothing — glibc is upstream's
|
|
56
|
+
* default and the overwhelmingly common case, so a failed probe must degrade to
|
|
57
|
+
* today's behaviour rather than to a guess.
|
|
58
|
+
*/
|
|
59
|
+
export declare function detectLibcFamily(probe: {
|
|
60
|
+
platform: string;
|
|
61
|
+
readProcSelfMaps: () => string | undefined;
|
|
62
|
+
readReportLibc: () => LibcFamily;
|
|
63
|
+
}): LibcFamily;
|
|
64
|
+
/** The real probe. Every leg is wrapped: an inconclusive read must not throw. */
|
|
65
|
+
export declare function nodeLibcFamily(): LibcFamily;
|
|
66
|
+
/**
|
|
67
|
+
* BB-326 (A1) — make the cockpit start on musl with no manual step.
|
|
68
|
+
*
|
|
69
|
+
* `@opentui/core@0.5.1` selects its native platform package from
|
|
70
|
+
* `process.env.OPENTUI_LIBC` and DEFAULTS TO GLIBC — it runs no probe of its
|
|
71
|
+
* own. Both prebuilts are always on disk (npm and pnpm filter optional deps by
|
|
72
|
+
* `os`/`cpu`, never by libc), so on Alpine the wrong one loads and the first
|
|
73
|
+
* frame dies with `Error loading shared library ld-linux-x86-64.so.2 (needed by
|
|
74
|
+
* libopentui.so)`. Requiring the user to export the variable IS a manual step,
|
|
75
|
+
* so A1 is only met once we set it.
|
|
76
|
+
*
|
|
77
|
+
* An explicit value always wins: an operator who exports `OPENTUI_LIBC=glibc`
|
|
78
|
+
* on a musl box is doing something deliberate, and a probe must not overrule
|
|
79
|
+
* them. Because a set value short-circuits BEFORE `detect` is called, the Bun
|
|
80
|
+
* child — which inherits this env — never pays for the probe a second time.
|
|
81
|
+
*/
|
|
82
|
+
export declare function applyOpentuiLibc(env: NodeJS.ProcessEnv, detect?: () => LibcFamily): void;
|
|
83
|
+
/** Replace this process with the same command under Bun. Never returns. */
|
|
84
|
+
export declare function reExecUnderBun(bin: string, argv: string[]): never;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
/**
|
|
6
|
+
* BB-326 — `@opentui/core` reaches its Zig core through FFI, which Node does
|
|
7
|
+
* not expose before 26.1 (and then only behind `--experimental-ffi`). So the
|
|
8
|
+
* cockpit runs under the `bun` that `@bakers-build/addon-bakers-ui` installs.
|
|
9
|
+
*
|
|
10
|
+
* `process.versions.bun` is the loop guard: it is defined under Bun and nowhere
|
|
11
|
+
* else, so a re-exec can never recurse and no sentinel variable is introduced.
|
|
12
|
+
* `--print-config` deliberately stays on Node — it is what a human runs when the
|
|
13
|
+
* cockpit will not start, and a diagnostic that needs the thing it diagnoses is
|
|
14
|
+
* worth nothing.
|
|
15
|
+
*
|
|
16
|
+
* `injectedRenderer` is the same argument one step further: the runtime is
|
|
17
|
+
* needed by the DYNAMICALLY-resolved `@bakers-build/addon-bakers-ui`, so a caller that
|
|
18
|
+
* supplies its own `runUi` never reaches OpenTUI and must not be re-execed.
|
|
19
|
+
*/
|
|
20
|
+
export function shouldReExecUnderBun(input) {
|
|
21
|
+
if (input.printConfig)
|
|
22
|
+
return false;
|
|
23
|
+
if (input.injectedRenderer)
|
|
24
|
+
return false;
|
|
25
|
+
if (input.bunVersion !== undefined)
|
|
26
|
+
return false;
|
|
27
|
+
if (input.noBun !== undefined && input.noBun !== '')
|
|
28
|
+
return false;
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolve the `bun` executable from the renderer addon's own base — the same
|
|
33
|
+
* dynamic seam `loadBbUiModule` resolves the renderer through, so the one
|
|
34
|
+
* dynamic edge in this package stays one edge.
|
|
35
|
+
*
|
|
36
|
+
* The manifest is read with a plain `readFileSync` rather than through the
|
|
37
|
+
* resolver: `createRequire(...).resolve()` gives an absolute path, and reading a
|
|
38
|
+
* known path needs no module system. `bun`'s manifest is resolved by name
|
|
39
|
+
* (`bun/package.json`) because the launcher's location is the thing we are
|
|
40
|
+
* looking up; its `bin` entry is a relative path from the manifest's directory.
|
|
41
|
+
*/
|
|
42
|
+
export function resolveBunBinary(bases) {
|
|
43
|
+
for (const base of bases) {
|
|
44
|
+
try {
|
|
45
|
+
const addon = createRequire(base).resolve('@bakers-build/addon-bakers-ui');
|
|
46
|
+
const manifest = createRequire(addon).resolve('bun/package.json');
|
|
47
|
+
const { bin } = JSON.parse(readFileSync(manifest, 'utf8'));
|
|
48
|
+
const relative = typeof bin === 'string' ? bin : bin?.bun;
|
|
49
|
+
if (relative)
|
|
50
|
+
return join(dirname(manifest), relative);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// try the next base
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The dynamic linker musl installs. Matched as a path segment ending in `.so`
|
|
60
|
+
* rather than a bare `includes('ld-musl')`, so an unrelated mapped file whose
|
|
61
|
+
* NAME happens to carry the string cannot be mistaken for the interpreter.
|
|
62
|
+
*/
|
|
63
|
+
const MUSL_INTERPRETER = /\/ld-musl-[^/\s]*\.so/;
|
|
64
|
+
/**
|
|
65
|
+
* BB-326 (A1) — which C library is this host running?
|
|
66
|
+
*
|
|
67
|
+
* Two evidence sources, cheapest first, both injected so the answer never
|
|
68
|
+
* depends on the machine the test runs on:
|
|
69
|
+
*
|
|
70
|
+
* - `/proc/self/maps` (~0.1 ms) names the interpreter the kernel ACTUALLY
|
|
71
|
+
* mapped into this process. That is the strongest signal available: a glibc
|
|
72
|
+
* box with a musl-compat package installed has `/lib/ld-musl-x86_64.so.1` on
|
|
73
|
+
* disk but never maps it, so the `existsSync` shortcut would misread it.
|
|
74
|
+
* - the node report's `header.glibcVersionRuntime` (~4.6 ms) — present on
|
|
75
|
+
* glibc, absent on musl. It needs no filesystem, which is why it is the
|
|
76
|
+
* fallback for a container that hides `/proc`. Verified on all four
|
|
77
|
+
* combinations: node+glibc `"2.43"`, bun+glibc `"2.43"`, node+musl absent,
|
|
78
|
+
* bun+musl absent (node:22-alpine).
|
|
79
|
+
*
|
|
80
|
+
* Non-Linux returns `unknown` and NOT `musl`: the report header omits
|
|
81
|
+
* `glibcVersionRuntime` on macOS and Windows too, so reading its absence as
|
|
82
|
+
* musl would mislabel every mac. musl is a Linux question.
|
|
83
|
+
*
|
|
84
|
+
* Inconclusive is `unknown`, and `unknown` changes nothing — glibc is upstream's
|
|
85
|
+
* default and the overwhelmingly common case, so a failed probe must degrade to
|
|
86
|
+
* today's behaviour rather than to a guess.
|
|
87
|
+
*/
|
|
88
|
+
export function detectLibcFamily(probe) {
|
|
89
|
+
if (probe.platform !== 'linux')
|
|
90
|
+
return 'unknown';
|
|
91
|
+
const maps = probe.readProcSelfMaps();
|
|
92
|
+
if (maps !== undefined)
|
|
93
|
+
return MUSL_INTERPRETER.test(maps) ? 'musl' : 'glibc';
|
|
94
|
+
return probe.readReportLibc();
|
|
95
|
+
}
|
|
96
|
+
/** The real probe. Every leg is wrapped: an inconclusive read must not throw. */
|
|
97
|
+
export function nodeLibcFamily() {
|
|
98
|
+
return detectLibcFamily({
|
|
99
|
+
platform: process.platform,
|
|
100
|
+
readProcSelfMaps: () => {
|
|
101
|
+
try {
|
|
102
|
+
return readFileSync('/proc/self/maps', 'utf8');
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
readReportLibc: () => {
|
|
109
|
+
try {
|
|
110
|
+
const report = process.report.getReport();
|
|
111
|
+
return report.header?.glibcVersionRuntime ? 'glibc' : 'musl';
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return 'unknown';
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* BB-326 (A1) — make the cockpit start on musl with no manual step.
|
|
121
|
+
*
|
|
122
|
+
* `@opentui/core@0.5.1` selects its native platform package from
|
|
123
|
+
* `process.env.OPENTUI_LIBC` and DEFAULTS TO GLIBC — it runs no probe of its
|
|
124
|
+
* own. Both prebuilts are always on disk (npm and pnpm filter optional deps by
|
|
125
|
+
* `os`/`cpu`, never by libc), so on Alpine the wrong one loads and the first
|
|
126
|
+
* frame dies with `Error loading shared library ld-linux-x86-64.so.2 (needed by
|
|
127
|
+
* libopentui.so)`. Requiring the user to export the variable IS a manual step,
|
|
128
|
+
* so A1 is only met once we set it.
|
|
129
|
+
*
|
|
130
|
+
* An explicit value always wins: an operator who exports `OPENTUI_LIBC=glibc`
|
|
131
|
+
* on a musl box is doing something deliberate, and a probe must not overrule
|
|
132
|
+
* them. Because a set value short-circuits BEFORE `detect` is called, the Bun
|
|
133
|
+
* child — which inherits this env — never pays for the probe a second time.
|
|
134
|
+
*/
|
|
135
|
+
export function applyOpentuiLibc(env, detect = nodeLibcFamily) {
|
|
136
|
+
const current = env.OPENTUI_LIBC;
|
|
137
|
+
if (current !== undefined && current !== '')
|
|
138
|
+
return;
|
|
139
|
+
if (detect() === 'musl')
|
|
140
|
+
env.OPENTUI_LIBC = 'musl';
|
|
141
|
+
}
|
|
142
|
+
/** Replace this process with the same command under Bun. Never returns. */
|
|
143
|
+
export function reExecUnderBun(bin, argv) {
|
|
144
|
+
const result = spawnSync(bin, argv.slice(1), {
|
|
145
|
+
stdio: 'inherit',
|
|
146
|
+
env: process.env,
|
|
147
|
+
});
|
|
148
|
+
process.exit(result.status ?? 1);
|
|
149
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { type HereContext, isTicketIdentifier, isUuid, normalizeRepoUrl, type ParsedTarget, parseTarget, parseTicketIdentifierFromBranch, resolveHere, type TargetInputs, UI_VIEW_MODES, type UiInitialRoute, type UiViewMode, } from './route.js';
|
|
2
|
+
export { cmdUi, default, type RunBbUiOptions, type UiDeps } from './ui.js';
|
|
3
|
+
export { type HomeConnection, type ProjectRooting, resolveAgentCommand, resolveProjectRooting, } from './ui-home.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// BB-201 / BB-219: `@bakers-build/ui` — the `bakers ui` command plugin. Its
|
|
2
|
+
// connection is resolved by core's `loadBbConfig` (explicit → project
|
|
3
|
+
// walk-up → home → shipped fallback), so running it inside a project uses that
|
|
4
|
+
// project's control plane; this package owns no precedence rule of its own.
|
|
5
|
+
// The default export is the `BbCommandPlugin` the host mounts; `cmdUi` + the
|
|
6
|
+
// bootstrap helpers are exported for tests.
|
|
7
|
+
// BB-223: the deep-link grammar (the pure CLI seam).
|
|
8
|
+
export { isTicketIdentifier, isUuid, normalizeRepoUrl, parseTarget, parseTicketIdentifierFromBranch, resolveHere, UI_VIEW_MODES, } from './route.js';
|
|
9
|
+
export { cmdUi, default } from './ui.js';
|
|
10
|
+
export { resolveAgentCommand, resolveProjectRooting, } from './ui-home.js';
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/** How a ticket target is rendered. Mirrors the Drupal `bb_ticket` view modes. */
|
|
2
|
+
export type UiViewMode = 'full' | 'teaser' | 'board_card';
|
|
3
|
+
/** The known `--view` modes, in the order error messages list them. */
|
|
4
|
+
export declare const UI_VIEW_MODES: readonly UiViewMode[];
|
|
5
|
+
/**
|
|
6
|
+
* A deep-link target as the CLI understands it — *unresolved*: it says what to
|
|
7
|
+
* look up and how, never what screen results. Crosses both package boundaries
|
|
8
|
+
* (`RunBbUiOptions` → `BbUiLauncherOptions` → `TuiOptions`) and is mirrored
|
|
9
|
+
* structurally in the renderer, per the `HomeConnection` precedent (Finding 6).
|
|
10
|
+
*/
|
|
11
|
+
export interface UiInitialRoute {
|
|
12
|
+
kind: 'ticket' | 'project' | 'run';
|
|
13
|
+
/**
|
|
14
|
+
* How `value` must be looked up. `'repo'` is the `--here` project rung: the
|
|
15
|
+
* value is a NORMALISED remote URL (`host/path`), matched client-side against
|
|
16
|
+
* the `bb_project.repository.url` of every project (spec Decision 3).
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* `'origin'` (BB-544) is a ticket looked up by its origin issue — `#19`,
|
|
20
|
+
* `bakers#19` or the issue URL — which the server's `filter[search]` resolves.
|
|
21
|
+
*/
|
|
22
|
+
by: 'uuid' | 'identifier' | 'name' | 'repo' | 'origin';
|
|
23
|
+
value: string;
|
|
24
|
+
/** Drupal view mode for a ticket target; default 'full'. */
|
|
25
|
+
view?: UiViewMode | undefined;
|
|
26
|
+
/** Set when the target came from `--here`; error messages only. */
|
|
27
|
+
fromBranch?: string | undefined;
|
|
28
|
+
}
|
|
29
|
+
/** The raw argv inputs `parseTarget` reads (commander's parsed options). */
|
|
30
|
+
export interface TargetInputs {
|
|
31
|
+
target?: string | undefined;
|
|
32
|
+
ticket?: string | undefined;
|
|
33
|
+
project?: string | undefined;
|
|
34
|
+
run?: string | undefined;
|
|
35
|
+
here?: boolean | undefined;
|
|
36
|
+
view?: string | undefined;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* What the grammar made of the argv: nothing (the dashboard), a `--here`
|
|
40
|
+
* request that still needs the ladder walked, or a finished route.
|
|
41
|
+
*/
|
|
42
|
+
export type ParsedTarget = {
|
|
43
|
+
kind: 'here';
|
|
44
|
+
view?: UiViewMode | undefined;
|
|
45
|
+
} | {
|
|
46
|
+
kind: 'route';
|
|
47
|
+
route: UiInitialRoute;
|
|
48
|
+
};
|
|
49
|
+
/** True for an `8-4-4-4-12` hex UUID (case-insensitive). */
|
|
50
|
+
export declare function isUuid(value: string): boolean;
|
|
51
|
+
/** True for a `KEY-123` ticket identifier (case-insensitive). */
|
|
52
|
+
export declare function isTicketIdentifier(value: string): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* The ticket identifier a branch name carries, uppercased, or `null`.
|
|
55
|
+
* Pure and offline — the `--here` ticket rung never needs the network.
|
|
56
|
+
*/
|
|
57
|
+
export declare function parseTicketIdentifierFromBranch(branch: string): string | null;
|
|
58
|
+
/**
|
|
59
|
+
* The grammar: at most ONE target source (the positional, `--ticket`,
|
|
60
|
+
* `--project`, `--run`, `--here`) plus the orthogonal `--view` modifier.
|
|
61
|
+
*
|
|
62
|
+
* Returns `undefined` for no target at all (the dashboard, AC-4), `{kind:'here'}`
|
|
63
|
+
* when the ladder still has to be walked, else the finished route. Throws — with
|
|
64
|
+
* a message naming exactly what went wrong — on a conflict or an unparseable
|
|
65
|
+
* form; the caller turns that into a stderr line and exit 1 before any network
|
|
66
|
+
* work happens.
|
|
67
|
+
*/
|
|
68
|
+
export declare function parseTarget(inputs: TargetInputs): ParsedTarget | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* A git remote URL reduced to a comparable `host/path` identity: scheme,
|
|
71
|
+
* userinfo, port and a trailing `.git` (and trailing slashes) dropped, the whole
|
|
72
|
+
* thing lowercased. Returns `undefined` for anything that carries no host+path.
|
|
73
|
+
*
|
|
74
|
+
* This is REQUIRED, not cosmetic (spec Decision 3): git and the control plane
|
|
75
|
+
* disagree on form. Measured in this repo — `git remote get-url origin` yields
|
|
76
|
+
* `git@git.key-tec.de:bakers-build/bakers-build.git` while `bb_project.repository.url` holds
|
|
77
|
+
* `ssh://git@git.key-tec.de/bakers-build/bakers-build.git`. Both must reduce to
|
|
78
|
+
* `git.key-tec.de/bakers-build/bakers-build` or the `--here` project rung never matches.
|
|
79
|
+
*
|
|
80
|
+
* | raw | normalised |
|
|
81
|
+
* |-----------------------------------------|---------------------|
|
|
82
|
+
* | `git@host:bakers-build/bakers-build.git` | `host/bakers-build/bakers-build` |
|
|
83
|
+
* | `ssh://git@host/bakers-build/bakers-build.git` | `host/bakers-build/bakers-build` |
|
|
84
|
+
* | `ssh://git@host:2222/bakers-build/bakers-build.git` | `host/bakers-build/bakers-build` |
|
|
85
|
+
* | `https://host/bakers-build/bakers-build.git` | `host/bakers-build/bakers-build` |
|
|
86
|
+
*/
|
|
87
|
+
export declare function normalizeRepoUrl(raw: string | undefined): string | undefined;
|
|
88
|
+
/** Everything `resolveHere` needs, all injected — two git reads, nothing else. */
|
|
89
|
+
export interface HereContext {
|
|
90
|
+
cwd: string;
|
|
91
|
+
view?: UiViewMode | undefined;
|
|
92
|
+
/** The current branch of `cwd` (git resolves it per worktree). May reject. */
|
|
93
|
+
readBranch: (cwd: string) => Promise<string>;
|
|
94
|
+
/**
|
|
95
|
+
* The cwd's `origin` remote URL, raw. Git resolves it per worktree too, and a
|
|
96
|
+
* worktree shares its main clone's remote — which is exactly the right answer
|
|
97
|
+
* (spec Decision 3). May reject (not a repo, no `origin`).
|
|
98
|
+
*/
|
|
99
|
+
readRemoteUrl: (cwd: string) => Promise<string>;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* `--here`: derive the target from the cwd (spec Decision 3).
|
|
103
|
+
*
|
|
104
|
+
* ```
|
|
105
|
+
* branch carries a ticket identifier? → that TICKET (feat/bb-223-… → BB-223)
|
|
106
|
+
* else: the cwd's git remote → the PROJECT(s) whose repository claims it
|
|
107
|
+
* else → a named error
|
|
108
|
+
* ```
|
|
109
|
+
*
|
|
110
|
+
* The BRANCH decides the first rung, not git mechanics: a worktree on a
|
|
111
|
+
* non-ticket branch and the main clone on a ticket branch both come out right.
|
|
112
|
+
*
|
|
113
|
+
* The project rung stops HERE at a normalised repo identity — matching it
|
|
114
|
+
* against `bb_project.repository` needs a JSON:API client, which the entry does
|
|
115
|
+
* not have (Finding 1), so `by: 'repo'` travels to the renderer's `resolveRoute`
|
|
116
|
+
* and the ambiguity ruling (one match / a picker / an error) is made there
|
|
117
|
+
* (Decisions 5 + 14).
|
|
118
|
+
*/
|
|
119
|
+
export declare function resolveHere(ctx: HereContext): Promise<UiInitialRoute>;
|