@eliware/snowflake 12.0.0 → 13.0.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/AGENTS.md CHANGED
@@ -2,53 +2,33 @@
2
2
 
3
3
  ## Project
4
4
 
5
- Build a Snowflake ID library with Node.js 26. Use native ESM.
5
+ Build a Snowflake ID library with Node.js 26.
6
+ Use native ESM.
6
7
 
7
8
  ## Scope and boundaries
8
9
 
9
- Keep this package focused on Snowflake ID generation.
10
- Do not use validation to publish, deploy, or change external systems.
11
-
12
10
  ## Layout
13
11
 
14
- Keep implementation modules under `src/` and matching tests under `tests/`.
15
- Keep user guides under `docs/`, examples under `examples/`, and rules under `specs/`.
16
-
17
12
  ## Development
18
13
 
19
- Keep each module focused on one purpose.
20
- Keep TypeScript declarations beside their matching ESM modules.
21
-
22
14
  ## Validation
23
15
 
24
- Use Node.js 26 and native ESM.
25
- Run `npm test` for aggregate validation.
26
- Run `npm run lint` for lint checks.
27
- Run `npm run audit` for dependency checks.
28
- Run `npm run format` to format files.
29
- Run `npm run format:check` to check file format.
30
- Run `npm run typecheck` to check TypeScript declarations.
31
- Run `npm run pack` to check package contents.
16
+ Run `npm test`.
17
+ Run `npm run lint`.
18
+ Run `npm run audit`.
19
+ Run `npm run format`.
20
+ Run `npm run format:check`.
32
21
 
33
22
  ## Security
34
23
 
35
- Protect credentials, tokens, secrets, private data, and machine-specific values.
36
- Treat Snowflake IDs as identifiers, not secrets.
37
-
38
24
  ## Changes
39
25
 
40
- Keep docs current and direct.
41
- Follow the Eliware Operations approval process for release changes.
42
- Do not treat validation as release approval.
43
-
44
26
  ## Library
45
27
 
46
- Export the public generator API from `src/index.mjs`.
47
- Return each generated ID as a decimal string.
48
- Keep declarations in `src/` beside their matching ESM modules.
28
+ Run focused tests first during development.
29
+ Run one test file with `npm test -- tests/path/to/example.test.mjs`.
30
+ Run multiple test files with `npm test -- tests/path/to/first.test.mjs tests/path/to/second.test.mjs`.
31
+ Prevent repeated full-suite runs during development.
32
+ Run the full `npm test` suite for final validation before a commit, milestone, or release.
49
33
 
50
34
  ## npm publication
51
-
52
- Publish the `@eliware/snowflake` package only after release approval.
53
- Use `package.json.version` as the package version.
54
- Run package validation before publication review.
package/README.md CHANGED
@@ -23,19 +23,18 @@ A Node.js Snowflake ID generator with BigInt support and a configurable epoch.
23
23
 
24
24
  ## Features
25
25
 
26
- Create time-ordered IDs with a custom epoch and unique worker and process IDs.
27
- The IDs use the Discord Snowflake bit layout.
28
- Receive decimal strings that preserve every ID bit in JavaScript and JSON.
29
- The generator handles clock rollback and reports sequence overflow.
26
+ Create time-ordered Snowflake IDs with a custom epoch and worker and process IDs.
27
+ Return decimal strings so JavaScript and JSON preserve each ID exactly.
28
+ The generator keeps IDs ordered when time decreases and reports sequence overflow.
30
29
 
31
30
  ## Requirements
32
31
 
33
32
  Use Node.js 26 or later.
34
- Assign a unique worker and process ID pair to each active generator.
33
+ Use one package instance in each Node.js process.
35
34
 
36
35
  ## Setup
37
36
 
38
- Install the package:
37
+ Install the package from npm:
39
38
 
40
39
  ```text
41
40
  npm install @eliware/snowflake
@@ -43,7 +42,7 @@ npm install @eliware/snowflake
43
42
 
44
43
  ## Usage
45
44
 
46
- Create a generator with a unique identity:
45
+ Import the generator factory and set a unique worker and process ID pair:
47
46
 
48
47
  ```js
