@avidian/duckdb-bun 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (231) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +284 -2
  3. package/package.json +95 -4
  4. package/src/bindings/api/appender.ts +288 -0
  5. package/src/bindings/api/data-chunk.ts +58 -0
  6. package/src/bindings/api/database.ts +232 -0
  7. package/src/bindings/api/datetime.ts +158 -0
  8. package/src/bindings/api/index.ts +13 -0
  9. package/src/bindings/api/logical-types.ts +227 -0
  10. package/src/bindings/api/query.ts +616 -0
  11. package/src/bindings/api/scalar-function.ts +274 -0
  12. package/src/bindings/api/table-function.ts +339 -0
  13. package/src/bindings/api/values.ts +546 -0
  14. package/src/bindings/api/vector.ts +266 -0
  15. package/src/bindings/generated/capi.ts +695 -0
  16. package/src/bindings/generated/contract.ts +162 -0
  17. package/src/bindings/index.ts +10 -0
  18. package/src/bindings/runtime/convert.ts +307 -0
  19. package/src/bindings/runtime/diagnostics.ts +45 -0
  20. package/src/bindings/runtime/dispatch.ts +200 -0
  21. package/src/bindings/runtime/exception.ts +103 -0
  22. package/src/bindings/runtime/flags.ts +19 -0
  23. package/src/bindings/runtime/handle.ts +217 -0
  24. package/src/bindings/runtime/kinds.ts +53 -0
  25. package/src/bindings/runtime/marshal.ts +190 -0
  26. package/src/bindings/runtime/native.ts +150 -0
  27. package/src/bindings/runtime/ring.ts +54 -0
  28. package/src/bindings/runtime/scope.ts +133 -0
  29. package/src/bindings/runtime/trace.ts +65 -0
  30. package/src/connection.ts +35 -0
  31. package/src/database.ts +27 -0
  32. package/src/errors.ts +30 -0
  33. package/src/file-assets.d.ts +10 -0
  34. package/src/index.ts +17 -0
  35. package/src/library.ts +83 -0
  36. package/src/neo.ts +16 -0
  37. package/src/reader.ts +47 -0
  38. package/src/statement.ts +61 -0
  39. package/src/types.ts +52 -0
  40. package/src/version.ts +6 -0
  41. package/vendor/node-api/LICENSE +7 -0
  42. package/vendor/node-api/PROVENANCE.md +54 -0
  43. package/vendor/node-api/node-bindings.d.ts +1669 -0
  44. package/vendor/node-api/src/DuckDBAppender.ts +237 -0
  45. package/vendor/node-api/src/DuckDBClientContext.ts +13 -0
  46. package/vendor/node-api/src/DuckDBConnection.ts +315 -0
  47. package/vendor/node-api/src/DuckDBDataChunk.ts +412 -0
  48. package/vendor/node-api/src/DuckDBDataChunkWriter.ts +144 -0
  49. package/vendor/node-api/src/DuckDBExtractedStatements.ts +37 -0
  50. package/vendor/node-api/src/DuckDBInstance.ts +35 -0
  51. package/vendor/node-api/src/DuckDBInstanceCache.ts +29 -0
  52. package/vendor/node-api/src/DuckDBLogicalType.ts +446 -0
  53. package/vendor/node-api/src/DuckDBMaterializedResult.ts +39 -0
  54. package/vendor/node-api/src/DuckDBPendingResult.ts +83 -0
  55. package/vendor/node-api/src/DuckDBPreparedStatement.ts +376 -0
  56. package/vendor/node-api/src/DuckDBPreparedStatementCollection.ts +5 -0
  57. package/vendor/node-api/src/DuckDBPreparedStatementWeakRefCollection.ts +31 -0
  58. package/vendor/node-api/src/DuckDBResult.ts +403 -0
  59. package/vendor/node-api/src/DuckDBResultReader.ts +283 -0
  60. package/vendor/node-api/src/DuckDBScalarFunction.ts +149 -0
  61. package/vendor/node-api/src/DuckDBScalarFunctionBindInfo.ts +34 -0
  62. package/vendor/node-api/src/DuckDBScalarFunctionInfo.ts +34 -0
  63. package/vendor/node-api/src/DuckDBScalarFunctionInitInfo.ts +35 -0
  64. package/vendor/node-api/src/DuckDBTableFunction.ts +149 -0
  65. package/vendor/node-api/src/DuckDBTableFunctionBindInfo.ts +56 -0
  66. package/vendor/node-api/src/DuckDBTableFunctionInfo.ts +36 -0
  67. package/vendor/node-api/src/DuckDBTableFunctionInitInfo.ts +52 -0
  68. package/vendor/node-api/src/DuckDBType.ts +1 -0
  69. package/vendor/node-api/src/DuckDBTypeId.ts +46 -0
  70. package/vendor/node-api/src/DuckDBValueConverter.ts +23 -0
  71. package/vendor/node-api/src/DuckDBValueConverters.ts +461 -0
  72. package/vendor/node-api/src/DuckDBVector.ts +1 -0
  73. package/vendor/node-api/src/JS.ts +11 -0
  74. package/vendor/node-api/src/JSDuckDBValueConverter.ts +85 -0
  75. package/vendor/node-api/src/JSInputTypeForTypeId.ts +101 -0
  76. package/vendor/node-api/src/JSToDuckDBValueConverter.ts +136 -0
  77. package/vendor/node-api/src/JSTypeForTypeId.ts +78 -0
  78. package/vendor/node-api/src/Json.ts +7 -0
  79. package/vendor/node-api/src/JsonDuckDBValueConverter.ts +72 -0
  80. package/vendor/node-api/src/JsonTypeForTypeId.ts +86 -0
  81. package/vendor/node-api/src/ToDuckDBValueConverter.ts +68 -0
  82. package/vendor/node-api/src/ToDuckDBValueConverters.ts +303 -0
  83. package/vendor/node-api/src/configurationOptionDescriptions.ts +13 -0
  84. package/vendor/node-api/src/conversion/bytesFromString.ts +5 -0
  85. package/vendor/node-api/src/conversion/dateTimeStringConversion.ts +343 -0
  86. package/vendor/node-api/src/conversion/displayStringForDuckDBValue.ts +12 -0
  87. package/vendor/node-api/src/conversion/stringFromBlob.ts +48 -0
  88. package/vendor/node-api/src/conversion/stringFromDecimal.ts +129 -0
  89. package/vendor/node-api/src/conversion/varintDecode.ts +42 -0
  90. package/vendor/node-api/src/convertColumnsFromChunks.ts +28 -0
  91. package/vendor/node-api/src/convertColumnsObjectFromChunks.ts +29 -0
  92. package/vendor/node-api/src/convertRowObjectsFromChunks.ts +21 -0
  93. package/vendor/node-api/src/convertRowsFromChunks.ts +16 -0
  94. package/vendor/node-api/src/createConfig.ts +14 -0
  95. package/vendor/node-api/src/createDuckDBValueConverter.ts +17 -0
  96. package/vendor/node-api/src/createResult.ts +15 -0
  97. package/vendor/node-api/src/createValue.ts +355 -0
  98. package/vendor/node-api/src/duckdb.ts +51 -0
  99. package/vendor/node-api/src/enums.ts +7 -0
  100. package/vendor/node-api/src/getColumnsFromChunks.ts +13 -0
  101. package/vendor/node-api/src/getColumnsObjectFromChunks.ts +16 -0
  102. package/vendor/node-api/src/getRowObjectsFromChunks.ts +13 -0
  103. package/vendor/node-api/src/getRowsFromChunks.ts +12 -0
  104. package/vendor/node-api/src/index.ts +3 -0
  105. package/vendor/node-api/src/readValue.ts +31 -0
  106. package/vendor/node-api/src/sleep.ts +5 -0
  107. package/vendor/node-api/src/sql.ts +7 -0
  108. package/vendor/node-api/src/tsconfig.json +7 -0
  109. package/vendor/node-api/src/typeForValue.ts +144 -0
  110. package/vendor/node-api/src/types/BaseDuckDBType.ts +31 -0
  111. package/vendor/node-api/src/types/DuckDBAnyType.ts +13 -0
  112. package/vendor/node-api/src/types/DuckDBArrayType.ts +43 -0
  113. package/vendor/node-api/src/types/DuckDBBigIntType.ts +21 -0
  114. package/vendor/node-api/src/types/DuckDBBigNumType.ts +21 -0
  115. package/vendor/node-api/src/types/DuckDBBitType.ts +13 -0
  116. package/vendor/node-api/src/types/DuckDBBlobType.ts +13 -0
  117. package/vendor/node-api/src/types/DuckDBBooleanType.ts +13 -0
  118. package/vendor/node-api/src/types/DuckDBDateType.ts +29 -0
  119. package/vendor/node-api/src/types/DuckDBDecimalType.ts +46 -0
  120. package/vendor/node-api/src/types/DuckDBDoubleType.ts +21 -0
  121. package/vendor/node-api/src/types/DuckDBEnumType.ts +111 -0
  122. package/vendor/node-api/src/types/DuckDBFloatType.ts +21 -0
  123. package/vendor/node-api/src/types/DuckDBGeometryType.ts +36 -0
  124. package/vendor/node-api/src/types/DuckDBHugeIntType.ts +21 -0
  125. package/vendor/node-api/src/types/DuckDBIntegerLiteralType.ts +13 -0
  126. package/vendor/node-api/src/types/DuckDBIntegerType.ts +21 -0
  127. package/vendor/node-api/src/types/DuckDBIntervalType.ts +13 -0
  128. package/vendor/node-api/src/types/DuckDBListType.ts +35 -0
  129. package/vendor/node-api/src/types/DuckDBMapType.ts +47 -0
  130. package/vendor/node-api/src/types/DuckDBSQLNullType.ts +17 -0
  131. package/vendor/node-api/src/types/DuckDBSmallIntType.ts +21 -0
  132. package/vendor/node-api/src/types/DuckDBStringLiteralType.ts +13 -0
  133. package/vendor/node-api/src/types/DuckDBStructType.ts +104 -0
  134. package/vendor/node-api/src/types/DuckDBTimeNSType.ts +20 -0
  135. package/vendor/node-api/src/types/DuckDBTimeTZType.ts +23 -0
  136. package/vendor/node-api/src/types/DuckDBTimeType.ts +20 -0
  137. package/vendor/node-api/src/types/DuckDBTimestampMillisecondsType.ts +29 -0
  138. package/vendor/node-api/src/types/DuckDBTimestampNanosecondsType.ts +29 -0
  139. package/vendor/node-api/src/types/DuckDBTimestampSecondsType.ts +29 -0
  140. package/vendor/node-api/src/types/DuckDBTimestampTZNanosecondsType.ts +32 -0
  141. package/vendor/node-api/src/types/DuckDBTimestampTZType.ts +32 -0
  142. package/vendor/node-api/src/types/DuckDBTimestampType.ts +32 -0
  143. package/vendor/node-api/src/types/DuckDBTinyIntType.ts +21 -0
  144. package/vendor/node-api/src/types/DuckDBType.ts +86 -0
  145. package/vendor/node-api/src/types/DuckDBUBigIntType.ts +21 -0
  146. package/vendor/node-api/src/types/DuckDBUHugeIntType.ts +21 -0
  147. package/vendor/node-api/src/types/DuckDBUIntegerType.ts +21 -0
  148. package/vendor/node-api/src/types/DuckDBUSmallIntType.ts +21 -0
  149. package/vendor/node-api/src/types/DuckDBUTinyIntType.ts +21 -0
  150. package/vendor/node-api/src/types/DuckDBUUIDType.ts +20 -0
  151. package/vendor/node-api/src/types/DuckDBUnionType.ts +74 -0
  152. package/vendor/node-api/src/types/DuckDBVarCharType.ts +13 -0
  153. package/vendor/node-api/src/types/DuckDBVariantType.ts +13 -0
  154. package/vendor/node-api/src/types/index.ts +44 -0
  155. package/vendor/node-api/src/values/DuckDBArrayValue.ts +18 -0
  156. package/vendor/node-api/src/values/DuckDBBitValue.ts +117 -0
  157. package/vendor/node-api/src/values/DuckDBBlobValue.ts +26 -0
  158. package/vendor/node-api/src/values/DuckDBDateValue.ts +43 -0
  159. package/vendor/node-api/src/values/DuckDBDecimalValue.ts +44 -0
  160. package/vendor/node-api/src/values/DuckDBGeometryValue.ts +30 -0
  161. package/vendor/node-api/src/values/DuckDBIntervalValue.ts +22 -0
  162. package/vendor/node-api/src/values/DuckDBListValue.ts +18 -0
  163. package/vendor/node-api/src/values/DuckDBMapValue.ts +25 -0
  164. package/vendor/node-api/src/values/DuckDBStructValue.ts +22 -0
  165. package/vendor/node-api/src/values/DuckDBTimeNSValue.ts +23 -0
  166. package/vendor/node-api/src/values/DuckDBTimeTZValue.ts +96 -0
  167. package/vendor/node-api/src/values/DuckDBTimeValue.ts +34 -0
  168. package/vendor/node-api/src/values/DuckDBTimestampMillisecondsValue.ts +29 -0
  169. package/vendor/node-api/src/values/DuckDBTimestampNanosecondsValue.ts +28 -0
  170. package/vendor/node-api/src/values/DuckDBTimestampSecondsValue.ts +28 -0
  171. package/vendor/node-api/src/values/DuckDBTimestampTZNanosecondsValue.ts +44 -0
  172. package/vendor/node-api/src/values/DuckDBTimestampTZValue.ts +54 -0
  173. package/vendor/node-api/src/values/DuckDBTimestampValue.ts +45 -0
  174. package/vendor/node-api/src/values/DuckDBTupleValue.ts +22 -0
  175. package/vendor/node-api/src/values/DuckDBUUIDValue.ts +48 -0
  176. package/vendor/node-api/src/values/DuckDBUnionValue.ts +22 -0
  177. package/vendor/node-api/src/values/DuckDBValue.ts +53 -0
  178. package/vendor/node-api/src/values/DuckDBVariantValue.ts +34 -0
  179. package/vendor/node-api/src/values/index.ts +24 -0
  180. package/vendor/node-api/src/vectors/DuckDBArrayVector.ts +107 -0
  181. package/vendor/node-api/src/vectors/DuckDBBigIntVector.ts +75 -0
  182. package/vendor/node-api/src/vectors/DuckDBBigNumVector.ts +98 -0
  183. package/vendor/node-api/src/vectors/DuckDBBitVector.ts +102 -0
  184. package/vendor/node-api/src/vectors/DuckDBBlobVector.ts +112 -0
  185. package/vendor/node-api/src/vectors/DuckDBBooleanVector.ts +84 -0
  186. package/vendor/node-api/src/vectors/DuckDBDateVector.ts +80 -0
  187. package/vendor/node-api/src/vectors/DuckDBDecimal128Vector.ts +107 -0
  188. package/vendor/node-api/src/vectors/DuckDBDecimal16Vector.ts +104 -0
  189. package/vendor/node-api/src/vectors/DuckDBDecimal32Vector.ts +104 -0
  190. package/vendor/node-api/src/vectors/DuckDBDecimal64Vector.ts +104 -0
  191. package/vendor/node-api/src/vectors/DuckDBDoubleVector.ts +68 -0
  192. package/vendor/node-api/src/vectors/DuckDBEnum16Vector.ts +75 -0
  193. package/vendor/node-api/src/vectors/DuckDBEnum32Vector.ts +75 -0
  194. package/vendor/node-api/src/vectors/DuckDBEnum8Vector.ts +75 -0
  195. package/vendor/node-api/src/vectors/DuckDBFloatVector.ts +68 -0
  196. package/vendor/node-api/src/vectors/DuckDBGeometryVector.ts +97 -0
  197. package/vendor/node-api/src/vectors/DuckDBHugeIntVector.ts +89 -0
  198. package/vendor/node-api/src/vectors/DuckDBIntegerVector.ts +75 -0
  199. package/vendor/node-api/src/vectors/DuckDBIntervalVector.ts +100 -0
  200. package/vendor/node-api/src/vectors/DuckDBListVector.ts +221 -0
  201. package/vendor/node-api/src/vectors/DuckDBMapVector.ts +88 -0
  202. package/vendor/node-api/src/vectors/DuckDBSQLNullVector.ts +53 -0
  203. package/vendor/node-api/src/vectors/DuckDBSmallIntVector.ts +75 -0
  204. package/vendor/node-api/src/vectors/DuckDBStructVector.ts +138 -0
  205. package/vendor/node-api/src/vectors/DuckDBTimeNSVector.ts +80 -0
  206. package/vendor/node-api/src/vectors/DuckDBTimeTZVector.ts +80 -0
  207. package/vendor/node-api/src/vectors/DuckDBTimeVector.ts +80 -0
  208. package/vendor/node-api/src/vectors/DuckDBTimestampMillisecondsVector.ts +88 -0
  209. package/vendor/node-api/src/vectors/DuckDBTimestampNanosecondsVector.ts +88 -0
  210. package/vendor/node-api/src/vectors/DuckDBTimestampSecondsVector.ts +88 -0
  211. package/vendor/node-api/src/vectors/DuckDBTimestampTZNanosecondsVector.ts +88 -0
  212. package/vendor/node-api/src/vectors/DuckDBTimestampTZVector.ts +86 -0
  213. package/vendor/node-api/src/vectors/DuckDBTimestampVector.ts +83 -0
  214. package/vendor/node-api/src/vectors/DuckDBTinyIntVector.ts +75 -0
  215. package/vendor/node-api/src/vectors/DuckDBTupleVector.ts +125 -0
  216. package/vendor/node-api/src/vectors/DuckDBUBigIntVector.ts +75 -0
  217. package/vendor/node-api/src/vectors/DuckDBUHugeIntVector.ts +89 -0
  218. package/vendor/node-api/src/vectors/DuckDBUIntegerVector.ts +75 -0
  219. package/vendor/node-api/src/vectors/DuckDBUSmallIntVector.ts +75 -0
  220. package/vendor/node-api/src/vectors/DuckDBUTinyIntVector.ts +75 -0
  221. package/vendor/node-api/src/vectors/DuckDBUUIDVector.ts +89 -0
  222. package/vendor/node-api/src/vectors/DuckDBUnionVector.ts +84 -0
  223. package/vendor/node-api/src/vectors/DuckDBValidity.ts +104 -0
  224. package/vendor/node-api/src/vectors/DuckDBVarCharVector.ts +100 -0
  225. package/vendor/node-api/src/vectors/DuckDBVariantVector.ts +609 -0
  226. package/vendor/node-api/src/vectors/DuckDBVector.ts +33 -0
  227. package/vendor/node-api/src/vectors/createVector.ts +269 -0
  228. package/vendor/node-api/src/vectors/dataAccessors.ts +387 -0
  229. package/vendor/node-api/src/vectors/index.ts +50 -0
  230. package/vendor/node-api/src/vectors/vectorRegistry.ts +24 -0
  231. package/vendor/node-api/src/version.ts +5 -0
