@parcae/sdk 0.3.2 → 0.4.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.
@@ -5,136 +5,104 @@ import { EventEmitter } from 'eventemitter3';
5
5
  import ShortId from 'short-unique-id';
6
6
  import { Model, FrontendAdapter } from '@parcae/model';
7
7
 
8
- // src/transports/socket.ts
8
+ // src/auth-gate.ts
9
+ var AuthGate = class {
10
+ _state = "pending";
11
+ _resolve = null;
12
+ _promise;
13
+ constructor() {
14
+ this._promise = this._makePending();
15
+ }
16
+ get state() {
17
+ return this._state;
18
+ }
19
+ get ready() {
20
+ return this._promise;
21
+ }
22
+ resolve() {
23
+ this._state = "ready";
24
+ if (this._resolve) {
25
+ this._resolve();
26
+ this._resolve = null;
27
+ }
28
+ }
29
+ reset() {
30
+ if (this._state === "ready") {
31
+ this._state = "pending";
32
+ this._promise = this._makePending();
33
+ }
34
+ }
35
+ _makePending() {
36
+ return new Promise((r) => {
37
+ this._resolve = r;
38
+ });
39
+ }
40
+ };
9
41
  var uid = new ShortId({ length: 10 });
10
- var SOCKET_CONNECTIONS = /* @__PURE__ */ new Map();
11
42
  var SocketTransport = class extends EventEmitter {
12
- socket = null;
13
- pendingHandlers = [];
14
- apiKey;
15
- key = null;
43
+ auth = new AuthGate();
44
+ isConnected = false;
45
+ isLoading = false;
46
+ userId = null;
47
+ socket;
16
48
  url;
17
49
  version;
18
- socketPath;
19
- waitForAuth = null;
20
- resolveAuth = null;
21
50
  inflight = /* @__PURE__ */ new Map();
22
- loading;
23
- isLoading = true;
24
- isConnected = false;
25
- isConnecting = false;
26
- authVersion = 0;
27
51
  constructor(config) {
28
52
  super();
29
53
  this.url = config.url;
30
- this.apiKey = config.key ?? null;
31
54
  this.version = config.version ?? "v1";
32
- this.socketPath = config.path ?? "/ws";
33
- this.loading = this.init(this.apiKey);
34
- }
35
- // ── Auth ──────────────────────────────────────────────────────────────
36
- async setKey(key) {
37
- this.apiKey = key;
38
- this.isLoading = true;
39
- this.loading = this.init(key);
40
- await this.loading;
41
- }
42
- // ── Init ──────────────────────────────────────────────────────────────
43
- async init(key) {
44
- this.isLoading = true;
45
- this.isConnecting = true;
46
- try {
47
- this.key = typeof key === "function" ? await key() : key;
48
- const socketKey = `${this.url}:${this.version}`;
49
- if (!SOCKET_CONNECTIONS.has(socketKey)) {
50
- SOCKET_CONNECTIONS.set(
51
- socketKey,
52
- SocketIO(this.url, {
53
- path: this.socketPath,
54
- agent: true,
55
- transports: ["websocket"],
56
- withCredentials: true,
57
- query: { key: this.key ?? void 0, compression: true }
58
- })
59
- );
60
- }
61
- this.socket = SOCKET_CONNECTIONS.get(socketKey);
62
- if (this.key) {
63
- const prevResolve = this.resolveAuth;
64
- this.waitForAuth = new Promise((resolve) => {
65
- this.resolveAuth = resolve;
66
- this.socket.emit("authenticate", this.key, () => {
67
- this.waitForAuth = null;
68
- this.resolveAuth = null;
69
- this.authVersion++;
70
- this.emit("authenticated");
71
- if (prevResolve) prevResolve();
72
- resolve();
73
- });
74
- });
75
- } else {
76
- this.waitForAuth = null;
77
- this.resolveAuth = null;
78
- }
79
- this.isLoading = false;
80
- } catch (error) {
81
- this.isLoading = false;
82
- this.isConnecting = false;
83
- this.emit("error", error);
84
- throw error;
85
- }
86
- this.setupEvents();
87
- }
88
- setupEvents() {
89
- if (!this.socket) return;
90
- for (const { event, handler } of this.pendingHandlers) {
91
- this.socket.on(event, handler);
92
- }
93
- this.pendingHandlers = [];
94
- let hasConnected = this.isConnected;
95
- this.socket.off("connect");
96
- this.socket.off("disconnect");
97
- this.socket.off("error");
55
+ this.socket = SocketIO(this.url, {
56
+ path: config.path ?? "/ws",
57
+ transports: ["websocket"],
58
+ withCredentials: true
59
+ });
98
60
  this.socket.on("connect", () => {
99
- const wasDisconnected = !this.isConnected && hasConnected;
61
+ const wasConnected = this.isConnected;
100
62
  this.isConnected = true;
101
- this.isConnecting = false;
102
- hasConnected = true;
103
- if (wasDisconnected && this.key) {
104
- this.socket.emit("authenticate", this.key, () => {
105
- this.emit("reconnected");
106
- });
107
- } else {
108
- this.emit(wasDisconnected ? "reconnected" : "connected");
109
- }
63
+ this.emit(wasConnected ? "reconnected" : "connected");
110
64
  });
111
65
  this.socket.on("disconnect", () => {
112
66
  this.isConnected = false;
67
+ this.auth.reset();
113
68
  this.emit("disconnected");
114
69
  });
115
- this.socket.on("error", (error) => {
116
- this.emit("error", error);
70
+ this.socket.on("error", (err) => {
71
+ this.emit("error", err);
117
72
  });
118
73
  }
119
- // ── Request/Response ──────────────────────────────────────────────────
120
- async fetch(method, path, data = {}) {
121
- await this.loading;
122
- if (this.waitForAuth) await this.waitForAuth;
123
- if (!this.socket) throw new Error("Socket not initialized");
124
- const upper = method.toUpperCase();
125
- if (upper === "GET") {
126
- const dedupeKey = `GET:${path}:${this.authVersion}:${JSON.stringify(data)}`;
127
- const existing = this.inflight.get(dedupeKey);
128
- if (existing) return existing;
129
- const req = this._doFetch(method, path, data);
130
- this.inflight.set(dedupeKey, req);
131
- req.finally(() => this.inflight.delete(dedupeKey));
132
- return req;
74
+ // ── Authenticate ──────────────────────────────────────────────────
75
+ /**
76
+ * Authenticate with the backend. Resolves the AuthGate when done.
77
+ *
78
+ * - token=string send to server, wait for confirmation
79
+ * - token=null → no auth, resolve gate immediately (unauthenticated)
80
+ */
81
+ async authenticate(token) {
82
+ this.auth.reset();
83
+ this.userId = null;
84
+ if (!token) {
85
+ this.auth.resolve();
86
+ return { userId: null };
133
87
  }
134
- return this._doFetch(method, path, data);
88
+ if (!this.socket.connected) {
89
+ await new Promise((resolve) => {
90
+ if (this.socket.connected) return resolve();
91
+ this.socket.once("connect", () => resolve());
92
+ });
93
+ }
94
+ return new Promise((resolve) => {
95
+ this.socket.emit("authenticate", token, (response) => {
96
+ this.userId = response?.userId ?? null;
97
+ this.auth.resolve();
98
+ resolve({ userId: this.userId });
99
+ });
100
+ });
135
101
  }
136
- async _doFetch(method, path, data = {}) {
137
- if (!this.isConnected) {
102
+ // ── Request/Response ──────────────────────────────────────────────
103
+ async fetch(method, path, data = {}) {
104
+ await this.auth.ready;
105
+ if (!this.socket.connected) {
138
106
  await new Promise((resolve, reject) => {
139
107
  if (this.socket.connected) return resolve();
140
108
  const timeout = setTimeout(() => {
@@ -147,7 +115,7 @@ var SocketTransport = class extends EventEmitter {
147
115
  };
148
116
  const onError = (err) => {
149
117
  cleanup();
150
- reject(new Error(`Connection failed: ${err.message}`));
118
+ reject(err);
151
119
  };
152
120
  const cleanup = () => {
153
121
  clearTimeout(timeout);
@@ -158,21 +126,39 @@ var SocketTransport = class extends EventEmitter {
158
126
  this.socket.once("connect_error", onError);
159
127
  });
160
128
  }
129
+ const upper = method.toUpperCase();
130
+ if (upper === "GET") {
131
+ const dedupeKey = `${path}:${JSON.stringify(data)}`;
132
+ const existing = this.inflight.get(dedupeKey);
133
+ if (existing) return existing;
134
+ const req = this._call(method, path, data);
135
+ this.inflight.set(dedupeKey, req);
136
+ req.finally(() => this.inflight.delete(dedupeKey));
137
+ return req;
138
+ }
139
+ return this._call(method, path, data);
140
+ }
141
+ _call(method, path, data) {
161
142
  const id = uid.rnd();
162
143
  return new Promise((resolve, reject) => {
144
+ const timeout = setTimeout(() => {
145
+ this.socket.off(id);
146
+ reject(new Error(`RPC timeout: ${method} ${path}`));
147
+ }, 3e4);
163
148
  this.socket.once(id, (msg) => {
149
+ clearTimeout(timeout);
164
150
  try {
165
151
  const uncompressed = pako.ungzip(msg, { to: "string" });
166
- const { success, result, message, error } = decompress(
167
- JSON.parse(uncompressed)
168
- );
169
- if (success) resolve(result);
170
- else
152
+ const parsed = decompress(JSON.parse(uncompressed));
153
+ if (parsed.success) {
154
+ resolve(parsed.result);
155
+ } else {
171
156
  reject(
172
157
  new Error(
173
- message || error || `Request failed: ${method}:${path}`
158
+ parsed.message || parsed.error || `${method} ${path} failed`
174
159
  )
175
160
  );
161
+ }
176
162
  } catch (err) {
177
163
  reject(err);
178
164
  }
@@ -187,58 +173,40 @@ var SocketTransport = class extends EventEmitter {
187
173
  });
188
174
  }
189
175
  async get(path, data) {
190
- return this.fetch("get", path, data);
176
+ return this.fetch("GET", path, data);
191
177
  }
192
178
  async post(path, data) {
193
- return this.fetch("post", path, data);
179
+ return this.fetch("POST", path, data);
194
180
  }
195
181
  async put(path, data) {
196
- return this.fetch("put", path, data);
182
+ return this.fetch("PUT", path, data);
197
183
  }
198
184
  async patch(path, data) {
199
- return this.fetch("patch", path, data);
185
+ return this.fetch("PATCH", path, data);
200
186
  }
201
187
  async delete(path, data) {
202
- return this.fetch("delete", path, data);
188
+ return this.fetch("DELETE", path, data);
203
189
  }
204
- // ── Subscriptions ─────────────────────────────────────────────────────
190
+ // ── Subscriptions ─────────────────────────────────────────────────
205
191
  subscribe(event, handler) {
206
- if (this.socket) {
207
- this.socket.on(event, handler);
208
- } else {
209
- this.pendingHandlers.push({ event, handler });
210
- }
211
- return () => this.unsubscribe(event, handler);
192
+ this.socket.on(event, handler);
193
+ return () => this.socket.off(event, handler);
212
194
  }
213
195
  unsubscribe(event, handler) {
214
- if (this.socket) this.socket.off(event, handler);
215
- this.pendingHandlers = this.pendingHandlers.filter(
216
- (h) => !(h.event === event && (!handler || h.handler === handler))
217
- );
196
+ this.socket.off(event, handler);
218
197
  }
219
- // ── Control messages ──────────────────────────────────────────────────
198
+ // ── Control ───────────────────────────────────────────────────────
220
199
  async send(event, ...args) {
221
- if (!this.isConnected && this.socket) {
222
- await new Promise((resolve) => {
223
- if (this.socket.connected) return resolve();
224
- this.socket.once("connect", () => resolve());
225
- });
226
- }
227
- if (this.waitForAuth) await this.waitForAuth;
228
- if (this.socket) this.socket.emit(event, ...args);
200
+ await this.auth.ready;
201
+ this.socket.emit(event, ...args);
229
202
  }
230
- // ── Lifecycle ─────────────────────────────────────────────────────────
203
+ // ── Lifecycle ─────────────────────────────────────────────────────
231
204
  disconnect() {
232
- if (this.socket) {
233
- this.socket.disconnect();
234
- SOCKET_CONNECTIONS.delete(`${this.url}:${this.version}`);
235
- this.socket = null;
236
- this.isConnected = false;
237
- }
205
+ this.socket.disconnect();
206
+ this.isConnected = false;
238
207
  }
239
208
  async reconnect() {
240
- this.loading = this.init(this.apiKey);
241
- await this.loading;
209
+ this.socket.connect();
242
210
  }
243
211
  };
244
212
  var SSETransport = class extends EventEmitter {
@@ -365,17 +333,9 @@ function createClient(config) {
365
333
  if (config.transport && typeof config.transport === "object") {
366
334
  transport = config.transport;
367
335
  } else if (config.transport === "sse") {
368
- transport = new SSETransport({
369
- url: config.url,
370
- key: config.key,
371
- version
372
- });
336
+ transport = new SSETransport({ url: config.url, version });
373
337
  } else {
374
- transport = new SocketTransport({
375
- url: config.url,
376
- key: config.key,
377
- version
378
- });
338
+ transport = new SocketTransport({ url: config.url, version });
379
339
  }
380
340
  Model.use(new FrontendAdapter(transport));
381
341
  const client = {
@@ -399,17 +359,11 @@ function createClient(config) {
399
359
  get isConnected() {
400
360
  return transport.isConnected ?? false;
401
361
  },
402
- get isLoading() {
403
- return transport.isLoading ?? false;
404
- },
405
- get loading() {
406
- return transport.loading ?? Promise.resolve();
407
- },
408
- get authVersion() {
409
- return transport.authVersion ?? 0;
410
- },
411
- async setKey(key) {
412
- if (transport.setKey) await transport.setKey(key);
362
+ async authenticate(token) {
363
+ if (typeof transport.authenticate === "function") {
364
+ return transport.authenticate(token);
365
+ }
366
+ return { userId: null };
413
367
  },
414
368
  on(event, handler) {
415
369
  transport.on?.(event, handler);
@@ -427,6 +381,6 @@ function createClient(config) {
427
381
  return client;
428
382
  }
429
383
 
430
- export { SSETransport, SocketTransport, createClient };
431
- //# sourceMappingURL=chunk-IETH2XOR.js.map
432
- //# sourceMappingURL=chunk-IETH2XOR.js.map
384
+ export { AuthGate, SSETransport, SocketTransport, createClient };
385
+ //# sourceMappingURL=chunk-KFISHMU4.js.map
386
+ //# sourceMappingURL=chunk-KFISHMU4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth-gate.ts","../src/transports/socket.ts","../src/transports/sse.ts","../src/client.ts"],"names":["EventEmitter","body"],"mappings":";;;;;;;;AAUO,IAAM,WAAN,MAAe;AAAA,EACZ,MAAA,GAAwB,SAAA;AAAA,EACxB,QAAA,GAAgC,IAAA;AAAA,EAChC,QAAA;AAAA,EAER,WAAA,GAAc;AACZ,IAAA,IAAA,CAAK,QAAA,GAAW,KAAK,YAAA,EAAa;AAAA,EACpC;AAAA,EAEA,IAAI,KAAA,GAAuB;AACzB,IAAA,OAAO,IAAA,CAAK,MAAA;AAAA,EACd;AAAA,EAEA,IAAI,KAAA,GAAuB;AACzB,IAAA,OAAO,IAAA,CAAK,QAAA;AAAA,EACd;AAAA,EAEA,OAAA,GAAgB;AACd,IAAA,IAAA,CAAK,MAAA,GAAS,OAAA;AACd,IAAA,IAAI,KAAK,QAAA,EAAU;AACjB,MAAA,IAAA,CAAK,QAAA,EAAS;AACd,MAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAAA,IAClB;AAAA,EACF;AAAA,EAEA,KAAA,GAAc;AACZ,IAAA,IAAI,IAAA,CAAK,WAAW,OAAA,EAAS;AAC3B,MAAA,IAAA,CAAK,MAAA,GAAS,SAAA;AACd,MAAA,IAAA,CAAK,QAAA,GAAW,KAAK,YAAA,EAAa;AAAA,IACpC;AAAA,EACF;AAAA,EAEQ,YAAA,GAA8B;AACpC,IAAA,OAAO,IAAI,OAAA,CAAc,CAAC,CAAA,KAAM;AAC9B,MAAA,IAAA,CAAK,QAAA,GAAW,CAAA;AAAA,IAClB,CAAC,CAAA;AAAA,EACH;AACF;AC7BA,IAAM,MAAM,IAAI,OAAA,CAAQ,EAAE,MAAA,EAAQ,IAAI,CAAA;AAQ/B,IAAM,eAAA,GAAN,cAA8B,YAAA,CAAkC;AAAA,EAC9D,IAAA,GAAO,IAAI,QAAA,EAAS;AAAA,EACpB,WAAA,GAAc,KAAA;AAAA,EACd,SAAA,GAAY,KAAA;AAAA,EACZ,MAAA,GAAwB,IAAA;AAAA,EAEvB,MAAA;AAAA,EACA,GAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA,uBAAe,GAAA,EAA0B;AAAA,EAEjD,YAAY,MAAA,EAA+B;AACzC,IAAA,KAAA,EAAM;AACN,IAAA,IAAA,CAAK,MAAM,MAAA,CAAO,GAAA;AAClB,IAAA,IAAA,CAAK,OAAA,GAAU,OAAO,OAAA,IAAW,IAAA;AAEjC,IAAA,IAAA,CAAK,MAAA,GAAS,QAAA,CAAS,IAAA,CAAK,GAAA,EAAK;AAAA,MAC/B,IAAA,EAAM,OAAO,IAAA,IAAQ,KAAA;AAAA,MACrB,UAAA,EAAY,CAAC,WAAW,CAAA;AAAA,MACxB,eAAA,EAAiB;AAAA,KAClB,CAAA;AAED,IAAA,IAAA,CAAK,MAAA,CAAO,EAAA,CAAG,SAAA,EAAW,MAAM;AAC9B,MAAA,MAAM,eAAe,IAAA,CAAK,WAAA;AAC1B,MAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AACnB,MAAA,IAAA,CAAK,IAAA,CAAK,YAAA,GAAe,aAAA,GAAgB,WAAW,CAAA;AAAA,IACtD,CAAC,CAAA;AAED,IAAA,IAAA,CAAK,MAAA,CAAO,EAAA,CAAG,YAAA,EAAc,MAAM;AACjC,MAAA,IAAA,CAAK,WAAA,GAAc,KAAA;AACnB,MAAA,IAAA,CAAK,KAAK,KAAA,EAAM;AAChB,MAAA,IAAA,CAAK,KAAK,cAAc,CAAA;AAAA,IAC1B,CAAC,CAAA;AAED,IAAA,IAAA,CAAK,MAAA,CAAO,EAAA,CAAG,OAAA,EAAS,CAAC,GAAA,KAAe;AACtC,MAAA,IAAA,CAAK,IAAA,CAAK,SAAS,GAAG,CAAA;AAAA,IACxB,CAAC,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,aAAa,KAAA,EAA0D;AAC3E,IAAA,IAAA,CAAK,KAAK,KAAA,EAAM;AAChB,IAAA,IAAA,CAAK,MAAA,GAAS,IAAA;AAEd,IAAA,IAAI,CAAC,KAAA,EAAO;AACV,MAAA,IAAA,CAAK,KAAK,OAAA,EAAQ;AAClB,MAAA,OAAO,EAAE,QAAQ,IAAA,EAAK;AAAA,IACxB;AAGA,IAAA,IAAI,CAAC,IAAA,CAAK,MAAA,CAAO,SAAA,EAAW;AAC1B,MAAA,MAAM,IAAI,OAAA,CAAc,CAAC,OAAA,KAAY;AACnC,QAAA,IAAI,IAAA,CAAK,MAAA,CAAO,SAAA,EAAW,OAAO,OAAA,EAAQ;AAC1C,QAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,SAAA,EAAW,MAAM,SAAS,CAAA;AAAA,MAC7C,CAAC,CAAA;AAAA,IACH;AAGA,IAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,KAAY;AAC9B,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,cAAA,EAAgB,KAAA,EAAO,CAAC,QAAA,KAAkB;AACzD,QAAA,IAAA,CAAK,MAAA,GAAS,UAAU,MAAA,IAAU,IAAA;AAClC,QAAA,IAAA,CAAK,KAAK,OAAA,EAAQ;AAClB,QAAA,OAAA,CAAQ,EAAE,MAAA,EAAQ,IAAA,CAAK,MAAA,EAAQ,CAAA;AAAA,MACjC,CAAC,CAAA;AAAA,IACH,CAAC,CAAA;AAAA,EACH;AAAA;AAAA,EAIA,MAAc,KAAA,CACZ,MAAA,EACA,IAAA,EACA,IAAA,GAAY,EAAC,EACC;AAEd,IAAA,MAAM,KAAK,IAAA,CAAK,KAAA;AAGhB,IAAA,IAAI,CAAC,IAAA,CAAK,MAAA,CAAO,SAAA,EAAW;AAC1B,MAAA,MAAM,IAAI,OAAA,CAAc,CAAC,OAAA,EAAS,MAAA,KAAW;AAC3C,QAAA,IAAI,IAAA,CAAK,MAAA,CAAO,SAAA,EAAW,OAAO,OAAA,EAAQ;AAC1C,QAAA,MAAM,OAAA,GAAU,WAAW,MAAM;AAC/B,UAAA,OAAA,EAAQ;AACR,UAAA,MAAA,CAAO,IAAI,KAAA,CAAM,oBAAoB,CAAC,CAAA;AAAA,QACxC,GAAG,GAAK,CAAA;AACR,QAAA,MAAM,YAAY,MAAM;AACtB,UAAA,OAAA,EAAQ;AACR,UAAA,OAAA,EAAQ;AAAA,QACV,CAAA;AACA,QAAA,MAAM,OAAA,GAAU,CAAC,GAAA,KAAe;AAC9B,UAAA,OAAA,EAAQ;AACR,UAAA,MAAA,CAAO,GAAG,CAAA;AAAA,QACZ,CAAA;AACA,QAAA,MAAM,UAAU,MAAM;AACpB,UAAA,YAAA,CAAa,OAAO,CAAA;AACpB,UAAA,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,SAAA,EAAW,SAAS,CAAA;AACpC,UAAA,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,eAAA,EAAiB,OAAO,CAAA;AAAA,QAC1C,CAAA;AACA,QAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,SAAA,EAAW,SAAS,CAAA;AACrC,QAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,eAAA,EAAiB,OAAO,CAAA;AAAA,MAC3C,CAAC,CAAA;AAAA,IACH;AAGA,IAAA,MAAM,KAAA,GAAQ,OAAO,WAAA,EAAY;AACjC,IAAA,IAAI,UAAU,KAAA,EAAO;AACnB,MAAA,MAAM,YAAY,CAAA,EAAG,IAAI,IAAI,IAAA,CAAK,SAAA,CAAU,IAAI,CAAC,CAAA,CAAA;AACjD,MAAA,MAAM,QAAA,GAAW,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,SAAS,CAAA;AAC5C,MAAA,IAAI,UAAU,OAAO,QAAA;AACrB,MAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,MAAA,EAAQ,MAAM,IAAI,CAAA;AACzC,MAAA,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,SAAA,EAAW,GAAG,CAAA;AAChC,MAAA,GAAA,CAAI,QAAQ,MAAM,IAAA,CAAK,QAAA,CAAS,MAAA,CAAO,SAAS,CAAC,CAAA;AACjD,MAAA,OAAO,GAAA;AAAA,IACT;AAEA,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,MAAA,EAAQ,IAAA,EAAM,IAAI,CAAA;AAAA,EACtC;AAAA,EAEQ,KAAA,CAAM,MAAA,EAAgB,IAAA,EAAc,IAAA,EAAyB;AACnE,IAAA,MAAM,EAAA,GAAK,IAAI,GAAA,EAAI;AAEnB,IAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,MAAA,MAAM,OAAA,GAAU,WAAW,MAAM;AAC/B,QAAA,IAAA,CAAK,MAAA,CAAO,IAAI,EAAE,CAAA;AAClB,QAAA,MAAA,CAAO,IAAI,KAAA,CAAM,CAAA,aAAA,EAAgB,MAAM,CAAA,CAAA,EAAI,IAAI,EAAE,CAAC,CAAA;AAAA,MACpD,GAAG,GAAK,CAAA;AAER,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,EAAA,EAAI,CAAC,GAAA,KAAa;AACjC,QAAA,YAAA,CAAa,OAAO,CAAA;AACpB,QAAA,IAAI;AACF,UAAA,MAAM,eAAe,IAAA,CAAK,MAAA,CAAO,KAAK,EAAE,EAAA,EAAI,UAAU,CAAA;AACtD,UAAA,MAAM,MAAA,GAAS,UAAA,CAAW,IAAA,CAAK,KAAA,CAAM,YAAY,CAAC,CAAA;AAClD,UAAA,IAAI,OAAO,OAAA,EAAS;AAClB,YAAA,OAAA,CAAQ,OAAO,MAAM,CAAA;AAAA,UACvB,CAAA,MAAO;AACL,YAAA,MAAA;AAAA,cACE,IAAI,KAAA;AAAA,gBACF,OAAO,OAAA,IAAW,MAAA,CAAO,SAAS,CAAA,EAAG,MAAM,IAAI,IAAI,CAAA,OAAA;AAAA;AACrD,aACF;AAAA,UACF;AAAA,QACF,SAAS,GAAA,EAAK;AACZ,UAAA,MAAA,CAAO,GAAG,CAAA;AAAA,QACZ;AAAA,MACF,CAAC,CAAA;AAED,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA;AAAA,QACV,MAAA;AAAA,QACA,EAAA;AAAA,QACA,OAAO,WAAA,EAAY;AAAA,QACnB,CAAA,CAAA,EAAI,IAAA,CAAK,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA;AAAA,QACvB;AAAA,OACF;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,GAAA,CAAI,IAAA,EAAc,IAAA,EAA0B;AAChD,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,KAAA,EAAO,IAAA,EAAM,IAAI,CAAA;AAAA,EACrC;AAAA,EACA,MAAM,IAAA,CAAK,IAAA,EAAc,IAAA,EAA0B;AACjD,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,MAAA,EAAQ,IAAA,EAAM,IAAI,CAAA;AAAA,EACtC;AAAA,EACA,MAAM,GAAA,CAAI,IAAA,EAAc,IAAA,EAA0B;AAChD,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,KAAA,EAAO,IAAA,EAAM,IAAI,CAAA;AAAA,EACrC;AAAA,EACA,MAAM,KAAA,CAAM,IAAA,EAAc,IAAA,EAA0B;AAClD,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,OAAA,EAAS,IAAA,EAAM,IAAI,CAAA;AAAA,EACvC;AAAA,EACA,MAAM,MAAA,CAAO,IAAA,EAAc,IAAA,EAA0B;AACnD,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,QAAA,EAAU,IAAA,EAAM,IAAI,CAAA;AAAA,EACxC;AAAA;AAAA,EAIA,SAAA,CAAU,OAAe,OAAA,EAA+C;AACtE,IAAA,IAAA,CAAK,MAAA,CAAO,EAAA,CAAG,KAAA,EAAO,OAAO,CAAA;AAC7B,IAAA,OAAO,MAAM,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,OAAO,OAAO,CAAA;AAAA,EAC7C;AAAA,EAEA,WAAA,CAAY,OAAe,OAAA,EAA0C;AACnE,IAAA,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,KAAA,EAAO,OAAO,CAAA;AAAA,EAChC;AAAA;AAAA,EAIA,MAAM,IAAA,CAAK,KAAA,EAAA,GAAkB,IAAA,EAA4B;AACvD,IAAA,MAAM,KAAK,IAAA,CAAK,KAAA;AAChB,IAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,KAAA,EAAO,GAAG,IAAI,CAAA;AAAA,EACjC;AAAA;AAAA,EAIA,UAAA,GAAmB;AACjB,IAAA,IAAA,CAAK,OAAO,UAAA,EAAW;AACvB,IAAA,IAAA,CAAK,WAAA,GAAc,KAAA;AAAA,EACrB;AAAA,EAEA,MAAM,SAAA,GAA2B;AAC/B,IAAA,IAAA,CAAK,OAAO,OAAA,EAAQ;AAAA,EACtB;AACF;AC9MO,IAAM,YAAA,GAAN,cAA2BA,YAAAA,CAAkC;AAAA,EAC1D,GAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA,EACA,GAAA,GAAqB,IAAA;AAAA,EACrB,YAAA,uBAAmB,GAAA,EAAyB;AAAA,EAE7C,WAAA,GAAc,IAAA;AAAA;AAAA,EACd,SAAA,GAAY,IAAA;AAAA,EACZ,OAAA;AAAA,EAEP,YAAY,MAAA,EAA4B;AACtC,IAAA,KAAA,EAAM;AACN,IAAA,IAAA,CAAK,GAAA,GAAM,MAAA,CAAO,GAAA,CAAI,OAAA,CAAQ,OAAO,EAAE,CAAA;AACvC,IAAA,IAAA,CAAK,OAAA,GAAU,OAAO,OAAA,IAAW,IAAA;AACjC,IAAA,IAAA,CAAK,MAAA,GAAS,OAAO,GAAA,IAAO,IAAA;AAC5B,IAAA,IAAA,CAAK,OAAA,GAAU,KAAK,UAAA,EAAW;AAAA,EACjC;AAAA,EAEA,MAAc,UAAA,GAA4B;AACxC,IAAA,IAAI;AACF,MAAA,IAAA,CAAK,GAAA,GACH,OAAO,IAAA,CAAK,MAAA,KAAW,aAAa,MAAM,IAAA,CAAK,MAAA,EAAO,GAAI,IAAA,CAAK,MAAA;AACjE,MAAA,IAAA,CAAK,SAAA,GAAY,KAAA;AACjB,MAAA,IAAA,CAAK,KAAK,WAAW,CAAA;AAAA,IACvB,SAAS,GAAA,EAAK;AACZ,MAAA,IAAA,CAAK,SAAA,GAAY,KAAA;AACjB,MAAA,IAAA,CAAK,IAAA,CAAK,SAAS,GAAG,CAAA;AAAA,IACxB;AAAA,EACF;AAAA,EAEQ,OAAA,GAAkC;AACxC,IAAA,MAAM,CAAA,GAA4B,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AACvE,IAAA,IAAI,KAAK,GAAA,EAAK,CAAA,CAAE,eAAe,CAAA,GAAI,CAAA,OAAA,EAAU,KAAK,GAAG,CAAA,CAAA;AACrD,IAAA,OAAO,CAAA;AAAA,EACT;AAAA,EAEQ,QAAQ,IAAA,EAAsB;AACpC,IAAA,OAAO,GAAG,IAAA,CAAK,GAAG,IAAI,IAAA,CAAK,OAAO,GAAG,IAAI,CAAA,CAAA;AAAA,EAC3C;AAAA;AAAA,EAIA,MAAc,OAAA,CACZ,MAAA,EACA,IAAA,EACA,IAAA,EACc;AACd,IAAA,MAAM,IAAA,CAAK,OAAA;AAEX,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,WAAA,EAAY,KAAM,KAAA;AACvC,IAAA,IAAI,GAAA,GAAM,IAAA,CAAK,OAAA,CAAQ,IAAI,CAAA;AAE3B,IAAA,IAAI,SAAS,IAAA,EAAM;AACjB,MAAA,MAAM,MAAA,GAAS,IAAI,eAAA,EAAgB;AACnC,MAAA,KAAA,MAAW,CAAC,CAAA,EAAG,CAAC,KAAK,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA,EAAG;AACzC,QAAA,MAAA,CAAO,GAAA,CAAI,CAAA,EAAG,OAAO,CAAA,KAAM,QAAA,GAAW,IAAA,CAAK,SAAA,CAAU,CAAC,CAAA,GAAI,MAAA,CAAO,CAAC,CAAC,CAAA;AAAA,MACrE;AACA,MAAA,GAAA,IAAO,CAAA,CAAA,EAAI,MAAA,CAAO,QAAA,EAAU,CAAA,CAAA;AAAA,IAC9B;AAEA,IAAA,MAAM,GAAA,GAAM,MAAM,KAAA,CAAM,GAAA,EAAK;AAAA,MAC3B,MAAA,EAAQ,OAAO,WAAA,EAAY;AAAA,MAC3B,OAAA,EAAS,KAAK,OAAA,EAAQ;AAAA,MACtB,IAAA,EAAM,KAAA,GAAQ,MAAA,GAAY,IAAA,CAAK,UAAU,IAAI;AAAA,KAC9C,CAAA;AAED,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAMC,KAAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK,CAAE,KAAA,CAAM,OAAO,EAAE,KAAA,EAAO,GAAA,CAAI,UAAA,EAAW,CAAE,CAAA;AACrE,MAAA,MAAM,IAAI,MAAMA,KAAAA,CAAK,KAAA,IAASA,MAAK,OAAA,IAAW,CAAA,KAAA,EAAQ,GAAA,CAAI,MAAM,CAAA,CAAE,CAAA;AAAA,IACpE;AAEA,IAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,IAAA,IAAI,IAAA,CAAK,YAAY,KAAA,EAAO,MAAM,IAAI,KAAA,CAAM,IAAA,CAAK,SAAS,gBAAgB,CAAA;AAC1E,IAAA,OAAO,KAAK,MAAA,IAAU,IAAA;AAAA,EACxB;AAAA,EAEA,MAAM,GAAA,CAAI,IAAA,EAAc,IAAA,EAA0B;AAChD,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,IAAA,EAAM,IAAI,CAAA;AAAA,EACvC;AAAA,EACA,MAAM,IAAA,CAAK,IAAA,EAAc,IAAA,EAA0B;AACjD,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAQ,IAAA,EAAM,IAAI,CAAA;AAAA,EACxC;AAAA,EACA,MAAM,GAAA,CAAI,IAAA,EAAc,IAAA,EAA0B;AAChD,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,KAAA,EAAO,IAAA,EAAM,IAAI,CAAA;AAAA,EACvC;AAAA,EACA,MAAM,KAAA,CAAM,IAAA,EAAc,IAAA,EAA0B;AAClD,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,OAAA,EAAS,IAAA,EAAM,IAAI,CAAA;AAAA,EACzC;AAAA,EACA,MAAM,MAAA,CAAO,IAAA,EAAc,IAAA,EAA0B;AACnD,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,QAAA,EAAU,IAAA,EAAM,IAAI,CAAA;AAAA,EAC1C;AAAA;AAAA,EAIA,SAAA,CAAU,OAAe,OAAA,EAA+C;AACtE,IAAA,MAAM,GAAA,GAAM,CAAA,EAAG,IAAA,CAAK,GAAG,CAAA,CAAA,EAAI,KAAK,OAAO,CAAA,UAAA,EAAa,kBAAA,CAAmB,KAAK,CAAC,CAAA,CAAA;AAC7E,IAAA,MAAM,SAAS,IAAI,WAAA,CAAY,KAAK,EAAE,eAAA,EAAiB,MAAM,CAAA;AAE7D,IAAA,MAAA,CAAO,SAAA,GAAY,CAAC,CAAA,KAAM;AACxB,MAAA,IAAI;AACF,QAAA,MAAM,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,CAAA,CAAE,IAAI,CAAA;AAC9B,QAAA,OAAA,CAAQ,IAAI,CAAA;AAAA,MACd,CAAA,CAAA,MAAQ;AACN,QAAA,OAAA,CAAQ,EAAE,IAAI,CAAA;AAAA,MAChB;AAAA,IACF,CAAA;AAEA,IAAA,MAAA,CAAO,UAAU,MAAM;AAAA,IAEvB,CAAA;AAEA,IAAA,IAAA,CAAK,YAAA,CAAa,GAAA,CAAI,KAAA,EAAO,MAAM,CAAA;AAEnC,IAAA,OAAO,MAAM;AACX,MAAA,MAAA,CAAO,KAAA,EAAM;AACb,MAAA,IAAA,CAAK,YAAA,CAAa,OAAO,KAAK,CAAA;AAAA,IAChC,CAAA;AAAA,EACF;AAAA,EAEA,YAAY,KAAA,EAAqB;AAC/B,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,YAAA,CAAa,GAAA,CAAI,KAAK,CAAA;AAC1C,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,MAAA,CAAO,KAAA,EAAM;AACb,MAAA,IAAA,CAAK,YAAA,CAAa,OAAO,KAAK,CAAA;AAAA,IAChC;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,IAAA,CAAK,KAAA,EAAA,GAAkB,IAAA,EAA4B;AACvD,IAAA,MAAM,KAAK,OAAA,CAAQ,MAAA,EAAQ,cAAc,EAAE,KAAA,EAAO,MAAM,CAAA;AAAA,EAC1D;AAAA;AAAA,EAIA,UAAA,GAAmB;AACjB,IAAA,KAAA,MAAW,GAAG,MAAM,KAAK,IAAA,CAAK,YAAA,SAAqB,KAAA,EAAM;AACzD,IAAA,IAAA,CAAK,aAAa,KAAA,EAAM;AACxB,IAAA,IAAA,CAAK,WAAA,GAAc,KAAA;AACnB,IAAA,IAAA,CAAK,KAAK,cAAc,CAAA;AAAA,EAC1B;AAAA,EAEA,MAAM,SAAA,GAA2B;AAC/B,IAAA,IAAA,CAAK,OAAA,GAAU,KAAK,UAAA,EAAW;AAC/B,IAAA,MAAM,IAAA,CAAK,OAAA;AAAA,EACb;AACF;ACjIO,SAAS,aAAa,MAAA,EAAoC;AAC/D,EAAA,MAAM,OAAA,GAAU,OAAO,OAAA,IAAW,IAAA;AAElC,EAAA,IAAI,SAAA;AAEJ,EAAA,IAAI,MAAA,CAAO,SAAA,IAAa,OAAO,MAAA,CAAO,cAAc,QAAA,EAAU;AAC5D,IAAA,SAAA,GAAY,MAAA,CAAO,SAAA;AAAA,EACrB,CAAA,MAAA,IAAW,MAAA,CAAO,SAAA,KAAc,KAAA,EAAO;AACrC,IAAA,SAAA,GAAY,IAAI,YAAA,CAAa,EAAE,KAAK,MAAA,CAAO,GAAA,EAAK,SAAS,CAAA;AAAA,EAC3D,CAAA,MAAO;AACL,IAAA,SAAA,GAAY,IAAI,eAAA,CAAgB,EAAE,KAAK,MAAA,CAAO,GAAA,EAAK,SAAS,CAAA;AAAA,EAC9D;AAGA,EAAA,KAAA,CAAM,GAAA,CAAI,IAAI,eAAA,CAAgB,SAAS,CAAC,CAAA;AAExC,EAAA,MAAM,MAAA,GAAuB;AAAA,IAC3B,SAAA;AAAA,IAEA,KAAK,CAAC,IAAA,EAAM,SAAS,SAAA,CAAU,GAAA,CAAI,MAAM,IAAI,CAAA;AAAA,IAC7C,MAAM,CAAC,IAAA,EAAM,SAAS,SAAA,CAAU,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,IAC/C,KAAK,CAAC,IAAA,EAAM,SAAS,SAAA,CAAU,GAAA,CAAI,MAAM,IAAI,CAAA;AAAA,IAC7C,OAAO,CAAC,IAAA,EAAM,SAAS,SAAA,CAAU,KAAA,CAAM,MAAM,IAAI,CAAA;AAAA,IACjD,QAAQ,CAAC,IAAA,EAAM,SAAS,SAAA,CAAU,MAAA,CAAO,MAAM,IAAI,CAAA;AAAA,IAEnD,SAAA,CAAU,OAAO,OAAA,EAAS;AACxB,MAAA,IAAI,UAAU,SAAA,EAAW,OAAO,SAAA,CAAU,SAAA,CAAU,OAAO,OAAO,CAAA;AAClE,MAAA,OAAO,MAAM;AAAA,MAAC,CAAA;AAAA,IAChB,CAAA;AAAA,IACA,WAAA,CAAY,OAAO,OAAA,EAAS;AAC1B,MAAA,SAAA,CAAU,WAAA,GAAc,OAAO,OAAO,CAAA;AAAA,IACxC,CAAA;AAAA,IACA,IAAA,CAAK,UAAU,IAAA,EAAM;AACnB,MAAA,SAAA,CAAU,IAAA,GAAO,KAAA,EAAO,GAAG,IAAI,CAAA;AAAA,IACjC,CAAA;AAAA,IAEA,IAAI,WAAA,GAAc;AAChB,MAAA,OAAO,UAAU,WAAA,IAAe,KAAA;AAAA,IAClC,CAAA;AAAA,IAEA,MAAM,aAAa,KAAA,EAAsB;AACvC,MAAA,IAAI,OAAO,SAAA,CAAU,YAAA,KAAiB,UAAA,EAAY;AAChD,QAAA,OAAO,SAAA,CAAU,aAAa,KAAK,CAAA;AAAA,MACrC;AAEA,MAAA,OAAO,EAAE,QAAQ,IAAA,EAAK;AAAA,IACxB,CAAA;AAAA,IAEA,EAAA,CAAG,OAAO,OAAA,EAAS;AACjB,MAAA,SAAA,CAAU,EAAA,GAAK,OAAO,OAAO,CAAA;AAAA,IAC/B,CAAA;AAAA,IACA,GAAA,CAAI,OAAO,OAAA,EAAS;AAClB,MAAA,SAAA,CAAU,GAAA,GAAM,OAAO,OAAO,CAAA;AAAA,IAChC,CAAA;AAAA,IAEA,UAAA,GAAa;AACX,MAAA,SAAA,CAAU,UAAA,IAAa;AAAA,IACzB,CAAA;AAAA,IACA,MAAM,SAAA,GAAY;AAChB,MAAA,MAAM,UAAU,SAAA,IAAY;AAAA,IAC9B;AAAA,GACF;AAEA,EAAA,OAAO,MAAA;AACT","file":"chunk-KFISHMU4.js","sourcesContent":["/**\n * AuthGate — resettable deferred promise for authentication state.\n *\n * - resolve(): auth confirmed (authenticated or confirmed unauthenticated)\n * - reset(): back to pending (reconnect, token change)\n * - ready: awaitable promise that resolves when auth is confirmed\n */\n\nexport type AuthGateState = \"pending\" | \"ready\";\n\nexport class AuthGate {\n private _state: AuthGateState = \"pending\";\n private _resolve: (() => void) | null = null;\n private _promise: Promise<void>;\n\n constructor() {\n this._promise = this._makePending();\n }\n\n get state(): AuthGateState {\n return this._state;\n }\n\n get ready(): Promise<void> {\n return this._promise;\n }\n\n resolve(): void {\n this._state = \"ready\";\n if (this._resolve) {\n this._resolve();\n this._resolve = null;\n }\n }\n\n reset(): void {\n if (this._state === \"ready\") {\n this._state = \"pending\";\n this._promise = this._makePending();\n }\n }\n\n private _makePending(): Promise<void> {\n return new Promise<void>((r) => {\n this._resolve = r;\n });\n }\n}\n","/**\n * SocketTransport — Socket.IO transport with AuthGate.\n *\n * Connection and authentication are separate concerns:\n * - Constructor connects the socket\n * - authenticate(token) sends the token and waits for server confirmation\n * - fetch() awaits auth.ready before sending any request\n * - Disconnect resets the gate; reconnect event lets the Provider re-auth\n */\n\nimport SocketIO from \"socket.io-client\";\nimport pako from \"pako\";\nimport { decompress } from \"compress-json\";\nimport { EventEmitter } from \"eventemitter3\";\nimport ShortId from \"short-unique-id\";\nimport type { Transport } from \"@parcae/model\";\nimport { AuthGate } from \"../auth-gate\";\n\nconst uid = new ShortId({ length: 10 });\n\nexport interface SocketTransportConfig {\n url: string;\n version?: string;\n path?: string;\n}\n\nexport class SocketTransport extends EventEmitter implements Transport {\n public auth = new AuthGate();\n public isConnected = false;\n public isLoading = false;\n public userId: string | null = null;\n\n private socket: any;\n private url: string;\n private version: string;\n private inflight = new Map<string, Promise<any>>();\n\n constructor(config: SocketTransportConfig) {\n super();\n this.url = config.url;\n this.version = config.version ?? \"v1\";\n\n this.socket = SocketIO(this.url, {\n path: config.path ?? \"/ws\",\n transports: [\"websocket\"],\n withCredentials: true,\n });\n\n this.socket.on(\"connect\", () => {\n const wasConnected = this.isConnected;\n this.isConnected = true;\n this.emit(wasConnected ? \"reconnected\" : \"connected\");\n });\n\n this.socket.on(\"disconnect\", () => {\n this.isConnected = false;\n this.auth.reset();\n this.emit(\"disconnected\");\n });\n\n this.socket.on(\"error\", (err: Error) => {\n this.emit(\"error\", err);\n });\n }\n\n // ── Authenticate ──────────────────────────────────────────────────\n\n /**\n * Authenticate with the backend. Resolves the AuthGate when done.\n *\n * - token=string → send to server, wait for confirmation\n * - token=null → no auth, resolve gate immediately (unauthenticated)\n */\n async authenticate(token: string | null): Promise<{ userId: string | null }> {\n this.auth.reset();\n this.userId = null;\n\n if (!token) {\n this.auth.resolve();\n return { userId: null };\n }\n\n // Wait for socket to be connected\n if (!this.socket.connected) {\n await new Promise<void>((resolve) => {\n if (this.socket.connected) return resolve();\n this.socket.once(\"connect\", () => resolve());\n });\n }\n\n // Send token, wait for server callback\n return new Promise((resolve) => {\n this.socket.emit(\"authenticate\", token, (response: any) => {\n this.userId = response?.userId ?? null;\n this.auth.resolve();\n resolve({ userId: this.userId });\n });\n });\n }\n\n // ── Request/Response ──────────────────────────────────────────────\n\n private async fetch(\n method: string,\n path: string,\n data: any = {},\n ): Promise<any> {\n // Wait for auth to be confirmed before any request\n await this.auth.ready;\n\n // Wait for socket connection\n if (!this.socket.connected) {\n await new Promise<void>((resolve, reject) => {\n if (this.socket.connected) return resolve();\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error(\"Connection timeout\"));\n }, 30000);\n const onConnect = () => {\n cleanup();\n resolve();\n };\n const onError = (err: Error) => {\n cleanup();\n reject(err);\n };\n const cleanup = () => {\n clearTimeout(timeout);\n this.socket.off(\"connect\", onConnect);\n this.socket.off(\"connect_error\", onError);\n };\n this.socket.once(\"connect\", onConnect);\n this.socket.once(\"connect_error\", onError);\n });\n }\n\n // Deduplicate GET requests\n const upper = method.toUpperCase();\n if (upper === \"GET\") {\n const dedupeKey = `${path}:${JSON.stringify(data)}`;\n const existing = this.inflight.get(dedupeKey);\n if (existing) return existing;\n const req = this._call(method, path, data);\n this.inflight.set(dedupeKey, req);\n req.finally(() => this.inflight.delete(dedupeKey));\n return req;\n }\n\n return this._call(method, path, data);\n }\n\n private _call(method: string, path: string, data: any): Promise<any> {\n const id = uid.rnd();\n\n return new Promise((resolve, reject) => {\n const timeout = setTimeout(() => {\n this.socket.off(id);\n reject(new Error(`RPC timeout: ${method} ${path}`));\n }, 30000);\n\n this.socket.once(id, (msg: any) => {\n clearTimeout(timeout);\n try {\n const uncompressed = pako.ungzip(msg, { to: \"string\" });\n const parsed = decompress(JSON.parse(uncompressed));\n if (parsed.success) {\n resolve(parsed.result);\n } else {\n reject(\n new Error(\n parsed.message || parsed.error || `${method} ${path} failed`,\n ),\n );\n }\n } catch (err) {\n reject(err);\n }\n });\n\n this.socket.emit(\n \"call\",\n id,\n method.toUpperCase(),\n `/${this.version}${path}`,\n data,\n );\n });\n }\n\n async get(path: string, data?: any): Promise<any> {\n return this.fetch(\"GET\", path, data);\n }\n async post(path: string, data?: any): Promise<any> {\n return this.fetch(\"POST\", path, data);\n }\n async put(path: string, data?: any): Promise<any> {\n return this.fetch(\"PUT\", path, data);\n }\n async patch(path: string, data?: any): Promise<any> {\n return this.fetch(\"PATCH\", path, data);\n }\n async delete(path: string, data?: any): Promise<any> {\n return this.fetch(\"DELETE\", path, data);\n }\n\n // ── Subscriptions ─────────────────────────────────────────────────\n\n subscribe(event: string, handler: (...args: any[]) => void): () => void {\n this.socket.on(event, handler);\n return () => this.socket.off(event, handler);\n }\n\n unsubscribe(event: string, handler?: (...args: any[]) => void): void {\n this.socket.off(event, handler);\n }\n\n // ── Control ───────────────────────────────────────────────────────\n\n async send(event: string, ...args: any[]): Promise<void> {\n await this.auth.ready;\n this.socket.emit(event, ...args);\n }\n\n // ── Lifecycle ─────────────────────────────────────────────────────\n\n disconnect(): void {\n this.socket.disconnect();\n this.isConnected = false;\n }\n\n async reconnect(): Promise<void> {\n this.socket.connect();\n }\n}\n\nexport default SocketTransport;\n","/**\n * SSETransport — HTTP + Server-Sent Events implementation of the Transport interface.\n *\n * Request/response via standard fetch(). Subscriptions via EventSource.\n * Simpler than Socket.IO — no websocket infra needed. Good for read-heavy\n * apps, serverless backends, or environments where WebSocket isn't available.\n *\n * Trade-offs vs SocketTransport:\n * - Simpler infra (no sticky sessions, works behind any CDN/proxy)\n * - Server → client streaming only (no client → server streaming)\n * - Request/response is standard HTTP (cacheable, observable, debuggable)\n * - No compression (relies on HTTP gzip)\n * - No request deduplication (relies on HTTP/2 multiplexing)\n */\n\nimport { EventEmitter } from \"eventemitter3\";\nimport type { Transport } from \"@parcae/model\";\n\nexport interface SSETransportConfig {\n /** Base URL of the Parcae backend. */\n url: string;\n /** API key or async function returning a key. */\n key?: string | null | (() => Promise<string | null>);\n /** API version prefix. Default: \"v1\" */\n version?: string;\n}\n\nexport class SSETransport extends EventEmitter implements Transport {\n private url: string;\n private version: string;\n private apiKey: string | null | (() => Promise<string | null>);\n private key: string | null = null;\n private eventSources = new Map<string, EventSource>();\n\n public isConnected = true; // HTTP is \"always connected\"\n public isLoading = true;\n public loading: Promise<void>;\n\n constructor(config: SSETransportConfig) {\n super();\n this.url = config.url.replace(/\\/$/, \"\");\n this.version = config.version ?? \"v1\";\n this.apiKey = config.key ?? null;\n this.loading = this.resolveKey();\n }\n\n private async resolveKey(): Promise<void> {\n try {\n this.key =\n typeof this.apiKey === \"function\" ? await this.apiKey() : this.apiKey;\n this.isLoading = false;\n this.emit(\"connected\");\n } catch (err) {\n this.isLoading = false;\n this.emit(\"error\", err);\n }\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (this.key) h[\"Authorization\"] = `Bearer ${this.key}`;\n return h;\n }\n\n private fullUrl(path: string): string {\n return `${this.url}/${this.version}${path}`;\n }\n\n // ── Request/Response ──────────────────────────────────────────────────\n\n private async request(\n method: string,\n path: string,\n data?: any,\n ): Promise<any> {\n await this.loading;\n\n const isGet = method.toUpperCase() === \"GET\";\n let url = this.fullUrl(path);\n\n if (isGet && data) {\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(data)) {\n params.set(k, typeof v === \"object\" ? JSON.stringify(v) : String(v));\n }\n url += `?${params.toString()}`;\n }\n\n const res = await fetch(url, {\n method: method.toUpperCase(),\n headers: this.headers(),\n body: isGet ? undefined : JSON.stringify(data),\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => ({ error: res.statusText }));\n throw new Error(body.error || body.message || `HTTP ${res.status}`);\n }\n\n const body = await res.json();\n if (body.success === false) throw new Error(body.error || \"Request failed\");\n return body.result ?? body;\n }\n\n async get(path: string, data?: any): Promise<any> {\n return this.request(\"GET\", path, data);\n }\n async post(path: string, data?: any): Promise<any> {\n return this.request(\"POST\", path, data);\n }\n async put(path: string, data?: any): Promise<any> {\n return this.request(\"PUT\", path, data);\n }\n async patch(path: string, data?: any): Promise<any> {\n return this.request(\"PATCH\", path, data);\n }\n async delete(path: string, data?: any): Promise<any> {\n return this.request(\"DELETE\", path, data);\n }\n\n // ── Subscriptions (via Server-Sent Events) ────────────────────────────\n\n subscribe(event: string, handler: (...args: any[]) => void): () => void {\n const url = `${this.url}/${this.version}/__events/${encodeURIComponent(event)}`;\n const source = new EventSource(url, { withCredentials: true });\n\n source.onmessage = (e) => {\n try {\n const data = JSON.parse(e.data);\n handler(data);\n } catch {\n handler(e.data);\n }\n };\n\n source.onerror = () => {\n // EventSource auto-reconnects\n };\n\n this.eventSources.set(event, source);\n\n return () => {\n source.close();\n this.eventSources.delete(event);\n };\n }\n\n unsubscribe(event: string): void {\n const source = this.eventSources.get(event);\n if (source) {\n source.close();\n this.eventSources.delete(event);\n }\n }\n\n // ── Control messages ──────────────────────────────────────────────────\n\n async send(event: string, ...args: any[]): Promise<void> {\n await this.request(\"POST\", \"/__control\", { event, args });\n }\n\n // ── Lifecycle ─────────────────────────────────────────────────────────\n\n disconnect(): void {\n for (const [, source] of this.eventSources) source.close();\n this.eventSources.clear();\n this.isConnected = false;\n this.emit(\"disconnected\");\n }\n\n async reconnect(): Promise<void> {\n this.loading = this.resolveKey();\n await this.loading;\n }\n}\n\nexport default SSETransport;\n","/**\n * @parcae/sdk — createClient()\n *\n * Creates a Parcae client with a pluggable transport.\n * Authentication is driven by the consumer (Provider) via client.authenticate().\n */\n\nimport { Model, FrontendAdapter } from \"@parcae/model\";\nimport type { Transport } from \"@parcae/model\";\nimport { SocketTransport } from \"./transports/socket\";\nimport { SSETransport } from \"./transports/sse\";\n\n// ─── Configuration ───────────────────────────────────────────────────────────\n\nexport interface ClientConfig {\n url: string;\n version?: string;\n transport?: \"socket\" | \"sse\" | Transport;\n}\n\nexport interface ParcaeClient {\n transport: Transport;\n get(path: string, data?: any): Promise<any>;\n post(path: string, data?: any): Promise<any>;\n put(path: string, data?: any): Promise<any>;\n patch(path: string, data?: any): Promise<any>;\n delete(path: string, data?: any): Promise<any>;\n subscribe(event: string, handler: (...args: any[]) => void): () => void;\n unsubscribe(event: string, handler?: (...args: any[]) => void): void;\n send(event: string, ...args: any[]): void;\n readonly isConnected: boolean;\n\n /** Authenticate with the backend. Resolves when auth is confirmed. */\n authenticate(token: string | null): Promise<{ userId: string | null }>;\n\n /** Listen for transport events: connected, disconnected, reconnected, error */\n on(event: string, handler: (...args: any[]) => void): void;\n off(event: string, handler?: (...args: any[]) => void): void;\n\n disconnect(): void;\n reconnect(): Promise<void>;\n}\n\n// ─── createClient ────────────────────────────────────────────────────────────\n\nexport function createClient(config: ClientConfig): ParcaeClient {\n const version = config.version ?? \"v1\";\n\n let transport: any;\n\n if (config.transport && typeof config.transport === \"object\") {\n transport = config.transport;\n } else if (config.transport === \"sse\") {\n transport = new SSETransport({ url: config.url, version });\n } else {\n transport = new SocketTransport({ url: config.url, version });\n }\n\n // Wire FrontendAdapter so Model.where() etc work\n Model.use(new FrontendAdapter(transport));\n\n const client: ParcaeClient = {\n transport,\n\n get: (path, data) => transport.get(path, data),\n post: (path, data) => transport.post(path, data),\n put: (path, data) => transport.put(path, data),\n patch: (path, data) => transport.patch(path, data),\n delete: (path, data) => transport.delete(path, data),\n\n subscribe(event, handler) {\n if (transport.subscribe) return transport.subscribe(event, handler);\n return () => {};\n },\n unsubscribe(event, handler) {\n transport.unsubscribe?.(event, handler);\n },\n send(event, ...args) {\n transport.send?.(event, ...args);\n },\n\n get isConnected() {\n return transport.isConnected ?? false;\n },\n\n async authenticate(token: string | null) {\n if (typeof transport.authenticate === \"function\") {\n return transport.authenticate(token);\n }\n // SSE/custom transports: resolve immediately\n return { userId: null };\n },\n\n on(event, handler) {\n transport.on?.(event, handler);\n },\n off(event, handler) {\n transport.off?.(event, handler);\n },\n\n disconnect() {\n transport.disconnect?.();\n },\n async reconnect() {\n await transport.reconnect?.();\n },\n };\n\n return client;\n}\n"]}
@@ -0,0 +1,38 @@
1
+ import { Transport } from '@parcae/model';
2
+
3
+ /**
4
+ * @parcae/sdk — createClient()
5
+ *
6
+ * Creates a Parcae client with a pluggable transport.
7
+ * Authentication is driven by the consumer (Provider) via client.authenticate().
8
+ */
9
+
10
+ interface ClientConfig {
11
+ url: string;
12
+ version?: string;
13
+ transport?: "socket" | "sse" | Transport;
14
+ }
15
+ interface ParcaeClient {
16
+ transport: Transport;
17
+ get(path: string, data?: any): Promise<any>;
18
+ post(path: string, data?: any): Promise<any>;
19
+ put(path: string, data?: any): Promise<any>;
20
+ patch(path: string, data?: any): Promise<any>;
21
+ delete(path: string, data?: any): Promise<any>;
22
+ subscribe(event: string, handler: (...args: any[]) => void): () => void;
23
+ unsubscribe(event: string, handler?: (...args: any[]) => void): void;
24
+ send(event: string, ...args: any[]): void;
25
+ readonly isConnected: boolean;
26
+ /** Authenticate with the backend. Resolves when auth is confirmed. */
27
+ authenticate(token: string | null): Promise<{
28
+ userId: string | null;
29
+ }>;
30
+ /** Listen for transport events: connected, disconnected, reconnected, error */
31
+ on(event: string, handler: (...args: any[]) => void): void;
32
+ off(event: string, handler?: (...args: any[]) => void): void;
33
+ disconnect(): void;
34
+ reconnect(): Promise<void>;
35
+ }
36
+ declare function createClient(config: ClientConfig): ParcaeClient;
37
+
38
+ export { type ClientConfig as C, type ParcaeClient as P, createClient as c };
package/dist/index.d.ts CHANGED
@@ -1,46 +1,64 @@
1
- export { C as ClientConfig, P as ParcaeClient, c as createClient } from './client-uWUdynht.js';
1
+ export { C as ClientConfig, P as ParcaeClient, c as createClient } from './client-CeIUZzoo.js';
2
2
  import { EventEmitter } from 'eventemitter3';
3
3
  import { Transport } from '@parcae/model';
4
4
  export { FrontendAdapter, Model, Transport } from '@parcae/model';
5
5
 
6
6
  /**
7
- * SocketTransportSocket.IO implementation of the Transport interface.
7
+ * AuthGateresettable deferred promise for authentication state.
8
8
  *
9
- * Bidirectional, full-duplex. Best for apps that need realtime subscriptions
10
- * (live query updates, collaborative editing, chat, etc.).
9
+ * - resolve(): auth confirmed (authenticated or confirmed unauthenticated)
10
+ * - reset(): back to pending (reconnect, token change)
11
+ * - ready: awaitable promise that resolves when auth is confirmed
12
+ */
13
+ type AuthGateState = "pending" | "ready";
14
+ declare class AuthGate {
15
+ private _state;
16
+ private _resolve;
17
+ private _promise;
18
+ constructor();
19
+ get state(): AuthGateState;
20
+ get ready(): Promise<void>;
21
+ resolve(): void;
22
+ reset(): void;
23
+ private _makePending;
24
+ }
25
+
26
+ /**
27
+ * SocketTransport — Socket.IO transport with AuthGate.
11
28
  *
12
- * Extracted from Dollhouse Studio's Dollhouse.ts (667 lines).
29
+ * Connection and authentication are separate concerns:
30
+ * - Constructor connects the socket
31
+ * - authenticate(token) sends the token and waits for server confirmation
32
+ * - fetch() awaits auth.ready before sending any request
33
+ * - Disconnect resets the gate; reconnect event lets the Provider re-auth
13
34
  */
14
35
 
15
36
  interface SocketTransportConfig {
16
37
  url: string;
17
- key?: string | null | (() => Promise<string | null>);
18
38
  version?: string;
19
- /** Socket.IO path. Default: "/ws" */
20
39
  path?: string;
21
40
  }
22
41
  declare class SocketTransport extends EventEmitter implements Transport {
42
+ auth: AuthGate;
43
+ isConnected: boolean;
44
+ isLoading: boolean;
45
+ userId: string | null;
23
46
  private socket;
24
- private pendingHandlers;
25
- private apiKey;
26
- private key;
27
47
  private url;
28
48
  private version;
29
- private socketPath;
30
- private waitForAuth;
31
- private resolveAuth;
32
49
  private inflight;
33
- loading: Promise<void>;
34
- isLoading: boolean;
35
- isConnected: boolean;
36
- isConnecting: boolean;
37
- authVersion: number;
38
50
  constructor(config: SocketTransportConfig);
39
- setKey(key: string | null | (() => Promise<string | null>)): Promise<void>;
40
- private init;
41
- private setupEvents;
51
+ /**
52
+ * Authenticate with the backend. Resolves the AuthGate when done.
53
+ *
54
+ * - token=string → send to server, wait for confirmation
55
+ * - token=null → no auth, resolve gate immediately (unauthenticated)
56
+ */
57
+ authenticate(token: string | null): Promise<{
58
+ userId: string | null;
59
+ }>;
42
60
  private fetch;
43
- private _doFetch;
61
+ private _call;
44
62
  get(path: string, data?: any): Promise<any>;
45
63
  post(path: string, data?: any): Promise<any>;
46
64
  put(path: string, data?: any): Promise<any>;
@@ -102,4 +120,4 @@ declare class SSETransport extends EventEmitter implements Transport {
102
120
  reconnect(): Promise<void>;
103
121
  }
104
122
 
105
- export { SSETransport, type SSETransportConfig, SocketTransport, type SocketTransportConfig };
123
+ export { AuthGate, type AuthGateState, SSETransport, type SSETransportConfig, SocketTransport, type SocketTransportConfig };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { SSETransport, SocketTransport, createClient } from './chunk-IETH2XOR.js';
1
+ export { AuthGate, SSETransport, SocketTransport, createClient } from './chunk-KFISHMU4.js';
2
2
  export { FrontendAdapter, Model } from '@parcae/model';
3
3
  //# sourceMappingURL=index.js.map
4
4
  //# sourceMappingURL=index.js.map
@@ -1,20 +1,15 @@
1
1
  import * as React from 'react';
2
2
  import React__default from 'react';
3
- import { P as ParcaeClient, C as ClientConfig } from '../client-uWUdynht.js';
3
+ import { P as ParcaeClient, C as ClientConfig } from '../client-CeIUZzoo.js';
4
4
  import '@parcae/model';
5
5
 
6
6
  interface ParcaeProviderProps {
7
- /** Pre-created client instance. If provided, url/key/transport are ignored. */
8
7
  client?: ParcaeClient;
9
- /** API base URL (required if no client provided). */
10
8
  url?: string;
11
- /** Bearer token, null (no session), or undefined (still loading). */
9
+ /** undefined = auth loading, null = no session, string = token */
12
10
  apiKey?: string | null | undefined;
13
- /** Stable user ID — triggers re-auth when it changes. */
14
11
  userId?: string | null;
15
- /** API version. Default: "v1" */
16
12
  version?: string;
17
- /** Transport type. Default: "socket" */
18
13
  transport?: ClientConfig["transport"];
19
14
  children: React__default.ReactNode;
20
15
  onReady?: (client: ParcaeClient) => void;
@@ -39,7 +34,6 @@ interface QueryChain<T> {
39
34
  __adapter?: any;
40
35
  }
41
36
  interface UseQueryOptions {
42
- /** Wait for auth before firing. Default: true. */
43
37
  waitForAuth?: boolean;
44
38
  }
45
39
  interface UseQueryResult<T> {
@@ -57,21 +51,11 @@ declare function useApi(): {
57
51
  patch: (path: string, data?: any) => Promise<any>;
58
52
  delete: (path: string, data?: any) => Promise<any>;
59
53
  };
60
- /**
61
- * useSDK — raw client instance.
62
- */
63
54
  declare function useSDK(): ParcaeClient;
64
- /**
65
- * useConnectionStatus — connection + auth state.
66
- */
67
55
  declare function useConnectionStatus(): {
68
56
  isConnected: boolean;
69
- isLoading: boolean;
70
57
  authState: AuthState;
71
58
  };
72
- /**
73
- * useAuthState — just the auth state.
74
- */
75
59
  declare function useAuthState(): AuthState;
76
60
 
77
61
  declare function useSetting<T = string>(key: string, defaultValue: T): [T, (value: T) => Promise<void>, {
@@ -1,4 +1,4 @@
1
- import { createClient } from '../chunk-IETH2XOR.js';
1
+ import { createClient } from '../chunk-KFISHMU4.js';
2
2
  import { createContext, useContext, useState, useMemo, useRef, useEffect, useSyncExternalStore, useCallback } from 'react';
3
3
  import { jsx } from 'react/jsx-runtime';
4
4
 
@@ -22,9 +22,6 @@ var ParcaeProvider = ({
22
22
  onError
23
23
  }) => {
24
24
  const [authState, setAuthState] = useState(
25
- // If apiKey is undefined, the frontend auth provider is still loading.
26
- // If apiKey is null, the user is not logged in.
27
- // If apiKey is a string, we have a token but haven't verified it yet.
28
25
  apiKey === void 0 ? "loading" : apiKey === null ? "unauthenticated" : "loading"
29
26
  );
30
27
  const [authVersion, setAuthVersion] = useState(0);
@@ -34,8 +31,10 @@ var ParcaeProvider = ({
34
31
  throw new Error(
35
32
  "ParcaeProvider requires either a `client` prop or a `url` prop"
36
33
  );
37
- return createClient({ url, version, transport, key: null });
34
+ return createClient({ url, version, transport });
38
35
  }, [externalClient, url, version, transport]);
36
+ const apiKeyRef = useRef(apiKey);
37
+ apiKeyRef.current = apiKey;
39
38
  const onReadyRef = useRef(onReady);
40
39
  onReadyRef.current = onReady;
41
40
  const onErrorRef = useRef(onError);
@@ -45,14 +44,9 @@ var ParcaeProvider = ({
45
44
  setAuthState("loading");
46
45
  return;
47
46
  }
48
- if (apiKey === null) {
49
- setAuthState("unauthenticated");
50
- setAuthVersion((v) => v + 1);
51
- return;
52
- }
53
47
  setAuthState("loading");
54
- client.setKey(apiKey).then(() => {
55
- setAuthState("authenticated");
48
+ client.authenticate(apiKey).then(({ userId: uid }) => {
49
+ setAuthState(uid ? "authenticated" : "unauthenticated");
56
50
  setAuthVersion((v) => v + 1);
57
51
  onReadyRef.current?.(client);
58
52
  }).catch((err) => {
@@ -61,6 +55,24 @@ var ParcaeProvider = ({
61
55
  onErrorRef.current?.(err);
62
56
  });
63
57
  }, [apiKey, userId, client]);
58
+ useEffect(() => {
59
+ const onReconnect = () => {
60
+ const key = apiKeyRef.current;
61
+ if (key === void 0) return;
62
+ setAuthState("loading");
63
+ client.authenticate(key).then(({ userId: uid }) => {
64
+ setAuthState(uid ? "authenticated" : "unauthenticated");
65
+ setAuthVersion((v) => v + 1);
66
+ }).catch(() => {
67
+ setAuthState("unauthenticated");
68
+ setAuthVersion((v) => v + 1);
69
+ });
70
+ };
71
+ client.on("reconnected", onReconnect);
72
+ return () => {
73
+ client.off("reconnected", onReconnect);
74
+ };
75
+ }, [client]);
64
76
  useEffect(() => {
65
77
  const onErr = (err) => onErrorRef.current?.(err);
66
78
  client.on("error", onErr);
@@ -77,17 +89,10 @@ var ParcaeProvider = ({
77
89
  var cache = /* @__PURE__ */ new Map();
78
90
  var GC_DELAY = 6e4;
79
91
  var EMPTY = [];
80
- var EMPTY_RESULT = {
81
- items: EMPTY,
82
- loading: true,
83
- error: null,
84
- refetch: () => {
85
- }
86
- };
87
92
  function getOrCreate(key) {
88
- let entry = cache.get(key);
89
- if (!entry) {
90
- entry = {
93
+ let e = cache.get(key);
94
+ if (!e) {
95
+ e = {
91
96
  items: EMPTY,
92
97
  loading: true,
93
98
  error: null,
@@ -96,14 +101,14 @@ function getOrCreate(key) {
96
101
  dispose: null,
97
102
  gcTimer: null
98
103
  };
99
- cache.set(key, entry);
104
+ cache.set(key, e);
100
105
  }
101
- return entry;
106
+ return e;
102
107
  }
103
- function notify(entry) {
104
- for (const fn of entry.listeners) fn();
108
+ function notify(e) {
109
+ for (const fn of e.listeners) fn();
105
110
  }
106
- function fetchAndSubscribe(key, entry, chain, client) {
111
+ function doFetch(key, entry, chain, client) {
107
112
  entry.loading = true;
108
113
  entry.error = null;
109
114
  notify(entry);
@@ -117,40 +122,23 @@ function fetchAndSubscribe(key, entry, chain, client) {
117
122
  notify(entry);
118
123
  });
119
124
  if (!entry.dispose && chain.__modelType) {
120
- const event = `query:${key}`;
121
- client.send("subscribe:query", {
122
- hash: key,
123
- modelType: chain.__modelType,
124
- steps: chain.__steps ?? []
125
- });
126
- const unsub = client.subscribe(event, (ops) => {
125
+ const diffEvent = `query:diff:${key}`;
126
+ const unsub = client.subscribe(diffEvent, (ops) => {
127
127
  if (!ops?.length) return;
128
- const map = new Map(entry.items.map((item) => [item.id, item]));
128
+ const map = new Map(entry.items.map((i) => [i.id, i]));
129
129
  let changed = false;
130
130
  for (const op of ops) {
131
- switch (op.op) {
132
- case "add":
133
- if (!map.has(op.id) && op.data && chain.__modelClass) {
134
- map.set(op.id, new chain.__modelClass(chain.__adapter, op.data));
135
- changed = true;
136
- }
137
- break;
138
- case "remove":
139
- if (map.has(op.id)) {
140
- map.delete(op.id);
141
- changed = true;
142
- }
143
- break;
144
- case "update": {
145
- const existing = map.get(op.id);
146
- if (existing && op.data) {
147
- for (const [k, v] of Object.entries(op.data)) {
148
- existing[k] = v;
149
- }
150
- changed = true;
151
- }
152
- break;
153
- }
131
+ if (op.op === "add" && !map.has(op.id) && op.data && chain.__modelClass) {
132
+ map.set(op.id, new chain.__modelClass(chain.__adapter, op.data));
133
+ changed = true;
134
+ } else if (op.op === "remove" && map.has(op.id)) {
135
+ map.delete(op.id);
136
+ changed = true;
137
+ } else if (op.op === "update" && map.has(op.id) && op.data) {
138
+ const existing = map.get(op.id);
139
+ for (const [k, v] of Object.entries(op.data))
140
+ existing[k] = v;
141
+ changed = true;
154
142
  }
155
143
  }
156
144
  if (changed) {
@@ -160,7 +148,6 @@ function fetchAndSubscribe(key, entry, chain, client) {
160
148
  });
161
149
  entry.dispose = () => {
162
150
  unsub();
163
- client.send("unsubscribe:query", { hash: key });
164
151
  entry.dispose = null;
165
152
  };
166
153
  }
@@ -172,23 +159,23 @@ function useQuery(chain, options = {}) {
172
159
  const key = chain && authReady ? `${chain.__modelType}:${authVersion}:${JSON.stringify(chain.__steps ?? [])}` : null;
173
160
  const keyRef = useRef(key);
174
161
  keyRef.current = key;
175
- const subscribe = (onStoreChange) => {
162
+ const subscribe = (onChange) => {
176
163
  const k = keyRef.current;
177
164
  if (!k) return () => {
178
165
  };
179
- const entry2 = getOrCreate(k);
180
- entry2.refs++;
181
- entry2.listeners.add(onStoreChange);
182
- if (entry2.gcTimer) {
183
- clearTimeout(entry2.gcTimer);
184
- entry2.gcTimer = null;
166
+ const e = getOrCreate(k);
167
+ e.refs++;
168
+ e.listeners.add(onChange);
169
+ if (e.gcTimer) {
170
+ clearTimeout(e.gcTimer);
171
+ e.gcTimer = null;
185
172
  }
186
173
  return () => {
187
- entry2.listeners.delete(onStoreChange);
188
- entry2.refs--;
189
- if (entry2.refs <= 0) {
190
- entry2.gcTimer = setTimeout(() => {
191
- entry2.dispose?.();
174
+ e.listeners.delete(onChange);
175
+ e.refs--;
176
+ if (e.refs <= 0) {
177
+ e.gcTimer = setTimeout(() => {
178
+ e.dispose?.();
192
179
  cache.delete(k);
193
180
  }, GC_DELAY);
194
181
  }
@@ -207,16 +194,22 @@ function useQuery(chain, options = {}) {
207
194
  useEffect(() => {
208
195
  if (!key || !chain) return;
209
196
  const entry2 = getOrCreate(key);
210
- if (entry2.items === EMPTY || entry2.items.length === 0) {
211
- fetchAndSubscribe(key, entry2, chain, client);
197
+ if (entry2.items === EMPTY) {
198
+ doFetch(key, entry2, chain, client);
212
199
  }
213
200
  }, [key]);
214
201
  const refetch = () => {
215
202
  if (!key || !chain) return;
216
- const entry2 = getOrCreate(key);
217
- fetchAndSubscribe(key, entry2, chain, client);
203
+ doFetch(key, getOrCreate(key), chain, client);
218
204
  };
219
- if (!key) return EMPTY_RESULT;
205
+ if (!key)
206
+ return {
207
+ items: EMPTY,
208
+ loading: !authReady,
209
+ error: null,
210
+ refetch: () => {
211
+ }
212
+ };
220
213
  const entry = cache.get(key);
221
214
  return {
222
215
  items,
@@ -243,11 +236,7 @@ function useSDK() {
243
236
  }
244
237
  function useConnectionStatus() {
245
238
  const { client, authState } = useParcae();
246
- return {
247
- isConnected: client.isConnected,
248
- isLoading: client.isLoading,
249
- authState
250
- };
239
+ return { isConnected: client.isConnected, authState };
251
240
  }
252
241
  function useAuthState() {
253
242
  return useParcae().authState;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/context.ts","../../src/react/Provider.tsx","../../src/react/useQuery.ts","../../src/react/useApi.ts","../../src/react/useSetting.ts"],"names":["useRef","entry","useEffect","useMemo","useState","useCallback"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { ParcaeClient } from \"../client\";\n\nexport type AuthState = \"loading\" | \"authenticated\" | \"unauthenticated\";\n\nexport interface ParcaeContextValue {\n client: ParcaeClient;\n authState: AuthState;\n authVersion: number;\n}\n\nexport const ParcaeContext = createContext<ParcaeContextValue | null>(null);\n\nexport function useParcae(): ParcaeContextValue {\n const ctx = useContext(ParcaeContext);\n if (!ctx) {\n throw new Error(\"useParcae must be used within a <ParcaeProvider>\");\n }\n return ctx;\n}\n","\"use client\";\n\nimport React, {\n useEffect,\n useMemo,\n useRef,\n useState,\n useCallback,\n} from \"react\";\nimport { createClient } from \"../client\";\nimport type { ParcaeClient, ClientConfig } from \"../client\";\nimport { ParcaeContext } from \"./context\";\nimport type { ParcaeContextValue, AuthState } from \"./context\";\n\nexport interface ParcaeProviderProps {\n /** Pre-created client instance. If provided, url/key/transport are ignored. */\n client?: ParcaeClient;\n /** API base URL (required if no client provided). */\n url?: string;\n /** Bearer token, null (no session), or undefined (still loading). */\n apiKey?: string | null | undefined;\n /** Stable user ID — triggers re-auth when it changes. */\n userId?: string | null;\n /** API version. Default: \"v1\" */\n version?: string;\n /** Transport type. Default: \"socket\" */\n transport?: ClientConfig[\"transport\"];\n children: React.ReactNode;\n onReady?: (client: ParcaeClient) => void;\n onError?: (error: Error) => void;\n}\n\nexport const ParcaeProvider: React.FC<ParcaeProviderProps> = ({\n client: externalClient,\n url,\n apiKey,\n userId,\n version = \"v1\",\n transport = \"socket\",\n children,\n onReady,\n onError,\n}) => {\n const [authState, setAuthState] = useState<AuthState>(\n // If apiKey is undefined, the frontend auth provider is still loading.\n // If apiKey is null, the user is not logged in.\n // If apiKey is a string, we have a token but haven't verified it yet.\n apiKey === undefined\n ? \"loading\"\n : apiKey === null\n ? \"unauthenticated\"\n : \"loading\",\n );\n const [authVersion, setAuthVersion] = useState(0);\n\n const client = useMemo(() => {\n if (externalClient) return externalClient;\n if (!url)\n throw new Error(\n \"ParcaeProvider requires either a `client` prop or a `url` prop\",\n );\n return createClient({ url, version, transport, key: null });\n }, [externalClient, url, version, transport]);\n\n const onReadyRef = useRef(onReady);\n onReadyRef.current = onReady;\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n\n // Authenticate when apiKey changes\n useEffect(() => {\n // apiKey is undefined → frontend auth still loading, do nothing\n if (apiKey === undefined) {\n setAuthState(\"loading\");\n return;\n }\n\n // apiKey is null → user is not logged in\n if (apiKey === null) {\n setAuthState(\"unauthenticated\");\n setAuthVersion((v) => v + 1);\n return;\n }\n\n // apiKey is a string → send to backend for verification\n setAuthState(\"loading\");\n client\n .setKey(apiKey)\n .then(() => {\n setAuthState(\"authenticated\");\n setAuthVersion((v) => v + 1);\n onReadyRef.current?.(client);\n })\n .catch((err) => {\n setAuthState(\"unauthenticated\");\n setAuthVersion((v) => v + 1);\n onErrorRef.current?.(err);\n });\n }, [apiKey, userId, client]);\n\n useEffect(() => {\n const onErr = (err: Error) => onErrorRef.current?.(err);\n client.on(\"error\", onErr);\n return () => {\n client.off(\"error\", onErr);\n };\n }, [client]);\n\n const contextValue = useMemo<ParcaeContextValue>(\n () => ({ client, authState, authVersion }),\n [client, authState, authVersion],\n );\n\n return (\n <ParcaeContext.Provider value={contextValue}>\n {children}\n </ParcaeContext.Provider>\n );\n};\n\nexport default ParcaeProvider;\n","\"use client\";\n\n/**\n * useQuery — efficient reactive data fetching.\n *\n * - Deduplicates: same query from multiple components = one fetch\n * - Caches: unmount/remount doesn't re-fetch if data is fresh\n * - Realtime: subscribes to server-pushed diffs (add/remove/update)\n * - Auth-aware: waits for auth before firing scoped queries\n * - Efficient: React only re-renders when the items array reference changes\n */\n\nimport { useEffect, useRef, useSyncExternalStore } from \"react\";\nimport { useParcae } from \"./context\";\nimport type { ParcaeClient } from \"../client\";\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\ninterface QueryChain<T> {\n find(): Promise<T[]>;\n __steps?: any[];\n __modelType?: string;\n __modelClass?: any;\n __adapter?: any;\n}\n\ninterface UseQueryOptions {\n /** Wait for auth before firing. Default: true. */\n waitForAuth?: boolean;\n}\n\ninterface UseQueryResult<T> {\n items: T[];\n loading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\n// ─── External Cache ──────────────────────────────────────────────────────────\n\ninterface CacheEntry {\n items: any[];\n loading: boolean;\n error: Error | null;\n refs: number;\n listeners: Set<() => void>;\n dispose: (() => void) | null;\n gcTimer: ReturnType<typeof setTimeout> | null;\n}\n\nconst cache = new Map<string, CacheEntry>();\nconst GC_DELAY = 60_000;\n\nconst EMPTY: any[] = [];\nconst EMPTY_RESULT: UseQueryResult<any> = {\n items: EMPTY,\n loading: true,\n error: null,\n refetch: () => {},\n};\n\nfunction getOrCreate(key: string): CacheEntry {\n let entry = cache.get(key);\n if (!entry) {\n entry = {\n items: EMPTY,\n loading: true,\n error: null,\n refs: 0,\n listeners: new Set(),\n dispose: null,\n gcTimer: null,\n };\n cache.set(key, entry);\n }\n return entry;\n}\n\nfunction notify(entry: CacheEntry): void {\n for (const fn of entry.listeners) fn();\n}\n\n// ─── Fetch + Subscribe ───────────────────────────────────────────────────────\n\nfunction fetchAndSubscribe(\n key: string,\n entry: CacheEntry,\n chain: QueryChain<any>,\n client: ParcaeClient,\n): void {\n // Fetch\n entry.loading = true;\n entry.error = null;\n notify(entry);\n\n chain\n .find()\n .then((result: any[]) => {\n entry.items = result;\n entry.loading = false;\n notify(entry);\n })\n .catch((err: Error) => {\n entry.error = err;\n entry.loading = false;\n notify(entry);\n });\n\n // Subscribe to realtime diffs\n if (!entry.dispose && chain.__modelType) {\n const event = `query:${key}`;\n\n client.send(\"subscribe:query\", {\n hash: key,\n modelType: chain.__modelType,\n steps: chain.__steps ?? [],\n });\n\n const unsub = client.subscribe(event, (ops: any[]) => {\n if (!ops?.length) return;\n\n const map = new Map(entry.items.map((item: any) => [item.id, item]));\n let changed = false;\n\n for (const op of ops) {\n switch (op.op) {\n case \"add\":\n if (!map.has(op.id) && op.data && chain.__modelClass) {\n map.set(op.id, new chain.__modelClass(chain.__adapter, op.data));\n changed = true;\n }\n break;\n case \"remove\":\n if (map.has(op.id)) {\n map.delete(op.id);\n changed = true;\n }\n break;\n case \"update\": {\n const existing = map.get(op.id);\n if (existing && op.data) {\n for (const [k, v] of Object.entries(op.data)) {\n (existing as any)[k] = v;\n }\n changed = true;\n }\n break;\n }\n }\n }\n\n if (changed) {\n entry.items = [...map.values()];\n notify(entry);\n }\n });\n\n entry.dispose = () => {\n unsub();\n client.send(\"unsubscribe:query\", { hash: key });\n entry.dispose = null;\n };\n }\n}\n\n// ─── useQuery ────────────────────────────────────────────────────────────────\n\nexport function useQuery<T>(\n chain: QueryChain<T> | null | undefined,\n options: UseQueryOptions = {},\n): UseQueryResult<T> {\n const { client, authState, authVersion } = useParcae();\n const waitForAuth = options.waitForAuth ?? true;\n const authReady = !waitForAuth || authState !== \"loading\";\n\n const key =\n chain && authReady\n ? `${chain.__modelType}:${authVersion}:${JSON.stringify(chain.__steps ?? [])}`\n : null;\n\n // Ref to track the current key (for cleanup)\n const keyRef = useRef(key);\n keyRef.current = key;\n\n // ── useSyncExternalStore ──────────────────────────────────────────\n\n const subscribe = (onStoreChange: () => void) => {\n const k = keyRef.current;\n if (!k) return () => {};\n\n const entry = getOrCreate(k);\n entry.refs++;\n entry.listeners.add(onStoreChange);\n\n // Cancel GC\n if (entry.gcTimer) {\n clearTimeout(entry.gcTimer);\n entry.gcTimer = null;\n }\n\n return () => {\n entry.listeners.delete(onStoreChange);\n entry.refs--;\n\n if (entry.refs <= 0) {\n entry.gcTimer = setTimeout(() => {\n entry.dispose?.();\n cache.delete(k);\n }, GC_DELAY);\n }\n };\n };\n\n const getSnapshot = (): any[] => {\n const k = keyRef.current;\n if (!k) return EMPTY;\n return cache.get(k)?.items ?? EMPTY;\n };\n\n const items = useSyncExternalStore(\n subscribe,\n getSnapshot,\n getSnapshot,\n ) as T[];\n\n // ── Trigger fetch when key changes ────────────────────────────────\n\n useEffect(() => {\n if (!key || !chain) return;\n\n const entry = getOrCreate(key);\n\n // Only fetch if this is the first subscriber or items are empty\n if (entry.items === EMPTY || entry.items.length === 0) {\n fetchAndSubscribe(key, entry, chain, client);\n }\n }, [key]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // ── Refetch ───────────────────────────────────────────────────────\n\n const refetch = () => {\n if (!key || !chain) return;\n const entry = getOrCreate(key);\n fetchAndSubscribe(key, entry, chain, client);\n };\n\n // ── Return ────────────────────────────────────────────────────────\n\n if (!key) return EMPTY_RESULT;\n\n const entry = cache.get(key);\n return {\n items,\n loading: entry?.loading ?? true,\n error: entry?.error ?? null,\n refetch,\n };\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { useParcae } from \"./context\";\n\nexport function useApi() {\n const { client } = useParcae();\n\n return useMemo(\n () => ({\n get: client.get.bind(client),\n post: client.post.bind(client),\n put: client.put.bind(client),\n patch: client.patch.bind(client),\n delete: client.delete.bind(client),\n }),\n [client],\n );\n}\n\n/**\n * useSDK — raw client instance.\n */\nexport function useSDK() {\n return useParcae().client;\n}\n\n/**\n * useConnectionStatus — connection + auth state.\n */\nexport function useConnectionStatus() {\n const { client, authState } = useParcae();\n return {\n isConnected: client.isConnected,\n isLoading: client.isLoading,\n authState,\n };\n}\n\n/**\n * useAuthState — just the auth state.\n */\nexport function useAuthState() {\n return useParcae().authState;\n}\n","\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { useParcae } from \"./context\";\n\nexport function useSetting<T = string>(\n key: string,\n defaultValue: T,\n): [T, (value: T) => Promise<void>, { isLoading: boolean }] {\n const { client, authState } = useParcae();\n const [value, setValue] = useState<T>(defaultValue);\n const [isLoading, setIsLoading] = useState(true);\n\n // Wait for auth before fetching settings (they're user-scoped)\n useEffect(() => {\n if (authState === \"loading\") return;\n if (authState === \"unauthenticated\") {\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n\n client\n .get(`/settings/${encodeURIComponent(key)}`)\n .then((result) => {\n if (!cancelled && result?.value !== undefined) {\n setValue(result.value);\n }\n })\n .catch(() => {})\n .finally(() => {\n if (!cancelled) setIsLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, [key, client, authState]);\n\n const update = useCallback(\n async (newValue: T) => {\n setValue(newValue);\n await client.put(`/settings/${encodeURIComponent(key)}`, {\n value: newValue,\n });\n },\n [key, client],\n );\n\n return [value, update, { isLoading }];\n}\n"]}
1
+ {"version":3,"sources":["../../src/react/context.ts","../../src/react/Provider.tsx","../../src/react/useQuery.ts","../../src/react/useApi.ts","../../src/react/useSetting.ts"],"names":["useRef","useEffect","entry","useMemo","useState"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { ParcaeClient } from \"../client\";\n\nexport type AuthState = \"loading\" | \"authenticated\" | \"unauthenticated\";\n\nexport interface ParcaeContextValue {\n client: ParcaeClient;\n authState: AuthState;\n authVersion: number;\n}\n\nexport const ParcaeContext = createContext<ParcaeContextValue | null>(null);\n\nexport function useParcae(): ParcaeContextValue {\n const ctx = useContext(ParcaeContext);\n if (!ctx) {\n throw new Error(\"useParcae must be used within a <ParcaeProvider>\");\n }\n return ctx;\n}\n","\"use client\";\n\nimport React, { useEffect, useMemo, useRef, useState } from \"react\";\nimport { createClient } from \"../client\";\nimport type { ParcaeClient, ClientConfig } from \"../client\";\nimport { ParcaeContext } from \"./context\";\nimport type { AuthState } from \"./context\";\n\nexport interface ParcaeProviderProps {\n client?: ParcaeClient;\n url?: string;\n /** undefined = auth loading, null = no session, string = token */\n apiKey?: string | null | undefined;\n userId?: string | null;\n version?: string;\n transport?: ClientConfig[\"transport\"];\n children: React.ReactNode;\n onReady?: (client: ParcaeClient) => void;\n onError?: (error: Error) => void;\n}\n\nexport const ParcaeProvider: React.FC<ParcaeProviderProps> = ({\n client: externalClient,\n url,\n apiKey,\n userId,\n version = \"v1\",\n transport = \"socket\",\n children,\n onReady,\n onError,\n}) => {\n const [authState, setAuthState] = useState<AuthState>(\n apiKey === undefined\n ? \"loading\"\n : apiKey === null\n ? \"unauthenticated\"\n : \"loading\",\n );\n const [authVersion, setAuthVersion] = useState(0);\n\n const client = useMemo(() => {\n if (externalClient) return externalClient;\n if (!url)\n throw new Error(\n \"ParcaeProvider requires either a `client` prop or a `url` prop\",\n );\n return createClient({ url, version, transport });\n }, [externalClient, url, version, transport]);\n\n const apiKeyRef = useRef(apiKey);\n apiKeyRef.current = apiKey;\n const onReadyRef = useRef(onReady);\n onReadyRef.current = onReady;\n const onErrorRef = useRef(onError);\n onErrorRef.current = onError;\n\n // Authenticate when apiKey changes\n useEffect(() => {\n if (apiKey === undefined) {\n setAuthState(\"loading\");\n return;\n }\n\n setAuthState(\"loading\");\n client\n .authenticate(apiKey)\n .then(({ userId: uid }) => {\n setAuthState(uid ? \"authenticated\" : \"unauthenticated\");\n setAuthVersion((v) => v + 1);\n onReadyRef.current?.(client);\n })\n .catch((err: Error) => {\n setAuthState(\"unauthenticated\");\n setAuthVersion((v) => v + 1);\n onErrorRef.current?.(err);\n });\n }, [apiKey, userId, client]);\n\n // Re-authenticate on reconnect\n useEffect(() => {\n const onReconnect = () => {\n const key = apiKeyRef.current;\n if (key === undefined) return;\n\n setAuthState(\"loading\");\n client\n .authenticate(key)\n .then(({ userId: uid }) => {\n setAuthState(uid ? \"authenticated\" : \"unauthenticated\");\n setAuthVersion((v) => v + 1);\n })\n .catch(() => {\n setAuthState(\"unauthenticated\");\n setAuthVersion((v) => v + 1);\n });\n };\n\n client.on(\"reconnected\", onReconnect);\n return () => {\n client.off(\"reconnected\", onReconnect);\n };\n }, [client]);\n\n // Forward errors\n useEffect(() => {\n const onErr = (err: Error) => onErrorRef.current?.(err);\n client.on(\"error\", onErr);\n return () => {\n client.off(\"error\", onErr);\n };\n }, [client]);\n\n const contextValue = useMemo(\n () => ({ client, authState, authVersion }),\n [client, authState, authVersion],\n );\n\n return (\n <ParcaeContext.Provider value={contextValue}>\n {children}\n </ParcaeContext.Provider>\n );\n};\n\nexport default ParcaeProvider;\n","\"use client\";\n\n/**\n * useQuery — calls chain.find() via socket RPC, subscribes to diffs automatically.\n *\n * No separate subscribe:query event. The server auto-subscribes when you\n * query a list endpoint. Diffs arrive on the same query key.\n */\n\nimport { useEffect, useRef, useSyncExternalStore } from \"react\";\nimport { useParcae } from \"./context\";\nimport type { ParcaeClient } from \"../client\";\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\ninterface QueryChain<T> {\n find(): Promise<T[]>;\n __steps?: any[];\n __modelType?: string;\n __modelClass?: any;\n __adapter?: any;\n}\n\ninterface UseQueryOptions {\n waitForAuth?: boolean;\n}\n\ninterface UseQueryResult<T> {\n items: T[];\n loading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\n// ─── External Cache ──────────────────────────────────────────────────────────\n\ninterface CacheEntry {\n items: any[];\n loading: boolean;\n error: Error | null;\n refs: number;\n listeners: Set<() => void>;\n dispose: (() => void) | null;\n gcTimer: ReturnType<typeof setTimeout> | null;\n}\n\nconst cache = new Map<string, CacheEntry>();\nconst GC_DELAY = 60_000;\nconst EMPTY: any[] = [];\n\nfunction getOrCreate(key: string): CacheEntry {\n let e = cache.get(key);\n if (!e) {\n e = {\n items: EMPTY,\n loading: true,\n error: null,\n refs: 0,\n listeners: new Set(),\n dispose: null,\n gcTimer: null,\n };\n cache.set(key, e);\n }\n return e;\n}\n\nfunction notify(e: CacheEntry): void {\n for (const fn of e.listeners) fn();\n}\n\n// ─── Fetch via chain.find() ──────────────────────────────────────────────────\n\nfunction doFetch(\n key: string,\n entry: CacheEntry,\n chain: QueryChain<any>,\n client: ParcaeClient,\n): void {\n entry.loading = true;\n entry.error = null;\n notify(entry);\n\n chain\n .find()\n .then((result: any[]) => {\n entry.items = result;\n entry.loading = false;\n notify(entry);\n })\n .catch((err: Error) => {\n entry.error = err;\n entry.loading = false;\n notify(entry);\n });\n\n // Listen for realtime diffs (server auto-subscribes on the call)\n if (!entry.dispose && chain.__modelType) {\n const diffEvent = `query:diff:${key}`;\n\n const unsub = client.subscribe(diffEvent, (ops: any[]) => {\n if (!ops?.length) return;\n\n const map = new Map(entry.items.map((i: any) => [i.id, i]));\n let changed = false;\n\n for (const op of ops) {\n if (\n op.op === \"add\" &&\n !map.has(op.id) &&\n op.data &&\n chain.__modelClass\n ) {\n map.set(op.id, new chain.__modelClass(chain.__adapter, op.data));\n changed = true;\n } else if (op.op === \"remove\" && map.has(op.id)) {\n map.delete(op.id);\n changed = true;\n } else if (op.op === \"update\" && map.has(op.id) && op.data) {\n const existing = map.get(op.id);\n for (const [k, v] of Object.entries(op.data))\n (existing as any)[k] = v;\n changed = true;\n }\n }\n\n if (changed) {\n entry.items = [...map.values()];\n notify(entry);\n }\n });\n\n entry.dispose = () => {\n unsub();\n entry.dispose = null;\n };\n }\n}\n\n// ─── useQuery ────────────────────────────────────────────────────────────────\n\nexport function useQuery<T>(\n chain: QueryChain<T> | null | undefined,\n options: UseQueryOptions = {},\n): UseQueryResult<T> {\n const { client, authState, authVersion } = useParcae();\n const waitForAuth = options.waitForAuth ?? true;\n const authReady = !waitForAuth || authState !== \"loading\";\n\n const key =\n chain && authReady\n ? `${chain.__modelType}:${authVersion}:${JSON.stringify(chain.__steps ?? [])}`\n : null;\n\n const keyRef = useRef(key);\n keyRef.current = key;\n\n const subscribe = (onChange: () => void) => {\n const k = keyRef.current;\n if (!k) return () => {};\n const e = getOrCreate(k);\n e.refs++;\n e.listeners.add(onChange);\n if (e.gcTimer) {\n clearTimeout(e.gcTimer);\n e.gcTimer = null;\n }\n return () => {\n e.listeners.delete(onChange);\n e.refs--;\n if (e.refs <= 0) {\n e.gcTimer = setTimeout(() => {\n e.dispose?.();\n cache.delete(k);\n }, GC_DELAY);\n }\n };\n };\n\n const getSnapshot = (): any[] => {\n const k = keyRef.current;\n if (!k) return EMPTY;\n return cache.get(k)?.items ?? EMPTY;\n };\n\n const items = useSyncExternalStore(\n subscribe,\n getSnapshot,\n getSnapshot,\n ) as T[];\n\n useEffect(() => {\n if (!key || !chain) return;\n const entry = getOrCreate(key);\n if (entry.items === EMPTY) {\n doFetch(key, entry, chain, client);\n }\n }, [key]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const refetch = () => {\n if (!key || !chain) return;\n doFetch(key, getOrCreate(key), chain, client);\n };\n\n if (!key)\n return {\n items: EMPTY as T[],\n loading: !authReady,\n error: null,\n refetch: () => {},\n };\n\n const entry = cache.get(key);\n return {\n items,\n loading: entry?.loading ?? true,\n error: entry?.error ?? null,\n refetch,\n };\n}\n","\"use client\";\n\nimport { useMemo } from \"react\";\nimport { useParcae } from \"./context\";\n\nexport function useApi() {\n const { client } = useParcae();\n return useMemo(\n () => ({\n get: client.get.bind(client),\n post: client.post.bind(client),\n put: client.put.bind(client),\n patch: client.patch.bind(client),\n delete: client.delete.bind(client),\n }),\n [client],\n );\n}\n\nexport function useSDK() {\n return useParcae().client;\n}\n\nexport function useConnectionStatus() {\n const { client, authState } = useParcae();\n return { isConnected: client.isConnected, authState };\n}\n\nexport function useAuthState() {\n return useParcae().authState;\n}\n","\"use client\";\n\nimport { useState, useEffect, useCallback } from \"react\";\nimport { useParcae } from \"./context\";\n\nexport function useSetting<T = string>(\n key: string,\n defaultValue: T,\n): [T, (value: T) => Promise<void>, { isLoading: boolean }] {\n const { client, authState } = useParcae();\n const [value, setValue] = useState<T>(defaultValue);\n const [isLoading, setIsLoading] = useState(true);\n\n // Wait for auth before fetching settings (they're user-scoped)\n useEffect(() => {\n if (authState === \"loading\") return;\n if (authState === \"unauthenticated\") {\n setIsLoading(false);\n return;\n }\n\n let cancelled = false;\n\n client\n .get(`/settings/${encodeURIComponent(key)}`)\n .then((result) => {\n if (!cancelled && result?.value !== undefined) {\n setValue(result.value);\n }\n })\n .catch(() => {})\n .finally(() => {\n if (!cancelled) setIsLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, [key, client, authState]);\n\n const update = useCallback(\n async (newValue: T) => {\n setValue(newValue);\n await client.put(`/settings/${encodeURIComponent(key)}`, {\n value: newValue,\n });\n },\n [key, client],\n );\n\n return [value, update, { isLoading }];\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parcae/sdk",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Parcae SDK — client transport and React hooks for Parcae backends",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/transports/socket.ts","../src/transports/sse.ts","../src/client.ts"],"names":["EventEmitter","body"],"mappings":"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","file":"chunk-IETH2XOR.js","sourcesContent":["/**\n * SocketTransport — Socket.IO implementation of the Transport interface.\n *\n * Bidirectional, full-duplex. Best for apps that need realtime subscriptions\n * (live query updates, collaborative editing, chat, etc.).\n *\n * Extracted from Dollhouse Studio's Dollhouse.ts (667 lines).\n */\n\nimport SocketIO from \"socket.io-client\";\nimport pako from \"pako\";\nimport { decompress } from \"compress-json\";\nimport { EventEmitter } from \"eventemitter3\";\nimport ShortId from \"short-unique-id\";\nimport type { Transport } from \"@parcae/model\";\n\nconst uid = new ShortId({ length: 10 });\nconst SOCKET_CONNECTIONS = new Map<string, any>();\n\nexport interface SocketTransportConfig {\n url: string;\n key?: string | null | (() => Promise<string | null>);\n version?: string;\n /** Socket.IO path. Default: \"/ws\" */\n path?: string;\n}\n\nexport class SocketTransport extends EventEmitter implements Transport {\n private socket: any = null;\n private pendingHandlers: Array<{\n event: string;\n handler: (...args: any[]) => void;\n }> = [];\n private apiKey: string | null | (() => Promise<string | null>);\n private key: string | null = null;\n private url: string;\n private version: string;\n private socketPath: string;\n private waitForAuth: Promise<void> | null = null;\n private resolveAuth: (() => void) | null = null;\n private inflight = new Map<string, Promise<any>>();\n\n public loading: Promise<void>;\n public isLoading = true;\n public isConnected = false;\n public isConnecting = false;\n public authVersion = 0;\n\n constructor(config: SocketTransportConfig) {\n super();\n this.url = config.url;\n this.apiKey = config.key ?? null;\n this.version = config.version ?? \"v1\";\n this.socketPath = config.path ?? \"/ws\";\n this.loading = this.init(this.apiKey);\n }\n\n // ── Auth ──────────────────────────────────────────────────────────────\n\n async setKey(\n key: string | null | (() => Promise<string | null>),\n ): Promise<void> {\n this.apiKey = key;\n this.isLoading = true;\n this.loading = this.init(key);\n await this.loading;\n }\n\n // ── Init ──────────────────────────────────────────────────────────────\n\n private async init(\n key: string | null | (() => Promise<string | null>),\n ): Promise<void> {\n this.isLoading = true;\n this.isConnecting = true;\n\n try {\n this.key = typeof key === \"function\" ? await key() : key;\n const socketKey = `${this.url}:${this.version}`;\n\n if (!SOCKET_CONNECTIONS.has(socketKey)) {\n SOCKET_CONNECTIONS.set(\n socketKey,\n SocketIO(this.url, {\n path: this.socketPath,\n agent: true,\n transports: [\"websocket\"],\n withCredentials: true,\n query: { key: this.key ?? undefined, compression: true },\n }),\n );\n }\n\n this.socket = SOCKET_CONNECTIONS.get(socketKey);\n\n if (this.key) {\n const prevResolve = this.resolveAuth;\n this.waitForAuth = new Promise<void>((resolve) => {\n this.resolveAuth = resolve;\n this.socket.emit(\"authenticate\", this.key, () => {\n this.waitForAuth = null;\n this.resolveAuth = null;\n this.authVersion++;\n this.emit(\"authenticated\");\n if (prevResolve) prevResolve();\n resolve();\n });\n });\n } else {\n this.waitForAuth = null;\n this.resolveAuth = null;\n }\n\n this.isLoading = false;\n } catch (error) {\n this.isLoading = false;\n this.isConnecting = false;\n this.emit(\"error\", error);\n throw error;\n }\n\n this.setupEvents();\n }\n\n private setupEvents(): void {\n if (!this.socket) return;\n\n for (const { event, handler } of this.pendingHandlers) {\n this.socket.on(event, handler);\n }\n this.pendingHandlers = [];\n\n let hasConnected = this.isConnected;\n this.socket.off(\"connect\");\n this.socket.off(\"disconnect\");\n this.socket.off(\"error\");\n\n this.socket.on(\"connect\", () => {\n const wasDisconnected = !this.isConnected && hasConnected;\n this.isConnected = true;\n this.isConnecting = false;\n hasConnected = true;\n\n // Re-authenticate on reconnect\n if (wasDisconnected && this.key) {\n this.socket.emit(\"authenticate\", this.key, () => {\n this.emit(\"reconnected\");\n });\n } else {\n this.emit(wasDisconnected ? \"reconnected\" : \"connected\");\n }\n });\n\n this.socket.on(\"disconnect\", () => {\n this.isConnected = false;\n this.emit(\"disconnected\");\n });\n\n this.socket.on(\"error\", (error: Error) => {\n this.emit(\"error\", error);\n });\n }\n\n // ── Request/Response ──────────────────────────────────────────────────\n\n private async fetch(\n method: string,\n path: string,\n data: any = {},\n ): Promise<any> {\n await this.loading;\n if (this.waitForAuth) await this.waitForAuth;\n if (!this.socket) throw new Error(\"Socket not initialized\");\n\n const upper = method.toUpperCase();\n if (upper === \"GET\") {\n const dedupeKey = `GET:${path}:${this.authVersion}:${JSON.stringify(data)}`;\n const existing = this.inflight.get(dedupeKey);\n if (existing) return existing;\n const req = this._doFetch(method, path, data);\n this.inflight.set(dedupeKey, req);\n req.finally(() => this.inflight.delete(dedupeKey));\n return req;\n }\n\n return this._doFetch(method, path, data);\n }\n\n private async _doFetch(\n method: string,\n path: string,\n data: any = {},\n ): Promise<any> {\n if (!this.isConnected) {\n await new Promise<void>((resolve, reject) => {\n if (this.socket.connected) return resolve();\n const timeout = setTimeout(() => {\n cleanup();\n reject(new Error(\"Connection timeout\"));\n }, 30000);\n const onConnect = () => {\n cleanup();\n resolve();\n };\n const onError = (err: Error) => {\n cleanup();\n reject(new Error(`Connection failed: ${err.message}`));\n };\n const cleanup = () => {\n clearTimeout(timeout);\n this.socket.off(\"connect\", onConnect);\n this.socket.off(\"connect_error\", onError);\n };\n this.socket.once(\"connect\", onConnect);\n this.socket.once(\"connect_error\", onError);\n });\n }\n\n const id = uid.rnd();\n\n return new Promise((resolve, reject) => {\n this.socket.once(id, (msg: any) => {\n try {\n const uncompressed = pako.ungzip(msg, { to: \"string\" });\n const { success, result, message, error } = decompress(\n JSON.parse(uncompressed),\n );\n if (success) resolve(result);\n else\n reject(\n new Error(\n message || error || `Request failed: ${method}:${path}`,\n ),\n );\n } catch (err) {\n reject(err);\n }\n });\n this.socket.emit(\n \"call\",\n id,\n method.toUpperCase(),\n `/${this.version}${path}`,\n data,\n );\n });\n }\n\n async get(path: string, data?: any): Promise<any> {\n return this.fetch(\"get\", path, data);\n }\n async post(path: string, data?: any): Promise<any> {\n return this.fetch(\"post\", path, data);\n }\n async put(path: string, data?: any): Promise<any> {\n return this.fetch(\"put\", path, data);\n }\n async patch(path: string, data?: any): Promise<any> {\n return this.fetch(\"patch\", path, data);\n }\n async delete(path: string, data?: any): Promise<any> {\n return this.fetch(\"delete\", path, data);\n }\n\n // ── Subscriptions ─────────────────────────────────────────────────────\n\n subscribe(event: string, handler: (...args: any[]) => void): () => void {\n if (this.socket) {\n this.socket.on(event, handler);\n } else {\n this.pendingHandlers.push({ event, handler });\n }\n return () => this.unsubscribe(event, handler);\n }\n\n unsubscribe(event: string, handler?: (...args: any[]) => void): void {\n if (this.socket) this.socket.off(event, handler);\n this.pendingHandlers = this.pendingHandlers.filter(\n (h) => !(h.event === event && (!handler || h.handler === handler)),\n );\n }\n\n // ── Control messages ──────────────────────────────────────────────────\n\n async send(event: string, ...args: any[]): Promise<void> {\n if (!this.isConnected && this.socket) {\n await new Promise<void>((resolve) => {\n if (this.socket.connected) return resolve();\n this.socket.once(\"connect\", () => resolve());\n });\n }\n if (this.waitForAuth) await this.waitForAuth;\n if (this.socket) this.socket.emit(event, ...args);\n }\n\n // ── Lifecycle ─────────────────────────────────────────────────────────\n\n disconnect(): void {\n if (this.socket) {\n this.socket.disconnect();\n SOCKET_CONNECTIONS.delete(`${this.url}:${this.version}`);\n this.socket = null;\n this.isConnected = false;\n }\n }\n\n async reconnect(): Promise<void> {\n this.loading = this.init(this.apiKey);\n await this.loading;\n }\n}\n\nexport default SocketTransport;\n","/**\n * SSETransport — HTTP + Server-Sent Events implementation of the Transport interface.\n *\n * Request/response via standard fetch(). Subscriptions via EventSource.\n * Simpler than Socket.IO — no websocket infra needed. Good for read-heavy\n * apps, serverless backends, or environments where WebSocket isn't available.\n *\n * Trade-offs vs SocketTransport:\n * - Simpler infra (no sticky sessions, works behind any CDN/proxy)\n * - Server → client streaming only (no client → server streaming)\n * - Request/response is standard HTTP (cacheable, observable, debuggable)\n * - No compression (relies on HTTP gzip)\n * - No request deduplication (relies on HTTP/2 multiplexing)\n */\n\nimport { EventEmitter } from \"eventemitter3\";\nimport type { Transport } from \"@parcae/model\";\n\nexport interface SSETransportConfig {\n /** Base URL of the Parcae backend. */\n url: string;\n /** API key or async function returning a key. */\n key?: string | null | (() => Promise<string | null>);\n /** API version prefix. Default: \"v1\" */\n version?: string;\n}\n\nexport class SSETransport extends EventEmitter implements Transport {\n private url: string;\n private version: string;\n private apiKey: string | null | (() => Promise<string | null>);\n private key: string | null = null;\n private eventSources = new Map<string, EventSource>();\n\n public isConnected = true; // HTTP is \"always connected\"\n public isLoading = true;\n public loading: Promise<void>;\n\n constructor(config: SSETransportConfig) {\n super();\n this.url = config.url.replace(/\\/$/, \"\");\n this.version = config.version ?? \"v1\";\n this.apiKey = config.key ?? null;\n this.loading = this.resolveKey();\n }\n\n private async resolveKey(): Promise<void> {\n try {\n this.key =\n typeof this.apiKey === \"function\" ? await this.apiKey() : this.apiKey;\n this.isLoading = false;\n this.emit(\"connected\");\n } catch (err) {\n this.isLoading = false;\n this.emit(\"error\", err);\n }\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (this.key) h[\"Authorization\"] = `Bearer ${this.key}`;\n return h;\n }\n\n private fullUrl(path: string): string {\n return `${this.url}/${this.version}${path}`;\n }\n\n // ── Request/Response ──────────────────────────────────────────────────\n\n private async request(\n method: string,\n path: string,\n data?: any,\n ): Promise<any> {\n await this.loading;\n\n const isGet = method.toUpperCase() === \"GET\";\n let url = this.fullUrl(path);\n\n if (isGet && data) {\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(data)) {\n params.set(k, typeof v === \"object\" ? JSON.stringify(v) : String(v));\n }\n url += `?${params.toString()}`;\n }\n\n const res = await fetch(url, {\n method: method.toUpperCase(),\n headers: this.headers(),\n body: isGet ? undefined : JSON.stringify(data),\n });\n\n if (!res.ok) {\n const body = await res.json().catch(() => ({ error: res.statusText }));\n throw new Error(body.error || body.message || `HTTP ${res.status}`);\n }\n\n const body = await res.json();\n if (body.success === false) throw new Error(body.error || \"Request failed\");\n return body.result ?? body;\n }\n\n async get(path: string, data?: any): Promise<any> {\n return this.request(\"GET\", path, data);\n }\n async post(path: string, data?: any): Promise<any> {\n return this.request(\"POST\", path, data);\n }\n async put(path: string, data?: any): Promise<any> {\n return this.request(\"PUT\", path, data);\n }\n async patch(path: string, data?: any): Promise<any> {\n return this.request(\"PATCH\", path, data);\n }\n async delete(path: string, data?: any): Promise<any> {\n return this.request(\"DELETE\", path, data);\n }\n\n // ── Subscriptions (via Server-Sent Events) ────────────────────────────\n\n subscribe(event: string, handler: (...args: any[]) => void): () => void {\n const url = `${this.url}/${this.version}/__events/${encodeURIComponent(event)}`;\n const source = new EventSource(url, { withCredentials: true });\n\n source.onmessage = (e) => {\n try {\n const data = JSON.parse(e.data);\n handler(data);\n } catch {\n handler(e.data);\n }\n };\n\n source.onerror = () => {\n // EventSource auto-reconnects\n };\n\n this.eventSources.set(event, source);\n\n return () => {\n source.close();\n this.eventSources.delete(event);\n };\n }\n\n unsubscribe(event: string): void {\n const source = this.eventSources.get(event);\n if (source) {\n source.close();\n this.eventSources.delete(event);\n }\n }\n\n // ── Control messages ──────────────────────────────────────────────────\n\n async send(event: string, ...args: any[]): Promise<void> {\n await this.request(\"POST\", \"/__control\", { event, args });\n }\n\n // ── Lifecycle ─────────────────────────────────────────────────────────\n\n disconnect(): void {\n for (const [, source] of this.eventSources) source.close();\n this.eventSources.clear();\n this.isConnected = false;\n this.emit(\"disconnected\");\n }\n\n async reconnect(): Promise<void> {\n this.loading = this.resolveKey();\n await this.loading;\n }\n}\n\nexport default SSETransport;\n","/**\n * @parcae/sdk — createClient()\n *\n * Creates a Parcae client with a pluggable transport layer.\n * Default: Socket.IO (bidirectional, realtime).\n * Alternative: SSE (HTTP + Server-Sent Events, simpler).\n *\n * The transport is abstracted — the client exposes the same API\n * regardless of which transport is used underneath.\n */\n\nimport { Model, FrontendAdapter } from \"@parcae/model\";\nimport type { Transport } from \"@parcae/model\";\nimport { SocketTransport } from \"./transports/socket\";\nimport type { SocketTransportConfig } from \"./transports/socket\";\nimport { SSETransport } from \"./transports/sse\";\nimport type { SSETransportConfig } from \"./transports/sse\";\n\n// ─── Configuration ───────────────────────────────────────────────────────────\n\nexport interface ClientConfig {\n /** URL of the Parcae backend. */\n url: string;\n /** API key — string or async function that returns a key. */\n key?: string | null | (() => Promise<string | null>);\n /** API version prefix. Default: \"v1\" */\n version?: string;\n /**\n * Transport type. Default: \"socket\"\n * - \"socket\": Socket.IO (bidirectional, realtime subscriptions)\n * - \"sse\": HTTP + Server-Sent Events (read-heavy, simpler infra)\n * - Transport instance: provide your own Transport implementation\n */\n transport?: \"socket\" | \"sse\" | Transport;\n}\n\nexport interface ParcaeClient {\n /** The underlying transport instance. */\n transport: Transport;\n /** Shorthand for transport methods. */\n get(path: string, data?: any): Promise<any>;\n post(path: string, data?: any): Promise<any>;\n put(path: string, data?: any): Promise<any>;\n patch(path: string, data?: any): Promise<any>;\n delete(path: string, data?: any): Promise<any>;\n /** Subscribe to a named event. Returns dispose function. */\n subscribe(event: string, handler: (...args: any[]) => void): () => void;\n /** Unsubscribe from a named event. */\n unsubscribe(event: string, handler?: (...args: any[]) => void): void;\n /** Send a control message. */\n send(event: string, ...args: any[]): void;\n /** Connection state. */\n readonly isConnected: boolean;\n readonly isLoading: boolean;\n /** Promise that resolves when the client is ready. */\n loading: Promise<void>;\n /** Update the auth key. */\n setKey(key: string | null | (() => Promise<string | null>)): Promise<void>;\n /** Listen for transport events. */\n on(event: string, handler: (...args: any[]) => void): void;\n off(event: string, handler?: (...args: any[]) => void): void;\n /** Disconnect from the server. */\n disconnect(): void;\n /** Reconnect. */\n reconnect(): Promise<void>;\n /** Auth version — incremented on auth changes. Useful for cache invalidation. */\n readonly authVersion: number;\n}\n\n// ─── createClient ────────────────────────────────────────────────────────────\n\nexport function createClient(config: ClientConfig): ParcaeClient {\n const version = config.version ?? \"v1\";\n\n // Create the transport\n let transport: Transport & {\n loading?: Promise<void>;\n isLoading?: boolean;\n isConnected?: boolean;\n authVersion?: number;\n setKey?: (key: any) => Promise<void>;\n on?: (event: string, handler: (...args: any[]) => void) => void;\n off?: (event: string, handler?: (...args: any[]) => void) => void;\n disconnect?: () => void;\n reconnect?: () => Promise<void>;\n };\n\n if (config.transport && typeof config.transport === \"object\") {\n // Custom transport instance\n transport = config.transport as any;\n } else if (config.transport === \"sse\") {\n transport = new SSETransport({\n url: config.url,\n key: config.key,\n version,\n });\n } else {\n // Default: Socket.IO\n transport = new SocketTransport({\n url: config.url,\n key: config.key,\n version,\n });\n }\n\n // Wire up FrontendAdapter so Model.where(), .findById() etc work\n Model.use(new FrontendAdapter(transport));\n\n const client: ParcaeClient = {\n transport,\n\n get: (path, data) => transport.get(path, data),\n post: (path, data) => transport.post(path, data),\n put: (path, data) => transport.put(path, data),\n patch: (path, data) => transport.patch(path, data),\n delete: (path, data) => transport.delete(path, data),\n\n subscribe(event, handler) {\n if (transport.subscribe) return transport.subscribe(event, handler);\n return () => {}; // no-op if transport doesn't support subscriptions\n },\n\n unsubscribe(event, handler) {\n transport.unsubscribe?.(event, handler);\n },\n\n send(event, ...args) {\n (transport as any).send?.(event, ...args);\n },\n\n get isConnected() {\n return transport.isConnected ?? false;\n },\n get isLoading() {\n return transport.isLoading ?? false;\n },\n get loading() {\n return (transport as any).loading ?? Promise.resolve();\n },\n get authVersion() {\n return (transport as any).authVersion ?? 0;\n },\n\n async setKey(key) {\n if ((transport as any).setKey) await (transport as any).setKey(key);\n },\n\n on(event, handler) {\n transport.on?.(event, handler);\n },\n off(event, handler) {\n transport.off?.(event, handler);\n },\n\n disconnect() {\n transport.disconnect?.();\n },\n async reconnect() {\n await transport.reconnect?.();\n },\n };\n\n return client;\n}\n"]}
@@ -1,63 +0,0 @@
1
- import { Transport } from '@parcae/model';
2
-
3
- /**
4
- * @parcae/sdk — createClient()
5
- *
6
- * Creates a Parcae client with a pluggable transport layer.
7
- * Default: Socket.IO (bidirectional, realtime).
8
- * Alternative: SSE (HTTP + Server-Sent Events, simpler).
9
- *
10
- * The transport is abstracted — the client exposes the same API
11
- * regardless of which transport is used underneath.
12
- */
13
-
14
- interface ClientConfig {
15
- /** URL of the Parcae backend. */
16
- url: string;
17
- /** API key — string or async function that returns a key. */
18
- key?: string | null | (() => Promise<string | null>);
19
- /** API version prefix. Default: "v1" */
20
- version?: string;
21
- /**
22
- * Transport type. Default: "socket"
23
- * - "socket": Socket.IO (bidirectional, realtime subscriptions)
24
- * - "sse": HTTP + Server-Sent Events (read-heavy, simpler infra)
25
- * - Transport instance: provide your own Transport implementation
26
- */
27
- transport?: "socket" | "sse" | Transport;
28
- }
29
- interface ParcaeClient {
30
- /** The underlying transport instance. */
31
- transport: Transport;
32
- /** Shorthand for transport methods. */
33
- get(path: string, data?: any): Promise<any>;
34
- post(path: string, data?: any): Promise<any>;
35
- put(path: string, data?: any): Promise<any>;
36
- patch(path: string, data?: any): Promise<any>;
37
- delete(path: string, data?: any): Promise<any>;
38
- /** Subscribe to a named event. Returns dispose function. */
39
- subscribe(event: string, handler: (...args: any[]) => void): () => void;
40
- /** Unsubscribe from a named event. */
41
- unsubscribe(event: string, handler?: (...args: any[]) => void): void;
42
- /** Send a control message. */
43
- send(event: string, ...args: any[]): void;
44
- /** Connection state. */
45
- readonly isConnected: boolean;
46
- readonly isLoading: boolean;
47
- /** Promise that resolves when the client is ready. */
48
- loading: Promise<void>;
49
- /** Update the auth key. */
50
- setKey(key: string | null | (() => Promise<string | null>)): Promise<void>;
51
- /** Listen for transport events. */
52
- on(event: string, handler: (...args: any[]) => void): void;
53
- off(event: string, handler?: (...args: any[]) => void): void;
54
- /** Disconnect from the server. */
55
- disconnect(): void;
56
- /** Reconnect. */
57
- reconnect(): Promise<void>;
58
- /** Auth version — incremented on auth changes. Useful for cache invalidation. */
59
- readonly authVersion: number;
60
- }
61
- declare function createClient(config: ClientConfig): ParcaeClient;
62
-
63
- export { type ClientConfig as C, type ParcaeClient as P, createClient as c };