@aouda/client 0.1.9 → 0.1.10

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.
@@ -1,4 +1,4 @@
1
- import { A as AoudaClient } from '../client-CxP9Vnc8.cjs';
1
+ import { A as AoudaClient } from '../client-Dgenr-cE.cjs';
2
2
 
3
3
  /**
4
4
  * CLI for @aouda/client.
@@ -1,4 +1,4 @@
1
- import { A as AoudaClient } from '../client-CxP9Vnc8.js';
1
+ import { A as AoudaClient } from '../client-Dgenr-cE.js';
2
2
 
3
3
  /**
4
4
  * CLI for @aouda/client.
package/dist/cli/index.js CHANGED
@@ -392,7 +392,7 @@ import { fileURLToPath } from "url";
392
392
  // package.json
393
393
  var package_default = {
394
394
  name: "@aouda/client",
395
- version: "0.1.9",
395
+ version: "0.1.10",
396
396
  description: "Official TypeScript/JavaScript client library for Aouda",
397
397
  type: "module",
398
398
  main: "./dist/index.cjs",
@@ -1483,7 +1483,15 @@ var ERROR_CODE_MAP = {
1483
1483
  AUTH_TOKEN_INVALID: AoudaAuthenticationError,
1484
1484
  AUTH_TOKEN_REVOKED: AoudaAuthenticationError,
1485
1485
  AUTH_API_KEY_INVALID: AoudaAuthenticationError,
1486
- AUTH_REQUIRED: AoudaAuthenticationError
1486
+ AUTH_REQUIRED: AoudaAuthenticationError,
1487
+ NAMED_QUERY_NOT_FOUND: AoudaNotFoundError,
1488
+ NAMED_MUTATION_NOT_FOUND: AoudaNotFoundError,
1489
+ NAMED_QUERY_BATCH_EMPTY: AoudaValidationError,
1490
+ NAMED_QUERY_BATCH_TOO_LARGE: AoudaValidationError,
1491
+ NAMED_QUERY_BATCH_MUTATION: AoudaValidationError,
1492
+ NAMED_QUERY_BIND_FAILED: AoudaValidationError,
1493
+ NAMED_QUERY_PARAM_REQUIRED: AoudaValidationError,
1494
+ NAMED_MUTATION_BIND_FAILED: AoudaValidationError
1487
1495
  };
1488
1496
  function createComposedAbortController(...signals) {
1489
1497
  const controller = new AbortController();
@@ -2376,6 +2384,7 @@ var TableSubscription = class {
2376
2384
  this._started = false;
2377
2385
  this._startPromise = null;
2378
2386
  this._lastVersion = 0;
2387
+ this._pendingSnapshotRows = [];
2379
2388
  this.id = createStreamingId("sub");
2380
2389
  this._transport = transport;
2381
2390
  this._tableName = tableName;
@@ -2432,6 +2441,7 @@ var TableSubscription = class {
2432
2441
  return;
2433
2442
  }
2434
2443
  try {
2444
+ this._pendingSnapshotRows = [];
2435
2445
  const resumeFrom = this._lastVersion > 0 ? this._lastVersion : void 0;
2436
2446
  await this._sendSubscribe(resumeFrom);
2437
2447
  } catch (error) {
@@ -2460,7 +2470,13 @@ var TableSubscription = class {
2460
2470
  }
2461
2471
  switch (message.type) {
2462
2472
  case "snapshot":
2463
- this._handleSnapshot(message);
2473
+ this._handleSnapshotPage(message);
2474
+ return;
2475
+ case "snapshot_complete":
2476
+ this._handleSnapshotComplete(message);
2477
+ return;
2478
+ case "gap":
2479
+ void this._handleGap(message);
2464
2480
  return;
2465
2481
  case "change":
2466
2482
  this._handleChange(message);
@@ -2472,9 +2488,13 @@ var TableSubscription = class {
2472
2488
  return;
2473
2489
  }
2474
2490
  }
2475
- _handleSnapshot(message) {
2491
+ _handleSnapshotPage(message) {
2492
+ this._pendingSnapshotRows.push(...message.rows);
2493
+ }
2494
+ _handleSnapshotComplete(message) {
2476
2495
  this._lastVersion = message.version;
2477
- const rows = message.rows;
2496
+ const rows = this._pendingSnapshotRows;
2497
+ this._pendingSnapshotRows = [];
2478
2498
  this._onSnapshot?.(rows, message.version);
2479
2499
  this._queue.push({
2480
2500
  type: "snapshot",
@@ -2482,6 +2502,16 @@ var TableSubscription = class {
2482
2502
  version: message.version
2483
2503
  });
2484
2504
  }
2505
+ async _handleGap(message) {
2506
+ this._pendingSnapshotRows = [];
2507
+ this._lastVersion = message.last_seq;
2508
+ try {
2509
+ await this._sendSubscribe(message.last_seq);
2510
+ } catch (error) {
2511
+ const wrapped = error instanceof Error ? error : new AoudaConnectionError("Subscription gap resume failed");
2512
+ this._notifyError(wrapped);
2513
+ }
2514
+ }
2485
2515
  _handleChange(message) {
2486
2516
  this._lastVersion = message.version;
2487
2517
  const event = {
@@ -2496,6 +2526,7 @@ var TableSubscription = class {
2496
2526
  this._queue.push(event);
2497
2527
  }
2498
2528
  _handleServerError(message) {
2529
+ this._pendingSnapshotRows = [];
2499
2530
  const error = new AoudaConnectionError(
2500
2531
  `Subscription error (${message.code}): ${message.message}`
2501
2532
  );
@@ -4903,6 +4934,147 @@ var MaterializedQueriesApi = class {
4903
4934
  }
4904
4935
  };
4905
4936
 
4937
+ // src/named-queries.ts
4938
+ var MAX_NAMED_QUERY_BATCH_SIZE = 32;
4939
+ function raiseDeprecationWarnings(sink, warnings) {
4940
+ if (warnings == null) return;
4941
+ for (const warning of warnings) {
4942
+ if (warning.code !== "NAMED_QUERY_DEPRECATED" && warning.code !== "NAMED_MUTATION_DEPRECATED") {
4943
+ continue;
4944
+ }
4945
+ const sunset = warning.sunsetAt != null ? ` sunsetAt=${warning.sunsetAt}` : "";
4946
+ const hash = warning.hash != null && warning.hash.length > 0 ? ` hash=${warning.hash}` : "";
4947
+ sink({
4948
+ code: warning.code,
4949
+ hash: warning.hash,
4950
+ sunsetAt: warning.sunsetAt,
4951
+ message: `${warning.code}:${hash}${sunset}`.trim()
4952
+ });
4953
+ }
4954
+ }
4955
+ function emptyStats() {
4956
+ return {
4957
+ rowsScanned: 0,
4958
+ rowsReturned: 0,
4959
+ segmentsAccessed: 0,
4960
+ executionMs: 0
4961
+ };
4962
+ }
4963
+ var NamedQueriesApi = class {
4964
+ constructor(transport, database, onWarning) {
4965
+ this.transport = transport;
4966
+ this.database = database;
4967
+ this.onWarning = onWarning;
4968
+ }
4969
+ async execute(hash, args, options) {
4970
+ if (typeof hash !== "string" || hash.trim().length === 0) {
4971
+ throw new Error("Named query hash must be a non-empty string");
4972
+ }
4973
+ const prefix = databasePath2(this.database);
4974
+ const path4 = `${prefix}/named-queries/${encodeURIComponent(hash)}/query?format=columnar`;
4975
+ const response = await this.transport.post(
4976
+ path4,
4977
+ { args: args ?? {} },
4978
+ { signal: options?.signal }
4979
+ );
4980
+ const rows = columnarToRows(response);
4981
+ raiseDeprecationWarnings(this.onWarning, response.warnings);
4982
+ return {
4983
+ rows,
4984
+ stats: response.stats,
4985
+ warnings: response.warnings
4986
+ };
4987
+ }
4988
+ async batch(items, options) {
4989
+ if (items.length === 0) {
4990
+ throw new AoudaValidationError(
4991
+ "Named query batch requires a non-empty queries array.",
4992
+ "NAMED_QUERY_BATCH_EMPTY",
4993
+ 400
4994
+ );
4995
+ }
4996
+ if (items.length > MAX_NAMED_QUERY_BATCH_SIZE) {
4997
+ throw new AoudaValidationError(
4998
+ `Named query batch exceeds ${MAX_NAMED_QUERY_BATCH_SIZE} elements.`,
4999
+ "NAMED_QUERY_BATCH_TOO_LARGE",
5000
+ 400
5001
+ );
5002
+ }
5003
+ const prefix = databasePath2(this.database);
5004
+ const path4 = `${prefix}/named-queries/batch?format=columnar`;
5005
+ const envelope = await this.transport.post(
5006
+ path4,
5007
+ { queries: items },
5008
+ { signal: options?.signal }
5009
+ );
5010
+ return (envelope.results ?? []).map((slot) => {
5011
+ if (slot.code != null && slot.code.length > 0) {
5012
+ return {
5013
+ isError: true,
5014
+ code: slot.code,
5015
+ error: slot.error
5016
+ };
5017
+ }
5018
+ const columnar = {
5019
+ columns: slot.columns ?? [],
5020
+ types: slot.types ?? [],
5021
+ data: slot.data ?? [],
5022
+ rowCount: slot.rowCount ?? 0,
5023
+ stats: slot.stats ?? emptyStats(),
5024
+ warnings: slot.warnings
5025
+ };
5026
+ const result = {
5027
+ rows: columnarToRows(columnar),
5028
+ stats: columnar.stats,
5029
+ warnings: slot.warnings
5030
+ };
5031
+ raiseDeprecationWarnings(this.onWarning, slot.warnings);
5032
+ return { isError: false, result };
5033
+ });
5034
+ }
5035
+ };
5036
+ var NamedMutationsApi = class {
5037
+ constructor(transport, database, onWarning) {
5038
+ this.transport = transport;
5039
+ this.database = database;
5040
+ this.onWarning = onWarning;
5041
+ }
5042
+ async execute(hash, args) {
5043
+ if (typeof hash !== "string" || hash.trim().length === 0) {
5044
+ throw new Error("Named mutation hash must be a non-empty string");
5045
+ }
5046
+ const prefix = databasePath2(this.database);
5047
+ const path4 = `${prefix}/named-mutations/${encodeURIComponent(hash)}/execute`;
5048
+ const wire = await this.transport.post(path4, {
5049
+ args: args ?? {}
5050
+ });
5051
+ let op = "unknown";
5052
+ let rowsAffected = 0;
5053
+ if (wire.rowsInserted != null) {
5054
+ op = "insert";
5055
+ rowsAffected = wire.rowsInserted;
5056
+ } else if (wire.rowsUpdated != null) {
5057
+ op = "update";
5058
+ rowsAffected = wire.rowsUpdated;
5059
+ } else if (wire.rowsDeleted != null) {
5060
+ op = "delete";
5061
+ rowsAffected = wire.rowsDeleted;
5062
+ }
5063
+ raiseDeprecationWarnings(this.onWarning, wire.warnings);
5064
+ const returning = wire.rows == null ? void 0 : {
5065
+ rows: columnarToRows(wire.rows),
5066
+ stats: wire.rows.stats,
5067
+ warnings: wire.rows.warnings
5068
+ };
5069
+ return {
5070
+ op,
5071
+ rowsAffected,
5072
+ returning,
5073
+ warnings: wire.warnings
5074
+ };
5075
+ }
5076
+ };
5077
+
4906
5078
  // src/streaming/websocket-transport.ts
4907
5079
  import { decode as decodeMessagePack, encode as encodeMessagePack } from "@msgpack/msgpack";
4908
5080
  var DEFAULT_PING_INTERVAL_MS = 2e4;
@@ -5665,6 +5837,19 @@ var AoudaClient = class {
5665
5837
  this.transport,
5666
5838
  this.database
5667
5839
  );
5840
+ const onNamedArtifactWarning = options.onNamedArtifactWarning ?? ((warning) => {
5841
+ console.warn(warning.message);
5842
+ });
5843
+ this._namedQueries = new NamedQueriesApi(
5844
+ this.transport,
5845
+ this.database,
5846
+ onNamedArtifactWarning
5847
+ );
5848
+ this._namedMutations = new NamedMutationsApi(
5849
+ this.transport,
5850
+ this.database,
5851
+ onNamedArtifactWarning
5852
+ );
5668
5853
  }
5669
5854
  /**
5670
5855
  * Connects to the Aouda server.
@@ -5807,6 +5992,18 @@ var AoudaClient = class {
5807
5992
  get materializedQueries() {
5808
5993
  return this._materializedQueries;
5809
5994
  }
5995
+ /**
5996
+ * Hash-only named-query execute and batch. Names are codegen aliases (D-5).
5997
+ */
5998
+ get namedQueries() {
5999
+ return this._namedQueries;
6000
+ }
6001
+ /**
6002
+ * Hash-only named-mutation execute. No batch.
6003
+ */
6004
+ get namedMutations() {
6005
+ return this._namedMutations;
6006
+ }
5810
6007
  /**
5811
6008
  * Access auth operations (signUp, signIn, signOut, refresh, me, changePassword).
5812
6009
  * @returns The auth API.
@@ -5954,7 +6151,7 @@ Usage:
5954
6151
  npx @aouda/client schema <command> [options]
5955
6152
 
5956
6153
  Commands:
5957
- generate Fetch schema from Aouda server and output TypeScript types
6154
+ generate Fetch schema from Aouda server and output TypeScript types (tables + named query/mutation hashes)
5958
6155
  schema Schema management (diff, apply, export, validate, history, seed)
5959
6156
  diff Show migration plan (desired vs current)
5960
6157
  apply Apply schema (use --allow-destructive for drops; --dry-run to preview)