@sayrio/public 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,9 +6,10 @@ real‑time updates via WebSockets.
6
6
 
7
7
  - ✅ REST + WebSocket
8
8
  - ✅ Browser‑safe
9
- - ✅ TypeScript first
9
+ - ✅ TypeScriptfirst
10
10
  - ✅ Zero runtime dependencies
11
11
  - ✅ Versioned API (`v1`)
12
+ - ✅ Consistent `ApiResult<T>` responses
12
13
 
13
14
  > React hooks are available via the **`@sayrio/public/react`** sub‑path export.
14
15
 
@@ -28,73 +29,154 @@ pnpm add @sayrio/public
28
29
 
29
30
  ---
30
31
 
32
+ ## Core Concepts
33
+
34
+ ### ✅ `ApiResult<T>`
35
+
36
+ All SDK methods return a **non‑throwing** result object:
37
+
38
+ ```ts
39
+ interface ApiResult<T> {
40
+ success: boolean;
41
+ data: T | null;
42
+ error: string | null;
43
+ }
44
+ ```
45
+
46
+ Always check `success` before accessing `data`.
47
+
48
+ ---
49
+
31
50
  ## Usage
32
51
 
33
52
  ### Basic Usage (REST)
34
53
 
35
- Fetch public organization data.
54
+ Fetch a public organization.
36
55
 
37
- `Sayr.org` is an alias for the current API version (`v1`):
56
+ `Sayr.org` is an alias for the latest API version (`v1`).
38
57
 
39
58
  ```ts
40
59
  import Sayr from "@sayrio/public";
41
60
 
42
- const org = await Sayr.org.get("acme");
61
+ const res = await Sayr.org.get("acme");
43
62
 
44
- console.log(org.name);
63
+ if (!res.success) {
64
+ console.error(res.error);
65
+ return;
66
+ }
67
+
68
+ console.log(res.data.name);
45
69
  ```
46
70
 
47
71
  You can also use the versioned API explicitly:
48
72
 
49
73
  ```ts
50
- const org = await Sayr.v1.org.get("acme");
74
+ const res = await Sayr.v1.org.get("acme");
75
+ ```
76
+
77
+ ---
78
+
79
+ ## Organizations
80
+
81
+ ### Fetch an Organization
82
+
83
+ ```ts
84
+ const res = await Sayr.org.get("acme");
85
+
86
+ if (res.success) {
87
+ console.log(res.data);
88
+ }
51
89
  ```
52
90
 
53
91
  ---
54
92
 
55
- ### Listing Tasks
93
+ ## Tasks
56
94
 
57
- Retrieve tasks for an organization:
95
+ ### List Tasks (Paginated)
58
96
 
59
97
  ```ts
60
- const { data: tasks } = await Sayr.org.tasks.list("acme", {
98
+ const res = await Sayr.org.tasks.list("acme", {
61
99
  order: "desc",
62
- limit: 10
100
+ limit: 10,
101
+ });
102
+
103
+ if (!res.success) return;
104
+
105
+ res.data.items.forEach((task) => {
106
+ console.log(task.title);
63
107
  });
64
108
 
65
- console.log(tasks);
109
+ console.log(res.data.pagination);
110
+ ```
111
+
112
+ Returned shape:
113
+
114
+ ```ts
115
+ ApiResult<{
116
+ items: Task[];
117
+ pagination: Pagination;
118
+ }>
66
119
  ```
67
120
 
68
121
  ---
69
122
 
70
- ### Fetching a Single Task
123
+ ### Fetch a Single Task
71
124
 
72
125
  ```ts
73
- const task = await Sayr.org.tasks.get("acme", 42);
126
+ const res = await Sayr.org.tasks.get("acme", 42);
74
127
 
75
- console.log(task.title);
128
+ if (res.success) {
129
+ console.log(res.data.title);
130
+ }
76
131
  ```
77
132
 
78
133
  ---
79
134
 
80
- ### Task Comments
135
+ ## Comments
81
136
 
82
- Fetch comments for a specific task:
137
+ ### List Task Comments (Paginated)
83
138
 
84
139
  ```ts
85
- const { data: comments } =
86
- await Sayr.org.comments.list("acme", 42);
140
+ const res = await Sayr.org.comments.list("acme", 42);
141
+
142
+ if (!res.success) return;
143
+
144
+ res.data.items.forEach((comment) => {
145
+ console.log(comment.contentMarkdown);
146
+ });
147
+ ```
148
+
149
+ Returned shape:
87
150
 
88
- console.log(comments);
151
+ ```ts
152
+ ApiResult<{
153
+ items: Comment[];
154
+ pagination: Pagination;
155
+ }>
89
156
  ```
90
157
 
91
158
  ---
