specrails-hub 1.61.1 → 1.61.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specrails-hub",
3
- "version": "1.61.1",
3
+ "version": "1.61.2",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.bundledChromiumCandidates = bundledChromiumCandidates;
6
+ exports.discoverChromiumExecutable = discoverChromiumExecutable;
7
7
  exports.resolveBundledChromiumPath = resolveBundledChromiumPath;
8
8
  const fs_1 = __importDefault(require("fs"));
9
9
  const path_1 = __importDefault(require("path"));
@@ -11,23 +11,20 @@ const path_1 = __importDefault(require("path"));
11
11
  * Resolve the Chromium executable the browser-capture feature should launch.
12
12
  *
13
13
  * Mirrors the bundled-runtime resolution used by `path-resolver.ts` for node/git:
14
- * in desktop mode (`SPECRAILS_IS_DESKTOP=1`) we existence-gate a bundled Chromium
15
- * shipped under `<runtimes>/chromium/` (declared in tauri.conf.json via the
16
- * `runtimes/**` glob, codesigned in CI before `tauri build`). When the bundled
17
- * binary is absent (dev, a runtimes-less build, or a partial extraction) we return
18
- * `null` so Playwright falls back to its own managed Chromium download — never
19
- * dead-ending the feature.
14
+ * in desktop mode (`SPECRAILS_IS_DESKTOP=1`) we DISCOVER a bundled Chromium shipped
15
+ * under `<runtimes>/chromium/` (declared in tauri.conf.json via the `runtimes/**`
16
+ * glob, codesigned in CI before `tauri build`). When no bundle is present (dev, a
17
+ * runtimes-less build, or a partial extraction) we return `null` so Playwright
18
+ * falls back to its own managed browser — never dead-ending the feature.
20
19
  *
21
- * Layout (matches the CI assembly + smoke test):
22
- * macOS/Linux: runtimes/chromium/chrome-<plat>/Chromium.app/Contents/MacOS/Chromium
23
- * or runtimes/chromium/bin/chromium
24
- * Windows: runtimes/chromium/chrome-win/chrome.exe
25
- * or runtimes/chromium/bin/chromium.exe
26
- *
27
- * Multiple candidate paths are probed so the exact Playwright tarball layout
28
- * (which nests under chrome-<platform>/) and a flattened bin/ layout both work.
20
+ * We DISCOVER rather than hard-code the path because Playwright's layout changes
21
+ * across versions (e.g. `chrome-mac/Chromium.app` → `chrome-mac-arm64/Google Chrome
22
+ * for Testing.app`, `chrome-win/chrome.exe`, `chrome-linux/chrome`). The CI assembly
23
+ * copies Playwright's platform folder verbatim under `<runtimes>/chromium/`; this
24
+ * walks that tree to find the real executable.
29
25
  */
