@sayrio/public 0.1.1 → 0.1.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/README.md CHANGED
@@ -190,82 +190,4 @@ This package ships with full TypeScript definitions.
190
190
 
191
191
  ```ts
192
192
  import type { Organization, Task } from "@sayrio/public";
193
- ```
194
-
195
- ---
196
-
197
- ## Basic HTML Example
198
-
199
- This example shows how to use `@sayrio/public` in a **plain HTML file** with no
200
- build tools.
201
-
202
- > **Important:**
203
- > This SDK is published as an **ES module**, so you must use
204
- > `type="module"` and `import`.
205
- > It does **not** create a global `Sayr` variable.
206
-
207
- ```html
208
- <!DOCTYPE html>
209
- <html lang="en">
210
- <head>
211
- <meta charset="utf-8" />
212
- <title>Sayr Public SDK – HTML Example</title>
213
- <style>
214
- body {
215
- background: #0b0b0b;
216
- color: #e5e7eb;
217
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
218
- padding: 24px;
219
- }
220
- </style>
221
- </head>
222
-
223
- <body>
224
- <h1>Sayr Public SDK</h1>
225
- <pre id="out">Loading…</pre>
226
-
227
- <script type="module">
228
- import Sayr from "https://esm.sh/@sayrio/public";
229
-
230
- const out = document.getElementById("out");
231
-
232
- function log(label, value) {
233
- out.textContent +=
234
- label + ": " + JSON.stringify(value, null, 2) + "\n\n";
235
- }
236
-
237
- const slug = "org";
238
-
239
- // Fetch public organization
240
- const org = await Sayr.org.get(slug);
241
- log("Organization", org);
242
-
243
- // Connect to public WebSocket
244
- Sayr.ws(org.wsUrl, {
245
- [Sayr.wsTypes.UPDATE_ORG]: (data) => {
246
- log("Org updated", data);
247
- },
248
-
249
- [Sayr.wsTypes.UPDATE_TASK]: (task) => {
250
- log("Task updated", task);
251
- },
252
-
253
- [Sayr.wsTypes.ERROR]: (err) => {
254
- log("WebSocket error", err);
255
- }
256
- });
257
- </script>
258
- </body>
259
- </html>
260
- ```
261
-
262
- ---
263
-
264
- ## Notes on Browser Usage
265
-
266
- - This package **does not expose a global `Sayr`**
267
- - Always use `type="module"` and `import`
268
- - For legacy `<script>` usage, a UMD build is not currently provided
269
- - `esm.sh` is recommended for CDN usage
270
-
271
- ---
193
+ ```
package/dist/index.cjs CHANGED
@@ -113,6 +113,11 @@ var wsTypes = {
113
113
  DISCONNECTED: "DISCONNECTED"
114
114
  };
115
115
  function ws(url, handlers = {}) {
116
+ if (!url) {
117
+ throw new Error(
118
+ "[Sayr.ws] WebSocket URL is required. Did you forget to pass org.wsUrl?"
119
+ );
120
+ }
116
121
  let socket;
117
122
  let retry = 0;
118
123
  let closed = false;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/org.ts","../src/ws.ts"],"sourcesContent":["import { org } from \"./org\";\r\nimport { ws, wsTypes } from \"./ws\";\r\n\r\nconst Sayr = { org, ws, wsTypes };\r\n\r\nexport default Sayr;\r\nexport { org, ws, wsTypes };\r\nexport * from \"./types\";","import {\r\n Organization,\r\n Label,\r\n Category,\r\n Task,\r\n Comment,\r\n Pagination,\r\n ApiSuccess\r\n} from \"./types\";\r\n\r\nconst API = \"https://sayr.io/api/public\";\r\n\r\nasync function get<T>(url: string): Promise<T> {\r\n const res = await fetch(url);\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n return json;\r\n}\r\n\r\nexport const org = {\r\n async get(slug: string): Promise<Organization> {\r\n const r = await get<ApiSuccess<Organization>>(\r\n `${API}/organization/${slug}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async labels(slug: string): Promise<Label[]> {\r\n const r = await get<ApiSuccess<Label[]>>(\r\n `${API}/organization/${slug}/labels`\r\n );\r\n return r.data;\r\n },\r\n\r\n async categories(\r\n slug: string,\r\n order: \"asc\" | \"desc\" = \"desc\"\r\n ): Promise<Category[]> {\r\n const r = await get<ApiSuccess<Category[]>>(\r\n `${API}/organization/${slug}/categories?order=${order}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async tasks(\r\n slug: string,\r\n opts?: { order?: \"asc\" | \"desc\"; limit?: number; page?: number }\r\n ): Promise<{ data: Task[]; pagination: Pagination }> {\r\n const q = new URLSearchParams({\r\n order: opts?.order ?? \"desc\",\r\n limit: String(opts?.limit ?? 5),\r\n page: String(opts?.page ?? 1)\r\n });\r\n\r\n const res = await fetch(\r\n `${API}/organization/${slug}/tasks?${q}`\r\n );\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n\r\n return {\r\n data: json.data,\r\n pagination: json.pagination\r\n };\r\n },\r\n\r\n async task(slug: string, shortId: number): Promise<Task> {\r\n const r = await get<ApiSuccess<Task>>(\r\n `${API}/organization/${slug}/tasks/${shortId}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async comments(\r\n slug: string,\r\n shortId: number,\r\n opts?: { order?: \"asc\" | \"desc\"; limit?: number; page?: number }\r\n ): Promise<{ data: Comment[]; pagination: Pagination }> {\r\n const q = new URLSearchParams({\r\n order: opts?.order ?? \"desc\",\r\n limit: String(opts?.limit ?? 5),\r\n page: String(opts?.page ?? 1)\r\n });\r\n\r\n const res = await fetch(\r\n `${API}/organization/${slug}/tasks/${shortId}/comments?${q}`\r\n );\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n\r\n return {\r\n data: json.data,\r\n pagination: json.pagination\r\n };\r\n }\r\n};","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\nexport const wsTypes: 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}\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 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 === wsTypes.PING) {\r\n socket.send(JSON.stringify({ type: wsTypes.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}"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/org.ts","../src/ws.ts"],"sourcesContent":["import { org } from \"./org\";\r\nimport { ws, wsTypes } from \"./ws\";\r\n\r\nconst Sayr = { org, ws, wsTypes };\r\n\r\nexport default Sayr;\r\nexport { org, ws, wsTypes };\r\nexport * from \"./types\";","import {\r\n Organization,\r\n Label,\r\n Category,\r\n Task,\r\n Comment,\r\n Pagination,\r\n ApiSuccess\r\n} from \"./types\";\r\n\r\nconst API = \"https://sayr.io/api/public\";\r\n\r\nasync function get<T>(url: string): Promise<T> {\r\n const res = await fetch(url);\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n return json;\r\n}\r\n\r\nexport const org = {\r\n async get(slug: string): Promise<Organization> {\r\n const r = await get<ApiSuccess<Organization>>(\r\n `${API}/organization/${slug}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async labels(slug: string): Promise<Label[]> {\r\n const r = await get<ApiSuccess<Label[]>>(\r\n `${API}/organization/${slug}/labels`\r\n );\r\n return r.data;\r\n },\r\n\r\n async categories(\r\n slug: string,\r\n order: \"asc\" | \"desc\" = \"desc\"\r\n ): Promise<Category[]> {\r\n const r = await get<ApiSuccess<Category[]>>(\r\n `${API}/organization/${slug}/categories?order=${order}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async tasks(\r\n slug: string,\r\n opts?: { order?: \"asc\" | \"desc\"; limit?: number; page?: number }\r\n ): Promise<{ data: Task[]; pagination: Pagination }> {\r\n const q = new URLSearchParams({\r\n order: opts?.order ?? \"desc\",\r\n limit: String(opts?.limit ?? 5),\r\n page: String(opts?.page ?? 1)\r\n });\r\n\r\n const res = await fetch(\r\n `${API}/organization/${slug}/tasks?${q}`\r\n );\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n\r\n return {\r\n data: json.data,\r\n pagination: json.pagination\r\n };\r\n },\r\n\r\n async task(slug: string, shortId: number): Promise<Task> {\r\n const r = await get<ApiSuccess<Task>>(\r\n `${API}/organization/${slug}/tasks/${shortId}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async comments(\r\n slug: string,\r\n shortId: number,\r\n opts?: { order?: \"asc\" | \"desc\"; limit?: number; page?: number }\r\n ): Promise<{ data: Comment[]; pagination: Pagination }> {\r\n const q = new URLSearchParams({\r\n order: opts?.order ?? \"desc\",\r\n limit: String(opts?.limit ?? 5),\r\n page: String(opts?.page ?? 1)\r\n });\r\n\r\n const res = await fetch(\r\n `${API}/organization/${slug}/tasks/${shortId}/comments?${q}`\r\n );\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n\r\n return {\r\n data: json.data,\r\n pagination: json.pagination\r\n };\r\n }\r\n};","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\nexport const wsTypes: 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}\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 === wsTypes.PING) {\r\n socket.send(JSON.stringify({ type: wsTypes.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}"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACUA,IAAM,MAAM;AAEZ,eAAe,IAAO,KAAyB;AAC3C,QAAM,MAAM,MAAM,MAAM,GAAG;AAC3B,QAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,MAAI,CAAC,KAAK,QAAS,OAAM;AACzB,SAAO;AACX;AAEO,IAAM,MAAM;AAAA,EACf,MAAM,IAAI,MAAqC;AAC3C,UAAM,IAAI,MAAM;AAAA,MACZ,GAAG,GAAG,iBAAiB,IAAI;AAAA,IAC/B;AACA,WAAO,EAAE;AAAA,EACb;AAAA,EAEA,MAAM,OAAO,MAAgC;AACzC,UAAM,IAAI,MAAM;AAAA,MACZ,GAAG,GAAG,iBAAiB,IAAI;AAAA,IAC/B;AACA,WAAO,EAAE;AAAA,EACb;AAAA,EAEA,MAAM,WACF,MACA,QAAwB,QACL;AACnB,UAAM,IAAI,MAAM;AAAA,MACZ,GAAG,GAAG,iBAAiB,IAAI,qBAAqB,KAAK;AAAA,IACzD;AACA,WAAO,EAAE;AAAA,EACb;AAAA,EAEA,MAAM,MACF,MACA,MACiD;AACjD,UAAM,IAAI,IAAI,gBAAgB;AAAA,MAC1B,OAAO,MAAM,SAAS;AAAA,MACtB,OAAO,OAAO,MAAM,SAAS,CAAC;AAAA,MAC9B,MAAM,OAAO,MAAM,QAAQ,CAAC;AAAA,IAChC,CAAC;AAED,UAAM,MAAM,MAAM;AAAA,MACd,GAAG,GAAG,iBAAiB,IAAI,UAAU,CAAC;AAAA,IAC1C;AACA,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI,CAAC,KAAK,QAAS,OAAM;AAEzB,WAAO;AAAA,MACH,MAAM,KAAK;AAAA,MACX,YAAY,KAAK;AAAA,IACrB;AAAA,EACJ;AAAA,EAEA,MAAM,KAAK,MAAc,SAAgC;AACrD,UAAM,IAAI,MAAM;AAAA,MACZ,GAAG,GAAG,iBAAiB,IAAI,UAAU,OAAO;AAAA,IAChD;AACA,WAAO,EAAE;AAAA,EACb;AAAA,EAEA,MAAM,SACF,MACA,SACA,MACoD;AACpD,UAAM,IAAI,IAAI,gBAAgB;AAAA,MAC1B,OAAO,MAAM,SAAS;AAAA,MACtB,OAAO,OAAO,MAAM,SAAS,CAAC;AAAA,MAC9B,MAAM,OAAO,MAAM,QAAQ,CAAC;AAAA,IAChC,CAAC;AAED,UAAM,MAAM,MAAM;AAAA,MACd,GAAG,GAAG,iBAAiB,IAAI,UAAU,OAAO,aAAa,CAAC;AAAA,IAC9D;AACA,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI,CAAC,KAAK,QAAS,OAAM;AAEzB,WAAO;AAAA,MACH,MAAM,KAAK;AAAA,MACX,YAAY,KAAK;AAAA,IACrB;AAAA,EACJ;AACJ;;;AC9EO,IAAM,UAAgD;AAAA,EACzD,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;AAaO,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,QAAQ,MAAM;AAC3B,eAAO,KAAK,KAAK,UAAU,EAAE,MAAM,QAAQ,KAAK,CAAC,CAAC;AAClD;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;;;AFtFA,IAAM,OAAO,EAAE,KAAK,IAAI,QAAQ;AAEhC,IAAO,gBAAQ;","names":[]}
package/dist/index.js CHANGED
@@ -84,6 +84,11 @@ var wsTypes = {
84
84
  DISCONNECTED: "DISCONNECTED"
85
85
  };
86
86
  function ws(url, handlers = {}) {
87
+ if (!url) {
88
+ throw new Error(
89
+ "[Sayr.ws] WebSocket URL is required. Did you forget to pass org.wsUrl?"
90
+ );
91
+ }
87
92
  let socket;
88
93
  let retry = 0;
89
94
  let closed = false;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/org.ts","../src/ws.ts","../src/index.ts"],"sourcesContent":["import {\r\n Organization,\r\n Label,\r\n Category,\r\n Task,\r\n Comment,\r\n Pagination,\r\n ApiSuccess\r\n} from \"./types\";\r\n\r\nconst API = \"https://sayr.io/api/public\";\r\n\r\nasync function get<T>(url: string): Promise<T> {\r\n const res = await fetch(url);\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n return json;\r\n}\r\n\r\nexport const org = {\r\n async get(slug: string): Promise<Organization> {\r\n const r = await get<ApiSuccess<Organization>>(\r\n `${API}/organization/${slug}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async labels(slug: string): Promise<Label[]> {\r\n const r = await get<ApiSuccess<Label[]>>(\r\n `${API}/organization/${slug}/labels`\r\n );\r\n return r.data;\r\n },\r\n\r\n async categories(\r\n slug: string,\r\n order: \"asc\" | \"desc\" = \"desc\"\r\n ): Promise<Category[]> {\r\n const r = await get<ApiSuccess<Category[]>>(\r\n `${API}/organization/${slug}/categories?order=${order}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async tasks(\r\n slug: string,\r\n opts?: { order?: \"asc\" | \"desc\"; limit?: number; page?: number }\r\n ): Promise<{ data: Task[]; pagination: Pagination }> {\r\n const q = new URLSearchParams({\r\n order: opts?.order ?? \"desc\",\r\n limit: String(opts?.limit ?? 5),\r\n page: String(opts?.page ?? 1)\r\n });\r\n\r\n const res = await fetch(\r\n `${API}/organization/${slug}/tasks?${q}`\r\n );\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n\r\n return {\r\n data: json.data,\r\n pagination: json.pagination\r\n };\r\n },\r\n\r\n async task(slug: string, shortId: number): Promise<Task> {\r\n const r = await get<ApiSuccess<Task>>(\r\n `${API}/organization/${slug}/tasks/${shortId}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async comments(\r\n slug: string,\r\n shortId: number,\r\n opts?: { order?: \"asc\" | \"desc\"; limit?: number; page?: number }\r\n ): Promise<{ data: Comment[]; pagination: Pagination }> {\r\n const q = new URLSearchParams({\r\n order: opts?.order ?? \"desc\",\r\n limit: String(opts?.limit ?? 5),\r\n page: String(opts?.page ?? 1)\r\n });\r\n\r\n const res = await fetch(\r\n `${API}/organization/${slug}/tasks/${shortId}/comments?${q}`\r\n );\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n\r\n return {\r\n data: json.data,\r\n pagination: json.pagination\r\n };\r\n }\r\n};","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\nexport const wsTypes: 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}\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 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 === wsTypes.PING) {\r\n socket.send(JSON.stringify({ type: wsTypes.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}","import { org } from \"./org\";\r\nimport { ws, wsTypes } from \"./ws\";\r\n\r\nconst Sayr = { org, ws, wsTypes };\r\n\r\nexport default Sayr;\r\nexport { org, ws, wsTypes };\r\nexport * from \"./types\";"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/org.ts","../src/ws.ts","../src/index.ts"],"sourcesContent":["import {\r\n Organization,\r\n Label,\r\n Category,\r\n Task,\r\n Comment,\r\n Pagination,\r\n ApiSuccess\r\n} from \"./types\";\r\n\r\nconst API = \"https://sayr.io/api/public\";\r\n\r\nasync function get<T>(url: string): Promise<T> {\r\n const res = await fetch(url);\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n return json;\r\n}\r\n\r\nexport const org = {\r\n async get(slug: string): Promise<Organization> {\r\n const r = await get<ApiSuccess<Organization>>(\r\n `${API}/organization/${slug}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async labels(slug: string): Promise<Label[]> {\r\n const r = await get<ApiSuccess<Label[]>>(\r\n `${API}/organization/${slug}/labels`\r\n );\r\n return r.data;\r\n },\r\n\r\n async categories(\r\n slug: string,\r\n order: \"asc\" | \"desc\" = \"desc\"\r\n ): Promise<Category[]> {\r\n const r = await get<ApiSuccess<Category[]>>(\r\n `${API}/organization/${slug}/categories?order=${order}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async tasks(\r\n slug: string,\r\n opts?: { order?: \"asc\" | \"desc\"; limit?: number; page?: number }\r\n ): Promise<{ data: Task[]; pagination: Pagination }> {\r\n const q = new URLSearchParams({\r\n order: opts?.order ?? \"desc\",\r\n limit: String(opts?.limit ?? 5),\r\n page: String(opts?.page ?? 1)\r\n });\r\n\r\n const res = await fetch(\r\n `${API}/organization/${slug}/tasks?${q}`\r\n );\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n\r\n return {\r\n data: json.data,\r\n pagination: json.pagination\r\n };\r\n },\r\n\r\n async task(slug: string, shortId: number): Promise<Task> {\r\n const r = await get<ApiSuccess<Task>>(\r\n `${API}/organization/${slug}/tasks/${shortId}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async comments(\r\n slug: string,\r\n shortId: number,\r\n opts?: { order?: \"asc\" | \"desc\"; limit?: number; page?: number }\r\n ): Promise<{ data: Comment[]; pagination: Pagination }> {\r\n const q = new URLSearchParams({\r\n order: opts?.order ?? \"desc\",\r\n limit: String(opts?.limit ?? 5),\r\n page: String(opts?.page ?? 1)\r\n });\r\n\r\n const res = await fetch(\r\n `${API}/organization/${slug}/tasks/${shortId}/comments?${q}`\r\n );\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n\r\n return {\r\n data: json.data,\r\n pagination: json.pagination\r\n };\r\n }\r\n};","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\nexport const wsTypes: 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}\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 === wsTypes.PING) {\r\n socket.send(JSON.stringify({ type: wsTypes.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}","import { org } from \"./org\";\r\nimport { ws, wsTypes } from \"./ws\";\r\n\r\nconst Sayr = { org, ws, wsTypes };\r\n\r\nexport default Sayr;\r\nexport { org, ws, wsTypes };\r\nexport * from \"./types\";"],"mappings":"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","names":[]}