92
159
 
93
- ### Labels & Categories
160
+ ## Labels & Categories
161
+
162
+ ### Labels
163
+
164
+ ```ts
165
+ const res = await Sayr.org.labels.list("acme");
166
+
167
+ if (res.success) {
168
+ console.log(res.data);
169
+ }
170
+ ```
171
+
172
+ ### Categories
94
173
 
95
174
  ```ts
96
- const labels = await Sayr.org.labels.list("acme");
97
- const categories = await Sayr.org.categories.list("acme");
175
+ const res = await Sayr.org.categories.list("acme", "desc");
176
+
177
+ if (res.success) {
178
+ console.log(res.data);
179
+ }
98
180
  ```
99
181
 
100
182
  ---
@@ -104,19 +186,27 @@ const categories = await Sayr.org.categories.list("acme");
104
186
  The `/me` namespace provides **read‑only access** to the currently
105
187
  authenticated user.
106
188
 
107
- > Authentication is required.
189
+ > Authentication is required
108
190
  > Set a token using `Sayr.client.setToken(...)`.
109
191
 
110
192
  ---
111
193
 
112
- ### Fetch Current User
194
+ ### Set Token
113
195
 
114
196
  ```ts
115
- Sayr.client.setToken(token);
197
+ Sayr.client.setToken("********");
198
+ ```
199
+
200
+ ---
116
201
 
117
- const me = await Sayr.me.get();
202
+ ### Fetch Current User
203
+
204
+ ```ts
205
+ const res = await Sayr.me.get();
118
206
 
119
- console.log(me.email);
207
+ if (res.success) {
208
+ console.log(res.data.email);
209
+ }
120
210
  ```
121
211
 
122
212
  ---
@@ -124,9 +214,11 @@ console.log(me.email);
124
214
  ### List Your Organizations
125
215
 
126
216
  ```ts
127
- const orgs = await Sayr.me.organizations();
217
+ const res = await Sayr.me.organizations();
128
218
 
129
- console.log(orgs);
219
+ if (res.success) {
220
+ console.log(res.data);
221
+ }
130
222
  ```
131
223
 
132
224
  ---
@@ -137,9 +229,9 @@ Subscribe to public real‑time events using WebSockets:
137
229
 
138
230
  ```ts
139
231
  Sayr.ws(org.wsUrl, {
140
- [Sayr.WS_EVENTS.UPDATE_TASK]: (task) => {
141
- console.log("Task updated", task);
142
- }
232
+ [Sayr.WS_EVENTS.UPDATE_TASK]: (data) => {
233
+ console.log("Task updated", data);
234
+ },
143
235
  });
144
236
  ```
145
237
 
