flatsql 0.2.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.
- package/LICENSE +190 -0
- package/README.md +489 -0
- package/dist/btree/btree.d.ts +34 -0
- package/dist/btree/btree.d.ts.map +1 -0
- package/dist/btree/btree.js +235 -0
- package/dist/btree/btree.js.map +1 -0
- package/dist/btree/index.d.ts +3 -0
- package/dist/btree/index.d.ts.map +1 -0
- package/dist/btree/index.js +3 -0
- package/dist/btree/index.js.map +1 -0
- package/dist/btree/types.d.ts +36 -0
- package/dist/btree/types.d.ts.map +1 -0
- package/dist/btree/types.js +55 -0
- package/dist/btree/types.js.map +1 -0
- package/dist/core/database.d.ts +45 -0
- package/dist/core/database.d.ts.map +1 -0
- package/dist/core/database.js +248 -0
- package/dist/core/database.js.map +1 -0
- package/dist/core/index.d.ts +3 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/table-store.d.ts +28 -0
- package/dist/core/table-store.d.ts.map +1 -0
- package/dist/core/table-store.js +153 -0
- package/dist/core/table-store.js.map +1 -0
- package/dist/flatbuffer-accessor.d.ts +35 -0
- package/dist/flatbuffer-accessor.d.ts.map +1 -0
- package/dist/flatbuffer-accessor.js +114 -0
- package/dist/flatbuffer-accessor.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/schema/index.d.ts +3 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +3 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/parser.d.ts +25 -0
- package/dist/schema/parser.d.ts.map +1 -0
- package/dist/schema/parser.js +394 -0
- package/dist/schema/parser.js.map +1 -0
- package/dist/schema/types.d.ts +33 -0
- package/dist/schema/types.d.ts.map +1 -0
- package/dist/schema/types.js +29 -0
- package/dist/schema/types.js.map +1 -0
- package/dist/storage/index.d.ts +3 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +3 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/stacked-flatbuffers.d.ts +38 -0
- package/dist/storage/stacked-flatbuffers.d.ts.map +1 -0
- package/dist/storage/stacked-flatbuffers.js +253 -0
- package/dist/storage/stacked-flatbuffers.js.map +1 -0
- package/dist/storage/types.d.ts +23 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +27 -0
- package/dist/storage/types.js.map +1 -0
- package/package.json +79 -0
- package/wasm/flatsql-client.js +280 -0
- package/wasm/flatsql.js +2 -0
- package/wasm/flatsql.wasm +0 -0
- package/wasm/flatsql.worker.js +209 -0
- package/wasm/index.d.ts +234 -0
- package/wasm/index.js +463 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
// Stacked FlatBuffers storage implementation
|
|
2
|
+
// Files are append-only sequences of FlatBuffer records with headers
|
|
3
|
+
// Fully readable by standard FlatBuffer tools
|
|
4
|
+
import { MAGIC, VERSION, crc32 } from './types.js';
|
|
5
|
+
// Header sizes (fixed layout for easy seeking)
|
|
6
|
+
const FILE_HEADER_SIZE = 64; // Padded for alignment
|
|
7
|
+
const RECORD_HEADER_SIZE = 48; // Fixed size record header
|
|
8
|
+
// Security: Default maximum storage size (1GB)
|
|
9
|
+
const DEFAULT_MAX_STORAGE_SIZE = 1024 * 1024 * 1024;
|
|
10
|
+
// Security: Warning threshold (80% of max)
|
|
11
|
+
const WARNING_THRESHOLD = 0.8;
|
|
12
|
+
export class StackedFlatBufferStore {
|
|
13
|
+
data;
|
|
14
|
+
dataView;
|
|
15
|
+
writeOffset;
|
|
16
|
+
recordCount = 0n;
|
|
17
|
+
sequence = 0n;
|
|
18
|
+
schemaName;
|
|
19
|
+
// Index of records by offset for quick lookup
|
|
20
|
+
recordIndex = new Map();
|
|
21
|
+
// Security: Storage limits
|
|
22
|
+
maxSize;
|
|
23
|
+
warningEmitted = false;
|
|
24
|
+
onStorageWarning;
|
|
25
|
+
constructor(schemaName, options = {}) {
|
|
26
|
+
// Support legacy signature: constructor(schemaName, initialCapacity)
|
|
27
|
+
const opts = typeof options === 'number'
|
|
28
|
+
? { initialCapacity: options }
|
|
29
|
+
: options;
|
|
30
|
+
this.schemaName = schemaName;
|
|
31
|
+
this.maxSize = opts.maxSize ?? DEFAULT_MAX_STORAGE_SIZE;
|
|
32
|
+
this.onStorageWarning = opts.onStorageWarning;
|
|
33
|
+
const initialCapacity = opts.initialCapacity ?? 1024 * 1024;
|
|
34
|
+
this.data = new Uint8Array(initialCapacity);
|
|
35
|
+
this.dataView = new DataView(this.data.buffer);
|
|
36
|
+
this.writeOffset = BigInt(FILE_HEADER_SIZE);
|
|
37
|
+
this.writeFileHeader();
|
|
38
|
+
}
|
|
39
|
+
writeFileHeader() {
|
|
40
|
+
const view = this.dataView;
|
|
41
|
+
// Magic number
|
|
42
|
+
view.setUint32(0, MAGIC, true);
|
|
43
|
+
// Version
|
|
44
|
+
view.setUint32(4, VERSION, true);
|
|
45
|
+
// Data start offset
|
|
46
|
+
view.setBigUint64(8, BigInt(FILE_HEADER_SIZE), true);
|
|
47
|
+
// Record count (updated on each append)
|
|
48
|
+
view.setBigUint64(16, 0n, true);
|
|
49
|
+
// Schema name (up to 40 bytes, null-terminated)
|
|
50
|
+
const nameBytes = new TextEncoder().encode(this.schemaName);
|
|
51
|
+
const nameLen = Math.min(nameBytes.length, 39);
|
|
52
|
+
this.data.set(nameBytes.slice(0, nameLen), 24);
|
|
53
|
+
this.data[24 + nameLen] = 0;
|
|
54
|
+
}
|
|
55
|
+
updateFileHeader() {
|
|
56
|
+
this.dataView.setBigUint64(16, this.recordCount, true);
|
|
57
|
+
}
|
|
58
|
+
ensureCapacity(needed) {
|
|
59
|
+
const totalNeeded = Number(this.writeOffset) + needed;
|
|
60
|
+
if (totalNeeded <= this.data.length)
|
|
61
|
+
return;
|
|
62
|
+
// Security: Check if we would exceed max size
|
|
63
|
+
if (this.maxSize > 0 && totalNeeded > this.maxSize) {
|
|
64
|
+
throw new Error(`Storage size limit exceeded: requested ${totalNeeded} bytes, ` +
|
|
65
|
+
`maximum is ${this.maxSize} bytes`);
|
|
66
|
+
}
|
|
67
|
+
// Grow by doubling
|
|
68
|
+
let newSize = this.data.length * 2;
|
|
69
|
+
while (newSize < totalNeeded) {
|
|
70
|
+
newSize *= 2;
|
|
71
|
+
}
|
|
72
|
+
// Security: Cap at max size
|
|
73
|
+
if (this.maxSize > 0 && newSize > this.maxSize) {
|
|
74
|
+
newSize = this.maxSize;
|
|
75
|
+
}
|
|
76
|
+
// Security: Emit warning when approaching limit
|
|
77
|
+
if (this.maxSize > 0 && !this.warningEmitted) {
|
|
78
|
+
const threshold = this.maxSize * WARNING_THRESHOLD;
|
|
79
|
+
if (newSize >= threshold) {
|
|
80
|
+
this.warningEmitted = true;
|
|
81
|
+
if (this.onStorageWarning) {
|
|
82
|
+
this.onStorageWarning(newSize, this.maxSize);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
const newData = new Uint8Array(newSize);
|
|
87
|
+
newData.set(this.data);
|
|
88
|
+
this.data = newData;
|
|
89
|
+
this.dataView = new DataView(this.data.buffer);
|
|
90
|
+
}
|
|
91
|
+
// Append a FlatBuffer record
|
|
92
|
+
// Returns the offset where the record was stored
|
|
93
|
+
append(tableName, flatbufferData) {
|
|
94
|
+
const checksum = crc32(flatbufferData);
|
|
95
|
+
const timestamp = BigInt(Date.now());
|
|
96
|
+
const seq = this.sequence++;
|
|
97
|
+
const header = {
|
|
98
|
+
sequence: seq,
|
|
99
|
+
tableName,
|
|
100
|
+
timestamp,
|
|
101
|
+
dataLength: flatbufferData.length,
|
|
102
|
+
checksum,
|
|
103
|
+
};
|
|
104
|
+
const totalSize = RECORD_HEADER_SIZE + flatbufferData.length;
|
|
105
|
+
this.ensureCapacity(totalSize);
|
|
106
|
+
const recordOffset = this.writeOffset;
|
|
107
|
+
// Write record header
|
|
108
|
+
const offset = Number(this.writeOffset);
|
|
109
|
+
this.dataView.setBigUint64(offset, seq, true);
|
|
110
|
+
// Table name (16 bytes, null-padded)
|
|
111
|
+
const tableBytes = new TextEncoder().encode(tableName);
|
|
112
|
+
const tableLen = Math.min(tableBytes.length, 15);
|
|
113
|
+
this.data.set(tableBytes.slice(0, tableLen), offset + 8);
|
|
114
|
+
this.data[offset + 8 + tableLen] = 0;
|
|
115
|
+
this.dataView.setBigUint64(offset + 24, timestamp, true);
|
|
116
|
+
this.dataView.setUint32(offset + 32, flatbufferData.length, true);
|
|
117
|
+
this.dataView.setUint32(offset + 36, checksum, true);
|
|
118
|
+
// Reserved bytes 40-47
|
|
119
|
+
// Write FlatBuffer data
|
|
120
|
+
this.data.set(flatbufferData, offset + RECORD_HEADER_SIZE);
|
|
121
|
+
this.writeOffset += BigInt(totalSize);
|
|
122
|
+
this.recordCount++;
|
|
123
|
+
this.recordIndex.set(recordOffset, header);
|
|
124
|
+
this.updateFileHeader();
|
|
125
|
+
return recordOffset;
|
|
126
|
+
}
|
|
127
|
+
// Read a record at a given offset
|
|
128
|
+
readRecord(offset) {
|
|
129
|
+
const off = Number(offset);
|
|
130
|
+
// Read header
|
|
131
|
+
const sequence = this.dataView.getBigUint64(off, true);
|
|
132
|
+
const tableNameBytes = this.data.slice(off + 8, off + 24);
|
|
133
|
+
const nullIdx = tableNameBytes.indexOf(0);
|
|
134
|
+
const tableName = new TextDecoder().decode(tableNameBytes.slice(0, nullIdx >= 0 ? nullIdx : tableNameBytes.length));
|
|
135
|
+
const timestamp = this.dataView.getBigUint64(off + 24, true);
|
|
136
|
+
const dataLength = this.dataView.getUint32(off + 32, true);
|
|
137
|
+
const checksum = this.dataView.getUint32(off + 36, true);
|
|
138
|
+
// Read data
|
|
139
|
+
const dataStart = off + RECORD_HEADER_SIZE;
|
|
140
|
+
const data = this.data.slice(dataStart, dataStart + dataLength);
|
|
141
|
+
// Verify checksum
|
|
142
|
+
const computedChecksum = crc32(data);
|
|
143
|
+
if (computedChecksum !== checksum) {
|
|
144
|
+
throw new Error(`Checksum mismatch at offset ${offset}: expected ${checksum}, got ${computedChecksum}`);
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
header: {
|
|
148
|
+
sequence,
|
|
149
|
+
tableName,
|
|
150
|
+
timestamp,
|
|
151
|
+
dataLength,
|
|
152
|
+
checksum,
|
|
153
|
+
},
|
|
154
|
+
offset,
|
|
155
|
+
data,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
// Iterate all records
|
|
159
|
+
*iterateRecords() {
|
|
160
|
+
let offset = BigInt(FILE_HEADER_SIZE);
|
|
161
|
+
const end = this.writeOffset;
|
|
162
|
+
while (offset < end) {
|
|
163
|
+
const record = this.readRecord(offset);
|
|
164
|
+
yield record;
|
|
165
|
+
offset += BigInt(RECORD_HEADER_SIZE + record.header.dataLength);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Get records for a specific table
|
|
169
|
+
*iterateTableRecords(tableName) {
|
|
170
|
+
for (const record of this.iterateRecords()) {
|
|
171
|
+
if (record.header.tableName === tableName) {
|
|
172
|
+
yield record;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Get the raw data (for export/persistence)
|
|
177
|
+
getData() {
|
|
178
|
+
return this.data.slice(0, Number(this.writeOffset));
|
|
179
|
+
}
|
|
180
|
+
// Load from existing data
|
|
181
|
+
static fromData(data, options = {}) {
|
|
182
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
183
|
+
// Verify magic
|
|
184
|
+
const magic = view.getUint32(0, true);
|
|
185
|
+
if (magic !== MAGIC) {
|
|
186
|
+
throw new Error(`Invalid file magic: expected ${MAGIC.toString(16)}, got ${magic.toString(16)}`);
|
|
187
|
+
}
|
|
188
|
+
const version = view.getUint32(4, true);
|
|
189
|
+
if (version !== VERSION) {
|
|
190
|
+
throw new Error(`Unsupported version: ${version}`);
|
|
191
|
+
}
|
|
192
|
+
// Read schema name
|
|
193
|
+
const nameBytes = data.slice(24, 64);
|
|
194
|
+
const nullIdx = nameBytes.indexOf(0);
|
|
195
|
+
const schemaName = new TextDecoder().decode(nameBytes.slice(0, nullIdx >= 0 ? nullIdx : nameBytes.length));
|
|
196
|
+
const store = new StackedFlatBufferStore(schemaName, {
|
|
197
|
+
...options,
|
|
198
|
+
initialCapacity: data.length,
|
|
199
|
+
});
|
|
200
|
+
store.data.set(data);
|
|
201
|
+
store.dataView = new DataView(store.data.buffer);
|
|
202
|
+
// Rebuild index by scanning records
|
|
203
|
+
store.writeOffset = BigInt(FILE_HEADER_SIZE);
|
|
204
|
+
store.recordCount = 0n;
|
|
205
|
+
store.sequence = 0n;
|
|
206
|
+
// Find end and rebuild index
|
|
207
|
+
let offset = BigInt(FILE_HEADER_SIZE);
|
|
208
|
+
while (offset < BigInt(data.length)) {
|
|
209
|
+
try {
|
|
210
|
+
const record = store.readRecord(offset);
|
|
211
|
+
store.recordIndex.set(offset, record.header);
|
|
212
|
+
store.recordCount++;
|
|
213
|
+
if (record.header.sequence >= store.sequence) {
|
|
214
|
+
store.sequence = record.header.sequence + 1n;
|
|
215
|
+
}
|
|
216
|
+
offset += BigInt(RECORD_HEADER_SIZE + record.header.dataLength);
|
|
217
|
+
store.writeOffset = offset;
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
// End of valid data
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return store;
|
|
225
|
+
}
|
|
226
|
+
getRecordCount() {
|
|
227
|
+
return this.recordCount;
|
|
228
|
+
}
|
|
229
|
+
getSchemaName() {
|
|
230
|
+
return this.schemaName;
|
|
231
|
+
}
|
|
232
|
+
// Security: Get current storage size
|
|
233
|
+
getCurrentSize() {
|
|
234
|
+
return Number(this.writeOffset);
|
|
235
|
+
}
|
|
236
|
+
// Security: Get maximum storage size (0 = unlimited)
|
|
237
|
+
getMaxSize() {
|
|
238
|
+
return this.maxSize;
|
|
239
|
+
}
|
|
240
|
+
// Security: Get storage usage as a percentage (0-100)
|
|
241
|
+
getUsagePercent() {
|
|
242
|
+
if (this.maxSize === 0)
|
|
243
|
+
return 0;
|
|
244
|
+
return (Number(this.writeOffset) / this.maxSize) * 100;
|
|
245
|
+
}
|
|
246
|
+
// Security: Check if storage is near capacity (>80%)
|
|
247
|
+
isNearCapacity() {
|
|
248
|
+
if (this.maxSize === 0)
|
|
249
|
+
return false;
|
|
250
|
+
return Number(this.writeOffset) >= this.maxSize * WARNING_THRESHOLD;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
//# sourceMappingURL=stacked-flatbuffers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stacked-flatbuffers.js","sourceRoot":"","sources":["../../src/storage/stacked-flatbuffers.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export interface RecordHeader {
|
|
2
|
+
sequence: bigint;
|
|
3
|
+
tableName: string;
|
|
4
|
+
timestamp: bigint;
|
|
5
|
+
dataLength: number;
|
|
6
|
+
checksum: number;
|
|
7
|
+
}
|
|
8
|
+
export interface DataFileHeader {
|
|
9
|
+
magic: number;
|
|
10
|
+
version: number;
|
|
11
|
+
dataStartOffset: bigint;
|
|
12
|
+
recordCount: bigint;
|
|
13
|
+
schemaName: string;
|
|
14
|
+
}
|
|
15
|
+
export interface StoredRecord {
|
|
16
|
+
header: RecordHeader;
|
|
17
|
+
offset: bigint;
|
|
18
|
+
data: Uint8Array;
|
|
19
|
+
}
|
|
20
|
+
export declare const MAGIC = 1179407441;
|
|
21
|
+
export declare const VERSION = 1;
|
|
22
|
+
export declare function crc32(data: Uint8Array): number;
|
|
23
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/storage/types.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,eAAO,MAAM,KAAK,aAAa,CAAC;AAChC,eAAO,MAAM,OAAO,IAAI,CAAC;AAGzB,wBAAgB,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAS9C"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Storage types for stacked FlatBuffers
|
|
2
|
+
export const MAGIC = 0x464c5451; // "FLTQ"
|
|
3
|
+
export const VERSION = 1;
|
|
4
|
+
// Simple CRC32 implementation
|
|
5
|
+
export function crc32(data) {
|
|
6
|
+
let crc = 0xffffffff;
|
|
7
|
+
const table = getCRC32Table();
|
|
8
|
+
for (let i = 0; i < data.length; i++) {
|
|
9
|
+
crc = (crc >>> 8) ^ table[(crc ^ data[i]) & 0xff];
|
|
10
|
+
}
|
|
11
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
12
|
+
}
|
|
13
|
+
let crc32Table = null;
|
|
14
|
+
function getCRC32Table() {
|
|
15
|
+
if (crc32Table)
|
|
16
|
+
return crc32Table;
|
|
17
|
+
crc32Table = new Uint32Array(256);
|
|
18
|
+
for (let i = 0; i < 256; i++) {
|
|
19
|
+
let c = i;
|
|
20
|
+
for (let j = 0; j < 8; j++) {
|
|
21
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
22
|
+
}
|
|
23
|
+
crc32Table[i] = c;
|
|
24
|
+
}
|
|
25
|
+
return crc32Table;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/storage/types.ts"],"names":[],"mappings":"AAAA,wCAAwC;AAwBxC,MAAM,CAAC,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,SAAS;AAC1C,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,CAAC;AAEzB,8BAA8B;AAC9B,MAAM,UAAU,KAAK,CAAC,IAAgB;IACpC,IAAI,GAAG,GAAG,UAAU,CAAC;IACrB,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;IAE9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,GAAG,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACpD,CAAC;IAED,OAAO,CAAC,GAAG,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,UAAU,GAAuB,IAAI,CAAC;AAE1C,SAAS,aAAa;IACpB,IAAI,UAAU;QAAE,OAAO,UAAU,CAAC;IAElC,UAAU,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,CAAC;QACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;YAC3B,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC/C,CAAC;QACD,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACpB,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "flatsql",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "SQL queries over raw FlatBuffer storage - zero conversion, real-time indexing, pure streaming",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./wasm": {
|
|
14
|
+
"types": "./wasm/index.d.ts",
|
|
15
|
+
"import": "./wasm/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist",
|
|
20
|
+
"wasm/*.js",
|
|
21
|
+
"wasm/*.wasm",
|
|
22
|
+
"wasm/*.d.ts",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc",
|
|
28
|
+
"build:wasm": "bash scripts/build-wasm.sh",
|
|
29
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
30
|
+
"test:wasm": "node wasm/test-node.mjs",
|
|
31
|
+
"dev": "tsc --watch",
|
|
32
|
+
"serve": "vite docs --port 8081",
|
|
33
|
+
"prepublishOnly": "npm run build",
|
|
34
|
+
"postinstall": "bash scripts/install-emsdk.sh",
|
|
35
|
+
"emsdk:install": "bash scripts/install-emsdk.sh",
|
|
36
|
+
"emsdk:env": "echo 'Run: source packages/emsdk/emsdk_env.sh'"
|
|
37
|
+
},
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/DigitalArsenal/flatsql.git"
|
|
41
|
+
},
|
|
42
|
+
"homepage": "https://digitalarsenal.github.io/flatsql/",
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/DigitalArsenal/flatsql/issues"
|
|
45
|
+
},
|
|
46
|
+
"keywords": [
|
|
47
|
+
"flatbuffers",
|
|
48
|
+
"sqlite",
|
|
49
|
+
"sql",
|
|
50
|
+
"database",
|
|
51
|
+
"streaming",
|
|
52
|
+
"binary",
|
|
53
|
+
"zero-copy",
|
|
54
|
+
"wasm",
|
|
55
|
+
"webassembly",
|
|
56
|
+
"query",
|
|
57
|
+
"virtual-table"
|
|
58
|
+
],
|
|
59
|
+
"author": "Digital Arsenal <tj@digitalarsenal.io>",
|
|
60
|
+
"license": "Apache-2.0",
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"flatc-wasm": "^26.1.14",
|
|
63
|
+
"sql.js": "^1.10.0"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@types/node": "^20.0.0",
|
|
67
|
+
"typescript": "^5.3.0",
|
|
68
|
+
"jest": "^29.7.0",
|
|
69
|
+
"@types/jest": "^29.5.0",
|
|
70
|
+
"ts-jest": "^29.1.0",
|
|
71
|
+
"vite": "^5.0.0"
|
|
72
|
+
},
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": ">=18.0.0"
|
|
75
|
+
},
|
|
76
|
+
"publishConfig": {
|
|
77
|
+
"access": "public"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FlatSQL Worker Client
|
|
3
|
+
* Provides a clean async API for communicating with the FlatSQL worker.
|
|
4
|
+
* Works in: Browser, Node.js, Deno
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* import { FlatSQLClient, FlatSQLWorkerDatabase } from './flatsql-client.js';
|
|
8
|
+
*
|
|
9
|
+
* const client = new FlatSQLClient();
|
|
10
|
+
* await client.init();
|
|
11
|
+
*
|
|
12
|
+
* const db = await client.createDatabase(schema, 'mydb');
|
|
13
|
+
* await db.registerFileId('USER', 'User');
|
|
14
|
+
* const result = await db.query('SELECT * FROM User');
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Environment detection
|
|
18
|
+
const isBrowser = typeof window !== 'undefined' || (typeof self !== 'undefined' && typeof importScripts === 'undefined');
|
|
19
|
+
const isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
|
|
20
|
+
const isDeno = typeof Deno !== 'undefined';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create a worker based on the environment
|
|
24
|
+
*/
|
|
25
|
+
async function createWorker(workerPath) {
|
|
26
|
+
if (isNode) {
|
|
27
|
+
const { Worker } = await import('worker_threads');
|
|
28
|
+
return new Worker(workerPath);
|
|
29
|
+
} else if (isDeno) {
|
|
30
|
+
return new Worker(new URL(workerPath, import.meta.url), { type: 'module' });
|
|
31
|
+
} else {
|
|
32
|
+
// Browser
|
|
33
|
+
return new Worker(workerPath, { type: 'module' });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Setup message handlers for the worker
|
|
39
|
+
*/
|
|
40
|
+
function setupWorkerHandlers(worker, handlers) {
|
|
41
|
+
if (isNode) {
|
|
42
|
+
worker.on('message', handlers.onMessage);
|
|
43
|
+
worker.on('error', handlers.onError);
|
|
44
|
+
worker.on('exit', handlers.onExit);
|
|
45
|
+
} else {
|
|
46
|
+
worker.onmessage = (e) => handlers.onMessage(e.data);
|
|
47
|
+
worker.onerror = handlers.onError;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* FlatSQL Worker Client
|
|
53
|
+
* Manages communication with the FlatSQL worker.
|
|
54
|
+
*/
|
|
55
|
+
export class FlatSQLClient {
|
|
56
|
+
constructor(workerPath = './flatsql.worker.js') {
|
|
57
|
+
this.workerPath = workerPath;
|
|
58
|
+
this.worker = null;
|
|
59
|
+
this.pending = new Map();
|
|
60
|
+
this.nextId = 1;
|
|
61
|
+
this.ready = false;
|
|
62
|
+
this.readyPromise = null;
|
|
63
|
+
this.databases = new Map();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Initialize the client and worker
|
|
68
|
+
*/
|
|
69
|
+
async init() {
|
|
70
|
+
if (this.worker) return this;
|
|
71
|
+
|
|
72
|
+
this.worker = await createWorker(this.workerPath);
|
|
73
|
+
|
|
74
|
+
this.readyPromise = new Promise((resolve, reject) => {
|
|
75
|
+
const timeout = setTimeout(() => {
|
|
76
|
+
reject(new Error('Worker initialization timeout'));
|
|
77
|
+
}, 30000);
|
|
78
|
+
|
|
79
|
+
const handlers = {
|
|
80
|
+
onMessage: (data) => {
|
|
81
|
+
if (data.type === 'ready') {
|
|
82
|
+
clearTimeout(timeout);
|
|
83
|
+
this.ready = true;
|
|
84
|
+
resolve();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const { id, success, result, error } = data;
|
|
89
|
+
const pending = this.pending.get(id);
|
|
90
|
+
if (pending) {
|
|
91
|
+
this.pending.delete(id);
|
|
92
|
+
if (success) {
|
|
93
|
+
pending.resolve(result);
|
|
94
|
+
} else {
|
|
95
|
+
pending.reject(new Error(error));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
onError: (err) => {
|
|
100
|
+
clearTimeout(timeout);
|
|
101
|
+
reject(err);
|
|
102
|
+
},
|
|
103
|
+
onExit: (code) => {
|
|
104
|
+
if (code !== 0) {
|
|
105
|
+
reject(new Error(`Worker exited with code ${code}`));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
setupWorkerHandlers(this.worker, handlers);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
await this.readyPromise;
|
|
114
|
+
|
|
115
|
+
// Initialize WASM in worker
|
|
116
|
+
await this.call('init');
|
|
117
|
+
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Call a method on the worker
|
|
123
|
+
*/
|
|
124
|
+
async call(method, params = {}) {
|
|
125
|
+
if (!this.worker) {
|
|
126
|
+
throw new Error('Client not initialized. Call init() first.');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const id = this.nextId++;
|
|
130
|
+
|
|
131
|
+
return new Promise((resolve, reject) => {
|
|
132
|
+
this.pending.set(id, { resolve, reject });
|
|
133
|
+
this.worker.postMessage({ id, method, params });
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Create a new database
|
|
139
|
+
*/
|
|
140
|
+
async createDatabase(schema, name = 'default') {
|
|
141
|
+
const dbId = `db_${this.nextId++}`;
|
|
142
|
+
await this.call('createDatabase', { id: dbId, schema, name });
|
|
143
|
+
const db = new FlatSQLWorkerDatabase(this, dbId);
|
|
144
|
+
this.databases.set(dbId, db);
|
|
145
|
+
return db;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Create a test User FlatBuffer (for demos)
|
|
150
|
+
*/
|
|
151
|
+
async createTestUser(id, name, email, age) {
|
|
152
|
+
const result = await this.call('createTestUser', { id, name, email, age });
|
|
153
|
+
return new Uint8Array(result.data);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Create a test Post FlatBuffer (for demos)
|
|
158
|
+
*/
|
|
159
|
+
async createTestPost(id, userId, title) {
|
|
160
|
+
const result = await this.call('createTestPost', { id, userId, title });
|
|
161
|
+
return new Uint8Array(result.data);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Terminate the worker
|
|
166
|
+
*/
|
|
167
|
+
terminate() {
|
|
168
|
+
if (this.worker) {
|
|
169
|
+
if (isNode) {
|
|
170
|
+
this.worker.terminate();
|
|
171
|
+
} else {
|
|
172
|
+
this.worker.terminate();
|
|
173
|
+
}
|
|
174
|
+
this.worker = null;
|
|
175
|
+
this.ready = false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* FlatSQL Database accessed through a worker
|
|
182
|
+
*/
|
|
183
|
+
export class FlatSQLWorkerDatabase {
|
|
184
|
+
constructor(client, dbId) {
|
|
185
|
+
this._client = client;
|
|
186
|
+
this._dbId = dbId;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Register a file ID for routing FlatBuffers to tables
|
|
191
|
+
*/
|
|
192
|
+
async registerFileId(fileId, tableName) {
|
|
193
|
+
await this._client.call('registerFileId', {
|
|
194
|
+
dbId: this._dbId,
|
|
195
|
+
fileId,
|
|
196
|
+
tableName
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Enable built-in demo extractors (User/Post)
|
|
202
|
+
*/
|
|
203
|
+
async enableDemoExtractors() {
|
|
204
|
+
await this._client.call('enableDemoExtractors', { dbId: this._dbId });
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Execute a SQL query
|
|
209
|
+
*/
|
|
210
|
+
async query(sql) {
|
|
211
|
+
return await this._client.call('query', { dbId: this._dbId, sql });
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Ingest a size-prefixed FlatBuffer stream
|
|
216
|
+
*/
|
|
217
|
+
async ingest(data) {
|
|
218
|
+
const result = await this._client.call('ingest', {
|
|
219
|
+
dbId: this._dbId,
|
|
220
|
+
data: Array.from(data)
|
|
221
|
+
});
|
|
222
|
+
return result.bytesConsumed;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Ingest a single FlatBuffer (without size prefix)
|
|
227
|
+
*/
|
|
228
|
+
async ingestOne(data) {
|
|
229
|
+
const result = await this._client.call('ingestOne', {
|
|
230
|
+
dbId: this._dbId,
|
|
231
|
+
data: Array.from(data)
|
|
232
|
+
});
|
|
233
|
+
return result.rowId;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Ingest multiple FlatBuffers (builds stream internally)
|
|
238
|
+
*/
|
|
239
|
+
async ingestStream(buffers) {
|
|
240
|
+
const result = await this._client.call('ingestStream', {
|
|
241
|
+
dbId: this._dbId,
|
|
242
|
+
buffers: buffers.map(b => Array.from(b))
|
|
243
|
+
});
|
|
244
|
+
return result;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Export database as binary blob
|
|
249
|
+
*/
|
|
250
|
+
async exportData() {
|
|
251
|
+
const result = await this._client.call('exportData', { dbId: this._dbId });
|
|
252
|
+
return new Uint8Array(result.data);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* List all tables
|
|
257
|
+
*/
|
|
258
|
+
async listTables() {
|
|
259
|
+
const result = await this._client.call('listTables', { dbId: this._dbId });
|
|
260
|
+
return result.tables;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Get database statistics
|
|
265
|
+
*/
|
|
266
|
+
async getStats() {
|
|
267
|
+
const result = await this._client.call('getStats', { dbId: this._dbId });
|
|
268
|
+
return result.stats;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Delete the database and free resources
|
|
273
|
+
*/
|
|
274
|
+
async delete() {
|
|
275
|
+
await this._client.call('deleteDatabase', { dbId: this._dbId });
|
|
276
|
+
this._client.databases.delete(this._dbId);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export default FlatSQLClient;
|