@sayrio/public 0.0.5

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 ADDED
@@ -0,0 +1,264 @@
1
+ # @sayrio/public
2
+
3
+ Public JavaScript & TypeScript SDK for **Sayr.io**.
4
+ Provides **read‑only access** to Sayr organizations, tasks, comments, and
5
+ real‑time updates via WebSockets.
6
+
7
+ - ✅ REST + WebSocket
8
+ - ✅ Browser‑safe
9
+ - ✅ TypeScript first
10
+ - ✅ Zero runtime dependencies
11
+ - ✅ Versioned API (`v1`)
12
+
13
+ > React hooks are available via the **`@sayrio/public/react`** sub‑path export.
14
+
15
+ ---
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @sayrio/public
21
+ ```
22
+
23
+ or
24
+
25
+ ```bash
26
+ pnpm add @sayrio/public
27
+ ```
28
+
29
+ ---
30
+
31
+ ## Usage
32
+
33
+ ### Basic Usage (REST)
34
+
35
+ Fetch public organization data.
36
+
37
+ `Sayr.org` is an alias for the current API version (`v1`):
38
+
39
+ ```ts
40
+ import Sayr from "@sayrio/public";
41
+
42
+ const org = await Sayr.org.get("acme");
43
+
44
+ console.log(org.name);
45
+ ```
46
+
47
+ You can also use the versioned API explicitly:
48
+
49
+ ```ts
50
+ const org = await Sayr.v1.org.get("acme");
51
+ ```
52
+
53
+ ---
54
+
55
+ ### Listing Tasks
56
+
57
+ Retrieve tasks for an organization:
58
+
59
+ ```ts
60
+ const { data: tasks } = await Sayr.org.tasks.list("acme", {
61
+ order: "desc",
62
+ limit: 10
63
+ });
64
+
65
+ console.log(tasks);
66
+ ```
67
+
68
+ ---
69
+
70
+ ### Fetching a Single Task
71
+
72
+ ```ts
73
+ const task = await Sayr.org.tasks.get("acme", 42);
74
+
75
+ console.log(task.title);
76
+ ```
77
+
78
+ ---
79
+
80
+ ### Task Comments
81
+
82
+ Fetch comments for a specific task:
83
+
84
+ ```ts
85
+ const { data: comments } =
86
+ await Sayr.org.comments.list("acme", 42);
87
+
88
+ console.log(comments);
89
+ ```
90
+
91
+ ---
92
+
93
+ ### Labels & Categories
94
+
95
+ ```ts
96
+ const labels = await Sayr.org.labels.list("acme");
97
+ const categories = await Sayr.org.categories.list("acme");
98
+ ```
99
+
100
+ ---
101
+
102
+ ## Authenticated User (`/me`)
103
+
104
+ The `/me` namespace provides **read‑only access** to the currently
105
+ authenticated user.
106
+
107
+ > Authentication is required.
108
+ > Set a token using `Sayr.client.setToken(...)`.
109
+
110
+ ---
111
+
112
+ ### Fetch Current User
113
+
114
+ ```ts
115
+ Sayr.client.setToken(token);
116
+
117
+ const me = await Sayr.me.get();
118
+
119
+ console.log(me.email);
120
+ ```
121
+
122
+ ---
123
+
124
+ ### List Your Organizations
125
+
126
+ ```ts
127
+ const orgs = await Sayr.me.organizations();
128
+
129
+ console.log(orgs);
130
+ ```
131
+
132
+ ---
133
+
134
+ ## Real‑Time Updates (WebSocket)
135
+
136
+ Subscribe to public real‑time events using WebSockets:
137
+
138
+ ```ts
139
+ Sayr.ws(org.wsUrl, {
140
+ [Sayr.WS_EVENTS.UPDATE_TASK]: (task) => {
141
+ console.log("Task updated", task);
142
+ }
143
+ });
144
+ ```
145
+
146
+ ### WebSocket Features
147
+
148
+ - Automatic reconnection
149
+ - Heartbeat support (PING / PONG)
150
+ - Typed event constants
151
+ - Public‑safe payloads only
152
+
153
+ ---
154
+
155
+ ## Browser Usage (No Bundler)
156
+
157
+ ```html
158
+ <script type="module">
159
+ import Sayr from "https://esm.sh/@sayrio/public";
160
+
161
+ const org = await Sayr.org.get("acme");
162
+ console.log(org);
163
+ </script>
164
+ ```
165
+
166
+ ---
167
+
168
+ ## API
169
+
170
+ ### `Sayr.org` (latest)
171
+
172
+ Alias for `Sayr.v1.org`.
173
+
174
+ #### Organization
175
+
176
+ | Method | Description |
177
+ | ----------- | --------------------------- |
178
+ | `get(slug)` | Fetch a public organization |
179
+
180
+ ---
181
+
182
+ #### Tasks
183
+
184
+ | Method | Description |
185
+ | --------------------------- | ---------------------- |
186
+ | `tasks.list(slug, opts?)` | List tasks (paginated) |
187
+ | `tasks.get(slug, shortId)` | Fetch a single task |
188
+
189
+ ---
190
+
191
+ #### Comments
192
+
193
+ | Method | Description |
194
+ | ------------------------------------- | ------------------ |
195
+ | `comments.list(slug, shortId, opts?)` | List task comments |
196
+
197
+ ---
198
+
199
+ #### Labels
200
+
201
+ | Method | Description |
202
+ | ------------------- | ------------------------ |
203
+ | `labels.list(slug)` | List organization labels |
204
+
205
+ ---
206
+
207
+ #### Categories
208
+
209
+ | Method | Description |
210
+ | ------------------------------ | --------------- |
211
+ | `categories.list(slug, order?)` | List categories |
212
+
213
+ ---
214
+
215
+ ### `Sayr.me`
216
+
217
+ Authenticated user endpoints.
218
+
219
+ | Method | Description |
220
+ | ------------------- | -------------------------------------- |
221
+ | `get()` | Fetch the current authenticated user |
222
+ | `organizations()` | List organizations the user belongs to |
223
+
224
+ ---
225
+
226
+ ### `Sayr.ws(url, handlers)`
227
+
228
+ Create a WebSocket connection for public events:
229
+
230
+ ```ts
231
+ const conn = Sayr.ws(wsUrl, {
232
+ UPDATE_TASK: () => {}
233
+ });
234
+
235
+ conn.close();
236
+ ```
237
+
238
+ ---
239
+
240
+ ### `WS_EVENTS`
241
+
242
+ Typed WebSocket event constants:
243
+
244
+ ```ts
245
+ Sayr.WS_EVENTS.UPDATE_TASK;
246
+ Sayr.WS_EVENTS.UPDATE_ORG;
247
+ Sayr.WS_EVENTS.ERROR;
248
+ ```
249
+
250
+ ---
251
+
252
+ ## TypeScript
253
+
254
+ This package ships with full TypeScript definitions:
255
+
256
+ ```ts
257
+ import type {
258
+ Organization,
259
+ Task,
260
+ Comment
261
+ } from "@sayrio/public";
262
+ ```
263
+
264
+ ---
package/dist/index.cjs ADDED
@@ -0,0 +1,356 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ SayrClient: () => SayrClient,
24
+ SayrV1: () => SayrV1,
25
+ SayrWS: () => SayrWS,
26
+ SayrWSEvents: () => SayrWSEvents,
27
+ WS_EVENTS: () => WS_EVENTS,
28
+ buildPaginationParams: () => buildPaginationParams,
29
+ default: () => index_default
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/client/index.ts
34
+ var DEFAULT_API = "https://api.sayr.io";
35
+ var config = {
36
+ fetch: globalThis.fetch,
37
+ baseUrl: DEFAULT_API
38
+ };
39
+ var hooks = {};
40
+ function setToken(token) {
41
+ config.token = token;
42
+ }
43
+ function setHeaders(headers) {
44
+ config.headers = {
45
+ ...config.headers,
46
+ ...headers
47
+ };
48
+ }
49
+ function setFetch(fn) {
50
+ config.fetch = fn;
51
+ }
52
+ function setBaseUrl(url) {
53
+ config.baseUrl = url.replace(/\/$/, "");
54
+ }
55
+ function setHooks(h) {
56
+ Object.assign(hooks, h);
57
+ }
58
+ function resetClient() {
59
+ config.token = void 0;
60
+ config.headers = void 0;
61
+ config.baseUrl = DEFAULT_API;
62
+ }
63
+ async function request(path, opts = {}) {
64
+ const url = path.startsWith("http") ? path : `${config.baseUrl}${path}`;
65
+ hooks.onRequest?.(url, opts);
66
+ let res;
67
+ try {
68
+ res = await config.fetch(url, {
69
+ method: opts.method ?? "GET",
70
+ headers: {
71
+ ...config.token ? { Authorization: `Bearer ${config.token}` } : {},
72
+ ...opts.body ? { "Content-Type": "application/json" } : {},
73
+ ...config.headers,
74
+ ...opts.headers
75
+ },
76
+ body: opts.body ? JSON.stringify(opts.body) : void 0,
77
+ signal: opts.signal
78
+ });
79
+ } catch (err) {
80
+ const error = {
81
+ success: false,
82
+ error: "NETWORK_ERROR",
83
+ message: "Failed to reach Sayr API"
84
+ };
85
+ hooks.onError?.(error);
86
+ throw error;
87
+ }
88
+ hooks.onResponse?.(res);
89
+ let json;
90
+ try {
91
+ json = await res.json();
92
+ } catch {
93
+ const error = {
94
+ success: false,
95
+ error: "INVALID_RESPONSE",
96
+ message: "Server returned invalid JSON",
97
+ status: res.status
98
+ };
99
+ hooks.onError?.(error);
100
+ throw error;
101
+ }
102
+ if (!res.ok || !json.success) {
103
+ const error = {
104
+ ...json,
105
+ success: false,
106
+ status: res.status
107
+ };
108
+ hooks.onError?.(error);
109
+ throw error;
110
+ }
111
+ return json;
112
+ }
113
+
114
+ // src/api/v1/org/org.ts
115
+ var org_default = {
116
+ /**
117
+ * Fetches a public organization by slug.
118
+ *
119
+ * @since v1.0.0
120
+ */
121
+ async get(slug, opts) {
122
+ const r = await request(
123
+ `/v1/organization/${slug}`,
124
+ opts
125
+ );
126
+ return r.data;
127
+ }
128
+ };
129
+
130
+ // src/shared/index.ts
131
+ function buildPaginationParams(params) {
132
+ return new URLSearchParams({
133
+ order: params?.order ?? "desc",
134
+ limit: String(params?.limit ?? 5),
135
+ page: String(params?.page ?? 1)
136
+ });
137
+ }
138
+
139
+ // src/api/v1/org/tasks.ts
140
+ var tasks_default = {
141
+ /**
142
+ * Lists public tasks for an organization.
143
+ *
144
+ * @since v1.0.0
145
+ */
146
+ async list(slug, params, opts) {
147
+ const q = buildPaginationParams(params);
148
+ const r = await request(
149
+ `/v1/organization/${slug}/tasks?${q}`,
150
+ opts
151
+ );
152
+ return {
153
+ data: r.data,
154
+ pagination: r.pagination
155
+ };
156
+ },
157
+ /**
158
+ * Fetches a single public task by short ID.
159
+ *
160
+ * @since v1.0.0
161
+ */
162
+ async get(slug, shortId, opts) {
163
+ const r = await request(
164
+ `/v1/organization/${slug}/tasks/${shortId}`,
165
+ opts
166
+ );
167
+ return r.data;
168
+ }
169
+ };
170
+
171
+ // src/api/v1/org/comments.ts
172
+ var comments_default = {
173
+ /**
174
+ * Lists public comments for a task.
175
+ *
176
+ * @since v1.0.0
177
+ */
178
+ async list(slug, shortId, params, opts) {
179
+ const q = buildPaginationParams(params);
180
+ const r = await request(
181
+ `/v1/organization/${slug}/tasks/${shortId}/comments?${q}`,
182
+ opts
183
+ );
184
+ return {
185
+ data: r.data,
186
+ pagination: r.pagination
187
+ };
188
+ }
189
+ };
190
+
191
+ // src/api/v1/org/labels.ts
192
+ var labels_default = {
193
+ /**
194
+ * Lists public labels for an organization.
195
+ *
196
+ * @since v1.0.0
197
+ */
198
+ async list(slug, opts) {
199
+ const r = await request(
200
+ `/v1/organization/${slug}/labels`,
201
+ opts
202
+ );
203
+ return r.data;
204
+ }
205
+ };
206
+
207
+ // src/api/v1/org/categories.ts
208
+ var categories_default = {
209
+ /**
210
+ * Lists public categories for an organization.
211
+ *
212
+ * @since v1.0.0
213
+ */
214
+ async list(slug, order = "desc", opts) {
215
+ const r = await request(
216
+ `/v1/organization/${slug}/categories?order=${order}`,
217
+ opts
218
+ );
219
+ return r.data;
220
+ }
221
+ };
222
+
223
+ // src/api/v1/org/index.ts
224
+ var OrgAPI = {
225
+ ...org_default,
226
+ tasks: tasks_default,
227
+ comments: comments_default,
228
+ labels: labels_default,
229
+ categories: categories_default
230
+ };
231
+ var org_default2 = OrgAPI;
232
+
233
+ // src/api/v1/me/index.ts
234
+ var me_default = {
235
+ /**
236
+ * Fetches the currently authenticated user.
237
+ *
238
+ * @since v1.0.0
239
+ */
240
+ async get(opts) {
241
+ const r = await request(
242
+ "/me",
243
+ opts
244
+ );
245
+ return r.data;
246
+ },
247
+ /**
248
+ * Lists organizations the current user belongs to.
249
+ *
250
+ * @since v1.0.0
251
+ */
252
+ async organizations(opts) {
253
+ const r = await request(
254
+ "/organizations",
255
+ opts
256
+ );
257
+ return r.data;
258
+ }
259
+ };
260
+
261
+ // src/api/v1/index.ts
262
+ var v1 = {
263
+ org: org_default2,
264
+ me: me_default
265
+ };
266
+ var v1_default = v1;
267
+
268
+ // src/ws/types.ts
269
+ var WS_EVENTS = {
270
+ CONNECTION_STATUS: "CONNECTION_STATUS",
271
+ SUBSCRIBED: "SUBSCRIBED",
272
+ ERROR: "ERROR",
273
+ PING: "PING",
274
+ PONG: "PONG",
275
+ UPDATE_ORG: "UPDATE_ORG",
276
+ CREATE_TASK: "CREATE_TASK",
277
+ UPDATE_TASK: "UPDATE_TASK",
278
+ UPDATE_TASK_COMMENTS: "UPDATE_TASK_COMMENTS",
279
+ UPDATE_TASK_VOTE: "UPDATE_TASK_VOTE",
280
+ UPDATE_LABELS: "UPDATE_LABELS",
281
+ UPDATE_VIEWS: "UPDATE_VIEWS",
282
+ UPDATE_CATEGORIES: "UPDATE_CATEGORIES",
283
+ UPDATE_ISSUE_TEMPLATES: "UPDATE_ISSUE_TEMPLATES",
284
+ DISCONNECTED: "DISCONNECTED"
285
+ };
286
+
287
+ // src/ws/index.ts
288
+ function ws(url, handlers = {}) {
289
+ if (!url) {
290
+ throw new Error(
291
+ "[Sayr.ws] WebSocket URL is required. Did you forget to pass org.wsUrl?"
292
+ );
293
+ }
294
+ let socket;
295
+ let retry = 0;
296
+ let closed = false;
297
+ function connect() {
298
+ if (closed) return;
299
+ socket = new WebSocket(url);
300
+ socket.onmessage = (e) => {
301
+ const msg = JSON.parse(e.data);
302
+ if (msg.type === WS_EVENTS.PING) {
303
+ socket.send(JSON.stringify({ type: WS_EVENTS.PONG }));
304
+ return;
305
+ }
306
+ handlers[msg.type]?.(msg.data, msg);
307
+ };
308
+ socket.onclose = () => {
309
+ if (closed) return;
310
+ setTimeout(connect, Math.min(1e3 * 2 ** retry++, 3e4));
311
+ };
312
+ socket.onerror = () => socket.close();
313
+ }
314
+ connect();
315
+ return {
316
+ close() {
317
+ closed = true;
318
+ socket?.close();
319
+ }
320
+ };
321
+ }
322
+
323
+ // src/index.ts
324
+ var SayrV1 = v1_default;
325
+ var SayrWS = ws;
326
+ var SayrWSEvents = WS_EVENTS;
327
+ var SayrClient = {
328
+ setToken,
329
+ setHeaders,
330
+ setBaseUrl,
331
+ resetClient,
332
+ setHooks,
333
+ setFetch
334
+ };
335
+ var Sayr = {
336
+ // client configuration
337
+ client: SayrClient,
338
+ // APIs
339
+ v1: v1_default,
340
+ org: v1_default.org,
341
+ me: v1_default.me,
342
+ // realtime
343
+ ws,
344
+ WS_EVENTS
345
+ };
346
+ var index_default = Sayr;
347
+ // Annotate the CommonJS export names for ESM import in node:
348
+ 0 && (module.exports = {
349
+ SayrClient,
350
+ SayrV1,
351
+ SayrWS,
352
+ SayrWSEvents,
353
+ WS_EVENTS,
354
+ buildPaginationParams
355
+ });
356
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../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"],"sourcesContent":["/* ────────────────────────────\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\";","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\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\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 const url = path.startsWith(\"http\")\r\n ? path\r\n : `${config.baseUrl}${path}`;\r\n\r\n hooks.onRequest?.(url, opts);\r\n\r\n let res: Response;\r\n\r\n try {\r\n res = await config.fetch(url, {\r\n method: opts.method ?? \"GET\",\r\n headers: {\r\n ...(config.token\r\n ? { Authorization: `Bearer ${config.token}` }\r\n : {}),\r\n ...(opts.body\r\n ? { \"Content-Type\": \"application/json\" }\r\n : {}),\r\n ...config.headers,\r\n ...opts.headers\r\n },\r\n body: opts.body\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 };\r\n hooks.onError?.(error);\r\n throw error;\r\n }\r\n\r\n hooks.onResponse?.(res);\r\n\r\n let json: any;\r\n try {\r\n json = await res.json();\r\n } catch {\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\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\r\n return json;\r\n}","import { Organization, ApiSuccess } from \"../../../types\";\r\nimport { 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<Organization> {\r\n const r = await request<ApiSuccess<Organization>>(\r\n `/v1/organization/${slug}`,\r\n opts\r\n );\r\n return r.data;\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 { request, type RequestOptions } 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<{ data: Task[]; pagination: Pagination }> {\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 data: r.data,\r\n pagination: r.pagination\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<Task> {\r\n const r = await request<ApiSuccess<Task>>(\r\n `/v1/organization/${slug}/tasks/${shortId}`,\r\n opts\r\n );\r\n return r.data;\r\n }\r\n};","import {\r\n Comment,\r\n Pagination,\r\n ApiSuccess\r\n} from \"../../../types\";\r\nimport { request, type RequestOptions } 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<{ data: Comment[]; pagination: Pagination }> {\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 data: r.data,\r\n pagination: r.pagination\r\n };\r\n }\r\n};","import { Label, ApiSuccess } from \"../../../types\";\r\nimport { 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<Label[]> {\r\n const r = await request<ApiSuccess<Label[]>>(\r\n `/v1/organization/${slug}/labels`,\r\n opts\r\n );\r\n return r.data;\r\n }\r\n};","import {\r\n Category,\r\n ApiSuccess\r\n} from \"../../../types\";\r\nimport { request, type RequestOptions } 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<Category[]> {\r\n const r = await request<ApiSuccess<Category[]>>(\r\n `/v1/organization/${slug}/categories?order=${order}`,\r\n opts\r\n );\r\n return r.data;\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 { 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 /**\r\n * Fetches the currently authenticated user.\r\n *\r\n * @since v1.0.0\r\n */\r\n async get(opts?: RequestOptions): Promise<Me> {\r\n const r = await request<ApiSuccess<Me>>(\r\n \"/me\",\r\n opts\r\n );\r\n return r.data;\r\n },\r\n\r\n /**\r\n * Lists organizations the current user belongs to.\r\n *\r\n * @since v1.0.0\r\n */\r\n async organizations(\r\n opts?: RequestOptions\r\n ): Promise<Organization[]> {\r\n const r = await request<ApiSuccess<Organization[]>>(\r\n \"/organizations\",\r\n opts\r\n );\r\n return r.data;\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}"],"mappings":"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","names":["org_default","org_default"]}