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 17 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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Changelog

## Version 21

### v21.0.0

- **Breaking changes**:
- Supported Node versions: ^20.9.0, ^22.0.0
- `BuiltinLogger::profile()` behavior changed for picoseconds: expressing them through nanoseconds.

## Version 20

### v20.14.2
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,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 @@ -116,7 +116,7 @@
"devDependencies": {
"@arethetypeswrong/cli": "^0.16.2",
"@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
19 changes: 17 additions & 2 deletions src/builtin-logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { performance } from "node:perf_hooks";
import type { FlatObject } from "./common-helpers";
import {
AbstractLogger,
formatDuration,
isHidden,
makeNumberFormat,
Severity,
} from "./logger-helpers";

Expand Down Expand Up @@ -49,6 +49,12 @@ interface ProfilerOptions {
/** @desc Built-in console logger with optional colorful inspections */
export class BuiltinLogger implements AbstractLogger {
protected hasColor: boolean;
protected picoFormat = makeNumberFormat("nanosecond", 3);
protected nanoFormat = makeNumberFormat("nanosecond");
protected microFormat = makeNumberFormat("microsecond");
protected milliFormat = makeNumberFormat("millisecond");
protected secondsFormat = makeNumberFormat("second", 2);
protected minutesFormat = makeNumberFormat("minute", 2);
protected readonly styles: Record<Severity, Ansis> = {
debug: blue,
info: green,
Expand Down Expand Up @@ -114,6 +120,15 @@ export class BuiltinLogger implements AbstractLogger {
return new BuiltinLogger({ ...this.config, ctx });
}

protected formatDuration = (ms: number) => {
if (ms < 1e-6) return this.picoFormat.format(ms / 1e-6);
if (ms < 1e-3) return this.nanoFormat.format(ms / 1e-6);
if (ms < 1) return this.microFormat.format(ms / 1e-3);
if (ms < 1e3) return this.milliFormat.format(ms);
if (ms < 6e4) return this.secondsFormat.format(ms / 1e3);
return this.minutesFormat.format(ms / 6e4);
};

/** @desc Measures the duration until you invoke the returned callback */
public profile(message: string): () => void;
public profile(options: ProfilerOptions): () => void;
Expand All @@ -124,7 +139,7 @@ export class BuiltinLogger implements AbstractLogger {
const {
message,
severity = "debug",
formatter = formatDuration,
formatter = this.formatDuration.bind(this),
} = typeof subject === "object" ? subject : { message: subject };
this.print(
typeof severity === "function" ? severity(duration) : severity,
Expand Down
36 changes: 12 additions & 24 deletions src/logger-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,32 +34,20 @@ 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";

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

// 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" : ""}`;
};
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 @@ -27,7 +27,7 @@ exports[`App > Protocol > Should fail on invalid method 1`] = `
exports[`App > 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 @@ -357,9 +357,7 @@ describe("App", 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
40 changes: 40 additions & 0 deletions tests/unit/__snapshots__/builtin-logger.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,43 @@ exports[`BuiltinLogger > constructor() > Should handle non-object meta 1 1`] = `
],
]
`;

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

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

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

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

exports[`BuiltinLogger > formatDuration() > 4 should format 0.00001 ms 1`] = `"10 nanoseconds"`;

exports[`BuiltinLogger > formatDuration() > 5 should format 0.0001 ms 1`] = `"100 nanoseconds"`;

exports[`BuiltinLogger > formatDuration() > 6 should format 0.001 ms 1`] = `"1 microsecond"`;

exports[`BuiltinLogger > formatDuration() > 7 should format 0.01 ms 1`] = `"10 microseconds"`;

exports[`BuiltinLogger > formatDuration() > 8 should format 0.1 ms 1`] = `"100 microseconds"`;

exports[`BuiltinLogger > formatDuration() > 9 should format 1 ms 1`] = `"1 millisecond"`;

exports[`BuiltinLogger > formatDuration() > 10 should format 10 ms 1`] = `"10 milliseconds"`;

exports[`BuiltinLogger > formatDuration() > 11 should format 100 ms 1`] = `"100 milliseconds"`;

exports[`BuiltinLogger > formatDuration() > 12 should format 1000 ms 1`] = `"1 second"`;

exports[`BuiltinLogger > formatDuration() > 13 should format 1500 ms 1`] = `"1.5 seconds"`;

exports[`BuiltinLogger > formatDuration() > 14 should format 10000 ms 1`] = `"10 seconds"`;

exports[`BuiltinLogger > formatDuration() > 15 should format 100000 ms 1`] = `"1.67 minutes"`;

exports[`BuiltinLogger > formatDuration() > 16 should format 1000000 ms 1`] = `"16.67 minutes"`;

exports[`BuiltinLogger > formatDuration() > 17 should format 10000000 ms 1`] = `"166.67 minutes"`;

exports[`BuiltinLogger > formatDuration() > 18 should format 100000000 ms 1`] = `"1666.67 minutes"`;

exports[`BuiltinLogger > formatDuration() > 19 should format 1000000000 ms 1`] = `"16666.67 minutes"`;
41 changes: 0 additions & 41 deletions tests/unit/__snapshots__/logger-helpers.spec.ts.snap

This file was deleted.

12 changes: 11 additions & 1 deletion tests/unit/builtin-logger.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,16 @@ describe("BuiltinLogger", () => {
});
});

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) => {
const { logger } = makeLogger({ level: "debug", color: false });
expect(logger["formatDuration"](duration)).toMatchSnapshot();
});
});

describe("profile()", () => {
test.each([1e-3, 1e-2, 1e-1, 1, 1e1, 1e2, 1e3])(
"should measure %s ms",
Expand All @@ -117,7 +127,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.]+ (micro|milli)?second(s)?'/,
RobinTail marked this conversation as resolved.
Show resolved Hide resolved
),
);
},
Expand Down
43 changes: 34 additions & 9 deletions tests/unit/logger-helpers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import { BuiltinLogger } from "../../src";
import { BuiltinLoggerConfig } from "../../src/builtin-logger";
import {
AbstractLogger,
formatDuration,
isLoggerInstance,
isSeverity,
isHidden,
makeNumberFormat,
} from "../../src/logger-helpers";

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

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(),
);
});
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,
});
});
},
);
});
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 @@ -679,10 +679,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