@naturalcycles/backend-lib 9.62.0 → 9.63.1

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.
@@ -1,12 +1,12 @@
1
+ import { _parseArgs } from '@naturalcycles/nodejs-lib/args';
1
2
  import { runScript } from '@naturalcycles/nodejs-lib/runScript';
2
- import { _yargs } from '@naturalcycles/nodejs-lib/yargs';
3
3
  import { deployGae } from '../deploy/deployGae.js';
4
- import { deployHealthCheckYargsOptions } from '../deploy/deployHealthCheck.js';
5
- import { deployPrepareYargsOptions } from '../deploy/deployPrepare.js';
4
+ import { deployHealthCheckCliOptions } from '../deploy/deployHealthCheck.js';
5
+ import { deployPrepareCliOptions } from '../deploy/deployPrepare.js';
6
6
  runScript(async () => {
7
- const opt = _yargs().options({
8
- ...deployPrepareYargsOptions,
9
- ...deployHealthCheckYargsOptions,
10
- }).argv;
7
+ const opt = _parseArgs({
8
+ ...deployPrepareCliOptions,
9
+ ...deployHealthCheckCliOptions,
10
+ });
11
11
  await deployGae(opt);
12
12
  });
@@ -6,16 +6,16 @@ yarn deploy-health-check --url https://service-dot-yourproject.appspot.com
6
6
  --intervalSec 2
7
7
 
8
8
  */
9
+ import { _parseArgs } from '@naturalcycles/nodejs-lib/args';
9
10
  import { runScript } from '@naturalcycles/nodejs-lib/runScript';
10
- import { _yargs } from '@naturalcycles/nodejs-lib/yargs';
11
- import { deployHealthCheck, deployHealthCheckYargsOptions } from '../deploy/deployHealthCheck.js';
11
+ import { deployHealthCheck, deployHealthCheckCliOptions } from '../deploy/deployHealthCheck.js';
12
12
  runScript(async () => {
13
- const { url, ...opt } = _yargs().options({
14
- ...deployHealthCheckYargsOptions,
13
+ const { url, ...opt } = _parseArgs({
14
+ ...deployHealthCheckCliOptions,
15
15
  url: {
16
16
  type: 'string',
17
17
  demandOption: true,
18
18
  },
19
- }).argv;
19
+ });
20
20
  await deployHealthCheck(url, opt);
21
21
  });
@@ -3,11 +3,11 @@
3
3
  yarn deploy-prepare
4
4
 
5
5
  */
6
+ import { _parseArgs } from '@naturalcycles/nodejs-lib/args';
6
7
  import { runScript } from '@naturalcycles/nodejs-lib/runScript';
7
- import { _yargs } from '@naturalcycles/nodejs-lib/yargs';
8
- import { deployPrepare, deployPrepareYargsOptions } from '../deploy/deployPrepare.js';
8
+ import { deployPrepare, deployPrepareCliOptions } from '../deploy/deployPrepare.js';
9
9
  runScript(async () => {
10
- const opt = _yargs().options(deployPrepareYargsOptions).argv;
10
+ const opt = _parseArgs(deployPrepareCliOptions);
11
11
  await deployPrepare(opt);
12
12
  });
13
13
  // deploy strategy
@@ -1,13 +1,13 @@
1
+ import { _parseArgs } from '@naturalcycles/nodejs-lib/args';
1
2
  import { runScript } from '@naturalcycles/nodejs-lib/runScript';
2
- import { _yargs } from '@naturalcycles/nodejs-lib/yargs';
3
3
  import { undeployGae } from '../deploy/deployGae.js';
4
4
  runScript(async () => {
5
- const { branch } = _yargs().options({
5
+ const { branch } = _parseArgs({
6
6
  branch: {
7
7
  type: 'string',
8
8
  demandOption: true,
9
9
  desc: `Because Github Actions delete event happens after the branch is already deleted - you need to pass it manually`,
10
10
  },
11
- }).argv;
11
+ });
12
12
  await undeployGae(branch);
13
13
  });
