@reddb-io/client-bun 1.26.4 → 1.26.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @reddb-io/client-bun
2
2
 
3
+ ## 1.26.6
4
+
5
+ ## 1.26.5
6
+
7
+ ### Patch Changes
8
+
9
+ - [#2478](https://github.com/reddb-io/reddb/pull/2478) [`5d41cee`](https://github.com/reddb-io/reddb/commit/5d41cee8f0d73e40e2620ef62c075b13073fd056) Thanks [@filipeforattini](https://github.com/filipeforattini)! - Accumulate fragmented RedWire input without repeated buffer concatenation and
10
+ copy completed payloads directly into owned result buffers. Release consumed
11
+ receive storage and reject pending/future queries on terminal framing, socket or
12
+ connection-close failures, while preserving per-query engine error recovery.
13
+
3
14
  ## 1.26.4
4
15
 
5
16
  ## 1.26.3
package/README.md CHANGED
@@ -28,6 +28,27 @@ Run the shared SDK smoke test and this package's actual TCP-client regression:
28
28
  REDDB_BINARY_PATH=/absolute/path/to/red bun run test
29
29
  ```
30
30
 
31
+ The receive path queues fragments and copies each completed payload into an owned
32
+ buffer. A terminal framing/socket failure or connection close rejects pending and
33
+ future queries. A server query error rejects its own query while the connection
34
+ remains usable. The existing 16 MiB frame limit and query return shapes apply.
35
+
36
+ From this directory in a repository checkout, compare receive processing against
37
+ a baseline checkout using the installed Bun runtime:
38
+
39
+ ```sh
40
+ REDDB_BENCH_BASELINE=/path/to/baseline REDDB_BENCH_OUTPUT=bun-receive.json \
41
+ bun run bench-receive.ts
42
+ bun ../js-client/bench/summarize.mjs bun-receive.json
43
+ ```
44
+
45
+ The benchmark alternates 20 paired samples of at least 20 ms. Scripted sockets
46
+ exercise production query encoding, receive parsing, UTF-8 and Promise delivery.
47
+ Input construction and exact response comparisons are outside timing. Separate
48
+ copy probes count top-level Buffer operations without double counting. Network,
49
+ database execution, the unchanged TCP callback ownership copy and total process
50
+ memory are outside these measurements. Timings never gate CI.
51
+
31
52
  <!-- contract-matrix:begin -->
32
53
  ## Public-surface support
33
54
 
package/index.ts CHANGED
@@ -64,7 +64,13 @@ export class RedDBError extends Error {
64
64
  export class RedDBConnection {
65
65
  private socket: ReturnType<typeof Bun.connect> extends Promise<infer T> ? T : never
66
66
  private pending: PendingRequest[] = []
67
- private buffer = Buffer.alloc(0)
67
+ private chunks: (Buffer | null)[] = []
68
+ private chunkIndex = 0
69
+ private chunkOffset = 0
70
+ private bufferedBytes = 0
71
+ private frameLength = 0
72
+ private frameType = 0
73
+ private failure: Error | null = null
68
74
  private nextCorr = 1n
69
75
  private serverFeatures = 0
70
76
 
@@ -73,39 +79,111 @@ export class RedDBConnection {
73
79
  }
74
80
 
75
81
  _onData(chunk: Buffer) {
76
- this.buffer = Buffer.concat([this.buffer, chunk])
82
+ if (this.failure || chunk.length === 0) return
83
+ this.chunks.push(chunk)
84
+ this.bufferedBytes += chunk.length
77
85
  this._tryResolve()
78
86
  }
79
87
 
88
+ _onClose() {
89
+ this._fail(new RedDBError('CONNECTION_CLOSED', 'redwire: connection closed'))
90
+ }
91
+
92
+ _onError(error: Error) {
93
+ this._fail(error)
94
+ }
95
+
80
96
  private _tryResolve() {
81
- while (this.buffer.length >= FRAME_HEADER_SIZE && this.pending.length > 0) {
82
- const totalLen = this.buffer.readUInt32LE(0)
83
- if (totalLen < FRAME_HEADER_SIZE || totalLen > MAX_FRAME_SIZE) {
84
- const { reject } = this.pending.shift()!
85
- reject(new RedDBError('FRAME_INVALID_LENGTH', `length=${totalLen}`))
97
+ while (this.bufferedBytes >= FRAME_HEADER_SIZE && this.pending.length > 0) {
98
+ let payload: Buffer
99
+ let msgType: number
100
+ try {
101
+ if (this.frameLength === 0) this._readHeader()
102
+ if (this.bufferedBytes < this.frameLength) break
103
+ msgType = this.frameType
104
+ this._consume(FRAME_HEADER_SIZE)
105
+ payload = Buffer.allocUnsafe(this.frameLength - FRAME_HEADER_SIZE)
106
+ this._consume(payload.length, payload)
107
+ this.frameLength = 0
108
+ } catch (error) {
109
+ this._fail(error as Error)
86
110
  return
87
111
  }
88
- if (this.buffer.length < totalLen) break
89
-
90
- const msgType = this.buffer[4]
91
- const payload = this.buffer.subarray(FRAME_HEADER_SIZE, totalLen)
92
- this.buffer = this.buffer.subarray(totalLen)
93
-
94
112
  const { resolve, reject } = this.pending.shift()!
95
113
  if (msgType === MSG_ERROR || msgType === MSG_AUTH_FAIL) {
96
114
  reject(new RedDBError('ENGINE', payload.toString('utf8')))
97
115
  } else {
98
- resolve({ type: msgType, payload: Buffer.from(payload) })
116
+ resolve({ type: msgType, payload })
99
117
  }
100
118
  }
101
119
  }
102
120
 
121
+ private _readHeader() {
122
+ const first = this.chunks[this.chunkIndex]!
123
+ let header = first
124
+ let offset = this.chunkOffset
125
+ if (first.length - offset < 5) {
126
+ // Only length and kind are needed; the rest of the 16-byte header is discarded later.
127
+ header = Buffer.allocUnsafe(5)
128
+ let written = 0
129
+ for (let index = this.chunkIndex; written < header.length; index++) {
130
+ const chunk = this.chunks[index]!
131
+ const start = index === this.chunkIndex ? this.chunkOffset : 0
132
+ const count = Math.min(header.length - written, chunk.length - start)
133
+ written += chunk.copy(header, written, start, start + count)
134
+ }
135
+ offset = 0
136
+ }
137
+ const length = header.readUInt32LE(offset)
138
+ if (length < FRAME_HEADER_SIZE || length > MAX_FRAME_SIZE) {
139
+ throw new RedDBError('FRAME_INVALID_LENGTH', `length=${length}`)
140
+ }
141
+ this.frameLength = length
142
+ this.frameType = header[offset + 4]
143
+ }
144
+
145
+ private _consume(length: number, output?: Buffer) {
146
+ if (length === 0) return
147
+ let written = 0
148
+ while (written < length) {
149
+ const chunk = this.chunks[this.chunkIndex]!
150
+ const count = Math.min(length - written, chunk.length - this.chunkOffset)
151
+ if (output) chunk.copy(output, written, this.chunkOffset, this.chunkOffset + count)
152
+ written += count
153
+ this.chunkOffset += count
154
+ if (this.chunkOffset === chunk.length) {
155
+ this.chunks[this.chunkIndex++] = null
156
+ this.chunkOffset = 0
157
+ }
158
+ }
159
+ this.bufferedBytes -= length
160
+ if (this.chunkIndex * 2 >= this.chunks.length) {
161
+ this.chunks = this.chunks.slice(this.chunkIndex)
162
+ this.chunkIndex = 0
163
+ }
164
+ }
165
+
166
+ private _fail(error: Error) {
167
+ if (this.failure) return
168
+ this.failure = error
169
+ this.chunks = []
170
+ this.chunkIndex = 0
171
+ this.chunkOffset = 0
172
+ this.bufferedBytes = 0
173
+ this.frameLength = 0
174
+ while (this.pending.length > 0) this.pending.shift()!.reject(error)
175
+ try { this.socket.end() } catch { /* The peer may already have closed the socket. */ }
176
+ }
177
+
103
178
  private _send(msgType: number, payload: Buffer): Promise<{ type: number; payload: Buffer }> {
179
+ if (this.failure) return Promise.reject(this.failure)
104
180
  return new Promise((resolve, reject) => {
105
- this.pending.push({ resolve, reject })
106
181
  const corr = this.nextCorr
107
182
  this.nextCorr += 1n
108
- this.socket.write(encodeFrame(msgType, corr, payload))
183
+ const request = encodeFrame(msgType, corr, payload)
184
+ this.pending.push({ resolve, reject })
185
+ try { this.socket.write(request) }
186
+ catch (error) { this._fail(error as Error) }
109
187
  })
110
188
  }
111
189
 
@@ -200,11 +278,12 @@ export class RedDBConnection {
200
278
  }
201
279
 
202
280
  close() {
281
+ if (this.failure) return
203
282
  try {
204
283
  this.socket.write(encodeFrame(MSG_BYE, this.nextCorr, Buffer.alloc(0)))
205
284
  this.nextCorr += 1n
206
285
  } finally {
207
- this.socket.end()
286
+ this._onClose()
208
287
  }
209
288
  }
210
289
  }
@@ -232,6 +311,12 @@ async function connectWithOptions(addr: string, tls?: { ca?: string; rejectUnaut
232
311
  const [host, portStr] = addr.split(':')
233
312
  const port = parseInt(portStr, 10)
234
313
  let connRef: RedDBConnection | null = null
314
+ let earlyFailure: Error | null = null
315
+ const fail = (error: Error) => {
316
+ if (connRef) connRef._onError(error)
317
+ else earlyFailure ??= error
318
+ }
319
+ const closed = () => fail(new RedDBError('CONNECTION_CLOSED', 'redwire: connection closed'))
235
320
 
236
321
  const socket = await Bun.connect({
237
322
  hostname: host,
@@ -242,15 +327,18 @@ async function connectWithOptions(addr: string, tls?: { ca?: string; rejectUnaut
242
327
  connRef?._onData(Buffer.from(data))
243
328
  },
244
329
  error(_socket, error) {
245
- console.error('RedDB wire error:', error)
330
+ fail(error)
246
331
  },
247
- close() {},
332
+ close: closed,
333
+ end: closed,
248
334
  open() {},
249
335
  },
250
336
  })
251
337
 
252
338
  connRef = new RedDBConnection(socket)
253
- await connRef._handshake()
339
+ if (earlyFailure) connRef._onError(earlyFailure)
340
+ try { await connRef._handshake() }
341
+ catch (error) { connRef._onError(error as Error); throw error }
254
342
  return connRef
255
343
  }
256
344
 
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "@reddb-io/client-bun",
3
- "version": "1.26.4",
3
+ "version": "1.26.6",
4
4
  "description": "RedDB wire protocol client for Bun — ultra-fast native TCP",
5
5
  "main": "index.ts",
6
+ "files": [
7
+ "index.ts",
8
+ "README.md",
9
+ "CHANGELOG.md"
10
+ ],
6
11
  "license": "MIT",
7
12
  "keywords": [
8
13
  "reddb",
@@ -12,6 +17,6 @@
12
17
  "wire-protocol"
13
18
  ],
14
19
  "scripts": {
15
- "test": "bun run params.test.ts && bun run wire-legacy.test.ts && bun run wire.test.ts"
20
+ "test": "bun test frame-reader.test.ts socket-close.test.ts && bun run params.test.ts && bun run wire-legacy.test.ts && bun run wire.test.ts"
16
21
  }
17
22
  }
package/bench.ts DELETED
@@ -1,66 +0,0 @@
1
- #!/usr/bin/env bun
2
- /**
3
- * RedDB Bun Wire Protocol Benchmark
4
- * Usage: bun run bench.ts [host:port]
5
- */
6
- import { connect } from './index'
7
-
8
- const ADDR = process.argv[2] || '127.0.0.1:5050'
9
- const N = 5000
10
- const POINT_LOOKUPS = 200
11
- const RANGE_QUERIES = 50
12
- const FILTER_QUERIES = 50
13
-
14
- function rng(seed: number) {
15
- let s = seed
16
- return () => { s = (s * 1103515245 + 12345) & 0x7fffffff; return s }
17
- }
18
-
19
- const rand = rng(42)
20
- const conn = await connect(ADDR)
21
- console.log(`Connected to ${ADDR}`)
22
-
23
- // Insert
24
- const records: string[] = []
25
- const cities = ['NYC', 'London', 'Tokyo', 'Paris', 'Berlin']
26
- for (let i = 0; i < N; i++) {
27
- records.push(JSON.stringify({
28
- fields: { id: i + 1, name: `User_${i}`, age: 18 + (rand() % 63), city: cities[rand() % 5], score: (rand() % 10000) / 100 }
29
- }))
30
- }
31
-
32
- let t0 = performance.now()
33
- const count = await conn.bulkInsert('users', records)
34
- let ms = performance.now() - t0
35
- console.log(` insert_bulk: ${(N / ms * 1000).toFixed(0).padStart(8)} ops/sec (${ms.toFixed(0)}ms) [${count} rows]`)
36
-
37
- // Point lookups
38
- const lids = Array.from({ length: POINT_LOOKUPS }, () => 1 + (rand() % N))
39
- t0 = performance.now()
40
- for (const rid of lids) {
41
- await conn.queryRaw(`SELECT * FROM users WHERE _entity_id = ${rid}`)
42
- }
43
- ms = performance.now() - t0
44
- console.log(` select_point: ${(POINT_LOOKUPS / ms * 1000).toFixed(0).padStart(8)} ops/sec (${ms.toFixed(0)}ms)`)
45
-
46
- // Range queries
47
- const rqs = Array.from({ length: RANGE_QUERIES }, () => {
48
- const l = 18 + (rand() % 53); return [l, l + 10] as const
49
- })
50
- t0 = performance.now()
51
- for (const [l, h] of rqs) {
52
- await conn.queryRaw(`SELECT * FROM users WHERE age BETWEEN ${l} AND ${h}`)
53
- }
54
- ms = performance.now() - t0
55
- console.log(` select_range: ${(RANGE_QUERIES / ms * 1000).toFixed(0).padStart(8)} ops/sec (${ms.toFixed(0)}ms)`)
56
-
57
- // Filtered queries
58
- const fqs = Array.from({ length: FILTER_QUERIES }, () => [cities[rand() % 5], 18 + (rand() % 43)] as const)
59
- t0 = performance.now()
60
- for (const [c, a] of fqs) {
61
- await conn.queryRaw(`SELECT * FROM users WHERE city = '${c}' AND age > ${a}`)
62
- }
63
- ms = performance.now() - t0
64
- console.log(` select_filtered: ${(FILTER_QUERIES / ms * 1000).toFixed(0).padStart(8)} ops/sec (${ms.toFixed(0)}ms)`)
65
-
66
- conn.close()
package/params.test.ts DELETED
@@ -1,73 +0,0 @@
1
- import { existsSync } from 'node:fs'
2
- import { dirname, resolve } from 'node:path'
3
- import { fileURLToPath } from 'node:url'
4
-
5
- import { connect } from '../js/src/index.js'
6
-
7
- const HERE = dirname(fileURLToPath(import.meta.url))
8
- const DEFAULT_BINARY = resolve(HERE, '..', '..', 'target', 'debug', 'red')
9
- const BINARY = process.env.REDDB_BINARY_PATH || DEFAULT_BINARY
10
-
11
- function assert(condition: unknown, message: string): asserts condition {
12
- if (!condition) throw new Error(message)
13
- }
14
-
15
- function assertEqual<T>(actual: T, expected: T, message: string) {
16
- if (actual !== expected) {
17
- throw new Error(
18
- `${message}: expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`,
19
- )
20
- }
21
- }
22
-
23
- if (!existsSync(BINARY)) {
24
- console.log(`SKIP: binary not found at ${BINARY}`)
25
- process.exit(0)
26
- }
27
-
28
- const db = await connect('memory://', { binary: BINARY })
29
- try {
30
- await db.query('CREATE TABLE bun_params (id INTEGER, name TEXT)')
31
- await db.query('INSERT INTO bun_params (id, name) VALUES ($1, $2)', [1, 'Bun'])
32
- await db.query('INSERT INTO bun_params (id, name) VALUES ($1, $2)', [2, 'Node'])
33
-
34
- const selected = await db.query(
35
- 'SELECT * FROM bun_params WHERE id = $1 AND name = $2',
36
- [1, 'Bun'],
37
- )
38
- assert(Array.isArray(selected.rows), 'SELECT rows should be an array')
39
- assertEqual(selected.rows.length, 1, 'SELECT should match one row')
40
- assertEqual(selected.rows[0].name, 'Bun', 'SELECT should bind text and int params')
41
-
42
- await db.query(
43
- 'INSERT INTO bun_embeddings VECTOR (dense, content) VALUES ($1, $2)',
44
- [new Float32Array([1.0, 0.0]), 'bun vector'],
45
- )
46
- await db.query(
47
- 'INSERT INTO bun_embeddings VECTOR (dense, content) VALUES ($1, $2)',
48
- [new Float32Array([0.0, 1.0]), 'other vector'],
49
- )
50
-
51
- const similar = await db.query(
52
- 'SEARCH SIMILAR $1 COLLECTION bun_embeddings LIMIT 1',
53
- [new Float32Array([1.0, 0.0])],
54
- )
55
- assert(Array.isArray(similar.rows), 'SEARCH rows should be an array')
56
- assertEqual(similar.rows.length, 1, 'SEARCH should match one vector row')
57
- assertEqual(similar.rows[0].score, 1, 'SEARCH should bind Float32Array vector')
58
-
59
- const fractional = new Float32Array([0.8, 0.6])
60
- await db.query('INSERT INTO fractional_vectors VECTOR (dense, content) VALUES ($1, $2)',
61
- [fractional, 'fractional embedding'])
62
- const fractionalMatch = await db.query('SEARCH SIMILAR $1 COLLECTION fractional_vectors LIMIT 1',
63
- [fractional])
64
- assertEqual(fractionalMatch.rows.length, 1, 'fractional vector should remain searchable')
65
- assert(Math.abs(fractionalMatch.rows[0].score - 1) < 1e-6, 'identical fractional vectors should match')
66
- const axisMatch = await db.query('SEARCH SIMILAR $1 COLLECTION fractional_vectors LIMIT 1',
67
- [new Float32Array([1, 0])])
68
- assert(Math.abs(axisMatch.rows[0].score - 0.8) < 1e-6, 'fractional coordinates survive binding')
69
- } finally {
70
- await db.close()
71
- }
72
-
73
- console.log('ok shared SDK parameterized queries run under Bun')
@@ -1,66 +0,0 @@
1
- import { strict as assert } from 'node:assert'
2
- import { createServer } from 'node:net'
3
- import { connect, RedDBError } from './index'
4
-
5
- // The old protocol is intentionally summary-only. A small protocol peer lets
6
- // us exercise capability negotiation without installing an obsolete engine.
7
- const queries: number[] = []
8
- const server = createServer((socket) => {
9
- socket.setTimeout(5_000, () => socket.destroy())
10
- let input = Buffer.alloc(0)
11
- let magicReceived = false
12
- socket.on('data', (chunk) => {
13
- input = Buffer.concat([input, chunk])
14
- if (!magicReceived) {
15
- if (input.length < 2) return
16
- assert.deepEqual([...input.subarray(0, 2)], [0xfe, 1])
17
- input = input.subarray(2)
18
- magicReceived = true
19
- }
20
- while (input.length >= 16) {
21
- const length = input.readUInt32LE(0)
22
- assert(length >= 16 && length <= 16 * 1024 * 1024)
23
- if (input.length < length) return
24
- const frame = input.subarray(0, length)
25
- input = input.subarray(length)
26
- const kind = frame[4]
27
- if (kind === 0x16) { socket.end(); return }
28
- let responseKind: number
29
- let body: object
30
- if (kind === 0x10) {
31
- responseKind = 0x11
32
- body = { auth: 'anonymous', features: 0 }
33
- } else if (kind === 0x13) {
34
- responseKind = 0x14
35
- body = { features: 0 }
36
- } else {
37
- queries.push(kind)
38
- responseKind = 0x02
39
- body = { ok: true, affected: 0, statement: 'select' }
40
- }
41
- const payload = Buffer.from(JSON.stringify(body))
42
- const response = Buffer.alloc(16 + payload.length)
43
- response.writeUInt32LE(response.length, 0)
44
- response[4] = responseKind
45
- frame.copy(response, 8, 8, 16)
46
- payload.copy(response, 16)
47
- socket.write(response)
48
- }
49
- })
50
- })
51
- await new Promise<void>((resolve) => server.listen(0, '127.0.0.1', resolve))
52
- const address = server.address()
53
- assert(address && typeof address === 'object')
54
- let db: Awaited<ReturnType<typeof connect>> | undefined
55
- try {
56
- db = await connect(`127.0.0.1:${address.port}`)
57
- assert.equal(db.supportsParams(), false)
58
- assert.deepEqual(await db.queryParsed('SELECT 42'), { ok: true, affected: 0, statement: 'select' })
59
- await assert.rejects(db.query('SELECT $1', [42]),
60
- (error) => error instanceof RedDBError && error.code === 'PARAMS_UNSUPPORTED')
61
- assert.deepEqual(queries, [0x01])
62
- console.log('ok Bun legacy capability fallback')
63
- } finally {
64
- db?.close()
65
- await new Promise<void>((resolve) => server.close(() => resolve()))
66
- }
package/wire.test.ts DELETED
@@ -1,58 +0,0 @@
1
- import { strict as assert } from 'node:assert'
2
- import { mkdtempSync, rmSync } from 'node:fs'
3
- import { tmpdir } from 'node:os'
4
- import { join, resolve } from 'node:path'
5
- import { createServer } from 'node:net'
6
- import { connect } from './index'
7
-
8
- // Exercise this package's TCP client against a real server. An absent binary
9
- // is a test failure: silently skipping hid the unbound-query regression.
10
- const binary = process.env.REDDB_BINARY_PATH || resolve(import.meta.dir, '../../target/debug/red')
11
- const reservation = createServer()
12
- await new Promise<void>((resolve, reject) => {
13
- reservation.once('error', reject)
14
- reservation.listen(0, '127.0.0.1', resolve)
15
- })
16
- const address = reservation.address()
17
- assert(address && typeof address === 'object')
18
- const endpoint = `127.0.0.1:${address.port}`
19
- await new Promise<void>((resolve) => reservation.close(() => resolve()))
20
- const directory = mkdtempSync(join(tmpdir(), 'reddb-bun-wire-'))
21
- const server = Bun.spawn([binary, 'server', '--path', join(directory, 'db.rdb'),
22
- '--bind', endpoint, '--no-auth', '--no-log-file'], { stdout: 'ignore', stderr: 'inherit' })
23
- let db: Awaited<ReturnType<typeof connect>> | undefined
24
- try {
25
- const deadline = Date.now() + 30_000
26
- while (Date.now() < deadline) {
27
- if (server.exitCode !== null) throw new Error(`server exited ${server.exitCode}`)
28
- try {
29
- const response = await fetch(`http://${endpoint}/health`, { signal: AbortSignal.timeout(500) })
30
- if (response.ok) break
31
- } catch {}
32
- await Bun.sleep(100)
33
- }
34
- db = await connect(endpoint)
35
- assert(db.supportsParams())
36
- await db.query('CREATE TABLE wire_rows (id INTEGER, name TEXT)')
37
- const inserted = await db.queryParsed("INSERT INTO wire_rows (id,name) VALUES (1,'Bun')")
38
- assert.equal(inserted.affected_rows, 1)
39
- const unbound = await db.queryParsed('SELECT id,name FROM wire_rows')
40
- const bound = await db.queryParsed('SELECT id,name FROM wire_rows WHERE id=$1', [1])
41
- assert.deepEqual(unbound.result.records.map((record: any) => record.values), [{ id: 1, name: 'Bun' }])
42
- assert.deepEqual(unbound.result.records.map((record: any) => record.values), bound.result.records.map((record: any) => record.values))
43
- assert.deepEqual((await db.queryParsed('SELECT id,name FROM wire_rows', [])).result.records.map((record: any) => record.values), bound.result.records.map((record: any) => record.values))
44
- assert.deepEqual((await db.queryParsed('SELECT id FROM wire_rows WHERE id=2')).result.records, [])
45
- await assert.rejects(db.query('SELECT * FROM absent_wire_table'))
46
- const cli = Bun.spawnSync([binary, 'query', '--bind', endpoint, '--json',
47
- "INSERT INTO wire_rows (id,name) VALUES (2,'CLI')"])
48
- assert.equal(cli.exitCode, 0, cli.stderr.toString())
49
- const insertedByCli = JSON.parse(cli.stdout.toString()).data
50
- assert.equal(insertedByCli.affected, 1)
51
- assert.equal(insertedByCli.statement, 'insert')
52
- console.log('ok Bun wire bound/unbound result contract')
53
- } finally {
54
- db?.close()
55
- server.kill()
56
- await server.exited
57
- rmSync(directory, { recursive: true, force: true })
58
- }