49
48
  import { createSnowflakeGenerator } from "@eliware/snowflake";
@@ -52,43 +51,49 @@ const generate = createSnowflakeGenerator({ workerId: 3, processId: 7 });
52
51
  console.log(generate());
53
52
  ```
54
53
 
55
- See [release notes](RELEASE_NOTES.md) for changes by version.
56
- See the [usage guide](docs/usage.md) for options, ID fields, and limits.
54
+ Each call returns one decimal string.
55
+
56
+ [Release notes](RELEASE_NOTES.md)
57
+
58
+ [Specifications](specs/README.md)
57
59
 
58
60
  ## Development
59
61
 
60
- Read [user documentation](docs/README.md), [examples](examples/README.md), and [specifications](specs/README.md).
62
+ [Documentation](docs/README.md)
63
+ See the [usage guide](docs/usage.md) for generator options and ID fields.
61
64
 
62
65
  ## Testing
63
66
 
64
- Run `npm test` for all required checks.
65
- Run `npm run lint`, `npm run format:check`, `npm run audit`, `npm run typecheck`, or `npm run pack` for focused checks.
67
+ Run `npm test` from a repository clone to run aggregate validation.
66
68
 
67
69
  ## Troubleshooting
68
70
 
69
- See [troubleshooting](docs/troubleshooting.md) for error causes and recovery steps.
71
+ Check the options and clock when the generator throws `RangeError`.
72
+ For sequence overflow, retry when the clock returns a timestamp greater than the last timestamp.
73
+ Read the [troubleshooting guide](docs/troubleshooting.md) for error causes and actions.
70
74
 
71
75
  ## Security
72
76
 
73
- Treat IDs as identifiers, not secrets.
74
- IDs reveal timestamp and deployment data.
75
- Do not use IDs as authentication tokens.
77
+ Use IDs as identifiers. Each ID contains its generation time and worker and process values.
78
+ Do not use IDs as secrets or authentication tokens.
79
+ Read the [usage guide](docs/usage.md) for ID fields and storage guidance.
76
80
 
77
81
  ## API
78
82
 
79
- `createSnowflakeGenerator(options)` returns a function that creates decimal-string IDs.
80
- Options set the epoch, worker ID, process ID, and clock.
81
- The default generator is `generate`, `snowflake`, and the default export.
82
- The `constants` export provides the default epoch and bit sizes.
83
+ Import `createSnowflakeGenerator` to create a generator with custom options.
84
+ Use `generate`, `snowflake`, or the default export for the default generator.
85
+ Read the [usage guide](docs/usage.md) for the API and option details.
83
86
 
84
87
  ## Packaging
85
88
 
86
- The package provides native ESM code and TypeScript declarations.
87
- It has no runtime dependencies.
89
+ The package uses native ESM and includes TypeScript declarations.
90
+ The package has no runtime dependencies.
91
+ Install it from [npm](https://www.npmjs.com/package/@eliware/snowflake).
88
92
 
89
93
  ## Examples
90
94
 
91
- Run the [native ESM example](examples/example.mjs) with Node.js 26 or later.
95
+ [Run the native ESM example](examples/example.mjs).
96
+ [Examples](examples/README.md)
92
97
 
93
98
  ## Support
94
99
 
package/RELEASE_NOTES.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Release Notes
2
2
 
3
+ ## 13.0.0 — 2026-10-19
4
+
5
+ ### Added
6
+
7
+ - Added the canonical npm publication workflow.
8
+ - Added full Git history to CI.
9
+
10
+ ### Changed
11
+
12
+ - Updated the package to version 13.0.0 and upgraded `@eliware/test` and npm dependencies.
13
+ - Aligned CI and npm publication workflows with the current templates and checkout action.
14
+ - Updated Knit deployment commands and the repository path.
15
+ - Updated specifications and repository guidance for Convention 13.
16
+ - Expanded user guides with API details, ID limits, timestamp decoding, and storage guidance.
17
+ - Documented the custom epoch for Discord timestamp decoding. It does not create official Discord IDs.
18
+ - Changed the invalid clock source and sequence overflow error messages.
19
+ - Reduced the root README and contributor guidance to convention-required content.
20
+
3
21
  ## 12.0.0 — 2026-10-12
4
22
 
5
23
  ### Changed
@@ -32,34 +50,3 @@
32
50
  ### Breaking changes
33
51
 
34
52
  - Changed the `test` and `lint` scripts to use `@eliware/test`.
35
-
36
- ## 1.1.5 — 2026-08-07
37
-
38
- ### Changed
39
-
40
- - Aligned the repository layout, scripts, documentation, and package contents.
41
- - Added TypeScript declaration checks and package validation.
42
- - Moved tests under `tests/` and added a runnable native ESM example.
43
- - Removed the legacy CommonJS package entry point.
44
-
45
- ## 1.1.4 — 2026-08-07
46
-
47
- ### Changed
48
-
49
- - Removed the unused `@eliware/common` runtime dependency.
50
- - Added manual GitHub Actions workflow dispatch support.
51
-
52
- ## 1.1.2 — 2026-08-06
53
-
54
- ### Added
55
-
56
- - Added a configurable BigInt-safe Snowflake ID generator.
57
- - Added the Eliware epoch, worker IDs, and process IDs.
58
- - Added clock rollback protection and sequence overflow handling.
59
- - Added injectable clocks, range checks, and timestamp capacity checks.
60
- - Added TypeScript declarations for public options and exports.
61
-
62
- ### Changed
63
-
64
- - Removed CommonJS entry points and tests. The package now uses native ESM.
65
- - Expanded deployment and bit layout guidance.
@@ -2,44 +2,44 @@
2
2
 
3
3
  ## Error reference
4
4
 
5
- | Error | Cause | Action |
6
- | --------------------------------------------- | ---------------------------------------------------- | --------------------------------------------------------------- |
7
- | `RangeError` for `workerId` or `processId` | The value is outside `0` through `31`. | Set an integer from `0` through `31`. |
8
- | `TypeError` for `now` | The option is not a function. | Pass a function that returns Unix time in milliseconds. |
9
- | `RangeError` for `epoch` | The epoch is outside the supported range. | Set the epoch from zero through the timestamp limit. |
10
- | `RangeError` for invalid clock number | The clock returned a fractional or unsafe number. | Return a safe integer number or a bigint. |
11
- | `RangeError` for time before epoch | The clock value is lower than `epoch`. | Set the correct epoch or fix the clock source. |
12
- | `RangeError` for timestamp capacity | Elapsed time is greater than the 42-bit limit. | Use a valid epoch for the required date range. |
13
- | `RangeError` for sequence overflow | The generator created 4096 IDs at one timestamp. | Wait until the clock moves past the last timestamp, then retry. |
14
- | `TypeError` or `RangeError` during conversion | An option or clock value cannot convert to `BigInt`. | Pass an integer number or bigint value. |
5
+ | Error | Cause | Action |
6
+ | --------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------- |
7
+ | `RangeError` for `workerId` or `processId` | The value is outside `0` through `31`. | Set an integer from `0` through `31`. |
8
+ | `TypeError` for `now` | The option is not a function. | Pass a function that returns Unix time in milliseconds. |
9
+ | `RangeError` for `epoch` | The epoch is outside the supported range. | Set the epoch from zero through the timestamp limit. |
10
+ | `RangeError` for invalid clock number | The clock returned a fractional or unsafe number. | Return a safe integer number or a bigint. |
11
+ | `RangeError` for time before epoch | The clock value is lower than `epoch`. | Set the correct epoch or set the clock source. |
12
+ | `RangeError` for timestamp capacity | Elapsed time is greater than the 42-bit limit. | Use a valid epoch for the required date range. |
13
+ | `RangeError` for sequence overflow | The generator created 4096 IDs at one timestamp. | Retry when the clock returns a greater timestamp. |
14
+ | `TypeError` or `RangeError` during conversion | An option or clock value cannot convert to `BigInt`. | Pass an integer number or bigint value. |
15
15
 
16
16
  ## Sequence overflow
17
17
 
18
18
  The generator does not wait for the clock.
19
19
  It fails when one timestamp reaches the 4096-ID limit.
20
- Wait until the clock moves past the last timestamp before you retry.
20
+ Retry when the clock returns a timestamp greater than the last timestamp.
21
21
  If the clock stalls, check the system clock or the `now` function.
22
22
 
23
23
  ## Duplicate IDs
24
24
 
25
- Assign a unique worker and process ID pair to each active generator.
25
+ Give each active generator a unique worker and process ID pair.
26
26
  Use the same epoch for each generator in one application.
27
27
  The default worker ID is `0`.
28
28
  The default process ID is `process.pid % 32` and can repeat across hosts.
29
- Set both IDs through deployment configuration when several hosts create IDs.
29
+ Set both IDs in deployment configuration when some hosts create IDs.
30
30
 
31
31
  ## Invalid timestamps
32
32
 
33
33
  The clock must return a safe integer number or a bigint.
34
34
  The value must be Unix time in milliseconds.
35
35
  The timestamp must be at or after the configured epoch.
36
- The elapsed time must fit in 42 bits.
36
+ The elapsed time must be 42 bits or less.
37
37
  Check the clock and epoch values when timestamp validation fails.
38
38
 
39
39
  ## Large ID values
40
40
 
41
- IDs are decimal strings because JavaScript numbers cannot represent every ID exactly.
42
- Keep the string in JSON and in database fields that preserve exact decimal values.
41
+ IDs are decimal strings because JavaScript numbers cannot store every ID exactly.
42
+ Keep the string in JSON and in database fields that store exact decimal values.
43
43
  Use `BigInt(id)` for exact integer operations.
44
44
 
45
45
  ## Node.js version
package/docs/usage.md CHANGED
@@ -25,7 +25,7 @@ const id = generate();
25
25
  console.log(id);
26
26
  ```