@@ -0,0 +1,190 @@
1
+ // Moving values across bun:ffi: strings, out slots, and the C structs the
2
+ // contract exposes as plain objects (Date_, Interval, Decimal, ...), encoded and
3
+ // decoded from the layouts generated from DuckDB's C API spec.
4
+ import { CString, type Pointer, read, toArrayBuffer } from "bun:ffi";
5
+ import { layouts } from "../generated/capi.ts";
6
+ import { duckdb } from "./native.ts";
7
+
8
+ export type LayoutName = keyof typeof layouts;
9
+ type FieldType = string;
10
+
11
+ const encoder = new TextEncoder();
12
+
13
+ /** A NUL-terminated UTF-8 copy of `text`, for a C `const char *` argument. */
14
+ export function cstr(text: string): Buffer {
15
+ return Buffer.from(`${text}\0`, "utf8");
16
+ }
17
+
18
+ /** UTF-8 bytes of `text` and their length, for a shim `(*const u8, usize)` argument. */
19
+ export function utf8(text: string): { bytes: Uint8Array; length: number } {
20
+ const bytes = encoder.encode(text);
21
+ // bun:ffi rejects a pointer to an empty view, and the shim copies `length`
22
+ // bytes, so one padding byte is safe and keeps the pointer valid.
23
+ return { bytes: bytes.length === 0 ? new Uint8Array(1) : bytes, length: bytes.length };
24
+ }
25
+
26
+ /** Reads a NUL-terminated C string DuckDB owns. */
27
+ export function readCString(pointer: Pointer | null): string | null {
28
+ return pointer === null ? null : new CString(pointer).toString();
29
+ }
30
+
31
+ /** Reads a C string DuckDB allocated for the caller, then frees it with duckdb_free. */
32
+ export function takeCString(pointer: Pointer | null): string | null {
33
+ if (pointer === null) return null;
34
+ try {
35
+ return new CString(pointer).toString();
36
+ } finally {
37
+ duckdb.duckdb_free(pointer);
38
+ }
39
+ }
40
+
41
+ /** A pointer-sized out slot, such as the `duckdb_database *out` of duckdb_open. */
42
+ export function slot(value: Pointer | null = null): BigUint64Array {
43
+ return new BigUint64Array([BigInt(value ?? 0)]);
44
+ }
45
+
46
+ export function slotValue(cell: BigUint64Array): Pointer | null {
47
+ const value = Number(cell[0]);
48
+ return value === 0 ? null : (value as Pointer);
49
+ }
50
+
51
+ /** A copy of `length` bytes at `pointer`, or an empty array for a null pointer. */
52
+ export function copyBytes(pointer: Pointer | null, length: number): Uint8Array {
53
+ if (pointer === null || length === 0) return new Uint8Array(0);
54
+ return new Uint8Array(toArrayBuffer(pointer, 0, length).slice(0));
55
+ }
56
+
57
+ // --- structs ----------------------------------------------------------------------
58
+
59
+ const BIGINT_FIELDS = new Set<FieldType>(["i64", "u64", "idx"]);
60
+ const WIDE_INTEGERS = new Set<string>(["hugeint", "uhugeint"]);
61
+
62
+ function readField(view: DataView, offset: number, type: FieldType): unknown {
63
+ switch (type) {
64
+ case "bool":
65
+ return view.getUint8(offset) !== 0;
66
+ case "i8":
67
+ return view.getInt8(offset);
68
+ case "u8":
69
+ return view.getUint8(offset);
70
+ case "i16":
71
+ return view.getInt16(offset, true);
72
+ case "u16":
73
+ return view.getUint16(offset, true);
74
+ case "i32":
75
+ case "TYPE":
76
+ return view.getInt32(offset, true);
77
+ case "u32":
78
+ return view.getUint32(offset, true);
79
+ case "i64":
80
+ return view.getBigInt64(offset, true);
81
+ case "u64":
82
+ case "idx":
83
+ return view.getBigUint64(offset, true);
84
+ case "f32":
85
+ return view.getFloat32(offset, true);
86
+ case "f64":
87
+ return view.getFloat64(offset, true);
88
+ case "ptr":
89
+ return Number(view.getBigUint64(offset, true));
90
+ default:
91
+ return decodeAt(type as LayoutName, view, offset);
92
+ }
93
+ }
94
+
95
+ function writeField(view: DataView, offset: number, type: FieldType, value: unknown): void {
96
+ switch (type) {
97
+ case "bool":
98
+ view.setUint8(offset, value ? 1 : 0);
99
+ return;
100
+ case "i8":
101
+ view.setInt8(offset, Number(value));
102
+ return;
103
+ case "u8":
104
+ view.setUint8(offset, Number(value));
105
+ return;
106
+ case "i16":
107
+ view.setInt16(offset, Number(value), true);
108
+ return;
109
+ case "u16":
110
+ view.setUint16(offset, Number(value), true);
111
+ return;
112
+ case "i32":
113
+ case "TYPE":
114
+ view.setInt32(offset, Number(value), true);
115
+ return;
116
+ case "u32":
117
+ view.setUint32(offset, Number(value), true);
118
+ return;
119
+ case "i64":
120
+ view.setBigInt64(offset, BigInt(value as bigint), true);
121
+ return;
122
+ case "u64":
123
+ case "idx":
124
+ view.setBigUint64(offset, BigInt(value as bigint), true);
125
+ return;
126
+ case "f32":
127
+ view.setFloat32(offset, Number(value), true);
128
+ return;
129
+ case "f64":
130
+ view.setFloat64(offset, Number(value), true);
131
+ return;
132
+ case "ptr":
133
+ view.setBigUint64(offset, BigInt(Number(value)), true);
134
+ return;
135
+ default:
136
+ encodeAt(type as LayoutName, view, offset, value);
137
+ }
138
+ }
139
+
140
+ function decodeAt(name: LayoutName, view: DataView, offset: number): unknown {
141
+ if (WIDE_INTEGERS.has(name)) {
142
+ const lower = view.getBigUint64(offset, true);
143
+ const upper = name === "hugeint" ? view.getBigInt64(offset + 8, true) : view.getBigUint64(offset + 8, true);
144
+ return (upper << 64n) | lower;
145
+ }
146
+ const out: Record<string, unknown> = {};
147
+ for (const [field, spec] of Object.entries(layouts[name].fields)) {
148
+ out[field] = readField(view, offset + spec.offset, spec.type);
149
+ }
150
+ return out;
151
+ }
152
+
153
+ function encodeAt(name: LayoutName, view: DataView, offset: number, value: unknown): void {
154
+ if (WIDE_INTEGERS.has(name)) {
155
+ const wide = BigInt(value as bigint);
156
+ view.setBigUint64(offset, BigInt.asUintN(64, wide), true);
157
+ if (name === "hugeint") view.setBigInt64(offset + 8, BigInt.asIntN(64, wide >> 64n), true);
158
+ else view.setBigUint64(offset + 8, BigInt.asUintN(64, wide >> 64n), true);
159
+ return;
160
+ }
161
+ const fields = value as Record<string, unknown>;
162
+ for (const [field, spec] of Object.entries(layouts[name].fields)) {
163
+ writeField(view, offset + spec.offset, spec.type, fields[field]);
164
+ }
165
+ }
166
+
167
+ /** A zeroed buffer of the struct's C size, for an out parameter. */
168
+ export function structBuffer(name: LayoutName): Uint8Array {
169
+ return new Uint8Array(layouts[name].size);
170
+ }
171
+
172
+ /** Encodes a contract object (or a bigint for hugeint/uhugeint) into the struct's C layout. */
173
+ export function encode(name: LayoutName, value: unknown): Uint8Array {
174
+ const buffer = structBuffer(name);
175
+ encodeAt(name, new DataView(buffer.buffer), 0, value);
176
+ return buffer;
177
+ }
178
+
179
+ /** Decodes a struct from a buffer or from native memory into a contract object. */
180
+ export function decode<T>(name: LayoutName, source: Uint8Array | Pointer): T {
181
+ const bytes = source instanceof Uint8Array ? source : new Uint8Array(toArrayBuffer(source, 0, layouts[name].size));
182
+ return decodeAt(name, new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength), 0) as T;
183
+ }
184
+
185
+ /** True when the field type reads as a bigint, for callers that convert to number. */
186
+ export function isBigIntField(type: FieldType): boolean {
187
+ return BIGINT_FIELDS.has(type);
188
+ }
189
+
190
+ export { read };
@@ -0,0 +1,150 @@
1
+ // Loads libduckdb and the Rust shim with bun:ffi. Every libduckdb C function the
2
+ // binding calls goes through the shim's C++ exception boundary (native/ABI.md,
3
+ // "C++ exceptions"): the shim hands over the address of a wrapper per function,
4
+ // in the order of the tables generated from DuckDB's C API spec
5
+ // (src/bindings/generated/capi.ts), and a call that caught a C++ exception throws
6
+ // DuckDB's message as an Error. The shim's own exports follow native/ABI.md.
7
+ import { CString, dlopen, type FFIFunction, FFIType, linkSymbols, type Pointer, toArrayBuffer } from "bun:ffi";
8
+ import packageJson from "../../../package.json" with { type: "json" };
9
+ import { nativePaths } from "../../library.ts";
10
+ import { byValue, capi } from "../generated/capi.ts";
11
+ import { exceptionMessage } from "./exception.ts";
12
+ import { debug, flags } from "./flags.ts";
13
+
14
+ /** The shim's exports from native/ABI.md, besides the generated by-value wrappers. */
15
+ const shimAbi = {
16
+ dbs_init: { args: ["ptr", "ptr", "ptr"], returns: "i32" },
17
+ dbs_set_sync_call: { args: ["ptr"], returns: "void" },
18
+ dbs_context_close: { args: [], returns: "void" },
19
+ dbs_counters: { args: [], returns: "ptr" },
20
+ dbs_version: { args: [], returns: "cstring" },
21
+ dbs_capi_table: { args: ["ptr"], returns: "ptr" },
22
+ dbs_exception_flag: { args: [], returns: "ptr" },
23
+ dbs_take_exception: { args: [], returns: "ptr" },
24
+
25
+ dbs_job_open: { args: ["ptr", "usize", "bool", "ptr"], returns: "ptr" },
26
+ dbs_job_get_or_create_from_cache: { args: ["ptr", "ptr", "usize", "bool", "ptr"], returns: "ptr" },
27
+ dbs_job_connect: { args: ["ptr"], returns: "ptr" },
28
+ dbs_job_query: { args: ["ptr", "ptr", "usize"], returns: "ptr" },
29
+ dbs_job_prepare: { args: ["ptr", "ptr", "usize"], returns: "ptr" },
30
+ dbs_job_execute_prepared: { args: ["ptr"], returns: "ptr" },
31
+ dbs_job_execute_prepared_streaming: { args: ["ptr"], returns: "ptr" },
32
+ dbs_job_extract_statements: { args: ["ptr", "ptr", "usize"], returns: "ptr" },
33
+ dbs_job_prepare_extracted_statement: { args: ["ptr", "ptr", "u64"], returns: "ptr" },
34
+ dbs_job_execute_pending: { args: ["ptr"], returns: "ptr" },
35
+ dbs_job_fetch_chunk: { args: ["ptr"], returns: "ptr" },
36
+ dbs_job_failed: { args: ["ptr"], returns: "bool" },
37
+ dbs_job_error: { args: ["ptr"], returns: "ptr" },
38
+ dbs_job_out0: { args: ["ptr"], returns: "ptr" },
39
+ dbs_job_out1: { args: ["ptr"], returns: FFIType.u64_fast },
40
+ dbs_job_free: { args: ["ptr"], returns: "void" },
41
+
42
+ dbs_scalar_function_set_callbacks: { args: ["ptr", "u64", "bool", "bool"], returns: "void" },
43
+ dbs_table_function_set_callbacks: { args: ["ptr", "u64", "bool", "bool", "bool"], returns: "void" },
44
+ dbs_set_bind_data: { args: ["ptr", "u32", "u64"], returns: "void" },
45
+ dbs_get_bind_data: { args: ["ptr", "u32"], returns: FFIType.u64_fast },
46
+ dbs_set_init_data: { args: ["ptr", "u64"], returns: "void" },
47
+ dbs_get_init_data: { args: ["ptr"], returns: FFIType.u64_fast },
48
+ dbs_get_local_init_data: { args: ["ptr"], returns: FFIType.u64_fast },
49
+ dbs_scalar_init_set_state: { args: ["ptr", "u64"], returns: "void" },
50
+ dbs_scalar_get_state: { args: ["ptr"], returns: FFIType.u64_fast },
51
+ dbs_call_done: { args: ["ptr"], returns: "void" },
52
+
53
+ dbs_ring_record: { args: ["u32", "u32", "u64", "u64", "ptr", "usize"], returns: "void" },
54
+ dbs_install_crash_handler: { args: ["ptr", "usize"], returns: "i32" },
55
+ dbs_crash_for_test: { args: ["u32"], returns: "void" },
56
+ } as const satisfies Record<string, FFIFunction>;
57
+
58
+ const paths = nativePaths();
59
+
60
+ // libduckdb first, by absolute path, so the shim's own dependency on it resolves
61
+ // to this already-loaded image. The binding calls it only through the shim.
62
+ const library = dlopen(paths.libPath, { duckdb_library_version: { args: [], returns: "ptr" } });
63
+ const shimLibrary = dlopen(paths.shimPath, shimAbi).symbols;
64
+
65
+ /** The wrappers' addresses, `capi` entries first, then `byValue` ones. */
66
+ const addresses = (() => {
67
+ const count = new BigUint64Array(1);
68
+ const table = shimLibrary.dbs_capi_table(count) as Pointer;
69
+ const expected = Object.keys(capi).length + Object.keys(byValue).length;
70
+ if (Number(count[0]) !== expected) {
71
+ throw new Error(`The shim at ${paths.shimPath} has ${count[0]} C API wrappers; this binding needs ${expected}.`);
72
+ }
73
+ return new BigUint64Array(toArrayBuffer(table, 0, expected * 8));
74
+ })();
75
+
76
+ /** `table` with each function's wrapper address, starting at `offset` in the address table. */
77
+ function located<T extends Record<string, FFIFunction>>(table: T, offset: number): T {
78
+ return Object.fromEntries(
79
+ Object.entries(table).map(([name, fn], i) => [name, { ...fn, ptr: addresses[offset + i] }]),
80
+ ) as T;
81
+ }
82
+
83
+ /** The calling JS thread's C++ exception flag, set by a wrapper that caught one. */
84
+ const exceptionFlag = new Int32Array(toArrayBuffer(shimLibrary.dbs_exception_flag() as Pointer, 0, 4));
85
+ const rawCapi = linkSymbols(located(capi, 0)).symbols;
86
+ const rawByValue = linkSymbols(located(byValue, Object.keys(capi).length)).symbols;
87
+
88
+ /** Throws the C++ exception the last call caught, with DuckDB's message for it. */
89
+ function raise(): never {
90
+ const pointer = shimLibrary.dbs_take_exception() as Pointer | null;
91
+ let what: string | null = null;
92
+ if (pointer !== null) {
93
+ try {
94
+ what = new CString(pointer).toString();
95
+ } finally {
96
+ rawCapi.duckdb_free(pointer);
97
+ }
98
+ }
99
+ throw new Error(exceptionMessage(what));
100
+ }
101
+
102
+ /**
103
+ * `raw` with each function wrapped to throw, through `raise`, when the call set the
104
+ * calling thread's exception flag. One shared wrapper costs about a nanosecond a
105
+ * call over one generated per function, against about 20 for the call itself.
106
+ */
107
+ function guarded<T extends Record<string, unknown>>(raw: T): T {
108
+ const out: Record<string, unknown> = {};
109
+ for (const [name, fn] of Object.entries(raw)) {
110
+ const call = fn as (...args: unknown[]) => unknown;
111
+ out[name] = (...args: unknown[]) => {
112
+ const result = call(...args);
113
+ if (exceptionFlag[0] !== 0) raise();
114
+ return result;
115
+ };
116
+ }
117
+ return out as T;
118
+ }
119
+
120
+ export const duckdb = guarded(rawCapi);
121
+ export const shim = { ...guarded(rawByValue), ...shimLibrary };
122
+ export const libraryPath = paths.libPath;
123
+ export const shimPath = paths.shimPath;
124
+
125
+ /** The versions a crash report and diagnostics() name. */
126
+ export const versions = {
127
+ package: packageJson.version,
128
+ duckdb: new CString(library.symbols.duckdb_library_version() as Pointer).toString(),
129
+ bun: Bun.version,
130
+ shim: String(shim.dbs_version()),
131
+ } as const;
132
+
133
+ debug(`loaded libduckdb ${versions.duckdb} from ${libraryPath}, shim ${versions.shim} from ${shimPath}`);
134
+
135
+ const CRASH_HANDLER_STATUS = ["installed", "failed: no alternate signal stack", "failed: sigaction"] as const;
136
+
137
+ /** The fatal-signal handler's state: native/ABI.md "Diagnostics". */
138
+ export const crashHandler: "disabled" | (typeof CRASH_HANDLER_STATUS)[number] = flags.crashHandler
139
+ ? installCrashHandler()
140
+ : "disabled";
141
+
142
+ function installCrashHandler(): (typeof CRASH_HANDLER_STATUS)[number] {
143
+ const header = Buffer.from(
144
+ `package ${versions.package}, duckdb ${versions.duckdb}, bun ${versions.bun}, shim ${versions.shim}`,
145
+ );
146
+ const status = CRASH_HANDLER_STATUS[shim.dbs_install_crash_handler(header, header.length)] ?? "failed: sigaction";
147
+ // Queries work without the handler, so a failed install is not an error: diagnostics() reports it.
148
+ debug(`crash handler ${status}`);
149
+ return status;
150
+ }
@@ -0,0 +1,54 @@
1
+ // Records binding events into the shim's crash-forensics ring (native/ABI.md,
2
+ // "Diagnostics"). Jobs record their own start and end in the shim; JavaScript
3
+ // records the events that happen on the JS thread.
4
+ import type { Pointer } from "bun:ffi";
5
+ import { shim } from "./native.ts";
6
+
7
+ /** Op codes from native/ABI.md. */
8
+ export const RingOp = {
9
+ close: 32,
10
+ disconnect: 33,
11
+ destroyResult: 34,
12
+ destroyPending: 35,
13
+ destroyPrepared: 36,
14
+ interrupt: 37,
15
+ registerScalarFunction: 38,
16
+ registerTableFunction: 39,
17
+ appenderFlush: 40,
18
+ appenderClose: 41,
19
+ pendingExecuteTask: 42,
20
+ } as const;
21
+
22
+ /** The shim's jobs, in native/ABI.md table order; a job's op code is its position plus one. */
23
+ const JOB_OPS = [
24
+ "open",
25
+ "get_or_create_from_cache",
26
+ "connect",
27
+ "query",
28
+ "prepare",
29
+ "execute_prepared",
30
+ "execute_prepared_streaming",
31
+ "extract_statements",
32
+ "prepare_extracted_statement",
33
+ "execute_pending",
34
+ "fetch_chunk",
35
+ ] as const;
36
+
37
+ /** The ring op code of a job, by its contract function name; 0 for none. */
38
+ export function jobOp(name: string): number {
39
+ return JOB_OPS.indexOf(name as (typeof JOB_OPS)[number]) + 1;
40
+ }
41
+
42
+ const STATE_EVENT = 4;
43
+ const TEXT_MAX = 120;
44
+ const encoder = new TextEncoder();
45
+ const empty = new Uint8Array(1);
46
+
47
+ /**
48
+ * Appends one event. `text` must already be redacted (see diagnostics.ts); it is
49
+ * truncated to the ring's 120 bytes.
50
+ */
51
+ export function record(op: number, handle: Pointer | null, connectionId = 0, text?: string): void {
52
+ const bytes = text ? encoder.encode(text).subarray(0, TEXT_MAX) : empty;
53
+ shim.dbs_ring_record(op, STATE_EVENT, handle ?? 0, connectionId, bytes, text ? bytes.length : 0);
54
+ }
@@ -0,0 +1,133 @@
1
+ // Native memory handed to JavaScript, and what a function callback hands out.
2
+ //
3
+ // Copies. The official binding returns vector data, validity masks and string
4
+ // bytes with Napi::Buffer::NewOrCopy under NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
5
+ // (src/napi_setup.h), so each is a copy: writes reach the vector only through
6
+ // copy_data_to_vector. Each copy is mapped to the handle that owns the memory it
7
+ // came from, so get_data_from_pointer can follow a string pointer in the copy
8
+ // only while that memory is alive, and a reachable copy keeps its vector, and
9
+ // through the vector its data chunk, from being collected.
10
+ //
11
+ // Callback scopes. The info, data chunk and vector a function callback receives
12
+ // are borrowed for the length of the call: DuckDB frees or reuses them once the
13
+ // callback returns. A scope collects those handles and every handle derived from
14
+ // them (a chunk's vectors, a vector's children), then invalidates them when the
15
+ // callback returns, so a use after the callback throws instead of touching freed
16
+ // memory.
17
+ //
18
+ // User data. JavaScript objects handed to DuckDB (bind data, init data, scalar
19
+ // function state) cross as ids (native/ABI.md, "Function callbacks"); the
20
+ // objects live here until the shim reports that DuckDB dropped the id.
21
+
22
+ import { type Pointer, toArrayBuffer } from "bun:ffi";
23
+ import { expectReferenceable } from "./convert.ts";
24
+ import { allocateId, onRelease } from "./dispatch.ts";
25
+ import { Handle } from "./handle.ts";
26
+
27
+ // --- callback scopes ------------------------------------------------------------------
28
+
29
+ const scopes = new WeakMap<object, CallbackScope>();
30
+
31
+ export class CallbackScope {
32
+ readonly #handles: Handle[] = [];
33
+
34
+ /** Adds a handle that dies with the callback. */
35
+ adopt<T extends Handle>(handle: T): T {
36
+ this.#handles.push(handle);
37
+ scopes.set(handle, this);
38
+ return handle;
39
+ }
40
+
41
+ /** Invalidates every handle the callback was given or derived. */
42
+ close(): void {
43
+ for (const handle of this.#handles) handle.invalidate();
44
+ this.#handles.length = 0;
45
+ }
46
+ }
47
+
48
+ /**
49
+ * A handle derived from `owner` (a vector of a chunk, a child of a vector):
50
+ * when the owner belongs to a callback, the derived handle dies with it too.
51
+ */
52
+ export function derived<T extends Handle>(owner: object, handle: T): T {
53
+ scopes.get(owner)?.adopt(handle);
54
+ return handle;
55
+ }
56
+
57
+ // --- copies of native memory ----------------------------------------------------------
58
+
59
+ /** The handle that owns the memory each copy came from. */
60
+ const owners = new WeakMap<ArrayBuffer, Handle>();
61
+
62
+ /**
63
+ * A Buffer holding a copy of `length` bytes of native memory at `pointer`, owned
64
+ * by `owner`. A null pointer or a zero length gives an empty Buffer.
65
+ */
66
+ export function nativeCopy(pointer: Pointer | number | null, length: number, owner: Handle | undefined): Buffer {
67
+ if (!pointer || length === 0) return Buffer.alloc(0);
68
+ const copy = Buffer.from(new Uint8Array(toArrayBuffer(pointer as Pointer, 0, length)));
69
+ if (owner !== undefined) owners.set(copy.buffer as ArrayBuffer, owner);
70
+ return copy;
71
+ }
72
+
73
+ /**
74
+ * The owner of the memory a copy came from, so a string pointer inside the copy
75
+ * can be followed. Throws when that memory is gone: its handle, or a handle it
76
+ * borrows from, was destroyed or belonged to a callback that has returned.
77
+ */
78
+ export function liveOwnerOfBuffer(buffer: ArrayBuffer): Handle | undefined {
79
+ const owner = owners.get(buffer);
80
+ for (let handle: unknown = owner; handle instanceof Handle; handle = handle.owner) {
81
+ if (handle.ptr === null) throw new Error(`Invalid ${handle.kind} argument`);
82
+ }
83
+ return owner;
84
+ }
85
+
86
+ // --- user data ---------------------------------------------------------------------------
87
+
88
+ const userData = new Map<number, unknown>();
89
+
90
+ /** Stores `value` under a new id that is dropped when DuckDB releases it. */
91
+ export function storeUserData(value: unknown): number {
92
+ expectReferenceable(value);
93
+ const id = allocateId();
94
+ userData.set(id, value);
95
+ onRelease(id, () => userData.delete(id));
96
+ return id;
97
+ }
98
+
99
+ /** The value stored under `id`, or undefined for 0 (none) or a released id. */
100
+ export function userDataOf(id: number | bigint): unknown {
101
+ return userData.get(Number(id));
102
+ }
103
+
104
+ // --- running user callbacks ----------------------------------------------------------------
105
+
106
+ /**
107
+ * The message DuckDB reports for a thrown value, as the official binding's
108
+ * Napi::Error::Message(): the value's "message" property when the value is an
109
+ * object and that property is a string, otherwise an empty message. A thrown
110
+ * string therefore fails the query with "Invalid Input Error: " and no text.
111
+ */
112
+ export function messageOf(error: unknown): string {
113
+ if ((typeof error === "object" && error !== null) || typeof error === "function") {
114
+ const message = (error as { message?: unknown }).message;
115
+ if (typeof message === "string") return message;
116
+ }
117
+ return "";
118
+ }
119
+
120
+ /**
121
+ * Runs a user callback inside `scope`. A throw is reported through `setError`,
122
+ * as the official binding's DuckDBThreadCallbackDispatch does, and the scope is
123
+ * closed whatever happens.
124
+ */
125
+ export function runInScope(scope: CallbackScope, run: () => void, setError: (message: string) => void): void {
126
+ try {
127
+ run();
128
+ } catch (error) {
129
+ setError(messageOf(error));
130
+ } finally {
131
+ scope.close();
132
+ }
133
+ }
@@ -0,0 +1,65 @@
1
+ // Tracing of native calls: the "duckdb-bun:call" tracing channel, debug lines,
2
+ // and the SQL text kept for crash reports, all with credentials redacted.
3
+ import { tracingChannel } from "node:diagnostics_channel";
4
+ import { debug, flags } from "./flags.ts";
5
+
6
+ /** What a traced native call publishes as the tracing channel's context. */
7
+ export interface CallContext {
8
+ /** The contract function, such as "query" or "connect". */
9
+ op: string;
10
+ /** The statement, redacted, only when DUCKDB_BUN_TRACE_SQL=1. */
11
+ sql?: string;
12
+ /** Set by the channel when the call resolves. */
13
+ result?: unknown;
14
+ /** Set by the channel when the call rejects. */
15
+ error?: unknown;
16
+ }
17
+
18
+ export const CALL_CHANNEL = "duckdb-bun:call";
19
+
20
+ const channel = tracingChannel<object, CallContext>(CALL_CHANNEL);
21
+ const subChannels = [channel.start, channel.end, channel.asyncStart, channel.asyncEnd, channel.error];
22
+
23
+ // Statements that can carry credentials: CREATE SECRET, ATTACH with a
24
+ // connection string, and settings or options that name a key, token or password.
25
+ // Matched anywhere, also inside identifiers such as s3_secret_access_key; a false
26
+ // match only redacts more.
27
+ const SENSITIVE = /secret|attach|passw|token|credential|key_id|access_key|account_key|account_name|connection_string/i;
28
+
29
+ // Single-quoted literals ('' escapes a quote; an unterminated one runs to the
30
+ // end) and dollar-quoted strings.
31
+ const LITERAL = /'(?:[^']|'')*(?:'|$)|\$([A-Za-z_]\w*)?\$[\s\S]*?(?:\$\1\$|$)/g;
32
+
33
+ /** Replaces every string literal with '<redacted>' in a statement that can carry credentials. */
34
+ export function redactSql(sql: string): string {
35
+ return SENSITIVE.test(sql) ? sql.replace(LITERAL, "'<redacted>'") : sql;
36
+ }
37
+
38
+ /** The context for one call; SQL is redacted and kept only when DUCKDB_BUN_TRACE_SQL=1. */
39
+ export function callContext(op: string, sql?: string): CallContext {
40
+ return flags.traceSql && sql !== undefined ? { op, sql: redactSql(sql) } : { op };
41
+ }
42
+
43
+ /**
44
+ * Runs `call` inside the tracing channel when anything subscribes, and logs its
45
+ * outcome and duration when DUCKDB_BUN_DEBUG=1.
46
+ */
47
+ export function traced<T>(context: CallContext, call: () => Promise<T>): Promise<T> {
48
+ const subscribed = subChannels.some((sub) => sub.hasSubscribers);
49
+ if (!subscribed && !flags.debug) return call();
50
+ const promise = subscribed ? channel.tracePromise(call, context) : call();
51
+ if (!flags.debug) return promise;
52
+ const started = performance.now();
53
+ const label = context.sql === undefined ? context.op : `${context.op} ${JSON.stringify(context.sql)}`;
54
+ const elapsed = () => `${(performance.now() - started).toFixed(2)}ms`;
55
+ return promise.then(
56
+ (value) => {
57
+ debug(`${label} ok ${elapsed()}`);
58
+ return value;
59
+ },
60
+ (error: unknown) => {
61
+ debug(`${label} error ${elapsed()}: ${error instanceof Error ? error.message : String(error)}`);
62
+ throw error;
63
+ },
64
+ );
65
+ }
@@ -0,0 +1,35 @@
1
+ import type { DuckDBConnection as OfficialConnection } from "../vendor/node-api/src/index.ts";
2
+ import { asDuckDBError } from "./errors.ts";
3
+ import { type Reader, readerFrom } from "./reader.ts";
4
+ import { PreparedStatement } from "./statement.ts";
5
+
6
+ /** A DuckDB connection. Drop-in for the @duckdb/node-api slice used downstream. */
7
+ export class DuckDBConnection {
8
+ #connection: OfficialConnection;
9
+ #closed = false;
10
+
11
+ constructor(connection: OfficialConnection) {
12
+ this.#connection = connection;
13
+ }
14
+
15
+ /** Execute a statement and ignore any result rows. */
16
+ async run(sql: string): Promise<void> {
17
+ await asDuckDBError(() => this.#connection.run(sql));
18
+ }
19
+
20
+ /** Execute a query and read every row into a Reader. */
21
+ async runAndReadAll(sql: string): Promise<Reader> {
22
+ return readerFrom(await asDuckDBError(() => this.#connection.runAndReadAll(sql)));
23
+ }
24
+
25
+ /** Prepare a parameterized statement. */
26
+ async prepare(sql: string): Promise<PreparedStatement> {
27
+ return new PreparedStatement(await asDuckDBError(() => this.#connection.prepare(sql)));
28
+ }
29
+
30
+ closeSync(): void {
31
+ if (this.#closed) return;
32
+ this.#connection.closeSync();
33
+ this.#closed = true;
34
+ }
35
+ }
@@ -0,0 +1,27 @@
1
+ import { DuckDBInstance as OfficialInstance } from "../vendor/node-api/src/index.ts";
2
+ import { DuckDBConnection } from "./connection.ts";
3
+ import { asDuckDBError } from "./errors.ts";
4
+
5
+ /** A DuckDB database instance. Use `:memory:` for an ephemeral in-memory DB. */
6
+ export class DuckDBInstance {
7
+ #instance: OfficialInstance;
8
+ #closed = false;
9
+
10
+ private constructor(instance: OfficialInstance) {
11
+ this.#instance = instance;
12
+ }
13
+
14
+ static async create(path = ":memory:"): Promise<DuckDBInstance> {
15
+ return new DuckDBInstance(await asDuckDBError(() => OfficialInstance.create(path)));
16
+ }
17
+
18
+ async connect(): Promise<DuckDBConnection> {
19
+ return new DuckDBConnection(await asDuckDBError(() => this.#instance.connect()));
20
+ }
21
+
22
+ closeSync(): void {
23
+ if (this.#closed) return;
24
+ this.#instance.closeSync();
25
+ this.#closed = true;
26
+ }
27
+ }