flatsql 0.4.3 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/README.md +116 -6
  2. package/dist/artifacts/builder.d.ts +31 -0
  3. package/dist/artifacts/builder.d.ts.map +1 -0
  4. package/dist/artifacts/builder.js +514 -0
  5. package/dist/artifacts/builder.js.map +1 -0
  6. package/dist/artifacts/demo-extractors.d.ts +15 -0
  7. package/dist/artifacts/demo-extractors.d.ts.map +1 -0
  8. package/dist/artifacts/demo-extractors.js +238 -0
  9. package/dist/artifacts/demo-extractors.js.map +1 -0
  10. package/dist/artifacts/index.d.ts +5 -0
  11. package/dist/artifacts/index.d.ts.map +1 -0
  12. package/dist/artifacts/index.js +5 -0
  13. package/dist/artifacts/index.js.map +1 -0
  14. package/dist/artifacts/standalone-builder.d.ts +62 -0
  15. package/dist/artifacts/standalone-builder.d.ts.map +1 -0
  16. package/dist/artifacts/standalone-builder.js +144 -0
  17. package/dist/artifacts/standalone-builder.js.map +1 -0
  18. package/dist/artifacts/transport.d.ts +5 -0
  19. package/dist/artifacts/transport.d.ts.map +1 -0
  20. package/dist/artifacts/transport.js +45 -0
  21. package/dist/artifacts/transport.js.map +1 -0
  22. package/dist/artifacts/types.d.ts +34 -0
  23. package/dist/artifacts/types.d.ts.map +1 -0
  24. package/dist/artifacts/types.js +2 -0
  25. package/dist/artifacts/types.js.map +1 -0
  26. package/dist/artifacts/worker-client.d.ts +32 -0
  27. package/dist/artifacts/worker-client.d.ts.map +1 -0
  28. package/dist/artifacts/worker-client.js +415 -0
  29. package/dist/artifacts/worker-client.js.map +1 -0
  30. package/dist/btree/btree.d.ts.map +1 -1
  31. package/dist/btree/btree.js +13 -14
  32. package/dist/btree/btree.js.map +1 -1
  33. package/dist/core/database.d.ts +2 -0
  34. package/dist/core/database.d.ts.map +1 -1
  35. package/dist/core/database.js +18 -0
  36. package/dist/core/database.js.map +1 -1
  37. package/dist/core/table-store.d.ts +1 -0
  38. package/dist/core/table-store.d.ts.map +1 -1
  39. package/dist/core/table-store.js +31 -0
  40. package/dist/core/table-store.js.map +1 -1
  41. package/dist/index.d.ts +3 -0
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +3 -0
  44. package/dist/index.js.map +1 -1
  45. package/dist/response/artifact.d.ts +5 -0
  46. package/dist/response/artifact.d.ts.map +1 -0
  47. package/dist/response/artifact.js +102 -0
  48. package/dist/response/artifact.js.map +1 -0
  49. package/dist/response/cache-key.d.ts +6 -0
  50. package/dist/response/cache-key.d.ts.map +1 -0
  51. package/dist/response/cache-key.js +114 -0
  52. package/dist/response/cache-key.js.map +1 -0
  53. package/dist/response/index.d.ts +5 -0
  54. package/dist/response/index.d.ts.map +1 -0
  55. package/dist/response/index.js +5 -0
  56. package/dist/response/index.js.map +1 -0
  57. package/dist/response/memory-cache.d.ts +17 -0
  58. package/dist/response/memory-cache.d.ts.map +1 -0
  59. package/dist/response/memory-cache.js +76 -0
  60. package/dist/response/memory-cache.js.map +1 -0
  61. package/dist/response/types.d.ts +51 -0
  62. package/dist/response/types.d.ts.map +1 -0
  63. package/dist/response/types.js +2 -0
  64. package/dist/response/types.js.map +1 -0
  65. package/dist/standalone/index.d.ts +3 -0
  66. package/dist/standalone/index.d.ts.map +1 -0
  67. package/dist/standalone/index.js +3 -0
  68. package/dist/standalone/index.js.map +1 -0
  69. package/dist/standalone/process-client.d.ts +85 -0
  70. package/dist/standalone/process-client.d.ts.map +1 -0
  71. package/dist/standalone/process-client.js +537 -0
  72. package/dist/standalone/process-client.js.map +1 -0
  73. package/dist/standalone/wasmedge-runner.d.ts +19 -0
  74. package/dist/standalone/wasmedge-runner.d.ts.map +1 -0
  75. package/dist/standalone/wasmedge-runner.js +108 -0
  76. package/dist/standalone/wasmedge-runner.js.map +1 -0
  77. package/dist/stress/aggregate.d.ts +3 -0
  78. package/dist/stress/aggregate.d.ts.map +1 -0
  79. package/dist/stress/aggregate.js +61 -0
  80. package/dist/stress/aggregate.js.map +1 -0
  81. package/dist/stress/docker-compose.d.ts +17 -0
  82. package/dist/stress/docker-compose.d.ts.map +1 -0
  83. package/dist/stress/docker-compose.js +156 -0
  84. package/dist/stress/docker-compose.js.map +1 -0
  85. package/dist/stress/index.d.ts +11 -0
  86. package/dist/stress/index.d.ts.map +1 -0
  87. package/dist/stress/index.js +11 -0
  88. package/dist/stress/index.js.map +1 -0
  89. package/dist/stress/metrics.d.ts +3 -0
  90. package/dist/stress/metrics.d.ts.map +1 -0
  91. package/dist/stress/metrics.js +83 -0
  92. package/dist/stress/metrics.js.map +1 -0
  93. package/dist/stress/native-publish-event-workload.d.ts +3 -0
  94. package/dist/stress/native-publish-event-workload.d.ts.map +1 -0
  95. package/dist/stress/native-publish-event-workload.js +455 -0
  96. package/dist/stress/native-publish-event-workload.js.map +1 -0
  97. package/dist/stress/production-gate.d.ts +4 -0
  98. package/dist/stress/production-gate.d.ts.map +1 -0
  99. package/dist/stress/production-gate.js +12 -0
  100. package/dist/stress/production-gate.js.map +1 -0
  101. package/dist/stress/publish-event-workload.d.ts +3 -0
  102. package/dist/stress/publish-event-workload.d.ts.map +1 -0
  103. package/dist/stress/publish-event-workload.js +467 -0
  104. package/dist/stress/publish-event-workload.js.map +1 -0
  105. package/dist/stress/report.d.ts +3 -0
  106. package/dist/stress/report.d.ts.map +1 -0
  107. package/dist/stress/report.js +174 -0
  108. package/dist/stress/report.js.map +1 -0
  109. package/dist/stress/sds-discovery.d.ts +4 -0
  110. package/dist/stress/sds-discovery.d.ts.map +1 -0
  111. package/dist/stress/sds-discovery.js +40 -0
  112. package/dist/stress/sds-discovery.js.map +1 -0
  113. package/dist/stress/smoke-runner.d.ts +3 -0
  114. package/dist/stress/smoke-runner.d.ts.map +1 -0
  115. package/dist/stress/smoke-runner.js +63 -0
  116. package/dist/stress/smoke-runner.js.map +1 -0
  117. package/dist/stress/types.d.ts +219 -0
  118. package/dist/stress/types.d.ts.map +1 -0
  119. package/dist/stress/types.js +2 -0
  120. package/dist/stress/types.js.map +1 -0
  121. package/dist/stress/workload.d.ts +6 -0
  122. package/dist/stress/workload.d.ts.map +1 -0
  123. package/dist/stress/workload.js +144 -0
  124. package/dist/stress/workload.js.map +1 -0
  125. package/docs/agents/response-artifacts.md +65 -0
  126. package/docs/response-artifacts.md +86 -0
  127. package/package.json +36 -6
  128. package/scripts/stress-docker.sh +279 -0
  129. package/sdm/flatsql-spatial.wasm +0 -0
  130. package/stress/docker/Dockerfile +11 -0
  131. package/stress/docker/docker-compose.yml +51 -0
  132. package/stress/sds/aggregate.mjs +97 -0
  133. package/stress/sds/run.mjs +105 -0
  134. package/stress/sds/write-compose.mjs +96 -0
  135. package/wasm/flatsql-artifact.worker.js +847 -0
  136. package/wasm/flatsql-wasi.wasm +0 -0
  137. package/wasm/flatsql.js +2 -16
  138. package/wasm/flatsql.wasm +0 -0
  139. package/wasm/index.d.ts +119 -2
  140. package/wasm/index.js +450 -60
  141. package/wasm/native/flatsql_wasmedge_runner.cpp +858 -0
  142. package/wasm/standalone.d.ts +91 -0
  143. package/wasm/standalone.js +740 -0
