@sayrio/public 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -38,7 +38,7 @@ interface Organization {
38
38
  createdAt: string;
39
39
  updatedAt: string;
40
40
  members: OrganizationMember[];
41
- wsUrl: string;
41
+ eventsUrl: string;
42
42
  }
43
43
  interface Label {
44
44
  id: string;
@@ -117,7 +117,6 @@ type Hooks = {
117
117
  };
118
118
  declare function setToken(token?: string): void;
119
119
  declare function setHeaders(headers?: Record<string, string>): void;
120
- declare function setFetch(fn: typeof fetch): void;
121
120
  declare function setBaseUrl(url: string): void;
122
121
  declare function setHooks(h: Hooks): void;
123
122
  declare function resetClient(): void;
@@ -169,14 +168,14 @@ declare const v1: {
169
168
  };
170
169
  };
171
170
 
172
- type WSMessageType = "CONNECTION_STATUS" | "SUBSCRIBED" | "ERROR" | "PING" | "PONG" | "UPDATE_ORG" | "CREATE_TASK" | "UPDATE_TASK" | "UPDATE_TASK_COMMENTS" | "UPDATE_TASK_VOTE" | "UPDATE_LABELS" | "UPDATE_VIEWS" | "UPDATE_CATEGORIES" | "UPDATE_ISSUE_TEMPLATES" | "DISCONNECTED";
171
+ type ServerEventMessageType = "CONNECTION_STATUS" | "ERROR" | "UPDATE_ORG" | "CREATE_TASK" | "UPDATE_TASK" | "UPDATE_TASK_COMMENTS" | "UPDATE_TASK_VOTE" | "UPDATE_LABELS" | "UPDATE_VIEWS" | "UPDATE_CATEGORIES" | "UPDATE_ISSUE_TEMPLATES";
173
172
  /**
174
173
  * String enum replacement for WS event names.
175
174
  * Use this instead of raw strings.
176
175
  */
