lazypock 0.9.0 → 0.10.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/dist/index.cjs CHANGED
@@ -61,8 +61,10 @@ var HttpClient = class {
61
61
  /**
62
62
  * @param baseUrl The API base URL (e.g. `http://localhost:4000/api`). Trailing slash stripped.
63
63
  * @param authStore The auth store providing the token for Authorization headers.
64
+ * @param connectionId Optional per-client connection id, sent as the
65
+ * `X-Connection-Id` header on every request (realtime origin-exclusion).
64
66
  */
65
- constructor(baseUrl, authStore) {
67
+ constructor(baseUrl, authStore, connectionId) {
66
68
  /**
67
69
  * Abort controllers for in-flight requests, keyed by their cancellation key
68
70
  * (default `METHOD path`). A new request with the same key aborts the
@@ -80,6 +82,7 @@ var HttpClient = class {
80
82
  this.enableAutoCancellation = true;
81
83
  this.baseUrl = baseUrl.replace(/\/+$/, "");
82
84
  this.authStore = authStore;
85
+ this.connectionId = connectionId;
83
86
  this.defaultFetch = globalThis.fetch.bind(globalThis);
84
87
  }
85
88
  async refreshAuth() {
@@ -232,6 +235,9 @@ var HttpClient = class {
232
235
  if (this.authStore.token) {
233
236
  headers["Authorization"] = "Bearer " + this.authStore.token;
234
237
  }
238
+ if (this.connectionId && !headers["X-Connection-Id"] && !headers["x-connection-id"]) {
239
+ headers["X-Connection-Id"] = this.connectionId;
240
+ }
235
241
  const init = {
236
242
  method,
237
243
  headers,
@@ -947,6 +953,16 @@ var RealtimeService = class {
947
953
  get lastToken() {
948
954
  return this.token;
949
955
  }
956
+ /**
957
+ * Set the per-client connection id (browser tab / device). The SDK
958
+ * appends it as the `connectionId` socket query param at connect time
959
+ * (unless the URL already carries one), matching the `X-Connection-Id`
960
+ * header the HTTP client sends — together they let the server exclude
961
+ * the originating connection from its own realtime broadcasts.
962
+ */
963
+ setConnectionId(id) {
964
+ this.connectionId = id;
965
+ }
950
966
  /**
951
967
  * Set the socket URL. Useful before subscribing so the SDK can
952
968
  * auto-connect on the first {@link subscribe}.
@@ -1068,6 +1084,9 @@ var RealtimeService = class {
1068
1084
  if (token) {
1069
1085
  url += (url.includes("?") ? "&" : "?") + "token=" + encodeURIComponent(token);
1070
1086
  }
1087
+ if (this.connectionId && !url.includes("connectionId=")) {
1088
+ url += (url.includes("?") ? "&" : "?") + "connectionId=" + encodeURIComponent(this.connectionId);
1089
+ }
1071
1090
  this.ws = new WebSocket(url);
1072
1091
  this.ws.onopen = () => {
1073
1092
  this.reconnectAttempt = 0;
@@ -1614,26 +1633,27 @@ export function createClient(options: LazypockClientOptions): TypedClient {
1614
1633
  }
1615
1634
 
1616
1635
  export class TypedClient extends LazypockClient {
1617
- // K extends keyof LazypockCollections (rather than a generic string with a
1618
- // conditional return) so the IDE suggests collection names and unknown names
1619
- // are rejected at compile time:
1620
- // client.collection("posts") // suggested + typed
1621
- // client.collection("nope") // TS error
1636
+ // The constraint is "keyof LazypockCollections | (string & {})" rather than a
1637
+ // bare generic string or a strict keyof:
1638
+ // - the keyof LazypockCollections member is what makes the IDE suggest
1639
+ // collection names, and known literals resolve to the typed service:
1640
+ // client.collection("posts") // suggested + typed
1641
+ // - the string & {} member keeps unknown/dynamic names compiling \u2014 they
1642
+ // resolve to the untyped service, so a studio that manages
1643
+ // user-created collections can still call collection(someString).
1622
1644
  //
1623
- // create()/update() take the collection's *CreateData \u2014 the read model
1624
- // omits password/hidden fields, but the write model carries them.
1625
- // T extends string (rather than keyof LazypockCollections) with a conditional
1626
- // return type, so the IDE suggests collection names AND unknown/dynamic names
1627
- // still resolve to the untyped service \u2014 a studio that manages
1628
- // user-created collections must be able to call collection(someString).
1645
+ // The (string & {}) intersection keeps the union from collapsing to plain
1646
+ // string (which would silently kill the suggestions).
1629
1647
  //
1630
1648
  // create()/update() take the collection's *CreateData \u2014 the read model
1631
1649
  // omits password/hidden fields, but the write model carries them.
1632
- override collection<T extends string>(name: T): T extends keyof LazypockCollections
1633
- ? CollectionService<LazypockCollections[T], LazypockCreateData[T]>
1650
+ override collection<K extends keyof LazypockCollections | (string & {})>(
1651
+ name: K,
1652
+ ): K extends keyof LazypockCollections
1653
+ ? CollectionService<LazypockCollections[K], LazypockCreateData[K]>
1634
1654
  : CollectionService<unknown> {
1635
- return super.collection(name) as T extends keyof LazypockCollections
1636
- ? CollectionService<LazypockCollections[T], LazypockCreateData[T]>
1655
+ return super.collection(name) as K extends keyof LazypockCollections
1656
+ ? CollectionService<LazypockCollections[K], LazypockCreateData[K]>
1637
1657
  : CollectionService<unknown>;
1638
1658
  }
1639
1659
  }
@@ -1675,6 +1695,15 @@ function createDataMemberLines(f) {
1675
1695
  }
1676
1696
 
1677
1697
  // src/lazypock.ts
1698
+ function createConnectionId() {
1699
+ try {
1700
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
1701
+ return crypto.randomUUID();
1702
+ }
1703
+ } catch {
1704
+ }
1705
+ return "conn-" + Math.random().toString(36).slice(2) + Date.now().toString(36);
1706
+ }
1678
1707
  var LazypockClient = class {
1679
1708
  /**
1680
1709
  * Create a new Lazypock client.
@@ -1685,11 +1714,13 @@ var LazypockClient = class {
1685
1714
  const baseUrl = options.baseUrl.replace(/\/+$/, "");
1686
1715
  this.authStore = options.authStore ?? new AuthStore(options.storage ?? memoryStorage);
1687
1716
  this.authReady = options.authStore ? Promise.resolve() : this.authStore.init();
1688
- this.http = new HttpClient(baseUrl, this.authStore);
1717
+ const connectionId = createConnectionId();
1718
+ this.http = new HttpClient(baseUrl, this.authStore, connectionId);
1689
1719
  this.realtime = options.realtime ?? new RealtimeService();
1690
1720
  if (!options.realtime) {
1691
1721
  this.realtime.setUrl(wsUrlFromBaseUrl(baseUrl));
1692
1722
  }
1723
+ this.realtime.setConnectionId(connectionId);
1693
1724
  this.realtime.setTokenProvider(() => this.authStore.token);
1694
1725
  this.authStore.onChange(() => this.realtime.refresh());
1695
1726
  this.authReady.then(() => this.realtime.refresh());