27
27
 
28
- The program prints one decimal string. The value changes on each call.
28
+ The command prints one decimal string. The value changes on each call.
29
29
 
30
30
  ## Set generator options
31
31
 
@@ -38,20 +38,36 @@ Pass an options object to `createSnowflakeGenerator`.
38
38
  | `processId` | number or bigint | `process.pid % 32` | Process value from `0` through `31`. |
39
39
  | `now` | function | `Date.now` | Function that returns Unix time in milliseconds. |
40
40
 
41
- Use the same epoch for all generators that create IDs for one application.
42
- Set the epoch from zero through the timestamp limit.
43
- Pass it as a safe integer number or a bigint.
44
- Set `epoch` to `1420070400000n` to decode timestamps with Discord's epoch.
45
- Assign a unique worker and process ID pair to each active generator.
46
- Set `processId` when several hosts or processes share worker IDs.
41
+ `createSnowflakeGenerator(options)` returns a function that creates IDs.
42
+ Each call returns one decimal string.
43
+ Set `epoch` as a safe integer number or a bigint from zero through `(1n << 42n) - 1n`.
44
+ Set `workerId` and `processId` to integer values from `0` through `31`.
45
+ Use the same epoch for all generators in one application.
46
+
47
+ Give each active generator a unique pair of `workerId` and `processId` values.
48
+ Keep each pair unique while IDs from that pair remain in use.
49
+ Set `processId` when some hosts or processes use the same `workerId`.
47
50
  The default process ID can repeat across hosts.