@@ -117,6 +117,11 @@ var wsTypes = {
117
117
  DISCONNECTED: "DISCONNECTED"
118
118
  };
119
119
  function ws(url, handlers = {}) {
120
+ if (!url) {
121
+ throw new Error(
122
+ "[Sayr.ws] WebSocket URL is required. Did you forget to pass org.wsUrl?"
123
+ );
124
+ }
120
125
  let socket;
121
126
  let retry = 0;
122
127
  let closed = false;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/index.ts","../../src/react/useOrg.ts","../../src/org.ts","../../src/ws.ts","../../src/react/useTasks.ts","../../src/react/useSayrWS.ts","../../src/react/useTask.ts","../../src/react/useComments.ts"],"sourcesContent":["export { useOrg } from \"./useOrg\";\r\nexport { useTasks } from \"./useTasks\";\r\nexport { useTask } from \"./useTask\";\r\nexport { useComments } from \"./useComments\";\r\nexport { useSayrWS } from \"./useSayrWS\";","import { useEffect, useState } from \"react\";\r\nimport { org, Organization } from \"../index\";\r\n\r\nexport function useOrg(slug?: string) {\r\n const [data, setData] = useState<Organization | null>(null);\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<unknown>(null);\r\n\r\n useEffect(() => {\r\n if (!slug) return;\r\n\r\n setLoading(true);\r\n org\r\n .get(slug)\r\n .then(setData)\r\n .catch(setError)\r\n .finally(() => setLoading(false));\r\n }, [slug]);\r\n\r\n return { data, loading, error };\r\n}","import {\r\n Organization,\r\n Label,\r\n Category,\r\n Task,\r\n Comment,\r\n Pagination,\r\n ApiSuccess\r\n} from \"./types\";\r\n\r\nconst API = \"https://sayr.io/api/public\";\r\n\r\nasync function get<T>(url: string): Promise<T> {\r\n const res = await fetch(url);\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n return json;\r\n}\r\n\r\nexport const org = {\r\n async get(slug: string): Promise<Organization> {\r\n const r = await get<ApiSuccess<Organization>>(\r\n `${API}/organization/${slug}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async labels(slug: string): Promise<Label[]> {\r\n const r = await get<ApiSuccess<Label[]>>(\r\n `${API}/organization/${slug}/labels`\r\n );\r\n return r.data;\r\n },\r\n\r\n async categories(\r\n slug: string,\r\n order: \"asc\" | \"desc\" = \"desc\"\r\n ): Promise<Category[]> {\r\n const r = await get<ApiSuccess<Category[]>>(\r\n `${API}/organization/${slug}/categories?order=${order}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async tasks(\r\n slug: string,\r\n opts?: { order?: \"asc\" | \"desc\"; limit?: number; page?: number }\r\n ): Promise<{ data: Task[]; pagination: Pagination }> {\r\n const q = new URLSearchParams({\r\n order: opts?.order ?? \"desc\",\r\n limit: String(opts?.limit ?? 5),\r\n page: String(opts?.page ?? 1)\r\n });\r\n\r\n const res = await fetch(\r\n `${API}/organization/${slug}/tasks?${q}`\r\n );\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n\r\n return {\r\n data: json.data,\r\n pagination: json.pagination\r\n };\r\n },\r\n\r\n async task(slug: string, shortId: number): Promise<Task> {\r\n const r = await get<ApiSuccess<Task>>(\r\n `${API}/organization/${slug}/tasks/${shortId}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async comments(\r\n slug: string,\r\n shortId: number,\r\n opts?: { order?: \"asc\" | \"desc\"; limit?: number; page?: number }\r\n ): Promise<{ data: Comment[]; pagination: Pagination }> {\r\n const q = new URLSearchParams({\r\n order: opts?.order ?? \"desc\",\r\n limit: String(opts?.limit ?? 5),\r\n page: String(opts?.page ?? 1)\r\n });\r\n\r\n const res = await fetch(\r\n `${API}/organization/${slug}/tasks/${shortId}/comments?${q}`\r\n );\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n\r\n return {\r\n data: json.data,\r\n pagination: json.pagination\r\n };\r\n }\r\n};","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\nexport const wsTypes: 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}\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 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 === wsTypes.PING) {\r\n socket.send(JSON.stringify({ type: wsTypes.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}","import { useEffect, useState } from \"react\";\r\nimport { org, wsTypes } from \"..\";\r\nimport type { Task } from \"../types\";\r\nimport { useSayrWS } from \"./useSayrWS\";\r\nexport function useTasks(\r\n slug?: string,\r\n wsUrl?: string\r\n) {\r\n const [tasks, setTasks] = useState<Task[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n\r\n function fetchTasks() {\r\n if (!slug) return;\r\n setLoading(true);\r\n org\r\n .tasks(slug)\r\n .then((r) => setTasks(r.data))\r\n .finally(() => setLoading(false));\r\n }\r\n\r\n useEffect(fetchTasks, [slug]);\r\n\r\n useSayrWS(wsUrl, {\r\n [wsTypes.CREATE_TASK]: fetchTasks,\r\n [wsTypes.UPDATE_TASK]: fetchTasks\r\n });\r\n\r\n return { tasks, loading, refetch: fetchTasks };\r\n}","import { useEffect, useRef } from \"react\";\r\nimport { ws } from \"..\";\r\nimport { WSMessageType } from \"../ws\";\r\n\r\ntype Handlers = Partial<\r\n Record<WSMessageType, (data: any, msg: any) => void>\r\n>;\r\n\r\nexport function useSayrWS(\r\n wsUrl?: string,\r\n handlers?: Handlers\r\n) {\r\n const connRef = useRef<ReturnType<typeof ws> | null>(null);\r\n\r\n useEffect(() => {\r\n if (!wsUrl) return;\r\n\r\n connRef.current = ws(wsUrl, handlers);\r\n\r\n return () => {\r\n connRef.current?.close();\r\n connRef.current = null;\r\n };\r\n }, [wsUrl]);\r\n\r\n return connRef;\r\n}","import { useEffect, useState } from \"react\";\r\nimport { org } from \"..\";\r\nimport type { Task } from \"../types\";\r\n\r\nexport function useTask(\r\n slug?: string,\r\n shortId?: number\r\n) {\r\n const [task, setTask] = useState<Task | null>(null);\r\n const [loading, setLoading] = useState(false);\r\n\r\n useEffect(() => {\r\n if (!slug || shortId == null) return;\r\n\r\n setLoading(true);\r\n org\r\n .task(slug, shortId)\r\n .then(setTask)\r\n .finally(() => setLoading(false));\r\n }, [slug, shortId]);\r\n\r\n return { task, loading };\r\n}","import { useEffect, useState } from \"react\";\r\nimport { org, wsTypes } from \"..\";\r\nimport type { Comment } from \"../types\";\r\nimport { useSayrWS } from \"./useSayrWS\";\r\n\r\nexport function useComments(\r\n slug?: string,\r\n shortId?: number,\r\n wsUrl?: string\r\n) {\r\n const [comments, setComments] = useState<Comment[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n\r\n function fetchComments() {\r\n if (!slug || shortId == null) return;\r\n\r\n setLoading(true);\r\n org\r\n .comments(slug, shortId)\r\n .then((r) => setComments(r.data))\r\n .finally(() => setLoading(false));\r\n }\r\n\r\n useEffect(fetchComments, [slug, shortId]);\r\n\r\n useSayrWS(wsUrl, {\r\n [wsTypes.UPDATE_TASK_COMMENTS]: fetchComments\r\n });\r\n\r\n return { comments, loading, refetch: fetchComments };\r\n}"],"mappings":"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","names":["import_react","import_react","import_react","import_react"]}
