quiverdb 0.6.1 → 0.9.2
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/LICENSE +21 -0
- package/README.md +95 -17
- package/libs/linux-x86_64/libquiver.so +0 -0
- package/libs/linux-x86_64/libquiver.so.0 +0 -0
- package/libs/linux-x86_64/libquiver_c.so +0 -0
- package/libs/windows-x86_64/libquiver.dll +0 -0
- package/libs/windows-x86_64/libquiver_c.dll +0 -0
- package/mod.ts +25 -0
- package/package.json +17 -30
- package/src/composites.ts +89 -0
- package/src/create.ts +152 -0
- package/src/csv.ts +118 -0
- package/src/database.ts +171 -0
- package/src/errors.ts +21 -0
- package/src/ffi-helpers.ts +157 -0
- package/src/index.ts +17 -0
- package/src/introspection.ts +33 -0
- package/src/loader.ts +285 -0
- package/src/lua-runner.ts +48 -0
- package/src/metadata.ts +218 -0
- package/src/query.ts +160 -0
- package/src/read.ts +572 -0
- package/src/time-series.ts +291 -0
- package/src/transaction.ts +27 -0
- package/{dist/types.d.ts → src/types.ts} +8 -1
- package/dist/composites.d.ts +0 -9
- package/dist/composites.d.ts.map +0 -1
- package/dist/composites.js +0 -69
- package/dist/composites.js.map +0 -1
- package/dist/create.d.ts +0 -9
- package/dist/create.d.ts.map +0 -1
- package/dist/create.js +0 -104
- package/dist/create.js.map +0 -1
- package/dist/csv.d.ts +0 -11
- package/dist/csv.d.ts.map +0 -1
- package/dist/csv.js +0 -67
- package/dist/csv.js.map +0 -1
- package/dist/database.d.ts +0 -13
- package/dist/database.d.ts.map +0 -1
- package/dist/database.js +0 -42
- package/dist/database.js.map +0 -1
- package/dist/errors.d.ts +0 -5
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -18
- package/dist/errors.js.map +0 -1
- package/dist/ffi-helpers.d.ts +0 -57
- package/dist/ffi-helpers.d.ts.map +0 -1
- package/dist/ffi-helpers.js +0 -136
- package/dist/ffi-helpers.js.map +0 -1
- package/dist/index.d.ts +0 -17
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -13
- package/dist/index.js.map +0 -1
- package/dist/introspection.d.ts +0 -10
- package/dist/introspection.d.ts.map +0 -1
- package/dist/introspection.js +0 -27
- package/dist/introspection.js.map +0 -1
- package/dist/loader.d.ts +0 -7
- package/dist/loader.d.ts.map +0 -1
- package/dist/loader.js +0 -165
- package/dist/loader.js.map +0 -1
- package/dist/lua-runner.d.ts +0 -11
- package/dist/lua-runner.d.ts.map +0 -1
- package/dist/lua-runner.js +0 -43
- package/dist/lua-runner.js.map +0 -1
- package/dist/metadata.d.ts +0 -28
- package/dist/metadata.d.ts.map +0 -1
- package/dist/metadata.js +0 -100
- package/dist/metadata.js.map +0 -1
- package/dist/query.d.ts +0 -9
- package/dist/query.d.ts.map +0 -1
- package/dist/query.js +0 -90
- package/dist/query.js.map +0 -1
- package/dist/read.d.ts +0 -25
- package/dist/read.d.ts.map +0 -1
- package/dist/read.js +0 -220
- package/dist/read.js.map +0 -1
- package/dist/time-series.d.ts +0 -12
- package/dist/time-series.d.ts.map +0 -1
- package/dist/time-series.js +0 -155
- package/dist/time-series.js.map +0 -1
- package/dist/transaction.d.ts +0 -10
- package/dist/transaction.d.ts.map +0 -1
- package/dist/transaction.js +0 -22
- package/dist/transaction.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -2
- package/dist/types.js.map +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 PSR
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,29 +1,41 @@
|
|
|
1
1
|
# quiverdb
|
|
2
2
|
|
|
3
|
-
SQLite wrapper binding for Quiver via
|
|
3
|
+
SQLite wrapper binding for Quiver via Bun FFI (`bun:ffi`).
|
|
4
4
|
|
|
5
5
|
## Requirements
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
- `libquiver.dll` and `libquiver_c.dll` (or `.so`/`.dylib`)
|
|
7
|
+
- [Bun](https://bun.sh) >= 1.1
|
|
8
|
+
- `libquiver.dll` and `libquiver_c.dll` (or `.so`/`.dylib`) accessible (see [Native Library Setup](#native-library-setup))
|
|
9
9
|
|
|
10
10
|
## Installation
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
|
|
13
|
+
bun add quiverdb
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
The native
|
|
16
|
+
The bundled native libraries ship inside the package, so no separate download
|
|
17
|
+
step is required on supported platforms. To build the native libraries from
|
|
18
|
+
source, see the Quiver project build instructions.
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { Database } from "quiverdb";
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Bun loads native code via FFI without any permission flags — unlike Deno, there
|
|
25
|
+
is no `--allow-ffi`/`--allow-read`/`--allow-write` to pass.
|
|
17
26
|
|
|
18
27
|
## Native Library Setup
|
|
19
28
|
|
|
20
|
-
|
|
29
|
+
The loader searches for native libraries in three tiers:
|
|
21
30
|
|
|
22
|
-
|
|
31
|
+
1. **Bundled**: `libs/{os}-{arch}/` directory next to the binding source (shipped
|
|
32
|
+
inside the published package).
|
|
33
|
+
2. **Dev mode**: Walks up directories looking for `build/bin/` (auto-discovered
|
|
34
|
+
during in-tree development against a local C++ build).
|
|
35
|
+
3. **System PATH**: Falls back to loading by library name.
|
|
23
36
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
3. Place both libraries alongside your application entry point
|
|
37
|
+
Currently prebuilt binaries ship for `linux-x86_64` and `windows-x86_64`. On other
|
|
38
|
+
platforms, build from source and point to `build/bin/` via tier 2.
|
|
27
39
|
|
|
28
40
|
## Quick Start
|
|
29
41
|
|
|
@@ -39,6 +51,12 @@ console.log(values); // [42]
|
|
|
39
51
|
db.close();
|
|
40
52
|
```
|
|
41
53
|
|
|
54
|
+
Run with:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
bun run example.ts
|
|
58
|
+
```
|
|
59
|
+
|
|
42
60
|
## API Methods
|
|
43
61
|
|
|
44
62
|
### Lifecycle
|
|
@@ -50,6 +68,7 @@ db.close();
|
|
|
50
68
|
### Create / Delete
|
|
51
69
|
|
|
52
70
|
- `createElement(collection, data)` -- Create element, returns numeric ID
|
|
71
|
+
- `updateElement(collection, id, data)` -- Update element by ID
|
|
53
72
|
- `deleteElement(collection, id)` -- Delete element by ID
|
|
54
73
|
|
|
55
74
|
### Read (bulk)
|
|
@@ -57,22 +76,50 @@ db.close();
|
|
|
57
76
|
- `readScalarIntegers(collection, attribute)` -- Read all integer scalars
|
|
58
77
|
- `readScalarFloats(collection, attribute)` -- Read all float scalars
|
|
59
78
|
- `readScalarStrings(collection, attribute)` -- Read all string scalars
|
|
79
|
+
- `readVectorIntegers(collection, attribute)` -- Read all integer vectors
|
|
80
|
+
- `readVectorFloats(collection, attribute)` -- Read all float vectors
|
|
81
|
+
- `readVectorStrings(collection, attribute)` -- Read all string vectors
|
|
82
|
+
- `readSetIntegers(collection, attribute)` -- Read all integer sets
|
|
83
|
+
- `readSetFloats(collection, attribute)` -- Read all float sets
|
|
84
|
+
- `readSetStrings(collection, attribute)` -- Read all string sets
|
|
60
85
|
|
|
61
86
|
### Read (by ID)
|
|
62
87
|
|
|
63
88
|
- `readScalarIntegerById(collection, attribute, id)` -- Read integer or null
|
|
64
89
|
- `readScalarFloatById(collection, attribute, id)` -- Read float or null
|
|
65
90
|
- `readScalarStringById(collection, attribute, id)` -- Read string or null
|
|
91
|
+
- `readVectorIntegersById(collection, attribute, id)` -- Read integer vector
|
|
92
|
+
- `readVectorFloatsById(collection, attribute, id)` -- Read float vector
|
|
93
|
+
- `readVectorStringsById(collection, attribute, id)` -- Read string vector
|
|
94
|
+
- `readSetIntegersById(collection, attribute, id)` -- Read integer set
|
|
95
|
+
- `readSetFloatsById(collection, attribute, id)` -- Read float set
|
|
96
|
+
- `readSetStringsById(collection, attribute, id)` -- Read string set
|
|
66
97
|
|
|
67
98
|
### Read (IDs)
|
|
68
99
|
|
|
69
100
|
- `readElementIds(collection)` -- Read all element IDs in a collection
|
|
70
101
|
|
|
102
|
+
### Metadata
|
|
103
|
+
|
|
104
|
+
- `getScalarMetadata(collection, attribute)` -- Get scalar attribute metadata
|
|
105
|
+
- `getVectorMetadata(collection, attribute)` -- Get vector group metadata
|
|
106
|
+
- `getSetMetadata(collection, attribute)` -- Get set group metadata
|
|
107
|
+
- `getTimeSeriesMetadata(collection, attribute)` -- Get time series group metadata
|
|
108
|
+
- `listScalarAttributes(collection)` -- List all scalar attributes
|
|
109
|
+
- `listVectorGroups(collection)` -- List all vector groups
|
|
110
|
+
- `listSetGroups(collection)` -- List all set groups
|
|
111
|
+
- `listTimeSeriesGroups(collection)` -- List all time series groups
|
|
112
|
+
|
|
113
|
+
### Time Series
|
|
114
|
+
|
|
115
|
+
- `readTimeSeriesGroup(collection, group, id)` -- Read time series data for an element
|
|
116
|
+
- `updateTimeSeriesGroup(collection, group, id, data)` -- Write time series data for an element
|
|
117
|
+
|
|
71
118
|
### Query
|
|
72
119
|
|
|
73
|
-
- `queryString(sql,
|
|
74
|
-
- `queryInteger(sql,
|
|
75
|
-
- `queryFloat(sql,
|
|
120
|
+
- `queryString(sql, parameters?)` -- Query returning string or null
|
|
121
|
+
- `queryInteger(sql, parameters?)` -- Query returning integer or null
|
|
122
|
+
- `queryFloat(sql, parameters?)` -- Query returning float or null
|
|
76
123
|
|
|
77
124
|
Parameters are passed as an array of `number | string | null`.
|
|
78
125
|
|
|
@@ -83,6 +130,30 @@ Parameters are passed as an array of `number | string | null`.
|
|
|
83
130
|
- `rollback()` -- Rollback current transaction
|
|
84
131
|
- `inTransaction()` -- Check if transaction is active
|
|
85
132
|
|
|
133
|
+
### CSV
|
|
134
|
+
|
|
135
|
+
- `exportCsv(collection, group, filePath, options?)` -- Export collection group to CSV file
|
|
136
|
+
- `importCsv(collection, group, filePath, options?)` -- Import CSV file into collection group
|
|
137
|
+
|
|
138
|
+
### Composites
|
|
139
|
+
|
|
140
|
+
- `readScalarsById(collection, id)` -- Read all scalar attributes for an element
|
|
141
|
+
- `readVectorsById(collection, id)` -- Read all vector groups for an element
|
|
142
|
+
- `readSetsById(collection, id)` -- Read all set groups for an element
|
|
143
|
+
|
|
144
|
+
### Introspection
|
|
145
|
+
|
|
146
|
+
- `describe()` -- Print schema info to stdout
|
|
147
|
+
- `isHealthy()` -- Check database health
|
|
148
|
+
- `path()` -- Get database file path
|
|
149
|
+
- `currentVersion()` -- Get current schema version
|
|
150
|
+
|
|
151
|
+
### Lua
|
|
152
|
+
|
|
153
|
+
- `LuaRunner(db)` -- Create Lua script runner with database access
|
|
154
|
+
- `run(script)` -- Execute a Lua script
|
|
155
|
+
- `close()` -- Close the Lua runner
|
|
156
|
+
|
|
86
157
|
## Types
|
|
87
158
|
|
|
88
159
|
Exported types available for TypeScript consumers:
|
|
@@ -93,16 +164,23 @@ Exported types available for TypeScript consumers:
|
|
|
93
164
|
- `ElementData` -- `Record<string, Value | undefined>`
|
|
94
165
|
- `QueryParam` -- `number | string | null`
|
|
95
166
|
- `QuiverError` -- Error class for all Quiver operations
|
|
167
|
+
- `ScalarMetadata` -- Scalar attribute metadata
|
|
168
|
+
- `GroupMetadata` -- Vector/set/time series group metadata
|
|
169
|
+
- `TimeSeriesData` -- Time series column data
|
|
170
|
+
- `CsvOptions` -- CSV import/export options
|
|
96
171
|
|
|
97
172
|
## Development
|
|
98
173
|
|
|
99
174
|
```bash
|
|
100
|
-
bun
|
|
101
|
-
bun
|
|
102
|
-
bun run lint
|
|
103
|
-
bun run format
|
|
175
|
+
bun install # Install dev dependencies (@types/bun, biome)
|
|
176
|
+
bun test test # Run all tests
|
|
177
|
+
bun run lint # Biome linting
|
|
178
|
+
bun run format # Biome formatting (auto-fix)
|
|
104
179
|
```
|
|
105
180
|
|
|
181
|
+
Tests discover the native library from a local `build/bin/` (tier 2). On Windows,
|
|
182
|
+
prepend `build/bin` to `PATH` first (the `test/test.bat` helper does this for you).
|
|
183
|
+
|
|
106
184
|
## License
|
|
107
185
|
|
|
108
186
|
MIT
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/mod.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module
|
|
3
|
+
* Quiver - SQLite wrapper with typed FFI bindings for Bun.
|
|
4
|
+
*
|
|
5
|
+
* @example
|
|
6
|
+
* ```ts
|
|
7
|
+
* import { Database } from "quiverdb";
|
|
8
|
+
*
|
|
9
|
+
* const db = Database.fromSchema("my.db", "schema.sql");
|
|
10
|
+
* db.close();
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export type {
|
|
15
|
+
ArrayValue,
|
|
16
|
+
CsvOptions,
|
|
17
|
+
ElementData,
|
|
18
|
+
GroupMetadata,
|
|
19
|
+
QueryParam,
|
|
20
|
+
ScalarMetadata,
|
|
21
|
+
ScalarValue,
|
|
22
|
+
TimeSeriesData,
|
|
23
|
+
Value,
|
|
24
|
+
} from "./src/index.ts";
|
|
25
|
+
export { Database, LuaRunner, QuiverError } from "./src/index.ts";
|
package/package.json
CHANGED
|
@@ -1,47 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quiverdb",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "SQLite wrapper binding for Quiver via koffi FFI",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"exports": {
|
|
9
|
-
".": {
|
|
10
|
-
"import": "./dist/index.js",
|
|
11
|
-
"types": "./dist/index.d.ts"
|
|
12
|
-
}
|
|
13
|
-
},
|
|
14
|
-
"files": [
|
|
15
|
-
"dist/"
|
|
16
|
-
],
|
|
3
|
+
"version": "0.9.2",
|
|
17
4
|
"license": "MIT",
|
|
18
5
|
"repository": {
|
|
19
6
|
"type": "git",
|
|
20
|
-
"url": "https://github.com/psrenergy/quiver",
|
|
7
|
+
"url": "git+https://github.com/psrenergy/quiver.git",
|
|
21
8
|
"directory": "bindings/js"
|
|
22
9
|
},
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
"
|
|
10
|
+
"type": "module",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": "./mod.ts"
|
|
13
|
+
},
|
|
14
|
+
"types": "./mod.ts",
|
|
15
|
+
"files": [
|
|
16
|
+
"mod.ts",
|
|
17
|
+
"src/**/*.ts",
|
|
18
|
+
"libs/**",
|
|
19
|
+
"LICENSE"
|
|
30
20
|
],
|
|
31
21
|
"scripts": {
|
|
32
|
-
"
|
|
33
|
-
"test": "vitest run",
|
|
34
|
-
"typecheck": "tsc --noEmit",
|
|
22
|
+
"test": "bun test test",
|
|
35
23
|
"lint": "biome check",
|
|
36
24
|
"format": "biome format --write"
|
|
37
25
|
},
|
|
38
|
-
"
|
|
39
|
-
"
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public",
|
|
28
|
+
"provenance": true
|
|
40
29
|
},
|
|
41
30
|
"devDependencies": {
|
|
42
31
|
"@biomejs/biome": "^2.4.6",
|
|
43
|
-
"@types/
|
|
44
|
-
"typescript": "^5.9.3",
|
|
45
|
-
"vitest": "^3.2.1"
|
|
32
|
+
"@types/bun": "latest"
|
|
46
33
|
}
|
|
47
34
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Database } from "./database.ts";
|
|
2
|
+
|
|
3
|
+
Database.prototype.readScalarsById = function (
|
|
4
|
+
this: Database,
|
|
5
|
+
collection: string,
|
|
6
|
+
id: number,
|
|
7
|
+
): Record<string, number | string | null> {
|
|
8
|
+
const attributes = this.listScalarAttributes(collection);
|
|
9
|
+
const result: Record<string, number | string | null> = {};
|
|
10
|
+
|
|
11
|
+
for (const attr of attributes) {
|
|
12
|
+
switch (attr.dataType) {
|
|
13
|
+
case 0: // INTEGER
|
|
14
|
+
result[attr.name] = this.readScalarIntegerById(collection, attr.name, id);
|
|
15
|
+
break;
|
|
16
|
+
case 1: // FLOAT
|
|
17
|
+
result[attr.name] = this.readScalarFloatById(collection, attr.name, id);
|
|
18
|
+
break;
|
|
19
|
+
case 2: // STRING
|
|
20
|
+
case 3: // DATE_TIME
|
|
21
|
+
result[attr.name] = this.readScalarStringById(collection, attr.name, id);
|
|
22
|
+
break;
|
|
23
|
+
default:
|
|
24
|
+
throw new Error(`Unknown data type ${attr.dataType} for attribute '${attr.name}'`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return result;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
Database.prototype.readVectorsById = function (
|
|
32
|
+
this: Database,
|
|
33
|
+
collection: string,
|
|
34
|
+
id: number,
|
|
35
|
+
): Record<string, number[] | string[]> {
|
|
36
|
+
const groups = this.listVectorGroups(collection);
|
|
37
|
+
const result: Record<string, number[] | string[]> = {};
|
|
38
|
+
|
|
39
|
+
for (const group of groups) {
|
|
40
|
+
for (const col of group.valueColumns) {
|
|
41
|
+
switch (col.dataType) {
|
|
42
|
+
case 0: // INTEGER
|
|
43
|
+
result[col.name] = this.readVectorIntegersById(collection, col.name, id);
|
|
44
|
+
break;
|
|
45
|
+
case 1: // FLOAT
|
|
46
|
+
result[col.name] = this.readVectorFloatsById(collection, col.name, id);
|
|
47
|
+
break;
|
|
48
|
+
case 2: // STRING
|
|
49
|
+
case 3: // DATE_TIME
|
|
50
|
+
result[col.name] = this.readVectorStringsById(collection, col.name, id);
|
|
51
|
+
break;
|
|
52
|
+
default:
|
|
53
|
+
throw new Error(`Unknown data type ${col.dataType} for column '${col.name}'`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return result;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
Database.prototype.readSetsById = function (
|
|
62
|
+
this: Database,
|
|
63
|
+
collection: string,
|
|
64
|
+
id: number,
|
|
65
|
+
): Record<string, number[] | string[]> {
|
|
66
|
+
const groups = this.listSetGroups(collection);
|
|
67
|
+
const result: Record<string, number[] | string[]> = {};
|
|
68
|
+
|
|
69
|
+
for (const group of groups) {
|
|
70
|
+
for (const col of group.valueColumns) {
|
|
71
|
+
switch (col.dataType) {
|
|
72
|
+
case 0: // INTEGER
|
|
73
|
+
result[col.name] = this.readSetIntegersById(collection, col.name, id);
|
|
74
|
+
break;
|
|
75
|
+
case 1: // FLOAT
|
|
76
|
+
result[col.name] = this.readSetFloatsById(collection, col.name, id);
|
|
77
|
+
break;
|
|
78
|
+
case 2: // STRING
|
|
79
|
+
case 3: // DATE_TIME
|
|
80
|
+
result[col.name] = this.readSetStringsById(collection, col.name, id);
|
|
81
|
+
break;
|
|
82
|
+
default:
|
|
83
|
+
throw new Error(`Unknown data type ${col.dataType} for column '${col.name}'`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return result;
|
|
89
|
+
};
|
package/src/create.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { ptr } from "bun:ffi";
|
|
2
|
+
import { Database } from "./database.ts";
|
|
3
|
+
import { check, QuiverError } from "./errors.ts";
|
|
4
|
+
import {
|
|
5
|
+
allocNativeFloat64,
|
|
6
|
+
allocNativeInt64,
|
|
7
|
+
allocNativeStringArray,
|
|
8
|
+
allocPtrOut,
|
|
9
|
+
readPtrOut,
|
|
10
|
+
toCString,
|
|
11
|
+
} from "./ffi-helpers.ts";
|
|
12
|
+
import { getSymbols, type NativePointer } from "./loader.ts";
|
|
13
|
+
import type { ElementData, Value } from "./types.ts";
|
|
14
|
+
|
|
15
|
+
type Symbols = ReturnType<typeof getSymbols>;
|
|
16
|
+
|
|
17
|
+
function setElementArray(
|
|
18
|
+
lib: Symbols,
|
|
19
|
+
elemPtr: NativePointer,
|
|
20
|
+
name: string,
|
|
21
|
+
values: unknown[],
|
|
22
|
+
): void {
|
|
23
|
+
const nameBuf = toCString(name);
|
|
24
|
+
|
|
25
|
+
if (values.length === 0) {
|
|
26
|
+
check(lib.quiver_element_set_array_integer(elemPtr, nameBuf.buf, null, 0));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const first = values[0];
|
|
31
|
+
|
|
32
|
+
if (typeof first === "bigint") {
|
|
33
|
+
const arr = allocNativeInt64((values as bigint[]).map(Number));
|
|
34
|
+
check(lib.quiver_element_set_array_integer(elemPtr, nameBuf.buf, arr.ptr, values.length));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (typeof first === "number") {
|
|
39
|
+
const allIntegers = (values as number[]).every((v) => Number.isInteger(v));
|
|
40
|
+
if (allIntegers) {
|
|
41
|
+
const arr = allocNativeInt64(values as number[]);
|
|
42
|
+
check(lib.quiver_element_set_array_integer(elemPtr, nameBuf.buf, arr.ptr, values.length));
|
|
43
|
+
} else {
|
|
44
|
+
const arr = allocNativeFloat64(values as number[]);
|
|
45
|
+
check(lib.quiver_element_set_array_float(elemPtr, nameBuf.buf, arr.ptr, values.length));
|
|
46
|
+
}
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (typeof first === "string") {
|
|
51
|
+
const { table, keepalive: _keepalive } = allocNativeStringArray(values as string[]);
|
|
52
|
+
check(lib.quiver_element_set_array_string(elemPtr, nameBuf.buf, table.ptr, values.length));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
throw new QuiverError(`Unsupported array element type for '${name}': ${typeof first}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function setElementField(lib: Symbols, elemPtr: NativePointer, name: string, value: Value): void {
|
|
60
|
+
const nameBuf = toCString(name);
|
|
61
|
+
|
|
62
|
+
if (value === null) {
|
|
63
|
+
check(lib.quiver_element_set_null(elemPtr, nameBuf.buf));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (typeof value === "bigint") {
|
|
68
|
+
check(lib.quiver_element_set_integer(elemPtr, nameBuf.buf, value));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (typeof value === "number") {
|
|
73
|
+
if (Number.isInteger(value)) {
|
|
74
|
+
check(lib.quiver_element_set_integer(elemPtr, nameBuf.buf, BigInt(value)));
|
|
75
|
+
} else {
|
|
76
|
+
check(lib.quiver_element_set_float(elemPtr, nameBuf.buf, value));
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (typeof value === "string") {
|
|
82
|
+
const valBuf = toCString(value);
|
|
83
|
+
check(lib.quiver_element_set_string(elemPtr, nameBuf.buf, valBuf.buf));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (Array.isArray(value)) {
|
|
88
|
+
setElementArray(lib, elemPtr, name, value);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
throw new QuiverError(`Unsupported value type for '${name}': ${typeof value}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
Database.prototype.createElement = function (
|
|
96
|
+
this: Database,
|
|
97
|
+
collection: string,
|
|
98
|
+
data: ElementData,
|
|
99
|
+
): number {
|
|
100
|
+
const lib = getSymbols();
|
|
101
|
+
const handle = this._handle;
|
|
102
|
+
|
|
103
|
+
const outElem = allocPtrOut();
|
|
104
|
+
check(lib.quiver_element_create(outElem.ptr));
|
|
105
|
+
const elemPtr = readPtrOut(outElem);
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
for (const [key, value] of Object.entries(data)) {
|
|
109
|
+
if (value === undefined) continue;
|
|
110
|
+
setElementField(lib, elemPtr, key, value);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const outIdBuf = new Uint8Array(8);
|
|
114
|
+
const outIdPtr = ptr(outIdBuf);
|
|
115
|
+
const collBuf = toCString(collection);
|
|
116
|
+
check(lib.quiver_database_create_element(handle, collBuf.buf, elemPtr, outIdPtr));
|
|
117
|
+
return Number(new DataView(outIdBuf.buffer).getBigInt64(0, true));
|
|
118
|
+
} finally {
|
|
119
|
+
lib.quiver_element_destroy(elemPtr);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
Database.prototype.updateElement = function (
|
|
124
|
+
this: Database,
|
|
125
|
+
collection: string,
|
|
126
|
+
id: number,
|
|
127
|
+
data: ElementData,
|
|
128
|
+
): void {
|
|
129
|
+
const lib = getSymbols();
|
|
130
|
+
const handle = this._handle;
|
|
131
|
+
|
|
132
|
+
const outElem = allocPtrOut();
|
|
133
|
+
check(lib.quiver_element_create(outElem.ptr));
|
|
134
|
+
const elemPtr = readPtrOut(outElem);
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
for (const [key, value] of Object.entries(data)) {
|
|
138
|
+
if (value === undefined) continue;
|
|
139
|
+
setElementField(lib, elemPtr, key, value);
|
|
140
|
+
}
|
|
141
|
+
const collBuf = toCString(collection);
|
|
142
|
+
check(lib.quiver_database_update_element(handle, collBuf.buf, BigInt(id), elemPtr));
|
|
143
|
+
} finally {
|
|
144
|
+
lib.quiver_element_destroy(elemPtr);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
Database.prototype.deleteElement = function (this: Database, collection: string, id: number): void {
|
|
149
|
+
const lib = getSymbols();
|
|
150
|
+
const collBuf = toCString(collection);
|
|
151
|
+
check(lib.quiver_database_delete_element(this._handle, collBuf.buf, BigInt(id)));
|
|
152
|
+
};
|
package/src/csv.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { ptr } from "bun:ffi";
|
|
2
|
+
import { Database } from "./database.ts";
|
|
3
|
+
import { check } from "./errors.ts";
|
|
4
|
+
import {
|
|
5
|
+
allocNativeInt64,
|
|
6
|
+
allocNativeString,
|
|
7
|
+
allocNativeStringArray,
|
|
8
|
+
nativeAddress,
|
|
9
|
+
toCString,
|
|
10
|
+
} from "./ffi-helpers.ts";
|
|
11
|
+
import { getSymbols } from "./loader.ts";
|
|
12
|
+
import type { Allocation } from "./types.ts";
|
|
13
|
+
|
|
14
|
+
export interface CsvOptions {
|
|
15
|
+
dateTimeFormat?: string;
|
|
16
|
+
enumLabels?: Record<string, Record<string, Record<string, number>>>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Build the 56-byte quiver_csv_options_t struct as an Allocation.
|
|
21
|
+
* Returns [structAllocation, keepalive] where keepalive prevents GC of native allocations.
|
|
22
|
+
*/
|
|
23
|
+
function buildCsvOptionsBuffer(options?: CsvOptions): [Allocation, Allocation[]] {
|
|
24
|
+
const buf = new Uint8Array(56);
|
|
25
|
+
const dv = new DataView(buf.buffer);
|
|
26
|
+
const keepalive: Allocation[] = [];
|
|
27
|
+
|
|
28
|
+
const dtfStr = allocNativeString(options?.dateTimeFormat ?? "");
|
|
29
|
+
keepalive.push(dtfStr);
|
|
30
|
+
dv.setBigUint64(0, nativeAddress(dtfStr.ptr), true);
|
|
31
|
+
|
|
32
|
+
if (!options?.enumLabels || Object.keys(options.enumLabels).length === 0) {
|
|
33
|
+
return [{ ptr: ptr(buf), buf }, keepalive];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const groupAttrNames: string[] = [];
|
|
37
|
+
const groupLocaleNames: string[] = [];
|
|
38
|
+
const groupEntryCounts: number[] = [];
|
|
39
|
+
const allLabels: string[] = [];
|
|
40
|
+
const allValues: number[] = [];
|
|
41
|
+
|
|
42
|
+
for (const [attrName, locales] of Object.entries(options.enumLabels)) {
|
|
43
|
+
for (const [localeName, entries] of Object.entries(locales)) {
|
|
44
|
+
groupAttrNames.push(attrName);
|
|
45
|
+
groupLocaleNames.push(localeName);
|
|
46
|
+
groupEntryCounts.push(Object.keys(entries).length);
|
|
47
|
+
for (const [label, value] of Object.entries(entries)) {
|
|
48
|
+
allLabels.push(label);
|
|
49
|
+
allValues.push(value);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const groupCount = groupAttrNames.length;
|
|
55
|
+
|
|
56
|
+
const { table: attrTable, keepalive: attrPtrs } = allocNativeStringArray(groupAttrNames);
|
|
57
|
+
keepalive.push(attrTable, ...attrPtrs);
|
|
58
|
+
|
|
59
|
+
const { table: localeTable, keepalive: localePtrs } = allocNativeStringArray(groupLocaleNames);
|
|
60
|
+
keepalive.push(localeTable, ...localePtrs);
|
|
61
|
+
|
|
62
|
+
const entryCountsBuf = new Uint8Array(groupCount * 8);
|
|
63
|
+
const entryCountsDv = new DataView(entryCountsBuf.buffer);
|
|
64
|
+
for (let i = 0; i < groupCount; i++) {
|
|
65
|
+
entryCountsDv.setBigUint64(i * 8, BigInt(groupEntryCounts[i]), true);
|
|
66
|
+
}
|
|
67
|
+
const entryCounts: Allocation = { ptr: ptr(entryCountsBuf), buf: entryCountsBuf };
|
|
68
|
+
keepalive.push(entryCounts);
|
|
69
|
+
|
|
70
|
+
const { table: labelsTable, keepalive: labelPtrs } = allocNativeStringArray(allLabels);
|
|
71
|
+
keepalive.push(labelsTable, ...labelPtrs);
|
|
72
|
+
|
|
73
|
+
const valuesArr = allocNativeInt64(allValues);
|
|
74
|
+
keepalive.push(valuesArr);
|
|
75
|
+
|
|
76
|
+
dv.setBigUint64(8, nativeAddress(attrTable.ptr), true);
|
|
77
|
+
dv.setBigUint64(16, nativeAddress(localeTable.ptr), true);
|
|
78
|
+
dv.setBigUint64(24, nativeAddress(entryCounts.ptr), true);
|
|
79
|
+
dv.setBigUint64(32, nativeAddress(labelsTable.ptr), true);
|
|
80
|
+
dv.setBigUint64(40, nativeAddress(valuesArr.ptr), true);
|
|
81
|
+
dv.setBigUint64(48, BigInt(groupCount), true);
|
|
82
|
+
|
|
83
|
+
return [{ ptr: ptr(buf), buf }, keepalive];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
Database.prototype.exportCsv = function (
|
|
87
|
+
this: Database,
|
|
88
|
+
collection: string,
|
|
89
|
+
group: string,
|
|
90
|
+
filePath: string,
|
|
91
|
+
options?: CsvOptions,
|
|
92
|
+
): void {
|
|
93
|
+
const lib = getSymbols();
|
|
94
|
+
const collBuf = toCString(collection);
|
|
95
|
+
const grpBuf = toCString(group);
|
|
96
|
+
const pathBuf = toCString(filePath);
|
|
97
|
+
const [optsBuf, _keepalive] = buildCsvOptionsBuffer(options);
|
|
98
|
+
check(
|
|
99
|
+
lib.quiver_database_export_csv(this._handle, collBuf.buf, grpBuf.buf, pathBuf.buf, optsBuf.ptr),
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
Database.prototype.importCsv = function (
|
|
104
|
+
this: Database,
|
|
105
|
+
collection: string,
|
|
106
|
+
group: string,
|
|
107
|
+
filePath: string,
|
|
108
|
+
options?: CsvOptions,
|
|
109
|
+
): void {
|
|
110
|
+
const lib = getSymbols();
|
|
111
|
+
const collBuf = toCString(collection);
|
|
112
|
+
const grpBuf = toCString(group);
|
|
113
|
+
const pathBuf = toCString(filePath);
|
|
114
|
+
const [optsBuf, _keepalive] = buildCsvOptionsBuffer(options);
|
|
115
|
+
check(
|
|
116
|
+
lib.quiver_database_import_csv(this._handle, collBuf.buf, grpBuf.buf, pathBuf.buf, optsBuf.ptr),
|
|
117
|
+
);
|
|
118
|
+
};
|