@clickhouse/client 1.23.0-head.dbc2960.1 → 1.23.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.
Files changed (79) hide show
  1. package/CHANGELOG.md +1342 -0
  2. package/README.md +18 -6
  3. package/dist/version.d.ts +1 -1
  4. package/dist/version.js +1 -1
  5. package/dist/version.js.map +1 -1
  6. package/package.json +7 -6
  7. package/skills/AGENTS.md +8 -0
  8. package/skills/clickhouse-js-node-rowbinary/AGENTS.md +44 -0
  9. package/skills/clickhouse-js-node-rowbinary/CHANGELOG.md +49 -0
  10. package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/README.md +78 -14
  11. package/skills/clickhouse-js-node-rowbinary/SKILL.md +111 -0
  12. package/skills/{clickhouse-js-node-rowbinary-parser/SKILL.md → clickhouse-js-node-rowbinary/reader.md} +51 -131
  13. package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/carts.ts +9 -5
  14. package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/events.ts +5 -5
  15. package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/iot.ts +4 -4
  16. package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/ledger.ts +3 -3
  17. package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/logs.ts +4 -4
  18. package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/observability.ts +9 -10
  19. package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/orders.ts +10 -9
  20. package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/profiles.ts +5 -5
  21. package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/src/examples/telemetry.ts +6 -6
  22. package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/compile.ts +18 -8
  23. package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/dynamic.ts +12 -8
  24. package/skills/clickhouse-js-node-rowbinary/src/readers/enums.ts +40 -0
  25. package/skills/clickhouse-js-node-rowbinary/src/writers/aggregateFunction.ts +18 -0
  26. package/skills/clickhouse-js-node-rowbinary/src/writers/bool.ts +10 -0
  27. package/skills/clickhouse-js-node-rowbinary/src/writers/composite.ts +140 -0
  28. package/skills/clickhouse-js-node-rowbinary/src/writers/core.ts +92 -0
  29. package/skills/clickhouse-js-node-rowbinary/src/writers/datetime.ts +123 -0
  30. package/skills/clickhouse-js-node-rowbinary/src/writers/decimals.ts +51 -0
  31. package/skills/clickhouse-js-node-rowbinary/src/writers/enums.ts +18 -0
  32. package/skills/clickhouse-js-node-rowbinary/src/writers/floats.ts +40 -0
  33. package/skills/clickhouse-js-node-rowbinary/src/writers/geo.ts +125 -0
  34. package/skills/clickhouse-js-node-rowbinary/src/writers/integers.ts +90 -0
  35. package/skills/clickhouse-js-node-rowbinary/src/writers/interval.ts +11 -0
  36. package/skills/clickhouse-js-node-rowbinary/src/writers/ip.ts +121 -0
  37. package/skills/clickhouse-js-node-rowbinary/src/writers/lowCardinality.ts +12 -0
  38. package/skills/clickhouse-js-node-rowbinary/src/writers/nested.ts +17 -0
  39. package/skills/clickhouse-js-node-rowbinary/src/writers/nothing.ts +21 -0
  40. package/skills/clickhouse-js-node-rowbinary/src/writers/rows.ts +144 -0
  41. package/skills/clickhouse-js-node-rowbinary/src/writers/simpleAggregateFunction.ts +12 -0
  42. package/skills/clickhouse-js-node-rowbinary/src/writers/strings.ts +77 -0
  43. package/skills/clickhouse-js-node-rowbinary/src/writers/time.ts +54 -0
  44. package/skills/clickhouse-js-node-rowbinary/src/writers/uuid.ts +60 -0
  45. package/skills/clickhouse-js-node-rowbinary/src/writers/varint.ts +64 -0
  46. package/skills/clickhouse-js-node-rowbinary/src/writers/writer.ts +101 -0
  47. package/skills/clickhouse-js-node-rowbinary/writer.md +96 -0
  48. package/skills/clickhouse-js-node-rowbinary-parser/src/enums.ts +0 -28
  49. /package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/EXAMPLES.md +0 -0
  50. /package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/case-studies/iot-rowbinary-vs-json.md +0 -0
  51. /package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/case-studies/ledger-rowbinary-vs-json.md +0 -0
  52. /package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/case-studies/logs-json-wins.md +0 -0
  53. /package/skills/{clickhouse-js-node-rowbinary-parser → clickhouse-js-node-rowbinary}/case-studies/wasm-vs-js.md +0 -0
  54. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/aggregateFunction.ts +0 -0
  55. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/bool.ts +0 -0
  56. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/columnar.ts +0 -0
  57. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/composite.ts +0 -0
  58. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/core.ts +0 -0
  59. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/datetime.ts +0 -0
  60. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/decimals.ts +0 -0
  61. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/floats.ts +0 -0
  62. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/geo.ts +0 -0
  63. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/header.ts +0 -0
  64. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/integers.ts +0 -0
  65. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/interval.ts +0 -0
  66. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/ip.ts +0 -0
  67. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/json.ts +0 -0
  68. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/lowCardinality.ts +0 -0
  69. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/nested.ts +0 -0
  70. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/nothing.ts +0 -0
  71. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/reader.ts +0 -0
  72. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/rowBinaryWithNamesAndTypes.ts +0 -0
  73. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/rows.ts +0 -0
  74. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/simpleAggregateFunction.ts +0 -0
  75. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/stream.ts +0 -0
  76. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/strings.ts +0 -0
  77. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/time.ts +0 -0
  78. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/uuid.ts +0 -0
  79. /package/skills/{clickhouse-js-node-rowbinary-parser/src → clickhouse-js-node-rowbinary/src/readers}/varint.ts +0 -0