177
- declare const WS_EVENTS: Record<WSMessageType, WSMessageType>;
178
- interface WSMessage<T = unknown> {
179
- type: WSMessageType;
176
+ declare const ServerEvent_EVENTS: Record<ServerEventMessageType, ServerEventMessageType>;
177
+ interface ServerEventMessage<T = unknown> {
178
+ type: ServerEventMessageType;
180
179
  scope: "PUBLIC";
181
180
  data: T;
182
181
  meta?: {
@@ -184,8 +183,8 @@ interface WSMessage<T = unknown> {
184
183
  };
185
184
  }
186
185
 
187
- type Handlers = Partial<Record<WSMessageType, (data: any, msg: WSMessage) => void>>;
188
- declare function ws(url: string, handlers?: Handlers): {
186
+ type Handlers = Partial<Record<ServerEventMessageType, (data: any, msg: ServerEventMessage) => void>>;
187
+ declare function sse(url: string, handlers?: Handlers): {
189
188
  close(): void;
190
189
  };
191
190
 
@@ -225,11 +224,11 @@ declare const SayrV1: {
225
224
  /**
226
225
  * Create a WebSocket connection for public real‑time updates.
227
226
  */
228
- declare const SayrWS: typeof ws;
227
+ declare const SayrSSE: typeof sse;
229
228
  /**
230
229
  * Typed WebSocket event constants.
231
230
  */
232
- declare const SayrWSEvents: Record<WSMessageType, WSMessageType>;
231
+ declare const SayrServerEvents: Record<ServerEventMessageType, ServerEventMessageType>;
233
232
  /**
234
233
  * Global client configuration helpers.
235
234
  */
@@ -239,7 +238,6 @@ declare const SayrClient: {
239
238
  setBaseUrl: typeof setBaseUrl;
240
239
  resetClient: typeof resetClient;
241
240
  setHooks: typeof setHooks;
242
- setFetch: typeof setFetch;
243
241
  };
244
242
  /**
245
243
  * Sayr Public SDK.
@@ -267,11 +265,11 @@ declare const Sayr: {
267
265
  /**
268
266
  * WebSocket helper for real‑time updates.
269
267
  */
270
- ws: typeof ws;
268
+ sse: typeof sse;
271
269
  /**
272
270
  * WebSocket event constants.
273
271
  */
274
- WS_EVENTS: typeof WS_EVENTS;
272
+ EVENTS: typeof ServerEvent_EVENTS;
275
273
  };
276
274
 
277
- export { type ApiError, type ApiSuccess, type Category, type Comment, type CommentReaction, type CommentUser, type Label, type Order, type OrderedPaginationParams, type Organization, type OrganizationMember, type OrganizationUser, type Pagination, type PaginationParams, SayrClient, SayrV1, SayrWS, SayrWSEvents, type Task, type TaskPriority, type TaskStatus, type WSMessage, type WSMessageType, WS_EVENTS, buildPaginationParams, Sayr as default };
275
+ export { type ApiError, type ApiSuccess, type Category, type Comment, type CommentReaction, type CommentUser, type Label, type Order, type OrderedPaginationParams, type Organization, type OrganizationMember, type OrganizationUser, type Pagination, type PaginationParams, SayrClient, SayrSSE, SayrServerEvents, SayrV1, type ServerEventMessage, type ServerEventMessageType, ServerEvent_EVENTS, type Task, type TaskPriority, type TaskStatus, buildPaginationParams, Sayr as default };
package/dist/index.js CHANGED
@@ -1,7 +1,6 @@
1
1
  // src/client/index.ts
2
2
  var DEFAULT_API = "https://api.sayr.io";
3
3
  var config = {
4
- fetch: globalThis.fetch,
5
4
  baseUrl: DEFAULT_API
6
5
  };
7
6
  var hooks = {};
@@ -14,9 +13,6 @@ function setHeaders(headers) {
14
13
  ...headers
15
14
  };
16
15
  }
17
- function setFetch(fn) {
18
- config.fetch = fn;
19
- }
20
16
  function setBaseUrl(url) {
21
17
  config.baseUrl = url.replace(/\/$/, "");
22
18
  }
@@ -46,7 +42,7 @@ async function request(path, opts = {}) {
46
42
  if (method === "GET" && opts.body !== void 0) {
47
43
  throw new Error("GET request cannot have a body");
48
44
  }
49
- res = await config.fetch(url, {
45
+ res = await fetch(url, {
50
46
  method,
51
47
  headers: {
52
48
  ...config.token ? { Authorization: `Bearer ${config.token}` } : {},
@@ -336,72 +332,62 @@ var v1 = {
336
332
  };
337
333
  var v1_default = v1;
338
334
 
339
- // src/ws/types.ts
340
- var WS_EVENTS = {
341
- CONNECTION_STATUS: "CONNECTION_STATUS",
342
- SUBSCRIBED: "SUBSCRIBED",
343
- ERROR: "ERROR",
344
- PING: "PING",
345
- PONG: "PONG",
346
- UPDATE_ORG: "UPDATE_ORG",
347
- CREATE_TASK: "CREATE_TASK",
348
- UPDATE_TASK: "UPDATE_TASK",
349
- UPDATE_TASK_COMMENTS: "UPDATE_TASK_COMMENTS",
350
- UPDATE_TASK_VOTE: "UPDATE_TASK_VOTE",
351
- UPDATE_LABELS: "UPDATE_LABELS",
352
- UPDATE_VIEWS: "UPDATE_VIEWS",
353
- UPDATE_CATEGORIES: "UPDATE_CATEGORIES",
354
- UPDATE_ISSUE_TEMPLATES: "UPDATE_ISSUE_TEMPLATES",
355
- DISCONNECTED: "DISCONNECTED"
356
- };
357
-
358
- // src/ws/index.ts
359
- function ws(url, handlers = {}) {
335
+ // src/events/index.ts
336
+ function sse(url, handlers = {}) {
360
337
  if (!url) {
361
338
  throw new Error(
362
- "[Sayr.ws] WebSocket URL is required. Did you forget to pass org.wsUrl?"
339
+ "[Sayr.sse] SSE URL is required. Did you forget to pass org.eventsUrl?"
363
340
  );
364
341
  }
365
- let socket;
366
- let retry = 0;
342
+ let source = null;
367
343
  let closed = false;
368
344
  function connect() {
369
345
  if (closed) return;
370
- socket = new WebSocket(url);
371
- socket.onmessage = (e) => {
346
+ source = new EventSource(url);
347
+ source.onmessage = (e) => {
372
348
  const msg = JSON.parse(e.data);
373
- if (msg.type === WS_EVENTS.PING) {
374
- socket.send(JSON.stringify({ type: WS_EVENTS.PONG }));
375
- return;
376
- }
377
349
  handlers[msg.type]?.(msg.data, msg);
378
350
  };
379
- socket.onclose = () => {
351
+ source.onerror = () => {
380
352
  if (closed) return;
381
- setTimeout(connect, Math.min(1e3 * 2 ** retry++, 3e4));
353
+ source?.close();
354
+ setTimeout(connect, 2e3);
382
355
  };
383
- socket.onerror = () => socket.close();
384
356
  }
385
357
  connect();
386
358
  return {
387
359
  close() {
388
360
  closed = true;
389
- socket?.close();
361
+ source?.close();
390
362
  }
391
363
  };
392
364
  }
393
365
 
366
+ // src/events/types.ts
367
+ var ServerEvent_EVENTS = {
368
+ CONNECTION_STATUS: "CONNECTION_STATUS",
369
+ ERROR: "ERROR",
370
+ UPDATE_ORG: "UPDATE_ORG",
371
+ CREATE_TASK: "CREATE_TASK",
372
+ UPDATE_TASK: "UPDATE_TASK",
373
+ UPDATE_TASK_COMMENTS: "UPDATE_TASK_COMMENTS",
374
+ UPDATE_TASK_VOTE: "UPDATE_TASK_VOTE",
375
+ UPDATE_LABELS: "UPDATE_LABELS",
376
+ UPDATE_VIEWS: "UPDATE_VIEWS",
377
+ UPDATE_CATEGORIES: "UPDATE_CATEGORIES",
378
+ UPDATE_ISSUE_TEMPLATES: "UPDATE_ISSUE_TEMPLATES"
379
+ };
380
+
394
381
  // src/index.ts
395
382
  var SayrV1 = v1_default;
396
- var SayrWS = ws;
397
- var SayrWSEvents = WS_EVENTS;
383
+ var SayrSSE = sse;
384
+ var SayrServerEvents = ServerEvent_EVENTS;
398
385
  var SayrClient = {
399
386
  setToken,
400
387
  setHeaders,
401
388
  setBaseUrl,
402
389
  resetClient,
403
- setHooks,
404
- setFetch
390
+ setHooks
405
391
  };
406
392
  var Sayr = {
407
393
  // client configuration
@@ -411,16 +397,16 @@ var Sayr = {
411
397
  org: v1_default.org,
412
398
  me: v1_default.me,
413
399
  // realtime
414
- ws,
415
- WS_EVENTS
400
+ sse,
401
+ EVENTS: ServerEvent_EVENTS
416
402
  };
417
403
  var index_default = Sayr;
418
404
  export {
419
405
  SayrClient,
406
+ SayrSSE,
407
+ SayrServerEvents,
420
408
  SayrV1,
421
- SayrWS,
422
- SayrWSEvents,
423
- WS_EVENTS,
409
+ ServerEvent_EVENTS,
424
410
  buildPaginationParams,
425
411
  index_default as default
426
412
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/index.ts","../src/api/v1/org/org.ts","../src/shared/index.ts","../src/api/v1/org/tasks.ts","../src/api/v1/org/comments.ts","../src/api/v1/org/labels.ts","../src/api/v1/org/categories.ts","../src/api/v1/org/index.ts","../src/api/v1/me/index.ts","../src/api/v1/index.ts","../src/ws/types.ts","../src/ws/index.ts","../src/index.ts"],"sourcesContent":["import { ApiError } from \"../types\";\r\n\r\n/* ────────────────────────────\r\n Types\r\n──────────────────────────── */\r\nexport type RequestOptions = {\r\n method?: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\r\n headers?: Record<string, string>;\r\n body?: Record<string, string>;\r\n signal?: AbortSignal;\r\n};\r\n\r\nexport interface ApiResult<T> {\r\n success: boolean;\r\n data: T | null;\r\n error: string | null;\r\n}\r\n\r\ntype Hooks = {\r\n onRequest?: (url: string, opts: RequestOptions) => void;\r\n onResponse?: (res: Response) => void;\r\n onError?: (error: ApiError | unknown) => void;\r\n};\r\n\r\ntype ClientConfig = {\r\n token?: string;\r\n headers?: Record<string, string>;\r\n fetch: typeof fetch;\r\n baseUrl: string;\r\n};\r\n\r\n/* ────────────────────────────\r\n Internal state\r\n──────────────────────────── */\r\nconst DEFAULT_API = \"https://api.sayr.io\";\r\n\r\nconst config: ClientConfig = {\r\n fetch: globalThis.fetch,\r\n baseUrl: DEFAULT_API\r\n};\r\n\r\nconst hooks: Hooks = {};\r\n\r\n/* ────────────────────────────\r\n Public config API\r\n──────────────────────────── */\r\nexport function setToken(token?: string) {\r\n config.token = token;\r\n}\r\n\r\nexport function getToken() {\r\n return config.token;\r\n}\r\n\r\nexport function setHeaders(headers?: Record<string, string>) {\r\n config.headers = {\r\n ...config.headers,\r\n ...headers\r\n };\r\n}\r\n\r\nexport function setFetch(fn: typeof fetch) {\r\n config.fetch = fn;\r\n}\r\n\r\nexport function setBaseUrl(url: string) {\r\n config.baseUrl = url.replace(/\\/$/, \"\");\r\n}\r\n\r\nexport function setHooks(h: Hooks) {\r\n Object.assign(hooks, h);\r\n}\r\nexport function resetClient() {\r\n config.token = undefined;\r\n config.headers = undefined;\r\n config.baseUrl = DEFAULT_API;\r\n}\r\n\r\n/* ────────────────────────────\r\n Request helper\r\n──────────────────────────── */\r\nexport async function request<T>(\r\n path: string,\r\n opts: RequestOptions = {}\r\n): Promise<T> {\r\n\r\n let url: string;\r\n try {\r\n url = path.startsWith(\"http\")\r\n ? path\r\n : `${config.baseUrl}${path}`;\r\n } catch (err) {\r\n throw err;\r\n }\r\n\r\n try {\r\n hooks.onRequest?.(url, opts);\r\n } catch (err) {\r\n throw err;\r\n }\r\n\r\n let res: Response;\r\n\r\n try {\r\n const method = opts.method ?? \"GET\";\r\n\r\n\r\n // 🔴 Guard: GET must not have body\r\n if (method === \"GET\" && opts.body !== undefined) {\r\n throw new Error(\"GET request cannot have a body\");\r\n }\r\n\r\n res = await config.fetch(url, {\r\n method,\r\n headers: {\r\n ...(config.token\r\n ? { Authorization: `Bearer ${config.token}` }\r\n : {}),\r\n ...(opts.body && method !== \"GET\"\r\n ? { \"Content-Type\": \"application/json\" }\r\n : {}),\r\n ...config.headers,\r\n ...opts.headers\r\n },\r\n body:\r\n opts.body && method !== \"GET\"\r\n ? JSON.stringify(opts.body)\r\n : undefined,\r\n signal: opts.signal\r\n });\r\n } catch (err) {\r\n const error: ApiError = {\r\n success: false,\r\n error: \"NETWORK_ERROR\",\r\n message: \"Failed to reach Sayr API\",\r\n // 👇 keep raw error so we can see it\r\n // @ts-ignore\r\n raw: err\r\n };\r\n\r\n hooks.onError?.(error);\r\n throw error;\r\n }\r\n\r\n try {\r\n hooks.onResponse?.(res);\r\n } catch (err) {\r\n throw err;\r\n }\r\n\r\n let json: any;\r\n try {\r\n json = await res.json();\r\n } catch (err) {\r\n const error: ApiError = {\r\n success: false,\r\n error: \"INVALID_RESPONSE\",\r\n message: \"Server returned invalid JSON\",\r\n status: res.status\r\n };\r\n hooks.onError?.(error);\r\n throw error;\r\n }\r\n if (!res.ok || !json.success) {\r\n const error: ApiError = {\r\n ...json,\r\n success: false,\r\n status: res.status\r\n };\r\n hooks.onError?.(error);\r\n throw error;\r\n }\r\n return json;\r\n}","import { Organization, ApiSuccess } from \"../../../types\";\r\nimport { ApiResult, request, type RequestOptions } from \"../../../client\";\r\n\r\n/**\r\n * Organization core operations.\r\n */\r\nexport default {\r\n /**\r\n * Fetches a public organization by slug.\r\n *\r\n * @since v1.0.0\r\n */\r\n async get(\r\n slug: string,\r\n opts?: RequestOptions,\r\n ): Promise<ApiResult<Organization>> {\r\n try {\r\n const r = await request<ApiSuccess<Organization>>(\r\n `/v1/organization/${slug}`,\r\n opts,\r\n );\r\n\r\n return {\r\n success: true,\r\n data: r.data,\r\n error: null,\r\n };\r\n } catch (err: any) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: err?.message ?? \"Failed to fetch organization\",\r\n };\r\n }\r\n },\r\n};","/* =======================\r\n * Shared params & helpers\r\n * ======================= */\r\n\r\nexport type Order = \"asc\" | \"desc\";\r\n\r\nexport interface PaginationParams {\r\n page?: number;\r\n limit?: number;\r\n}\r\n\r\nexport interface OrderedPaginationParams extends PaginationParams {\r\n order?: Order;\r\n}\r\n\r\nexport function buildPaginationParams(\r\n params?: OrderedPaginationParams\r\n): URLSearchParams {\r\n return new URLSearchParams({\r\n order: params?.order ?? \"desc\",\r\n limit: String(params?.limit ?? 5),\r\n page: String(params?.page ?? 1)\r\n });\r\n}","import {\r\n Task,\r\n Pagination,\r\n ApiSuccess,\r\n} from \"../../../types\";\r\nimport {\r\n ApiResult,\r\n request,\r\n type RequestOptions,\r\n} from \"../../../client\";\r\nimport {\r\n type OrderedPaginationParams,\r\n buildPaginationParams,\r\n} from \"../../../shared\";\r\n\r\n/**\r\n * Organization tasks.\r\n */\r\nexport default {\r\n /**\r\n * Lists public tasks for an organization.\r\n *\r\n * @since v1.0.0\r\n */\r\n async list(\r\n slug: string,\r\n params?: OrderedPaginationParams,\r\n opts?: RequestOptions,\r\n ): Promise<\r\n ApiResult<{\r\n items: Task[];\r\n pagination: Pagination;\r\n }>\r\n > {\r\n try {\r\n const q = buildPaginationParams(params);\r\n\r\n const r = await request<\r\n ApiSuccess<Task[]> & { pagination: Pagination }\r\n >(\r\n `/v1/organization/${slug}/tasks?${q}`,\r\n opts,\r\n );\r\n\r\n return {\r\n success: true,\r\n data: {\r\n items: r.data,\r\n pagination: r.pagination,\r\n },\r\n error: null,\r\n };\r\n } catch (err: any) {\r\n return {\r\n success: false,\r\n data: null,\r\n error:\r\n err?.message ??\r\n \"Failed to fetch tasks\",\r\n };\r\n }\r\n },\r\n\r\n /**\r\n * Fetches a single public task by short ID.\r\n *\r\n * @since v1.0.0\r\n */\r\n async get(\r\n slug: string,\r\n shortId: number,\r\n opts?: RequestOptions,\r\n ): Promise<ApiResult<Task>> {\r\n try {\r\n const r = await request<ApiSuccess<Task>>(\r\n `/v1/organization/${slug}/tasks/${shortId}`,\r\n opts,\r\n );\r\n\r\n return {\r\n success: true,\r\n data: r.data,\r\n error: null,\r\n };\r\n } catch (err: any) {\r\n return {\r\n success: false,\r\n data: null,\r\n error:\r\n err?.message ??\r\n \"Failed to fetch task\",\r\n };\r\n }\r\n },\r\n};","import {\r\n Comment,\r\n Pagination,\r\n ApiSuccess,\r\n} from \"../../../types\";\r\nimport {\r\n ApiResult,\r\n request,\r\n type RequestOptions,\r\n} from \"../../../client\";\r\nimport {\r\n type OrderedPaginationParams,\r\n buildPaginationParams,\r\n} from \"../../../shared\";\r\n\r\n/**\r\n * Organization task comments.\r\n */\r\nexport default {\r\n /**\r\n * Lists public comments for a task.\r\n *\r\n * @since v1.0.0\r\n */\r\n async list(\r\n slug: string,\r\n shortId: number,\r\n params?: OrderedPaginationParams,\r\n opts?: RequestOptions,\r\n ): Promise<\r\n ApiResult<{\r\n items: Comment[];\r\n pagination: Pagination;\r\n }>\r\n > {\r\n try {\r\n const q = buildPaginationParams(params);\r\n\r\n const r = await request<\r\n ApiSuccess<Comment[]> & { pagination: Pagination }\r\n >(\r\n `/v1/organization/${slug}/tasks/${shortId}/comments?${q}`,\r\n opts,\r\n );\r\n\r\n return {\r\n success: true,\r\n data: {\r\n items: r.data,\r\n pagination: r.pagination,\r\n },\r\n error: null,\r\n };\r\n } catch (err: any) {\r\n return {\r\n success: false,\r\n data: null,\r\n error:\r\n err?.message ??\r\n \"Failed to fetch task comments\",\r\n };\r\n }\r\n },\r\n};","import { Label, ApiSuccess } from \"../../../types\";\r\nimport { ApiResult, request, type RequestOptions } from \"../../../client\";\r\n\r\n/**\r\n * Organization labels.\r\n */\r\nexport default {\r\n /**\r\n * Lists public labels for an organization.\r\n *\r\n * @since v1.0.0\r\n */\r\n async list(\r\n slug: string,\r\n opts?: RequestOptions,\r\n ): Promise<ApiResult<Label[]>> {\r\n try {\r\n const r = await request<ApiSuccess<Label[]>>(\r\n `/v1/organization/${slug}/labels`,\r\n opts,\r\n );\r\n\r\n return {\r\n success: true,\r\n data: r.data,\r\n error: null,\r\n };\r\n } catch (err: any) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: err?.message ?? \"Failed to fetch labels\",\r\n };\r\n }\r\n },\r\n};","import {\r\n Category,\r\n ApiSuccess,\r\n} from \"../../../types\";\r\nimport {\r\n ApiResult,\r\n request,\r\n type RequestOptions,\r\n} from \"../../../client\";\r\nimport { type Order } from \"../../../shared\";\r\n\r\n/**\r\n * Organization categories.\r\n */\r\nexport default {\r\n /**\r\n * Lists public categories for an organization.\r\n *\r\n * @since v1.0.0\r\n */\r\n async list(\r\n slug: string,\r\n order: Order = \"desc\",\r\n opts?: RequestOptions,\r\n ): Promise<ApiResult<Category[]>> {\r\n try {\r\n const r = await request<ApiSuccess<Category[]>>(\r\n `/v1/organization/${slug}/categories?order=${order}`,\r\n opts,\r\n );\r\n\r\n return {\r\n success: true,\r\n data: r.data,\r\n error: null,\r\n };\r\n } catch (err: any) {\r\n return {\r\n success: false,\r\n data: null,\r\n error:\r\n err?.message ??\r\n \"Failed to fetch categories\",\r\n };\r\n }\r\n },\r\n};","import org from \"./org\";\r\nimport tasks from \"./tasks\";\r\nimport comments from \"./comments\";\r\nimport labels from \"./labels\";\r\nimport categories from \"./categories\";\r\n\r\n/**\r\n * Public Sayr Organization API — Version 1.\r\n *\r\n * @since v1.0.0\r\n */\r\nconst OrgAPI = {\r\n ...org,\r\n tasks,\r\n comments,\r\n labels,\r\n categories\r\n};\r\n\r\nexport default OrgAPI;","import { ApiResult, request, type RequestOptions } from \"../../../client\";\r\nimport { ApiSuccess, Organization } from \"../../../types\";\r\n\r\nexport interface Me {\r\n id: string;\r\n name: string | null;\r\n email: string | null;\r\n image: string | null;\r\n createdAt: string;\r\n}\r\n\r\n/**\r\n * Authenticated user API — Version 1.\r\n *\r\n * @since v1.0.0\r\n */\r\nexport default {\r\n async get(opts?: RequestOptions): Promise<ApiResult<Me>> {\r\n try {\r\n const r = await request<ApiSuccess<Me>>(\"/v1/me\", opts);\r\n return {\r\n success: true,\r\n data: r.data,\r\n error: null,\r\n };\r\n } catch (err: any) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: err?.message ?? \"Failed to fetch user\",\r\n };\r\n }\r\n },\r\n\r\n async organizations(\r\n opts?: RequestOptions,\r\n ): Promise<ApiResult<Organization[]>> {\r\n try {\r\n const r = await request<ApiSuccess<Organization[]>>(\r\n \"/v1/me/organizations\",\r\n opts,\r\n );\r\n return {\r\n success: true,\r\n data: r.data,\r\n error: null,\r\n };\r\n } catch (err: any) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: err?.message ?? \"Failed to fetch organizations\",\r\n };\r\n }\r\n },\r\n};","import org from \"./org\";\r\nimport me from \"./me\";\r\nexport const v1 = {\r\n org,\r\n me\r\n};\r\n\r\nexport default v1;","export type WSMessageType =\r\n | \"CONNECTION_STATUS\"\r\n | \"SUBSCRIBED\"\r\n | \"ERROR\"\r\n | \"PING\"\r\n | \"PONG\"\r\n | \"UPDATE_ORG\"\r\n | \"CREATE_TASK\"\r\n | \"UPDATE_TASK\"\r\n | \"UPDATE_TASK_COMMENTS\"\r\n | \"UPDATE_TASK_VOTE\"\r\n | \"UPDATE_LABELS\"\r\n | \"UPDATE_VIEWS\"\r\n | \"UPDATE_CATEGORIES\"\r\n | \"UPDATE_ISSUE_TEMPLATES\"\r\n | \"DISCONNECTED\";\r\n\r\n/**\r\n * String enum replacement for WS event names.\r\n * Use this instead of raw strings.\r\n */\r\nexport const WS_EVENTS: Record<WSMessageType, WSMessageType> = {\r\n CONNECTION_STATUS: \"CONNECTION_STATUS\",\r\n SUBSCRIBED: \"SUBSCRIBED\",\r\n ERROR: \"ERROR\",\r\n PING: \"PING\",\r\n PONG: \"PONG\",\r\n UPDATE_ORG: \"UPDATE_ORG\",\r\n CREATE_TASK: \"CREATE_TASK\",\r\n UPDATE_TASK: \"UPDATE_TASK\",\r\n UPDATE_TASK_COMMENTS: \"UPDATE_TASK_COMMENTS\",\r\n UPDATE_TASK_VOTE: \"UPDATE_TASK_VOTE\",\r\n UPDATE_LABELS: \"UPDATE_LABELS\",\r\n UPDATE_VIEWS: \"UPDATE_VIEWS\",\r\n UPDATE_CATEGORIES: \"UPDATE_CATEGORIES\",\r\n UPDATE_ISSUE_TEMPLATES: \"UPDATE_ISSUE_TEMPLATES\",\r\n DISCONNECTED: \"DISCONNECTED\"\r\n};\r\n\r\nexport interface WSMessage<T = unknown> {\r\n type: WSMessageType;\r\n scope: \"PUBLIC\";\r\n data: T;\r\n meta?: { ts: number };\r\n}","import { WS_EVENTS, type WSMessage, type WSMessageType } from \"./types\";\r\n\r\ntype Handlers = Partial<\r\n Record<WSMessageType, (data: any, msg: WSMessage) => void>\r\n>;\r\n\r\nexport function ws(url: string, handlers: Handlers = {}) {\r\n if (!url) {\r\n throw new Error(\r\n \"[Sayr.ws] WebSocket URL is required. \" +\r\n \"Did you forget to pass org.wsUrl?\"\r\n );\r\n }\r\n let socket: WebSocket;\r\n let retry = 0;\r\n let closed = false;\r\n\r\n function connect() {\r\n if (closed) return;\r\n\r\n socket = new WebSocket(url);\r\n\r\n socket.onmessage = (e) => {\r\n const msg = JSON.parse(e.data) as WSMessage;\r\n\r\n if (msg.type === WS_EVENTS.PING) {\r\n socket.send(JSON.stringify({ type: WS_EVENTS.PONG }));\r\n return;\r\n }\r\n\r\n handlers[msg.type]?.(msg.data, msg);\r\n };\r\n\r\n socket.onclose = () => {\r\n if (closed) return;\r\n setTimeout(connect, Math.min(1000 * 2 ** retry++, 30000));\r\n };\r\n\r\n socket.onerror = () => socket.close();\r\n }\r\n\r\n connect();\r\n\r\n return {\r\n close() {\r\n closed = true;\r\n socket?.close();\r\n }\r\n };\r\n}","/* ────────────────────────────\r\n API versions\r\n──────────────────────────── */\r\nimport v1 from \"./api/v1\";\r\n\r\n/* ────────────────────────────\r\n Realtime\r\n──────────────────────────── */\r\nimport { ws } from \"./ws\";\r\nimport { WS_EVENTS } from \"./ws/types\";\r\n\r\n/* ────────────────────────────\r\n Client config\r\n──────────────────────────── */\r\nimport {\r\n setToken,\r\n setHeaders,\r\n setBaseUrl,\r\n resetClient,\r\n setHooks,\r\n setFetch\r\n} from \"./client\";\r\n\r\n/* ────────────────────────────\r\n Named exports (power users)\r\n──────────────────────────── */\r\n\r\n/**\r\n * Sayr Public API — Version 1.\r\n *\r\n * @since v1.0.0\r\n */\r\nexport const SayrV1 = v1;\r\n\r\n\r\n/**\r\n * Create a WebSocket connection for public real‑time updates.\r\n */\r\nexport const SayrWS = ws;\r\n\r\n/**\r\n * Typed WebSocket event constants.\r\n */\r\nexport const SayrWSEvents = WS_EVENTS;\r\n\r\n/**\r\n * Global client configuration helpers.\r\n */\r\nexport const SayrClient = {\r\n setToken,\r\n setHeaders,\r\n setBaseUrl,\r\n resetClient,\r\n setHooks,\r\n setFetch\r\n};\r\n\r\n/* ────────────────────────────\r\n Default facade\r\n──────────────────────────── */\r\n\r\n/**\r\n * Sayr Public SDK.\r\n *\r\n * Read‑only access to public Sayr data via REST and WebSockets.\r\n *\r\n * @since v1.0.0\r\n */\r\nconst Sayr: {\r\n /**\r\n * Client configuration helpers.\r\n */\r\n client: typeof SayrClient;\r\n\r\n /**\r\n * Versioned API namespaces.\r\n */\r\n v1: typeof v1;\r\n\r\n /**\r\n * Alias for the current API version (`v1`).\r\n *\r\n * @since v1.0.0\r\n */\r\n org: typeof v1.org;\r\n me: typeof v1.me;\r\n\r\n /**\r\n * WebSocket helper for real‑time updates.\r\n */\r\n ws: typeof ws;\r\n\r\n /**\r\n * WebSocket event constants.\r\n */\r\n WS_EVENTS: typeof WS_EVENTS;\r\n} = {\r\n // client configuration\r\n client: SayrClient,\r\n\r\n // APIs\r\n v1,\r\n org: v1.org,\r\n me: v1.me,\r\n\r\n // realtime\r\n ws,\r\n WS_EVENTS\r\n};\r\n\r\nexport default Sayr;\r\n\r\n/* ────────────────────────────\r\n Types & shared helpers\r\n──────────────────────────── */\r\nexport * from \"./types\";\r\nexport * from \"./shared\";\r\nexport * from \"./ws/types\";"],"mappings":";AAkCA,IAAM,cAAc;AAEpB,IAAM,SAAuB;AAAA,EACzB,OAAO,WAAW;AAAA,EAClB,SAAS;AACb;AAEA,IAAM,QAAe,CAAC;AAKf,SAAS,SAAS,OAAgB;AACrC,SAAO,QAAQ;AACnB;AAMO,SAAS,WAAW,SAAkC;AACzD,SAAO,UAAU;AAAA,IACb,GAAG,OAAO;AAAA,IACV,GAAG;AAAA,EACP;AACJ;AAEO,SAAS,SAAS,IAAkB;AACvC,SAAO,QAAQ;AACnB;AAEO,SAAS,WAAW,KAAa;AACpC,SAAO,UAAU,IAAI,QAAQ,OAAO,EAAE;AAC1C;AAEO,SAAS,SAAS,GAAU;AAC/B,SAAO,OAAO,OAAO,CAAC;AAC1B;AACO,SAAS,cAAc;AAC1B,SAAO,QAAQ;AACf,SAAO,UAAU;AACjB,SAAO,UAAU;AACrB;AAKA,eAAsB,QAClB,MACA,OAAuB,CAAC,GACd;AAEV,MAAI;AACJ,MAAI;AACA,UAAM,KAAK,WAAW,MAAM,IACtB,OACA,GAAG,OAAO,OAAO,GAAG,IAAI;AAAA,EAClC,SAAS,KAAK;AACV,UAAM;AAAA,EACV;AAEA,MAAI;AACA,UAAM,YAAY,KAAK,IAAI;AAAA,EAC/B,SAAS,KAAK;AACV,UAAM;AAAA,EACV;AAEA,MAAI;AAEJ,MAAI;AACA,UAAM,SAAS,KAAK,UAAU;AAI9B,QAAI,WAAW,SAAS,KAAK,SAAS,QAAW;AAC7C,YAAM,IAAI,MAAM,gCAAgC;AAAA,IACpD;AAEA,UAAM,MAAM,OAAO,MAAM,KAAK;AAAA,MAC1B;AAAA,MACA,SAAS;AAAA,QACL,GAAI,OAAO,QACL,EAAE,eAAe,UAAU,OAAO,KAAK,GAAG,IAC1C,CAAC;AAAA,QACP,GAAI,KAAK,QAAQ,WAAW,QACtB,EAAE,gBAAgB,mBAAmB,IACrC,CAAC;AAAA,QACP,GAAG,OAAO;AAAA,QACV,GAAG,KAAK;AAAA,MACZ;AAAA,MACA,MACI,KAAK,QAAQ,WAAW,QAClB,KAAK,UAAU,KAAK,IAAI,IACxB;AAAA,MACV,QAAQ,KAAK;AAAA,IACjB,CAAC;AAAA,EACL,SAAS,KAAK;AACV,UAAM,QAAkB;AAAA,MACpB,SAAS;AAAA,MACT,OAAO;AAAA,MACP,SAAS;AAAA;AAAA;AAAA,MAGT,KAAK;AAAA,IACT;AAEA,UAAM,UAAU,KAAK;AACrB,UAAM;AAAA,EACV;AAEA,MAAI;AACA,UAAM,aAAa,GAAG;AAAA,EAC1B,SAAS,KAAK;AACV,UAAM;AAAA,EACV;AAEA,MAAI;AACJ,MAAI;AACA,WAAO,MAAM,IAAI,KAAK;AAAA,EAC1B,SAAS,KAAK;AACV,UAAM,QAAkB;AAAA,MACpB,SAAS;AAAA,MACT,OAAO;AAAA,MACP,SAAS;AAAA,MACT,QAAQ,IAAI;AAAA,IAChB;AACA,UAAM,UAAU,KAAK;AACrB,UAAM;AAAA,EACV;AACA,MAAI,CAAC,IAAI,MAAM,CAAC,KAAK,SAAS;AAC1B,UAAM,QAAkB;AAAA,MACpB,GAAG;AAAA,MACH,SAAS;AAAA,MACT,QAAQ,IAAI;AAAA,IAChB;AACA,UAAM,UAAU,KAAK;AACrB,UAAM;AAAA,EACV;AACA,SAAO;AACX;;;ACvKA,IAAO,cAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMX,MAAM,IACF,MACA,MACgC;AAChC,QAAI;AACA,YAAM,IAAI,MAAM;AAAA,QACZ,oBAAoB,IAAI;AAAA,QACxB;AAAA,MACJ;AAEA,aAAO;AAAA,QACH,SAAS;AAAA,QACT,MAAM,EAAE;AAAA,QACR,OAAO;AAAA,MACX;AAAA,IACJ,SAAS,KAAU;AACf,aAAO;AAAA,QACH,SAAS;AAAA,QACT,MAAM;AAAA,QACN,OAAO,KAAK,WAAW;AAAA,MAC3B;AAAA,IACJ;AAAA,EACJ;AACJ;;;ACpBO,SAAS,sBACZ,QACe;AACf,SAAO,IAAI,gBAAgB;AAAA,IACvB,OAAO,QAAQ,SAAS;AAAA,IACxB,OAAO,OAAO,QAAQ,SAAS,CAAC;AAAA,IAChC,MAAM,OAAO,QAAQ,QAAQ,CAAC;AAAA,EAClC,CAAC;AACL;;;ACLA,IAAO,gBAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMX,MAAM,KACF,MACA,QACA,MAMF;AACE,QAAI;AACA,YAAM,IAAI,sBAAsB,MAAM;AAEtC,YAAM,IAAI,MAAM;AAAA,QAGZ,oBAAoB,IAAI,UAAU,CAAC;AAAA,QACnC;AAAA,MACJ;AAEA,aAAO;AAAA,QACH,SAAS;AAAA,QACT,MAAM;AAAA,UACF,OAAO,EAAE;AAAA,UACT,YAAY,EAAE;AAAA,QAClB;AAAA,QACA,OAAO;AAAA,MACX;AAAA,IACJ,SAAS,KAAU;AACf,aAAO;AAAA,QACH,SAAS;AAAA,QACT,MAAM;AAAA,QACN,OACI,KAAK,WACL;AAAA,MACR;AAAA,IACJ;AAAA,EACJ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,IACF,MACA,SACA,MACwB;AACxB,QAAI;AACA,YAAM,IAAI,MAAM;AAAA,QACZ,oBAAoB,IAAI,UAAU,OAAO;AAAA,QACzC;AAAA,MACJ;AAEA,aAAO;AAAA,QACH,SAAS;AAAA,QACT,MAAM,EAAE;AAAA,QACR,OAAO;AAAA,MACX;AAAA,IACJ,SAAS,KAAU;AACf,aAAO;AAAA,QACH,SAAS;AAAA,QACT,MAAM;AAAA,QACN,OACI,KAAK,WACL;AAAA,MACR;AAAA,IACJ;AAAA,EACJ;AACJ;;;AC5EA,IAAO,mBAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMX,MAAM,KACF,MACA,SACA,QACA,MAMF;AACE,QAAI;AACA,YAAM,IAAI,sBAAsB,MAAM;AAEtC,YAAM,IAAI,MAAM;AAAA,QAGZ,oBAAoB,IAAI,UAAU,OAAO,aAAa,CAAC;AAAA,QACvD;AAAA,MACJ;AAEA,aAAO;AAAA,QACH,SAAS;AAAA,QACT,MAAM;AAAA,UACF,OAAO,EAAE;AAAA,UACT,YAAY,EAAE;AAAA,QAClB;AAAA,QACA,OAAO;AAAA,MACX;AAAA,IACJ,SAAS,KAAU;AACf,aAAO;AAAA,QACH,SAAS;AAAA,QACT,MAAM;AAAA,QACN,OACI,KAAK,WACL;AAAA,MACR;AAAA,IACJ;AAAA,EACJ;AACJ;;;ACzDA,IAAO,iBAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMX,MAAM,KACF,MACA,MAC2B;AAC3B,QAAI;AACA,YAAM,IAAI,MAAM;AAAA,QACZ,oBAAoB,IAAI;AAAA,QACxB;AAAA,MACJ;AAEA,aAAO;AAAA,QACH,SAAS;AAAA,QACT,MAAM,EAAE;AAAA,QACR,OAAO;AAAA,MACX;AAAA,IACJ,SAAS,KAAU;AACf,aAAO;AAAA,QACH,SAAS;AAAA,QACT,MAAM;AAAA,QACN,OAAO,KAAK,WAAW;AAAA,MAC3B;AAAA,IACJ;AAAA,EACJ;AACJ;;;ACrBA,IAAO,qBAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMX,MAAM,KACF,MACA,QAAe,QACf,MAC8B;AAC9B,QAAI;AACA,YAAM,IAAI,MAAM;AAAA,QACZ,oBAAoB,IAAI,qBAAqB,KAAK;AAAA,QAClD;AAAA,MACJ;AAEA,aAAO;AAAA,QACH,SAAS;AAAA,QACT,MAAM,EAAE;AAAA,QACR,OAAO;AAAA,MACX;AAAA,IACJ,SAAS,KAAU;AACf,aAAO;AAAA,QACH,SAAS;AAAA,QACT,MAAM;AAAA,QACN,OACI,KAAK,WACL;AAAA,MACR;AAAA,IACJ;AAAA,EACJ;AACJ;;;ACnCA,IAAM,SAAS;AAAA,EACX,GAAG;AAAA,EACH;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACJ;AAEA,IAAOA,eAAQ;;;ACHf,IAAO,aAAQ;AAAA,EACX,MAAM,IAAI,MAA+C;AACrD,QAAI;AACA,YAAM,IAAI,MAAM,QAAwB,UAAU,IAAI;AACtD,aAAO;AAAA,QACH,SAAS;AAAA,QACT,MAAM,EAAE;AAAA,QACR,OAAO;AAAA,MACX;AAAA,IACJ,SAAS,KAAU;AACf,aAAO;AAAA,QACH,SAAS;AAAA,QACT,MAAM;AAAA,QACN,OAAO,KAAK,WAAW;AAAA,MAC3B;AAAA,IACJ;AAAA,EACJ;AAAA,EAEA,MAAM,cACF,MACkC;AAClC,QAAI;AACA,YAAM,IAAI,MAAM;AAAA,QACZ;AAAA,QACA;AAAA,MACJ;AACA,aAAO;AAAA,QACH,SAAS;AAAA,QACT,MAAM,EAAE;AAAA,QACR,OAAO;AAAA,MACX;AAAA,IACJ,SAAS,KAAU;AACf,aAAO;AAAA,QACH,SAAS;AAAA,QACT,MAAM;AAAA,QACN,OAAO,KAAK,WAAW;AAAA,MAC3B;AAAA,IACJ;AAAA,EACJ;AACJ;;;ACrDO,IAAM,KAAK;AAAA,EACd,KAAAC;AAAA,EACA;AACJ;AAEA,IAAO,aAAQ;;;ACcR,IAAM,YAAkD;AAAA,EAC3D,mBAAmB;AAAA,EACnB,YAAY;AAAA,EACZ,OAAO;AAAA,EACP,MAAM;AAAA,EACN,MAAM;AAAA,EACN,YAAY;AAAA,EACZ,aAAa;AAAA,EACb,aAAa;AAAA,EACb,sBAAsB;AAAA,EACtB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,cAAc;AAAA,EACd,mBAAmB;AAAA,EACnB,wBAAwB;AAAA,EACxB,cAAc;AAClB;;;AC/BO,SAAS,GAAG,KAAa,WAAqB,CAAC,GAAG;AACrD,MAAI,CAAC,KAAK;AACN,UAAM,IAAI;AAAA,MACN;AAAA,IAEJ;AAAA,EACJ;AACA,MAAI;AACJ,MAAI,QAAQ;AACZ,MAAI,SAAS;AAEb,WAAS,UAAU;AACf,QAAI,OAAQ;AAEZ,aAAS,IAAI,UAAU,GAAG;AAE1B,WAAO,YAAY,CAAC,MAAM;AACtB,YAAM,MAAM,KAAK,MAAM,EAAE,IAAI;AAE7B,UAAI,IAAI,SAAS,UAAU,MAAM;AAC7B,eAAO,KAAK,KAAK,UAAU,EAAE,MAAM,UAAU,KAAK,CAAC,CAAC;AACpD;AAAA,MACJ;AAEA,eAAS,IAAI,IAAI,IAAI,IAAI,MAAM,GAAG;AAAA,IACtC;AAEA,WAAO,UAAU,MAAM;AACnB,UAAI,OAAQ;AACZ,iBAAW,SAAS,KAAK,IAAI,MAAO,KAAK,SAAS,GAAK,CAAC;AAAA,IAC5D;AAEA,WAAO,UAAU,MAAM,OAAO,MAAM;AAAA,EACxC;AAEA,UAAQ;AAER,SAAO;AAAA,IACH,QAAQ;AACJ,eAAS;AACT,cAAQ,MAAM;AAAA,IAClB;AAAA,EACJ;AACJ;;;ACjBO,IAAM,SAAS;AAMf,IAAM,SAAS;AAKf,IAAM,eAAe;AAKrB,IAAM,aAAa;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACH;AAaA,IAAM,OA4BF;AAAA;AAAA,EAED,QAAQ;AAAA;AAAA,EAGR;AAAA,EACA,KAAK,WAAG;AAAA,EACR,IAAI,WAAG;AAAA;AAAA,EAGP;AAAA,EACA;AACH;AAEA,IAAO,gBAAQ;","names":["org_default","org_default"]}
1
+ {"version":3,"sources":["../src/client/index.ts","../src/api/v1/org/org.ts","../src/shared/index.ts","../src/api/v1/org/tasks.ts","../src/api/v1/org/comments.ts","../src/api/v1/org/labels.ts","../src/api/v1/org/categories.ts","../src/api/v1/org/index.ts","../src/api/v1/me/index.ts","../src/api/v1/index.ts","../src/events/index.ts","../src/events/types.ts","../src/index.ts"],"sourcesContent":["import { ApiError } from \"../types\";\r\n\r\n/* ────────────────────────────\r\n Types\r\n──────────────────────────── */\r\nexport type RequestOptions = {\r\n method?: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\r\n headers?: Record<string, string>;\r\n body?: Record<string, string>;\r\n signal?: AbortSignal;\r\n};\r\n\r\nexport interface ApiResult<T> {\r\n success: boolean;\r\n data: T | null;\r\n error: string | null;\r\n}\r\n\r\ntype Hooks = {\r\n onRequest?: (url: string, opts: RequestOptions) => void;\r\n onResponse?: (res: Response) => void;\r\n onError?: (error: ApiError | unknown) => void;\r\n};\r\n\r\ntype ClientConfig = {\r\n token?: string;\r\n headers?: Record<string, string>;\r\n baseUrl: string;\r\n};\r\n\r\n/* ────────────────────────────\r\n Internal state\r\n──────────────────────────── */\r\nconst DEFAULT_API = \"https://api.sayr.io\";\r\n\r\nconst config: ClientConfig = {\r\n baseUrl: DEFAULT_API\r\n};\r\n\r\nconst hooks: Hooks = {};\r\n\r\n/* ────────────────────────────\r\n Public config API\r\n──────────────────────────── */\r\nexport function setToken(token?: string) {\r\n config.token = token;\r\n}\r\n\r\nexport function getToken() {\r\n return config.token;\r\n}\r\n\r\nexport function setHeaders(headers?: Record<string, string>) {\r\n config.headers = {\r\n ...config.headers,\r\n ...headers\r\n };\r\n}\r\n\r\nexport function setBaseUrl(url: string) {\r\n config.baseUrl = url.replace(/\\/$/, \"\");\r\n}\r\n\r\nexport function setHooks(h: Hooks) {\r\n Object.assign(hooks, h);\r\n}\r\nexport function resetClient() {\r\n config.token = undefined;\r\n config.headers = undefined;\r\n config.baseUrl = DEFAULT_API;\r\n}\r\n\r\n/* ────────────────────────────\r\n Request helper\r\n──────────────────────────── */\r\nexport async function request<T>(\r\n path: string,\r\n opts: RequestOptions = {}\r\n): Promise<T> {\r\n\r\n let url: string;\r\n try {\r\n url = path.startsWith(\"http\")\r\n ? path\r\n : `${config.baseUrl}${path}`;\r\n } catch (err) {\r\n throw err;\r\n }\r\n\r\n try {\r\n hooks.onRequest?.(url, opts);\r\n } catch (err) {\r\n throw err;\r\n }\r\n\r\n let res: Response;\r\n\r\n try {\r\n const method = opts.method ?? \"GET\";\r\n\r\n\r\n // 🔴 Guard: GET must not have body\r\n if (method === \"GET\" && opts.body !== undefined) {\r\n throw new Error(\"GET request cannot have a body\");\r\n }\r\n\r\n res = await fetch(url, {\r\n method,\r\n headers: {\r\n ...(config.token\r\n ? { Authorization: `Bearer ${config.token}` }\r\n : {}),\r\n ...(opts.body && method !== \"GET\"\r\n ? { \"Content-Type\": \"application/json\" }\r\n : {}),\r\n ...config.headers,\r\n ...opts.headers\r\n },\r\n body:\r\n opts.body && method !== \"GET\"\r\n ? JSON.stringify(opts.body)\r\n : undefined,\r\n signal: opts.signal\r\n });\r\n } catch (err) {\r\n const error: ApiError = {\r\n success: false,\r\n error: \"NETWORK_ERROR\",\r\n message: \"Failed to reach Sayr API\",\r\n // 👇 keep raw error so we can see it\r\n // @ts-ignore\r\n raw: err\r\n };\r\n\r\n hooks.onError?.(error);\r\n throw error;\r\n }\r\n\r\n try {\r\n hooks.onResponse?.(res);\r\n } catch (err) {\r\n throw err;\r\n }\r\n\r\n let json: any;\r\n try {\r\n json = await res.json();\r\n } catch (err) {\r\n const error: ApiError = {\r\n success: false,\r\n error: \"INVALID_RESPONSE\",\r\n message: \"Server returned invalid JSON\",\r\n status: res.status\r\n };\r\n hooks.onError?.(error);\r\n throw error;\r\n }\r\n if (!res.ok || !json.success) {\r\n const error: ApiError = {\r\n ...json,\r\n success: false,\r\n status: res.status\r\n };\r\n hooks.onError?.(error);\r\n throw error;\r\n }\r\n return json;\r\n}","import { Organization, ApiSuccess } from \"../../../types\";\r\nimport { ApiResult, request, type RequestOptions } from \"../../../client\";\r\n\r\n/**\r\n * Organization core operations.\r\n */\r\nexport default {\r\n /**\r\n * Fetches a public organization by slug.\r\n *\r\n * @since v1.0.0\r\n */\r\n async get(\r\n slug: string,\r\n opts?: RequestOptions,\r\n ): Promise<ApiResult<Organization>> {\r\n try {\r\n const r = await request<ApiSuccess<Organization>>(\r\n `/v1/organization/${slug}`,\r\n opts,\r\n );\r\n\r\n return {\r\n success: true,\r\n data: r.data,\r\n error: null,\r\n };\r\n } catch (err: any) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: err?.message ?? \"Failed to fetch organization\",\r\n };\r\n }\r\n },\r\n};","/* =======================\r\n * Shared params & helpers\r\n * ======================= */\r\n\r\nexport type Order = \"asc\" | \"desc\";\r\n\r\nexport interface PaginationParams {\r\n page?: number;\r\n limit?: number;\r\n}\r\n\r\nexport interface OrderedPaginationParams extends PaginationParams {\r\n order?: Order;\r\n}\r\n\r\nexport function buildPaginationParams(\r\n params?: OrderedPaginationParams\r\n): URLSearchParams {\r\n return new URLSearchParams({\r\n order: params?.order ?? \"desc\",\r\n limit: String(params?.limit ?? 5),\r\n page: String(params?.page ?? 1)\r\n });\r\n}","import {\r\n Task,\r\n Pagination,\r\n ApiSuccess,\r\n} from \"../../../types\";\r\nimport {\r\n ApiResult,\r\n request,\r\n type RequestOptions,\r\n} from \"../../../client\";\r\nimport {\r\n type OrderedPaginationParams,\r\n buildPaginationParams,\r\n} from \"../../../shared\";\r\n\r\n/**\r\n * Organization tasks.\r\n */\r\nexport default {\r\n /**\r\n * Lists public tasks for an organization.\r\n *\r\n * @since v1.0.0\r\n */\r\n async list(\r\n slug: string,\r\n params?: OrderedPaginationParams,\r\n opts?: RequestOptions,\r\n ): Promise<\r\n ApiResult<{\r\n items: Task[];\r\n pagination: Pagination;\r\n }>\r\n > {\r\n try {\r\n const q = buildPaginationParams(params);\r\n\r\n const r = await request<\r\n ApiSuccess<Task[]> & { pagination: Pagination }\r\n >(\r\n `/v1/organization/${slug}/tasks?${q}`,\r\n opts,\r\n );\r\n\r\n return {\r\n success: true,\r\n data: {\r\n items: r.data,\r\n pagination: r.pagination,\r\n },\r\n error: null,\r\n };\r\n } catch (err: any) {\r\n return {\r\n success: false,\r\n data: null,\r\n error:\r\n err?.message ??\r\n \"Failed to fetch tasks\",\r\n };\r\n }\r\n },\r\n\r\n /**\r\n * Fetches a single public task by short ID.\r\n *\r\n * @since v1.0.0\r\n */\r\n async get(\r\n slug: string,\r\n shortId: number,\r\n opts?: RequestOptions,\r\n ): Promise<ApiResult<Task>> {\r\n try {\r\n const r = await request<ApiSuccess<Task>>(\r\n `/v1/organization/${slug}/tasks/${shortId}`,\r\n opts,\r\n );\r\n\r\n return {\r\n success: true,\r\n data: r.data,\r\n error: null,\r\n };\r\n } catch (err: any) {\r\n return {\r\n success: false,\r\n data: null,\r\n error:\r\n err?.message ??\r\n \"Failed to fetch task\",\r\n };\r\n }\r\n },\r\n};","import {\r\n Comment,\r\n Pagination,\r\n ApiSuccess,\r\n} from \"../../../types\";\r\nimport {\r\n ApiResult,\r\n request,\r\n type RequestOptions,\r\n} from \"../../../client\";\r\nimport {\r\n type OrderedPaginationParams,\r\n buildPaginationParams,\r\n} from \"../../../shared\";\r\n\r\n/**\r\n * Organization task comments.\r\n */\r\nexport default {\r\n /**\r\n * Lists public comments for a task.\r\n *\r\n * @since v1.0.0\r\n */\r\n async list(\r\n slug: string,\r\n shortId: number,\r\n params?: OrderedPaginationParams,\r\n opts?: RequestOptions,\r\n ): Promise<\r\n ApiResult<{\r\n items: Comment[];\r\n pagination: Pagination;\r\n }>\r\n > {\r\n try {\r\n const q = buildPaginationParams(params);\r\n\r\n const r = await request<\r\n ApiSuccess<Comment[]> & { pagination: Pagination }\r\n >(\r\n `/v1/organization/${slug}/tasks/${shortId}/comments?${q}`,\r\n opts,\r\n );\r\n\r\n return {\r\n success: true,\r\n data: {\r\n items: r.data,\r\n pagination: r.pagination,\r\n },\r\n error: null,\r\n };\r\n } catch (err: any) {\r\n return {\r\n success: false,\r\n data: null,\r\n error:\r\n err?.message ??\r\n \"Failed to fetch task comments\",\r\n };\r\n }\r\n },\r\n};","import { Label, ApiSuccess } from \"../../../types\";\r\nimport { ApiResult, request, type RequestOptions } from \"../../../client\";\r\n\r\n/**\r\n * Organization labels.\r\n */\r\nexport default {\r\n /**\r\n * Lists public labels for an organization.\r\n *\r\n * @since v1.0.0\r\n */\r\n async list(\r\n slug: string,\r\n opts?: RequestOptions,\r\n ): Promise<ApiResult<Label[]>> {\r\n try {\r\n const r = await request<ApiSuccess<Label[]>>(\r\n `/v1/organization/${slug}/labels`,\r\n opts,\r\n );\r\n\r\n return {\r\n success: true,\r\n data: r.data,\r\n error: null,\r\n };\r\n } catch (err: any) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: err?.message ?? \"Failed to fetch labels\",\r\n };\r\n }\r\n },\r\n};","import {\r\n Category,\r\n ApiSuccess,\r\n} from \"../../../types\";\r\nimport {\r\n ApiResult,\r\n request,\r\n type RequestOptions,\r\n} from \"../../../client\";\r\nimport { type Order } from \"../../../shared\";\r\n\r\n/**\r\n * Organization categories.\r\n */\r\nexport default {\r\n /**\r\n * Lists public categories for an organization.\r\n *\r\n * @since v1.0.0\r\n */\r\n async list(\r\n slug: string,\r\n order: Order = \"desc\",\r\n opts?: RequestOptions,\r\n ): Promise<ApiResult<Category[]>> {\r\n try {\r\n const r = await request<ApiSuccess<Category[]>>(\r\n `/v1/organization/${slug}/categories?order=${order}`,\r\n opts,\r\n );\r\n\r\n return {\r\n success: true,\r\n data: r.data,\r\n error: null,\r\n };\r\n } catch (err: any) {\r\n return {\r\n success: false,\r\n data: null,\r\n error:\r\n err?.message ??\r\n \"Failed to fetch categories\",\r\n };\r\n }\r\n },\r\n};","import org from \"./org\";\r\nimport tasks from \"./tasks\";\r\nimport comments from \"./comments\";\r\nimport labels from \"./labels\";\r\nimport categories from \"./categories\";\r\n\r\n/**\r\n * Public Sayr Organization API — Version 1.\r\n *\r\n * @since v1.0.0\r\n */\r\nconst OrgAPI = {\r\n ...org,\r\n tasks,\r\n comments,\r\n labels,\r\n categories\r\n};\r\n\r\nexport default OrgAPI;","import { ApiResult, request, type RequestOptions } from \"../../../client\";\r\nimport { ApiSuccess, Organization } from \"../../../types\";\r\n\r\nexport interface Me {\r\n id: string;\r\n name: string | null;\r\n email: string | null;\r\n image: string | null;\r\n createdAt: string;\r\n}\r\n\r\n/**\r\n * Authenticated user API — Version 1.\r\n *\r\n * @since v1.0.0\r\n */\r\nexport default {\r\n async get(opts?: RequestOptions): Promise<ApiResult<Me>> {\r\n try {\r\n const r = await request<ApiSuccess<Me>>(\"/v1/me\", opts);\r\n return {\r\n success: true,\r\n data: r.data,\r\n error: null,\r\n };\r\n } catch (err: any) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: err?.message ?? \"Failed to fetch user\",\r\n };\r\n }\r\n },\r\n\r\n async organizations(\r\n opts?: RequestOptions,\r\n ): Promise<ApiResult<Organization[]>> {\r\n try {\r\n const r = await request<ApiSuccess<Organization[]>>(\r\n \"/v1/me/organizations\",\r\n opts,\r\n );\r\n return {\r\n success: true,\r\n data: r.data,\r\n error: null,\r\n };\r\n } catch (err: any) {\r\n return {\r\n success: false,\r\n data: null,\r\n error: err?.message ?? \"Failed to fetch organizations\",\r\n };\r\n }\r\n },\r\n};","import org from \"./org\";\r\nimport me from \"./me\";\r\nexport const v1 = {\r\n org,\r\n me\r\n};\r\n\r\nexport default v1;","import { type ServerEventMessage, type ServerEventMessageType } from \"./types\";\r\n\r\ntype Handlers = Partial<\r\n Record<ServerEventMessageType, (data: any, msg: ServerEventMessage) => void>\r\n>;\r\n\r\nexport function sse(url: string, handlers: Handlers = {}) {\r\n if (!url) {\r\n throw new Error(\r\n \"[Sayr.sse] SSE URL is required. Did you forget to pass org.eventsUrl?\"\r\n );\r\n }\r\n\r\n let source: EventSource | null = null;\r\n let closed = false;\r\n\r\n function connect() {\r\n if (closed) return;\r\n\r\n source = new EventSource(url);\r\n\r\n source.onmessage = (e) => {\r\n const msg = JSON.parse(e.data) as ServerEventMessage;\r\n handlers[msg.type]?.(msg.data, msg);\r\n };\r\n\r\n source.onerror = () => {\r\n if (closed) return;\r\n source?.close();\r\n setTimeout(connect, 2000); // simple retry\r\n };\r\n }\r\n\r\n connect();\r\n\r\n return {\r\n close() {\r\n closed = true;\r\n source?.close();\r\n }\r\n };\r\n}","export type ServerEventMessageType =\r\n | \"CONNECTION_STATUS\"\r\n | \"ERROR\"\r\n | \"UPDATE_ORG\"\r\n | \"CREATE_TASK\"\r\n | \"UPDATE_TASK\"\r\n | \"UPDATE_TASK_COMMENTS\"\r\n | \"UPDATE_TASK_VOTE\"\r\n | \"UPDATE_LABELS\"\r\n | \"UPDATE_VIEWS\"\r\n | \"UPDATE_CATEGORIES\"\r\n | \"UPDATE_ISSUE_TEMPLATES\";\r\n\r\n/**\r\n * String enum replacement for WS event names.\r\n * Use this instead of raw strings.\r\n */\r\nexport const ServerEvent_EVENTS: Record<ServerEventMessageType, ServerEventMessageType> = {\r\n CONNECTION_STATUS: \"CONNECTION_STATUS\",\r\n ERROR: \"ERROR\",\r\n UPDATE_ORG: \"UPDATE_ORG\",\r\n CREATE_TASK: \"CREATE_TASK\",\r\n UPDATE_TASK: \"UPDATE_TASK\",\r\n UPDATE_TASK_COMMENTS: \"UPDATE_TASK_COMMENTS\",\r\n UPDATE_TASK_VOTE: \"UPDATE_TASK_VOTE\",\r\n UPDATE_LABELS: \"UPDATE_LABELS\",\r\n UPDATE_VIEWS: \"UPDATE_VIEWS\",\r\n UPDATE_CATEGORIES: \"UPDATE_CATEGORIES\",\r\n UPDATE_ISSUE_TEMPLATES: \"UPDATE_ISSUE_TEMPLATES\",\r\n};\r\n\r\nexport interface ServerEventMessage<T = unknown> {\r\n type: ServerEventMessageType;\r\n scope: \"PUBLIC\";\r\n data: T;\r\n meta?: { ts: number };\r\n}","/* ────────────────────────────\r\n API versions\r\n──────────────────────────── */\r\nimport v1 from \"./api/v1\";\r\n\r\n/* ────────────────────────────\r\n Realtime\r\n──────────────────────────── */\r\nimport { ws } from \"./ws\";\r\nimport { WS_EVENTS } from \"./ws/types\";\r\nimport { sse } from \"./events\";\r\nimport { ServerEvent_EVENTS } from \"./events/types\";\r\n\r\n/* ────────────────────────────\r\n Client config\r\n──────────────────────────── */\r\nimport {\r\n setToken,\r\n setHeaders,\r\n setBaseUrl,\r\n resetClient,\r\n setHooks,\r\n} from \"./client\";\r\n\r\n/* ────────────────────────────\r\n Named exports (power users)\r\n──────────────────────────── */\r\n\r\n/**\r\n * Sayr Public API — Version 1.\r\n *\r\n * @since v1.0.0\r\n */\r\nexport const SayrV1 = v1;\r\n\r\n\r\n/**\r\n * Create a WebSocket connection for public real‑time updates.\r\n */\r\nexport const SayrSSE = sse;\r\n\r\n/**\r\n * Typed WebSocket event constants.\r\n */\r\nexport const SayrServerEvents = ServerEvent_EVENTS;\r\n\r\n/**\r\n * Global client configuration helpers.\r\n */\r\nexport const SayrClient = {\r\n setToken,\r\n setHeaders,\r\n setBaseUrl,\r\n resetClient,\r\n setHooks,\r\n};\r\n\r\n/* ────────────────────────────\r\n Default facade\r\n──────────────────────────── */\r\n\r\n/**\r\n * Sayr Public SDK.\r\n *\r\n * Read‑only access to public Sayr data via REST and WebSockets.\r\n *\r\n * @since v1.0.0\r\n */\r\nconst Sayr: {\r\n /**\r\n * Client configuration helpers.\r\n */\r\n client: typeof SayrClient;\r\n\r\n /**\r\n * Versioned API namespaces.\r\n */\r\n v1: typeof v1;\r\n\r\n /**\r\n * Alias for the current API version (`v1`).\r\n *\r\n * @since v1.0.0\r\n */\r\n org: typeof v1.org;\r\n me: typeof v1.me;\r\n\r\n /**\r\n * WebSocket helper for real‑time updates.\r\n */\r\n sse: typeof sse;\r\n\r\n /**\r\n * WebSocket event constants.\r\n */\r\n EVENTS: typeof ServerEvent_EVENTS;\r\n} = {\r\n // client configuration\r\n client: SayrClient,\r\n\r\n // APIs\r\n v1,\r\n org: v1.org,\r\n me: v1.me,\r\n\r\n // realtime\r\n sse,\r\n EVENTS: ServerEvent_EVENTS\r\n};\r\n\r\nexport default Sayr;\r\n\r\n/* ────────────────────────────\r\n Types & shared helpers\r\n──────────────────────────── */\r\nexport * from \"./types\";\r\nexport * from \"./shared\";\r\n// export * from \"./ws/types\";\r\nexport * from \"./events/types\";"],"mappings":"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","names":["org_default","org_default"]}
@@ -22,7 +22,7 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  useComments: () => useComments,
24
24
  useOrg: () => useOrg,
25
- useSayrWS: () => useSayrWS,
25
+ useSayrSSE: () => useSayrSSE,
26
26
  useTask: () => useTask,
27
27
  useTasks: () => useTasks
28
28
  });
@@ -34,7 +34,6 @@ var import_react = require("react");
34
34
  // src/client/index.ts
35
35
  var DEFAULT_API = "https://api.sayr.io";
36
36
  var config = {
37
- fetch: globalThis.fetch,
38
37
  baseUrl: DEFAULT_API
39
38
  };
40
39
  var hooks = {};
@@ -47,9 +46,6 @@ function setHeaders(headers) {
47
46
  ...headers
48
47
  };
49
48
  }
50
- function setFetch(fn) {
51
- config.fetch = fn;
52
- }
53
49
  function setBaseUrl(url) {
54
50
  config.baseUrl = url.replace(/\/$/, "");
55
51
  }
@@ -79,7 +75,7 @@ async function request(path, opts = {}) {
79
75
  if (method === "GET" && opts.body !== void 0) {
80
76
  throw new Error("GET request cannot have a body");
81
77
  }
82
- res = await config.fetch(url, {
78
+ res = await fetch(url, {
83
79
  method,
84
80
  headers: {
85
81
  ...config.token ? { Authorization: `Bearer ${config.token}` } : {},
@@ -369,69 +365,59 @@ var v1 = {
369
365
  };
370
366
  var v1_default = v1;
371
367
 
372
- // src/ws/types.ts
373
- var WS_EVENTS = {
374
- CONNECTION_STATUS: "CONNECTION_STATUS",
375
- SUBSCRIBED: "SUBSCRIBED",
376
- ERROR: "ERROR",
377
- PING: "PING",
378
- PONG: "PONG",
379
- UPDATE_ORG: "UPDATE_ORG",
380
- CREATE_TASK: "CREATE_TASK",
381
- UPDATE_TASK: "UPDATE_TASK",
382
- UPDATE_TASK_COMMENTS: "UPDATE_TASK_COMMENTS",
383
- UPDATE_TASK_VOTE: "UPDATE_TASK_VOTE",
384
- UPDATE_LABELS: "UPDATE_LABELS",
385
- UPDATE_VIEWS: "UPDATE_VIEWS",
386
- UPDATE_CATEGORIES: "UPDATE_CATEGORIES",
387
- UPDATE_ISSUE_TEMPLATES: "UPDATE_ISSUE_TEMPLATES",
388
- DISCONNECTED: "DISCONNECTED"
389
- };
390
-
391
- // src/ws/index.ts
392
- function ws(url, handlers = {}) {
368
+ // src/events/index.ts
369
+ function sse(url, handlers = {}) {
393
370
  if (!url) {
394
371
  throw new Error(
395
- "[Sayr.ws] WebSocket URL is required. Did you forget to pass org.wsUrl?"
372
+ "[Sayr.sse] SSE URL is required. Did you forget to pass org.eventsUrl?"
396
373
  );
397
374
  }
398
- let socket;
399
- let retry = 0;
375
+ let source = null;
400
376
  let closed = false;
401
377
  function connect() {
402
378
  if (closed) return;
403
- socket = new WebSocket(url);
404
- socket.onmessage = (e) => {
379
+ source = new EventSource(url);
380
+ source.onmessage = (e) => {
405
381
  const msg = JSON.parse(e.data);
406
- if (msg.type === WS_EVENTS.PING) {
407
- socket.send(JSON.stringify({ type: WS_EVENTS.PONG }));
408
- return;
409
- }
410
382
  handlers[msg.type]?.(msg.data, msg);
411
383
  };
412
- socket.onclose = () => {
384
+ source.onerror = () => {
413
385
  if (closed) return;
414
- setTimeout(connect, Math.min(1e3 * 2 ** retry++, 3e4));
386
+ source?.close();
387
+ setTimeout(connect, 2e3);
415
388
  };
416
- socket.onerror = () => socket.close();
417
389
  }
418
390
  connect();
419
391
  return {
420
392
  close() {
421
393
  closed = true;
422
- socket?.close();
394
+ source?.close();
423
395
  }
424
396
  };
425
397
  }
426
398
 
399
+ // src/events/types.ts
400
+ var ServerEvent_EVENTS = {
401
+ CONNECTION_STATUS: "CONNECTION_STATUS",
402
+ ERROR: "ERROR",
403
+ UPDATE_ORG: "UPDATE_ORG",
404
+ CREATE_TASK: "CREATE_TASK",
405
+ UPDATE_TASK: "UPDATE_TASK",
406
+ UPDATE_TASK_COMMENTS: "UPDATE_TASK_COMMENTS",
407
+ UPDATE_TASK_VOTE: "UPDATE_TASK_VOTE",
408
+ UPDATE_LABELS: "UPDATE_LABELS",
409
+ UPDATE_VIEWS: "UPDATE_VIEWS",
410
+ UPDATE_CATEGORIES: "UPDATE_CATEGORIES",
411
+ UPDATE_ISSUE_TEMPLATES: "UPDATE_ISSUE_TEMPLATES"
412
+ };
413
+
427
414
  // src/index.ts
428
415
  var SayrClient = {
429
416
  setToken,
430
417
  setHeaders,
431
418
  setBaseUrl,
432
419
  resetClient,
433
- setHooks,
434
- setFetch
420
+ setHooks
435
421
  };
436
422
  var Sayr = {
437
423
  // client configuration
@@ -441,8 +427,8 @@ var Sayr = {
441
427
  org: v1_default.org,
442
428
  me: v1_default.me,
443
429
  // realtime
444
- ws,
445
- WS_EVENTS
430
+ sse,
431
+ EVENTS: ServerEvent_EVENTS
446
432
  };
447
433
  var index_default = Sayr;
448
434
 
@@ -482,16 +468,16 @@ function useOrg(slug) {
482
468
  // src/react/useTasks.ts
483
469
  var import_react3 = require("react");
484
470
 
485
- // src/react/useSayrWS.ts
471
+ // src/react/useSayrSSE.ts
486
472
  var import_react2 = require("react");
487
- function useSayrWS(wsUrl, handlers) {
473
+ function useSayrSSE(eventsUrl, handlers) {
488
474
  const connRef = (0, import_react2.useRef)(null);
489
475
  const handlersRef = (0, import_react2.useRef)(handlers);
490
476
  (0, import_react2.useEffect)(() => {
491
477
  handlersRef.current = handlers;
492
478
  }, [handlers]);
493
479
  (0, import_react2.useEffect)(() => {
494
- if (!wsUrl) return;
480
+ if (!eventsUrl) return;
495
481
  const proxyHandlers = new Proxy(
496
482
  {},
497
483
  {
@@ -505,17 +491,17 @@ function useSayrWS(wsUrl, handlers) {
505
491
  }
506
492
  }
507
493
  );
508
- connRef.current = ws(wsUrl, proxyHandlers);
494
+ connRef.current = sse(eventsUrl, proxyHandlers);
509
495
  return () => {
510
496
  connRef.current?.close();
511
497
  connRef.current = null;
512
498
  };
513
- }, [wsUrl]);
499
+ }, [eventsUrl]);
514
500
  return connRef;
515
501
  }
516
502
 
517
503
  // src/react/useTasks.ts
518
- function useTasks(slug, wsUrl) {
504
+ function useTasks(slug, eventsUrl) {
519
505
  const [tasks, setTasks] = (0, import_react3.useState)([]);
520
506
  const [loading, setLoading] = (0, import_react3.useState)(false);
521
507
  const [error, setError] = (0, import_react3.useState)(null);
@@ -542,9 +528,9 @@ function useTasks(slug, wsUrl) {
542
528
  (0, import_react3.useEffect)(() => {
543
529
  fetchTasks();
544
530
  }, [fetchTasks]);
545
- useSayrWS(wsUrl, {
546
- [index_default.WS_EVENTS.CREATE_TASK]: fetchTasks,
547
- [index_default.WS_EVENTS.UPDATE_TASK]: fetchTasks
531
+ useSayrSSE(eventsUrl, {
532
+ [index_default.EVENTS.CREATE_TASK]: fetchTasks,
533
+ [index_default.EVENTS.UPDATE_TASK]: fetchTasks
548
534
  });
549
535
  return {
550
536
  tasks,
@@ -590,7 +576,7 @@ function useTask(slug, shortId) {
590
576
 
591
577
  // src/react/useComments.ts
592
578
  var import_react5 = require("react");
593
- function useComments(slug, shortId, wsUrl) {
579
+ function useComments(slug, shortId, eventsUrl) {
594
580
  const [comments, setComments] = (0, import_react5.useState)([]);
595
581
  const [loading, setLoading] = (0, import_react5.useState)(false);
596
582
  const [error, setError] = (0, import_react5.useState)(null);
@@ -604,10 +590,7 @@ function useComments(slug, shortId, wsUrl) {
604
590
  if (!slug || shortId == null) return;
605
591
  setLoading(true);
606
592
  setError(null);
607
- const res = await index_default.org.comments.list(
608
- slug,
609
- shortId
610
- );
593
+ const res = await index_default.org.comments.list(slug, shortId);
611
594
  if (!mountedRef.current) return;
612
595
  if (!res.success) {
613
596
  setError(res.error);
@@ -620,8 +603,8 @@ function useComments(slug, shortId, wsUrl) {
620
603
  (0, import_react5.useEffect)(() => {
621
604
  fetchComments();
622
605
  }, [fetchComments]);
623
- useSayrWS(wsUrl, {
624
- [index_default.WS_EVENTS.UPDATE_TASK_COMMENTS]: fetchComments
606
+ useSayrSSE(eventsUrl, {
607
+ [index_default.EVENTS.UPDATE_TASK_COMMENTS]: fetchComments
625
608
  });
626
609
  return {
627
610
  comments,
@@ -634,7 +617,7 @@ function useComments(slug, shortId, wsUrl) {
634
617
  0 && (module.exports = {
635
618
  useComments,
636
619
  useOrg,
637
- useSayrWS,
620
+ useSayrSSE,
638
621
  useTask,
639
622
  useTasks
640
623
  });