30
- function fileExists(p) {
26
+ const MAX_DEPTH = 6;
27
+ function isFile(p) {
31
28
  try {
32
29
  return fs_1.default.statSync(p).isFile();
33
30
  }
@@ -35,28 +32,81 @@ function fileExists(p) {
35
32
  return false;
36
33
  }
37
34
  }
38
- function bundledChromiumCandidates(runtimesPath) {
39
- const root = path_1.default.join(runtimesPath, 'chromium');
35
+ /** Depth-bounded search for the first file whose basename satisfies `match`. */
36
+ function findFirstFile(root, match, depth = 0) {
37
+ if (depth > MAX_DEPTH)
38
+ return null;
39
+ let entries;
40
+ try {
41
+ entries = fs_1.default.readdirSync(root, { withFileTypes: true });
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ // Files first (cheap), then recurse into dirs.
47
+ for (const e of entries) {
48
+ if (e.isFile() && match(e.name))
49
+ return path_1.default.join(root, e.name);
50
+ }
51
+ for (const e of entries) {
52
+ if (e.isDirectory()) {
53
+ const hit = findFirstFile(path_1.default.join(root, e.name), match, depth + 1);
54
+ if (hit)
55
+ return hit;
56
+ }
57
+ }
58
+ return null;
59
+ }
60
+ /** On macOS: locate the main executable inside the first `*.app` under `root`. */
61
+ function findMacAppExecutable(root, depth = 0) {
62
+ if (depth > MAX_DEPTH)
63
+ return null;
64
+ let entries;
65
+ try {
66
+ entries = fs_1.default.readdirSync(root, { withFileTypes: true });
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ for (const e of entries) {
72
+ if (e.isDirectory() && e.name.endsWith('.app')) {
73
+ const macosDir = path_1.default.join(root, e.name, 'Contents', 'MacOS');
74
+ // The main binary is conventionally named like the app (sans ".app");
75
+ // fall back to the first regular file in MacOS/.
76
+ const preferred = path_1.default.join(macosDir, e.name.slice(0, -'.app'.length));
77
+ if (isFile(preferred))
78
+ return preferred;
79
+ try {
80
+ for (const inner of fs_1.default.readdirSync(macosDir, { withFileTypes: true })) {
81
+ if (inner.isFile())
82
+ return path_1.default.join(macosDir, inner.name);
83
+ }
84
+ }
85
+ catch { /* keep searching */ }
86
+ }
87
+ }
88
+ for (const e of entries) {
89
+ if (e.isDirectory() && !e.name.endsWith('.app')) {
90
+ const hit = findMacAppExecutable(path_1.default.join(root, e.name), depth + 1);
91
+ if (hit)
92
+ return hit;
93
+ }
94
+ }
95
+ return null;
96
+ }
97
+ /** Find the bundled Chromium executable under `<chromiumRoot>`, or null. */
98
+ function discoverChromiumExecutable(chromiumRoot) {
99
+ if (!fs_1.default.existsSync(chromiumRoot))
100
+ return null;
40
101
  if (process.platform === 'win32') {
41
- return [
42
- path_1.default.join(root, 'bin', 'chromium.exe'),
43
- path_1.default.join(root, 'chrome-win', 'chrome.exe'),
44
- path_1.default.join(root, 'chrome.exe'),
45
- ];
102
+ return findFirstFile(chromiumRoot, (n) => n === 'chrome.exe' || n === 'chromium.exe');
46
103
  }
47
104
  if (process.platform === 'darwin') {
48
- return [
49
- path_1.default.join(root, 'bin', 'chromium'),
50
- path_1.default.join(root, 'chrome-mac', 'Chromium.app', 'Contents', 'MacOS', 'Chromium'),
51
- path_1.default.join(root, 'Chromium.app', 'Contents', 'MacOS', 'Chromium'),
52
- ];
105
+ return (findMacAppExecutable(chromiumRoot) ??
106
+ findFirstFile(chromiumRoot, (n) => n === 'Chromium' || n === 'chromium' || n === 'chrome'));
53
107
  }
54
108
  // linux
55
- return [
56
- path_1.default.join(root, 'bin', 'chromium'),
57
- path_1.default.join(root, 'chrome-linux', 'chrome'),
58
- path_1.default.join(root, 'chrome'),
59
- ];
109
+ return findFirstFile(chromiumRoot, (n) => n === 'chrome' || n === 'chromium' || n === 'chrome-wrapper');
60
110
  }
61
111
  /**
62
112
  * Returns the absolute path to the bundled Chromium binary, or `null` when no
@@ -68,9 +118,10 @@ function resolveBundledChromiumPath() {
68
118
  const runtimesPath = process.env.SPECRAILS_BUNDLED_RUNTIMES_PATH;
69
119
  if (!runtimesPath)
70
120
  return null;
71
- for (const candidate of bundledChromiumCandidates(runtimesPath)) {
72
- if (fileExists(candidate))
73
- return candidate;
121
+ try {
122
+ return discoverChromiumExecutable(path_1.default.join(runtimesPath, 'chromium'));
123
+ }
124
+ catch {
125
+ return null;
74
126
  }
75
- return null;
76
127
  }