@parix/cli 0.1.7 → 0.1.8

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/cli.cjs CHANGED
@@ -25,8 +25,14 @@ async function main() {
25
25
  .addCommand((0, tb_1.createTbCommand)());
26
26
  await program.parseAsync(process.argv);
27
27
  }
28
- main().catch((error) => {
28
+ main()
29
+ .then(() => {
30
+ // CLIs that open HTTP servers / use undici fetch can leave sockets that keep
31
+ // Node alive after the command is done. Exit explicitly once work is finished.
32
+ process.exit(process.exitCode ?? 0);
33
+ })
34
+ .catch((error) => {
29
35
  const message = error instanceof Error ? error.message : String(error);
30
36
  prompts_1.log.error(message);
31
- process.exitCode = 1;
37
+ process.exit(1);
32
38
  });
package/dist/cli.js CHANGED
@@ -23,8 +23,14 @@ async function main() {
23
23
  .addCommand(createTbCommand());
24
24
  await program.parseAsync(process.argv);
25
25
  }
26
- main().catch((error) => {
26
+ main()
27
+ .then(() => {
28
+ // CLIs that open HTTP servers / use undici fetch can leave sockets that keep
29
+ // Node alive after the command is done. Exit explicitly once work is finished.
30
+ process.exit(process.exitCode ?? 0);
31
+ })
32
+ .catch((error) => {
27
33
  const message = error instanceof Error ? error.message : String(error);
28
34
  log.error(message);
29
- process.exitCode = 1;
35
+ process.exit(1);
30
36
  });
@@ -69,6 +69,9 @@ async function handleLogin(options) {
69
69
  waitSpinner.start('Waiting for browser callback');
70
70
  const callbackResult = await loopbackServer.waitForResult();
71
71
  waitSpinner.stop('Received browser callback');
72
+ // Shut down the loopback server before token exchange so browser keep-alive
73
+ // sockets cannot keep the process open after sign-in completes.
74
+ await loopbackServer.close().catch(() => { });
72
75
  const tokenSet = await (0, oauth_api_1.exchangeAuthorizationCode)({
73
76
  baseUrl,
74
77
  code: callbackResult.code,
@@ -66,6 +66,9 @@ async function handleLogin(options) {
66
66
  waitSpinner.start('Waiting for browser callback');
67
67
  const callbackResult = await loopbackServer.waitForResult();
68
68
  waitSpinner.stop('Received browser callback');
69
+ // Shut down the loopback server before token exchange so browser keep-alive
70
+ // sockets cannot keep the process open after sign-in completes.
71
+ await loopbackServer.close().catch(() => { });
69
72
  const tokenSet = await exchangeAuthorizationCode({
70
73
  baseUrl,
71
74
  code: callbackResult.code,
@@ -37,6 +37,8 @@ async function startLoopbackServer(options) {
37
37
  rejectResult = reject;
38
38
  });
39
39
  server.on('request', (request, response) => {
40
+ // One-shot OAuth loopback: do not keep browser sockets alive after the response.
41
+ response.setHeader('connection', 'close');
40
42
  const requestUrl = new URL(request.url ?? '/', 'http://127.0.0.1');
41
43
  if (requestUrl.pathname === '/favicon.ico') {
42
44
  response.writeHead(204);
@@ -120,17 +122,37 @@ function listen(server, port) {
120
122
  }
121
123
  function closeServer(server) {
122
124
  return new Promise((resolve, reject) => {
125
+ let settled = false;
126
+ const finish = (error) => {
127
+ if (settled) {
128
+ return;
129
+ }
130
+ settled = true;
131
+ if (error) {
132
+ reject(error);
133
+ return;
134
+ }
135
+ resolve();
136
+ };
123
137
  server.close((error) => {
124
138
  if (error) {
125
139
  if ('code' in error && error.code === 'ERR_SERVER_NOT_RUNNING') {
126
- resolve();
140
+ finish();
127
141
  return;
128
142
  }
129
- reject(error);
143
+ finish(error);
130
144
  return;
131
145
  }
132
- resolve();
146
+ finish();
133
147
  });
148
+ // `server.close()` waits for existing connections. Browsers often keep the
149
+ // loopback socket open via HTTP keep-alive, which would hang login forever
150
+ // after the session is already stored. Force those sockets closed (Node >= 18.2).
151
+ server.closeAllConnections();
152
+ // Last resort: never let close block the CLI indefinitely.
153
+ setTimeout(() => {
154
+ finish();
155
+ }, 250).unref();
134
156
  });
135
157
  }
136
158
  function buildHtml(title, message) {
@@ -34,6 +34,8 @@ export async function startLoopbackServer(options) {
34
34
  rejectResult = reject;
35
35
  });
36
36
  server.on('request', (request, response) => {
37
+ // One-shot OAuth loopback: do not keep browser sockets alive after the response.
38
+ response.setHeader('connection', 'close');
37
39
  const requestUrl = new URL(request.url ?? '/', 'http://127.0.0.1');
38
40
  if (requestUrl.pathname === '/favicon.ico') {
39
41
  response.writeHead(204);
@@ -117,17 +119,37 @@ function listen(server, port) {
117
119
  }
118
120
  function closeServer(server) {
119
121
  return new Promise((resolve, reject) => {
122
+ let settled = false;
123
+ const finish = (error) => {
124
+ if (settled) {
125
+ return;
126
+ }
127
+ settled = true;
128
+ if (error) {
129
+ reject(error);
130
+ return;
131
+ }
132
+ resolve();
133
+ };
120
134
  server.close((error) => {
121
135
  if (error) {
122
136
  if ('code' in error && error.code === 'ERR_SERVER_NOT_RUNNING') {
123
- resolve();
137
+ finish();
124
138
  return;
125
139
  }
126
- reject(error);
140
+ finish(error);
127
141
  return;
128
142
  }
129
- resolve();
143
+ finish();
130
144
  });
145
+ // `server.close()` waits for existing connections. Browsers often keep the
146
+ // loopback socket open via HTTP keep-alive, which would hang login forever
147
+ // after the session is already stored. Force those sockets closed (Node >= 18.2).
148
+ server.closeAllConnections();
149
+ // Last resort: never let close block the CLI indefinitely.
150
+ setTimeout(() => {
151
+ finish();
152
+ }, 250).unref();
131
153
  });
132
154
  }
133
155
  function buildHtml(title, message) {
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@parix/cli",
3
3
  "description": "Parix command line interface",
4
4
  "type": "module",
5
- "version": "0.1.7",
5
+ "version": "0.1.8",
6
6
  "bin": {
7
7
  "parix": "dist/cli.cjs"
8
8
  },