@jsenv/https-local 3.3.0 → 3.6.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,365 +1,161 @@
1
- # https local [![npm package](https://img.shields.io/npm/v/@jsenv/https-local.svg?logo=npm&label=package)](https://www.npmjs.com/package/@jsenv/https-local)
1
+ # HTTPS Local [![npm package](https://img.shields.io/npm/v/@jsenv/https-local.svg?logo=npm&label=package)](https://www.npmjs.com/package/@jsenv/https-local)
2
2
 
3
- MOVED TO https://github.com/jsenv/core/tree/main/packages/independent/https-local
3
+ Generate locally trusted HTTPS certificates for local development.
4
4
 
5
- A programmatic way to generate locally trusted certificates.
5
+ 🔒 Certificates trusted by your operating system and browsers
6
+ 🌐 Perfect for local HTTPS development
7
+ 🖥️ Works on macOS, Linux, and Windows
8
+ ⚡ Simple CLI and JavaScript API
6
9
 
7
- Generate certificate(s) trusted by your operating system and browsers.
8
- This certificate can be used to start your development server in HTTPS.
9
- Works on mac, linux and windows.
10
+ ## Table of Contents
10
11
 
11
- # How to use
12
+ - [HTTPS Local ](#https-local-)
13
+ - [Table of Contents](#table-of-contents)
14
+ - [Quick Start](#quick-start)
15
+ - [CLI](#cli)
16
+ - [init](#init)
17
+ - [generate](#generate)
18
+ - [cleanup](#cleanup)
19
+ - [Certificate Expiration](#certificate-expiration)
20
+ - [JavaScript API](#javascript-api)
21
+ - [requestCertificate](#requestcertificate)
22
+ - [verifyHostsFile](#verifyhostsfile)
23
+ - [Auto Update Hosts](#auto-update-hosts)
24
+ - [installCertificateAuthority](#installcertificateauthority)
25
+ - [Auto Trust](#auto-trust)
12
26
 
13
- 1 - Install _@jsenv/https-local_
27
+ ## Quick Start
14
28
 
15
29
  ```console
16
- npm install --save-dev @jsenv/https-local
17
- ```
18
-
19
- 2 - Create _install_certificate_authority.mjs_
20
-
21
- ```js
22
- /*
23
- * This file needs to be executed once.
24
- * After that the root certificate is valid for 20 years.
25
- * Re-executing this file will log the current root certificate validity and trust status.
26
- * Re-executing this file 20 years later would reinstall a root certificate and re-trust it.
27
- *
28
- * Read more in https://github.com/jsenv/https-local#installCertificateAuthority
29
- */
30
-
31
- import {
32
- installCertificateAuthority,
33
- verifyHostsFile,
34
- } from "@jsenv/https-local";
35
-
36
- await installCertificateAuthority({
37
- tryToTrust: true,
38
- NSSDynamicInstall: true,
39
- });
40
- await verifyHostsFile({
41
- ipMappings: {
42
- "127.0.0.1": ["localhost"],
43
- },
44
- tryToUpdatesHostsFile: true,
45
- });
30
+ npx @jsenv/https-local init
31
+ npx @jsenv/https-local generate
46
32
  ```
47
33
 
48
- 3 - Run with node
49
-
50
- ```console
51
- node ./install_certificate_authority.mjs
52
- ```
53
-
54
- 4 - Create _start_dev_server.mjs_
34
+ Then start your server reading the generated certificate files:
55
35
 
56
36
  ```js
57
- /*
58
- * This file uses "@jsenv/https-local" to obtain a certificate used to start a server in https.
59
- * The certificate is valid for 1 year (396 days) and is issued by a certificate authority trusted on this machine.
60
- * If the certificate authority was not installed before executing this file, an error is thrown
61
- * explaining that certificate authority must be installed first.
62
- *
63
- * To install the certificate authority, you can use the following command
64
- *
65
- * > node ./install_certificate_authority.mjs
66
- *
67
- * Read more in https://github.com/jsenv/https-local#requestCertificate
68
- */
69
-
70
37
  import { createServer } from "node:https";
71
- import { requestCertificate } from "@jsenv/https-local";
72
-
73
- const { certificate, privateKey } = requestCertificate();
38
+ import { readFileSync } from "node:fs";
74
39
 
75
40
  const server = createServer(
76
41
  {
77
- cert: certificate,
78
- key: privateKey,
42
+ cert: readFileSync("certificate.pem"),
43
+ key: readFileSync("private_key.pem"),
79
44
  },
80
45
  (request, response) => {
81
- const body = "Hello world";
82
- response.writeHead(200, {
83
- "content-type": "text/plain",
84
- "content-length": Buffer.byteLength(body),
85
- });
86
- response.write(body);
87
- response.end();
46
+ response.end("Hello HTTPS world!");
88
47
  },
89
- );
90
- server.listen(8080);
91
- console.log(`Server listening at https://local.example:8080`);
92
- ```
93
-
94
- 5 - Start server with node
95
-
96
- ```console
97
- node ./start_dev_server.mjs
48
+ ).listen(8443, () => {
49
+ console.log("HTTPS server running at https://localhost:8443");
50
+ });
98
51
  ```
99
52
 
100
- At this stage you have a server running in https.
101
- The rest of this documentation goes into more details.
102
-
103
- # Certificate expiration
53
+ ## CLI
104
54
 
105
- | Certificate | Expires after | How to renew? |
106
- | ----------- | ------------- | ------------------------------------ |
107
- | server | 1 year | Re-run _requestCertificate_ |
108
- | authority | 20 year | Re-run _installCertificateAuthority_ |
55
+ ### init
109
56
 
110
- The **server certificate** expires after one year which is the maximum duration allowed by web browsers.
111
- In the unlikely scenario where a local server is running for more than a year without interruption, restart it to re-run requestCertificate.
112
-
113
- The **authority root certificate** expires after 20 years which is close to the maximum allowed duration.
114
- In the very unlikely scenario where you are using the same machine for more than 20 years, re-execute [installCertificateAuthority](#installCertificateAuthority) to update certificate authority then restart your server.
115
-
116
- # installCertificateAuthority
117
-
118
- _installCertificateAuthority_ function generates a certificate authority valid for 20 years.
119
- This certificate authority is needed to generate local certificates that will be trusted by the operating system and web browsers.
120
-
121
- ```js
122
- import { installCertificateAuthority } from "@jsenv/https-local";
123
-
124
- await installCertificateAuthority();
57
+ ```console
58
+ npx @jsenv/https-local init
125
59
  ```
126
60
 
127
- By default, trusting authority root certificate is a manual process. This manual process is documented in [BenMorel/dev-certificates#Import the CA in your browser](https://github.com/BenMorel/dev-certificates/tree/c10cd68945da772f31815b7a36721ddf848ff3a3#import-the-ca-in-your-browser). This process can be done programmatically as explained in [Auto trust](#Auto-trust).
128
-
129
- Find below logs written in terminal when this function is executed.
61
+ Installs a root certificate authority, trusts it in your OS and browsers, and ensures `localhost` is mapped to `127.0.0.1` in your hosts file. Safe to re-run — subsequent runs report the current status.
130
62
 
131
63
  <details>
132
- <summary>mac</summary>
64
+ <summary>First execution (macOS)</summary>
133
65
 
134
66
  ```console
135
- > node ./install_certificate_authority.mjs
67
+ > npx @jsenv/https-local init
136
68
 
137
69
  ℹ authority root certificate not found in filesystem
138
70
  Generating authority root certificate with a validity of 20 years...
139
- ✔ authority root certificate written at /Users/dmail/https_local/http_local_root_certificate.crt
140
- ℹ You should add root certificate to mac keychain
141
- ℹ You should add root certificate to firefox
142
- ```
143
-
144
- _second execution logs_
145
-
146
- ```console
147
- > node ./install_certificate_authority.mjs
148
-
149
- ✔ authority root certificate found in filesystem
150
- Checking certificate validity...
151
- ✔ certificate still valid for 19 years
152
- Detect if certificate attributes have changed...
153
- ✔ certificate attributes are the same
154
- Check if certificate is in mac keychain...
155
- ℹ certificate not found in mac keychain
156
- Check if certificate is in firefox...
157
- ℹ certificate not found in firefox
71
+ ✔ authority root certificate written at /Users/you/https_local/https_local_root_certificate.crt
72
+ Adding certificate to mac keychain...
73
+ ❯ sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "/Users/you/https_local/https_local_root_certificate.crt"
74
+ Password:
75
+ ✔ certificate added to mac keychain
76
+ Adding certificate to firefox...
77
+ ✔ certificate added to Firefox
78
+ Check hosts file content...
79
+ ✔ all ip mappings found in hosts file
158
80
  ```
159
81
 
160
82
  </details>
161
83
 
162
84
  <details>
163
- <summary>linux</summary>
85
+ <summary>Second execution (macOS)</summary>
164
86
 
165
87
  ```console
166
- > node ./install_certificate_authority.mjs
167
-
168
- ℹ authority root certificate not found in filesystem
169
- Generating authority root certificate with a validity of 20 years...
170
- ✔ authority root certificate written at /home/dmail/.config/https_local/https_local_root_certificate.crt
171
- ℹ You should add certificate to linux
172
- ℹ You should add certificate to chrome
173
- ℹ You should add certificate to firefox
174
- ```
175
-
176
- _second execution logs_
177
-
178
- ```console
179
- > node ./install_certificate_authority.mjs
88
+ > npx @jsenv/https-local init
180
89
 
181
90
  ✔ authority root certificate found in filesystem
182
91
  Checking certificate validity...
183
92
  ✔ certificate still valid for 19 years
184
93
  Detect if certificate attributes have changed...
185
94
  ✔ certificate attributes are the same
186
- Check if certificate is in linux...
187
- ℹ certificate in linux is outdated
188
- Check if certificate is in chrome...
189
- ℹ certificate not found in chrome
190
- Check if certificate is in firefox...
191
- ℹ certificate not found in firefox
95
+ Check if certificate is in mac keychain...
96
+ ✔ certificate found in mac keychain
97
+ Check if certificate is in Firefox...
98
+ ✔ certificate found in Firefox
99
+ Check hosts file content...
100
+ ✔ all ip mappings found in hosts file
192
101
  ```
193
102
 
194
103
  </details>
195
104
 
196
- <details>
197
- <summary>windows</summary>
198
-
199
- ```console
200
- > node ./install_certificate_authority.mjs
201
-
202
- ℹ authority root certificate not found in filesystem
203
- Generating authority root certificate with a validity of 20 years...
204
- ✔ authority root certificate written at C:\Users\Dmail\AppData\Local\https_local\https_local_root_certificate.crt
205
- ℹ You should add certificate to windows
206
- ℹ You should add certificate to firefox
207
- ```
208
-
209
- _second execution logs_
105
+ ### generate
210
106
 
211
107
  ```console
212
- > node ./install_certificate_authority.mjs
213
-
214
- ✔ authority root certificate found in filesystem
215
- Checking certificate validity...
216
- ✔ certificate still valid for 19 years
217
- Detect if certificate attributes have changed...
218
- ✔ certificate attributes are the same
219
- Check if certificate is trusted by windows...
220
- ℹ certificate is not trusted by windows
221
- Check if certificate is trusted by firefox...
222
- ℹ unable to detect if certificate is trusted by firefox (not implemented on windows)
108
+ npx @jsenv/https-local generate
223
109
  ```
224
110
 
225
- </details>
111
+ Generates a server certificate signed by the local certificate authority and writes it to files. Requires `init` to have been run first.
226
112
 
227
- ## Auto trust
113
+ > **Note:** Certificate files are static — they are not renewed automatically. Re-run `generate` after one year to replace expired files.
228
114
 
229
- It's possible to trust root certificate programmatically using _tryToTrust_
115
+ Options:
230
116
 
231
- ```js
232
- import { installCertificateAuthority } from "@jsenv/https-local";
233
-
234
- await installCertificateAuthority({
235
- tryToTrust: true,
236
- });
237
- ```
238
-
239
- <details>
240
- <summary>mac</summary>
241
-
242
- ```console
243
- > node ./install_certificate_authority.mjs
244
-
245
- ℹ authority root certificate not found in filesystem
246
- Generating authority root certificate with a validity of 20 years...
247
- ✔ authority root certificate written at /Users/dmail/https_local/https_local_root_certificate.crt
248
- Adding certificate to mac keychain...
249
- ❯ sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain "/Users/dmail/https_local/https_local_root_certificate.crt"
250
- Password:
251
- ✔ certificate added to mac keychain
252
- Adding certificate to firefox...
253
- ✔ certificate added to Firefox
254
- ```
117
+ | Option | Description | Default |
118
+ | --------------- | --------------------------------- | ----------------- |
119
+ | `--certificate` | Path for the certificate file | `certificate.pem` |
120
+ | `--private-key` | Path for the private key file | `private_key.pem` |
121
+ | `--hostnames` | Comma-separated list of hostnames | `localhost` |
255
122
 
256
- _second execution logs_
123
+ Example:
257
124
 
258
125
  ```console
259
- > node ./install_certificate_authority.mjs
260
-
261
- ✔ authority root certificate found in filesystem
262
- Checking certificate validity...
263
- ✔ certificate still valid for 19 years
264
- Detect if certificate attributes have changed...
265
- ✔ certificate attributes are the same
266
- Check if certificate is in mac keychain...
267
- ✔ certificate found in mac keychain
268
- Check if certificate is in Firefox...
269
- ✔ certificate found in Firefox
126
+ npx @jsenv/https-local generate --certificate server.pem --private-key server.key --hostnames localhost,myapp.local
270
127
  ```
271
128
 
272
- </details>
273
-
274
- <details>
275
- <summary>linux</summary>
129
+ ### cleanup
276
130
 
277
131
  ```console
278
- > node ./install_certificate_authority.mjs
279
-
280
- ✔ authority root certificate found in filesystem
281
- Checking certificate validity...
282
- ✔ certificate still valid for 19 years
283
- Detect if certificate attributes have changed...
284
- ✔ certificate attributes are the same
285
- Check if certificate is in linux...
286
- ℹ certificate not in linux
287
- Adding certificate to linux...
288
- ❯ sudo /bin/cp -f "/home/dmail/.config/https_local/https_local_root_certificate.crt" /usr/local/share/ca-certificates/https_local_root_certificate.crt
289
- [sudo] Password for dmail :
290
- ❯ sudo update-ca-certificates
291
- ✔ certificate added to linux
292
- Check if certificate is in chrome...
293
- ℹ certificate not found in chrome
294
- Adding certificate to chrome...
295
- ✔ certificate added to chrome
296
- Check if certificate is in firefox...
297
- ℹ certificate not found in firefox
298
- Adding certificate to firefox...
299
- ✔ certificate added to firefox
132
+ npx @jsenv/https-local cleanup
300
133
  ```
301
134
 
302
- _second execution logs_
135
+ Uninstalls the root certificate and removes its trust from your OS and browsers.
303
136
 
304
- ```console
305
- > node ./install_certificate_authority.mjs
137
+ ## Certificate Expiration
306
138
 
307
- ✔ authority root certificate found in filesystem
308
- Checking certificate validity...
309
- ✔ certificate still valid for 19 years
310
- Detect if certificate attributes have changed...
311
- ✔ certificate attributes are the same
312
- Check if certificate is in linux...
313
- ✔ certificate found in linux
314
- Check if certificate is in chrome...
315
- ✔ certificate found in chrome
316
- Check if certificate is in firefox...
317
- ✔ certificate found in firefox
318
- ```
139
+ | Certificate | Expires after | How to renew? |
140
+ | ----------- | ------------- | ----------------- |
141
+ | server | 1 year | Re-run `generate` |
142
+ | authority | 20 years | Re-run `init` |
319
143
 
320
- </details>
144
+ The **server certificate** expires after one year, which is the maximum duration allowed by web browsers.
321
145
 
322
- <details>
323
- <summary>windows</summary>
324
-
325
- ```console
326
- > node ./install_certificate_authority.mjs
146
+ The **authority root certificate** expires after 20 years. Re-running `init` after expiry will reinstall and re-trust a new one.
327
147
 
328
- ✔ authority root certificate found in filesystem
329
- Checking certificate validity...
330
- ✔ certificate still valid for 19 years
331
- Detect if certificate attributes have changed...
332
- ✔ certificate attributes are the same
333
- Check if certificate is trusted by windows...
334
- ℹ certificate not trusted by windows
335
- Adding certificate to windows...
336
- ❯ certutil -addstore -user root C:\Users\Dmail\AppData\Local\https_local\https_local_root_certificate.crt
337
- ✔ certificate added to windows
338
- Check if certificate is trusted by firefox...
339
- ℹ unable to detect if certificate is trusted by firefox (not implemented on windows)
340
- ```
148
+ ## JavaScript API
341
149
 
342
- _second execution logs_
150
+ To use the JavaScript API, add the package to your dev dependencies:
343
151
 
344
152
  ```console
345
- > node ./install_certificate_authority.mjs
346
-
347
- ✔ authority root certificate found in filesystem
348
- Checking certificate validity...
349
- ✔ certificate still valid for 19 years
350
- Detect if certificate attributes have changed...
351
- ✔ certificate attributes are the same
352
- Check if certificate is trusted by windows...
353
- ✔ certificate trusted by windows
354
- Check if certificate is trusted by firefox...
355
- ℹ unable to detect if certificate is trusted by firefox (not implemented on windows)
153
+ npm install --save-dev @jsenv/https-local
356
154
  ```
357
155
 
358
- </details>
359
-
360
- # requestCertificate
156
+ ### requestCertificate
361
157
 
362
- _requestCertificate_ function returns a certificate and private key that can be used to start a server in HTTPS.
158
+ The `requestCertificate` function generates a fresh certificate each time it is called and returns it in memory. Because the certificate is generated on every server startup, it is always valid — as long as your server is restarted at least once a year.
363
159
 
364
160
  ```js
365
161
  import { createServer } from "node:https";
@@ -368,14 +164,21 @@ import { requestCertificate } from "@jsenv/https-local";
368
164
  const { certificate, privateKey } = requestCertificate({
369
165
  altNames: ["localhost", "local.example"],
370
166
  });
167
+ const server = createServer(
168
+ { cert: certificate, key: privateKey },
169
+ (request, response) => {
170
+ response.end("Hello HTTPS world!");
171
+ },
172
+ ).listen(8443, () => {
173
+ console.log("HTTPS server running at https://localhost:8443");
174
+ });
371
175
  ```
372
176
 
373
- [installCertificateAuthority](#installCertificateAuthority) must be called before this function.
177
+ [`init`](#init) (or `installCertificateAuthority`) must be called once before using this function.
374
178
 
375
- # verifyHostsFile
179
+ ### verifyHostsFile
376
180
 
377
- This function is not mandatory to obtain the https certificates.
378
- But it is useful to programmatically verify ip mappings that are important for your local server are present in hosts file.
181
+ Verifies that IP mappings important for your local server are present in the hosts file.
379
182
 
380
183
  ```js
381
184
  import { verifyHostsFile } from "@jsenv/https-local";
@@ -387,43 +190,9 @@ await verifyHostsFile({
387
190
  });
388
191
  ```
389
192
 
390
- Find below logs written in terminal when this function is executed.
391
-
392
- <details>
393
- <summary>mac and linux</summary>
193
+ #### Auto Update Hosts
394
194
 
395
- ```console
396
- > node ./verify_hosts.mjs
397
-
398
- Check hosts file content...
399
- ⚠ 1 mapping is missing in hosts file
400
- --- hosts file path ---
401
- /etc/hosts
402
- --- line(s) to add ---
403
- 127.0.0.1 localhost local.example
404
- ```
405
-
406
- </details>
407
-
408
- <details>
409
- <summary>windows</summary>
410
-
411
- ```console
412
- > node ./verify_hosts.mjs
413
-
414
- Check hosts file content...
415
- ⚠ 1 mapping is missing in hosts file
416
- --- hosts file path ---
417
- C:\\Windows\\System32\\Drivers\\etc\\hosts
418
- --- line(s) to add ---
419
- 127.0.0.1 localhost local.example
420
- ```
421
-
422
- </details>
423
-
424
- ## Auto update hosts
425
-
426
- It's possible to update hosts file programmatically using _tryToUpdateHostsFile_.
195
+ It's possible to update hosts file programmatically using `tryToUpdateHostsFile`:
427
196
 
428
197
  ```js
429
198
  import { verifyHostsFile } from "@jsenv/https-local";
@@ -436,48 +205,27 @@ await verifyHostsFile({
436
205
  });
437
206
  ```
438
207
 
439
- <details>
440
- <summary>mac and linux</summary>
441
-
442
- ```console
443
- Check hosts file content...
444
- ℹ 1 mapping is missing in hosts file
445
- Adding 1 mapping(s) in hosts file...
446
- ❯ echo "127.0.0.1 local.example" | sudo tee -a /etc/hosts
447
- Password:
448
- ✔ mappings added to hosts file
449
- ```
208
+ ### installCertificateAuthority
450
209
 
451
- _Second execution logs_
210
+ The `installCertificateAuthority` function generates a certificate authority valid for 20 years.
211
+ This certificate authority is needed to generate local certificates that will be trusted by the operating system and web browsers.
452
212
 
453
- ```console
454
- > node ./verify_hosts.mjs
213
+ ```js
214
+ import { installCertificateAuthority } from "@jsenv/https-local";
455
215
 
456
- Check hosts file content...
457
- ✔ all ip mappings found in hosts file
216
+ await installCertificateAuthority();
458
217
  ```
459
218
 
460
- </details>
219
+ By default, trusting the root certificate is a manual process. See [BenMorel/dev-certificates](https://github.com/BenMorel/dev-certificates/tree/c10cd68945da772f31815b7a36721ddf848ff3a3#import-the-ca-in-your-browser) for instructions. This can also be done programmatically as shown in [Auto Trust](#auto-trust).
461
220
 
462
- <details>
463
- <summary>windows</summary>
221
+ #### Auto Trust
464
222
 
465
- ```console
466
- Check hosts file content...
467
- ℹ 1 mapping is missing in hosts file
468
- Adding 1 mapping(s) in hosts file...
469
- ❯ (echo 127.0.0.1 local.example) >> C:\\Windows\\System32\\Drivers\\etc\\hosts
470
- Password:
471
- ✔ mappings added to hosts file
472
- ```
473
-
474
- _Second execution logs_
223
+ It's possible to trust root certificate programmatically using `tryToTrust`:
475
224
 
476
- ```console
477
- > node ./verify_hosts.mjs
225
+ ```js
226
+ import { installCertificateAuthority } from "@jsenv/https-local";
478
227
 
479
- Check hosts file content...
480
- ✔ all ip mappings found in hosts file
228
+ await installCertificateAuthority({
229
+ tryToTrust: true,
230
+ });
481
231
  ```
482
-
483
- </details>
package/package.json CHANGED
@@ -1,20 +1,18 @@
1
1
  {
2
2
  "name": "@jsenv/https-local",
3
- "version": "3.3.0",
3
+ "version": "3.6.0",
4
4
  "type": "module",
5
5
  "description": "A programmatic way to generate locally trusted certificates",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "https://github.com/jsenv/https-local"
9
- },
10
- "author": {
11
- "name": "dmail",
12
- "email": "dmaillard06@gmail.com"
8
+ "url": "https://github.com/jsenv/core",
9
+ "directory": "packages/tooling/https-local"
13
10
  },
14
11
  "license": "MIT",
15
12
  "engines": {
16
- "node": ">=25.0.0"
13
+ "node": ">=20.0.0"
17
14
  },
15
+ "bin": "./src/https_local_cli.mjs",
18
16
  "main": "./src/main.js",
19
17
  "exports": {
20
18
  ".": {
@@ -26,25 +24,21 @@
26
24
  "/src/"
27
25
  ],
28
26
  "scripts": {
29
- "eslint": "npx eslint . --ext=.js,.mjs,.cjs",
30
- "test": "node ./scripts/test/test.mjs",
31
- "performance": "node --expose-gc ./scripts/performance/performance.mjs --local --log",
32
- "test:start-node-server": "node ./scripts/certificate/start_node_server.mjs",
33
27
  "ca:install": "node ./scripts/certificate/install_ca.mjs",
34
28
  "ca:log-trust": "node ./scripts/certificate/log_root_certificate_trust.mjs",
35
29
  "ca:trust": "node ./scripts/certificate/trust_root_certificate.mjs",
36
- "ca:untrust": "node ./scripts/certificate/untrust_root_certificate.mjs",
37
30
  "ca:uninstall": "node ./scripts/certificate/uninstall_certificate_authority.mjs",
31
+ "ca:untrust": "node ./scripts/certificate/untrust_root_certificate.mjs",
38
32
  "hosts:add-localhost-mappings": "node ./scripts/hosts/add_localhost_mappings.mjs",
33
+ "hosts:ensure-localhost-mappings": "node ./scripts/hosts/ensure_localhost_mappings.mjs",
39
34
  "hosts:remove-localhost-mappings": "node ./scripts/hosts/remove_localhost_mappings.mjs",
40
35
  "hosts:verify-localhost-mappings": "node ./scripts/hosts/verify_localhost_mappings.mjs",
41
- "hosts:ensure-localhost-mappings": "node ./scripts/hosts/ensure_localhost_mappings.mjs",
42
- "prettier": "prettier --write .",
43
- "playwright:install": "npx playwright install-deps && npx playwright install"
36
+ "performance": "node --expose-gc ./scripts/performance/performance.mjs --local --log",
37
+ "test:start-node-server": "node ./scripts/certificate/start_node_server.mjs"
44
38
  },
45
39
  "dependencies": {
46
40
  "@jsenv/filesystem": "4.15.15",
47
- "@jsenv/log": "3.5.2",
41
+ "@jsenv/humanize": "1.7.6",
48
42
  "@jsenv/urls": "2.9.8",
49
43
  "command-exists": "1.2.9",
50
44
  "node-forge": "1.4.0",
@@ -52,24 +46,13 @@
52
46
  "which": "6.0.1"
53
47
  },
54
48
  "devDependencies": {
55
- "@jsenv/assert": "4.5.6",
56
- "@jsenv/eslint-config-relax": "1.8.5",
57
- "@jsenv/github-release-package": "1.6.42",
58
- "@jsenv/package-publish": "1.11.44",
59
- "@jsenv/performance-impact": "4.4.42",
60
- "@jsenv/test": "3.7.21",
61
- "eslint": "9.39.2",
62
- "playwright": "1.59.1",
63
- "prettier": "3.8.3",
64
- "prettier-plugin-embed": "0.5.1",
65
- "prettier-plugin-organize-imports": "4.3.0",
66
- "prettier-plugin-pkg": "0.22.1"
49
+ "@jsenv/assert": "../assert",
50
+ "@jsenv/https-local": "./",
51
+ "@jsenv/performance-impact": "../performance-impact",
52
+ "@jsenv/test": "../../related/test",
53
+ "playwright": "1.59.1"
67
54
  },
68
55
  "publishConfig": {
69
56
  "access": "public"
70
- },
71
- "volta": {
72
- "node": "25.8.1",
73
- "npm": "11.6.2"
74
57
  }
75
58
  }
@@ -1,6 +1,5 @@
1
1
  import { readFile, removeEntry, writeFile } from "@jsenv/filesystem";
2
- import { UNICODE, createDetailedMessage, createLogger } from "@jsenv/log";
3
-
2
+ import { UNICODE, createDetailedMessage, createLogger } from "@jsenv/humanize";
4
3
  import { getAuthorityFileInfos } from "./internal/authority_file_infos.js";
5
4
  import { attributeDescriptionFromAttributeArray } from "./internal/certificate_data_converter.js";
6
5
  import { createAuthorityRootCertificate } from "./internal/certificate_generator.js";
@@ -213,7 +212,7 @@ export const installCertificateAuthority = async ({
213
212
  certificateValidityDurationInMs,
214
213
  },
215
214
  );
216
- if (Object.keys(rootCertificateDifferences).length) {
215
+ if (rootCertificateDifferences.length) {
217
216
  const paramNames = Object.keys(rootCertificateDifferences);
218
217
  logger.info(
219
218
  `${UNICODE.INFO} certificate attributes are outdated: ${paramNames}`,
@@ -327,13 +326,13 @@ export const uninstallCertificateAuthority = async ({
327
326
  const rootCertificateCommonName = attributeDescriptionFromAttributeArray(
328
327
  rootCertificateForgeObject.subject.attributes,
329
328
  ).commonName;
330
- const { removeCertificateFromTrustStores } =
331
- await importPlatformMethods();
332
- await removeCertificateFromTrustStores({
329
+ const platformMethods = await importPlatformMethods();
330
+ await platformMethods.executeTrustQuery({
333
331
  logger,
334
- certificate: rootCertificate,
335
- certificateFileUrl: rootCertificateFileInfo.url,
336
332
  certificateCommonName: rootCertificateCommonName,
333
+ certificateFileUrl: rootCertificateFileInfo.url,
334
+ certificate: rootCertificate,
335
+ verb: "REMOVE_TRUST",
337
336
  });
338
337
  }
339
338
  filesToRemove.push(rootCertificateFileInfo.url);
@@ -1,7 +1,6 @@
1
1
  import { writeFileSync } from "@jsenv/filesystem";
2
- import { UNICODE, createDetailedMessage, createLogger } from "@jsenv/log";
2
+ import { UNICODE, createDetailedMessage, createLogger } from "@jsenv/humanize";
3
3
  import { readFileSync } from "node:fs";
4
-
5
4
  import { getAuthorityFileInfos } from "./internal/authority_file_infos.js";
6
5
  import { requestCertificateFromAuthority } from "./internal/certificate_generator.js";
7
6
  import { forge } from "./internal/forge.js";
@@ -47,7 +46,9 @@ export const requestCertificate = ({
47
46
  } = getAuthorityFileInfos();
48
47
  if (!rootCertificateFileInfo.exists) {
49
48
  throw new Error(
50
- `Certificate authority not found, "installCertificateAuthority" must be called before "requestServerCertificate"`,
49
+ `Certificate authority not found, "installCertificateAuthority" must be called before "requestServerCertificate".
50
+ --- Suggested command to run ---
51
+ npx @jsenv/https-local init`,
51
52
  );
52
53
  }
53
54
  if (!rootCertificatePrivateKeyFileInfo.exists) {
@@ -84,7 +85,6 @@ export const requestCertificate = ({
84
85
  logger.debug(`Generating server certificate...`);
85
86
  const { certificateForgeObject, certificatePrivateKeyForgeObject } =
86
87
  requestCertificateFromAuthority({
87
- logger,
88
88
  authorityCertificateForgeObject: rootCertificateForgeObject,
89
89
  auhtorityCertificatePrivateKeyForgeObject:
90
90
  rootCertificatePrivateKeyForgeObject,
@@ -1,5 +1,4 @@
1
- import { createDetailedMessage, createLogger, UNICODE } from "@jsenv/log";
2
-
1
+ import { createDetailedMessage, createLogger, UNICODE } from "@jsenv/humanize";
3
2
  import {
4
3
  HOSTS_FILE_PATH,
5
4
  parseHosts,
@@ -0,0 +1,126 @@
1
+ #!/usr/bin/env node
2
+
3
+ import {
4
+ installCertificateAuthority,
5
+ requestCertificate,
6
+ uninstallCertificateAuthority,
7
+ verifyHostsFile,
8
+ } from "@jsenv/https-local";
9
+ import { writeFileSync } from "node:fs";
10
+ import { parseArgs } from "node:util";
11
+
12
+ const options = {
13
+ "help": {
14
+ type: "boolean",
15
+ },
16
+ "trust": {
17
+ type: "boolean",
18
+ },
19
+ "certificate": {
20
+ type: "string",
21
+ },
22
+ "private-key": {
23
+ type: "string",
24
+ },
25
+ "hostnames": {
26
+ type: "string",
27
+ },
28
+ };
29
+ const { values, positionals } = parseArgs({
30
+ options,
31
+ allowPositionals: true,
32
+ });
33
+
34
+ if (values.help || positionals.length === 0) {
35
+ console.log(`https-local: Generate https certificates to use on your machine.
36
+
37
+ Usage:
38
+
39
+ npx @jsenv/https-local init
40
+ Install root certificate, trust it and ensure localhost is mapped to 127.0.0.1
41
+
42
+ npx @jsenv/https-local cleanup
43
+ Uninstall root certificate and remove its trust from os and browsers
44
+
45
+ npx @jsenv/https-local generate
46
+ Generate a server certificate and write it to files
47
+ - certificate: Path where to write the certificate file (default: certificate.pem)
48
+ - private-key: Path where to write the private key file (default: private_key.pem)
49
+ - hostnames: Comma-separated list of hostnames (default: localhost)
50
+
51
+ Advanced commands:
52
+
53
+ npx @jsenv/https-local install --trust
54
+ Install root certificate on the filesystem
55
+ - trust: Try to add root certificate to os and browser trusted stores
56
+
57
+ npx @jsenv/https-local uninstall
58
+ Uninstall root certificate from the filesystem
59
+
60
+ npx @jsenv/https-local localhost-mapping
61
+ Ensure localhost mapping to 127.0.0.1 is set on the filesystem
62
+
63
+ https://github.com/jsenv/core/tree/main/packages/tooling/https-local
64
+
65
+ `);
66
+
67
+ process.exit(0);
68
+ }
69
+
70
+ const commandHandlers = {
71
+ init: async () => {
72
+ await installCertificateAuthority({
73
+ tryToTrust: true,
74
+ NSSDynamicInstall: true,
75
+ });
76
+ await verifyHostsFile({
77
+ ipMappings: {
78
+ "127.0.0.1": ["localhost"],
79
+ },
80
+ tryToUpdateHostsFile: true,
81
+ });
82
+ },
83
+ cleanup: async () => {
84
+ await uninstallCertificateAuthority({
85
+ tryToUntrust: true,
86
+ });
87
+ },
88
+ install: async ({ trust }) => {
89
+ await installCertificateAuthority({
90
+ tryToTrust: trust,
91
+ NSSDynamicInstall: trust,
92
+ });
93
+ },
94
+ uninstall: async () => {
95
+ await uninstallCertificateAuthority({
96
+ tryToUntrust: true,
97
+ });
98
+ },
99
+ ["localhost-mapping"]: async () => {
100
+ await verifyHostsFile({
101
+ ipMappings: {
102
+ "127.0.0.1": ["localhost"],
103
+ },
104
+ tryToUpdateHostsFile: true,
105
+ });
106
+ },
107
+ generate: async ({ certificate, "private-key": privateKey, hostnames }) => {
108
+ const certificateFilePath = certificate || "certificate.pem";
109
+ const privateKeyFilePath = privateKey || "private_key.pem";
110
+ const altNames = hostnames ? hostnames.split(",") : ["localhost"];
111
+ const result = requestCertificate({ altNames });
112
+ writeFileSync(certificateFilePath, result.certificate);
113
+ writeFileSync(privateKeyFilePath, result.privateKey);
114
+ console.log(`certificate written to ${certificateFilePath}`);
115
+ console.log(`private key written to ${privateKeyFilePath}`);
116
+ },
117
+ };
118
+
119
+ const [command] = positionals;
120
+ const commandHandler = commandHandlers[command];
121
+ if (!commandHandler) {
122
+ console.error(`Error: unknown command ${command}.`);
123
+ process.exit(1);
124
+ }
125
+
126
+ await commandHandler(values);
@@ -1,6 +1,5 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { fileURLToPath } from "node:url";
3
-
4
3
  import { getCertificateAuthorityFileUrls } from "./certificate_authority_file_urls.js";
5
4
 
6
5
  export const getAuthorityFileInfos = () => {
@@ -8,9 +8,8 @@ export const parseHosts = (
8
8
  const lines = [];
9
9
  hosts.split(/\r?\n/).forEach((line) => {
10
10
  const lineWithoutComments = line.replace(/#.*/, "");
11
- const matches = /^\s*(\S+)\s+(\S+(?:\s+\S+)*)\s*$/.exec(
12
- lineWithoutComments,
13
- );
11
+ // eslint-disable-next-line regexp/no-super-linear-backtracking
12
+ const matches = /^\s*?(.+?)\s+(.+?)\s*$/.exec(lineWithoutComments);
14
13
  if (matches && matches.length === 3) {
15
14
  const [, ip, host] = matches;
16
15
  const hostnames = host.split(" ");
@@ -1,5 +1,4 @@
1
1
  import { readFile } from "@jsenv/filesystem";
2
-
3
2
  import { HOSTS_FILE_PATH } from "./hosts_utils.js";
4
3
 
5
4
  export const readHostsFile = async (hostsFilePath = HOSTS_FILE_PATH) => {
@@ -1,7 +1,5 @@
1
1
  import { createRequire } from "node:module";
2
-
3
2
  import { exec } from "../exec.js";
4
-
5
3
  import { HOSTS_FILE_PATH } from "./hosts_utils.js";
6
4
 
7
5
  export const writeHostsFile = async (
@@ -1,8 +1,6 @@
1
1
  import { readFile } from "@jsenv/filesystem";
2
2
  import { createRequire } from "node:module";
3
-
4
3
  import { exec } from "../exec.js";
5
-
6
4
  import { HOSTS_FILE_PATH } from "./hosts_utils.js";
7
5
 
8
6
  export const writeLineInHostsFile = async (
@@ -1,7 +1,6 @@
1
1
  import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem";
2
- import { UNICODE } from "@jsenv/log";
2
+ import { UNICODE } from "@jsenv/humanize";
3
3
  import { execSync } from "node:child_process";
4
-
5
4
  import { executeTrustQueryOnBrowserNSSDB } from "../nssdb_browser.js";
6
5
  import {
7
6
  detectIfNSSIsInstalled,
@@ -1,7 +1,6 @@
1
1
  import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem";
2
- import { UNICODE } from "@jsenv/log";
2
+ import { UNICODE } from "@jsenv/humanize";
3
3
  import { execSync } from "node:child_process";
4
-
5
4
  import { executeTrustQueryOnBrowserNSSDB } from "../nssdb_browser.js";
6
5
  import {
7
6
  detectIfNSSIsInstalled,
@@ -13,7 +13,6 @@ export const executeTrustQuery = async ({
13
13
  }) => {
14
14
  const linuxTrustInfo = await executeTrustQueryOnLinux({
15
15
  logger,
16
- certificateCommonName,
17
16
  certificateFileUrl,
18
17
  certificateIsNew,
19
18
  certificate,
@@ -3,10 +3,9 @@
3
3
  */
4
4
 
5
5
  import { readFile } from "@jsenv/filesystem";
6
- import { createDetailedMessage, UNICODE } from "@jsenv/log";
6
+ import { createDetailedMessage, UNICODE } from "@jsenv/humanize";
7
7
  import { existsSync } from "node:fs";
8
8
  import { fileURLToPath } from "node:url";
9
-
10
9
  import { exec } from "../exec.js";
11
10
  import {
12
11
  VERB_ADD_TRUST,
@@ -1,7 +1,6 @@
1
1
  // https://github.com/FiloSottile/mkcert/issues/447
2
2
 
3
- import { UNICODE } from "@jsenv/log";
4
-
3
+ import { UNICODE } from "@jsenv/humanize";
5
4
  import { exec } from "../exec.js";
6
5
  import { memoize } from "../memoize.js";
7
6
 
@@ -1,6 +1,5 @@
1
- import { UNICODE } from "@jsenv/log";
1
+ import { UNICODE } from "@jsenv/humanize";
2
2
  import { existsSync } from "node:fs";
3
-
4
3
  import { memoize } from "../memoize.js";
5
4
 
6
5
  const REASON_CHROME_NOT_DETECTED = `Chrome not detected`;
@@ -1,7 +1,6 @@
1
1
  import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem";
2
- import { UNICODE, createTaskLog } from "@jsenv/log";
2
+ import { UNICODE, createTaskLog } from "@jsenv/humanize";
3
3
  import { execSync } from "node:child_process";
4
-
5
4
  import { executeTrustQueryOnBrowserNSSDB } from "../nssdb_browser.js";
6
5
  import {
7
6
  detectIfNSSIsInstalled,
@@ -44,7 +44,6 @@ export const executeTrustQuery = async ({
44
44
  });
45
45
 
46
46
  const safariTrustInfo = await executeTrustQueryOnSafari({
47
- logger,
48
47
  // safari needs macTrustInfo because it uses OS trust store
49
48
  macTrustInfo,
50
49
  });
@@ -1,8 +1,7 @@
1
1
  // https://ss64.com/osx/security.html
2
2
 
3
- import { createDetailedMessage, UNICODE } from "@jsenv/log";
3
+ import { createDetailedMessage, UNICODE } from "@jsenv/humanize";
4
4
  import { fileURLToPath } from "node:url";
5
-
6
5
  import { exec } from "../exec.js";
7
6
  import { searchCertificateInCommandOutput } from "../search_certificate_in_command_output.js";
8
7
  import {
@@ -1,7 +1,6 @@
1
1
  import { assertAndNormalizeDirectoryUrl } from "@jsenv/filesystem";
2
- import { UNICODE } from "@jsenv/log";
2
+ import { UNICODE } from "@jsenv/humanize";
3
3
  import { fileURLToPath } from "node:url";
4
-
5
4
  import { commandExists } from "../command.js";
6
5
  import { exec } from "../exec.js";
7
6
  import { memoize } from "../memoize.js";
@@ -7,11 +7,10 @@ import {
7
7
  assertAndNormalizeDirectoryUrl,
8
8
  collectFiles,
9
9
  } from "@jsenv/filesystem";
10
- import { createDetailedMessage, UNICODE } from "@jsenv/log";
10
+ import { createDetailedMessage, UNICODE } from "@jsenv/humanize";
11
11
  import { urlToFilename } from "@jsenv/urls";
12
12
  import { existsSync } from "node:fs";
13
13
  import { fileURLToPath } from "node:url";
14
-
15
14
  import { detectBrowser } from "./browser_detection.js";
16
15
  import { exec } from "./exec.js";
17
16
  import { searchCertificateInCommandOutput } from "./search_certificate_in_command_output.js";
@@ -1,4 +1,4 @@
1
- import { UNICODE } from "@jsenv/log";
1
+ import { UNICODE } from "@jsenv/humanize";
2
2
 
3
3
  const platformTrustInfo = {
4
4
  status: "unknown",
@@ -1,7 +1,6 @@
1
- import { UNICODE } from "@jsenv/log";
1
+ import { UNICODE } from "@jsenv/humanize";
2
2
  import { existsSync } from "node:fs";
3
3
  import { createRequire } from "node:module";
4
-
5
4
  import { memoize } from "../memoize.js";
6
5
 
7
6
  const require = createRequire(import.meta.url);
@@ -3,10 +3,9 @@
3
3
  * - A way to install and use NSS command on windows to update firefox NSS dabatase file
4
4
  */
5
5
 
6
- import { UNICODE } from "@jsenv/log";
6
+ import { UNICODE } from "@jsenv/humanize";
7
7
  import { existsSync } from "node:fs";
8
8
  import { createRequire } from "node:module";
9
-
10
9
  import { memoize } from "../memoize.js";
11
10
 
12
11
  const require = createRequire(import.meta.url);
@@ -14,7 +14,6 @@ export const executeTrustQuery = async ({
14
14
  certificateCommonName,
15
15
  certificateFileUrl,
16
16
  certificateIsNew,
17
- certificate,
18
17
  verb,
19
18
  }) => {
20
19
  const windowsTrustInfo = await executeTrustQueryOnWindows({
@@ -22,7 +21,6 @@ export const executeTrustQuery = async ({
22
21
  certificateCommonName,
23
22
  certificateFileUrl,
24
23
  certificateIsNew,
25
- certificate,
26
24
  verb,
27
25
  });
28
26
 
@@ -3,9 +3,8 @@
3
3
  * https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/certutil
4
4
  */
5
5
 
6
- import { createDetailedMessage, UNICODE } from "@jsenv/log";
6
+ import { createDetailedMessage, UNICODE } from "@jsenv/humanize";
7
7
  import { fileURLToPath } from "node:url";
8
-
9
8
  import { exec } from "../exec.js";
10
9
  import {
11
10
  VERB_ADD_TRUST,
package/src/main.js CHANGED
@@ -12,12 +12,9 @@ export {
12
12
  installCertificateAuthority,
13
13
  uninstallCertificateAuthority,
14
14
  } from "./certificate_authority.js";
15
-
15
+ export { requestCertificate } from "./certificate_request.js";
16
+ export { verifyHostsFile } from "./hosts_file_verif.js";
16
17
  export {
17
18
  createValidityDurationOfXDays,
18
19
  createValidityDurationOfXYears,
19
20
  } from "./validity_duration.js";
20
-
21
- export { verifyHostsFile } from "./hosts_file_verif.js";
22
-
23
- export { requestCertificate } from "./certificate_request.js";