@@ -11,50 +11,7 @@ export interface DeployHealthCheckOptions {
11
11
  gaeVersion?: string;
12
12
  authHeader?: string;
13
13
  }
14
- export declare const deployHealthCheckYargsOptions: {
15
- readonly thresholdHealthy: {
16
- readonly type: 'number';
17
- readonly default: 5;
18
- };
19
- readonly thresholdUnhealthy: {
20
- readonly type: 'number';
21
- readonly default: 8;
22
- };
23
- readonly maxTries: {
24
- readonly type: 'number';
25
- readonly default: 30;
26
- };
27
- readonly timeoutSec: {
28
- readonly type: 'number';
29
- readonly default: 180;
30
- };
31
- readonly intervalSec: {
32
- readonly type: 'number';
33
- readonly default: 1;
34
- };
35
- readonly logOnFailure: {
36
- readonly type: 'boolean';
37
- readonly default: true;
38
- readonly desc: 'Show server logs on health check failure (requires gaeProject, gaeService, gaeVersion)';
39
- };
40
- readonly logOnSuccess: {
41
- readonly type: 'boolean';
42
- readonly default: false;
43
- readonly desc: 'Show server logs on health check success (requires gaeProject, gaeService, gaeVersion)';
44
- };
45
- readonly gaeProject: {
46
- readonly type: 'string';
47
- };
48
- readonly gaeService: {
49
- readonly type: 'string';
50
- };
51
- readonly gaeVersion: {
52
- readonly type: 'string';
53
- };
54
- readonly authHeader: {
55
- readonly type: 'string';
56
- };
57
- };
14
+ export declare const deployHealthCheckCliOptions: CliOptions;
58
15
  /**
59
16
  * Requires up to `healthyThreshold` consecutive OK responses to succeed.
60
17
  * Fails on up to `unhealthyThreshold` consecutive FAIL responses.
@@ -6,7 +6,7 @@ import { pDelay } from '@naturalcycles/js-lib/promise/pDelay.js';
6
6
  import { dimGrey, red } from '@naturalcycles/nodejs-lib/colors';
7
7
  import { exec2 } from '@naturalcycles/nodejs-lib/exec2';
8
8
  import { coloredHttpCode } from '../server/request.log.util.js';
9
- export const deployHealthCheckYargsOptions = {
9
+ export const deployHealthCheckCliOptions = {
10
10
  thresholdHealthy: {
11
11
  type: 'number',
12
12
  default: 5,
@@ -9,23 +9,5 @@ export interface DeployPrepareOptions {
9
9
  */
10
10
  appYamlPassEnv?: string;
11
11
  }
12
- export declare const deployPrepareYargsOptions: {
13
- readonly projectDir: {
14
- readonly type: 'string';
15
- readonly default: '.';
16
- };
17
- readonly targetDir: {
18
- readonly type: 'string';
19
- readonly default: `./tmp/deploy`;
20
- };
21
- readonly createNpmrc: {
22
- readonly type: 'boolean';
23
- readonly desc: 'Create .npmrc in targetDir if NPM_TOKEN env var is set';
24
- readonly default: true;
25
- };
26
- readonly appYamlPassEnv: {
27
- readonly type: 'string';
28
- readonly desc: 'Comma-separated list of env variables that will be passed to app.yaml from process.env';
29
- };
30
- };
12
+ export declare const deployPrepareCliOptions: CliOptions;
31
13
  export declare function deployPrepare(opt?: DeployPrepareOptions): Promise<DeployInfo>;
@@ -5,7 +5,7 @@ import { kpySync } from '@naturalcycles/nodejs-lib/kpy';
5
5
  import { srcDir } from '../paths.cnst.js';
6
6
  import { getBackendCfg } from './backend.cfg.util.js';
7
7
  import { createAndSaveAppYaml, createAndSaveDeployInfo } from './deploy.util.js';