48
51
 
52
+ To align timestamp decoding with Discord, set `epoch` to `1420070400000n`.
53
+ This epoch matches Discord's timestamp epoch.
54
+ It does not create an official Discord resource ID.
55
+
56
+ ```js
57
+ const createDiscordTimeGenerator = createSnowflakeGenerator({
58
+ epoch: 1420070400000n,
59
+ workerId: 3,
60
+ processId: 7,
61
+ });
62
+ ```
63
+
49
64
  ## Understand the ID fields
50
65
 
51
66
  Each ID has a 42-bit timestamp, a 5-bit worker ID, a 5-bit process ID, and a 12-bit sequence.
52
67
  The timestamp stores milliseconds since the configured epoch.
53
- The sequence starts at zero when the timestamp advances.
68
+ The sequence starts at zero when the timestamp changes.
54
69
  The default epoch is `365662380000n`, or `1981-08-03T04:53:00Z`.
70
+ The timestamp field sorts before the worker, process, and sequence fields.
55
71
 
56
72
  The generator combines the fields as follows:
57
73
 
@@ -60,14 +76,34 @@ The generator combines the fields as follows:
60
76
  ```
61
77
 
62
78
  The maximum elapsed timestamp is `(1n << 42n) - 1n` milliseconds.
63
- IDs can exceed JavaScript's safe integer range.
79
+ IDs can be greater than JavaScript's safe integer limit.
64
80
 
65
81
  ## Use the default generator
66
82
 
67
83
  Use `generate`, `snowflake`, or the default export to create IDs.
68
84
  These exports refer to the same generator instance.
69
85
  The default generator uses worker ID `0` and process ID `process.pid % 32`.
70
- Create a custom generator when you need a different identity, epoch, or clock.
86
+ Create a custom generator to set a different identity, epoch, or clock.
87
+
88
+ The `constants` export has `epoch`, `sequenceBits`, `processBits`, and `workerBits` fields.
89
+ Each field has a `bigint` value.
90
+ The sequence, process, and worker fields use 12, 5, and 5 bits.
91
+ The `constants` object is frozen.
92
+
93
+ ## Decode an ID timestamp
94
+
95
+ Convert the ID string to a `bigint` before you read its timestamp field.
96
+ Move the value right by 22 bits to read elapsed milliseconds.
97
+ Add the configured epoch to get Unix time in milliseconds.
98
+ Convert that value to a JavaScript `Date` to get its ISO 8601 value.
99
+
100
+ ```js
101
+ const epoch = 365662380000n;
102
+ const timestamp = new Date(Number((BigInt(id) >> 22n) + epoch));
103
+ console.log(timestamp.toISOString());
104
+ ```
105
+
106
+ Use the same epoch that the generator used to create the ID.
71
107
 
72
108
  ## Use a custom clock
73
109
 
@@ -76,29 +112,33 @@ Return a safe integer number or a bigint from that function.
76
112
  The value must be Unix time in milliseconds.
77
113
  Use this option for deterministic tests or a controlled clock source.
78
114
 
79
- ## Handle time and sequence limits
115
+ ## Time and sequence limits
116
+
117
+ The generator uses the last timestamp when the clock returns a lower value.
118
+ The generator sets the sequence to zero when the timestamp changes.
119
+ The generator increments the sequence while the timestamp stays the same.
80
120
 
81
- The generator uses the last timestamp when the clock moves backward.
82
- It increments the sequence while that timestamp remains active.
83
- Each generator can create up to 4096 IDs for one millisecond.
84
- The next call throws `RangeError` until the clock advances past the last timestamp.
85
- Wait for the clock to advance before retry.
86
- The generator throws `RangeError` for a timestamp before the epoch or beyond its 42-bit limit.
121
+ Each generator can create 4096 IDs in one millisecond.
122
+ The generator throws `RangeError` after that limit.
123
+ Retry when the clock returns a timestamp greater than the last timestamp.
124
+ The generator throws `RangeError` for a timestamp before the epoch or greater than its 42-bit limit.
87
125
 
88
- ## Store and transfer IDs
126
+ ## Store and share IDs
89
127
 
90
128
  Keep each ID as a decimal string in JavaScript and JSON.
91
129
  Do not convert an ID to `number`.
130
+ JSON cannot encode a `bigint` value without custom code.
92
131
  Use a text field or an unsigned 64-bit field in a database.
93
- Use `BigInt(id)` when you need exact integer math in JavaScript.
132
+ Use `BigInt(id)` for exact integer math in JavaScript.
94
133
 
95
134
  ## Run the example
96
135
 
97
- Use Node.js 26 or later. Run this command from the repository root:
136
+ Use Node.js 26 or later.
137
+ Run this command from the repository root:
98
138
 
99
139
  ```text