1
+ {"version":3,"sources":["../../src/react/index.ts","../../src/react/useOrg.ts","../../src/org.ts","../../src/ws.ts","../../src/react/useTasks.ts","../../src/react/useSayrWS.ts","../../src/react/useTask.ts","../../src/react/useComments.ts"],"sourcesContent":["export { useOrg } from \"./useOrg\";\r\nexport { useTasks } from \"./useTasks\";\r\nexport { useTask } from \"./useTask\";\r\nexport { useComments } from \"./useComments\";\r\nexport { useSayrWS } from \"./useSayrWS\";","import { useEffect, useState } from \"react\";\r\nimport { org, Organization } from \"../index\";\r\n\r\nexport function useOrg(slug?: string) {\r\n const [data, setData] = useState<Organization | null>(null);\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<unknown>(null);\r\n\r\n useEffect(() => {\r\n if (!slug) return;\r\n\r\n setLoading(true);\r\n org\r\n .get(slug)\r\n .then(setData)\r\n .catch(setError)\r\n .finally(() => setLoading(false));\r\n }, [slug]);\r\n\r\n return { data, loading, error };\r\n}","import {\r\n Organization,\r\n Label,\r\n Category,\r\n Task,\r\n Comment,\r\n Pagination,\r\n ApiSuccess\r\n} from \"./types\";\r\n\r\nconst API = \"https://sayr.io/api/public\";\r\n\r\nasync function get<T>(url: string): Promise<T> {\r\n const res = await fetch(url);\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n return json;\r\n}\r\n\r\nexport const org = {\r\n async get(slug: string): Promise<Organization> {\r\n const r = await get<ApiSuccess<Organization>>(\r\n `${API}/organization/${slug}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async labels(slug: string): Promise<Label[]> {\r\n const r = await get<ApiSuccess<Label[]>>(\r\n `${API}/organization/${slug}/labels`\r\n );\r\n return r.data;\r\n },\r\n\r\n async categories(\r\n slug: string,\r\n order: \"asc\" | \"desc\" = \"desc\"\r\n ): Promise<Category[]> {\r\n const r = await get<ApiSuccess<Category[]>>(\r\n `${API}/organization/${slug}/categories?order=${order}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async tasks(\r\n slug: string,\r\n opts?: { order?: \"asc\" | \"desc\"; limit?: number; page?: number }\r\n ): Promise<{ data: Task[]; pagination: Pagination }> {\r\n const q = new URLSearchParams({\r\n order: opts?.order ?? \"desc\",\r\n limit: String(opts?.limit ?? 5),\r\n page: String(opts?.page ?? 1)\r\n });\r\n\r\n const res = await fetch(\r\n `${API}/organization/${slug}/tasks?${q}`\r\n );\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n\r\n return {\r\n data: json.data,\r\n pagination: json.pagination\r\n };\r\n },\r\n\r\n async task(slug: string, shortId: number): Promise<Task> {\r\n const r = await get<ApiSuccess<Task>>(\r\n `${API}/organization/${slug}/tasks/${shortId}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async comments(\r\n slug: string,\r\n shortId: number,\r\n opts?: { order?: \"asc\" | \"desc\"; limit?: number; page?: number }\r\n ): Promise<{ data: Comment[]; pagination: Pagination }> {\r\n const q = new URLSearchParams({\r\n order: opts?.order ?? \"desc\",\r\n limit: String(opts?.limit ?? 5),\r\n page: String(opts?.page ?? 1)\r\n });\r\n\r\n const res = await fetch(\r\n `${API}/organization/${slug}/tasks/${shortId}/comments?${q}`\r\n );\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n\r\n return {\r\n data: json.data,\r\n pagination: json.pagination\r\n };\r\n }\r\n};","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\nexport const wsTypes: 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}\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 === wsTypes.PING) {\r\n socket.send(JSON.stringify({ type: wsTypes.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}","import { useEffect, useState } from \"react\";\r\nimport { org, wsTypes } from \"..\";\r\nimport type { Task } from \"../types\";\r\nimport { useSayrWS } from \"./useSayrWS\";\r\nexport function useTasks(\r\n slug?: string,\r\n wsUrl?: string\r\n) {\r\n const [tasks, setTasks] = useState<Task[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n\r\n function fetchTasks() {\r\n if (!slug) return;\r\n setLoading(true);\r\n org\r\n .tasks(slug)\r\n .then((r) => setTasks(r.data))\r\n .finally(() => setLoading(false));\r\n }\r\n\r\n useEffect(fetchTasks, [slug]);\r\n\r\n useSayrWS(wsUrl, {\r\n [wsTypes.CREATE_TASK]: fetchTasks,\r\n [wsTypes.UPDATE_TASK]: fetchTasks\r\n });\r\n\r\n return { tasks, loading, refetch: fetchTasks };\r\n}","import { useEffect, useRef } from \"react\";\r\nimport { ws } from \"..\";\r\nimport { WSMessageType } from \"../ws\";\r\n\r\ntype Handlers = Partial<\r\n Record<WSMessageType, (data: any, msg: any) => void>\r\n>;\r\n\r\nexport function useSayrWS(\r\n wsUrl?: string,\r\n handlers?: Handlers\r\n) {\r\n const connRef = useRef<ReturnType<typeof ws> | null>(null);\r\n\r\n useEffect(() => {\r\n if (!wsUrl) return;\r\n\r\n connRef.current = ws(wsUrl, handlers);\r\n\r\n return () => {\r\n connRef.current?.close();\r\n connRef.current = null;\r\n };\r\n }, [wsUrl]);\r\n\r\n return connRef;\r\n}","import { useEffect, useState } from \"react\";\r\nimport { org } from \"..\";\r\nimport type { Task } from \"../types\";\r\n\r\nexport function useTask(\r\n slug?: string,\r\n shortId?: number\r\n) {\r\n const [task, setTask] = useState<Task | null>(null);\r\n const [loading, setLoading] = useState(false);\r\n\r\n useEffect(() => {\r\n if (!slug || shortId == null) return;\r\n\r\n setLoading(true);\r\n org\r\n .task(slug, shortId)\r\n .then(setTask)\r\n .finally(() => setLoading(false));\r\n }, [slug, shortId]);\r\n\r\n return { task, loading };\r\n}","import { useEffect, useState } from \"react\";\r\nimport { org, wsTypes } from \"..\";\r\nimport type { Comment } from \"../types\";\r\nimport { useSayrWS } from \"./useSayrWS\";\r\n\r\nexport function useComments(\r\n slug?: string,\r\n shortId?: number,\r\n wsUrl?: string\r\n) {\r\n const [comments, setComments] = useState<Comment[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n\r\n function fetchComments() {\r\n if (!slug || shortId == null) return;\r\n\r\n setLoading(true);\r\n org\r\n .comments(slug, shortId)\r\n .then((r) => setComments(r.data))\r\n .finally(() => setLoading(false));\r\n }\r\n\r\n useEffect(fetchComments, [slug, shortId]);\r\n\r\n useSayrWS(wsUrl, {\r\n [wsTypes.UPDATE_TASK_COMMENTS]: fetchComments\r\n });\r\n\r\n return { comments, loading, refetch: fetchComments };\r\n}"],"mappings":"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","names":["import_react","import_react","import_react","import_react"]}
@@ -87,6 +87,11 @@ var wsTypes = {
87
87
  DISCONNECTED: "DISCONNECTED"
88
88
  };