package/wasm/index.js CHANGED
@@ -5,6 +5,14 @@ import FlatSQLModule from './flatsql.js';
5
5
 
6
6
  let Module = null;
7
7
  let api = null;
8
+ const textEncoder = new TextEncoder();
9
+
10
+ const PARAM_NULL = 0;
11
+ const PARAM_BOOL = 1;
12
+ const PARAM_INT64 = 2;
13
+ const PARAM_FLOAT64 = 3;
14
+ const PARAM_STRING = 4;
15
+ const PARAM_BYTES = 5;
8
16
 
9
17
  // Security: Track if integrity was verified
10
18
  let integrityVerified = false;
@@ -253,6 +261,22 @@ export async function initFlatSQL(moduleFactoryOrOptions) {
253
261
 
254
262
  // Query execution
255
263
  query: Module.cwrap('flatsql_query', 'number', ['number', 'string']),
264
+ queryParams: Module.cwrap('flatsql_query_params', 'number', ['number', 'string', 'number', 'number', 'number']),
265
+ queryMany: Module.cwrap('flatsql_query_many', 'number', ['number', 'number', 'number', 'number']),
266
+ buildQueryCacheKey: Module.cwrap('flatsql_build_query_cache_key', 'string', ['string', 'string', 'string', 'number', 'number', 'number']),
267
+ buildResponseArtifactCacheKey: Module.cwrap('flatsql_build_response_artifact_cache_key', 'string', ['string', 'string', 'string', 'string', 'string', 'string', 'number', 'number', 'number']),
268
+ registerQueryTemplate: Module.cwrap('flatsql_register_query_template', 'number', ['number', 'string', 'string', 'number']),
269
+ queryTemplate: Module.cwrap('flatsql_query_template', 'number', ['number', 'string', 'number', 'number', 'number']),
270
+ clearQueryCache: Module.cwrap('flatsql_clear_query_cache', null, ['number']),
271
+ configureQueryCache: Module.cwrap('flatsql_configure_query_cache', 'number', ['number', 'number', 'number']),
272
+ queryCacheHits: Module.cwrap('flatsql_query_cache_hits', 'number', ['number']),
273
+ queryCacheMisses: Module.cwrap('flatsql_query_cache_misses', 'number', ['number']),
274
+ queryCacheSize: Module.cwrap('flatsql_query_cache_size', 'number', ['number']),
275
+ queryCacheGeneration: Module.cwrap('flatsql_query_cache_generation', 'number', ['number']),
276
+ queryCacheMaxEntries: Module.cwrap('flatsql_query_cache_max_entries', 'number', ['number']),
277
+ queryCacheMaxRows: Module.cwrap('flatsql_query_cache_max_rows', 'number', ['number']),
278
+ batchResultCount: Module.cwrap('flatsql_batch_result_count', 'number', []),
279
+ selectBatchResult: Module.cwrap('flatsql_select_batch_result', 'number', ['number']),
256
280
  getError: Module.cwrap('flatsql_get_error', 'string', []),
257
281
 
258
282
  // Result access
@@ -264,17 +288,25 @@ export async function initFlatSQL(moduleFactoryOrOptions) {
264
288
  resultCellString: Module.cwrap('flatsql_result_cell_string', 'string', ['number', 'number']),
265
289
  resultCellBlob: Module.cwrap('flatsql_result_cell_blob', 'number', ['number', 'number']),
266
290
  resultCellBlobSize: Module.cwrap('flatsql_result_cell_blob_size', 'number', ['number', 'number']),
291
+ queryRawFlatBufferStream: Module.cwrap('flatsql_query_raw_flatbuffer_stream', 'number', ['number', 'string', 'number', 'number', 'number']),
292
+ responseArtifactData: Module.cwrap('flatsql_response_artifact_data', 'number', []),
293
+ responseArtifactSize: Module.cwrap('flatsql_response_artifact_size', 'number', []),
294
+ responseArtifactRowCount: Module.cwrap('flatsql_response_artifact_row_count', 'number', []),
295
+ responseArtifactColumnCount: Module.cwrap('flatsql_response_artifact_column_count', 'number', []),
267
296
 
268
297
  // Export/Import
269
298
  exportData: Module.cwrap('flatsql_export_data', 'number', ['number']),
270
299
  exportSize: Module.cwrap('flatsql_export_size', 'number', []),
271
300
  loadAndRebuild: Module.cwrap('flatsql_load_and_rebuild', null, ['number', 'number', 'number']),
301
+ reserveStorage: Module.cwrap('flatsql_reserve_storage', null, ['number', 'number']),
302
+ loadFromDb: Module.cwrap('flatsql_load_from_db', null, ['number', 'number']),
272
303
 
273
304
  // Test helpers
274
305
  createTestUser: Module.cwrap('flatsql_create_test_user', 'number', ['number', 'string', 'string', 'number']),
275
306
  createTestPost: Module.cwrap('flatsql_create_test_post', 'number', ['number', 'number', 'string']),
276
307
  createTestMPE: Module.cwrap('flatsql_create_test_mpe', 'number', ['string', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number', 'number']),
277
308
  createTestTelemetry: Module.cwrap('flatsql_create_test_telemetry', 'number', ['number', 'string', 'string', 'string', 'number', 'number', 'number']),
309
+ createTestPublishEvent: Module.cwrap('flatsql_create_test_publish_event', 'number', ['string', 'string', 'number', 'number']),
278
310
  testBufferSize: Module.cwrap('flatsql_test_buffer_size', 'number', []),
279
311
 
280
312
  // Stats
@@ -294,6 +326,15 @@ export async function initFlatSQL(moduleFactoryOrOptions) {
294
326
  getDeletedCount: Module.cwrap('flatsql_get_deleted_count', 'number', ['number', 'string']),
295
327
  clearTombstones: Module.cwrap('flatsql_clear_tombstones', null, ['number', 'string']),
296
328
 
329
+ // Raw FlatBuffer access
330
+ getFlatBufferById: Module.cwrap('flatsql_get_flatbuffer_by_id', 'number', ['number', 'string', 'number']),
331
+ getFlatBufferByEmail: Module.cwrap('flatsql_get_flatbuffer_by_email', 'number', ['number', 'string', 'string']),
332
+ getFlatBufferByIndex: Module.cwrap('flatsql_get_flatbuffer_by_index', 'number', ['number', 'string', 'string', 'number', 'number', 'number']),
333
+ getRawFlatBufferSize: Module.cwrap('flatsql_get_raw_flatbuffer_size', 'number', []),
334
+ getRawFlatBufferSequence: Module.cwrap('flatsql_get_raw_flatbuffer_sequence', 'number', []),
335
+ getStorageBuffer: Module.cwrap('flatsql_get_storage_buffer', 'number', ['number']),
336
+ getStorageSize: Module.cwrap('flatsql_get_storage_size', 'number', ['number']),
337
+
297
338
  // Encryption
298
339
  setEncryptionKey: Module.cwrap('flatsql_set_encryption_key', 'number', ['number', 'number', 'number']),
299
340
  isEncrypted: Module.cwrap('flatsql_is_encrypted', 'number', ['number']),
@@ -318,16 +359,166 @@ export function wasIntegrityVerified() {
318
359
  return integrityVerified;
319
360
  }
320
361
 
362
+ function wasmPointerToByteOffset(ptr) {
363
+ const numeric = Number(ptr);
364
+ if (!Number.isFinite(numeric)) {
365
+ throw new TypeError(`Invalid WASM pointer: ${ptr}`);
366
+ }
367
+ return numeric >>> 0;
368
+ }
369
+
370
+ export const __testWasmPointerToByteOffset = wasmPointerToByteOffset;
371
+
372
+ function heapBytes(ptr, size) {
373
+ return new Uint8Array(Module.HEAPU8.buffer, wasmPointerToByteOffset(ptr), size);
374
+ }
375
+
376
+ function copyHeapBytes(ptr, size) {
377
+ return heapBytes(ptr, size).slice();
378
+ }
379
+
380
+ function setHeapBytes(data, ptr) {
381
+ Module.HEAPU8.set(data, wasmPointerToByteOffset(ptr));
382
+ }
383
+
321
384
  function withHeapBytes(data, callback) {
322
385
  const ptr = Module._malloc(data.length);
323
386
  try {
324
- Module.HEAPU8.set(data, ptr);
387
+ setHeapBytes(data, ptr);
325
388
  return callback(ptr);
326
389
  } finally {
327
390
  Module._free(ptr);
328
391
  }
329
392
  }
330
393
 
394
+ function encodeQueryParams(params) {
395
+ const parts = [];
396
+ let total = 0;
397
+
398
+ for (const value of params) {
399
+ let tag;
400
+ let payload;
401
+
402
+ if (value === null) {
403
+ tag = PARAM_NULL;
404
+ payload = new Uint8Array(0);
405
+ } else if (typeof value === 'boolean') {
406
+ tag = PARAM_BOOL;
407
+ payload = Uint8Array.of(value ? 1 : 0);
408
+ } else if (typeof value === 'number' && Number.isInteger(value)) {
409
+ if (!Number.isSafeInteger(value)) {
410
+ throw new TypeError('Integer query parameters must be safe integers');
411
+ }
412
+ tag = PARAM_INT64;
413
+ payload = new Uint8Array(8);
414
+ new DataView(payload.buffer, payload.byteOffset, payload.byteLength).setBigInt64(0, BigInt(value), true);
415
+ } else if (typeof value === 'number') {
416
+ tag = PARAM_FLOAT64;
417
+ payload = new Uint8Array(8);
418
+ new DataView(payload.buffer, payload.byteOffset, payload.byteLength).setFloat64(0, value, true);
419
+ } else if (typeof value === 'string') {
420
+ tag = PARAM_STRING;
421
+ payload = textEncoder.encode(value);
422
+ } else if (value instanceof Uint8Array) {
423
+ tag = PARAM_BYTES;
424
+ payload = value;
425
+ } else {
426
+ throw new TypeError(`Unsupported query parameter type: ${typeof value}`);
427
+ }
428
+
429
+ const header = new Uint8Array(5);
430
+ const view = new DataView(header.buffer);
431
+ view.setUint8(0, tag);
432
+ view.setUint32(1, payload.length, true);
433
+
434
+ parts.push(header, payload);
435
+ total += header.length + payload.length;
436
+ }
437
+
438
+ const encoded = new Uint8Array(total);
439
+ let offset = 0;
440
+ for (const part of parts) {
441
+ encoded.set(part, offset);
442
+ offset += part.length;
443
+ }
444
+
445
+ return encoded;
446
+ }
447
+
448
+ function encodeQueryRequests(queries) {
449
+ const parts = [];
450
+ let total = 0;
451
+
452
+ for (const query of queries) {
453
+ const sqlBytes = textEncoder.encode(query.sql);
454
+ const paramList = query.params ?? [];
455
+ const paramBytes = encodeQueryParams(paramList);
456
+ const header = new Uint8Array(12);
457
+ const view = new DataView(header.buffer);
458
+ view.setUint32(0, sqlBytes.length, true);
459
+ view.setUint32(4, paramList.length, true);
460
+ view.setUint32(8, paramBytes.length, true);
461
+
462
+ parts.push(header, sqlBytes, paramBytes);
463
+ total += header.length + sqlBytes.length + paramBytes.length;
464
+ }
465
+
466
+ const encoded = new Uint8Array(total);
467
+ let offset = 0;
468
+ for (const part of parts) {
469
+ encoded.set(part, offset);
470
+ offset += part.length;
471
+ }
472
+
473
+ return encoded;
474
+ }
475
+
476
+ function readCellValue(row, col) {
477
+ const type = api.resultCellType(row, col);
478
+ switch (type) {
479
+ case 0:
480
+ return null;
481
+ case 1:
482
+ return api.resultCellNumber(row, col) !== 0;
483
+ case 2:
484
+ case 3:
485
+ case 4:
486
+ return api.resultCellNumber(row, col);
487
+ case 5:
488
+ return api.resultCellString(row, col);
489
+ case 6: {
490
+ const blobPtr = api.resultCellBlob(row, col);
491
+ const blobSize = api.resultCellBlobSize(row, col);
492
+ return blobPtr && blobSize > 0
493
+ ? Array.from(copyHeapBytes(blobPtr, blobSize))
494
+ : [];
495
+ }
496
+ default:
497
+ return null;
498
+ }
499
+ }
500
+
501
+ function readQueryResult() {
502
+ const colCount = api.resultColumnCount();
503
+ const rowCount = api.resultRowCount();
504
+
505
+ const columns = [];
506
+ for (let i = 0; i < colCount; i++) {
507
+ columns.push(api.resultColumnName(i));
508
+ }
509
+
510
+ const rows = [];
511
+ for (let r = 0; r < rowCount; r++) {
512
+ const row = [];
513
+ for (let c = 0; c < colCount; c++) {
514
+ row.push(readCellValue(r, c));
515
+ }
516
+ rows.push(row);
517
+ }
518
+
519
+ return { columns, rows };
520
+ }
521
+
331
522
  function buildSizePrefixedStream(buffers) {
332
523
  let total = 0;
333
524
  for (const buffer of buffers) {
@@ -356,13 +547,19 @@ export class FlatSQL {
356
547
  createTestUser(id, name, email, age) {
357
548
  const ptr = api.createTestUser(id, name, email, age);
358
549
  const size = api.testBufferSize();
359
- return new Uint8Array(Module.HEAPU8.buffer, ptr, size).slice();
550
+ return copyHeapBytes(ptr, size);
360
551
  }
361
552
 
362
553
  createTestPost(id, userId, title) {
363
554
  const ptr = api.createTestPost(id, userId, title);
364
555
  const size = api.testBufferSize();
365
- return new Uint8Array(Module.HEAPU8.buffer, ptr, size).slice();
556
+ return copyHeapBytes(ptr, size);
557
+ }
558
+
559
+ createTestPublishEvent(fileId, recordId, eventIndex, payloadSize) {
560
+ const ptr = api.createTestPublishEvent(fileId, recordId, eventIndex, payloadSize);
561
+ const size = api.testBufferSize();
562
+ return copyHeapBytes(ptr, size);
366
563
  }
367
564
 
368
565
  createTestMPE(entityId, epoch, meanMotion, eccentricity, inclination,
@@ -380,13 +577,13 @@ export class FlatSQL {
380
577
  meanElementTheory
381
578
  );
382
579
  const size = api.testBufferSize();
383
- return new Uint8Array(Module.HEAPU8.buffer, ptr, size).slice();
580
+ return copyHeapBytes(ptr, size);
384
581
  }
385
582
 
386
583
  createTestTelemetry(packetId, spacecraft, subsystem, mode, temperatureC, signalDb, timestampS) {
387
584
  const ptr = api.createTestTelemetry(packetId, spacecraft, subsystem, mode, temperatureC, signalDb, timestampS);
388
585
  const size = api.testBufferSize();
389
- return new Uint8Array(Module.HEAPU8.buffer, ptr, size).slice();
586
+ return copyHeapBytes(ptr, size);
390
587
  }
391
588
 
392
589
  /**
@@ -396,6 +593,58 @@ export class FlatSQL {
396
593
  wasIntegrityVerified() {
397
594
  return integrityVerified;
398
595
  }
596
+
597
+ buildQueryCacheKey(dataset, artifactVersion, queryId, params = []) {
598
+ const encodedParams = encodeQueryParams(params);
599
+ const key = encodedParams.length === 0
600
+ ? api.buildQueryCacheKey(dataset, artifactVersion, queryId, 0, 0, 0)
601
+ : withHeapBytes(
602
+ encodedParams,
603
+ (ptr) => api.buildQueryCacheKey(dataset, artifactVersion, queryId, ptr, encodedParams.length, params.length)
604
+ );
605
+ if (!key) {
606
+ throw new Error(api.getError());
607
+ }
608
+ return key;
609
+ }
610
+
611
+ buildResponseArtifactCacheKey(schemaName, schemaVersion, sql, options = {}) {
612
+ const format = options.format ?? 'json';
613
+ const publishEventKey = options.publishEventKey ?? '';
614
+ const projectionList = (options.projection ?? []).join('\n');
615
+ const params = options.params ?? [];
616
+ const encodedParams = encodeQueryParams(params);
617
+ const key = encodedParams.length === 0
618
+ ? api.buildResponseArtifactCacheKey(
619
+ schemaName,
620
+ schemaVersion,
621
+ sql,
622
+ format,
623
+ publishEventKey,
624
+ projectionList,
625
+ 0,
626
+ 0,
627
+ 0
628
+ )
629
+ : withHeapBytes(
630
+ encodedParams,
631
+ (ptr) => api.buildResponseArtifactCacheKey(
632
+ schemaName,
633
+ schemaVersion,
634
+ sql,
635
+ format,
636
+ publishEventKey,
637
+ projectionList,
638
+ ptr,
639
+ encodedParams.length,
640
+ params.length
641
+ )
642
+ );
643
+ if (!key) {
644
+ throw new Error(api.getError());
645
+ }
646
+ return key;
647
+ }
399
648
  }
400
649
 
401
650
  // Database wrapper class
@@ -465,73 +714,140 @@ export class FlatSQLDatabase {
465
714
  return sources;
466
715
  }
467
716
 
468
- query(sql) {
469
- const success = api.query(this._handle, sql);
717
+ query(sql, params = undefined) {
718
+ const success = params === undefined
719
+ ? api.query(this._handle, sql)
720
+ : (() => {
721
+ const encodedParams = encodeQueryParams(params);
722
+ if (encodedParams.length === 0) {
723
+ return api.queryParams(this._handle, sql, 0, 0, 0);
724
+ }
725
+ return withHeapBytes(
726
+ encodedParams,
727
+ (ptr) => api.queryParams(this._handle, sql, ptr, encodedParams.length, params.length)
728
+ );
729
+ })();
470
730
  if (!success) {
471
731
  throw new Error(api.getError());
472
732
  }
733
+ return readQueryResult();
734
+ }
473
735
 
474
- // Read results
475
- const colCount = api.resultColumnCount();
476
- const rowCount = api.resultRowCount();
736
+ registerQueryTemplate(queryId, sql, cacheable = true) {
737
+ const success = api.registerQueryTemplate(this._handle, queryId, sql, cacheable ? 1 : 0);
738
+ if (!success) {
739
+ throw new Error(api.getError());
740
+ }
741
+ }
477
742
 
478
- const columns = [];
479
- for (let i = 0; i < colCount; i++) {
480
- columns.push(api.resultColumnName(i));
743
+ queryTemplate(queryId, params = []) {
744
+ const encodedParams = encodeQueryParams(params);
745
+ const success = encodedParams.length === 0
746
+ ? api.queryTemplate(this._handle, queryId, 0, 0, 0)
747
+ : withHeapBytes(
748
+ encodedParams,
749
+ (ptr) => api.queryTemplate(this._handle, queryId, ptr, encodedParams.length, params.length)
750
+ );
751
+ if (!success) {
752
+ throw new Error(api.getError());
481
753
  }
754
+ return readQueryResult();
755
+ }
482
756
 
483
- const rows = [];
484
- for (let r = 0; r < rowCount; r++) {
485
- const row = [];
486
- for (let c = 0; c < colCount; c++) {
487
- const type = api.resultCellType(r, c);
488
- switch (type) {
489
- case 0: // null
490
- row.push(null);
491
- break;
492
- case 1: // bool
493
- row.push(api.resultCellNumber(r, c) !== 0);
494
- break;
495
- case 2: // int32
496
- case 3: // int64
497
- case 4: // double
498
- row.push(api.resultCellNumber(r, c));
499
- break;
500
- case 5: // string
501
- row.push(api.resultCellString(r, c));
502
- break;
503
- case 6: // blob
504
- const blobPtr = api.resultCellBlob(r, c);
505
- const blobSize = api.resultCellBlobSize(r, c);
506
- if (blobPtr && blobSize > 0) {
507
- row.push(Array.from(new Uint8Array(Module.HEAPU8.buffer, blobPtr, blobSize)));
508
- } else {
509
- row.push([]);
510
- }
511
- break;
512
- default:
513
- row.push(null);
514
- }
757
+ clearQueryCache() {
758
+ api.clearQueryCache(this._handle);
759
+ }
760
+
761
+ configureQueryCache({ maxEntries, maxRows }) {
762
+ if (!Number.isSafeInteger(maxEntries) || maxEntries <= 0) {
763
+ throw new TypeError(`maxEntries must be a positive safe integer, received: ${maxEntries}`);
764
+ }
765
+ if (!Number.isSafeInteger(maxRows) || maxRows <= 0) {
766
+ throw new TypeError(`maxRows must be a positive safe integer, received: ${maxRows}`);
767
+ }
768
+ const success = api.configureQueryCache(this._handle, maxEntries, maxRows);
769
+ if (!success) {
770
+ throw new Error(api.getError());
771
+ }
772
+ }
773
+
774
+ getQueryCacheStats() {
775
+ return {
776
+ hits: api.queryCacheHits(this._handle),
777
+ misses: api.queryCacheMisses(this._handle),
778
+ size: api.queryCacheSize(this._handle),
779
+ generation: api.queryCacheGeneration(this._handle),
780
+ maxEntries: api.queryCacheMaxEntries(this._handle),
781
+ maxRows: api.queryCacheMaxRows(this._handle)
782
+ };
783
+ }
784
+
785
+ queryMany(queries) {
786
+ if (queries.length === 0) {
787
+ return [];
788
+ }
789
+
790
+ const encodedRequests = encodeQueryRequests(queries);
791
+ const success = withHeapBytes(
792
+ encodedRequests,
793
+ (ptr) => api.queryMany(this._handle, ptr, encodedRequests.length, queries.length)
794
+ );
795
+ if (!success) {
796
+ throw new Error(api.getError());
797
+ }
798
+
799
+ const resultCount = api.batchResultCount();
800
+ const results = [];
801
+ for (let index = 0; index < resultCount; index++) {
802
+ if (!api.selectBatchResult(index)) {
803
+ throw new Error(`Failed to select batch result ${index}`);
515
804
  }
516
- rows.push(row);
805
+ results.push(readQueryResult());
517
806
  }
807
+ return results;
808
+ }
518
809
 
519
- return { columns, rows };
810
+ queryRawFlatBufferStream(sql, params = []) {
811
+ const encodedParams = encodeQueryParams(params);
812
+ const success = encodedParams.length === 0
813
+ ? api.queryRawFlatBufferStream(this._handle, sql, 0, 0, 0)
814
+ : withHeapBytes(
815
+ encodedParams,
816
+ (ptr) => api.queryRawFlatBufferStream(this._handle, sql, ptr, encodedParams.length, params.length)
817
+ );
818
+ if (!success) {
819
+ throw new Error(api.getError());
820
+ }
821
+
822
+ const ptr = api.responseArtifactData();
823
+ const size = api.responseArtifactSize();
824
+ return ptr && size > 0 ? copyHeapBytes(ptr, size) : new Uint8Array();
520
825
  }
521
826
 
522
827
  exportData() {
523
828
  const ptr = api.exportData(this._handle);
524
829
  const size = api.exportSize();
525
- return new Uint8Array(Module.HEAPU8.buffer, ptr, size).slice();
830
+ return copyHeapBytes(ptr, size);
526
831
  }
527
832
 
528
833
  loadAndRebuild(data) {
529
834
  const ptr = Module._malloc(data.length);
530
- Module.HEAPU8.set(data, ptr);
835
+ setHeapBytes(data, ptr);
531
836
  api.loadAndRebuild(this._handle, ptr, data.length);
532
837
  Module._free(ptr);
533
838
  }
534
839
 
840
+ reserveStorageBytes(bytes) {
841
+ api.reserveStorage(this._handle, bytes);
842
+ }
843
+
844
+ loadAndRebuildFrom(sourceDb) {
845
+ if (!sourceDb?._handle) {
846
+ throw new TypeError('loadAndRebuildFrom requires a FlatSQL WASM database.');
847
+ }
848
+ api.loadFromDb(this._handle, sourceDb._handle);
849
+ }
850
+
535
851
  getStats() {
536
852
  const count = api.getStatsCount(this._handle);
537
853
  const stats = [];
@@ -571,6 +887,80 @@ export class FlatSQLDatabase {
571
887
  api.clearTombstones(this._handle, tableName);
572
888
  }
573
889
 
890
+ getFlatBufferById(tableName, id) {
891
+ const ptr = api.getFlatBufferById(this._handle, tableName, id);
892
+ if (!ptr) {
893
+ return null;
894
+ }
895
+ return {
896
+ ptr,
897
+ size: api.getRawFlatBufferSize(),
898
+ sequence: api.getRawFlatBufferSequence()
899
+ };
900
+ }
901
+
902
+ getFlatBufferByEmail(tableName, email) {
903
+ const ptr = api.getFlatBufferByEmail(this._handle, tableName, email);
904
+ if (!ptr) {
905
+ return null;
906
+ }
907
+ return {
908
+ ptr,
909
+ size: api.getRawFlatBufferSize(),
910
+ sequence: api.getRawFlatBufferSequence()
911
+ };
912
+ }
913
+
914
+ getFlatBufferByIndex(tableName, columnName, value) {
915
+ const encodedParams = encodeQueryParams([value]);
916
+ const ptr = withHeapBytes(
917
+ encodedParams,
918
+ (paramPtr) => api.getFlatBufferByIndex(
919
+ this._handle,
920
+ tableName,
921
+ columnName,
922
+ paramPtr,
923
+ encodedParams.length,
924
+ 1
925
+ )
926
+ );
927
+ if (!ptr) {
928
+ const error = api.getError();
929
+ if (error) {
930
+ throw new Error(error);
931
+ }
932
+ return null;
933
+ }
934
+ return {
935
+ ptr,
936
+ size: api.getRawFlatBufferSize(),
937
+ sequence: api.getRawFlatBufferSequence()
938
+ };
939
+ }
940
+
941
+ getFlatBufferDataById(tableName, id) {
942
+ const record = this.getFlatBufferById(tableName, id);
943
+ if (!record) {
944
+ return null;
945
+ }
946
+ return copyHeapBytes(record.ptr, record.size);
947
+ }
948
+
949
+ getFlatBufferDataByIndex(tableName, columnName, value) {
950
+ const record = this.getFlatBufferByIndex(tableName, columnName, value);
951
+ if (!record) {
952
+ return null;
953
+ }
954
+ return copyHeapBytes(record.ptr, record.size);
955
+ }
956
+
957
+ getStorageInfo() {
958
+ return {
959
+ ptr: api.getStorageBuffer(this._handle),
960
+ size: api.getStorageSize(this._handle)
961
+ };
962
+ }
963
+
574
964
  // ==================== Encryption API ====================
575
965
 
576
966
  /**
@@ -579,7 +969,7 @@ export class FlatSQLDatabase {
579
969
  */
580
970
  setEncryptionKey(key) {
581
971
  const ptr = Module._malloc(key.length);
582
- Module.HEAPU8.set(key, ptr);
972
+ setHeapBytes(key, ptr);
583
973
  const result = api.setEncryptionKey(this._handle, ptr, key.length);
584
974
  Module._free(ptr);
585
975
  if (!result) throw new Error(api.getError());
@@ -601,11 +991,11 @@ export class FlatSQLDatabase {
601
991
  */
602
992
  encryptBuffer(buffer, schema) {
603
993
  const bufPtr = Module._malloc(buffer.length);
604
- Module.HEAPU8.set(buffer, bufPtr);
994
+ setHeapBytes(buffer, bufPtr);
605
995
  const schemaPtr = Module._malloc(schema.length);
606
- Module.HEAPU8.set(schema, schemaPtr);
996
+ setHeapBytes(schema, schemaPtr);
607
997
  const result = api.encryptBuffer(this._handle, bufPtr, buffer.length, schemaPtr, schema.length);
608
- const encrypted = new Uint8Array(Module.HEAPU8.buffer, bufPtr, buffer.length).slice();
998
+ const encrypted = copyHeapBytes(bufPtr, buffer.length);
609
999
  Module._free(bufPtr);
610
1000
  Module._free(schemaPtr);
611
1001
  if (!result) throw new Error(api.getError());
@@ -620,11 +1010,11 @@ export class FlatSQLDatabase {
620
1010
  */
621
1011
  decryptBuffer(buffer, schema) {
622
1012
  const bufPtr = Module._malloc(buffer.length);
623
- Module.HEAPU8.set(buffer, bufPtr);
1013
+ setHeapBytes(buffer, bufPtr);
624
1014
  const schemaPtr = Module._malloc(schema.length);
625
- Module.HEAPU8.set(schema, schemaPtr);
1015
+ setHeapBytes(schema, schemaPtr);
626
1016
  const result = api.decryptBuffer(this._handle, bufPtr, buffer.length, schemaPtr, schema.length);
627
- const decrypted = new Uint8Array(Module.HEAPU8.buffer, bufPtr, buffer.length).slice();
1017
+ const decrypted = copyHeapBytes(bufPtr, buffer.length);
628
1018
  Module._free(bufPtr);
629
1019
  Module._free(schemaPtr);
630
1020
  if (!result) throw new Error(api.getError());
@@ -658,10 +1048,10 @@ export class FlatSQLDatabase {
658
1048
  */
659
1049
  computeHMAC(buffer) {
660
1050
  const bufPtr = Module._malloc(buffer.length);
661
- Module.HEAPU8.set(buffer, bufPtr);
1051
+ setHeapBytes(buffer, bufPtr);
662
1052
  const macPtr = Module._malloc(32);
663
1053
  const result = api.computeHMAC(this._handle, bufPtr, buffer.length, macPtr);
664
- const mac = new Uint8Array(Module.HEAPU8.buffer, macPtr, 32).slice();
1054
+ const mac = copyHeapBytes(macPtr, 32);
665
1055
  Module._free(bufPtr);
666
1056
  Module._free(macPtr);
667
1057
  if (!result) throw new Error('HMAC computation failed - is encryption key set?');
@@ -676,9 +1066,9 @@ export class FlatSQLDatabase {
676
1066
  */
677
1067
  verifyHMAC(buffer, mac) {
678
1068
  const bufPtr = Module._malloc(buffer.length);
679
- Module.HEAPU8.set(buffer, bufPtr);
1069
+ setHeapBytes(buffer, bufPtr);
680
1070
  const macPtr = Module._malloc(32);
681
- Module.HEAPU8.set(mac, macPtr);
1071
+ setHeapBytes(mac, macPtr);
682
1072
  const result = api.verifyHMAC(this._handle, bufPtr, buffer.length, macPtr);
683
1073
  Module._free(bufPtr);
684
1074
  Module._free(macPtr);