@aouda/client 0.1.10 → 0.1.12

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/dist/index.cjs CHANGED
@@ -86,7 +86,7 @@ module.exports = __toCommonJS(index_exports);
86
86
  // package.json
87
87
  var package_default = {
88
88
  name: "@aouda/client",
89
- version: "0.1.10",
89
+ version: "0.1.12",
90
90
  description: "Official TypeScript/JavaScript client library for Aouda",
91
91
  type: "module",
92
92
  main: "./dist/index.cjs",
@@ -357,6 +357,7 @@ var AuthHandler = class {
357
357
  this._tokenExpiry = null;
358
358
  this._authenticated = false;
359
359
  this._refreshPromise = null;
360
+ this._onAccessTokenRefreshed = null;
360
361
  this._serverUrl = serverUrl.replace(/\/+$/, "");
361
362
  this._timeout = timeout;
362
363
  this._authBasePath = config.basePath;
@@ -606,10 +607,16 @@ var AuthHandler = class {
606
607
  const thresholdSeconds = this._refreshThresholdMs / 1e3;
607
608
  return this._tokenExpiry <= nowSeconds + thresholdSeconds;
608
609
  }
610
+ setOnAccessTokenRefreshed(handler) {
611
+ this._onAccessTokenRefreshed = handler;
612
+ }
609
613
  async _doRefresh() {
610
614
  try {
611
615
  const result = await this.callRefreshEndpoint();
612
616
  this.handleSignInResult(result);
617
+ if (result.accessToken) {
618
+ this._onAccessTokenRefreshed?.(result.accessToken);
619
+ }
613
620
  return true;
614
621
  } catch {
615
622
  return false;
@@ -2105,20 +2112,22 @@ var AsyncEventQueue = class {
2105
2112
  }
2106
2113
  };
2107
2114
  var TableSubscription = class {
2108
- constructor(transport, tableName, options = {}, baseFilter) {
2115
+ constructor(transport, identity, options = {}, onWarnings) {
2109
2116
  this._queue = new AsyncEventQueue();
2110
2117
  this._active = true;
2111
2118
  this._started = false;
2112
2119
  this._startPromise = null;
2113
2120
  this._lastVersion = 0;
2114
2121
  this._pendingSnapshotRows = [];
2122
+ this._forceFreshSubscribe = false;
2115
2123
  this.id = createStreamingId("sub");
2116
2124
  this._transport = transport;
2117
- this._tableName = tableName;
2118
- this._baseFilter = baseFilter;
2125
+ this._identity = identity;
2119
2126
  this._onSnapshot = options.onSnapshot;
2120
2127
  this._onChange = options.onChange;
2121
2128
  this._onError = options.onError;
2129
+ this._onWarnings = onWarnings;
2130
+ this._conflate = options.conflate;
2122
2131
  this._reconnectHandlerKey = `${this.id}::reconnect`;
2123
2132
  }
2124
2133
  get lastVersion() {
@@ -2169,7 +2178,8 @@ var TableSubscription = class {
2169
2178
  }
2170
2179
  try {
2171
2180
  this._pendingSnapshotRows = [];
2172
- const resumeFrom = this._lastVersion > 0 ? this._lastVersion : void 0;
2181
+ const resumeFrom = this._forceFreshSubscribe || this._lastVersion <= 0 ? void 0 : this._lastVersion;
2182
+ this._forceFreshSubscribe = false;
2173
2183
  await this._sendSubscribe(resumeFrom);
2174
2184
  } catch (error) {
2175
2185
  const wrapped = error instanceof Error ? error : new AoudaConnectionError("Subscription reconnect failed");
@@ -2179,16 +2189,25 @@ var TableSubscription = class {
2179
2189
  async _sendSubscribe(resumeFrom) {
2180
2190
  const message = {
2181
2191
  type: "subscribe",
2182
- id: this.id,
2183
- target: this._tableName
2192
+ id: this.id
2184
2193
  };
2185
- const filter = this._baseFilter;
2186
- if (filter !== void 0) {
2187
- message.filter = filter;
2194
+ if (this._identity.kind === "named") {
2195
+ message.hash = this._identity.hash;
2196
+ if (this._identity.args !== void 0) {
2197
+ message.args = this._identity.args;
2198
+ }
2199
+ } else {
2200
+ message.target = this._identity.target;
2201
+ if (this._identity.filter !== void 0) {
2202
+ message.filter = this._identity.filter;
2203
+ }
2188
2204
  }
2189
2205
  if (resumeFrom !== void 0) {
2190
2206
  message.resume_from = resumeFrom;
2191
2207
  }
2208
+ if (this._conflate !== void 0) {
2209
+ message.conflate = this._conflate;
2210
+ }
2192
2211
  await this._transport.send(message);
2193
2212
  }
2194
2213
  _handleMessage(message) {
@@ -2220,6 +2239,9 @@ var TableSubscription = class {
2220
2239
  }
2221
2240
  _handleSnapshotComplete(message) {
2222
2241
  this._lastVersion = message.version;
2242
+ if (message.warnings != null && message.warnings.length > 0) {
2243
+ this._onWarnings?.(message.warnings);
2244
+ }
2223
2245
  const rows = this._pendingSnapshotRows;
2224
2246
  this._pendingSnapshotRows = [];
2225
2247
  this._onSnapshot?.(rows, message.version);
@@ -2249,10 +2271,19 @@ var TableSubscription = class {
2249
2271
  key: message.key,
2250
2272
  version: message.version
2251
2273
  };
2274
+ if (message.values_skipped !== void 0) {
2275
+ event.values_skipped = message.values_skipped;
2276
+ }
2252
2277
  this._onChange?.(event);
2253
2278
  this._queue.push(event);
2254
2279
  }
2255
2280
  _handleServerError(message) {
2281
+ if (message.code === "SLOW_CONSUMER") {
2282
+ this._pendingSnapshotRows = [];
2283
+ this._lastVersion = 0;
2284
+ this._forceFreshSubscribe = true;
2285
+ return;
2286
+ }
2256
2287
  this._pendingSnapshotRows = [];
2257
2288
  const error = new AoudaConnectionError(
2258
2289
  `Subscription error (${message.code}): ${message.message}`
@@ -2960,16 +2991,16 @@ var TableQuery = class _TableQuery {
2960
2991
  const whereClause = this.buildWhereClause();
2961
2992
  const queryFilter = buildSubscriptionFilter(whereClause);
2962
2993
  const mergedFilter = mergeFilterObjects(queryFilter, options.filter);
2963
- const subscription = new TableSubscription(
2964
- wsTransport,
2965
- this.tableName,
2966
- {
2967
- onSnapshot: options.onSnapshot,
2968
- onChange: options.onChange,
2969
- onError: options.onError
2970
- },
2971
- mergedFilter
2972
- );
2994
+ const subscription = new TableSubscription(wsTransport, {
2995
+ kind: "table",
2996
+ target: this.tableName,
2997
+ filter: mergedFilter
2998
+ }, {
2999
+ onSnapshot: options.onSnapshot,
3000
+ onChange: options.onChange,
3001
+ onError: options.onError,
3002
+ conflate: options.conflate
3003
+ });
2973
3004
  subscription.start();
2974
3005
  return subscription;
2975
3006
  }
@@ -3555,10 +3586,84 @@ function serializeUpdateValue(col, val) {
3555
3586
  else: toNode(cond.else)
3556
3587
  };
3557
3588
  }
3589
+ if ("$upper" in val) {
3590
+ return { type: "call", fn: "upper", args: [unaryCallArg(col, val.$upper)] };
3591
+ }
3592
+ if ("$lower" in val) {
3593
+ return { type: "call", fn: "lower", args: [unaryCallArg(col, val.$lower)] };
3594
+ }
3595
+ if ("$trim" in val) {
3596
+ return { type: "call", fn: "trim", args: [unaryCallArg(col, val.$trim)] };
3597
+ }
3598
+ if ("$concat" in val) {
3599
+ const parts = val.$concat;
3600
+ return {
3601
+ type: "call",
3602
+ fn: "concat",
3603
+ args: parts.map((p) => valueToExprNode(p))
3604
+ };
3605
+ }
3606
+ if ("$substring" in val) {
3607
+ const spec = val.$substring;
3608
+ if (Array.isArray(spec)) {
3609
+ return {
3610
+ type: "call",
3611
+ fn: "substring",
3612
+ args: [{ type: "colRef", col }, ...spec.map((p) => valueToExprNode(p))]
3613
+ };
3614
+ }
3615
+ return {
3616
+ type: "call",
3617
+ fn: "substring",
3618
+ args: [{ type: "colRef", col }, { type: "literal", value: spec }]
3619
+ };
3620
+ }
3621
+ if ("$round" in val) {
3622
+ return {
3623
+ type: "call",
3624
+ fn: "round",
3625
+ args: [
3626
+ { type: "colRef", col },
3627
+ { type: "literal", value: val.$round }
3628
+ ]
3629
+ };
3630
+ }
3631
+ if ("$roundTo" in val) {
3632
+ return {
3633
+ type: "call",
3634
+ fn: "roundTo",
3635
+ args: [
3636
+ { type: "colRef", col },
3637
+ { type: "literal", value: val.$roundTo }
3638
+ ]
3639
+ };
3640
+ }
3641
+ if ("$cast" in val) {
3642
+ return {
3643
+ type: "call",
3644
+ fn: "cast",
3645
+ args: [
3646
+ { type: "colRef", col },
3647
+ { type: "literal", value: val.$cast }
3648
+ ]
3649
+ };
3650
+ }
3558
3651
  throw new Error(
3559
3652
  `Unknown expression operator in update() for column '${col}': ${JSON.stringify(val)}`
3560
3653
  );
3561
3654
  }
3655
+ function unaryCallArg(col, operand) {
3656
+ if (operand === true || operand === 1) {
3657
+ return { type: "colRef", col };
3658
+ }
3659
+ return valueToExprNode(operand);
3660
+ }
3661
+ function valueToExprNode(v) {
3662
+ if (typeof v === "string" && v.startsWith("$")) {
3663
+ return { type: "colRef", col: v.slice(1) };
3664
+ }
3665
+ return { type: "literal", value: v };
3666
+ }
3562
3667
 
3563
3668
  // src/tables.ts
3564
3669
  function validateNonEmptyString(value, name) {
@@ -4719,10 +4824,11 @@ function emptyStats() {
4719
4824
  };
4720
4825
  }
4721
4826
  var NamedQueriesApi = class {
4722
- constructor(transport, database, onWarning) {
4827
+ constructor(transport, database, onWarning, getStreamingTransport) {
4723
4828
  this.transport = transport;
4724
4829
  this.database = database;
4725
4830
  this.onWarning = onWarning;
4831
+ this.getStreamingTransport = getStreamingTransport;
4726
4832
  }
4727
4833
  async execute(hash, args, options) {
4728
4834
  if (typeof hash !== "string" || hash.trim().length === 0) {
@@ -4790,6 +4896,26 @@ var NamedQueriesApi = class {
4790
4896
  return { isError: false, result };
4791
4897
  });
4792
4898
  }
4899
+ subscribe(hash, args, options = {}) {
4900
+ if (typeof hash !== "string" || hash.trim().length === 0) {
4901
+ throw new Error("Named query hash must be a non-empty string");
4902
+ }
4903
+ const subscription = new TableSubscription(
4904
+ this.getStreamingTransport(),
4905
+ { kind: "named", hash, args },
4906
+ {
4907
+ onSnapshot: options.onSnapshot,
4908
+ onChange: options.onChange,
4909
+ onError: options.onError,
4910
+ conflate: options.conflate
4911
+ },
4912
+ (warnings) => {
4913
+ raiseDeprecationWarnings(this.onWarning, warnings);
4914
+ }
4915
+ );
4916
+ subscription.start();
4917
+ return subscription;
4918
+ }
4793
4919
  };
4794
4920
  var NamedMutationsApi = class {
4795
4921
  constructor(transport, database, onWarning) {
@@ -4857,6 +4983,7 @@ var WebSocketTransport = class {
4857
4983
  // Resolved/rejected when the auth handshake completes.
4858
4984
  this._authResolve = null;
4859
4985
  this._authReject = null;
4986
+ this._handshakeComplete = false;
4860
4987
  this._serverUrl = serverUrl;
4861
4988
  this._database = database;
4862
4989
  this._authHandler = options.authHandler;
@@ -4866,6 +4993,11 @@ var WebSocketTransport = class {
4866
4993
  this._maxMissedHeartbeats = options.maxMissedHeartbeats ?? DEFAULT_MAX_MISSED_HEARTBEATS;
4867
4994
  this._enableCompression = options.enableCompression ?? true;
4868
4995
  this._wireMode = options.wireMode ?? "json";
4996
+ this._authHandler?.setOnAccessTokenRefreshed((token) => {
4997
+ if (this._handshakeComplete && this._ws?.readyState === WS_READY_STATE_OPEN) {
4998
+ void this.send({ type: "re_auth", token });
4999
+ }
5000
+ });
4869
5001
  }
4870
5002
  // ─── Public API ────────────────────────────────────────────────────────────
4871
5003
  /** Latest version number from the most recent server `heartbeat` message. */
@@ -5038,6 +5170,7 @@ var WebSocketTransport = class {
5038
5170
  const resolve = this._authResolve;
5039
5171
  this._authResolve = null;
5040
5172
  this._authReject = null;
5173
+ this._handshakeComplete = true;
5041
5174
  if (resolve) resolve();
5042
5175
  break;
5043
5176
  }
@@ -5064,10 +5197,15 @@ var WebSocketTransport = class {
5064
5197
  break;
5065
5198
  }
5066
5199
  const id = "id" in msg ? msg.id : void 0;
5067
- if (id !== void 0 && id !== null) {
5200
+ if (id !== void 0 && id !== null && id !== "") {
5068
5201
  this._handlers.get(id)?.(msg);
5069
5202
  } else {
5070
5203
  this._handlers.get("__global__")?.(msg);
5204
+ for (const [key, handler] of this._handlers) {
5205
+ if (key !== "__global__") {
5206
+ handler(msg);
5207
+ }
5208
+ }
5071
5209
  }
5072
5210
  }
5073
5211
  // ─── Internal: reconnect ──────────────────────────────────────────────────
@@ -5601,7 +5739,8 @@ var AoudaClient = class {
5601
5739
  this._namedQueries = new NamedQueriesApi(
5602
5740
  this.transport,
5603
5741
  this.database,
5604
- onNamedArtifactWarning
5742
+ onNamedArtifactWarning,
5743
+ () => this._getOrCreateWebSocketTransport()
5605
5744
  );
5606
5745
  this._namedMutations = new NamedMutationsApi(
5607
5746
  this.transport,