Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Future: Drop Node 18 #2063

Draft
wants to merge 27 commits into
base: make-v21
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
51a2b3d
Drop Node 18.
RobinTail Sep 29, 2024
9ed3675
Stop testing against Node 18.
RobinTail Sep 29, 2024
27f36a9
Changing tsconfig base.
RobinTail Sep 29, 2024
a0232bb
Removing variant in system test.
RobinTail Sep 29, 2024
e9cee9f
Simpler snapshot in system test.
RobinTail Sep 29, 2024
cb6702d
Revert "Simpler snapshot in system test."
RobinTail Sep 29, 2024
38a9da8
Feat: using Intl units formatting, expressing picoseconds through nan…
RobinTail Sep 29, 2024
ea20927
Changelog: future v21.
RobinTail Sep 29, 2024
25e618d
Ref: rm redundant function.
RobinTail Sep 29, 2024
fa08f8b
Ref: minor, naming.
RobinTail Sep 29, 2024
c715aa6
Ref: extracting time units and enforcing constraints.
RobinTail Sep 29, 2024
1c94bf2
CI: Changing Node version to 20 in all other workflows.
RobinTail Sep 29, 2024
12bf12c
Merge branch 'master' into drop-node18
RobinTail Oct 7, 2024
c611457
Merge branch 'make-v21' into drop-node18
RobinTail Oct 8, 2024
95716bf
Merge branch 'make-v21' into drop-node18
RobinTail Oct 8, 2024
2e1b276
REF: moving Intl formatters into the BuiltinLogger class props.
RobinTail Oct 8, 2024
1610a20
Test for makeNumberFormat().
RobinTail Oct 8, 2024
b2d7521
Update tests/unit/builtin-logger.spec.ts
RobinTail Oct 8, 2024
067cb6f
Merge branch 'make-v21' into drop-node18
RobinTail Oct 10, 2024
44c123e
Merge branch 'make-v21' into drop-node18
RobinTail Oct 10, 2024
d8c8351
Merge branch 'make-v21' into drop-node18
RobinTail Oct 16, 2024
2598dc3
Merge branch 'make-v21' into drop-node18
RobinTail Nov 5, 2024
403d42e
FIX: memoizing makeNumberFormat().
RobinTail Nov 6, 2024
9e735fc
Moving formatDuration back to helpers outta class.
RobinTail Nov 6, 2024
29ddde4
Ref: minor, no return in bench.
RobinTail Nov 6, 2024
a24bda6
Merge branch 'make-v21' into drop-node18
RobinTail Nov 8, 2024
3cd7d46
Merge branch 'make-v21' into drop-node18
RobinTail Nov 12, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/minor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
- uses: fregante/setup-git-user@v2
- run: |
yarn install
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
node-version: [18.18.0, 18.x, 20.9.0, 20.x, 22.0.0, 22.x]
node-version: [20.9.0, 20.x, 22.0.0, 22.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- name: Get yarn cache dir
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
registry-url: https://registry.npmjs.org/
- run: yarn install
- run: npm publish --provenance --tag ${{ inputs.tag }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/patch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
node-version: 20
- uses: fregante/setup-git-user@v2
- run: |
yarn install
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
### v21.0.0

- Minimum supported versions of `express`: 4.21.1 and 5.0.1 (fixed vulnerabilities);
- Minimum supported Node versions: 20.9.0 and 22.0.0;
- `BuiltinLogger::profile()` behavior changed for picoseconds: expressing them through nanoseconds;
- Running HTTP server made optional (can now configure HTTPS only):
- Object argument of the `createConfig()` method changed:
- The `server` property renamed to `http` and made optional;
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"*.md"
],
"engines": {
"node": "^18.18.0 || ^20.9.0 || ^22.0.0"
"node": "^20.9.0 || ^22.0.0"
},
"dependencies": {
"ansis": "^3.2.0",
Expand Down Expand Up @@ -114,7 +114,7 @@
"devDependencies": {
"@arethetypeswrong/cli": "^0.17.0",
"@eslint/eslintrc": "^3",
"@tsconfig/node18": "^18.2.1",
"@tsconfig/node20": "^20.1.4",
"@types/compression": "^1.7.5",
"@types/cors": "^2.8.14",
"@types/depd": "^1.1.36",
Expand Down
46 changes: 24 additions & 22 deletions src/logger-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { memoizeWith } from "ramda";
import { isObject } from "./common-helpers";

const severity = {
Expand Down Expand Up @@ -34,32 +35,33 @@ export const isSeverity = (subject: PropertyKey): subject is Severity =>
export const isHidden = (subject: Severity, gate: Severity) =>
severity[subject] < severity[gate];

/**
* @todo consider Intl units when Node 18 dropped (microsecond unit is missing, picosecond is not in list)
* @link https://tc39.es/ecma402/#table-sanctioned-single-unit-identifiers
* */
const makeNumberFormat = (fraction = 0) =>
/** @link https://tc39.es/ecma402/#table-sanctioned-single-unit-identifiers */
type TimeUnit =
| "nanosecond"
| "microsecond"
| "millisecond"
| "second"
| "minute";

const _makeNumberFormat = (unit: TimeUnit, fraction = 0) =>
Intl.NumberFormat(undefined, {
useGrouping: false,
minimumFractionDigits: 0,
maximumFractionDigits: fraction,
style: "unit",
unitDisplay: "long",
unit,
});
export const makeNumberFormat = memoizeWith(
(unit, fraction) => `${unit}${fraction}`,
_makeNumberFormat,
);

// creating them once increases the performance significantly
const intFormat = makeNumberFormat();
const floatFormat = makeNumberFormat(2);

// not using R.cond for performance optimization
const pickTimeUnit = (ms: number): [string, number, Intl.NumberFormat] => {
if (ms < 1e-6) return ["picosecond", ms / 1e-9, intFormat];
if (ms < 1e-3) return ["nanosecond", ms / 1e-6, intFormat];
if (ms < 1) return ["microsecond", ms / 1e-3, intFormat];
if (ms < 1e3) return ["millisecond", ms, intFormat];
if (ms < 6e4) return ["second", ms / 1e3, floatFormat];
return ["minute", ms / 6e4, floatFormat];
};

export const formatDuration = (durationMs: number) => {
const [unit, converted, formatter] = pickTimeUnit(durationMs);
return `${formatter.format(converted)} ${unit}${converted > 1 ? "s" : ""}`;
export const formatDuration = (ms: number) => {
if (ms < 1e-6) return makeNumberFormat("nanosecond", 3).format(ms / 1e-6);
if (ms < 1e-3) return makeNumberFormat("nanosecond").format(ms / 1e-6);
if (ms < 1) return makeNumberFormat("microsecond").format(ms / 1e-3);
if (ms < 1e3) return makeNumberFormat("millisecond").format(ms);
if (ms < 6e4) return makeNumberFormat("second", 2).format(ms / 1e3);
return makeNumberFormat("minute", 2).format(ms / 6e4);
};
27 changes: 21 additions & 6 deletions tests/bench/experiment.bench.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
import { bench } from "vitest";
import { BuiltinLogger } from "../../src";

const objects = [{ a: 1 }, { b: 2 }, { c: 3, a: 2 }];
describe("Experiment for builtin logger", () => {
const fixed = (a: string, b?: number) => `${a}${b}`;
const generic = (...args: unknown[]) => args.join();
const logger = new BuiltinLogger();

describe("Experiment for reducer", () => {
bench("current", () => {
objects.reduce((agg, obj) => ({ ...agg, ...obj }), {});
bench("fixed 2", () => {
fixed("second", 2);
});

bench("featured", () => {
objects.reduce((agg, obj) => Object.assign(agg, obj), {});
bench("fixed 1", () => {
fixed("second");
});

bench("generic 2", () => {
generic("second", 2);
});

bench("generic 1", () => {
generic("second");
});

bench(".child", () => {
logger.child({});
});
});
2 changes: 1 addition & 1 deletion tests/system/__snapshots__/system.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ exports[`App in production mode > Protocol > Should fail on invalid method 1`] =
exports[`App in production mode > Protocol > Should fail on malformed body 1`] = `
{
"error": {
"message": StringMatching /\\(Unexpected end of JSON input\\|Unterminated string in JSON at position 25\\)/,
"message": StringMatching /Unterminated string in JSON at position 25/,
},
"status": "error",
}
Expand Down
4 changes: 1 addition & 3 deletions tests/system/system.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,9 +329,7 @@ describe("App in production mode", async () => {
expect(json).toMatchSnapshot({
error: {
message: expect.stringMatching(
// @todo revisit when Node 18 dropped
// the 2nd option is for Node 20+
/(Unexpected end of JSON input|Unterminated string in JSON at position 25)/,
/Unterminated string in JSON at position 25/,
),
},
});
Expand Down
6 changes: 3 additions & 3 deletions tests/unit/__snapshots__/logger-helpers.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`Logger helpers > formatDuration() > 0 should format 1e-9 ms 1`] = `"1 picosecond"`;
exports[`Logger helpers > formatDuration() > 0 should format 1e-9 ms 1`] = `"0.001 nanoseconds"`;

exports[`Logger helpers > formatDuration() > 1 should format 1e-8 ms 1`] = `"10 picoseconds"`;
exports[`Logger helpers > formatDuration() > 1 should format 1e-8 ms 1`] = `"0.01 nanoseconds"`;

exports[`Logger helpers > formatDuration() > 2 should format 1e-7 ms 1`] = `"100 picoseconds"`;
exports[`Logger helpers > formatDuration() > 2 should format 1e-7 ms 1`] = `"0.1 nanoseconds"`;

exports[`Logger helpers > formatDuration() > 3 should format 0.000001 ms 1`] = `"1 nanosecond"`;

Expand Down
2 changes: 1 addition & 1 deletion tests/unit/builtin-logger.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ describe("BuiltinLogger", () => {
stop();
expect(logSpy).toHaveBeenCalledWith(
expect.stringMatching(
/2022-01-01T00:00:00.000Z debug: test '[\d.]+ (pico|micro|milli)?second(s)?'/,
/2022-01-01T00:00:00.000Z debug: test '[\d.]+ (nano|micro|milli)?second(s)?'/,
),
);
},
Expand Down
43 changes: 39 additions & 4 deletions tests/unit/logger-helpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { BuiltinLogger } from "../../src";
import { BuiltinLoggerConfig } from "../../src/builtin-logger";
import {
AbstractLogger,
formatDuration,
isLoggerInstance,
isSeverity,
isHidden,
makeNumberFormat,
formatDuration,
} from "../../src/logger-helpers";

describe("Logger helpers", () => {
Expand Down Expand Up @@ -78,12 +79,46 @@ describe("Logger helpers", () => {
});
});

describe.each([undefined, 0, 2])(
"makeNumberFormat() with %s fraction",
(fraction) => {
const defaultLocale = new Intl.NumberFormat().resolvedOptions().locale;
test.each([
"nanosecond",
"microsecond",
"millisecond",
"second",
"minute",
] as const)("should return Intl instance for %s unit", (unit) => {
const instance = makeNumberFormat(unit, fraction);
expect(instance).toBeInstanceOf(Intl.NumberFormat);
expect(instance.resolvedOptions()).toEqual({
unit,
maximumFractionDigits: fraction || 0,
locale: defaultLocale,
minimumFractionDigits: 0,
minimumIntegerDigits: 1,
notation: "standard",
numberingSystem: "latn",
roundingIncrement: 1,
roundingMode: "halfExpand",
roundingPriority: "auto",
signDisplay: "auto",
style: "unit",
trailingZeroDisplay: "auto",
unitDisplay: "long",
useGrouping: false,
});
});
},
);

describe("formatDuration()", () => {
test.each([
1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1, 1e1, 1e2, 1e3,
15e2, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
])("%# should format %s ms", (duration) =>
expect(formatDuration(duration)).toMatchSnapshot(),
);
])("%# should format %s ms", (duration) => {
expect(formatDuration(duration)).toMatchSnapshot();
});
});
});
2 changes: 1 addition & 1 deletion tsconfig.base.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"extends": "@tsconfig/node18/tsconfig.json",
"extends": "@tsconfig/node20/tsconfig.json",
"compilerOptions": {
"noImplicitAny": true,
"noImplicitOverride": true,
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -706,10 +706,10 @@
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"
integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==

"@tsconfig/node18@^18.2.1":
version "18.2.4"
resolved "https://registry.yarnpkg.com/@tsconfig/node18/-/node18-18.2.4.tgz#094efbdd70f697d37c09f34067bf41bc4a828ae3"
integrity sha512-5xxU8vVs9/FNcvm3gE07fPbn9tl6tqGGWA9tSlwsUEkBxtRnTsNmwrV8gasZ9F/EobaSv9+nu8AxUKccw77JpQ==
"@tsconfig/node20@^20.1.4":
version "20.1.4"
resolved "https://registry.yarnpkg.com/@tsconfig/node20/-/node20-20.1.4.tgz#3457d42eddf12d3bde3976186ab0cd22b85df928"
integrity sha512-sqgsT69YFeLWf5NtJ4Xq/xAF8p4ZQHlmGW74Nu2tD4+g5fAsposc4ZfaaPixVu4y01BEiDCWLRDCvDM5JOsRxg==

"@types/body-parser@*":
version "1.19.5"
Expand Down