89
89
  function ws(url, handlers = {}) {
90
+ if (!url) {
91
+ throw new Error(
92
+ "[Sayr.ws] WebSocket URL is required. Did you forget to pass org.wsUrl?"
93
+ );
94
+ }
90
95
  let socket;
91
96
  let retry = 0;
92
97
  let closed = false;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/react/useOrg.ts","../../src/org.ts","../../src/ws.ts","../../src/react/useTasks.ts","../../src/react/useSayrWS.ts","../../src/react/useTask.ts","../../src/react/useComments.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\r\nimport { org, Organization } from \"../index\";\r\n\r\nexport function useOrg(slug?: string) {\r\n const [data, setData] = useState<Organization | null>(null);\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<unknown>(null);\r\n\r\n useEffect(() => {\r\n if (!slug) return;\r\n\r\n setLoading(true);\r\n org\r\n .get(slug)\r\n .then(setData)\r\n .catch(setError)\r\n .finally(() => setLoading(false));\r\n }, [slug]);\r\n\r\n return { data, loading, error };\r\n}","import {\r\n Organization,\r\n Label,\r\n Category,\r\n Task,\r\n Comment,\r\n Pagination,\r\n ApiSuccess\r\n} from \"./types\";\r\n\r\nconst API = \"https://sayr.io/api/public\";\r\n\r\nasync function get<T>(url: string): Promise<T> {\r\n const res = await fetch(url);\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n return json;\r\n}\r\n\r\nexport const org = {\r\n async get(slug: string): Promise<Organization> {\r\n const r = await get<ApiSuccess<Organization>>(\r\n `${API}/organization/${slug}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async labels(slug: string): Promise<Label[]> {\r\n const r = await get<ApiSuccess<Label[]>>(\r\n `${API}/organization/${slug}/labels`\r\n );\r\n return r.data;\r\n },\r\n\r\n async categories(\r\n slug: string,\r\n order: \"asc\" | \"desc\" = \"desc\"\r\n ): Promise<Category[]> {\r\n const r = await get<ApiSuccess<Category[]>>(\r\n `${API}/organization/${slug}/categories?order=${order}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async tasks(\r\n slug: string,\r\n opts?: { order?: \"asc\" | \"desc\"; limit?: number; page?: number }\r\n ): Promise<{ data: Task[]; pagination: Pagination }> {\r\n const q = new URLSearchParams({\r\n order: opts?.order ?? \"desc\",\r\n limit: String(opts?.limit ?? 5),\r\n page: String(opts?.page ?? 1)\r\n });\r\n\r\n const res = await fetch(\r\n `${API}/organization/${slug}/tasks?${q}`\r\n );\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n\r\n return {\r\n data: json.data,\r\n pagination: json.pagination\r\n };\r\n },\r\n\r\n async task(slug: string, shortId: number): Promise<Task> {\r\n const r = await get<ApiSuccess<Task>>(\r\n `${API}/organization/${slug}/tasks/${shortId}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async comments(\r\n slug: string,\r\n shortId: number,\r\n opts?: { order?: \"asc\" | \"desc\"; limit?: number; page?: number }\r\n ): Promise<{ data: Comment[]; pagination: Pagination }> {\r\n const q = new URLSearchParams({\r\n order: opts?.order ?? \"desc\",\r\n limit: String(opts?.limit ?? 5),\r\n page: String(opts?.page ?? 1)\r\n });\r\n\r\n const res = await fetch(\r\n `${API}/organization/${slug}/tasks/${shortId}/comments?${q}`\r\n );\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n\r\n return {\r\n data: json.data,\r\n pagination: json.pagination\r\n };\r\n }\r\n};","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\nexport const wsTypes: 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}\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 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 === wsTypes.PING) {\r\n socket.send(JSON.stringify({ type: wsTypes.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}","import { useEffect, useState } from \"react\";\r\nimport { org, wsTypes } from \"..\";\r\nimport type { Task } from \"../types\";\r\nimport { useSayrWS } from \"./useSayrWS\";\r\nexport function useTasks(\r\n slug?: string,\r\n wsUrl?: string\r\n) {\r\n const [tasks, setTasks] = useState<Task[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n\r\n function fetchTasks() {\r\n if (!slug) return;\r\n setLoading(true);\r\n org\r\n .tasks(slug)\r\n .then((r) => setTasks(r.data))\r\n .finally(() => setLoading(false));\r\n }\r\n\r\n useEffect(fetchTasks, [slug]);\r\n\r\n useSayrWS(wsUrl, {\r\n [wsTypes.CREATE_TASK]: fetchTasks,\r\n [wsTypes.UPDATE_TASK]: fetchTasks\r\n });\r\n\r\n return { tasks, loading, refetch: fetchTasks };\r\n}","import { useEffect, useRef } from \"react\";\r\nimport { ws } from \"..\";\r\nimport { WSMessageType } from \"../ws\";\r\n\r\ntype Handlers = Partial<\r\n Record<WSMessageType, (data: any, msg: any) => void>\r\n>;\r\n\r\nexport function useSayrWS(\r\n wsUrl?: string,\r\n handlers?: Handlers\r\n) {\r\n const connRef = useRef<ReturnType<typeof ws> | null>(null);\r\n\r\n useEffect(() => {\r\n if (!wsUrl) return;\r\n\r\n connRef.current = ws(wsUrl, handlers);\r\n\r\n return () => {\r\n connRef.current?.close();\r\n connRef.current = null;\r\n };\r\n }, [wsUrl]);\r\n\r\n return connRef;\r\n}","import { useEffect, useState } from \"react\";\r\nimport { org } from \"..\";\r\nimport type { Task } from \"../types\";\r\n\r\nexport function useTask(\r\n slug?: string,\r\n shortId?: number\r\n) {\r\n const [task, setTask] = useState<Task | null>(null);\r\n const [loading, setLoading] = useState(false);\r\n\r\n useEffect(() => {\r\n if (!slug || shortId == null) return;\r\n\r\n setLoading(true);\r\n org\r\n .task(slug, shortId)\r\n .then(setTask)\r\n .finally(() => setLoading(false));\r\n }, [slug, shortId]);\r\n\r\n return { task, loading };\r\n}","import { useEffect, useState } from \"react\";\r\nimport { org, wsTypes } from \"..\";\r\nimport type { Comment } from \"../types\";\r\nimport { useSayrWS } from \"./useSayrWS\";\r\n\r\nexport function useComments(\r\n slug?: string,\r\n shortId?: number,\r\n wsUrl?: string\r\n) {\r\n const [comments, setComments] = useState<Comment[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n\r\n function fetchComments() {\r\n if (!slug || shortId == null) return;\r\n\r\n setLoading(true);\r\n org\r\n .comments(slug, shortId)\r\n .then((r) => setComments(r.data))\r\n .finally(() => setLoading(false));\r\n }\r\n\r\n useEffect(fetchComments, [slug, shortId]);\r\n\r\n useSayrWS(wsUrl, {\r\n [wsTypes.UPDATE_TASK_COMMENTS]: fetchComments\r\n });\r\n\r\n return { comments, loading, refetch: fetchComments };\r\n}"],"mappings":"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","names":["useEffect","useState","useEffect","useEffect","useState","useEffect","useEffect","useState","useState","useEffect","useEffect","useState","useState","useEffect"]}
