@parcae/sdk 0.0.1

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.
@@ -0,0 +1,426 @@
1
+ import SocketIO from 'socket.io-client';
2
+ import pako from 'pako';
3
+ import { decompress } from 'compress-json';
4
+ import { EventEmitter } from 'eventemitter3';
5
+ import ShortId from 'short-unique-id';
6
+ import { Model, FrontendAdapter } from '@parcae/model';
7
+
8
+ // src/transports/socket.ts
9
+ var uid = new ShortId({ length: 10 });
10
+ var SOCKET_CONNECTIONS = /* @__PURE__ */ new Map();
11
+ var SocketTransport = class extends EventEmitter {
12
+ socket = null;
13
+ pendingHandlers = [];
14
+ apiKey;
15
+ key = null;
16
+ url;
17
+ version;
18
+ socketPath;
19
+ waitForAuth = null;
20
+ resolveAuth = null;
21
+ inflight = /* @__PURE__ */ new Map();
22
+ loading;
23
+ isLoading = true;
24
+ isConnected = false;
25
+ isConnecting = false;
26
+ authVersion = 0;
27
+ constructor(config) {
28
+ super();
29
+ this.url = config.url;
30
+ this.apiKey = config.key ?? null;
31
+ 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");
98
+ this.socket.on("connect", () => {
99
+ const wasDisconnected = !this.isConnected && hasConnected;
100
+ this.isConnected = true;
101
+ this.isConnecting = false;
102
+ hasConnected = true;
103
+ this.emit(wasDisconnected ? "reconnected" : "connected");
104
+ });
105
+ this.socket.on("disconnect", () => {
106
+ this.isConnected = false;
107
+ this.emit("disconnected");
108
+ });
109
+ this.socket.on("error", (error) => {
110
+ this.emit("error", error);
111
+ });
112
+ }
113
+ // ── Request/Response ──────────────────────────────────────────────────
114
+ async fetch(method, path, data = {}) {
115
+ await this.loading;
116
+ if (this.waitForAuth) await this.waitForAuth;
117
+ if (!this.socket) throw new Error("Socket not initialized");
118
+ const upper = method.toUpperCase();
119
+ if (upper === "GET") {
120
+ const dedupeKey = `GET:${path}:${this.authVersion}:${JSON.stringify(data)}`;
121
+ const existing = this.inflight.get(dedupeKey);
122
+ if (existing) return existing;
123
+ const req = this._doFetch(method, path, data);
124
+ this.inflight.set(dedupeKey, req);
125
+ req.finally(() => this.inflight.delete(dedupeKey));
126
+ return req;
127
+ }
128
+ return this._doFetch(method, path, data);
129
+ }
130
+ async _doFetch(method, path, data = {}) {
131
+ if (!this.isConnected) {
132
+ await new Promise((resolve, reject) => {
133
+ if (this.socket.connected) return resolve();
134
+ const timeout = setTimeout(() => {
135
+ cleanup();
136
+ reject(new Error("Connection timeout"));
137
+ }, 3e4);
138
+ const onConnect = () => {
139
+ cleanup();
140
+ resolve();
141
+ };
142
+ const onError = (err) => {
143
+ cleanup();
144
+ reject(new Error(`Connection failed: ${err.message}`));
145
+ };
146
+ const cleanup = () => {
147
+ clearTimeout(timeout);
148
+ this.socket.off("connect", onConnect);
149
+ this.socket.off("connect_error", onError);
150
+ };
151
+ this.socket.once("connect", onConnect);
152
+ this.socket.once("connect_error", onError);
153
+ });
154
+ }
155
+ const id = uid.rnd();
156
+ return new Promise((resolve, reject) => {
157
+ this.socket.once(id, (msg) => {
158
+ try {
159
+ const uncompressed = pako.ungzip(msg, { to: "string" });
160
+ const { success, result, message, error } = decompress(
161
+ JSON.parse(uncompressed)
162
+ );
163
+ if (success) resolve(result);
164
+ else
165
+ reject(
166
+ new Error(
167
+ message || error || `Request failed: ${method}:${path}`
168
+ )
169
+ );
170
+ } catch (err) {
171
+ reject(err);
172
+ }
173
+ });
174
+ this.socket.emit(
175
+ "call",
176
+ id,
177
+ method.toUpperCase(),
178
+ `/${this.version}${path}`,
179
+ data
180
+ );
181
+ });
182
+ }
183
+ async get(path, data) {
184
+ return this.fetch("get", path, data);
185
+ }
186
+ async post(path, data) {
187
+ return this.fetch("post", path, data);
188
+ }
189
+ async put(path, data) {
190
+ return this.fetch("put", path, data);
191
+ }
192
+ async patch(path, data) {
193
+ return this.fetch("patch", path, data);
194
+ }
195
+ async delete(path, data) {
196
+ return this.fetch("delete", path, data);
197
+ }
198
+ // ── Subscriptions ─────────────────────────────────────────────────────
199
+ subscribe(event, handler) {
200
+ if (this.socket) {
201
+ this.socket.on(event, handler);
202
+ } else {
203
+ this.pendingHandlers.push({ event, handler });
204
+ }
205
+ return () => this.unsubscribe(event, handler);
206
+ }
207
+ unsubscribe(event, handler) {
208
+ if (this.socket) this.socket.off(event, handler);
209
+ this.pendingHandlers = this.pendingHandlers.filter(
210
+ (h) => !(h.event === event && (!handler || h.handler === handler))
211
+ );
212
+ }
213
+ // ── Control messages ──────────────────────────────────────────────────
214
+ async send(event, ...args) {
215
+ if (!this.isConnected && this.socket) {
216
+ await new Promise((resolve) => {
217
+ if (this.socket.connected) return resolve();
218
+ this.socket.once("connect", () => resolve());
219
+ });
220
+ }
221
+ if (this.waitForAuth) await this.waitForAuth;
222
+ if (this.socket) this.socket.emit(event, ...args);
223
+ }
224
+ // ── Lifecycle ─────────────────────────────────────────────────────────
225
+ disconnect() {
226
+ if (this.socket) {
227
+ this.socket.disconnect();
228
+ SOCKET_CONNECTIONS.delete(`${this.url}:${this.version}`);
229
+ this.socket = null;
230
+ this.isConnected = false;
231
+ }
232
+ }
233
+ async reconnect() {
234
+ this.loading = this.init(this.apiKey);
235
+ await this.loading;
236
+ }
237
+ };
238
+ var SSETransport = class extends EventEmitter {
239
+ url;
240
+ version;
241
+ apiKey;
242
+ key = null;
243
+ eventSources = /* @__PURE__ */ new Map();
244
+ isConnected = true;
245
+ // HTTP is "always connected"
246
+ isLoading = true;
247
+ loading;
248
+ constructor(config) {
249
+ super();
250
+ this.url = config.url.replace(/\/$/, "");
251
+ this.version = config.version ?? "v1";
252
+ this.apiKey = config.key ?? null;
253
+ this.loading = this.resolveKey();
254
+ }
255
+ async resolveKey() {
256
+ try {
257
+ this.key = typeof this.apiKey === "function" ? await this.apiKey() : this.apiKey;
258
+ this.isLoading = false;
259
+ this.emit("connected");
260
+ } catch (err) {
261
+ this.isLoading = false;
262
+ this.emit("error", err);
263
+ }
264
+ }
265
+ headers() {
266
+ const h = { "Content-Type": "application/json" };
267
+ if (this.key) h["Authorization"] = `Bearer ${this.key}`;
268
+ return h;
269
+ }
270
+ fullUrl(path) {
271
+ return `${this.url}/${this.version}${path}`;
272
+ }
273
+ // ── Request/Response ──────────────────────────────────────────────────
274
+ async request(method, path, data) {
275
+ await this.loading;
276
+ const isGet = method.toUpperCase() === "GET";
277
+ let url = this.fullUrl(path);
278
+ if (isGet && data) {
279
+ const params = new URLSearchParams();
280
+ for (const [k, v] of Object.entries(data)) {
281
+ params.set(k, typeof v === "object" ? JSON.stringify(v) : String(v));
282
+ }
283
+ url += `?${params.toString()}`;
284
+ }
285
+ const res = await fetch(url, {
286
+ method: method.toUpperCase(),
287
+ headers: this.headers(),
288
+ body: isGet ? void 0 : JSON.stringify(data)
289
+ });
290
+ if (!res.ok) {
291
+ const body2 = await res.json().catch(() => ({ error: res.statusText }));
292
+ throw new Error(body2.error || body2.message || `HTTP ${res.status}`);
293
+ }
294
+ const body = await res.json();
295
+ if (body.success === false) throw new Error(body.error || "Request failed");
296
+ return body.result ?? body;
297
+ }
298
+ async get(path, data) {
299
+ return this.request("GET", path, data);
300
+ }
301
+ async post(path, data) {
302
+ return this.request("POST", path, data);
303
+ }
304
+ async put(path, data) {
305
+ return this.request("PUT", path, data);
306
+ }
307
+ async patch(path, data) {
308
+ return this.request("PATCH", path, data);
309
+ }
310
+ async delete(path, data) {
311
+ return this.request("DELETE", path, data);
312
+ }
313
+ // ── Subscriptions (via Server-Sent Events) ────────────────────────────
314
+ subscribe(event, handler) {
315
+ const url = `${this.url}/${this.version}/__events/${encodeURIComponent(event)}`;
316
+ const source = new EventSource(url, { withCredentials: true });
317
+ source.onmessage = (e) => {
318
+ try {
319
+ const data = JSON.parse(e.data);
320
+ handler(data);
321
+ } catch {
322
+ handler(e.data);
323
+ }
324
+ };
325
+ source.onerror = () => {
326
+ };
327
+ this.eventSources.set(event, source);
328
+ return () => {
329
+ source.close();
330
+ this.eventSources.delete(event);
331
+ };
332
+ }
333
+ unsubscribe(event) {
334
+ const source = this.eventSources.get(event);
335
+ if (source) {
336
+ source.close();
337
+ this.eventSources.delete(event);
338
+ }
339
+ }
340
+ // ── Control messages ──────────────────────────────────────────────────
341
+ async send(event, ...args) {
342
+ await this.request("POST", "/__control", { event, args });
343
+ }
344
+ // ── Lifecycle ─────────────────────────────────────────────────────────
345
+ disconnect() {
346
+ for (const [, source] of this.eventSources) source.close();
347
+ this.eventSources.clear();
348
+ this.isConnected = false;
349
+ this.emit("disconnected");
350
+ }
351
+ async reconnect() {
352
+ this.loading = this.resolveKey();
353
+ await this.loading;
354
+ }
355
+ };
356
+ function createClient(config) {
357
+ const version = config.version ?? "v1";
358
+ let transport;
359
+ if (config.transport && typeof config.transport === "object") {
360
+ transport = config.transport;
361
+ } else if (config.transport === "sse") {
362
+ transport = new SSETransport({
363
+ url: config.url,
364
+ key: config.key,
365
+ version
366
+ });
367
+ } else {
368
+ transport = new SocketTransport({
369
+ url: config.url,
370
+ key: config.key,
371
+ version
372
+ });
373
+ }
374
+ Model.use(new FrontendAdapter(transport));
375
+ const client = {
376
+ transport,
377
+ get: (path, data) => transport.get(path, data),
378
+ post: (path, data) => transport.post(path, data),
379
+ put: (path, data) => transport.put(path, data),
380
+ patch: (path, data) => transport.patch(path, data),
381
+ delete: (path, data) => transport.delete(path, data),
382
+ subscribe(event, handler) {
383
+ if (transport.subscribe) return transport.subscribe(event, handler);
384
+ return () => {
385
+ };
386
+ },
387
+ unsubscribe(event, handler) {
388
+ transport.unsubscribe?.(event, handler);
389
+ },
390
+ send(event, ...args) {
391
+ transport.send?.(event, ...args);
392
+ },
393
+ get isConnected() {
394
+ return transport.isConnected ?? false;
395
+ },
396
+ get isLoading() {
397
+ return transport.isLoading ?? false;
398
+ },
399
+ get loading() {
400
+ return transport.loading ?? Promise.resolve();
401
+ },
402
+ get authVersion() {
403
+ return transport.authVersion ?? 0;
404
+ },
405
+ async setKey(key) {
406
+ if (transport.setKey) await transport.setKey(key);
407
+ },
408
+ on(event, handler) {
409
+ transport.on?.(event, handler);
410
+ },
411
+ off(event, handler) {
412
+ transport.off?.(event, handler);
413
+ },
414
+ disconnect() {
415
+ transport.disconnect?.();
416
+ },
417
+ async reconnect() {
418
+ await transport.reconnect?.();
419
+ }
420
+ };
421
+ return client;
422
+ }
423
+
424
+ export { SSETransport, SocketTransport, createClient };
425
+ //# sourceMappingURL=chunk-XAFYMW5P.js.map
426
+ //# sourceMappingURL=chunk-XAFYMW5P.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/transports/socket.ts","../src/transports/sse.ts","../src/client.ts"],"names":["EventEmitter","body"],"mappings":";;;;;;;;AAgBA,IAAM,MAAM,IAAI,OAAA,CAAQ,EAAE,MAAA,EAAQ,IAAI,CAAA;AACtC,IAAM,kBAAA,uBAAyB,GAAA,EAAiB;AAUzC,IAAM,eAAA,GAAN,cAA8B,YAAA,CAAkC;AAAA,EAC7D,MAAA,GAAc,IAAA;AAAA,EACd,kBAGH,EAAC;AAAA,EACE,MAAA;AAAA,EACA,GAAA,GAAqB,IAAA;AAAA,EACrB,GAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA,GAAoC,IAAA;AAAA,EACpC,WAAA,GAAmC,IAAA;AAAA,EACnC,QAAA,uBAAe,GAAA,EAA0B;AAAA,EAE1C,OAAA;AAAA,EACA,SAAA,GAAY,IAAA;AAAA,EACZ,WAAA,GAAc,KAAA;AAAA,EACd,YAAA,GAAe,KAAA;AAAA,EACf,WAAA,GAAc,CAAA;AAAA,EAErB,YAAY,MAAA,EAA+B;AACzC,IAAA,KAAA,EAAM;AACN,IAAA,IAAA,CAAK,MAAM,MAAA,CAAO,GAAA;AAClB,IAAA,IAAA,CAAK,MAAA,GAAS,OAAO,GAAA,IAAO,IAAA;AAC5B,IAAA,IAAA,CAAK,OAAA,GAAU,OAAO,OAAA,IAAW,IAAA;AACjC,IAAA,IAAA,CAAK,UAAA,GAAa,OAAO,IAAA,IAAQ,KAAA;AACjC,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,MAAM,CAAA;AAAA,EACtC;AAAA;AAAA,EAIA,MAAM,OACJ,GAAA,EACe;AACf,IAAA,IAAA,CAAK,MAAA,GAAS,GAAA;AACd,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AACjB,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,IAAA,CAAK,GAAG,CAAA;AAC5B,IAAA,MAAM,IAAA,CAAK,OAAA;AAAA,EACb;AAAA;AAAA,EAIA,MAAc,KACZ,GAAA,EACe;AACf,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AACjB,IAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AAEpB,IAAA,IAAI;AACF,MAAA,IAAA,CAAK,MAAM,OAAO,GAAA,KAAQ,UAAA,GAAa,MAAM,KAAI,GAAI,GAAA;AACrD,MAAA,MAAM,YAAY,CAAA,EAAG,IAAA,CAAK,GAAG,CAAA,CAAA,EAAI,KAAK,OAAO,CAAA,CAAA;AAE7C,MAAA,IAAI,CAAC,kBAAA,CAAmB,GAAA,CAAI,SAAS,CAAA,EAAG;AACtC,QAAA,kBAAA,CAAmB,GAAA;AAAA,UACjB,SAAA;AAAA,UACA,QAAA,CAAS,KAAK,GAAA,EAAK;AAAA,YACjB,MAAM,IAAA,CAAK,UAAA;AAAA,YACX,KAAA,EAAO,IAAA;AAAA,YACP,UAAA,EAAY,CAAC,WAAW,CAAA;AAAA,YACxB,eAAA,EAAiB,IAAA;AAAA,YACjB,OAAO,EAAE,GAAA,EAAK,KAAK,GAAA,IAAO,KAAA,CAAA,EAAW,aAAa,IAAA;AAAK,WACxD;AAAA,SACH;AAAA,MACF;AAEA,MAAA,IAAA,CAAK,MAAA,GAAS,kBAAA,CAAmB,GAAA,CAAI,SAAS,CAAA;AAE9C,MAAA,IAAI,KAAK,GAAA,EAAK;AACZ,QAAA,MAAM,cAAc,IAAA,CAAK,WAAA;AACzB,QAAA,IAAA,CAAK,WAAA,GAAc,IAAI,OAAA,CAAc,CAAC,OAAA,KAAY;AAChD,UAAA,IAAA,CAAK,WAAA,GAAc,OAAA;AACnB,UAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,cAAA,EAAgB,IAAA,CAAK,KAAK,MAAM;AAC/C,YAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AACnB,YAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AACnB,YAAA,IAAA,CAAK,WAAA,EAAA;AACL,YAAA,IAAA,CAAK,KAAK,eAAe,CAAA;AACzB,YAAA,IAAI,aAAa,WAAA,EAAY;AAC7B,YAAA,OAAA,EAAQ;AAAA,UACV,CAAC,CAAA;AAAA,QACH,CAAC,CAAA;AAAA,MACH,CAAA,MAAO;AACL,QAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AACnB,QAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AAAA,MACrB;AAEA,MAAA,IAAA,CAAK,SAAA,GAAY,KAAA;AAAA,IACnB,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,SAAA,GAAY,KAAA;AACjB,MAAA,IAAA,CAAK,YAAA,GAAe,KAAA;AACpB,MAAA,IAAA,CAAK,IAAA,CAAK,SAAS,KAAK,CAAA;AACxB,MAAA,MAAM,KAAA;AAAA,IACR;AAEA,IAAA,IAAA,CAAK,WAAA,EAAY;AAAA,EACnB;AAAA,EAEQ,WAAA,GAAoB;AAC1B,IAAA,IAAI,CAAC,KAAK,MAAA,EAAQ;AAElB,IAAA,KAAA,MAAW,EAAE,KAAA,EAAO,OAAA,EAAQ,IAAK,KAAK,eAAA,EAAiB;AACrD,MAAA,IAAA,CAAK,MAAA,CAAO,EAAA,CAAG,KAAA,EAAO,OAAO,CAAA;AAAA,IAC/B;AACA,IAAA,IAAA,CAAK,kBAAkB,EAAC;AAExB,IAAA,IAAI,eAAe,IAAA,CAAK,WAAA;AACxB,IAAA,IAAA,CAAK,MAAA,CAAO,IAAI,SAAS,CAAA;AACzB,IAAA,IAAA,CAAK,MAAA,CAAO,IAAI,YAAY,CAAA;AAC5B,IAAA,IAAA,CAAK,MAAA,CAAO,IAAI,OAAO,CAAA;AAEvB,IAAA,IAAA,CAAK,MAAA,CAAO,EAAA,CAAG,SAAA,EAAW,MAAM;AAC9B,MAAA,MAAM,eAAA,GAAkB,CAAC,IAAA,CAAK,WAAA,IAAe,YAAA;AAC7C,MAAA,IAAA,CAAK,WAAA,GAAc,IAAA;AACnB,MAAA,IAAA,CAAK,YAAA,GAAe,KAAA;AACpB,MAAA,YAAA,GAAe,IAAA;AACf,MAAA,IAAA,CAAK,IAAA,CAAK,eAAA,GAAkB,aAAA,GAAgB,WAAW,CAAA;AAAA,IACzD,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,cAAc,CAAA;AAAA,IAC1B,CAAC,CAAA;AAED,IAAA,IAAA,CAAK,MAAA,CAAO,EAAA,CAAG,OAAA,EAAS,CAAC,KAAA,KAAiB;AACxC,MAAA,IAAA,CAAK,IAAA,CAAK,SAAS,KAAK,CAAA;AAAA,IAC1B,CAAC,CAAA;AAAA,EACH;AAAA;AAAA,EAIA,MAAc,KAAA,CACZ,MAAA,EACA,IAAA,EACA,IAAA,GAAY,EAAC,EACC;AACd,IAAA,MAAM,IAAA,CAAK,OAAA;AACX,IAAA,IAAI,IAAA,CAAK,WAAA,EAAa,MAAM,IAAA,CAAK,WAAA;AACjC,IAAA,IAAI,CAAC,IAAA,CAAK,MAAA,EAAQ,MAAM,IAAI,MAAM,wBAAwB,CAAA;AAE1D,IAAA,MAAM,KAAA,GAAQ,OAAO,WAAA,EAAY;AACjC,IAAA,IAAI,UAAU,KAAA,EAAO;AACnB,MAAA,MAAM,SAAA,GAAY,CAAA,IAAA,EAAO,IAAI,CAAA,CAAA,EAAI,IAAA,CAAK,WAAW,CAAA,CAAA,EAAI,IAAA,CAAK,SAAA,CAAU,IAAI,CAAC,CAAA,CAAA;AACzE,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,QAAA,CAAS,MAAA,EAAQ,MAAM,IAAI,CAAA;AAC5C,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,QAAA,CAAS,MAAA,EAAQ,IAAA,EAAM,IAAI,CAAA;AAAA,EACzC;AAAA,EAEA,MAAc,QAAA,CACZ,MAAA,EACA,IAAA,EACA,IAAA,GAAY,EAAC,EACC;AACd,IAAA,IAAI,CAAC,KAAK,WAAA,EAAa;AACrB,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,IAAI,KAAA,CAAM,CAAA,mBAAA,EAAsB,GAAA,CAAI,OAAO,EAAE,CAAC,CAAA;AAAA,QACvD,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;AAEA,IAAA,MAAM,EAAA,GAAK,IAAI,GAAA,EAAI;AAEnB,IAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,EAAS,MAAA,KAAW;AACtC,MAAA,IAAA,CAAK,MAAA,CAAO,IAAA,CAAK,EAAA,EAAI,CAAC,GAAA,KAAa;AACjC,QAAA,IAAI;AACF,UAAA,MAAM,eAAe,IAAA,CAAK,MAAA,CAAO,KAAK,EAAE,EAAA,EAAI,UAAU,CAAA;AACtD,UAAA,MAAM,EAAE,OAAA,EAAS,MAAA,EAAQ,OAAA,EAAS,OAAM,GAAI,UAAA;AAAA,YAC1C,IAAA,CAAK,MAAM,YAAY;AAAA,WACzB;AACA,UAAA,IAAI,OAAA,UAAiB,MAAM,CAAA;AAAA;AAEzB,YAAA,MAAA;AAAA,cACE,IAAI,KAAA;AAAA,gBACF,OAAA,IAAW,KAAA,IAAS,CAAA,gBAAA,EAAmB,MAAM,IAAI,IAAI,CAAA;AAAA;AACvD,aACF;AAAA,QACJ,SAAS,GAAA,EAAK;AACZ,UAAA,MAAA,CAAO,GAAG,CAAA;AAAA,QACZ;AAAA,MACF,CAAC,CAAA;AACD,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,IAAI,KAAK,MAAA,EAAQ;AACf,MAAA,IAAA,CAAK,MAAA,CAAO,EAAA,CAAG,KAAA,EAAO,OAAO,CAAA;AAAA,IAC/B,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,eAAA,CAAgB,IAAA,CAAK,EAAE,KAAA,EAAO,SAAS,CAAA;AAAA,IAC9C;AACA,IAAA,OAAO,MAAM,IAAA,CAAK,WAAA,CAAY,KAAA,EAAO,OAAO,CAAA;AAAA,EAC9C;AAAA,EAEA,WAAA,CAAY,OAAe,OAAA,EAA0C;AACnE,IAAA,IAAI,KAAK,MAAA,EAAQ,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,OAAO,OAAO,CAAA;AAC/C,IAAA,IAAA,CAAK,eAAA,GAAkB,KAAK,eAAA,CAAgB,MAAA;AAAA,MAC1C,CAAC,MAAM,EAAE,CAAA,CAAE,UAAU,KAAA,KAAU,CAAC,OAAA,IAAW,CAAA,CAAE,OAAA,KAAY,OAAA,CAAA;AAAA,KAC3D;AAAA,EACF;AAAA;AAAA,EAIA,MAAM,IAAA,CAAK,KAAA,EAAA,GAAkB,IAAA,EAA4B;AACvD,IAAA,IAAI,CAAC,IAAA,CAAK,WAAA,IAAe,IAAA,CAAK,MAAA,EAAQ;AACpC,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;AACA,IAAA,IAAI,IAAA,CAAK,WAAA,EAAa,MAAM,IAAA,CAAK,WAAA;AACjC,IAAA,IAAI,KAAK,MAAA,EAAQ,IAAA,CAAK,OAAO,IAAA,CAAK,KAAA,EAAO,GAAG,IAAI,CAAA;AAAA,EAClD;AAAA;AAAA,EAIA,UAAA,GAAmB;AACjB,IAAA,IAAI,KAAK,MAAA,EAAQ;AACf,MAAA,IAAA,CAAK,OAAO,UAAA,EAAW;AACvB,MAAA,kBAAA,CAAmB,OAAO,CAAA,EAAG,IAAA,CAAK,GAAG,CAAA,CAAA,EAAI,IAAA,CAAK,OAAO,CAAA,CAAE,CAAA;AACvD,MAAA,IAAA,CAAK,MAAA,GAAS,IAAA;AACd,MAAA,IAAA,CAAK,WAAA,GAAc,KAAA;AAAA,IACrB;AAAA,EACF;AAAA,EAEA,MAAM,SAAA,GAA2B;AAC/B,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,MAAM,CAAA;AACpC,IAAA,MAAM,IAAA,CAAK,OAAA;AAAA,EACb;AACF;ACnRO,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;ACvGO,SAAS,aAAa,MAAA,EAAoC;AAC/D,EAAA,MAAM,OAAA,GAAU,OAAO,OAAA,IAAW,IAAA;AAGlC,EAAA,IAAI,SAAA;AAYJ,EAAA,IAAI,MAAA,CAAO,SAAA,IAAa,OAAO,MAAA,CAAO,cAAc,QAAA,EAAU;AAE5D,IAAA,SAAA,GAAY,MAAA,CAAO,SAAA;AAAA,EACrB,CAAA,MAAA,IAAW,MAAA,CAAO,SAAA,KAAc,KAAA,EAAO;AACrC,IAAA,SAAA,GAAY,IAAI,YAAA,CAAa;AAAA,MAC3B,KAAK,MAAA,CAAO,GAAA;AAAA,MACZ,KAAK,MAAA,CAAO,GAAA;AAAA,MACZ;AAAA,KACD,CAAA;AAAA,EACH,CAAA,MAAO;AAEL,IAAA,SAAA,GAAY,IAAI,eAAA,CAAgB;AAAA,MAC9B,KAAK,MAAA,CAAO,GAAA;AAAA,MACZ,KAAK,MAAA,CAAO,GAAA;AAAA,MACZ;AAAA,KACD,CAAA;AAAA,EACH;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,IAEA,WAAA,CAAY,OAAO,OAAA,EAAS;AAC1B,MAAA,SAAA,CAAU,WAAA,GAAc,OAAO,OAAO,CAAA;AAAA,IACxC,CAAA;AAAA,IAEA,IAAA,CAAK,UAAU,IAAA,EAAM;AACnB,MAAC,SAAA,CAAkB,IAAA,GAAO,KAAA,EAAO,GAAG,IAAI,CAAA;AAAA,IAC1C,CAAA;AAAA,IAEA,IAAI,WAAA,GAAc;AAChB,MAAA,OAAO,UAAU,WAAA,IAAe,KAAA;AAAA,IAClC,CAAA;AAAA,IACA,IAAI,SAAA,GAAY;AACd,MAAA,OAAO,UAAU,SAAA,IAAa,KAAA;AAAA,IAChC,CAAA;AAAA,IACA,IAAI,OAAA,GAAU;AACZ,MAAA,OAAQ,SAAA,CAAkB,OAAA,IAAW,OAAA,CAAQ,OAAA,EAAQ;AAAA,IACvD,CAAA;AAAA,IACA,IAAI,WAAA,GAAc;AAChB,MAAA,OAAQ,UAAkB,WAAA,IAAe,CAAA;AAAA,IAC3C,CAAA;AAAA,IAEA,MAAM,OAAO,GAAA,EAAK;AAChB,MAAA,IAAK,SAAA,CAAkB,MAAA,EAAQ,MAAO,SAAA,CAAkB,OAAO,GAAG,CAAA;AAAA,IACpE,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-XAFYMW5P.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 this.emit(wasDisconnected ? \"reconnected\" : \"connected\");\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"]}
@@ -0,0 +1,63 @@
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 };
@@ -0,0 +1,105 @@
1
+ export { C as ClientConfig, P as ParcaeClient, c as createClient } from './client-uWUdynht.js';
2
+ import { EventEmitter } from 'eventemitter3';
3
+ import { Transport } from '@parcae/model';
4
+ export { FrontendAdapter, Model, Transport } from '@parcae/model';
5
+
6
+ /**
7
+ * SocketTransport — Socket.IO implementation of the Transport interface.
8
+ *
9
+ * Bidirectional, full-duplex. Best for apps that need realtime subscriptions
10
+ * (live query updates, collaborative editing, chat, etc.).
11
+ *
12
+ * Extracted from Dollhouse Studio's Dollhouse.ts (667 lines).
13
+ */
14
+
15
+ interface SocketTransportConfig {
16
+ url: string;
17
+ key?: string | null | (() => Promise<string | null>);
18
+ version?: string;
19
+ /** Socket.IO path. Default: "/ws" */
20
+ path?: string;
21
+ }
22
+ declare class SocketTransport extends EventEmitter implements Transport {
23
+ private socket;
24
+ private pendingHandlers;
25
+ private apiKey;
26
+ private key;
27
+ private url;
28
+ private version;
29
+ private socketPath;
30
+ private waitForAuth;
31
+ private resolveAuth;
32
+ private inflight;
33
+ loading: Promise<void>;
34
+ isLoading: boolean;
35
+ isConnected: boolean;
36
+ isConnecting: boolean;
37
+ authVersion: number;
38
+ constructor(config: SocketTransportConfig);
39
+ setKey(key: string | null | (() => Promise<string | null>)): Promise<void>;
40
+ private init;
41
+ private setupEvents;
42
+ private fetch;
43
+ private _doFetch;
44
+ get(path: string, data?: any): Promise<any>;
45
+ post(path: string, data?: any): Promise<any>;
46
+ put(path: string, data?: any): Promise<any>;
47
+ patch(path: string, data?: any): Promise<any>;
48
+ delete(path: string, data?: any): Promise<any>;
49
+ subscribe(event: string, handler: (...args: any[]) => void): () => void;
50
+ unsubscribe(event: string, handler?: (...args: any[]) => void): void;
51
+ send(event: string, ...args: any[]): Promise<void>;
52
+ disconnect(): void;
53
+ reconnect(): Promise<void>;
54
+ }
55
+
56
+ /**
57
+ * SSETransport — HTTP + Server-Sent Events implementation of the Transport interface.
58
+ *
59
+ * Request/response via standard fetch(). Subscriptions via EventSource.
60
+ * Simpler than Socket.IO — no websocket infra needed. Good for read-heavy
61
+ * apps, serverless backends, or environments where WebSocket isn't available.
62
+ *
63
+ * Trade-offs vs SocketTransport:
64
+ * - Simpler infra (no sticky sessions, works behind any CDN/proxy)
65
+ * - Server → client streaming only (no client → server streaming)
66
+ * - Request/response is standard HTTP (cacheable, observable, debuggable)
67
+ * - No compression (relies on HTTP gzip)
68
+ * - No request deduplication (relies on HTTP/2 multiplexing)
69
+ */
70
+
71
+ interface SSETransportConfig {
72
+ /** Base URL of the Parcae backend. */
73
+ url: string;
74
+ /** API key or async function returning a key. */
75
+ key?: string | null | (() => Promise<string | null>);
76
+ /** API version prefix. Default: "v1" */
77
+ version?: string;
78
+ }
79
+ declare class SSETransport extends EventEmitter implements Transport {
80
+ private url;
81
+ private version;
82
+ private apiKey;
83
+ private key;
84
+ private eventSources;
85
+ isConnected: boolean;
86
+ isLoading: boolean;
87
+ loading: Promise<void>;
88
+ constructor(config: SSETransportConfig);
89
+ private resolveKey;
90
+ private headers;
91
+ private fullUrl;
92
+ private request;
93
+ get(path: string, data?: any): Promise<any>;
94
+ post(path: string, data?: any): Promise<any>;
95
+ put(path: string, data?: any): Promise<any>;
96
+ patch(path: string, data?: any): Promise<any>;
97
+ delete(path: string, data?: any): Promise<any>;
98
+ subscribe(event: string, handler: (...args: any[]) => void): () => void;
99
+ unsubscribe(event: string): void;
100
+ send(event: string, ...args: any[]): Promise<void>;
101
+ disconnect(): void;
102
+ reconnect(): Promise<void>;
103
+ }
104
+
105
+ export { SSETransport, type SSETransportConfig, SocketTransport, type SocketTransportConfig };
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { SSETransport, SocketTransport, createClient } from './chunk-XAFYMW5P.js';
2
+ export { FrontendAdapter, Model } from '@parcae/model';
3
+ //# sourceMappingURL=index.js.map
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js","sourcesContent":[]}
@@ -0,0 +1,83 @@
1
+ import * as React from 'react';
2
+ import React__default from 'react';
3
+ import { P as ParcaeClient, C as ClientConfig } from '../client-uWUdynht.js';
4
+ import '@parcae/model';
5
+
6
+ interface ParcaeProviderProps {
7
+ /** Pre-created client instance. If provided, url/key/transport are ignored. */
8
+ client?: ParcaeClient;
9
+ /** API base URL (required if no client provided). */
10
+ url?: string;
11
+ /** Bearer token or null (pre-auth). */
12
+ apiKey?: string | null;
13
+ /** Stable user ID — triggers re-auth when it changes. */
14
+ userId?: string | null;
15
+ /** API version. Default: "v1" */
16
+ version?: string;
17
+ /** Transport type. Default: "socket" */
18
+ transport?: ClientConfig["transport"];
19
+ children: React__default.ReactNode;
20
+ onReady?: (client: ParcaeClient) => void;
21
+ onError?: (error: Error) => void;
22
+ }
23
+ /**
24
+ * ParcaeProvider — creates the SDK client once and re-authenticates on userId change.
25
+ *
26
+ * Usage with pre-created client:
27
+ * ```tsx
28
+ * const client = createClient({ url: "...", transport: "socket" });
29
+ * <ParcaeProvider client={client}><App /></ParcaeProvider>
30
+ * ```
31
+ *
32
+ * Usage with inline config:
33
+ * ```tsx
34
+ * <ParcaeProvider url="http://localhost:3000" apiKey={token} userId={user.id}>
35
+ * <App />
36
+ * </ParcaeProvider>
37
+ * ```
38
+ */
39
+ declare const ParcaeProvider: React__default.FC<ParcaeProviderProps>;
40
+
41
+ declare const ParcaeContext: React.Context<ParcaeClient | null>;
42
+ declare function useParcae(): ParcaeClient;
43
+
44
+ interface QueryChain<T> {
45
+ find(): Promise<T[]>;
46
+ __steps?: any[];
47
+ __modelType?: string;
48
+ __modelClass?: any;
49
+ __adapter?: any;
50
+ __debounceMs?: number;
51
+ }
52
+ interface UseQueryResult<T> {
53
+ items: T[];
54
+ loading: boolean;
55
+ error: Error | null;
56
+ refetch: () => void;
57
+ }
58
+ declare function useQuery<T>(chain: QueryChain<T> | null | undefined): UseQueryResult<T>;
59
+
60
+ declare function useApi(): {
61
+ get: (path: string, data?: any) => Promise<any>;
62
+ post: (path: string, data?: any) => Promise<any>;
63
+ put: (path: string, data?: any) => Promise<any>;
64
+ patch: (path: string, data?: any) => Promise<any>;
65
+ delete: (path: string, data?: any) => Promise<any>;
66
+ };
67
+ /**
68
+ * useSDK — raw client instance.
69
+ */
70
+ declare function useSDK(): ParcaeClient;
71
+ /**
72
+ * useConnectionStatus — reactive connection state.
73
+ */
74
+ declare function useConnectionStatus(): {
75
+ isConnected: boolean;
76
+ isLoading: boolean;
77
+ };
78
+
79
+ declare function useSetting<T = string>(key: string, defaultValue: T): [T, (value: T) => Promise<void>, {
80
+ isLoading: boolean;
81
+ }];
82
+
83
+ export { ParcaeContext, ParcaeProvider, type ParcaeProviderProps, useApi, useConnectionStatus, useParcae, useQuery, useSDK, useSetting };
@@ -0,0 +1,260 @@
1
+ import { createClient } from '../chunk-XAFYMW5P.js';
2
+ import { createContext, useContext, useMemo, useRef, useEffect, useCallback, useSyncExternalStore, useState } from 'react';
3
+ import { jsx } from 'react/jsx-runtime';
4
+
5
+ var ParcaeContext = createContext(null);
6
+ function useParcae() {
7
+ const client = useContext(ParcaeContext);
8
+ if (!client) {
9
+ throw new Error("useParcae must be used within a <ParcaeProvider>");
10
+ }
11
+ return client;
12
+ }
13
+ var ParcaeProvider = ({
14
+ client: externalClient,
15
+ url,
16
+ apiKey,
17
+ userId,
18
+ version = "v1",
19
+ transport = "socket",
20
+ children,
21
+ onReady,
22
+ onError
23
+ }) => {
24
+ const client = useMemo(() => {
25
+ if (externalClient) return externalClient;
26
+ if (!url)
27
+ throw new Error(
28
+ "ParcaeProvider requires either a `client` prop or a `url` prop"
29
+ );
30
+ return createClient({ url, version, transport, key: null });
31
+ }, [externalClient, url, version, transport]);
32
+ const apiKeyRef = useRef(apiKey);
33
+ apiKeyRef.current = apiKey;
34
+ const onReadyRef = useRef(onReady);
35
+ onReadyRef.current = onReady;
36
+ const onErrorRef = useRef(onError);
37
+ onErrorRef.current = onError;
38
+ useEffect(() => {
39
+ client.setKey(apiKeyRef.current ?? null).then(() => onReadyRef.current?.(client)).catch((err) => onErrorRef.current?.(err));
40
+ }, [userId, client]);
41
+ useEffect(() => {
42
+ const onErr = (err) => onErrorRef.current?.(err);
43
+ client.on("error", onErr);
44
+ return () => {
45
+ client.off("error", onErr);
46
+ };
47
+ }, [client]);
48
+ return /* @__PURE__ */ jsx(ParcaeContext.Provider, { value: client, children });
49
+ };
50
+ var CACHE_TIMEOUT_MS = 6e4;
51
+ var DEFAULT_DEBOUNCE_MS = 100;
52
+ var queryCache = /* @__PURE__ */ new Map();
53
+ function buildCacheKey(chain, authVersion) {
54
+ const type = chain.__modelType ?? "unknown";
55
+ const steps = JSON.stringify(chain.__steps ?? []);
56
+ return `${type}:${authVersion}:${steps}`;
57
+ }
58
+ function useQuery(chain) {
59
+ const client = useParcae();
60
+ const authVersion = client.authVersion;
61
+ const cacheKey = chain ? buildCacheKey(chain, authVersion) : "__null__";
62
+ if (!queryCache.has(cacheKey) && chain) {
63
+ queryCache.set(cacheKey, {
64
+ items: [],
65
+ itemMap: /* @__PURE__ */ new Map(),
66
+ loading: true,
67
+ error: null,
68
+ refCount: 0,
69
+ timeoutHandle: null,
70
+ version: 0,
71
+ stateVersion: 0,
72
+ listeners: /* @__PURE__ */ new Set(),
73
+ subscriptionHash: null,
74
+ pendingOps: [],
75
+ debounceTimer: null,
76
+ debounceMs: chain.__debounceMs ?? DEFAULT_DEBOUNCE_MS,
77
+ disposeSubscription: null
78
+ });
79
+ }
80
+ const entry = queryCache.get(cacheKey);
81
+ const subscribe = useCallback(
82
+ (listener) => {
83
+ if (!entry) return () => {
84
+ };
85
+ entry.listeners.add(listener);
86
+ entry.refCount++;
87
+ if (entry.timeoutHandle) {
88
+ clearTimeout(entry.timeoutHandle);
89
+ entry.timeoutHandle = null;
90
+ }
91
+ return () => {
92
+ entry.listeners.delete(listener);
93
+ entry.refCount--;
94
+ if (entry.refCount <= 0) {
95
+ entry.timeoutHandle = setTimeout(() => {
96
+ entry.disposeSubscription?.();
97
+ queryCache.delete(cacheKey);
98
+ }, CACHE_TIMEOUT_MS);
99
+ }
100
+ };
101
+ },
102
+ [entry, cacheKey]
103
+ );
104
+ const getSnapshot = useCallback(() => entry?.stateVersion ?? 0, [entry]);
105
+ useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
106
+ const refetch = useCallback(() => {
107
+ if (!chain || !entry) return;
108
+ entry.loading = true;
109
+ notifyListeners(entry);
110
+ chain.find().then((items) => {
111
+ entry.items = items;
112
+ entry.itemMap = new Map(items.map((item) => [item.id, item]));
113
+ entry.loading = false;
114
+ entry.error = null;
115
+ entry.version++;
116
+ notifyListeners(entry);
117
+ }).catch((err) => {
118
+ entry.error = err;
119
+ entry.loading = false;
120
+ notifyListeners(entry);
121
+ });
122
+ if (!entry.disposeSubscription && chain.__modelType) {
123
+ const subEvent = `query:${cacheKey}`;
124
+ client.send("subscribe:query", {
125
+ hash: cacheKey,
126
+ modelType: chain.__modelType,
127
+ steps: chain.__steps ?? []
128
+ });
129
+ const dispose = client.subscribe(subEvent, (ops) => {
130
+ if (!entry) return;
131
+ entry.pendingOps.push(...ops);
132
+ if (entry.debounceTimer) clearTimeout(entry.debounceTimer);
133
+ entry.debounceTimer = setTimeout(() => {
134
+ applyDiffOps(entry, chain);
135
+ entry.debounceTimer = null;
136
+ }, entry.debounceMs);
137
+ });
138
+ entry.subscriptionHash = cacheKey;
139
+ entry.disposeSubscription = () => {
140
+ dispose();
141
+ client.send("unsubscribe:query", { hash: cacheKey });
142
+ };
143
+ }
144
+ }, [chain, entry, cacheKey, client]);
145
+ useEffect(() => {
146
+ if (chain) refetch();
147
+ }, [cacheKey]);
148
+ if (!entry) {
149
+ return { items: [], loading: false, error: null, refetch: () => {
150
+ } };
151
+ }
152
+ return {
153
+ items: entry.items,
154
+ loading: entry.loading,
155
+ error: entry.error,
156
+ refetch
157
+ };
158
+ }
159
+ function notifyListeners(entry) {
160
+ entry.stateVersion++;
161
+ for (const listener of entry.listeners) listener();
162
+ }
163
+ function applyDiffOps(entry, chain) {
164
+ const ops = entry.pendingOps.splice(0);
165
+ if (!ops.length) return;
166
+ const ModelClass = chain.__modelClass;
167
+ const adapter = chain.__adapter;
168
+ let changed = false;
169
+ for (const op of ops) {
170
+ switch (op.op) {
171
+ case "add": {
172
+ if (!entry.itemMap.has(op.id) && op.data && ModelClass && adapter) {
173
+ const instance = new ModelClass(adapter, op.data);
174
+ entry.items.push(instance);
175
+ entry.itemMap.set(op.id, instance);
176
+ changed = true;
177
+ }
178
+ break;
179
+ }
180
+ case "remove": {
181
+ if (entry.itemMap.has(op.id)) {
182
+ entry.items = entry.items.filter((item) => item.id !== op.id);
183
+ entry.itemMap.delete(op.id);
184
+ changed = true;
185
+ }
186
+ break;
187
+ }
188
+ case "update": {
189
+ const existing = entry.itemMap.get(op.id);
190
+ if (existing && op.data) {
191
+ for (const [key, value] of Object.entries(op.data)) {
192
+ existing.__data[key] = value;
193
+ }
194
+ changed = true;
195
+ }
196
+ break;
197
+ }
198
+ }
199
+ }
200
+ if (changed) {
201
+ entry.version++;
202
+ notifyListeners(entry);
203
+ }
204
+ }
205
+ function useApi() {
206
+ const client = useParcae();
207
+ return useMemo(
208
+ () => ({
209
+ get: client.get.bind(client),
210
+ post: client.post.bind(client),
211
+ put: client.put.bind(client),
212
+ patch: client.patch.bind(client),
213
+ delete: client.delete.bind(client)
214
+ }),
215
+ [client]
216
+ );
217
+ }
218
+ function useSDK() {
219
+ return useParcae();
220
+ }
221
+ function useConnectionStatus() {
222
+ const client = useParcae();
223
+ return {
224
+ isConnected: client.isConnected,
225
+ isLoading: client.isLoading
226
+ };
227
+ }
228
+ function useSetting(key, defaultValue) {
229
+ const client = useParcae();
230
+ const [value, setValue] = useState(defaultValue);
231
+ const [isLoading, setIsLoading] = useState(true);
232
+ useEffect(() => {
233
+ let cancelled = false;
234
+ client.get(`/settings/${encodeURIComponent(key)}`).then((result) => {
235
+ if (!cancelled && result?.value !== void 0) {
236
+ setValue(result.value);
237
+ }
238
+ }).catch(() => {
239
+ }).finally(() => {
240
+ if (!cancelled) setIsLoading(false);
241
+ });
242
+ return () => {
243
+ cancelled = true;
244
+ };
245
+ }, [key, client]);
246
+ const update = useCallback(
247
+ async (newValue) => {
248
+ setValue(newValue);
249
+ await client.put(`/settings/${encodeURIComponent(key)}`, {
250
+ value: newValue
251
+ });
252
+ },
253
+ [key, client]
254
+ );
255
+ return [value, update, { isLoading }];
256
+ }
257
+
258
+ export { ParcaeContext, ParcaeProvider, useApi, useConnectionStatus, useParcae, useQuery, useSDK, useSetting };
259
+ //# sourceMappingURL=index.js.map
260
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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":["useEffect","useMemo","useCallback"],"mappings":"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","file":"index.js","sourcesContent":["import { createContext, useContext } from \"react\";\nimport type { ParcaeClient } from \"../client\";\n\nexport const ParcaeContext = createContext<ParcaeClient | null>(null);\n\nexport function useParcae(): ParcaeClient {\n const client = useContext(ParcaeContext);\n if (!client) {\n throw new Error(\"useParcae must be used within a <ParcaeProvider>\");\n }\n return client;\n}\n","\"use client\";\n\nimport React, { useEffect, useMemo, useRef } from \"react\";\nimport { createClient } from \"../client\";\nimport type { ParcaeClient, ClientConfig } from \"../client\";\nimport { ParcaeContext } 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 or null (pre-auth). */\n apiKey?: string | null;\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\n/**\n * ParcaeProvider — creates the SDK client once and re-authenticates on userId change.\n *\n * Usage with pre-created client:\n * ```tsx\n * const client = createClient({ url: \"...\", transport: \"socket\" });\n * <ParcaeProvider client={client}><App /></ParcaeProvider>\n * ```\n *\n * Usage with inline config:\n * ```tsx\n * <ParcaeProvider url=\"http://localhost:3000\" apiKey={token} userId={user.id}>\n * <App />\n * </ParcaeProvider>\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 // Create client once per url+version+transport (or use external)\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 // Refs for callbacks to avoid re-running effects on unstable inline functions\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 // Re-authenticate when userId changes\n useEffect(() => {\n client\n .setKey(apiKeyRef.current ?? null)\n .then(() => onReadyRef.current?.(client))\n .catch((err) => onErrorRef.current?.(err));\n }, [userId, client]);\n\n // Forward transport 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 return (\n <ParcaeContext.Provider value={client}>{children}</ParcaeContext.Provider>\n );\n};\n\nexport default ParcaeProvider;\n","\"use client\";\n\n/**\n * useQuery — reactive data fetching with realtime subscriptions.\n *\n * Takes a query chain, returns an array of typed model instances.\n * Subscribes to realtime updates — the server diffs queries on model changes\n * and pushes surgical add/remove/update ops.\n *\n * @example\n * ```tsx\n * const { items, loading } = useQuery(Post.where({ published: true }));\n * ```\n */\n\nimport { useCallback, useEffect, useSyncExternalStore } from \"react\";\nimport { useParcae } from \"./context\";\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\ninterface QueryChain<T> {\n find(): Promise<T[]>;\n __steps?: any[];\n __modelType?: string;\n __modelClass?: any;\n __adapter?: any;\n __debounceMs?: number;\n}\n\ninterface UseQueryResult<T> {\n items: T[];\n loading: boolean;\n error: Error | null;\n refetch: () => void;\n}\n\ninterface DiffOp {\n op: \"add\" | \"remove\" | \"update\";\n id: string;\n data?: Record<string, any>;\n}\n\n// ─── Query Cache ─────────────────────────────────────────────────────────────\n\nconst CACHE_TIMEOUT_MS = 60_000;\nconst DEFAULT_DEBOUNCE_MS = 100;\n\ninterface CacheEntry<T = any> {\n items: T[];\n itemMap: Map<string, T>;\n loading: boolean;\n error: Error | null;\n refCount: number;\n timeoutHandle: ReturnType<typeof setTimeout> | null;\n version: number;\n stateVersion: number;\n listeners: Set<() => void>;\n subscriptionHash: string | null;\n pendingOps: DiffOp[];\n debounceTimer: ReturnType<typeof setTimeout> | null;\n debounceMs: number;\n disposeSubscription: (() => void) | null;\n}\n\nconst queryCache = new Map<string, CacheEntry>();\n\nfunction buildCacheKey(chain: QueryChain<any>, authVersion: number): string {\n const type = chain.__modelType ?? \"unknown\";\n const steps = JSON.stringify(chain.__steps ?? []);\n return `${type}:${authVersion}:${steps}`;\n}\n\n// ─── useQuery ────────────────────────────────────────────────────────────────\n\nexport function useQuery<T>(\n chain: QueryChain<T> | null | undefined,\n): UseQueryResult<T> {\n const client = useParcae();\n const authVersion = client.authVersion;\n\n const cacheKey = chain ? buildCacheKey(chain, authVersion) : \"__null__\";\n\n // ── Get or create cache entry ──────────────────────────────────────\n\n if (!queryCache.has(cacheKey) && chain) {\n queryCache.set(cacheKey, {\n items: [],\n itemMap: new Map(),\n loading: true,\n error: null,\n refCount: 0,\n timeoutHandle: null,\n version: 0,\n stateVersion: 0,\n listeners: new Set(),\n subscriptionHash: null,\n pendingOps: [],\n debounceTimer: null,\n debounceMs: chain.__debounceMs ?? DEFAULT_DEBOUNCE_MS,\n disposeSubscription: null,\n });\n }\n\n const entry = queryCache.get(cacheKey);\n\n // ── useSyncExternalStore for tear-safe rendering ───────────────────\n\n const subscribe = useCallback(\n (listener: () => void) => {\n if (!entry) return () => {};\n entry.listeners.add(listener);\n entry.refCount++;\n\n // Cancel pending GC\n if (entry.timeoutHandle) {\n clearTimeout(entry.timeoutHandle);\n entry.timeoutHandle = null;\n }\n\n return () => {\n entry.listeners.delete(listener);\n entry.refCount--;\n\n // GC: if no subscribers left, schedule cleanup\n if (entry.refCount <= 0) {\n entry.timeoutHandle = setTimeout(() => {\n entry.disposeSubscription?.();\n queryCache.delete(cacheKey);\n }, CACHE_TIMEOUT_MS);\n }\n };\n },\n [entry, cacheKey],\n );\n\n const getSnapshot = useCallback(() => entry?.stateVersion ?? 0, [entry]);\n\n useSyncExternalStore(subscribe, getSnapshot, getSnapshot);\n\n // ── Fetch + subscribe ──────────────────────────────────────────────\n\n const refetch = useCallback(() => {\n if (!chain || !entry) return;\n\n entry.loading = true;\n notifyListeners(entry);\n\n chain\n .find()\n .then((items) => {\n entry.items = items;\n entry.itemMap = new Map(items.map((item: any) => [item.id, item]));\n entry.loading = false;\n entry.error = null;\n entry.version++;\n notifyListeners(entry);\n })\n .catch((err) => {\n entry.error = err;\n entry.loading = false;\n notifyListeners(entry);\n });\n\n // Set up realtime subscription if transport supports it\n if (!entry.disposeSubscription && chain.__modelType) {\n const subEvent = `query:${cacheKey}`;\n\n // Ask server to subscribe to this query\n client.send(\"subscribe:query\", {\n hash: cacheKey,\n modelType: chain.__modelType,\n steps: chain.__steps ?? [],\n });\n\n // Listen for diff ops from the server\n const dispose = client.subscribe(subEvent, (ops: DiffOp[]) => {\n if (!entry) return;\n entry.pendingOps.push(...ops);\n\n // Debounce application of ops\n if (entry.debounceTimer) clearTimeout(entry.debounceTimer);\n entry.debounceTimer = setTimeout(() => {\n applyDiffOps(entry, chain);\n entry.debounceTimer = null;\n }, entry.debounceMs);\n });\n\n entry.subscriptionHash = cacheKey;\n entry.disposeSubscription = () => {\n dispose();\n client.send(\"unsubscribe:query\", { hash: cacheKey });\n };\n }\n }, [chain, entry, cacheKey, client]);\n\n // Initial fetch on mount / chain change\n useEffect(() => {\n if (chain) refetch();\n }, [cacheKey]); // eslint-disable-line react-hooks/exhaustive-deps\n\n if (!entry) {\n return { items: [], loading: false, error: null, refetch: () => {} };\n }\n\n return {\n items: entry.items,\n loading: entry.loading,\n error: entry.error,\n refetch,\n };\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction notifyListeners(entry: CacheEntry): void {\n entry.stateVersion++;\n for (const listener of entry.listeners) listener();\n}\n\nfunction applyDiffOps<T>(entry: CacheEntry<T>, chain: QueryChain<T>): void {\n const ops = entry.pendingOps.splice(0);\n if (!ops.length) return;\n\n const ModelClass = chain.__modelClass;\n const adapter = chain.__adapter;\n let changed = false;\n\n for (const op of ops) {\n switch (op.op) {\n case \"add\": {\n if (!entry.itemMap.has(op.id) && op.data && ModelClass && adapter) {\n const instance = new ModelClass(adapter, op.data);\n entry.items.push(instance);\n entry.itemMap.set(op.id, instance);\n changed = true;\n }\n break;\n }\n case \"remove\": {\n if (entry.itemMap.has(op.id)) {\n entry.items = entry.items.filter((item: any) => item.id !== op.id);\n entry.itemMap.delete(op.id);\n changed = true;\n }\n break;\n }\n case \"update\": {\n const existing = entry.itemMap.get(op.id) as any;\n if (existing && op.data) {\n for (const [key, value] of Object.entries(op.data)) {\n existing.__data[key] = value;\n }\n changed = true;\n }\n break;\n }\n }\n }\n\n if (changed) {\n entry.version++;\n notifyListeners(entry);\n }\n}\n","\"use client\";\n\n/**\n * useApi — pre-bound HTTP methods from the Parcae client.\n *\n * @example\n * ```tsx\n * const { get, post } = useApi();\n * const data = await get(\"/custom-endpoint\");\n * ```\n */\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();\n}\n\n/**\n * useConnectionStatus — reactive connection state.\n */\nexport function useConnectionStatus() {\n const client = useParcae();\n // This is a snapshot — for true reactivity, components should\n // listen to client.on(\"connected\"/\"disconnected\") events.\n return {\n isConnected: client.isConnected,\n isLoading: client.isLoading,\n };\n}\n","\"use client\";\n\n/**\n * useSetting — key-value user settings stored as a Setting model.\n *\n * @example\n * ```tsx\n * const [theme, setTheme, { isLoading }] = useSetting(\"theme\", \"light\");\n * ```\n */\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 = useParcae();\n const [value, setValue] = useState<T>(defaultValue);\n const [isLoading, setIsLoading] = useState(true);\n\n useEffect(() => {\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 // Setting doesn't exist yet — use default\n })\n .finally(() => {\n if (!cancelled) setIsLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, [key, client]);\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 ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@parcae/sdk",
3
+ "version": "0.0.1",
4
+ "description": "Parcae SDK — client transport and React hooks for Parcae backends",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./react": {
15
+ "types": "./dist/react/index.d.ts",
16
+ "import": "./dist/react/index.js"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsup",
24
+ "dev": "tsup --watch",
25
+ "typecheck": "tsc --noEmit",
26
+ "clean": "rm -rf dist"
27
+ },
28
+ "dependencies": {
29
+ "@parcae/model": "^0.0.1",
30
+ "compress-json": "^3.1.0",
31
+ "eventemitter3": "^5.0.1",
32
+ "pako": "^2.1.0",
33
+ "short-unique-id": "^5.2.0",
34
+ "socket.io-client": "^4.8.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/pako": "^2.0.3",
38
+ "@types/react": "^19.0.0",
39
+ "react": "^19.0.0",
40
+ "tsup": "^8.5.0",
41
+ "typescript": "^5.8.0"
42
+ },
43
+ "peerDependencies": {
44
+ "react": ">=18.0.0"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "react": {
48
+ "optional": true
49
+ }
50
+ },
51
+ "license": "MIT"
52
+ }