100
140
  node examples/example.mjs
101
141
  ```
102
142
 
103
143
  The command prints two decimal strings. The values depend on the current time and process ID.
104
- You do not need cleanup.
144
+ The example writes no files.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eliware/snowflake",
3
- "version": "12.0.0",
3
+ "version": "13.0.0",
4
4
  "type": "module",
5
5
  "description": "A Node.js Snowflake ID generator with BigInt support and a configurable epoch.",
6
6
  "keywords": [
@@ -42,7 +42,7 @@
42
42
  "node": ">=26"
43
43
  },
44
44
  "devDependencies": {
45
- "@eliware/test": "^12.0.0",
45
+ "@eliware/test": "^13.0.0",
46
46
  "typescript": "^7.0.2"
47
47
  },
48
48
  "jest": {
@@ -1,25 +1,23 @@
1
- version: "12.0"
1
+ version: "13.0"
2
2
  description: Defines behavior and limits for the Snowflake ID generator.
3
- requires: []
4
- conflicts: []
5
3
  directives:
6
4
  - id: E-106.0
7
5
  dos:
8
- - Generate time-ordered Snowflake IDs.
6
+ - Create time-ordered Snowflake IDs.
9
7
  - Return each ID as a decimal string.
10
8
  - Use configurable epoch and identity values.
11
9
  donts:
12
- - Do not present generated IDs as secrets.
10
+ - Do not show generated IDs as secrets.
13
11
  children:
14
12
  - id: E-106.0.0
15
13
  dos:
16
14
  - Validate worker and process IDs before generation.
17
15
  - Reject timestamps before the configured epoch.
18
16
  donts:
19
- - Do not permit identity values outside the supported bit range.
17
+ - Do not use identity values outside the supported bit range.
20
18
  - id: E-106.0.1
21
19
  dos:
22
- - Protect generation from clock rollback.
20
+ - Keep IDs unique when the clock returns a lower value.
23
21
  - Report sequence overflow before an ID can repeat.
24
22
  donts:
25
23
  - Do not return an ID when the sequence overflows.
@@ -1,7 +1,5 @@
1
- version: "12.0"
1
+ version: "13.0"
2
2
  description: Defines work outside the Snowflake ID library scope.
3
- requires: []
4
- conflicts: []
5
3
  directives:
6
4
  - id: E-106.2
7
5
  dos:
@@ -1,13 +1,11 @@
1
- version: "12.0"
1
+ version: "13.0"
2
2
  description: Defines repository requirements for the Snowflake ID library.
3
- requires: []
4
- conflicts: []
5
3
  directives:
6
4
  - id: E-106.1
7
5
  dos:
8
6
  - Preserve the documented Snowflake ID behavior.
9
7
  - Return IDs as decimal strings.
10
- - Protect credentials and private data.
11
- - Pass the repository validation commands before review.
8
+ - Keep credentials and private data private.
9
+ - Pass repository validation before inspection.
12
10
  donts:
13
11
  - Do not use IDs as secrets or authentication tokens.
@@ -15,7 +15,7 @@ export function validateGeneratorOptions({
15
15
  if (baseEpoch < 0n || baseEpoch > MAX_TIMESTAMP)
16
16
  throw new RangeError("epoch must be between 0 and the timestamp limit");
17
17
  const identity = validateIdentity(workerId, processId);
18
- if (typeof now !== "function") throw new TypeError("now must be a function");
18
+ if (typeof now !== "function") throw new TypeError("clock source must be a function");
19
19
 
20
20
  return { baseEpoch, ...identity, now };
21
21
  }
package/src/generator.mjs CHANGED
@@ -28,7 +28,9 @@ export function createGenerator(options = {}) {
28
28
  let nextSequence;
29
29
  if (timestamp === lastTimestamp) {
30
30
  if (sequence === SEQUENCE_MASK)
31
- throw new RangeError("sequence overflow; clock must advance before retrying");
31
+ throw new RangeError(
32
+ "sequence overflow. Retry when the clock returns a greater timestamp.",
33
+ );
32
34
  nextSequence = sequence + 1n;
33
35
  } else {
34
36
  nextSequence = 0n;