1
+ {"version":3,"sources":["../../src/react/useOrg.ts","../../src/org.ts","../../src/ws.ts","../../src/react/useTasks.ts","../../src/react/useSayrWS.ts","../../src/react/useTask.ts","../../src/react/useComments.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\r\nimport { org, Organization } from \"../index\";\r\n\r\nexport function useOrg(slug?: string) {\r\n const [data, setData] = useState<Organization | null>(null);\r\n const [loading, setLoading] = useState(false);\r\n const [error, setError] = useState<unknown>(null);\r\n\r\n useEffect(() => {\r\n if (!slug) return;\r\n\r\n setLoading(true);\r\n org\r\n .get(slug)\r\n .then(setData)\r\n .catch(setError)\r\n .finally(() => setLoading(false));\r\n }, [slug]);\r\n\r\n return { data, loading, error };\r\n}","import {\r\n Organization,\r\n Label,\r\n Category,\r\n Task,\r\n Comment,\r\n Pagination,\r\n ApiSuccess\r\n} from \"./types\";\r\n\r\nconst API = \"https://sayr.io/api/public\";\r\n\r\nasync function get<T>(url: string): Promise<T> {\r\n const res = await fetch(url);\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n return json;\r\n}\r\n\r\nexport const org = {\r\n async get(slug: string): Promise<Organization> {\r\n const r = await get<ApiSuccess<Organization>>(\r\n `${API}/organization/${slug}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async labels(slug: string): Promise<Label[]> {\r\n const r = await get<ApiSuccess<Label[]>>(\r\n `${API}/organization/${slug}/labels`\r\n );\r\n return r.data;\r\n },\r\n\r\n async categories(\r\n slug: string,\r\n order: \"asc\" | \"desc\" = \"desc\"\r\n ): Promise<Category[]> {\r\n const r = await get<ApiSuccess<Category[]>>(\r\n `${API}/organization/${slug}/categories?order=${order}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async tasks(\r\n slug: string,\r\n opts?: { order?: \"asc\" | \"desc\"; limit?: number; page?: number }\r\n ): Promise<{ data: Task[]; pagination: Pagination }> {\r\n const q = new URLSearchParams({\r\n order: opts?.order ?? \"desc\",\r\n limit: String(opts?.limit ?? 5),\r\n page: String(opts?.page ?? 1)\r\n });\r\n\r\n const res = await fetch(\r\n `${API}/organization/${slug}/tasks?${q}`\r\n );\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n\r\n return {\r\n data: json.data,\r\n pagination: json.pagination\r\n };\r\n },\r\n\r\n async task(slug: string, shortId: number): Promise<Task> {\r\n const r = await get<ApiSuccess<Task>>(\r\n `${API}/organization/${slug}/tasks/${shortId}`\r\n );\r\n return r.data;\r\n },\r\n\r\n async comments(\r\n slug: string,\r\n shortId: number,\r\n opts?: { order?: \"asc\" | \"desc\"; limit?: number; page?: number }\r\n ): Promise<{ data: Comment[]; pagination: Pagination }> {\r\n const q = new URLSearchParams({\r\n order: opts?.order ?? \"desc\",\r\n limit: String(opts?.limit ?? 5),\r\n page: String(opts?.page ?? 1)\r\n });\r\n\r\n const res = await fetch(\r\n `${API}/organization/${slug}/tasks/${shortId}/comments?${q}`\r\n );\r\n const json = await res.json();\r\n if (!json.success) throw json;\r\n\r\n return {\r\n data: json.data,\r\n pagination: json.pagination\r\n };\r\n }\r\n};","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\nexport const wsTypes: 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}\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 === wsTypes.PING) {\r\n socket.send(JSON.stringify({ type: wsTypes.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}","import { useEffect, useState } from \"react\";\r\nimport { org, wsTypes } from \"..\";\r\nimport type { Task } from \"../types\";\r\nimport { useSayrWS } from \"./useSayrWS\";\r\nexport function useTasks(\r\n slug?: string,\r\n wsUrl?: string\r\n) {\r\n const [tasks, setTasks] = useState<Task[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n\r\n function fetchTasks() {\r\n if (!slug) return;\r\n setLoading(true);\r\n org\r\n .tasks(slug)\r\n .then((r) => setTasks(r.data))\r\n .finally(() => setLoading(false));\r\n }\r\n\r\n useEffect(fetchTasks, [slug]);\r\n\r\n useSayrWS(wsUrl, {\r\n [wsTypes.CREATE_TASK]: fetchTasks,\r\n [wsTypes.UPDATE_TASK]: fetchTasks\r\n });\r\n\r\n return { tasks, loading, refetch: fetchTasks };\r\n}","import { useEffect, useRef } from \"react\";\r\nimport { ws } from \"..\";\r\nimport { WSMessageType } from \"../ws\";\r\n\r\ntype Handlers = Partial<\r\n Record<WSMessageType, (data: any, msg: any) => void>\r\n>;\r\n\r\nexport function useSayrWS(\r\n wsUrl?: string,\r\n handlers?: Handlers\r\n) {\r\n const connRef = useRef<ReturnType<typeof ws> | null>(null);\r\n\r\n useEffect(() => {\r\n if (!wsUrl) return;\r\n\r\n connRef.current = ws(wsUrl, handlers);\r\n\r\n return () => {\r\n connRef.current?.close();\r\n connRef.current = null;\r\n };\r\n }, [wsUrl]);\r\n\r\n return connRef;\r\n}","import { useEffect, useState } from \"react\";\r\nimport { org } from \"..\";\r\nimport type { Task } from \"../types\";\r\n\r\nexport function useTask(\r\n slug?: string,\r\n shortId?: number\r\n) {\r\n const [task, setTask] = useState<Task | null>(null);\r\n const [loading, setLoading] = useState(false);\r\n\r\n useEffect(() => {\r\n if (!slug || shortId == null) return;\r\n\r\n setLoading(true);\r\n org\r\n .task(slug, shortId)\r\n .then(setTask)\r\n .finally(() => setLoading(false));\r\n }, [slug, shortId]);\r\n\r\n return { task, loading };\r\n}","import { useEffect, useState } from \"react\";\r\nimport { org, wsTypes } from \"..\";\r\nimport type { Comment } from \"../types\";\r\nimport { useSayrWS } from \"./useSayrWS\";\r\n\r\nexport function useComments(\r\n slug?: string,\r\n shortId?: number,\r\n wsUrl?: string\r\n) {\r\n const [comments, setComments] = useState<Comment[]>([]);\r\n const [loading, setLoading] = useState(false);\r\n\r\n function fetchComments() {\r\n if (!slug || shortId == null) return;\r\n\r\n setLoading(true);\r\n org\r\n .comments(slug, shortId)\r\n .then((r) => setComments(r.data))\r\n .finally(() => setLoading(false));\r\n }\r\n\r\n useEffect(fetchComments, [slug, shortId]);\r\n\r\n useSayrWS(wsUrl, {\r\n [wsTypes.UPDATE_TASK_COMMENTS]: fetchComments\r\n });\r\n\r\n return { comments, loading, refetch: fetchComments };\r\n}"],"mappings":"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","names":["useEffect","useState","useEffect","useEffect","useState","useEffect","useEffect","useState","useState","useEffect","useEffect","useState","useState","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sayrio/public",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Sayr.io public REST + WebSocket SDK",
5
5
  "license": "MIT",
6
6
  "type": "module",