8
- export const deployPrepareYargsOptions = {
8
+ export const deployPrepareCliOptions = {
9
9
  projectDir: {
10
10
  type: 'string',
11
11
  default: '.',
@@ -32,7 +32,7 @@ export interface StartServerCfg extends DefaultAppCfg {
32
32
  */
33
33
  onShutdown?: () => Promise<void>;
34
34
  /**
35
- * @default 3000
35
+ * @default 8_000
36
36
  */
37
37
  forceShutdownTimeout?: number;
38
38
  sentryService?: SentrySharedService;
@@ -45,8 +45,13 @@ export class BackendServer {
45
45
  resolve(server);
46
46
  });
47
47
  });
48
- // This is to fix GCP LoadBalancer race condition
49
- this.server.keepAliveTimeout = 600 * 1000; // 10 minutes
48
+ // This is to fix GCP LoadBalancer race condition:
49
+ // GCLB's backend keep-alive timeout is fixed at 600s, and the backend must use
50
+ // a strictly higher value, otherwise the server can close an idle connection at the
51
+ // same moment GCLB reuses it, resulting in intermittent 502s.
52
+ // https://cloud.google.com/load-balancing/docs/https#timeouts_and_retries
53
+ this.server.keepAliveTimeout = 620 * 1000; // slightly above GCLB's 600s
54
+ this.server.headersTimeout = 630 * 1000; // conventionally kept above keepAliveTimeout
50
55
  let address = `http://localhost:${port}`; // default
51
56
  const addr = this.server.address();
52
57
  if (addr) {
@@ -77,7 +82,7 @@ export class BackendServer {
77
82
  const shutdownTimeout = setTimeout(() => {
78
83
  console.log(boldGrey('Forceful shutdown after timeout'));
79
84
  process.exit(0);
80
- }, this.cfg.forceShutdownTimeout ?? 10_000);
85
+ }, this.cfg.forceShutdownTimeout ?? 8000);
81
86
  try {
82
87
  await Promise.all([
83
88
  this.server && new Promise(r => this.server.close(r)),
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@naturalcycles/backend-lib",
3
3
  "type": "module",
4
- "version": "9.62.0",
4
+ "version": "9.63.1",
5
5
  "dependencies": {
6
6
  "@naturalcycles/db-lib": "^10",
7
7
  "@naturalcycles/js-lib": "^15",
8
8
  "@naturalcycles/nodejs-lib": "^15",
9
9
  "@types/body-parser": "^1",
10
- "@types/compressible": "^2",
11
10
  "@types/cookie-parser": "^1",
12
11
  "@types/cors": "^2",
13
12
  "@types/express": "^5",
13
+ "@types/compressible": "^2",
14
14
  "@types/on-finished": "^2",
15
15
  "@types/on-headers": "^1",
16
16
  "@types/vary": "^1",
@@ -20,8 +20,8 @@
20
20
  "express": "^5",
21
21
  "helmet": "^8",
22
22
  "negotiator": "^1",
23
- "on-finished": "^2",
24
23
  "on-headers": "^1",
24
+ "on-finished": "^2",
25
25
  "tslib": "^2",
26
26
  "vary": "^1"
27
27
  },
@@ -43,9 +43,9 @@
43
43
  },
44
44
  "devDependencies": {
45
45
  "@sentry/node-core": "^10",
46
- "@typescript/native-preview": "beta",
46
+ "typescript": "rc",
47
47
  "fastify": "^5",
48
- "@naturalcycles/dev-lib": "18.4.2"
48
+ "@naturalcycles/dev-lib": "0.0.0"
49
49
  },
50
50
  "exports": {
51
51
  ".": "./dist/index.js",
@@ -1,14 +1,14 @@
1
+ import { _parseArgs } from '@naturalcycles/nodejs-lib/args'
1
2
  import { runScript } from '@naturalcycles/nodejs-lib/runScript'
2
- import { _yargs } from '@naturalcycles/nodejs-lib/yargs'
3
3
  import { deployGae } from '../deploy/deployGae.js'
4
- import { deployHealthCheckYargsOptions } from '../deploy/deployHealthCheck.js'
5
- import { deployPrepareYargsOptions } from '../deploy/deployPrepare.js'
4
+ import { deployHealthCheckCliOptions } from '../deploy/deployHealthCheck.js'
5
+ import { deployPrepareCliOptions } from '../deploy/deployPrepare.js'
6
6
 
7
7
  runScript(async () => {
8
- const opt = _yargs().options({
9
- ...deployPrepareYargsOptions,
10
- ...deployHealthCheckYargsOptions,
11
- }).argv
8
+ const opt = _parseArgs({
9
+ ...deployPrepareCliOptions,
10
+ ...deployHealthCheckCliOptions,
11
+ })
12
12
 
13
13
  await deployGae(opt)
14
14
  })
@@ -7,18 +7,18 @@ yarn deploy-health-check --url https://service-dot-yourproject.appspot.com
7
7
 
8
8
  */
9
9
 
10
+ import { _parseArgs } from '@naturalcycles/nodejs-lib/args'
10
11
  import { runScript } from '@naturalcycles/nodejs-lib/runScript'
11
- import { _yargs } from '@naturalcycles/nodejs-lib/yargs'
12
- import { deployHealthCheck, deployHealthCheckYargsOptions } from '../deploy/deployHealthCheck.js'
12
+ import { deployHealthCheck, deployHealthCheckCliOptions } from '../deploy/deployHealthCheck.js'
13
13
 
14
14
  runScript(async () => {
15
- const { url, ...opt } = _yargs().options({
16
- ...deployHealthCheckYargsOptions,
15
+ const { url, ...opt } = _parseArgs({
16
+ ...deployHealthCheckCliOptions,
17
17
  url: {
18
18
  type: 'string',
19
19
  demandOption: true,
20
20
  },
21
- }).argv
21
+ })
22
22
 
23
23
  await deployHealthCheck(url, opt)
24
24
  })
@@ -4,12 +4,12 @@ yarn deploy-prepare
4
4
 
5
5
  */
6
6
 
7
+ import { _parseArgs } from '@naturalcycles/nodejs-lib/args'
7
8
  import { runScript } from '@naturalcycles/nodejs-lib/runScript'
8
- import { _yargs } from '@naturalcycles/nodejs-lib/yargs'
9
- import { deployPrepare, deployPrepareYargsOptions } from '../deploy/deployPrepare.js'
9
+ import { deployPrepare, deployPrepareCliOptions } from '../deploy/deployPrepare.js'
10
10
 
11
11
  runScript(async () => {
12
- const opt = _yargs().options(deployPrepareYargsOptions).argv
12
+ const opt = _parseArgs(deployPrepareCliOptions)
13
13
 
14
14
  await deployPrepare(opt)
15
15
  })
@@ -1,15 +1,15 @@
1
+ import { _parseArgs } from '@naturalcycles/nodejs-lib/args'
1
2
  import { runScript } from '@naturalcycles/nodejs-lib/runScript'
2
- import { _yargs } from '@naturalcycles/nodejs-lib/yargs'
3
3
  import { undeployGae } from '../deploy/deployGae.js'
4
4
 
5
5
  runScript(async () => {
6
- const { branch } = _yargs().options({
6
+ const { branch } = _parseArgs({
7
7
  branch: {
8
8
  type: 'string',
9
9
  demandOption: true,
10
10
  desc: `Because Github Actions delete event happens after the branch is already deleted - you need to pass it manually`,
11
11
  },
12
- }).argv
12
+ })
13
13
 
14
14
  await undeployGae(branch)
15
15
  })
@@ -8,6 +8,7 @@ import {
8
8
  } from '@naturalcycles/js-lib/object/object.util.js'
9
9
  import { pDelay } from '@naturalcycles/js-lib/promise/pDelay.js'
10
10
  import type { UnixTimestampMillis } from '@naturalcycles/js-lib/types'
11
+ import type { CliOptions } from '@naturalcycles/nodejs-lib/args'
11
12
  import { dimGrey, red } from '@naturalcycles/nodejs-lib/colors'
12
13
  import { exec2 } from '@naturalcycles/nodejs-lib/exec2'
13
14
  import { coloredHttpCode } from '../server/request.log.util.js'
@@ -26,7 +27,7 @@ export interface DeployHealthCheckOptions {
26
27
  authHeader?: string
27
28
  }
28
29
 
29
- export const deployHealthCheckYargsOptions = {
30
+ export const deployHealthCheckCliOptions = {
30
31
  thresholdHealthy: {
31
32
  type: 'number',
32
33
  default: 5,
@@ -69,7 +70,7 @@ export const deployHealthCheckYargsOptions = {
69
70
  authHeader: {
70
71
  type: 'string',
71
72
  },
72
- } as const
73
+ } as const satisfies CliOptions
73
74
 
74
75
  const inspectOpt: InspectOptions = {
75
76
  colors: true,
@@ -1,4 +1,5 @@
1
1
  import { loadEnvFileIfExists } from '@naturalcycles/nodejs-lib'
2
+ import type { CliOptions } from '@naturalcycles/nodejs-lib/args'
2
3
  import { dimGrey } from '@naturalcycles/nodejs-lib/colors'
3
4
  import { fs2 } from '@naturalcycles/nodejs-lib/fs2'
4
5
  import { kpySync } from '@naturalcycles/nodejs-lib/kpy'
@@ -19,7 +20,7 @@ export interface DeployPrepareOptions {
19
20
  appYamlPassEnv?: string
20
21
  }
21
22
 
22
- export const deployPrepareYargsOptions = {
23
+ export const deployPrepareCliOptions = {
23
24
  projectDir: {
24
25
  type: 'string',
25
26
  default: '.',
@@ -37,7 +38,7 @@ export const deployPrepareYargsOptions = {
37
38
  type: 'string',
38
39
  desc: 'Comma-separated list of env variables that will be passed to app.yaml from process.env',
39
40
  },
40
- } as const
41
+ } as const satisfies CliOptions
41
42
 
42
43
  const DEFAULT_FILES = [
43
44
  'dist',
@@ -53,8 +53,13 @@ export class BackendServer {
53
53
  })
54
54
  })
55
55
 
56
- // This is to fix GCP LoadBalancer race condition
57
- this.server.keepAliveTimeout = 600 * 1000 // 10 minutes
56
+ // This is to fix GCP LoadBalancer race condition:
57
+ // GCLB's backend keep-alive timeout is fixed at 600s, and the backend must use
58
+ // a strictly higher value, otherwise the server can close an idle connection at the
59
+ // same moment GCLB reuses it, resulting in intermittent 502s.
60
+ // https://cloud.google.com/load-balancing/docs/https#timeouts_and_retries
61
+ this.server.keepAliveTimeout = 620 * 1000 // slightly above GCLB's 600s
62
+ this.server.headersTimeout = 630 * 1000 // conventionally kept above keepAliveTimeout
58
63
 
59
64
  let address = `http://localhost:${port}` // default
60
65
 
@@ -97,7 +102,7 @@ export class BackendServer {
97
102
  const shutdownTimeout = setTimeout(() => {
98
103
  console.log(boldGrey('Forceful shutdown after timeout'))
99
104
  process.exit(0)
100
- }, this.cfg.forceShutdownTimeout ?? 10_000)
105
+ }, this.cfg.forceShutdownTimeout ?? 8000)
101
106
 
102
107
  try {
103
108
  await Promise.all([
@@ -146,7 +151,7 @@ export interface StartServerCfg extends DefaultAppCfg {
146
151
  onShutdown?: () => Promise<void>
147
152
 
148
153
  /**
149
- * @default 3000
154
+ * @default 8_000
150
155
  */
151
156
  forceShutdownTimeout?: number
152
157