@clickhouse/client 1.22.0 → 1.23.0-head.70ad405.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.
Files changed (191) hide show
  1. package/CHANGELOG.md +1342 -0
  2. package/README.md +20 -7
  3. package/dist/client.d.ts +2 -2
  4. package/dist/client.js +11 -4
  5. package/dist/client.js.map +1 -1
  6. package/dist/common/clickhouse_types.d.ts +98 -0
  7. package/dist/common/clickhouse_types.js +30 -0
  8. package/dist/common/clickhouse_types.js.map +1 -0
  9. package/dist/common/client.d.ts +233 -0
  10. package/dist/common/client.js +414 -0
  11. package/dist/common/client.js.map +1 -0
  12. package/dist/common/config.d.ts +234 -0
  13. package/dist/common/config.js +364 -0
  14. package/dist/common/config.js.map +1 -0
  15. package/dist/common/connection.d.ts +124 -0
  16. package/dist/common/connection.js +3 -0
  17. package/dist/common/connection.js.map +1 -0
  18. package/dist/common/data_formatter/format_query_params.d.ts +11 -0
  19. package/dist/common/data_formatter/format_query_params.js +128 -0
  20. package/dist/common/data_formatter/format_query_params.js.map +1 -0
  21. package/dist/common/data_formatter/format_query_settings.d.ts +2 -0
  22. package/dist/common/data_formatter/format_query_settings.js +20 -0
  23. package/dist/common/data_formatter/format_query_settings.js.map +1 -0
  24. package/dist/common/data_formatter/formatter.d.ts +41 -0
  25. package/dist/common/data_formatter/formatter.js +78 -0
  26. package/dist/common/data_formatter/formatter.js.map +1 -0
  27. package/dist/common/data_formatter/index.d.ts +3 -0
  28. package/dist/common/data_formatter/index.js +24 -0
  29. package/dist/common/data_formatter/index.js.map +1 -0
  30. package/dist/common/error/error.d.ts +20 -0
  31. package/dist/common/error/error.js +73 -0
  32. package/dist/common/error/error.js.map +1 -0
  33. package/dist/common/error/index.d.ts +1 -0
  34. package/dist/common/error/index.js +18 -0
  35. package/dist/common/error/index.js.map +1 -0
  36. package/dist/common/index.d.ts +67 -0
  37. package/dist/common/index.js +97 -0
  38. package/dist/common/index.js.map +1 -0
  39. package/dist/common/logger.d.ts +80 -0
  40. package/dist/common/logger.js +154 -0
  41. package/dist/common/logger.js.map +1 -0
  42. package/dist/common/parse/column_types.d.ts +155 -0
  43. package/dist/common/parse/column_types.js +594 -0
  44. package/dist/common/parse/column_types.js.map +1 -0
  45. package/dist/common/parse/index.d.ts +2 -0
  46. package/dist/common/parse/index.js +19 -0
  47. package/dist/common/parse/index.js.map +1 -0
  48. package/dist/common/parse/json_handling.d.ts +19 -0
  49. package/dist/common/parse/json_handling.js +8 -0
  50. package/dist/common/parse/json_handling.js.map +1 -0
  51. package/dist/common/result.d.ts +90 -0
  52. package/dist/common/result.js +3 -0
  53. package/dist/common/result.js.map +1 -0
  54. package/dist/common/settings.d.ts +2007 -0
  55. package/dist/common/settings.js +19 -0
  56. package/dist/common/settings.js.map +1 -0
  57. package/dist/common/tracing.d.ts +146 -0
  58. package/dist/common/tracing.js +76 -0
  59. package/dist/common/tracing.js.map +1 -0
  60. package/dist/common/ts_utils.d.ts +4 -0
  61. package/dist/common/ts_utils.js +3 -0
  62. package/dist/common/ts_utils.js.map +1 -0
  63. package/dist/common/utils/connection.d.ts +21 -0
  64. package/dist/common/utils/connection.js +43 -0
  65. package/dist/common/utils/connection.js.map +1 -0
  66. package/dist/common/utils/index.d.ts +5 -0
  67. package/dist/common/utils/index.js +22 -0
  68. package/dist/common/utils/index.js.map +1 -0
  69. package/dist/common/utils/multipart.d.ts +34 -0
  70. package/dist/common/utils/multipart.js +81 -0
  71. package/dist/common/utils/multipart.js.map +1 -0
  72. package/dist/common/utils/sleep.d.ts +4 -0
  73. package/dist/common/utils/sleep.js +12 -0
  74. package/dist/common/utils/sleep.js.map +1 -0
  75. package/dist/common/utils/stream.d.ts +15 -0
  76. package/dist/common/utils/stream.js +50 -0
  77. package/dist/common/utils/stream.js.map +1 -0
  78. package/dist/common/utils/url.d.ts +20 -0
  79. package/dist/common/utils/url.js +67 -0
  80. package/dist/common/utils/url.js.map +1 -0
  81. package/dist/common/version.d.ts +2 -0
  82. package/dist/common/version.js +4 -0
  83. package/dist/common/version.js.map +1 -0
  84. package/dist/config.d.ts +22 -2
  85. package/dist/config.js +2 -2
  86. package/dist/config.js.map +1 -1
  87. package/dist/connection/compression.d.ts +2 -2
  88. package/dist/connection/compression.js +4 -4
  89. package/dist/connection/compression.js.map +1 -1
  90. package/dist/connection/create_connection.d.ts +1 -1
  91. package/dist/connection/node_base_connection.d.ts +3 -3
  92. package/dist/connection/node_base_connection.js +22 -22
  93. package/dist/connection/node_base_connection.js.map +1 -1
  94. package/dist/connection/node_custom_agent_connection.js +2 -2
  95. package/dist/connection/node_custom_agent_connection.js.map +1 -1
  96. package/dist/connection/node_http_connection.js +2 -2
  97. package/dist/connection/node_http_connection.js.map +1 -1
  98. package/dist/connection/node_https_connection.d.ts +1 -1
  99. package/dist/connection/node_https_connection.js +3 -3
  100. package/dist/connection/node_https_connection.js.map +1 -1
  101. package/dist/connection/socket_pool.d.ts +1 -1
  102. package/dist/connection/socket_pool.js +30 -30
  103. package/dist/connection/socket_pool.js.map +1 -1
  104. package/dist/connection/stream.d.ts +1 -1
  105. package/dist/connection/stream.js +9 -9
  106. package/dist/connection/stream.js.map +1 -1
  107. package/dist/index.d.ts +9 -7
  108. package/dist/index.js +26 -24
  109. package/dist/index.js.map +1 -1
  110. package/dist/result_set.d.ts +1 -1
  111. package/dist/result_set.js +10 -10
  112. package/dist/result_set.js.map +1 -1
  113. package/dist/utils/encoder.d.ts +1 -1
  114. package/dist/utils/encoder.js +5 -5
  115. package/dist/utils/encoder.js.map +1 -1
  116. package/dist/version.d.ts +1 -1
  117. package/dist/version.js +1 -1
  118. package/dist/version.js.map +1 -1
  119. package/package.json +10 -7
  120. package/skills/AGENTS.md +8 -0
  121. package/skills/clickhouse-js-node-rowbinary/AGENTS.md +44 -0
  122. package/skills/clickhouse-js-node-rowbinary/CHANGELOG.md +49 -0
  123. package/skills/clickhouse-js-node-rowbinary/EXAMPLES.md +48 -0
  124. package/skills/clickhouse-js-node-rowbinary/README.md +319 -0
  125. package/skills/clickhouse-js-node-rowbinary/SKILL.md +111 -0
  126. package/skills/clickhouse-js-node-rowbinary/case-studies/iot-rowbinary-vs-json.md +83 -0
  127. package/skills/clickhouse-js-node-rowbinary/case-studies/ledger-rowbinary-vs-json.md +103 -0
  128. package/skills/clickhouse-js-node-rowbinary/case-studies/logs-json-wins.md +86 -0
  129. package/skills/clickhouse-js-node-rowbinary/case-studies/wasm-vs-js.md +172 -0
  130. package/skills/clickhouse-js-node-rowbinary/reader.md +126 -0
  131. package/skills/clickhouse-js-node-rowbinary/src/examples/carts.ts +75 -0
  132. package/skills/clickhouse-js-node-rowbinary/src/examples/events.ts +51 -0
  133. package/skills/clickhouse-js-node-rowbinary/src/examples/iot.ts +158 -0
  134. package/skills/clickhouse-js-node-rowbinary/src/examples/ledger.ts +98 -0
  135. package/skills/clickhouse-js-node-rowbinary/src/examples/logs.ts +73 -0
  136. package/skills/clickhouse-js-node-rowbinary/src/examples/observability.ts +141 -0
  137. package/skills/clickhouse-js-node-rowbinary/src/examples/orders.ts +66 -0
  138. package/skills/clickhouse-js-node-rowbinary/src/examples/profiles.ts +60 -0
  139. package/skills/clickhouse-js-node-rowbinary/src/examples/telemetry.ts +102 -0
  140. package/skills/clickhouse-js-node-rowbinary/src/readers/aggregateFunction.ts +34 -0
  141. package/skills/clickhouse-js-node-rowbinary/src/readers/bool.ts +10 -0
  142. package/skills/clickhouse-js-node-rowbinary/src/readers/columnar.ts +125 -0
  143. package/skills/clickhouse-js-node-rowbinary/src/readers/compile.ts +328 -0
  144. package/skills/clickhouse-js-node-rowbinary/src/readers/composite.ts +181 -0
  145. package/skills/clickhouse-js-node-rowbinary/src/readers/core.ts +77 -0
  146. package/skills/clickhouse-js-node-rowbinary/src/readers/datetime.ts +113 -0
  147. package/skills/clickhouse-js-node-rowbinary/src/readers/decimals.ts +57 -0
  148. package/skills/clickhouse-js-node-rowbinary/src/readers/dynamic.ts +332 -0
  149. package/skills/clickhouse-js-node-rowbinary/src/readers/enums.ts +40 -0
  150. package/skills/clickhouse-js-node-rowbinary/src/readers/floats.ts +32 -0
  151. package/skills/clickhouse-js-node-rowbinary/src/readers/geo.ts +109 -0
  152. package/skills/clickhouse-js-node-rowbinary/src/readers/header.ts +29 -0
  153. package/skills/clickhouse-js-node-rowbinary/src/readers/integers.ts +95 -0
  154. package/skills/clickhouse-js-node-rowbinary/src/readers/interval.ts +54 -0
  155. package/skills/clickhouse-js-node-rowbinary/src/readers/ip.ts +93 -0
  156. package/skills/clickhouse-js-node-rowbinary/src/readers/json.ts +33 -0
  157. package/skills/clickhouse-js-node-rowbinary/src/readers/lowCardinality.ts +18 -0
  158. package/skills/clickhouse-js-node-rowbinary/src/readers/nested.ts +23 -0
  159. package/skills/clickhouse-js-node-rowbinary/src/readers/nothing.ts +29 -0
  160. package/skills/clickhouse-js-node-rowbinary/src/readers/reader.ts +68 -0
  161. package/skills/clickhouse-js-node-rowbinary/src/readers/rowBinaryWithNamesAndTypes.ts +155 -0
  162. package/skills/clickhouse-js-node-rowbinary/src/readers/rows.ts +58 -0
  163. package/skills/clickhouse-js-node-rowbinary/src/readers/simpleAggregateFunction.ts +20 -0
  164. package/skills/clickhouse-js-node-rowbinary/src/readers/stream.ts +276 -0
  165. package/skills/clickhouse-js-node-rowbinary/src/readers/strings.ts +55 -0
  166. package/skills/clickhouse-js-node-rowbinary/src/readers/time.ts +61 -0
  167. package/skills/clickhouse-js-node-rowbinary/src/readers/uuid.ts +153 -0
  168. package/skills/clickhouse-js-node-rowbinary/src/readers/varint.ts +70 -0
  169. package/skills/clickhouse-js-node-rowbinary/src/writers/aggregateFunction.ts +18 -0
  170. package/skills/clickhouse-js-node-rowbinary/src/writers/bool.ts +10 -0
  171. package/skills/clickhouse-js-node-rowbinary/src/writers/composite.ts +140 -0
  172. package/skills/clickhouse-js-node-rowbinary/src/writers/core.ts +92 -0
  173. package/skills/clickhouse-js-node-rowbinary/src/writers/datetime.ts +123 -0
  174. package/skills/clickhouse-js-node-rowbinary/src/writers/decimals.ts +51 -0
  175. package/skills/clickhouse-js-node-rowbinary/src/writers/enums.ts +18 -0
  176. package/skills/clickhouse-js-node-rowbinary/src/writers/floats.ts +40 -0
  177. package/skills/clickhouse-js-node-rowbinary/src/writers/geo.ts +125 -0
  178. package/skills/clickhouse-js-node-rowbinary/src/writers/integers.ts +90 -0
  179. package/skills/clickhouse-js-node-rowbinary/src/writers/interval.ts +11 -0
  180. package/skills/clickhouse-js-node-rowbinary/src/writers/ip.ts +121 -0
  181. package/skills/clickhouse-js-node-rowbinary/src/writers/lowCardinality.ts +12 -0
  182. package/skills/clickhouse-js-node-rowbinary/src/writers/nested.ts +17 -0
  183. package/skills/clickhouse-js-node-rowbinary/src/writers/nothing.ts +21 -0
  184. package/skills/clickhouse-js-node-rowbinary/src/writers/rows.ts +144 -0
  185. package/skills/clickhouse-js-node-rowbinary/src/writers/simpleAggregateFunction.ts +12 -0
  186. package/skills/clickhouse-js-node-rowbinary/src/writers/strings.ts +77 -0
  187. package/skills/clickhouse-js-node-rowbinary/src/writers/time.ts +54 -0
  188. package/skills/clickhouse-js-node-rowbinary/src/writers/uuid.ts +60 -0
  189. package/skills/clickhouse-js-node-rowbinary/src/writers/varint.ts +64 -0
  190. package/skills/clickhouse-js-node-rowbinary/src/writers/writer.ts +101 -0
  191. package/skills/clickhouse-js-node-rowbinary/writer.md +96 -0
