@ragable/sdk 0.6.12 → 0.6.14

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.mjs CHANGED
@@ -1457,6 +1457,13 @@ var PostgrestTableApi = class {
1457
1457
  this.databaseInstanceId = databaseInstanceId;
1458
1458
  this.table = table;
1459
1459
  }
1460
+ /**
1461
+ * Start a SELECT. Pass a PostgREST `select` string; use embedded resources for joins — see
1462
+ * {@link PostgrestSelectBuilder}.
1463
+ *
1464
+ * @param columns Column list and optional embeds (default `"*"`). Omitted or `"*"` means all base columns.
1465
+ * @typeParam RowResult Row shape returned by the query; defaults to this table’s `Row`. Override when using joins.
1466
+ */
1460
1467
  select(columns = "*") {
1461
1468
  return new PostgrestSelectBuilder(
1462
1469
  this.pgFetch,
@@ -2287,6 +2294,18 @@ var RagableBrowserDatabaseClient = class {
2287
2294
  return payload;
2288
2295
  });
2289
2296
  });
2297
+ /**
2298
+ * Postgres `LISTEN` / `NOTIFY` realtime via server-proxied SSE.
2299
+ * Channels must be lowercase identifiers: `[a-z_][a-z0-9_]*` (max 63 chars).
2300
+ */
2301
+ __publicField(this, "realtime", {
2302
+ subscribe: (params) => subscribeBrowserRealtime(
2303
+ this.options,
2304
+ this.ragableAuth,
2305
+ this.fetchImpl,
2306
+ params
2307
+ )
2308
+ });
2290
2309
  this.fetchImpl = bindFetch(options.fetch);
2291
2310
  }
2292
2311
  /** @internal Called by RagableBrowser to share the Transport instance. */
@@ -2300,6 +2319,89 @@ var RagableBrowserDatabaseClient = class {
2300
2319
  return new Headers(this.options.headers);
2301
2320
  }
2302
2321
  };
2322
+ function followAbortSignal(parent, child) {
2323
+ if (!parent) return;
2324
+ if (parent.aborted) {
2325
+ child.abort();
2326
+ return;
2327
+ }
2328
+ parent.addEventListener("abort", () => child.abort(), { once: true });
2329
+ }
2330
+ async function subscribeBrowserRealtime(options, ragableAuth, fetchImpl, params) {
2331
+ const gid = requireAuthGroupId(options);
2332
+ const token = await resolveDatabaseAuthBearer(options, ragableAuth);
2333
+ const databaseInstanceId = params.databaseInstanceId?.trim() || options.databaseInstanceId?.trim();
2334
+ if (!databaseInstanceId) {
2335
+ throw new RagableError(
2336
+ "realtime.subscribe requires databaseInstanceId in params or on createBrowserClient({ databaseInstanceId })",
2337
+ 400,
2338
+ { code: "SDK_MISSING_DATABASE_INSTANCE_ID" }
2339
+ );
2340
+ }
2341
+ if (!Array.isArray(params.channels) || params.channels.length === 0) {
2342
+ throw new RagableError(
2343
+ "realtime.subscribe requires a non-empty channels array",
2344
+ 400,
2345
+ { code: "SDK_REALTIME_CHANNELS_REQUIRED" }
2346
+ );
2347
+ }
2348
+ const ac = new AbortController();
2349
+ followAbortSignal(params.signal, ac);
2350
+ const headers = new Headers(options.headers);
2351
+ headers.set("Authorization", `Bearer ${token}`);
2352
+ headers.set("Content-Type", "application/json");
2353
+ const response = await fetchImpl(
2354
+ `${normalizeBrowserApiBase()}/auth-groups/${gid}/data/realtime/stream`,
2355
+ {
2356
+ method: "POST",
2357
+ headers,
2358
+ body: JSON.stringify({
2359
+ databaseInstanceId,
2360
+ channels: params.channels
2361
+ }),
2362
+ signal: ac.signal
2363
+ }
2364
+ );
2365
+ const payload = await parseMaybeJsonBody(response);
2366
+ if (!response.ok) {
2367
+ const message = extractErrorMessage(payload, response.statusText);
2368
+ throw new RagableError(message, response.status, payload);
2369
+ }
2370
+ const streamBody = response.body;
2371
+ if (!streamBody) {
2372
+ throw new RagableError(
2373
+ "Realtime stream has no body",
2374
+ 502,
2375
+ { code: "SDK_REALTIME_NO_BODY" }
2376
+ );
2377
+ }
2378
+ void (async () => {
2379
+ try {
2380
+ for await (const evt of readSseStream(streamBody)) {
2381
+ if (evt.type === "realtime:ready") {
2382
+ const ch = evt.channels;
2383
+ params.onReady?.(
2384
+ Array.isArray(ch) ? ch.map((c) => String(c)) : []
2385
+ );
2386
+ } else if (evt.type === "notify") {
2387
+ params.onNotify?.({
2388
+ channel: String(evt.channel ?? ""),
2389
+ payload: evt.payload === void 0 || evt.payload === null ? null : String(evt.payload),
2390
+ processId: Number(evt.processId ?? 0)
2391
+ });
2392
+ } else if (evt.type === "realtime:error") {
2393
+ params.onError?.(String(evt.message ?? "Realtime error"));
2394
+ }
2395
+ }
2396
+ } catch (e) {
2397
+ if (e.name === "AbortError") return;
2398
+ params.onError?.(e.message);
2399
+ }
2400
+ })();
2401
+ return {
2402
+ unsubscribe: () => ac.abort()
2403
+ };
2404
+ }
2303
2405
  var RagableBrowserAgentsClient = class {
2304
2406
  constructor(options) {
2305
2407
  this.options = options;