package/README.md CHANGED
@@ -63,12 +63,12 @@ npm i @clickhouse/client-web
63
63
 
64
64
  Node.js must be available in the environment to run the Node.js client. The client is compatible with all the [maintained](https://github.com/nodejs/release#readme) Node.js releases.
65
65
 
66
- | Node.js version | Supported? |
67
- | --------------- | ----------- |
68
- | 24.x | ✔ |
69
- | 22.x | ✔ |
70
- | 20.x | ✔ |
71
- | 18.x | Best effort |
66
+ | Node.js version | Supported? |
67
+ | --------------- | ---------- |
68
+ | 26.x | ✔ |
69
+ | 24.x | ✔ |
70
+ | 22.x | ✔ |
71
+ | 20.x | |
72
72
 
73
73
  ### TypeScript
74
74
 
@@ -110,6 +110,18 @@ See more examples in the [examples directory](./examples).
110
110
 
111
111
  See the [ClickHouse website](https://clickhouse.com/docs/integrations/javascript) for the full documentation.
112
112
 
113
+ ## Changelog
114
+
115
+ Each package keeps its own changelog:
116
+
117
+ - `@clickhouse/client` — [`packages/client-node/CHANGELOG.md`](./packages/client-node/CHANGELOG.md)
118
+ - `@clickhouse/client-web` — [`packages/client-web/CHANGELOG.md`](./packages/client-web/CHANGELOG.md)
119
+ - `@clickhouse/client-common` (deprecated) — [`packages/client-common/CHANGELOG.md`](./packages/client-common/CHANGELOG.md)
120
+ - `@clickhouse/datatype-parser` — [`packages/datatype-parser/CHANGELOG.md`](./packages/datatype-parser/CHANGELOG.md)
121
+ - `@clickhouse/rowbinary` — [`skills/clickhouse-js-node-rowbinary/CHANGELOG.md`](./skills/clickhouse-js-node-rowbinary/CHANGELOG.md)
122
+
123
+ History through `@clickhouse/client` 1.23.0 lives in the now-frozen repository-wide [`CHANGELOG.md`](./CHANGELOG.md).
124
+
113
125
  ## AI Agent Skills
114
126
 
115
127
  This repository contains agent skills for working with the client:
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- declare const _default: "1.23.0-head.dbc2960.1";
1
+ declare const _default: "1.23.0";
2
2
  export default _default;
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.default = "1.23.0-head.dbc2960.1";
3
+ exports.default = "1.23.0";
4
4
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":";;AAAA,kBAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":";;AAAA,kBAAe,QAAQ,CAAC"}
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@clickhouse/client",
3
3
  "description": "Official JS client for ClickHouse DB - Node.js implementation",
4
4
  "homepage": "https://clickhouse.com",
5
- "version": "1.23.0-head.dbc2960.1",
5
+ "version": "1.23.0",
6
6
  "license": "Apache-2.0",
7
7
  "keywords": [
8
8
  "clickhouse",
@@ -15,13 +15,14 @@
15
15
  },
16
16
  "private": false,
17
17
  "engines": {
18
- "node": ">=16"
18
+ "node": ">=20"
19
19
  },
20
20
  "main": "dist/index.js",
21
21
  "types": "dist/index.d.ts",
22
22
  "files": [
23
23
  "dist",
24
- "skills"
24
+ "skills",
25
+ "CHANGELOG.md"
25
26
  ],
26
27
  "agents": {
27
28
  "skills": [
@@ -34,14 +35,14 @@
34
35
  "path": "./skills/clickhouse-js-node-troubleshooting"
35
36
  },
36
37
  {
37
- "name": "clickhouse-js-node-rowbinary-parser",
38
- "path": "./skills/clickhouse-js-node-rowbinary-parser"
38
+ "name": "clickhouse-js-node-rowbinary",
39
+ "path": "./skills/clickhouse-js-node-rowbinary"
39
40
  }
40
41
  ]
41
42
  },
42
43
  "scripts": {
43
44
  "pack": "npm pack",
44
- "prepack": "rm -rf skills && cp ../../README.md ../../LICENSE . && cp -r ../../skills . && RBP=skills/clickhouse-js-node-rowbinary-parser && rm -rf $RBP/tests $RBP/node_modules $RBP/dist $RBP/package.json $RBP/package-lock.json $RBP/tsconfig.json $RBP/tsconfig.build.json $RBP/vitest.config.ts $RBP/.gitignore $RBP/LICENSE $RBP/eval_result*.md",
45
+ "prepack": "rm -rf skills && cp ../../README.md ../../LICENSE . && cp -r ../../skills . && RBP=skills/clickhouse-js-node-rowbinary && rm -rf $RBP/tests $RBP/node_modules $RBP/dist $RBP/package.json $RBP/package-lock.json $RBP/tsconfig.json $RBP/tsconfig.build.json $RBP/vitest.config.ts $RBP/.gitignore $RBP/LICENSE $RBP/eval_result*.md",
45
46
  "typecheck": "tsc --noEmit",
46
47
  "lint": "eslint --max-warnings=0 .",
47
48
  "lint:fix": "eslint . --fix",
@@ -0,0 +1,8 @@
1
+ # Recommendations for AI agents — `skills/`
2
+
3
+ Guidance for the shipped agent skills. See the [repo-root `AGENTS.md`](../AGENTS.md) for cross-cutting guidance. The `@clickhouse/rowbinary` skill has its own [`clickhouse-js-node-rowbinary/AGENTS.md`](clickhouse-js-node-rowbinary/AGENTS.md).
4
+
5
+ - Each shipped skill must also be listed in the `agents.skills` array of
6
+ [`packages/client-node/package.json`](../packages/client-node/package.json) so downstream tooling can
7
+ discover it. The [`Skills E2E`](../.github/workflows/e2e-skills.yml) workflow
8
+ (`tests/e2e/skills/check.js`) asserts that the packaged tarball contains the declared skills.
@@ -0,0 +1,44 @@
1
+ # Recommendations for AI agents — `@clickhouse/rowbinary`
2
+
3
+ Guidance for the [`clickhouse-js-node-rowbinary`](.) package (the RowBinary codec library and agent skill). See the [repo-root `AGENTS.md`](../../AGENTS.md) for cross-cutting guidance.
4
+
5
+ This package has a symmetric reader/writer codebase.
6
+
7
+ ## Tests
8
+
9
+ The tests follow a few conventions worth preserving:
10
+
11
+ - **Reader and writer tests are separate files.** Readers are tested in `tests/*.test.ts`; writers in
12
+ `tests/*.write.test.ts`. Keep the two independent: a writer test must **never** decode its bytes back
13
+ through a reader (and vice versa), so a bug on one side cannot mask a bug on the other. Writer tests
14
+ assert the encoded bytes against **live ClickHouse output** as the source of truth.
15
+ - **Each case is an isolated `it()` with a fully-inline body.** Write the assertion out per case, e.g.
16
+ `expect(encode(writer, value)).toEqual(await query("SELECT … FORMAT RowBinary"))`. Do **not** hide the
17
+ assertion behind a thunk-factory helper (`it("name", expectFoo(...))`), and do **not** wrap the query
18
+ in a per-file helper — embed the literal SQL inline, including any `SETTINGS` clause, so the full
19
+ query is visible in the test. The only shared helpers are the generic `query()` (`tests/clickhouse.ts`,
20
+ runs SQL → bytes) and `encode()` (`tests/encode.ts`, value → bytes). Repeating SQL across cases is
21
+ fine; reviewability beats DRY here. See [`tests/Integers.write.test.ts`](tests/Integers.write.test.ts)
22
+ as the canonical example.
23
+
24
+ ## No defensive validation in readers/writers
25
+
26
+ These are hot-path codecs. **Do not add runtime validation of input values** (`isFinite`,
27
+ range/`NaN` checks, type guards, etc.) to the `readX`/`writeX` functions. The data at this level is
28
+ expected to be correct, and an invalid value is a programming error — document the precondition in the
29
+ JSDoc instead (see `writeUVarint` and the `writeDate*`/`writeDateTime` writers as the canonical
30
+ examples). A `Math.round`-style transform that silently shifts a _valid_ value to the wrong encoding is
31
+ a correctness bug and must be fixed; rejecting an _invalid_ value is not our job here.
32
+
33
+ Two narrow exceptions where a check **is** warranted:
34
+
35
+ 1. **It keeps the protocol in sync.** A check belongs in only when skipping it would desync the wire
36
+ stream — e.g. `writeIPv6` requires exactly 16 bytes because a wrong length shifts every subsequent
37
+ field, and `parseIPv6` rejects malformed groups because it can't otherwise produce 16 well-defined
38
+ bytes. These guard the _framing_, not the user's data semantics.
39
+ 2. **The cost is genuinely zero or it can't reach the server.** Pure parse-time helpers (string →
40
+ bytes, before anything is on the wire) may validate, since there's no hot loop and no server to fall
41
+ back on.
42
+
43
+ Otherwise, prefer letting the ClickHouse server reject bad bytes server-side over guarding client-side:
44
+ it already validates, and duplicating that on the encode path costs throughput for no real safety.
@@ -0,0 +1,49 @@
1
+ # Changelog — `@clickhouse/rowbinary`
2
+
3
+ > This file tracks the standalone `@clickhouse/rowbinary` package (the RowBinary
4
+ > parser library and agent skill). Entries relevant to it (through
5
+ > `@clickhouse/client` 1.23.0) were previously recorded in the now-frozen
6
+ > repository-wide [`CHANGELOG.md`](../../CHANGELOG.md).
7
+
8
+ # 0.2.0
9
+
10
+ ## New features
11
+
12
+ - Added the RowBinary **writer** — the encode mirror of the reader: type-specific `writeX(sink, value)` building blocks and combinators that compose with no per-element closures, exported from `@clickhouse/rowbinary/writer` (or the per-type modules). ([#911])
13
+
14
+ `writeRows(writeRow)` drives an `Iterable<T>` of rows into a plain `RowBinary` payload. Note its shape: it is a **streaming generator**, not a one-shot `Writer<readonly T[]>`. It writes into a fixed-size buffer (`bufferSize`, default 64 KiB), yields each batch of whole rows when the buffer fills — rewinding so a half-written row never leaks — and starts a fresh buffer for the rest, so a result larger than the buffer (or an unbounded row source) streams out chunk by chunk. An oversized row grows the buffer (doubling) rather than failing, and per-buffer fill is published on the `@clickhouse/rowbinary:writeRows.flush` `node:diagnostics_channel` for buffer-utilization metrics. ([#915])
15
+
16
+ ```ts
17
+ import {
18
+ writeRows,
19
+ writeTupleNamed,
20
+ writeUInt64,
21
+ writeString,
22
+ } from "@clickhouse/rowbinary/writer";
23
+
24
+ const writeRow = writeTupleNamed({ id: writeUInt64, name: writeString });
25
+ for (const chunk of writeRows(writeRow)(rows, 64 * 1024)) send(chunk);
26
+ ```
27
+
28
+ Writer edge-case hardening: `writeDate`/`writeDate32`/`writeDateTime` floor to the calendar day / whole second instead of rounding (so a non-midnight `Date` or sub-second `DateTime` no longer rounds up); `parseIPv6` rejects malformed hex groups instead of silently encoding `0`; and `writeGeometry` validates the discriminant before writing its byte, so an out-of-range value can't leave a partial payload. ([#916])
29
+
30
+ [#911]: https://github.com/ClickHouse/clickhouse-js/pull/911
31
+ [#915]: https://github.com/ClickHouse/clickhouse-js/pull/915
32
+ [#916]: https://github.com/ClickHouse/clickhouse-js/pull/916
33
+
34
+ # 0.1.2
35
+
36
+ ## Improvements
37
+
38
+ - Bumped the bundled `@clickhouse/datatype-parser` dependency to `0.1.2`. ([#895])
39
+ - Patch release. ([#903])
40
+
41
+ # 0.1.1
42
+
43
+ ## New features
44
+
45
+ - Initial release of `@clickhouse/rowbinary`: a RowBinary reader library (and the companion agent skill) shipping type-specific, monomorphizable building blocks for decoding `RowBinary` / `RowBinaryWithNames` / `RowBinaryWithNamesAndTypes` streams (full-buffer and chunked), plus a skill that guides an agent to generate bespoke high-performance parsers from a query's column types. The same library is also bundled into `@clickhouse/client` (registered in `agents.skills`). Requires Node.js `>=20`. A matching RowBinary writer is planned. ([#864])
46
+
47
+ [#864]: https://github.com/ClickHouse/clickhouse-js/pull/864
48
+ [#895]: https://github.com/ClickHouse/clickhouse-js/pull/895
49
+ [#903]: https://github.com/ClickHouse/clickhouse-js/pull/903
@@ -1,10 +1,10 @@
1
- # ClickHouse Node.js RowBinary Parser Generator
1
+ # ClickHouse Node.js RowBinary Codec Generator
2
2
 
3
- **If JS had a -O3 compiler flag, this skill would be it.** (for RowBinary parsing)
3
+ **If JS had a -O3 compiler flag, this skill would be it.** (for RowBinary read & write)
4
4
 
5
- A skill and a library that lets a coding agent generate bespoke RowBinary parsers on the first pass from the column type definitions of a ClickHouse response. The [spirit](#the-spirit) behind the approach.
5
+ A skill and a library that lets a coding agent generate bespoke RowBinary codecs on the first pass from the column type definitions of a ClickHouse response. The [spirit](#the-spirit) behind the approach.
6
6
 
7
- **Reader only** for now. Today this covers reading (decoding) RowBinary streams. A matching RowBinary writer (encoding) is planned.
7
+ **Reads and writes.** Both directions are covered: readers (decode bytes → values) and writers (encode values bytes), split under `src/readers/` and `src/writers/`. The reader path is the more mature one — the writers mirror it type-for-type, with a few decode-only paths (`Dynamic`, `JSON`, the runtime header/compile path, and the columnar typed-array path) not yet mirrored.
8
8
 
9
9
  ## Status
10
10
 
@@ -12,7 +12,7 @@ A skill and a library that lets a coding agent generate bespoke RowBinary parser
12
12
  - ✅ Opus 4.8: 71% -> 94.7% pass rate
13
13
  - ✅ Haiku 4.5: 52% -> 86.0% pass rate
14
14
  - ✅ Composer 2.5 Fast: 3x parser performance
15
- - ✅ 469/469 tests
15
+ - ✅ 724/724 tests (readers + writers)
16
16
  - ✅ type-checked
17
17
  - ✅ benchmarked
18
18
 
@@ -35,7 +35,7 @@ const readOrderRow: Reader<OrderRow> = (s) => ({
35
35
  id: readUInt8(s),
36
36
  uid: formatUUID(readUUID(s)),
37
37
  price: readDecimal64(2)(s),
38
- status: readEnum8(s),
38
+ status: readInt8(s), // raw enum int; `readEnum8(map)` resolves it to the name
39
39
  });
40
40
  ```
41
41
 
@@ -70,14 +70,74 @@ npx skills-npm setup
70
70
  As a skill only:
71
71
 
72
72
  ```bash
73
- npx skills add ClickHouse/clickhouse-js/skills/clickhouse-js-node-rowbinary-parser
73
+ npx skills add ClickHouse/clickhouse-js/skills/clickhouse-js-node-rowbinary
74
74
  ```
75
75
 
76
76
  ```console
77
- > Hey, Claude, tell me what the rowbinary parser skill can do for me.
78
- > A lot! It generates custom, high-performance RowBinary parsers
79
- > Super, generate a parser for the queries in app/src/model.ts.
80
- < Reading skill clickhouse-js-node-rowbinary-parser
77
+ > Hey, Claude, tell me what the rowbinary skill can do for me.
78
+ > A lot! It generates custom, high-performance RowBinary readers and writers
79
+ > Super, generate a reader for the queries in app/src/model.ts.
80
+ < Reading skill clickhouse-js-node-rowbinary…
81
+ ```
82
+
83
+ ## Using it with the ClickHouse JS client
84
+
85
+ This library only **decodes** the bytes — it doesn't open connections. Pair it
86
+ with the official client to fetch a `RowBinary` response and feed the byte chunks
87
+ into `streamRowBatches(chunks, readRow)`.
88
+
89
+ `RowBinary` isn't one of the formats the client decodes itself, so don't use
90
+ `client.query({ format: ... })` for it. Instead use `client.exec({ query })` with
91
+ the `FORMAT RowBinary` clause written into the SQL yourself — `exec` hands back the
92
+ **raw, undecoded byte stream** of the response, which is exactly what this library
93
+ consumes. (Use plain `RowBinary`, not `RowBinaryWithNamesAndTypes`, unless your
94
+ reader also skips the leading names/types header.)
95
+
96
+ The row reader below is the `orders` example from [EXAMPLES.md](EXAMPLES.md); swap
97
+ in the reader the skill generates for your own columns.
98
+
99
+ ```ts
100
+ import {
101
+ type Reader,
102
+ readUInt8,
103
+ readInt8,
104
+ readUUID,
105
+ formatUUID,
106
+ readDecimal64,
107
+ type DecimalValue,
108
+ streamRowBatches,
109
+ } from "@clickhouse/rowbinary";
110
+ import { createClient } from "@clickhouse/client";
111
+
112
+ type OrderRow = {
113
+ id: number;
114
+ uid: string;
115
+ price: DecimalValue;
116
+ status: number;
117
+ };
118
+
119
+ const readOrderRow: Reader<OrderRow> = (s) => ({
120
+ id: readUInt8(s),
121
+ uid: formatUUID(readUUID(s)),
122
+ price: readDecimal64(2)(s),
123
+ status: readInt8(s), // raw enum int; `readEnum8(map)` resolves it to the name
124
+ });
125
+
126
+ // `exec` resolves to a Node `Stream.Readable`. It is already an
127
+ // `AsyncIterable<Uint8Array>` (chunks are `Buffer`/`Uint8Array`, which
128
+ // `streamRowBatches` normalizes), so pass `stream` straight in:
129
+
130
+ const client = createClient();
131
+
132
+ const { stream } = await client.exec({
133
+ query: "SELECT id, uid, price, status FROM orders FORMAT RowBinary",
134
+ });
135
+
136
+ for await (const rows of streamRowBatches(stream, readOrderRow)) {
137
+ for (const row of rows) console.log(row); // { id, uid, price: [unscaled, scale], status }
138
+ }
139
+
140
+ await client.close();
81
141
  ```
82
142
 
83
143
  ## Why it's worth it
@@ -208,18 +268,22 @@ Measure, don't assume.
208
268
 
209
269
  ## Scope
210
270
 
211
- - **In scope:** `RowBinary`, `RowBinaryWithNames`, and
271
+ - **In scope (reading):** `RowBinary`, `RowBinaryWithNames`, and
212
272
  `RowBinaryWithNamesAndTypes` decoding for Node.js — full-buffer and streaming
213
273
  (chunked) via `advance()`/`NeedMoreData`, `readRows()`, and the async
214
274
  `streamRowBatches()` (with a built-in small-chunk warning and the optional
215
275
  `coalesceChunks()` debounce filter).
216
- - **Planned:** RowBinary **writing / encoding** (the inverse of everything above)
276
+ - **In scope (writing):** the inverse encode path — a `writeX` mirroring every
277
+ `readX`, appending bytes to a `Sink`, plus `writeRows()`. Imported from
278
+ `@clickhouse/rowbinary/writer`. A handful of decode-only paths are not yet
279
+ mirrored: `Dynamic`, `JSON`, the runtime header/compile path, and the columnar
280
+ typed-array path.
217
281
  - **Out of scope (for now):** browsers and Edge runtimes, non-RowBinary formats
218
282
  (JSON / CSV / TSV / Parquet), and big-endian hosts.
219
283
 
220
284
  ## The spirit
221
285
 
222
- A RowBinary parser generator is a narrow thing. But it's built as an instance of
286
+ A RowBinary codec generator is a narrow thing. But it's built as an instance of
223
287
  a broader bet about what libraries become once a capable LLM is part of the
224
288
  toolchain. Three shifts, each already visible in this repo:
225
289
 
@@ -0,0 +1,111 @@
1
+ ---
2
+ name: clickhouse-js-node-rowbinary
3
+ description: >
4
+ Generate TypeScript/JavaScript code that reads/decodes AND writes/encodes
5
+ ClickHouse RowBinary streams for the ClickHouse HTTP server.
6
+ Use this skill whenever a user wants to parse or produce `RowBinary`,
7
+ `RowBinaryWithNames`, or `RowBinaryWithNamesAndTypes`.
8
+ Node.js only, doesn't cover browsers.
9
+ ---
10
+
11
+ # ClickHouse JS RowBinary Codec Generator for Node.js
12
+
13
+ This skill generates both directions of the wire format: **readers** (decode
14
+ bytes → values) and **writers** (encode values → bytes, the mirror). A given
15
+ task normally needs only one side. This file is the shared entry point — the
16
+ format gate plus the principles common to both directions; the per-direction
17
+ decisions, guidance, and the per-type reference tables live in two sibling files.
18
+
19
+ **Pick your side — read only the one you need:**
20
+
21
+ - **Decoding a `RowBinary*` response** from ClickHouse into JS values →
22
+ **[reader.md](reader.md)**. Streaming vs whole-buffer, row-objects vs columnar,
23
+ fixed vs runtime schema, and the per-type reader reference.
24
+ - **Encoding JS values into a `RowBinary` payload** to send to ClickHouse →
25
+ **[writer.md](writer.md)**. The `Sink`/`writeX` building blocks, `writeRows`
26
+ streaming, and the per-type writer reference.
27
+
28
+ The per-type code is real, split by direction under `src/readers/` and
29
+ `src/writers/`.
30
+
31
+ ## First: is RowBinary even the right format?
32
+
33
+ RowBinary exists for throughput, but it is **not automatically the fastest
34
+ path** — match the format to the shape of the data before committing to a
35
+ bespoke parser.
36
+
37
+ **Prefer a `JSON*` format (e.g. `JSONEachRow`) when** the result is mostly
38
+ strings / JSON-like values that you consume wholesale — randomly accessing
39
+ essentially every field, running string/regexp methods on them, treating values
40
+ as text. V8's native `JSON.parse` is heavily optimized C++ and builds JS strings
41
+ and objects faster than a JS-level RowBinary decoder can; pair it with HTTP
42
+ response compression (`gzip` / `zstd`, which crushes JSON's repetitive keys) and
43
+ the wire cost shrinks too.
44
+
45
+ **RowBinary clearly wins when** the result is dominated by:
46
+
47
+ - **Wide numerics** — `Int128`/`Int256`/`UInt128`/`UInt256`,
48
+ `Decimal128`/`Decimal256`.
49
+ - **Binary / fixed-width blobs** — `IPv4`, `IPv6`, `UUID`, `FixedString`.
50
+ - **High-volume fixed-width numeric columns** generally, where each value is a
51
+ single `DataView` read.
52
+
53
+ **Prefer the `Native` format when** columnar load and client-side analytics are
54
+ the main goal (fold/scan/filter columns, feed typed arrays to a Worker or WASM).
55
+ `Native` is column-major, so it loads straight into one typed array per column
56
+ with no transpose.
57
+
58
+ For help choosing and consuming a `JSON*` format (or CSV / TSV) instead, use the
59
+ **`clickhouse-js-node-coding`** skill.
60
+
61
+ ## Core guidance (both directions)
62
+
63
+ These principles apply whether you are generating a reader or a writer; the
64
+ side-specific operational guidance is in [reader.md](reader.md) /
65
+ [writer.md](writer.md).
66
+
67
+ - **Little-endian only.** RowBinary is little-endian; target x86/ARM. Read and
68
+ write every multi-byte number with `DataView` accessors passing a **literal**
69
+ `true` for the `littleEndian` flag.
70
+
71
+ - **Correct first, then optimize.** First emit a correct codec built from the
72
+ plain per-type API. Only after it's correct (and tested) specialize it. Don't
73
+ bake performance assumptions in before correctness.
74
+
75
+ - **Monomorphize generic/composite types.** Emit specialized, inlined code per
76
+ type combination instead of passing functions as arguments where the type is
77
+ known ahead of time.
78
+
79
+ - **Inline the leaf ops.** The per-type `readX`/`writeX` functions are the
80
+ correct, composable reference; the generated codec should INLINE their bodies,
81
+ not call them, so the row loop is straight-line with no per-field indirection
82
+ (and so the fixed-width coalescing can fold the offset arithmetic together).
83
+
84
+ - **Annotate the type per column.** Inlining erases the type structure, so put a
85
+ short comment above each column's encode/decode block naming the ClickHouse
86
+ type it handles.
87
+
88
+ - **Shared scratch is not reentrant.** Some hot methods reuse a module-level
89
+ scratch buffer as a write-then-read pair — correct only because the access is
90
+ fully synchronous. An `async`/`yield` boundary between populating and reading
91
+ it corrupts the value.
92
+
93
+ - **TypeScript by default.** Generate TypeScript code and helpers unless the user
94
+ explicitly asks for plain JavaScript.
95
+
96
+ ## Worked examples
97
+
98
+ Six end-to-end examples with real speedup are catalogued in [EXAMPLES.md](EXAMPLES.md).
99
+
100
+ ## Out of scope
101
+
102
+ - **JSON / CSV / TSV / Parquet parsing** → use `clickhouse-js-node-coding`.
103
+ - **Connection errors, hangs, type mismatches** → use
104
+ `clickhouse-js-node-troubleshooting`.
105
+ - **Browser / Web Worker / Edge** → `@clickhouse/client-web`.
106
+
107
+ ## Still Stuck?
108
+
109
+ - [ClickHouse RowBinary format](https://clickhouse.com/docs/interfaces/formats#rowbinary)
110
+ - [ClickHouse data types](https://clickhouse.com/docs/sql-reference/data-types)
111
+ - [ClickHouse JS client docs](https://clickhouse.com/docs/integrations/javascript)