@@ -0,0 +1,140 @@
1
+ import { type Writer } from "./core.js";
2
+ import { writeUInt8 } from "./integers.js";
3
+ import { writeUVarint } from "./varint.js";
4
+
5
+ // --- Writers: the encode mirror of the combinators in `composite.ts`. Each takes
6
+ // sub-WRITERS (instead of sub-readers) and returns a Writer; MONOMORPHIZE when
7
+ // generating code, exactly as noted for the readers.
8
+
9
+ /**
10
+ * Write a `Nullable(T)`: a 1-byte null flag (0 = present, 1 = NULL), then the
11
+ * inner value ONLY when present. The inverse of `readNullable`; curried — pass the
12
+ * inner writer, get a `Writer<T | null>`.
13
+ */
14
+ export function writeNullable<T>(writeValue: Writer<T>): Writer<T | null> {
15
+ return (sink, value) => {
16
+ if (value === null) {
17
+ writeUInt8(sink, 1);
18
+ } else {
19
+ writeUInt8(sink, 0);
20
+ writeValue(sink, value);
21
+ }
22
+ };
23
+ }
24
+
25
+ /**
26
+ * Write an `Array(T)`: a LEB128 element count, then each element back-to-back. The
27
+ * inverse of `readArray`; curried — pass the element writer, get a `Writer<T[]>`.
28
+ */
29
+ export function writeArray<T>(writeElement: Writer<T>): Writer<readonly T[]> {
30
+ return (sink, values) => {
31
+ writeUVarint(sink, values.length);
32
+ // C-style loop, not for-of: this is a hot path and we don't want the
33
+ // iterator protocol overhead on a plain array.
34
+ for (let i = 0; i < values.length; i++) writeElement(sink, values[i]!);
35
+ };
36
+ }
37
+
38
+ /**
39
+ * Write a `QBit(element_type, dimension)`: in RowBinary it is byte-for-byte an
40
+ * `Array(element_type)`, so this is just {@link writeArray}. The inverse of
41
+ * `readQBit`.
42
+ */
43
+ export function writeQBit<T>(writeElement: Writer<T>): Writer<readonly T[]> {
44
+ return writeArray(writeElement);
45
+ }
46
+
47
+ /**
48
+ * Write a `Tuple(...)` from a positional array: each element's value
49
+ * back-to-back, with NO count and NO delimiter. The inverse of `readTuple`;
50
+ * curried — pass one writer per element (in order), get a `Writer` of the tuple.
51
+ */
52
+ export function writeTuple<T extends readonly unknown[]>(writers: {
53
+ [K in keyof T]: Writer<T[K]>;
54
+ }): Writer<T> {
55
+ const fns = writers as ReadonlyArray<Writer<unknown>>;
56
+ return (sink, value) => {
57
+ for (let i = 0; i < fns.length; i++) fns[i]!(sink, value[i]);
58
+ };
59
+ }
60
+
61
+ /**
62
+ * Write a named `Tuple(name1 T1, ...)` from an object. The wire is identical to an
63
+ * unnamed tuple — values back-to-back, no count or delimiter — so the writers run
64
+ * in the `writers` object's key order, which MUST match the tuple's declared field
65
+ * order. The inverse of `readTupleNamed`; curried.
66
+ */
67
+ export function writeTupleNamed<T extends Record<string, unknown>>(writers: {
68
+ [K in keyof T]: Writer<T[K]>;
69
+ }): Writer<T> {
70
+ const fns = writers as Record<string, Writer<unknown>>;
71
+ const keys = Object.keys(fns);
72
+ return (sink, value) => {
73
+ // C-style loop, not for-of: hot path, plain array of keys.
74
+ for (let i = 0; i < keys.length; i++) {
75
+ const key = keys[i]!;
76
+ fns[key]!(sink, value[key]);
77
+ }
78
+ };
79
+ }
80
+
81
+ /**
82
+ * Write a `Map(K, V)`: a LEB128 pair count, then key/value interleaved
83
+ * (k, v, k, v, ...). The inverse of `readMap`; curried — pass the key and value
84
+ * writers, get a `Writer<Map<K, V>>` (`Map` iteration order is preserved).
85
+ */
86
+ export function writeMap<K, V>(
87
+ writeKey: Writer<K>,
88
+ writeValue: Writer<V>,
89
+ ): Writer<ReadonlyMap<K, V>> {
90
+ return (sink, map) => {
91
+ writeUVarint(sink, map.size);
92
+ // for-of is intentional here: it is the fastest way to iterate a `Map`
93
+ // (unlike plain arrays, where a C-style index loop wins).
94
+ for (const [key, value] of map) {
95
+ writeKey(sink, key);
96
+ writeValue(sink, value);
97
+ }
98
+ };
99
+ }
100
+
101
+ /**
102
+ * A tagged `Variant` value for {@link writeVariant}: the active alternative's
103
+ * `discriminant` (its index in the sorted-type-name order) paired with its value,
104
+ * or `null` for a NULL.
105
+ *
106
+ * WHY TAGGED: `readVariant` returns only the decoded VALUE — the discriminant is
107
+ * consumed from the wire and not surfaced — so encode cannot recover which
108
+ * alternative a bare value belongs to (e.g. is `5` the `UInt8` or the `Int32`
109
+ * alternative?). The discriminant must therefore be supplied explicitly, the
110
+ * encode-side analog of the `readGeometry` switch.
111
+ */
112
+ export type VariantValue =
113
+ | readonly [discriminant: number, value: unknown]
114
+ | null;
115
+
116
+ /**
117
+ * Write a `Variant(T1, ..., Tn)`: a 1-byte discriminant then the chosen
118
+ * alternative's value (discriminant `0xFF` = NULL, no value). The inverse of
119
+ * `readVariant`; curried — pass the alternative writers in sorted-type-name order
120
+ * (same order the reader expects), get a `Writer<VariantValue>`.
121
+ */
122
+ export function writeVariant(
123
+ writers: ReadonlyArray<Writer<never>>,
124
+ ): Writer<VariantValue> {
125
+ return (sink, value) => {
126
+ if (value === null) {
127
+ writeUInt8(sink, 0xff);
128
+ return;
129
+ }
130
+ const [discriminant, inner] = value;
131
+ const fn = writers[discriminant] as Writer<unknown> | undefined;
132
+ if (fn === undefined) {
133
+ throw new RangeError(
134
+ `RowBinary Variant: discriminant ${discriminant} out of range (${writers.length} alternatives)`,
135
+ );
136
+ }
137
+ writeUInt8(sink, discriminant);
138
+ fn(sink, inner);
139
+ };
140
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Thrown by {@link reserve} when the sink's buffer is full — i.e. lacks the bytes
3
+ * a write needs. The encode-side mirror of the reader's `NeedMoreData`. Like the
4
+ * reader, the `Sink` treats its buffer as a FIXED-length window: when a write
5
+ * would overflow it, `reserve` throws this sentinel WITHOUT moving the position,
6
+ * so a driver can flush the bytes written so far down the connection (a transport
7
+ * filter can glue successive buffers back together) and continue into a fresh
8
+ * buffer.
9
+ *
10
+ * A bare sentinel, NOT an `Error` subclass, on purpose — exactly as `NeedMoreData`
11
+ * on the read side: constructing an `Error` captures a stack trace (the expensive
12
+ * part of throwing), pure waste on a path that fires once per buffer boundary.
13
+ */
14
+ export const BufferFull = Symbol("RowBinary.BufferFull");
15
+
16
+ /**
17
+ * The write-side mirror of the reader's `Cursor`: the cursor every writer threads
18
+ * through. A `Buffer` to write into, the current write position, and a
19
+ * `DataView` over the same bytes. The encode counterpart of decode's `Cursor`.
20
+ *
21
+ * Deliberately STATE only — no write methods. Encoding lives in the free
22
+ * `writeX(sink, value)` functions in the sibling modules, so a generated encoder
23
+ * pulls in only the per-type writers a result needs (exactly like the reader
24
+ * side). `view`/`buf` are public so those free functions can reach them.
25
+ *
26
+ * Like a `Cursor`, a `Sink` wraps a FIXED-length buffer (supplied by the caller):
27
+ * it never reallocates. {@link reserve} throws {@link BufferFull} when the next
28
+ * write would overflow, the encode mirror of the reader's `advance` throwing
29
+ * `NeedMoreData` on underflow. Size the buffer to a chunk you intend to flush, and
30
+ * pull the written bytes with {@link Sink.bytes}.
31
+ */
32
+ export class Sink {
33
+ pos = 0;
34
+
35
+ /**
36
+ * The buffer being written into. Only `buf.subarray(0, pos)` (see
37
+ * {@link Sink.bytes}) holds written bytes; the tail is unwritten headroom.
38
+ * Built with the buffer's own `byteOffset`/`byteLength` view in
39
+ * {@link Sink.view}, exactly like the reader's `Cursor`.
40
+ */
41
+ readonly buf: Buffer;
42
+
43
+ /**
44
+ * `DataView` over {@link Sink.buf}, for fixed-width integer/float writes. Built
45
+ * with the buffer's own `byteOffset`/`byteLength`: a `Buffer` is often a window
46
+ * into a larger pooled `ArrayBuffer`, so `new DataView(buf.buffer)` alone would
47
+ * point at the wrong bytes.
48
+ */
49
+ readonly view: DataView;
50
+
51
+ constructor(buf: Buffer) {
52
+ this.buf = buf;
53
+ this.view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
54
+ }
55
+
56
+ /**
57
+ * The written bytes — `buf.subarray(0, pos)`. A zero-copy VIEW into the sink's
58
+ * buffer, so use `Buffer.from(sink.bytes())` if you need an independent copy.
59
+ */
60
+ bytes(): Buffer {
61
+ return this.buf.subarray(0, this.pos);
62
+ }
63
+ }
64
+
65
+ /**
66
+ * A `Writer<T>` encodes one value of type `T` into the sink, advancing it — the
67
+ * mirror of the reader's `Reader<T>`. Leaf writers (e.g. `writeUInt32`) are
68
+ * `Writer`s directly; combinators (e.g. `writeArray`) take sub-`Writer`s and
69
+ * return a `Writer`, so types compose with no per-element closures.
70
+ */
71
+ export type Writer<T> = (sink: Sink, value: T) => void;
72
+
73
+ /**
74
+ * Reserve `n` bytes for the next write: bounds-check them, advance the position
75
+ * past them, and return the offset the write starts at (the value BEFORE
76
+ * advancing). The write-side mirror of the reader's `advance`: every fixed-width
77
+ * write goes through it, so the capacity check and position bookkeeping live in
78
+ * one place:
79
+ *
80
+ * function writeInt32(s, v) { s.view.setInt32(reserve(s, 4), v, true); }
81
+ *
82
+ * Throws {@link BufferFull} when fewer than `n` bytes remain, WITHOUT moving
83
+ * the position — the buffer is fixed-length, exactly as the reader's input is, so
84
+ * a driver flushes what is written and retries the row into a fresh buffer.
85
+ */
86
+ export function reserve(sink: Sink, n: number): number {
87
+ const start = sink.pos;
88
+ const next = start + n;
89
+ if (next > sink.buf.length) throw BufferFull;
90
+ sink.pos = next;
91
+ return start;
92
+ }
@@ -0,0 +1,123 @@
1
+ import { type Writer, Sink, reserve } from "./core.js";
2
+ import { type Microseconds, type Nanoseconds } from "../readers/datetime.js";
3
+
4
+ const MS_PER_DAY = 86_400_000;
5
+
6
+ /**
7
+ * Write a `Date`: 2-byte `UInt16` count of days since 1970-01-01 (UTC). The
8
+ * inverse of `readDate` — the `Date` it produced is at UTC midnight, so
9
+ * `getTime() / 86_400_000` recovers the whole-day count exactly. A non-midnight
10
+ * input is floored to its calendar day (matching ClickHouse's truncation),
11
+ * never rounded up into the next day.
12
+ *
13
+ * PRECONDITION: a valid `Date` whose day count fits the `UInt16` range
14
+ * (1970-01-01 … ~2149-06-06). Like every leaf writer (see `writeUVarint`) this
15
+ * is not range-checked — an invalid or out-of-range `Date` (e.g. a pre-1970 one,
16
+ * which belongs in {@link writeDate32}) is a programming error; the resulting
17
+ * bytes are rejected server-side.
18
+ */
19
+ export function writeDate(sink: Sink, value: Date): void {
20
+ sink.view.setUint16(
21
+ reserve(sink, 2),
22
+ Math.floor(value.getTime() / MS_PER_DAY),
23
+ true,
24
+ );
25
+ }
26
+
27
+ /**
28
+ * Write a `Date32`: 4-byte signed `Int32` count of days since 1970-01-01 (UTC),
29
+ * negative for pre-1970 dates. The inverse of `readDate32`. A non-midnight input
30
+ * is floored toward -inf to its calendar day, so pre-1970 instants land on the
31
+ * correct (more negative) day rather than rounding toward the epoch.
32
+ *
33
+ * PRECONDITION: a valid `Date` whose day count fits `Int32`. Not range-checked
34
+ * (as elsewhere) — an invalid `Date` is a programming error, rejected server-side.
35
+ */
36
+ export function writeDate32(sink: Sink, value: Date): void {
37
+ sink.view.setInt32(
38
+ reserve(sink, 4),
39
+ Math.floor(value.getTime() / MS_PER_DAY),
40
+ true,
41
+ );
42
+ }
43
+
44
+ /**
45
+ * Write a `DateTime`: 4-byte `UInt32` Unix seconds. The inverse of `readDateTime`;
46
+ * the column timezone is metadata, not in the bytes. Sub-second components are
47
+ * floored away (matching the reader and `writeDateTime64`'s `Math.floor`), never
48
+ * rounded up to the next second.
49
+ *
50
+ * PRECONDITION: a valid `Date` whose Unix-seconds fit the `UInt32` range
51
+ * (1970-01-01 … 2106-02-07). Not range-checked (as elsewhere) — an invalid or
52
+ * out-of-range `Date` is a programming error, rejected server-side.
53
+ */
54
+ export function writeDateTime(sink: Sink, value: Date): void {
55
+ sink.view.setUint32(
56
+ reserve(sink, 4),
57
+ Math.floor(value.getTime() / 1000),
58
+ true,
59
+ );
60
+ }
61
+
62
+ /**
63
+ * Write a `DateTime64(P)`: 8-byte signed `Int64` count of `10^-P`-second ticks.
64
+ * Curried: `writeDateTime64(P)` returns the writer. The inverse of
65
+ * `readDateTime64`, which returns `[date, nanoseconds]` (date truncated to whole
66
+ * seconds, nanoseconds the sub-second remainder regardless of P). This recombines
67
+ * them: `ticks = seconds * 10^P + nanoseconds / 10^(9 - P)`. The reader floors
68
+ * seconds toward -inf with a non-negative remainder, so the seconds are computed
69
+ * with `Math.floor` on the cheap JS-number millisecond value (not bigint division,
70
+ * which truncates toward zero) — exact for negative instants too.
71
+ */
72
+ export function writeDateTime64(
73
+ precision: number,
74
+ ): Writer<[Date, Nanoseconds]> {
75
+ const scale = 10n ** BigInt(precision);
76
+ const nsPerTick = 10n ** BigInt(9 - precision);
77
+ return (sink, [date, nanoseconds]) => {
78
+ const seconds = BigInt(Math.floor(date.getTime() / 1000));
79
+ sink.buf.writeBigInt64LE(
80
+ seconds * scale + BigInt(nanoseconds) / nsPerTick,
81
+ reserve(sink, 8),
82
+ );
83
+ };
84
+ }
85
+
86
+ /**
87
+ * Write a `DateTime64(3)` (milliseconds) from a plain `Date` — the inverse of
88
+ * `readDateTime64P3`. P=3 is a `Date`'s own resolution, so the tick count is
89
+ * exactly `getTime()` in milliseconds.
90
+ */
91
+ export function writeDateTime64P3(sink: Sink, value: Date): void {
92
+ sink.buf.writeBigInt64LE(BigInt(value.getTime()), reserve(sink, 8));
93
+ }
94
+
95
+ /**
96
+ * Write a `DateTime64(6)` (microseconds) from `[date, microseconds]` — the
97
+ * inverse of `readDateTime64P6`. `ticks = seconds * 1_000_000 + micros`.
98
+ */
99
+ export function writeDateTime64P6(
100
+ sink: Sink,
101
+ [date, microseconds]: [Date, Microseconds],
102
+ ): void {
103
+ const seconds = BigInt(Math.floor(date.getTime() / 1000));
104
+ sink.buf.writeBigInt64LE(
105
+ seconds * 1_000_000n + BigInt(microseconds),
106
+ reserve(sink, 8),
107
+ );
108
+ }
109
+
110
+ /**
111
+ * Write a `DateTime64(9)` (nanoseconds) from `[date, nanoseconds]` — the inverse
112
+ * of `readDateTime64P9`. `ticks = seconds * 1_000_000_000 + nanos`.
113
+ */
114
+ export function writeDateTime64P9(
115
+ sink: Sink,
116
+ [date, nanoseconds]: [Date, Nanoseconds],
117
+ ): void {
118
+ const seconds = BigInt(Math.floor(date.getTime() / 1000));
119
+ sink.buf.writeBigInt64LE(
120
+ seconds * 1_000_000_000n + BigInt(nanoseconds),
121
+ reserve(sink, 8),
122
+ );
123
+ }
@@ -0,0 +1,51 @@
1
+ import { type Writer } from "./core.js";
2
+ import { type DecimalValue } from "../readers/decimals.js";
3
+ import {
4
+ writeInt32,
5
+ writeInt64,
6
+ writeInt128,
7
+ writeInt256,
8
+ } from "./integers.js";
9
+
10
+ /**
11
+ * Parse a fixed-point decimal string into a {@link DecimalValue} at the given
12
+ * `scale` — the inverse of `formatDecimal`. `"1.5000"` with scale 4 ->
13
+ * `[15000n, 4]`. A shorter fraction is right-padded with zeros to `scale`; a
14
+ * longer one is truncated (not rounded). Plug in only when you start from a
15
+ * string; if you already have the unscaled bigint, build the pair directly.
16
+ */
17
+ export function parseDecimal(text: string, scale: number): DecimalValue {
18
+ const neg = text.startsWith("-");
19
+ const body = neg ? text.slice(1) : text;
20
+ const dot = body.indexOf(".");
21
+ const intPart = dot < 0 ? body : body.slice(0, dot);
22
+ const fracPart = dot < 0 ? "" : body.slice(dot + 1);
23
+ const frac = (fracPart + "0".repeat(scale)).slice(0, scale);
24
+ let unscaled = BigInt((intPart || "0") + frac);
25
+ if (neg) unscaled = -unscaled;
26
+ return [unscaled, scale];
27
+ }
28
+
29
+ /**
30
+ * Write a `Decimal32(P, S)`: the `unscaled` part of a {@link DecimalValue} as a
31
+ * 4-byte little-endian signed integer (same wire as `Int32`). The inverse of
32
+ * `readDecimal32`; the `scale` lives in the type, so only `unscaled` is written
33
+ * (it must fit in `Int32`).
34
+ *
35
+ * `Decimal(P, S)` picks the width by precision P, exactly as the readers: P<=9 ->
36
+ * Decimal32, <=18 -> Decimal64, <=38 -> Decimal128, <=76 -> Decimal256.
37
+ */
38
+ export const writeDecimal32: Writer<DecimalValue> = (sink, [unscaled]) =>
39
+ writeInt32(sink, Number(unscaled));
40
+
41
+ /** Write a `Decimal64(P, S)`: 8-byte LE signed integer. Inverse of `readDecimal64`. */
42
+ export const writeDecimal64: Writer<DecimalValue> = (sink, [unscaled]) =>
43
+ writeInt64(sink, unscaled);
44
+
45
+ /** Write a `Decimal128(P, S)`: 16-byte LE signed integer. Inverse of `readDecimal128`. */
46
+ export const writeDecimal128: Writer<DecimalValue> = (sink, [unscaled]) =>
47
+ writeInt128(sink, unscaled);
48
+
49
+ /** Write a `Decimal256(P, S)`: 32-byte LE signed integer. Inverse of `readDecimal256`. */
50
+ export const writeDecimal256: Writer<DecimalValue> = (sink, [unscaled]) =>
51
+ writeInt256(sink, unscaled);
@@ -0,0 +1,18 @@
1
+ import { Sink, reserve } from "./core.js";
2
+
3
+ /**
4
+ * Write an `Enum8`: the value's underlying signed `Int8` (1 byte). Mirror of
5
+ * `readEnum8` — the name<->value map lives in the column type, so take the raw
6
+ * numeric value.
7
+ */
8
+ export function writeEnum8(sink: Sink, value: number): void {
9
+ sink.view.setInt8(reserve(sink, 1), value);
10
+ }
11
+
12
+ /**
13
+ * Write an `Enum16`: the value's underlying signed `Int16` (2 bytes,
14
+ * little-endian). Mirror of `readEnum16`.
15
+ */
16
+ export function writeEnum16(sink: Sink, value: number): void {
17
+ sink.view.setInt16(reserve(sink, 2), value, true);
18
+ }
@@ -0,0 +1,40 @@
1
+ import { Sink, reserve } from "./core.js";
2
+
3
+ /** Write a `Float32`: 4 bytes, little-endian IEEE 754. Mirror of `readFloat32`. */
4
+ export function writeFloat32(sink: Sink, value: number): void {
5
+ sink.view.setFloat32(reserve(sink, 4), value, true);
6
+ }
7
+
8
+ /** Write a `Float64`: 8 bytes, little-endian IEEE 754. Mirror of `readFloat64`. */
9
+ export function writeFloat64(sink: Sink, value: number): void {
10
+ sink.view.setFloat64(reserve(sink, 8), value, true);
11
+ }
12
+
13
+ /**
14
+ * Scratch view for narrowing a float32 to a `BFloat16`: BFloat16's 16 bits are
15
+ * the top half of an IEEE 754 float32, so we stage the float32 and take its high
16
+ * 16 bits back out.
17
+ */
18
+ const bf16Scratch = new DataView(new ArrayBuffer(4));
19
+
20
+ /**
21
+ * Write a `BFloat16`: 2 bytes, little-endian — the high 16 bits of `value`'s
22
+ * float32 representation (same 8-bit exponent, 7-bit mantissa). Mirror of
23
+ * `readBFloat16`: it widens a BFloat16 to a float32 by placing the bits in the
24
+ * top half, so here we stage the float32 and take that top half back.
25
+ *
26
+ * NOTE: this TRUNCATES the float32 mantissa to BFloat16's 7 bits (no rounding),
27
+ * matching the reader's exact inverse for values that originated as BFloat16. An
28
+ * arbitrary float32 loses precision, exactly as ClickHouse's own BFloat16 cast.
29
+ *
30
+ * NOTE: `bf16Scratch` is module-level shared state written-then-read; safe
31
+ * because the body is synchronous (do NOT introduce an `await`/`yield` between
32
+ * the `setFloat32` and the `getUint16`).
33
+ */
34
+ export function writeBFloat16(sink: Sink, value: number): void {
35
+ bf16Scratch.setFloat32(0, value, true);
36
+ // The float32's little-endian bytes are [lo16, hi16]; the high 16 bits at byte
37
+ // offset 2 are the BFloat16 payload.
38
+ const bits = bf16Scratch.getUint16(2, true);
39
+ sink.view.setUint16(reserve(sink, 2), bits, true);
40
+ }
@@ -0,0 +1,125 @@
1
+ import { type Writer, Sink, reserve } from "./core.js";
2
+ import { type Point } from "../readers/geo.js";
3
+ import { writeUInt8 } from "./integers.js";
4
+ import { writeUVarint } from "./varint.js";
5
+
6
+ /**
7
+ * Write a `Point`: `Tuple(Float64, Float64)` -> `[x, y]`. Inverse of `readPoint`.
8
+ * A single `reserve(16)` then two inlined `setFloat64`s — no per-coordinate
9
+ * `writeFloat64` call and only one bounds check.
10
+ */
11
+ export function writePoint(sink: Sink, [x, y]: Point): void {
12
+ const o = reserve(sink, 16);
13
+ sink.view.setFloat64(o, x, true);
14
+ sink.view.setFloat64(o + 8, y, true);
15
+ }
16
+
17
+ /**
18
+ * Write a `Ring`: `Array(Point)` — a LEB128 point count, then each point. The
19
+ * inverse of `readRing`; a SINGLE `reserve(16 * length)` covers the whole point
20
+ * block (one bounds check per ring, not per point), then the coordinates are
21
+ * written into it inline, mirroring the reader.
22
+ */
23
+ export function writeRing(sink: Sink, points: readonly Point[]): void {
24
+ writeUVarint(sink, points.length);
25
+ let p = reserve(sink, points.length * 16);
26
+ for (let i = 0; i < points.length; i++) {
27
+ const [x, y] = points[i]!;
28
+ sink.view.setFloat64(p, x, true);
29
+ sink.view.setFloat64(p + 8, y, true);
30
+ p += 16;
31
+ }
32
+ }
33
+
34
+ /** Write a `LineString`: `Array(Point)` (identical wire to a `Ring`). Inverse of `readLineString`. */
35
+ export function writeLineString(sink: Sink, points: readonly Point[]): void {
36
+ writeUVarint(sink, points.length);
37
+ let p = reserve(sink, points.length * 16);
38
+ for (let i = 0; i < points.length; i++) {
39
+ const [x, y] = points[i]!;
40
+ sink.view.setFloat64(p, x, true);
41
+ sink.view.setFloat64(p + 8, y, true);
42
+ p += 16;
43
+ }
44
+ }
45
+
46
+ /** Write a `Polygon`: `Array(Ring)` — outer ring first, then holes. Inverse of `readPolygon`. */
47
+ export function writePolygon(sink: Sink, rings: readonly Point[][]): void {
48
+ writeUVarint(sink, rings.length);
49
+ for (let i = 0; i < rings.length; i++) writeRing(sink, rings[i]!);
50
+ }
51
+
52
+ /** Write a `MultiLineString`: `Array(LineString)`. Inverse of `readMultiLineString`. */
53
+ export function writeMultiLineString(
54
+ sink: Sink,
55
+ lines: readonly Point[][],
56
+ ): void {
57
+ writeUVarint(sink, lines.length);
58
+ for (let i = 0; i < lines.length; i++) writeLineString(sink, lines[i]!);
59
+ }
60
+
61
+ /** Write a `MultiPolygon`: `Array(Polygon)`. Inverse of `readMultiPolygon`. */
62
+ export function writeMultiPolygon(
63
+ sink: Sink,
64
+ polygons: readonly Point[][][],
65
+ ): void {
66
+ writeUVarint(sink, polygons.length);
67
+ for (let i = 0; i < polygons.length; i++) writePolygon(sink, polygons[i]!);
68
+ }
69
+
70
+ /**
71
+ * A tagged `Geometry` value for {@link writeGeometry}: the alternative's
72
+ * `discriminant` paired with its value, or `null` for NULL. Like
73
+ * `readVariant`/`writeVariant`, `readGeometry` returns only the value — and the
74
+ * geo value shapes overlap (LineString and Ring are both `Point[]`,
75
+ * MultiLineString and Polygon both `Point[][]`) — so encode must be told which geo
76
+ * type it is via the discriminant.
77
+ *
78
+ * Discriminants (sorted by type name): LineString(0), MultiLineString(1),
79
+ * MultiPolygon(2), Point(3), Polygon(4), Ring(5); `0xFF` = NULL.
80
+ */
81
+ export type GeometryValue =
82
+ | readonly [discriminant: number, value: unknown]
83
+ | null;
84
+
85
+ /**
86
+ * Write a `Geometry`: a 1-byte discriminant then the chosen geo type's value. The
87
+ * inverse of `readGeometry` (a switch over the discriminant with each branch
88
+ * inlined). Takes a tagged {@link GeometryValue} because the value shapes are
89
+ * ambiguous on their own.
90
+ */
91
+ export const writeGeometry: Writer<GeometryValue> = (sink, value) => {
92
+ if (value === null) {
93
+ writeUInt8(sink, 0xff);
94
+ return;
95
+ }
96
+ // The discriminant byte is written inside each case, only after the switch
97
+ // has accepted it — an out-of-range value throws from `default` before the
98
+ // sink is advanced, so it never leaves a partially-written payload behind
99
+ // (mirrors `writeVariant`).
100
+ const [discriminant, geo] = value;
101
+ switch (discriminant) {
102
+ case 0:
103
+ writeUInt8(sink, discriminant);
104
+ return writeLineString(sink, geo as Point[]);
105
+ case 1:
106
+ writeUInt8(sink, discriminant);
107
+ return writeMultiLineString(sink, geo as Point[][]);
108
+ case 2:
109
+ writeUInt8(sink, discriminant);
110
+ return writeMultiPolygon(sink, geo as Point[][][]);
111
+ case 3:
112
+ writeUInt8(sink, discriminant);
113
+ return writePoint(sink, geo as Point);
114
+ case 4:
115
+ writeUInt8(sink, discriminant);
116
+ return writePolygon(sink, geo as Point[][]);
117
+ case 5:
118
+ writeUInt8(sink, discriminant);
119
+ return writeRing(sink, geo as Point[]);
120
+ default:
121
+ throw new RangeError(
122
+ `RowBinary: unknown Geometry discriminant ${discriminant}`,
123
+ );
124
+ }
125
+ };
@@ -0,0 +1,90 @@
1
+ import { Sink, reserve } from "./core.js";
2
+
3
+ // --- Writers: the encode mirror of the readers in `integers.ts`. Each writeX is
4
+ // the inverse of the matching readX.
5
+
6
+ /** Write a `UInt8`: 1 byte (0 .. 255). Mirror of `readUInt8`. */
7
+ export function writeUInt8(sink: Sink, value: number): void {
8
+ sink.buf[reserve(sink, 1)] = value;
9
+ }
10
+
11
+ /** Write an `Int8`: 1 byte, two's-complement signed (-128 .. 127). Mirror of `readInt8`. */
12
+ export function writeInt8(sink: Sink, value: number): void {
13
+ sink.view.setInt8(reserve(sink, 1), value);
14
+ }
15
+
16
+ /** Write a `UInt16`: 2 bytes, little-endian. Mirror of `readUInt16`. */
17
+ export function writeUInt16(sink: Sink, value: number): void {
18
+ sink.view.setUint16(reserve(sink, 2), value, true);
19
+ }
20
+
21
+ /** Write an `Int16`: 2 bytes, little-endian, two's-complement. Mirror of `readInt16`. */
22
+ export function writeInt16(sink: Sink, value: number): void {
23
+ sink.view.setInt16(reserve(sink, 2), value, true);
24
+ }
25
+
26
+ /** Write a `UInt32`: 4 bytes, little-endian. Mirror of `readUInt32`. */
27
+ export function writeUInt32(sink: Sink, value: number): void {
28
+ sink.view.setUint32(reserve(sink, 4), value, true);
29
+ }
30
+
31
+ /** Write an `Int32`: 4 bytes, little-endian, two's-complement. Mirror of `readInt32`. */
32
+ export function writeInt32(sink: Sink, value: number): void {
33
+ sink.view.setInt32(reserve(sink, 4), value, true);
34
+ }
35
+
36
+ /**
37
+ * Write a `UInt64`: 8 bytes, little-endian. Takes a `bigint` (mirror of
38
+ * `readUInt64`). Uses Node's `Buffer.writeBigUInt64LE`, which writes the 64-bit
39
+ * value straight from the bigint — no narrowing to a JS number. The value must be
40
+ * in `[0, 2^64)`.
41
+ */
42
+ export function writeUInt64(sink: Sink, value: bigint): void {
43
+ sink.buf.writeBigUInt64LE(value, reserve(sink, 8));
44
+ }
45
+
46
+ /**
47
+ * Write an `Int64`: 8 bytes, little-endian, two's-complement. Takes a `bigint`
48
+ * (mirror of `readInt64`). Uses Node's `Buffer.writeBigInt64LE`, writing the
49
+ * signed 64-bit value straight from the bigint (range `[-2^63, 2^63)`).
50
+ */
51
+ export function writeInt64(sink: Sink, value: bigint): void {
52
+ sink.buf.writeBigInt64LE(value, reserve(sink, 8));
53
+ }
54
+
55
+ /** Mask reducing a bigint word to its unsigned 64-bit (two's-complement) value. */
56
+ const MASK64 = (1n << 64n) - 1n;
57
+
58
+ /**
59
+ * Write a `UInt128`/`Int128`: 16 bytes, little-endian, as two 64-bit words (low
60
+ * then high). Each word is masked to 64 bits with `& MASK64` — a pure bigint
61
+ * operation that yields the correct unsigned (two's-complement) representation for
62
+ * negatives too — and written with Node's `Buffer.writeBigUInt64LE`, so this one
63
+ * function serves both `readUInt128` and `readInt128` without narrowing to a JS
64
+ * number.
65
+ */
66
+ export function writeUInt128(sink: Sink, value: bigint): void {
67
+ const o = reserve(sink, 16);
68
+ sink.buf.writeBigUInt64LE(value & MASK64, o);
69
+ sink.buf.writeBigUInt64LE((value >> 64n) & MASK64, o + 8);
70
+ }
71
+
72
+ /** Write an `Int128`: 16 bytes LE two's-complement. Same word layout as {@link writeUInt128}. */
73
+ export const writeInt128 = writeUInt128;
74
+
75
+ /**
76
+ * Write a `UInt256`/`Int256`: 32 bytes, little-endian, as four 64-bit words
77
+ * (least-significant first). Like {@link writeUInt128}, each word is masked with
78
+ * `& MASK64` and written via `Buffer.writeBigUInt64LE`, handling both unsigned and
79
+ * signed (two's complement) values straight from the bigint.
80
+ */
81
+ export function writeUInt256(sink: Sink, value: bigint): void {
82
+ const o = reserve(sink, 32);
83
+ sink.buf.writeBigUInt64LE(value & MASK64, o);
84
+ sink.buf.writeBigUInt64LE((value >> 64n) & MASK64, o + 8);
85
+ sink.buf.writeBigUInt64LE((value >> 128n) & MASK64, o + 16);
86
+ sink.buf.writeBigUInt64LE((value >> 192n) & MASK64, o + 24);
87
+ }
88
+
89
+ /** Write an `Int256`: 32 bytes LE two's-complement. Same word layout as {@link writeUInt256}. */
90
+ export const writeInt256 = writeUInt256;