safaridriver 1.0.1 → 3.0.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/README.md CHANGED
@@ -1,9 +1,11 @@
1
- # Safaridriver for Node.js [![CI](https://github.com/webdriverio-community/node-safaridriver/actions/workflows/ci.yml/badge.svg)](https://github.com/webdriverio-community/node-safaridriver/actions/workflows/ci.yml) [![Audit](https://github.com/webdriverio-community/node-safaridriver/actions/workflows/audit.yml/badge.svg)](https://github.com/webdriverio-community/node-safaridriver/actions/workflows/audit.yml)
1
+ # Safaridriver for Node.js [![CI](https://github.com/webdriverio/driver/actions/workflows/ci.yml/badge.svg)](https://github.com/webdriverio/driver/actions/workflows/ci.yml) [![Audit](https://github.com/webdriverio/driver/actions/workflows/audit.yml/badge.svg)](https://github.com/webdriverio/driver/actions/workflows/audit.yml)
2
2
 
3
- > A Node.js untility to manage Safaridriver sessions.
3
+ > A Node.js utility to manage Safaridriver sessions.
4
4
 
5
5
  The Safaridriver utility is used to launch an HTTP server that implements the [WebDriver](https://w3c.github.io/webdriver/) REST API. When launched, Safaridriver allows for automated testing of web content using the version of Safari that is installed with macOS.
6
6
 
7
+ __Requirements:__ Node.js 22.19.0 or newer. See [Compatibility](https://github.com/webdriverio/driver#compatibility).
8
+
7
9
  ## Install
8
10
 
9
11
  To install the package, run:
@@ -19,7 +21,7 @@ To start a Safaridriver server, import the package and run:
19
21
  ```js
20
22
  import safaridriver from 'safaridriver'
21
23
 
22
- await safaridriver.start()
24
+ safaridriver.start()
23
25
 
24
26
  // run some automation...
25
27
 
@@ -27,6 +29,8 @@ await safaridriver.start()
27
29
  safaridriver.stop()
28
30
  ```
29
31
 
32
+ `start()` returns the [`ChildProcess`](https://nodejs.org/api/child_process.html#class-childprocess). Read its `stdout` and `stderr`, or pass `spawnOpts: { stdio: 'ignore' }`: an unread pipe fills up and then blocks the driver.
33
+
30
34
  ## Options
31
35
 
32
36
  ### `port`
@@ -45,7 +49,7 @@ __Default:__ `/usr/bin/safaridriver`
45
49
 
46
50
  ### `useTechnologyPreview`
47
51
 
48
- If set to `true`, it will start the Safaridriver for Safari Technology Preview. Make sure you have it installed by installing Safari Technology Preview on Apple's [offical website](https://developer.apple.com/safari/technology-preview/).
52
+ If set to `true`, it will start the Safaridriver for Safari Technology Preview. Make sure you have it installed by installing Safari Technology Preview on Apple's [official website](https://developer.apple.com/safari/technology-preview/).
49
53
 
50
54
  __Type:__ `boolean`<br />
51
55
  __Default:__ `false`
@@ -66,6 +70,13 @@ Enables diagnostic logging for all sessions hosted by this safaridriver instance
66
70
  __Type:__ `boolean`<br />
67
71
  __Default:__ `false`
68
72
 
73
+ ### `spawnOpts`
74
+
75
+ Options for the Safaridriver process, passed to [`child_process.spawn`](https://nodejs.org/api/child_process.html#child_processspawncommand-args-options).
76
+
77
+ __Type:__ `SpawnOptions`<br />
78
+ __Default:__ `undefined`
79
+
69
80
  ----
70
81
 
71
- For more information on WebdriverIO see the [homepage](http://webdriver.io).
82
+ For more information on WebdriverIO see the [homepage](https://webdriver.io).
@@ -1,3 +1,50 @@
1
- declare const start: (options: never) => Promise<import("node:child_process").ChildProcess>;
2
- declare const stop: () => Promise<void>;
3
- //# sourceMappingURL=index.d.ts.map
1
+ import { type ChildProcess, type SpawnOptions } from 'node:child_process';
2
+ export declare const DEFAULT_PATH = "/usr/bin/safaridriver";
3
+ export declare const DEFAULT_STP_PATH = "/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver";
4
+ export declare const DEFAULT_PORT = 4444;
5
+ export interface SafaridriverOptions {
6
+ /**
7
+ * Specifies the port on which the HTTP server should listen for incoming connections.
8
+ * If the port is already in use or otherwise unavailable, Safaridriver will exit
9
+ * immediately with a non-zero return code.
10
+ * @default 4444
11
+ */
12
+ port?: number;
13
+ /**
14
+ * Path to Safaridriver binary.
15
+ * @default /usr/bin/safaridriver
16
+ */
17
+ path?: string;
18
+ /**
19
+ * Applies configuration changes so that subsequent WebDriver sessions will run without
20
+ * further authentication. This includes checking "Enable Remote Automation" in Safari's
21
+ * `Develop` menu. The user must authenticate via password for the changes to be applied.
22
+ *
23
+ * When this option is specified, safaridriver exits immediately without starting up the
24
+ * REST API service. If the changes were successful or already applied, safaridriver exits 0;
25
+ * otherwise, safaridriver exits >0 and prints an error message to stderr.
26
+ * @default false
27
+ */
28
+ enable?: boolean;
29
+ /**
30
+ * Enables diagnostic logging for all sessions hosted by this safaridriver instance.
31
+ * @default false
32
+ */
33
+ diagnose?: boolean;
34
+ /**
35
+ * If enabled, it starts the Safaridriver binary from the Safari Technology Preview app.
36
+ */
37
+ useTechnologyPreview?: boolean;
38
+ /**
39
+ * Options for `child_process.spawn`. Pass `{ stdio: 'ignore' }` if you don't read the
40
+ * driver output: an unread pipe fills up and then blocks the driver.
41
+ */
42
+ spawnOpts?: SpawnOptions;
43
+ }
44
+ export declare const start: (options?: SafaridriverOptions) => ChildProcess;
45
+ export declare const stop: () => void;
46
+ declare const _default: {
47
+ start: typeof start;
48
+ stop: typeof stop;
49
+ };
50
+ export default _default;
package/dist/cjs/index.js CHANGED
@@ -1,10 +1,8 @@
1
- const start = exports.start = async function (options) {
2
- const driver = await import('../index.js');
3
- return driver.start(options);
4
- };
5
- const stop = exports.stop = async function () {
6
- const driver = await import('../index.js');
7
- return driver.stop();
8
- };
9
- module.exports = { start, stop };
1
+ "use strict";
2
+ /**
3
+ * Node.js 22.12+ can require() the ESM build synchronously, so CommonJS gets
4
+ * every export of it. dist/cjs/package.json makes this file CommonJS.
5
+ */
6
+ // oxlint-disable-next-line typescript/no-require-imports
7
+ module.exports = require('../index.js');
10
8
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cjs/index.ts"],"names":[],"mappings":"AAAA,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,GAAG,KAAK,WAAW,OAAc;IACxD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAA;IAC1C,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;AAChC,CAAC,CAAA;AAED,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,GAAG,KAAK;IAC7B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,CAAA;IAC1C,OAAO,MAAM,CAAC,IAAI,EAAE,CAAA;AACxB,CAAC,CAAA;AAED,MAAM,CAAC,OAAO,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cjs/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;AACH,yDAAyD;AACzD,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC,CAAA"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import cp from 'node:child_process';
1
+ import { type ChildProcess, type SpawnOptions } from 'node:child_process';
2
2
  export declare const DEFAULT_PATH = "/usr/bin/safaridriver";
3
3
  export declare const DEFAULT_STP_PATH = "/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver";
4
4
  export declare const DEFAULT_PORT = 4444;
@@ -35,12 +35,17 @@ export interface SafaridriverOptions {
35
35
  * If enabled, it starts the Safaridriver binary from the Safari Technology Preview app.
36
36
  */
37
37
  useTechnologyPreview?: boolean;
38
+ /**
39
+ * Options for `child_process.spawn`. Pass `{ stdio: 'ignore' }` if you don't read the
40
+ * driver output: an unread pipe fills up and then blocks the driver.
41
+ */
42
+ spawnOpts?: SpawnOptions;
38
43
  }
39
- export declare const start: (options?: SafaridriverOptions) => cp.ChildProcess;
44
+ export declare const start: (options?: SafaridriverOptions) => ChildProcess;
40
45
  export declare const stop: () => void;
41
46
  declare const _default: {
42
- start: (options?: SafaridriverOptions) => cp.ChildProcess;
43
- stop: () => void;
47
+ start: typeof start;
48
+ stop: typeof stop;
44
49
  };
45
50
  export default _default;
46
51
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,MAAM,oBAAoB,CAAA;AAE1D,eAAO,MAAM,YAAY,0BAA0B,CAAA;AACnD,eAAO,MAAM,gBAAgB,4EAA4E,CAAA;AACzG,eAAO,MAAM,YAAY,OAAO,CAAA;AAEhC,MAAM,WAAW,mBAAmB;IAChC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;OAEG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;CACjC;AAID,eAAO,MAAM,KAAK,GAAI,UAAS,mBAAwB,oBAgCtD,CAAA;AAED,eAAO,MAAM,IAAI,YAKhB,CAAA;;sBAvC8B,mBAAmB;;;AAyClD,wBAA8B"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAW,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAE7E,eAAO,MAAM,YAAY,0BAA0B,CAAA;AACnD,eAAO,MAAM,gBAAgB,4EAA4E,CAAA;AACzG,eAAO,MAAM,YAAY,OAAO,CAAA;AAEhC,MAAM,WAAW,mBAAmB;IAChC;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;OAEG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B;;;OAGG;IACH,SAAS,CAAC,EAAE,YAAY,CAAA;CAC3B;AAID,eAAO,MAAM,KAAK,aAAa,mBAAmB,iBA6CjD,CAAA;AAED,eAAO,MAAM,IAAI,YAKhB,CAAA"}
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ export const DEFAULT_PATH = '/usr/bin/safaridriver';
4
4
  export const DEFAULT_STP_PATH = '/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver';
5
5
  export const DEFAULT_PORT = 4444;
6
6
  let instance;
7
- let instanceOptions;
7
+ let instancePort;
8
8
  export const start = (options = {}) => {
9
9
  const port = typeof options.port === 'number' ? options.port : DEFAULT_PORT;
10
10
  const args = [`--port=${port}`];
@@ -23,11 +23,24 @@ export const start = (options = {}) => {
23
23
  args.push('--diagnose');
24
24
  }
25
25
  if (instance) {
26
- throw new Error(`There is already a Safaridriver instance running on port ${instanceOptions.port}!`);
26
+ throw new Error(`There is already a Safaridriver instance running on port ${instancePort}!`);
27
27
  }
28
- instanceOptions = options;
29
- instance = cp.execFile(driverPath, args);
30
- return instance;
28
+ // spawn, not execFile: execFile buffers the output and kills the driver after 1 MB of it
29
+ const child = cp.spawn(driverPath, args, options.spawnOpts ?? {});
30
+ instance = child;
31
+ instancePort = port;
32
+ // `exit` when the driver ran (e.g. `--enable` exits at once), `close` when it failed to start
33
+ const forget = () => {
34
+ if (instance === child) {
35
+ instance = undefined;
36
+ }
37
+ };
38
+ child.once('exit', forget);
39
+ child.once('close', forget);
40
+ // execFile listened for `error` itself; without a listener, a missing binary would crash the
41
+ // caller. Callers still get the error through their own listeners.
42
+ child.on('error', forget);
43
+ return child;
31
44
  };
32
45
  export const stop = () => {
33
46
  if (instance) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,EAAyB,MAAM,oBAAoB,CAAA;AAE1D,MAAM,CAAC,MAAM,YAAY,GAAG,uBAAuB,CAAA;AACnD,MAAM,CAAC,MAAM,gBAAgB,GAAG,yEAAyE,CAAA;AACzG,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAA;AAqChC,IAAI,QAAsB,CAAA;AAC1B,IAAI,eAAoC,CAAA;AACxC,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,UAA+B,EAAE,EAAE,EAAE;IACvD,MAAM,IAAI,GAAG,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAA;IAC3E,MAAM,IAAI,GAAa,CAAC,UAAU,IAAI,EAAE,CAAC,CAAA;IACzC,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,IAAI,CAC/B,OAAO,CAAC,oBAAoB;QACxB,CAAC,CAAC,gBAAgB;QAClB,CAAC,CAAC,YAAY,CACrB,CAAA;IAED,MAAM,cAAc,GAAG,OAAO,CAAC,oBAAoB,IAAI,EAAE,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAA;IACtF,IAAI,OAAO,CAAC,oBAAoB,IAAI,CAAC,cAAc,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CACX,2DAA2D;YAC3D,wEAAwE,CAC3E,CAAA;IACL,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACjB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACzB,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACnB,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;IAC3B,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,4DAA4D,eAAe,CAAC,IAAI,GAAG,CAAC,CAAA;IACxG,CAAC;IAED,eAAe,GAAG,OAAO,CAAA;IACzB,QAAQ,GAAG,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,CAAA;IACxC,OAAO,QAAQ,CAAA;AACnB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,IAAI,GAAG,GAAG,EAAE;IACrB,IAAI,QAAQ,EAAE,CAAC;QACX,QAAQ,CAAC,IAAI,EAAE,CAAA;QACf,QAAQ,GAAG,SAAS,CAAA;IACxB,CAAC;AACL,CAAC,CAAA;AAED,eAAe,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,EAA4C,MAAM,oBAAoB,CAAA;AAE7E,MAAM,CAAC,MAAM,YAAY,GAAG,uBAAuB,CAAA;AACnD,MAAM,CAAC,MAAM,gBAAgB,GAAG,yEAAyE,CAAA;AACzG,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAAA;AA0ChC,IAAI,QAAkC,CAAA;AACtC,IAAI,YAAgC,CAAA;AACpC,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,OAAO,GAAwB,EAAE,EAAE,EAAE;IACvD,MAAM,IAAI,GAAG,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAA;IAC3E,MAAM,IAAI,GAAa,CAAC,UAAU,IAAI,EAAE,CAAC,CAAA;IACzC,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,IAAI,CAC/B,OAAO,CAAC,oBAAoB;QACxB,CAAC,CAAC,gBAAgB;QAClB,CAAC,CAAC,YAAY,CACrB,CAAA;IAED,MAAM,cAAc,GAAG,OAAO,CAAC,oBAAoB,IAAI,EAAE,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAA;IACtF,IAAI,OAAO,CAAC,oBAAoB,IAAI,CAAC,cAAc,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CACX,2DAA2D;YAC3D,wEAAwE,CAC3E,CAAA;IACL,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACjB,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;IACzB,CAAC;IAED,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACnB,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;IAC3B,CAAC;IAED,IAAI,QAAQ,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,4DAA4D,YAAY,GAAG,CAAC,CAAA;IAChG,CAAC;IAED,yFAAyF;IACzF,MAAM,KAAK,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,SAAS,IAAI,EAAE,CAAC,CAAA;IACjE,QAAQ,GAAG,KAAK,CAAA;IAChB,YAAY,GAAG,IAAI,CAAA;IACnB,8FAA8F;IAC9F,MAAM,MAAM,GAAG,GAAG,EAAE;QAChB,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;YACrB,QAAQ,GAAG,SAAS,CAAA;QACxB,CAAC;IACL,CAAC,CAAA;IACD,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1B,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IAC3B,6FAA6F;IAC7F,mEAAmE;IACnE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAA;IACzB,OAAO,KAAK,CAAA;AAChB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,IAAI,GAAG,GAAG,EAAE;IACrB,IAAI,QAAQ,EAAE,CAAC;QACX,QAAQ,CAAC,IAAI,EAAE,CAAA;QACf,QAAQ,GAAG,SAAS,CAAA;IACxB,CAAC;AACL,CAAC,CAAA;AAED,eAAe,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA"}
package/package.json CHANGED
@@ -1,13 +1,14 @@
1
1
  {
2
2
  "name": "safaridriver",
3
- "version": "1.0.1",
3
+ "version": "3.0.0",
4
4
  "author": "Christian Bromann <mail@bromann.dev>",
5
5
  "license": "MIT",
6
6
  "description": "An NPM wrapper for handling the Safaridriver binary.",
7
- "homepage": "https://github.com/webdriverio-community/node-safaridriver#readme",
7
+ "homepage": "https://github.com/webdriverio/driver/tree/main/packages/node-safaridriver#readme",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "git+https://github.com/webdriverio-community/node-safaridriver.git"
10
+ "url": "git+https://github.com/webdriverio/driver.git",
11
+ "directory": "packages/node-safaridriver"
11
12
  },
12
13
  "keywords": [
13
14
  "safari",
@@ -17,46 +18,36 @@
17
18
  "selenium"
18
19
  ],
19
20
  "bugs": {
20
- "url": "https://github.com/webdriverio-community/node-safaridriver/issues"
21
+ "url": "https://github.com/webdriverio/driver/issues"
21
22
  },
22
23
  "engines": {
23
- "node": ">=18.0.0"
24
+ "node": ">=22.19.0"
24
25
  },
25
26
  "main": "./dist/cjs/index.js",
26
27
  "type": "module",
27
28
  "module": "./dist/index.js",
28
29
  "exports": {
29
- ".": [
30
- {
30
+ ".": {
31
+ "import": {
31
32
  "types": "./dist/index.d.ts",
32
- "import": "./dist/index.js",
33
- "require": "./dist/cjs/index.js"
33
+ "default": "./dist/index.js"
34
34
  },
35
- "./dist/cjs/index.js"
36
- ]
35
+ "require": {
36
+ "types": "./dist/cjs/index.d.ts",
37
+ "default": "./dist/cjs/index.js"
38
+ }
39
+ }
37
40
  },
38
- "types": "./dist/index.d.ts",
39
- "typeScriptVersion": "3.8.3",
41
+ "types": "./dist/cjs/index.d.ts",
40
42
  "scripts": {
41
- "build": "tsc -b .",
42
- "ci": "run-s lint build test",
43
- "clean": "rimraf ./node_modules package-lock.yaml ./dist",
44
- "prepare": "husky install",
43
+ "build": "tsc -b . && node ../../scripts/copy-cjs-pkg.mjs",
44
+ "clean": "rimraf ./dist ./tsconfig.tsbuildinfo",
45
45
  "test": "run-s test:*",
46
- "test:lint": "eslint src tests",
47
- "test:unit": "vitest run",
46
+ "test:lint": "oxlint .",
47
+ "test:typecheck": "tsc -p tsconfig.test.json",
48
+ "test:unit": "vitest run --coverage",
48
49
  "test:interop": "node ./tests/interop/cjs.test.js",
49
- "watch": "npm run build -- --watch"
50
- },
51
- "devDependencies": {
52
- "@types/node": "^25.0.3",
53
- "@vitest/coverage-v8": "^4.0.16",
54
- "@wdio/eslint": "^0.1.3",
55
- "eslint": "^9.39.2",
56
- "husky": "^9.1.7",
57
- "npm-run-all2": "^8.0.4",
58
- "release-it": "^19.2.2",
59
- "typescript": "^5.9.3",
60
- "vitest": "^4.0.16"
50
+ "check:pack": "node ../../scripts/check-pack.mjs && publint && attw --pack . --ignore-rules false-export-default",
51
+ "watch": "pnpm run build -- --watch"
61
52
  }
62
53
  }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Node.js 22.12+ can require() the ESM build synchronously, so CommonJS gets
3
+ * every export of it. dist/cjs/package.json makes this file CommonJS.
4
+ */
5
+ // oxlint-disable-next-line typescript/no-require-imports
6
+ module.exports = require('../index.js')
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
package/src/index.ts ADDED
@@ -0,0 +1,104 @@
1
+ import fs from 'node:fs'
2
+ import cp, { type ChildProcess, type SpawnOptions } from 'node:child_process'
3
+
4
+ export const DEFAULT_PATH = '/usr/bin/safaridriver'
5
+ export const DEFAULT_STP_PATH = '/Applications/Safari Technology Preview.app/Contents/MacOS/safaridriver'
6
+ export const DEFAULT_PORT = 4444
7
+
8
+ export interface SafaridriverOptions {
9
+ /**
10
+ * Specifies the port on which the HTTP server should listen for incoming connections.
11
+ * If the port is already in use or otherwise unavailable, Safaridriver will exit
12
+ * immediately with a non-zero return code.
13
+ * @default 4444
14
+ */
15
+ port?: number
16
+ /**
17
+ * Path to Safaridriver binary.
18
+ * @default /usr/bin/safaridriver
19
+ */
20
+ path?: string
21
+ /**
22
+ * Applies configuration changes so that subsequent WebDriver sessions will run without
23
+ * further authentication. This includes checking "Enable Remote Automation" in Safari's
24
+ * `Develop` menu. The user must authenticate via password for the changes to be applied.
25
+ *
26
+ * When this option is specified, safaridriver exits immediately without starting up the
27
+ * REST API service. If the changes were successful or already applied, safaridriver exits 0;
28
+ * otherwise, safaridriver exits >0 and prints an error message to stderr.
29
+ * @default false
30
+ */
31
+ enable?: boolean
32
+ /**
33
+ * Enables diagnostic logging for all sessions hosted by this safaridriver instance.
34
+ * @default false
35
+ */
36
+ diagnose?: boolean
37
+ /**
38
+ * If enabled, it starts the Safaridriver binary from the Safari Technology Preview app.
39
+ */
40
+ useTechnologyPreview?: boolean
41
+ /**
42
+ * Options for `child_process.spawn`. Pass `{ stdio: 'ignore' }` if you don't read the
43
+ * driver output: an unread pipe fills up and then blocks the driver.
44
+ */
45
+ spawnOpts?: SpawnOptions
46
+ }
47
+
48
+ let instance: ChildProcess | undefined
49
+ let instancePort: number | undefined
50
+ export const start = (options: SafaridriverOptions = {}) => {
51
+ const port = typeof options.port === 'number' ? options.port : DEFAULT_PORT
52
+ const args: string[] = [`--port=${port}`]
53
+ const driverPath = options.path || (
54
+ options.useTechnologyPreview
55
+ ? DEFAULT_STP_PATH
56
+ : DEFAULT_PATH
57
+ )
58
+
59
+ const isSTPInstalled = options.useTechnologyPreview && fs.existsSync(DEFAULT_STP_PATH)
60
+ if (options.useTechnologyPreview && !isSTPInstalled) {
61
+ throw new Error(
62
+ 'Safari Technology Preview is not installed! Please go to ' +
63
+ 'https://developer.apple.com/safari/technology-preview/ and install it.'
64
+ )
65
+ }
66
+
67
+ if (options.enable) {
68
+ args.push('--enable')
69
+ }
70
+
71
+ if (options.diagnose) {
72
+ args.push('--diagnose')
73
+ }
74
+
75
+ if (instance) {
76
+ throw new Error(`There is already a Safaridriver instance running on port ${instancePort}!`)
77
+ }
78
+
79
+ // spawn, not execFile: execFile buffers the output and kills the driver after 1 MB of it
80
+ const child = cp.spawn(driverPath, args, options.spawnOpts ?? {})
81
+ instance = child
82
+ instancePort = port
83
+ // `exit` when the driver ran (e.g. `--enable` exits at once), `close` when it failed to start
84
+ const forget = () => {
85
+ if (instance === child) {
86
+ instance = undefined
87
+ }
88
+ }
89
+ child.once('exit', forget)
90
+ child.once('close', forget)
91
+ // execFile listened for `error` itself; without a listener, a missing binary would crash the
92
+ // caller. Callers still get the error through their own listeners.
93
+ child.on('error', forget)
94
+ return child
95
+ }
96
+
97
+ export const stop = () => {
98
+ if (instance) {
99
+ instance.kill()
100
+ instance = undefined
101
+ }
102
+ }
103
+
104
+ export default { start, stop }
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cjs/index.ts"],"names":[],"mappings":"AAAA,QAAA,MAAM,KAAK,YAA4C,KAAK,uDAG3D,CAAA;AAED,QAAA,MAAM,IAAI,qBAGT,CAAA"}
@@ -1 +0,0 @@
1
- {"root":["../src/index.ts","../src/cjs/index.ts","../src/cjs/package.json"],"version":"5.9.3"}