@juxtapose/core 0.5.0 → 0.7.0
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/dist/browser/acquire.d.ts +5 -1
- package/dist/browser/acquire.js +14 -3
- package/dist/browser/acquire.js.map +1 -1
- package/dist/browser/browser.d.ts +1 -1
- package/dist/browser/daemon/client.d.ts +145 -0
- package/dist/browser/daemon/client.js +764 -0
- package/dist/browser/daemon/client.js.map +1 -0
- package/dist/browser/daemon/state.d.ts +99 -0
- package/dist/browser/daemon/state.js +297 -0
- package/dist/browser/daemon/state.js.map +1 -0
- package/dist/browser/daemon/supervisor-main.d.ts +1 -0
- package/dist/browser/daemon/supervisor-main.js +54 -0
- package/dist/browser/daemon/supervisor-main.js.map +1 -0
- package/dist/browser/daemon/supervisor.d.ts +37 -0
- package/dist/browser/daemon/supervisor.js +777 -0
- package/dist/browser/daemon/supervisor.js.map +1 -0
- package/dist/{capture/launch.js → browser/flags.js} +1 -1
- package/dist/browser/flags.js.map +1 -0
- package/dist/browser/index.d.ts +7 -2
- package/dist/browser/index.js +6 -2
- package/dist/browser/index.js.map +1 -1
- package/dist/browser/launch.js +4 -15
- package/dist/browser/launch.js.map +1 -1
- package/dist/browser/page.d.ts +1 -1
- package/dist/browser/private-dir.d.ts +7 -0
- package/dist/browser/private-dir.js +24 -0
- package/dist/browser/private-dir.js.map +1 -0
- package/dist/browser/resolve.d.ts +6 -0
- package/dist/browser/resolve.js +11 -0
- package/dist/browser/resolve.js.map +1 -1
- package/dist/browser/transport.d.ts +12 -0
- package/dist/browser/transport.js +26 -1
- package/dist/browser/transport.js.map +1 -1
- package/dist/browser/types.d.ts +4 -0
- package/dist/browser/types.js +2 -0
- package/dist/browser/types.js.map +1 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +9 -4
- package/dist/types.js +0 -2
- package/dist/types.js.map +1 -1
- package/package.json +2 -1
- package/dist/capture/launch.js.map +0 -1
- /package/dist/{capture/launch.d.ts → browser/flags.d.ts} +0 -0
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { Browser } from './browser.js';
|
|
2
|
+
import { connectWarmBrowser } from './daemon/client.js';
|
|
2
3
|
import type { SpawnFn } from './launch.js';
|
|
3
4
|
export interface AcquireOptions {
|
|
4
5
|
/** `--chrome <path>`; wins over JUX_CHROME and the caches. */
|
|
5
6
|
chrome?: string;
|
|
6
7
|
env?: NodeJS.ProcessEnv;
|
|
8
|
+
/** Fresh launches only; the daemon spawns its own Chrome. */
|
|
7
9
|
spawn?: SpawnFn;
|
|
8
|
-
/**
|
|
10
|
+
/** `--no-daemon`. */
|
|
9
11
|
noDaemon?: boolean;
|
|
12
|
+
/** Test seam. */
|
|
13
|
+
connectWarm?: typeof connectWarmBrowser;
|
|
10
14
|
}
|
|
11
15
|
export declare function acquireBrowser(opts?: AcquireOptions): Promise<Browser>;
|
package/dist/browser/acquire.js
CHANGED
|
@@ -2,14 +2,25 @@
|
|
|
2
2
|
//
|
|
3
3
|
// The one place that decides where a Browser comes from. Every command and
|
|
4
4
|
// the review server call this; PR 2 adds the warm daemon here and nowhere
|
|
5
|
-
// else, so no caller changes when the policy does.
|
|
5
|
+
// else, so no caller changes when the policy does (Ruling 3).
|
|
6
6
|
import { Browser } from './browser.js';
|
|
7
|
-
import {
|
|
7
|
+
import { connectWarmBrowser, daemonEnabled, daemonIdleMinutes } from './daemon/client.js';
|
|
8
|
+
import { daemonSupported } from './daemon/state.js';
|
|
9
|
+
import { juxStateDir, noBrowserMessage, resolveChrome } from './resolve.js';
|
|
8
10
|
export async function acquireBrowser(opts = {}) {
|
|
9
11
|
const env = opts.env ?? process.env;
|
|
10
12
|
const resolved = resolveChrome({ flag: opts.chrome, env });
|
|
11
13
|
if (!resolved)
|
|
12
14
|
throw new Error(noBrowserMessage());
|
|
13
|
-
|
|
15
|
+
// Ruling 22: no daemon on Windows; CI and the opt-outs get PR 1's path exactly.
|
|
16
|
+
if (!daemonSupported() || !daemonEnabled(env, opts.noDaemon))
|
|
17
|
+
return Browser.launch({ executablePath: resolved.path, spawn: opts.spawn });
|
|
18
|
+
const { browser } = await (opts.connectWarm ?? connectWarmBrowser)({
|
|
19
|
+
executablePath: resolved.path,
|
|
20
|
+
stateDir: juxStateDir({ env }),
|
|
21
|
+
env,
|
|
22
|
+
idleMinutes: daemonIdleMinutes(env),
|
|
23
|
+
});
|
|
24
|
+
return browser;
|
|
14
25
|
}
|
|
15
26
|
//# sourceMappingURL=acquire.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"acquire.js","sourceRoot":"","sources":["../../src/browser/acquire.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,EAAE;AACF,2EAA2E;AAC3E,0EAA0E;AAC1E,
|
|
1
|
+
{"version":3,"file":"acquire.js","sourceRoot":"","sources":["../../src/browser/acquire.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,EAAE;AACF,2EAA2E;AAC3E,0EAA0E;AAC1E,8DAA8D;AAE9D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1F,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAc5E,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAuB,EAAE;IAC5D,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC;IACpC,MAAM,QAAQ,GAAG,aAAa,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IAC3D,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACnD,gFAAgF;IAChF,IAAI,CAAC,eAAe,EAAE,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,QAAQ,CAAC;QAC1D,OAAO,OAAO,CAAC,MAAM,CAAC,EAAE,cAAc,EAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IAC9E,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,WAAW,IAAI,kBAAkB,CAAC,CAAC;QACjE,cAAc,EAAE,QAAQ,CAAC,IAAI;QAC7B,QAAQ,EAAE,WAAW,CAAC,EAAE,GAAG,EAAE,CAAC;QAC9B,GAAG;QACH,WAAW,EAAE,iBAAiB,CAAC,GAAG,CAAC;KACpC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { type ChildProcess } from 'node:child_process';
|
|
2
|
+
import { Browser } from '../browser.js';
|
|
3
|
+
import { type DaemonPaths, type DaemonState } from './state.js';
|
|
4
|
+
export interface SpawnArgs {
|
|
5
|
+
executablePath: string;
|
|
6
|
+
args: string[];
|
|
7
|
+
stateDir: string;
|
|
8
|
+
key: string;
|
|
9
|
+
idleMinutes: number;
|
|
10
|
+
}
|
|
11
|
+
export interface WarmOptions {
|
|
12
|
+
executablePath: string;
|
|
13
|
+
args?: string[];
|
|
14
|
+
stateDir: string;
|
|
15
|
+
/** The caller's environment: the key is computed from it and the daemon runs with it. */
|
|
16
|
+
env?: NodeJS.ProcessEnv;
|
|
17
|
+
idleMinutes?: number;
|
|
18
|
+
/** Test seam: start a daemon for these args with this env. Default: a detached supervisor-main. */
|
|
19
|
+
spawnDaemon?: (args: SpawnArgs, env: NodeJS.ProcessEnv) => ChildProcess | void;
|
|
20
|
+
connectTimeoutMs?: number;
|
|
21
|
+
/** Test seam: see CommandReader. */
|
|
22
|
+
readCommand?: CommandReader;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The command line of a pid, '' when no such process exists, null when this
|
|
26
|
+
* machine gives no way to read it (no /proc and no ps).
|
|
27
|
+
*/
|
|
28
|
+
export type CommandReader = (pid: number) => Promise<string | null>;
|
|
29
|
+
/**
|
|
30
|
+
* `CI` is the convention; these are the systems that do not set it (GitHub
|
|
31
|
+
* Actions does, but `CI` can be unset in a step's env; Jenkins sets BUILD_ID).
|
|
32
|
+
*/
|
|
33
|
+
export declare const CI_ENV_VARS: readonly ["CI", "GITHUB_ACTIONS", "JENKINS_URL", "BUILD_ID", "TEAMCITY_VERSION", "BUILDKITE", "GITLAB_CI", "TF_BUILD", "CODEBUILD_BUILD_ID"];
|
|
34
|
+
/** Off with `--no-daemon`, JUX_NO_DAEMON (any truthy value), or any CI marker. */
|
|
35
|
+
export declare function daemonEnabled(env: NodeJS.ProcessEnv, flagNoDaemon?: boolean): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* JUX_BROWSER_IDLE_MINUTES within [1, 35000], else the default (undefined).
|
|
38
|
+
* The upper bound is setTimeout's: 100000 minutes overflowed to a 1 ms idle
|
|
39
|
+
* timer and the daemon quit as soon as its last client left. Infinity used to
|
|
40
|
+
* pass too, became null in the spawn JSON, and every supervisor exited 1.
|
|
41
|
+
*/
|
|
42
|
+
export declare function daemonIdleMinutes(env: NodeJS.ProcessEnv): number | undefined;
|
|
43
|
+
type StatusOptions = Pick<WarmOptions, 'executablePath' | 'stateDir' | 'args' | 'env' | 'readCommand'>;
|
|
44
|
+
/**
|
|
45
|
+
* A hash of `entry` and every file it reaches through relative imports. The
|
|
46
|
+
* supervisor that serves a client is whichever build spawned it; after a
|
|
47
|
+
* rebuild or an upgrade the supervisor's flags, allowlist or protocol can
|
|
48
|
+
* differ from this client's, and reusing it would render with the old code.
|
|
49
|
+
* A missing file hashes as missing (vitest runs the .ts sources, where the
|
|
50
|
+
* .js entry does not exist).
|
|
51
|
+
*/
|
|
52
|
+
export declare function buildIdentity(entry: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* `<@juxtapose/core version>+<build hash of the supervisor entry>`. Cached
|
|
55
|
+
* per process, but rechecked by stat on every call (about ten files): a
|
|
56
|
+
* Storybook addon or review server that stays up across a `pnpm build` kept
|
|
57
|
+
* the old key while spawning the new supervisor-main from disk, so it never
|
|
58
|
+
* shared a daemon with the CLI and ran a mixed build (PR #102 re-review N5).
|
|
59
|
+
*/
|
|
60
|
+
export declare function juxIdentity(entry?: string): string;
|
|
61
|
+
/**
|
|
62
|
+
* The daemon key for these options. It covers everything a fresh launch
|
|
63
|
+
* would render with: the binary (path, size, mtime), the pin, the full Chrome
|
|
64
|
+
* command line jux would launch, the render env and this jux build. Keys that
|
|
65
|
+
* differ name different sockets, so a daemon from an older jux or other
|
|
66
|
+
* flags is never reused; it idles out.
|
|
67
|
+
*/
|
|
68
|
+
export declare function daemonKeyFor(opts: Pick<WarmOptions, 'executablePath' | 'args' | 'env'>, jux?: string): string;
|
|
69
|
+
/**
|
|
70
|
+
* Start a detached supervisor-main with the caller's env: the key was computed
|
|
71
|
+
* from that env, so Chrome must render with it (TZ, LANG, fonts), not with
|
|
72
|
+
* whatever env this process happens to have. Detached in its own process
|
|
73
|
+
* group with stdio ignored: it outlives this jux, and a Ctrl-C to this
|
|
74
|
+
* terminal must not reach it.
|
|
75
|
+
*
|
|
76
|
+
* NODE_OPTIONS is not a render input and is dropped: VS Code's debug
|
|
77
|
+
* terminal (`--require …/bootloader.js`), `--inspect` or `--cpu-prof` would
|
|
78
|
+
* otherwise attach to a process that lives for hours, and `--cpu-prof` would
|
|
79
|
+
* write a profile into the project when it finally exits. The cwd is the
|
|
80
|
+
* state dir for the same reason: not the project that happened to start it.
|
|
81
|
+
*/
|
|
82
|
+
export declare function spawnSupervisor(args: SpawnArgs, env: NodeJS.ProcessEnv, entry?: string): ChildProcess;
|
|
83
|
+
export declare const defaultReadCommand: CommandReader;
|
|
84
|
+
/**
|
|
85
|
+
* Remove the files of the daemon `s` describes, and nothing newer. The state
|
|
86
|
+
* goes only if it still names the same pid and start time: between our read
|
|
87
|
+
* and this call another client may have spawned a replacement that wrote its
|
|
88
|
+
* own (the review traced `jux browser stop` deleting a new daemon's files
|
|
89
|
+
* while the old one's Chrome took 5 s to close, orphaning the new one). The
|
|
90
|
+
* socket goes only if it is the path that state named and nothing listens on
|
|
91
|
+
* it, compared by inode so a socket bound meanwhile is left alone.
|
|
92
|
+
*/
|
|
93
|
+
export declare function clearStaleState(paths: DaemonPaths, s: DaemonState): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* The warm daemon's browser, or a private one if the daemon cannot be had.
|
|
96
|
+
* Never rejects because of the daemon; a private launch failure rejects as in
|
|
97
|
+
* PR 1.
|
|
98
|
+
*/
|
|
99
|
+
export declare function connectWarmBrowser(opts: WarmOptions): Promise<{
|
|
100
|
+
browser: Browser;
|
|
101
|
+
warm: boolean;
|
|
102
|
+
}>;
|
|
103
|
+
/**
|
|
104
|
+
* Start (or find) the daemon without a private fallback: `jux browser start`
|
|
105
|
+
* must not launch and close a whole Chrome just to report that the daemon
|
|
106
|
+
* could not start.
|
|
107
|
+
*/
|
|
108
|
+
export declare function startDaemon(opts: WarmOptions): Promise<{
|
|
109
|
+
state: DaemonState;
|
|
110
|
+
} | {
|
|
111
|
+
error: string;
|
|
112
|
+
}>;
|
|
113
|
+
/** The running daemon for this fingerprint, or null. A dead daemon's files are cleared. */
|
|
114
|
+
export declare function daemonStatus(given: StatusOptions): Promise<DaemonState | null>;
|
|
115
|
+
type ListOptions = Pick<WarmOptions, 'stateDir' | 'readCommand'>;
|
|
116
|
+
/** Every live daemon in `stateDir`, whichever key; dead ones' files are cleared on the way. */
|
|
117
|
+
export declare function listDaemons(opts: ListOptions): Promise<DaemonState[]>;
|
|
118
|
+
/**
|
|
119
|
+
* What stopping one daemon did: `none` (nothing running, or the state named a
|
|
120
|
+
* pid that is not ours and nothing listened on its socket), `stopped` (its
|
|
121
|
+
* pid is gone), `unverified` (a live pid we could not confirm as ours:
|
|
122
|
+
* nothing is signalled or removed), or `running` (ours, signalled, and still
|
|
123
|
+
* alive after 2 s).
|
|
124
|
+
*/
|
|
125
|
+
export type StopResult = {
|
|
126
|
+
outcome: 'none' | 'stopped';
|
|
127
|
+
} | {
|
|
128
|
+
outcome: 'unverified' | 'running';
|
|
129
|
+
pid: number;
|
|
130
|
+
reason: string;
|
|
131
|
+
};
|
|
132
|
+
/** Stop the daemon for this fingerprint only. */
|
|
133
|
+
export declare function stopDaemon(given: StatusOptions): Promise<StopResult>;
|
|
134
|
+
/** One daemon `stopAllDaemons` found, and what stopping it did. */
|
|
135
|
+
export type DaemonStop = {
|
|
136
|
+
key: string;
|
|
137
|
+
pid: number;
|
|
138
|
+
} & StopResult;
|
|
139
|
+
/**
|
|
140
|
+
* Stop every daemon in `stateDir`, whichever key it runs under, each through
|
|
141
|
+
* the same verification as `stopDaemon`. Entries whose outcome is `none`
|
|
142
|
+
* (stale files, reused pids) are cleared and left out.
|
|
143
|
+
*/
|
|
144
|
+
export declare function stopAllDaemons(opts: ListOptions): Promise<DaemonStop[]>;
|
|
145
|
+
export {};
|