@@ -158,14 +250,17 @@ Sayr.ws(org.wsUrl, {
158
250
  <script type="module">
159
251
  import Sayr from "https://esm.sh/@sayrio/public";
160
252
 
161
- const org = await Sayr.org.get("acme");
162
- console.log(org);
253
+ const res = await Sayr.org.get("acme");
254
+
255
+ if (res.success) {
256
+ console.log(res.data);
257
+ }
163
258
  </script>
164
259
  ```
165
260
 
166
261
  ---
167
262
 
168
- ## API
263
+ ## API Overview
169
264
 
170
265
  ### `Sayr.org` (latest)
171
266
 
@@ -181,18 +276,18 @@ Alias for `Sayr.v1.org`.
181
276
 
182
277
  #### Tasks
183
278
 
184
- | Method | Description |
185
- | --------------------------- | ---------------------- |
186
- | `tasks.list(slug, opts?)` | List tasks (paginated) |
187
- | `tasks.get(slug, shortId)` | Fetch a single task |
279
+ | Method | Description |
280
+ | -------------------------- | ---------------------- |
281
+ | `tasks.list(slug, opts?)` | List tasks (paginated) |
282
+ | `tasks.get(slug, shortId)` | Fetch a single task |
188
283
 
189
284
  ---
190
285
 
191
286
  #### Comments
192
287
 
193
- | Method | Description |
194
- | ------------------------------------- | ------------------ |
195
- | `comments.list(slug, shortId, opts?)` | List task comments |
288
+ | Method | Description |
289
+ | ------------------------------------- | ------------------------ |
290
+ | `comments.list(slug, shortId, opts?)` | List task comments |
196
291
 
197
292
  ---
198
293
 
@@ -206,8 +301,8 @@ Alias for `Sayr.v1.org`.
206
301
 
207
302
  #### Categories
208
303
 
209
- | Method | Description |
210
- | ------------------------------ | --------------- |
304
+ | Method | Description |
305
+ | ------------------------------- | --------------- |
211
306
  | `categories.list(slug, order?)` | List categories |
212
307
 
213
308
  ---
@@ -216,10 +311,10 @@ Alias for `Sayr.v1.org`.
216
311
 
217
312
  Authenticated user endpoints.
218
313
 
219
- | Method | Description |
220
- | ------------------- | -------------------------------------- |
221
- | `get()` | Fetch the current authenticated user |
222
- | `organizations()` | List organizations the user belongs to |
314
+ | Method | Description |
315
+ | ----------------- | -------------------------------------- |
316
+ | `get()` | Fetch the authenticated user |
317
+ | `organizations()` | List organizations the user belongs to |
223
318
 
224
319
  ---
225
320
 
@@ -229,7 +324,7 @@ Create a WebSocket connection for public events:
229
324
 
230
325
  ```ts
231
326
  const conn = Sayr.ws(wsUrl, {
232
- UPDATE_TASK: () => {}
327
+ UPDATE_TASK: () => {},
233
328
  });
234
329
 
235
330
  conn.close();
@@ -242,8 +337,9 @@ conn.close();
242
337
  Typed WebSocket event constants:
243
338
 
244
339
  ```ts
340
+ Sayr.WS_EVENTS.CREATE_TASK;
245
341
  Sayr.WS_EVENTS.UPDATE_TASK;
246
- Sayr.WS_EVENTS.UPDATE_ORG;
342
+ Sayr.WS_EVENTS.UPDATE_TASK_COMMENTS;
247
343
  Sayr.WS_EVENTS.ERROR;
248
344
  ```
249
345
 
@@ -257,8 +353,27 @@ This package ships with full TypeScript definitions:
257
353
  import type {
258
354
  Organization,
259
355
  Task,
260
- Comment
356
+ Comment,
357
+ Label,
358
+ Category,
261
359
  } from "@sayrio/public";
262
360
  ```
263
361
 
362
+ ---
363
+
364
+ ## React Hooks
365
+
366
+ React bindings are available via:
367
+
368
+ ```ts
369
+ import {
370
+ useOrg,
371
+ useTasks,
372
+ useTask,
373
+ useComments,
374
+ } from "@sayrio/public/react";
375
+ ```
376
+
377
+ See **`@sayrio/public/react` README** for full hook documentation.
378
+
264
379
  ---
package/dist/index.cjs CHANGED
@@ -78,7 +78,7 @@ async function request(path, opts = {}) {
78
78
  if (method === "GET" && opts.body !== void 0) {
79
79
  throw new Error("GET request cannot have a body");
80
80
  }
81
- res = await fetch(url, {
81
+ res = await config.fetch(url, {
82
82
  method,
83
83
  headers: {
84
84
  ...config.token ? { Authorization: `Bearer ${config.token}` } : {},
@@ -139,11 +139,23 @@ var org_default = {
139
139
  * @since v1.0.0
140
140
  */
141
141
  async get(slug, opts) {
142
- const r = await request(
143
- `/v1/organization/${slug}`,
144
- opts
145
- );
146
- return r.data;
142
+ try {
143
+ const r = await request(
144
+ `/v1/organization/${slug}`,
145
+ opts
146
+ );
147
+ return {
148
+ success: true,
149
+ data: r.data,
150
+ error: null
151
+ };
152
+ } catch (err) {
153
+ return {
154
+ success: false,
155
+ data: null,
156
+ error: err?.message ?? "Failed to fetch organization"
157
+ };
158
+ }
147
159
  }
148
160
  };
149
161
 
@@ -164,15 +176,27 @@ var tasks_default = {
164
176
  * @since v1.0.0
165
177
  */
166
178
  async list(slug, params, opts) {
167
- const q = buildPaginationParams(params);
168
- const r = await request(
169
- `/v1/organization/${slug}/tasks?${q}`,
170
- opts
171
- );
172
- return {
173
- data: r.data,
174
- pagination: r.pagination
175
- };
179
+ try {
180
+ const q = buildPaginationParams(params);
181
+ const r = await request(
182
+ `/v1/organization/${slug}/tasks?${q}`,
183
+ opts
184
+ );
185
+ return {
186
+ success: true,
187
+ data: {
188
+ items: r.data,
189
+ pagination: r.pagination
190
+ },
191
+ error: null
192
+ };
193
+ } catch (err) {
194
+ return {
195
+ success: false,
196
+ data: null,
197
+ error: err?.message ?? "Failed to fetch tasks"
198
+ };
199
+ }
176
200
  },
177
201
  /**
178
202
  * Fetches a single public task by short ID.
@@ -180,11 +204,23 @@ var tasks_default = {
180
204
  * @since v1.0.0
181
205
  */
182
206
  async get(slug, shortId, opts) {
183
- const r = await request(
184
- `/v1/organization/${slug}/tasks/${shortId}`,
185
- opts
186
- );
187
- return r.data;
207
+ try {
208
+ const r = await request(
209
+ `/v1/organization/${slug}/tasks/${shortId}`,
210
+ opts
211
+ );
212
+ return {
213
+ success: true,
214
+ data: r.data,
215
+ error: null
216
+ };
217
+ } catch (err) {
218
+ return {
219
+ success: false,
220
+ data: null,
221
+ error: err?.message ?? "Failed to fetch task"
222
+ };
223
+ }
188
224
  }
189
225
  };
190
226
 
@@ -196,15 +232,27 @@ var comments_default = {
196
232
  * @since v1.0.0
197
233
  */
198
234
  async list(slug, shortId, params, opts) {
199
- const q = buildPaginationParams(params);
200
- const r = await request(
201
- `/v1/organization/${slug}/tasks/${shortId}/comments?${q}`,
202
- opts
203
- );
204
- return {
205
- data: r.data,
206
- pagination: r.pagination
207
- };
235
+ try {
236
+ const q = buildPaginationParams(params);
237
+ const r = await request(
238
+ `/v1/organization/${slug}/tasks/${shortId}/comments?${q}`,
239
+ opts
240
+ );
241
+ return {
242
+ success: true,
243
+ data: {
244
+ items: r.data,
245
+ pagination: r.pagination
246
+ },
247
+ error: null
248
+ };
249
+ } catch (err) {
250
+ return {
251
+ success: false,
252
+ data: null,
253
+ error: err?.message ?? "Failed to fetch task comments"
254
+ };
255
+ }
208
256
  }
209
257
  };
210
258
 
@@ -216,11 +264,23 @@ var labels_default = {
216
264
  * @since v1.0.0
217
265
  */
218
266
  async list(slug, opts) {
219
- const r = await request(
220
- `/v1/organization/${slug}/labels`,
221
- opts
222
- );
223
- return r.data;
267
+ try {
268
+ const r = await request(
269
+ `/v1/organization/${slug}/labels`,
270
+ opts
271
+ );
272
+ return {
273
+ success: true,
274
+ data: r.data,
275
+ error: null
276
+ };
277
+ } catch (err) {
278
+ return {
279
+ success: false,
280
+ data: null,
281
+ error: err?.message ?? "Failed to fetch labels"
282
+ };
283
+ }
224
284
  }
225
285
  };
226
286
 
@@ -232,11 +292,23 @@ var categories_default = {
232
292
  * @since v1.0.0
233
293
  */
234
294
  async list(slug, order = "desc", opts) {
235
- const r = await request(
236
- `/v1/organization/${slug}/categories?order=${order}`,
237
- opts
238
- );
239
- return r.data;
295
+ try {
296
+ const r = await request(
297
+ `/v1/organization/${slug}/categories?order=${order}`,
298
+ opts
299
+ );
300
+ return {
301
+ success: true,
302
+ data: r.data,
303
+ error: null
304
+ };
305
+ } catch (err) {
306
+ return {
307
+ success: false,
308
+ data: null,
309
+ error: err?.message ?? "Failed to fetch categories"
310
+ };
311
+ }
240
312
  }
241
313
  };
242
314
 
@@ -252,29 +324,40 @@ var org_default2 = OrgAPI;
252
324
 
253
325
  // src/api/v1/me/index.ts
254
326
  var me_default = {
255
- /**
256
- * Fetches the currently authenticated user.
257
- *
258
- * @since v1.0.0
259
- */
260
327
  async get(opts) {
261
- const r = await request(
262
- "/me",
263
- opts
264
- );
265
- return r.data;
328
+ try {
329
+ const r = await request("/v1/me", opts);
330
+ return {
331
+ success: true,
332
+ data: r.data,
333
+ error: null
334
+ };
335
+ } catch (err) {
336
+ return {
337
+ success: false,
338
+ data: null,
339
+ error: err?.message ?? "Failed to fetch user"
340
+ };
341
+ }
266
342
  },
267
- /**
268
- * Lists organizations the current user belongs to.
269
- *
270
- * @since v1.0.0
271
- */
272
343
  async organizations(opts) {
273
- const r = await request(
274
- "/organizations",
275
- opts
276
- );
277
- return r.data;
344
+ try {
345
+ const r = await request(
346
+ "/v1/me/organizations",
347
+ opts
348
+ );
349
+ return {
350
+ success: true,
351
+ data: r.data,
352
+ error: null
353
+ };
354
+ } catch (err) {
355
+ return {
356
+ success: false,
357
+ data: null,
358
+ error: err?.message ?? "Failed to fetch organizations"
359
+ };
360
+ }
278
361
  }
279
362
  };
280
363