@sayrio/public 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,154 @@
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
+ default: () => index_default,
24
+ org: () => org,
25
+ ws: () => ws,
26
+ wsTypes: () => wsTypes
27
+ });
28
+ module.exports = __toCommonJS(index_exports);
29
+
30
+ // src/org.ts
31
+ var API = "https://sayr.io/api/public";
32
+ async function get(url) {
33
+ const res = await fetch(url);
34
+ const json = await res.json();
35
+ if (!json.success) throw json;
36
+ return json;
37
+ }
38
+ var org = {
39
+ async get(slug) {
40
+ const r = await get(
41
+ `${API}/organization/${slug}`
42
+ );
43
+ return r.data;
44
+ },
45
+ async labels(slug) {
46
+ const r = await get(
47
+ `${API}/organization/${slug}/labels`
48
+ );
49
+ return r.data;
50
+ },
51
+ async categories(slug, order = "desc") {
52
+ const r = await get(
53
+ `${API}/organization/${slug}/categories?order=${order}`
54
+ );
55
+ return r.data;
56
+ },
57
+ async tasks(slug, opts) {
58
+ const q = new URLSearchParams({
59
+ order: opts?.order ?? "desc",
60
+ limit: String(opts?.limit ?? 5),
61
+ page: String(opts?.page ?? 1)
62
+ });
63
+ const res = await fetch(
64
+ `${API}/organization/${slug}/tasks?${q}`
65
+ );
66
+ const json = await res.json();
67
+ if (!json.success) throw json;
68
+ return {
69
+ data: json.data,
70
+ pagination: json.pagination
71
+ };
72
+ },
73
+ async task(slug, shortId) {
74
+ const r = await get(
75
+ `${API}/organization/${slug}/tasks/${shortId}`
76
+ );
77
+ return r.data;
78
+ },
79
+ async comments(slug, shortId, opts) {
80
+ const q = new URLSearchParams({
81
+ order: opts?.order ?? "desc",
82
+ limit: String(opts?.limit ?? 5),
83
+ page: String(opts?.page ?? 1)
84
+ });
85
+ const res = await fetch(
86
+ `${API}/organization/${slug}/tasks/${shortId}/comments?${q}`
87
+ );
88
+ const json = await res.json();
89
+ if (!json.success) throw json;
90
+ return {
91
+ data: json.data,
92
+ pagination: json.pagination
93
+ };
94
+ }
95
+ };
96
+
97
+ // src/ws.ts
98
+ var wsTypes = {
99
+ CONNECTION_STATUS: "CONNECTION_STATUS",
100
+ SUBSCRIBED: "SUBSCRIBED",
101
+ ERROR: "ERROR",
102
+ PING: "PING",
103
+ PONG: "PONG",
104
+ UPDATE_ORG: "UPDATE_ORG",
105
+ CREATE_TASK: "CREATE_TASK",
106
+ UPDATE_TASK: "UPDATE_TASK",
107
+ UPDATE_TASK_COMMENTS: "UPDATE_TASK_COMMENTS",
108
+ UPDATE_TASK_VOTE: "UPDATE_TASK_VOTE",
109
+ UPDATE_LABELS: "UPDATE_LABELS",
110
+ UPDATE_VIEWS: "UPDATE_VIEWS",
111
+ UPDATE_CATEGORIES: "UPDATE_CATEGORIES",
112
+ UPDATE_ISSUE_TEMPLATES: "UPDATE_ISSUE_TEMPLATES",
113
+ DISCONNECTED: "DISCONNECTED"
114
+ };
115
+ function ws(url, handlers = {}) {
116
+ let socket;
117
+ let retry = 0;
118
+ let closed = false;
119
+ function connect() {
120
+ if (closed) return;
121
+ socket = new WebSocket(url);
122
+ socket.onmessage = (e) => {
123
+ const msg = JSON.parse(e.data);
124
+ if (msg.type === wsTypes.PING) {
125
+ socket.send(JSON.stringify({ type: wsTypes.PONG }));
126
+ return;
127
+ }
128
+ handlers[msg.type]?.(msg.data, msg);
129
+ };
130
+ socket.onclose = () => {
131
+ if (closed) return;
132
+ setTimeout(connect, Math.min(1e3 * 2 ** retry++, 3e4));
133
+ };
134
+ socket.onerror = () => socket.close();
135
+ }
136
+ connect();
137
+ return {
138
+ close() {
139
+ closed = true;
140
+ socket?.close();
141
+ }
142
+ };
143
+ }
144
+
145
+ // src/index.ts
146
+ var Sayr = { org, ws, wsTypes };
147
+ var index_default = Sayr;
148
+ // Annotate the CommonJS export names for ESM import in node:
149
+ 0 && (module.exports = {
150
+ org,
151
+ ws,
152
+ wsTypes
153
+ });
154
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +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":[]}
@@ -0,0 +1,152 @@
1
+ type WSMessageType = "CONNECTION_STATUS" | "SUBSCRIBED" | "ERROR" | "PING" | "PONG" | "UPDATE_ORG" | "CREATE_TASK" | "UPDATE_TASK" | "UPDATE_TASK_COMMENTS" | "UPDATE_TASK_VOTE" | "UPDATE_LABELS" | "UPDATE_VIEWS" | "UPDATE_CATEGORIES" | "UPDATE_ISSUE_TEMPLATES" | "DISCONNECTED";
2
+ declare const wsTypes: Record<WSMessageType, WSMessageType>;
3
+ interface WSMessage<T = unknown> {
4
+ type: WSMessageType;
5
+ scope: "PUBLIC";
6
+ data: T;
7
+ meta?: {
8
+ ts: number;
9
+ };
10
+ }
11
+ type Handlers = Partial<Record<WSMessageType, (data: any, msg: WSMessage) => void>>;
12
+ declare function ws(url: string, handlers?: Handlers): {
13
+ close(): void;
14
+ };
15
+
16
+ interface ApiSuccess<T> {
17
+ success: true;
18
+ data: T;
19
+ }
20
+ interface ApiError {
21
+ success: false;
22
+ error: string;
23
+ message?: string;
24
+ }
25
+ interface Pagination {
26
+ limit: number;
27
+ page: number;
28
+ totalPages: number;
29
+ totalItems: number;
30
+ hasMore: boolean;
31
+ }
32
+ interface Organization {
33
+ id: string;
34
+ name: string;
35
+ slug: string;
36
+ logo: string | null;
37
+ bannerImg: string | null;
38
+ description: string;
39
+ createdAt: string;
40
+ updatedAt: string;
41
+ wsUrl: string;
42
+ }
43
+ interface Label {
44
+ id: string;
45
+ organizationId: string;
46
+ name: string;
47
+ color: string | null;
48
+ createdAt: string;
49
+ }
50
+ interface Category {
51
+ id: string;
52
+ organizationId: string;
53
+ name: string;
54
+ color: string | null;
55
+ icon: string | null;
56
+ createdAt: string;
57
+ }
58
+ type TaskStatus = "backlog" | "todo" | "in-progress" | "done" | "canceled";
59
+ type TaskPriority = "none" | "low" | "medium" | "high" | "urgent";
60
+ interface Task {
61
+ id: string;
62
+ organizationId: string;
63
+ shortId: number | null;
64
+ visible: "public" | "private";
65
+ createdAt: string;
66
+ updatedAt: string;
67
+ title: string | null;
68
+ description: unknown | null;
69
+ status: TaskStatus;
70
+ priority: TaskPriority;
71
+ createdBy: string | null;
72
+ category: string | null;
73
+ voteCount: number;
74
+ descriptionHtml: string;
75
+ descriptionMarkdown: string;
76
+ }
77
+ interface CommentUser {
78
+ name: string | null;
79
+ image: string | null;
80
+ }
81
+ interface CommentReaction {
82
+ count: number;
83
+ users: string[];
84
+ }
85
+ interface Comment {
86
+ id: string;
87
+ organizationId: string;
88
+ taskId: string | null;
89
+ createdAt: string;
90
+ updatedAt: string;
91
+ content: unknown | null;
92
+ visibility: "public" | "internal";
93
+ contentHtml: string;
94
+ contentMarkdown: string;
95
+ createdBy: CommentUser | null;
96
+ reactions?: {
97
+ total: number;
98
+ reactions: Record<string, CommentReaction>;
99
+ };
100
+ }
101
+
102
+ declare const org: {
103
+ get(slug: string): Promise<Organization>;
104
+ labels(slug: string): Promise<Label[]>;
105
+ categories(slug: string, order?: "asc" | "desc"): Promise<Category[]>;
106
+ tasks(slug: string, opts?: {
107
+ order?: "asc" | "desc";
108
+ limit?: number;
109
+ page?: number;
110
+ }): Promise<{
111
+ data: Task[];
112
+ pagination: Pagination;
113
+ }>;
114
+ task(slug: string, shortId: number): Promise<Task>;
115
+ comments(slug: string, shortId: number, opts?: {
116
+ order?: "asc" | "desc";
117
+ limit?: number;
118
+ page?: number;
119
+ }): Promise<{
120
+ data: Comment[];
121
+ pagination: Pagination;
122
+ }>;
123
+ };
124
+
125
+ declare const Sayr: {
126
+ org: {
127
+ get(slug: string): Promise<Organization>;
128
+ labels(slug: string): Promise<Label[]>;
129
+ categories(slug: string, order?: "asc" | "desc"): Promise<Category[]>;
130
+ tasks(slug: string, opts?: {
131
+ order?: "asc" | "desc";
132
+ limit?: number;
133
+ page?: number;
134
+ }): Promise<{
135
+ data: Task[];
136
+ pagination: Pagination;
137
+ }>;
138
+ task(slug: string, shortId: number): Promise<Task>;
139
+ comments(slug: string, shortId: number, opts?: {
140
+ order?: "asc" | "desc";
141
+ limit?: number;
142
+ page?: number;
143
+ }): Promise<{
144
+ data: Comment[];
145
+ pagination: Pagination;
146
+ }>;
147
+ };
148
+ ws: typeof ws;
149
+ wsTypes: Record<WSMessageType, WSMessageType>;
150
+ };
151
+
152
+ export { type ApiError, type ApiSuccess, type Category, type Comment, type CommentReaction, type CommentUser, type Label, type Organization, type Pagination, type Task, type TaskPriority, type TaskStatus, Sayr as default, org, ws, wsTypes };
@@ -0,0 +1,152 @@
1
+ type WSMessageType = "CONNECTION_STATUS" | "SUBSCRIBED" | "ERROR" | "PING" | "PONG" | "UPDATE_ORG" | "CREATE_TASK" | "UPDATE_TASK" | "UPDATE_TASK_COMMENTS" | "UPDATE_TASK_VOTE" | "UPDATE_LABELS" | "UPDATE_VIEWS" | "UPDATE_CATEGORIES" | "UPDATE_ISSUE_TEMPLATES" | "DISCONNECTED";
2
+ declare const wsTypes: Record<WSMessageType, WSMessageType>;
3
+ interface WSMessage<T = unknown> {
4
+ type: WSMessageType;
5
+ scope: "PUBLIC";
6
+ data: T;
7
+ meta?: {
8
+ ts: number;
9
+ };
10
+ }
11
+ type Handlers = Partial<Record<WSMessageType, (data: any, msg: WSMessage) => void>>;
12
+ declare function ws(url: string, handlers?: Handlers): {
13
+ close(): void;
14
+ };
15
+
16
+ interface ApiSuccess<T> {
17
+ success: true;
18
+ data: T;
19
+ }
20
+ interface ApiError {
21
+ success: false;
22
+ error: string;
23
+ message?: string;
24
+ }
25
+ interface Pagination {
26
+ limit: number;
27
+ page: number;
28
+ totalPages: number;
29
+ totalItems: number;
30
+ hasMore: boolean;
31
+ }
32
+ interface Organization {
33
+ id: string;
34
+ name: string;
35
+ slug: string;
36
+ logo: string | null;
37
+ bannerImg: string | null;
38
+ description: string;
39
+ createdAt: string;
40
+ updatedAt: string;
41
+ wsUrl: string;
42
+ }
43
+ interface Label {
44
+ id: string;
45
+ organizationId: string;
46
+ name: string;
47
+ color: string | null;
48
+ createdAt: string;
49
+ }
50
+ interface Category {
51
+ id: string;
52
+ organizationId: string;
53
+ name: string;
54
+ color: string | null;
55
+ icon: string | null;
56
+ createdAt: string;
57
+ }
58
+ type TaskStatus = "backlog" | "todo" | "in-progress" | "done" | "canceled";
59
+ type TaskPriority = "none" | "low" | "medium" | "high" | "urgent";
60
+ interface Task {
61
+ id: string;
62
+ organizationId: string;
63
+ shortId: number | null;
64
+ visible: "public" | "private";
65
+ createdAt: string;
66
+ updatedAt: string;
67
+ title: string | null;
68
+ description: unknown | null;
69
+ status: TaskStatus;
70
+ priority: TaskPriority;
71
+ createdBy: string | null;
72
+ category: string | null;
73
+ voteCount: number;
74
+ descriptionHtml: string;
75
+ descriptionMarkdown: string;
76
+ }
77
+ interface CommentUser {
78
+ name: string | null;
79
+ image: string | null;
80
+ }
81
+ interface CommentReaction {
82
+ count: number;
83
+ users: string[];
84
+ }
85
+ interface Comment {
86
+ id: string;
87
+ organizationId: string;
88
+ taskId: string | null;
89
+ createdAt: string;
90
+ updatedAt: string;
91
+ content: unknown | null;
92
+ visibility: "public" | "internal";
93
+ contentHtml: string;
94
+ contentMarkdown: string;
95
+ createdBy: CommentUser | null;
96
+ reactions?: {
97
+ total: number;
98
+ reactions: Record<string, CommentReaction>;
99
+ };
100
+ }
101
+
102
+ declare const org: {
103
+ get(slug: string): Promise<Organization>;
104
+ labels(slug: string): Promise<Label[]>;
105
+ categories(slug: string, order?: "asc" | "desc"): Promise<Category[]>;
106
+ tasks(slug: string, opts?: {
107
+ order?: "asc" | "desc";
108
+ limit?: number;
109
+ page?: number;
110
+ }): Promise<{
111
+ data: Task[];
112
+ pagination: Pagination;
113
+ }>;
114
+ task(slug: string, shortId: number): Promise<Task>;
115
+ comments(slug: string, shortId: number, opts?: {
116
+ order?: "asc" | "desc";
117
+ limit?: number;
118
+ page?: number;
119
+ }): Promise<{
120
+ data: Comment[];
121
+ pagination: Pagination;
122
+ }>;
123
+ };
124
+
125
+ declare const Sayr: {
126
+ org: {
127
+ get(slug: string): Promise<Organization>;
128
+ labels(slug: string): Promise<Label[]>;
129
+ categories(slug: string, order?: "asc" | "desc"): Promise<Category[]>;
130
+ tasks(slug: string, opts?: {
131
+ order?: "asc" | "desc";
132
+ limit?: number;
133
+ page?: number;
134
+ }): Promise<{
135
+ data: Task[];
136
+ pagination: Pagination;
137
+ }>;
138
+ task(slug: string, shortId: number): Promise<Task>;
139
+ comments(slug: string, shortId: number, opts?: {
140
+ order?: "asc" | "desc";
141
+ limit?: number;
142
+ page?: number;
143
+ }): Promise<{
144
+ data: Comment[];
145
+ pagination: Pagination;
146
+ }>;
147
+ };
148
+ ws: typeof ws;
149
+ wsTypes: Record<WSMessageType, WSMessageType>;
150
+ };
151
+
152
+ export { type ApiError, type ApiSuccess, type Category, type Comment, type CommentReaction, type CommentUser, type Label, type Organization, type Pagination, type Task, type TaskPriority, type TaskStatus, Sayr as default, org, ws, wsTypes };
package/dist/index.js ADDED
@@ -0,0 +1,125 @@
1
+ // src/org.ts
2
+ var API = "https://sayr.io/api/public";
3
+ async function get(url) {
4
+ const res = await fetch(url);
5
+ const json = await res.json();
6
+ if (!json.success) throw json;
7
+ return json;
8
+ }
9
+ var org = {
10
+ async get(slug) {
11
+ const r = await get(
12
+ `${API}/organization/${slug}`
13
+ );
14
+ return r.data;
15
+ },
16
+ async labels(slug) {
17
+ const r = await get(
18
+ `${API}/organization/${slug}/labels`
19
+ );
20
+ return r.data;
21
+ },
22
+ async categories(slug, order = "desc") {
23
+ const r = await get(
24
+ `${API}/organization/${slug}/categories?order=${order}`
25
+ );
26
+ return r.data;
27
+ },
28
+ async tasks(slug, opts) {
29
+ const q = new URLSearchParams({
30
+ order: opts?.order ?? "desc",
31
+ limit: String(opts?.limit ?? 5),
32
+ page: String(opts?.page ?? 1)
33
+ });
34
+ const res = await fetch(
35
+ `${API}/organization/${slug}/tasks?${q}`
36
+ );
37
+ const json = await res.json();
38
+ if (!json.success) throw json;
39
+ return {
40
+ data: json.data,
41
+ pagination: json.pagination
42
+ };
43
+ },
44
+ async task(slug, shortId) {
45
+ const r = await get(
46
+ `${API}/organization/${slug}/tasks/${shortId}`
47
+ );
48
+ return r.data;
49
+ },
50
+ async comments(slug, shortId, opts) {
51
+ const q = new URLSearchParams({
52
+ order: opts?.order ?? "desc",
53
+ limit: String(opts?.limit ?? 5),
54
+ page: String(opts?.page ?? 1)
55
+ });
56
+ const res = await fetch(
57
+ `${API}/organization/${slug}/tasks/${shortId}/comments?${q}`
58
+ );
59
+ const json = await res.json();
60
+ if (!json.success) throw json;
61
+ return {
62
+ data: json.data,
63
+ pagination: json.pagination
64
+ };
65
+ }
66
+ };
67
+
68
+ // src/ws.ts
69
+ var wsTypes = {
70
+ CONNECTION_STATUS: "CONNECTION_STATUS",
71
+ SUBSCRIBED: "SUBSCRIBED",
72
+ ERROR: "ERROR",
73
+ PING: "PING",
74
+ PONG: "PONG",
75
+ UPDATE_ORG: "UPDATE_ORG",
76
+ CREATE_TASK: "CREATE_TASK",
77
+ UPDATE_TASK: "UPDATE_TASK",
78
+ UPDATE_TASK_COMMENTS: "UPDATE_TASK_COMMENTS",
79
+ UPDATE_TASK_VOTE: "UPDATE_TASK_VOTE",
80
+ UPDATE_LABELS: "UPDATE_LABELS",
81
+ UPDATE_VIEWS: "UPDATE_VIEWS",
82
+ UPDATE_CATEGORIES: "UPDATE_CATEGORIES",
83
+ UPDATE_ISSUE_TEMPLATES: "UPDATE_ISSUE_TEMPLATES",
84
+ DISCONNECTED: "DISCONNECTED"
85
+ };
86
+ function ws(url, handlers = {}) {
87
+ let socket;
88
+ let retry = 0;
89
+ let closed = false;
90
+ function connect() {
91
+ if (closed) return;
92
+ socket = new WebSocket(url);
93
+ socket.onmessage = (e) => {
94
+ const msg = JSON.parse(e.data);
95
+ if (msg.type === wsTypes.PING) {
96
+ socket.send(JSON.stringify({ type: wsTypes.PONG }));
97
+ return;
98
+ }
99
+ handlers[msg.type]?.(msg.data, msg);
100
+ };
101
+ socket.onclose = () => {
102
+ if (closed) return;
103
+ setTimeout(connect, Math.min(1e3 * 2 ** retry++, 3e4));
104
+ };
105
+ socket.onerror = () => socket.close();
106
+ }
107
+ connect();
108
+ return {
109
+ close() {
110
+ closed = true;
111
+ socket?.close();
112
+ }
113
+ };
114
+ }
115
+
116
+ // src/index.ts
117
+ var Sayr = { org, ws, wsTypes };
118
+ var index_default = Sayr;
119
+ export {
120
+ index_default as default,
121
+ org,
122
+ ws,
123
+ wsTypes
124
+ };
125
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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":";AAUA,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;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;;;AChFA,IAAM,OAAO,EAAE,KAAK,IAAI,QAAQ;AAEhC,IAAO,gBAAQ;","names":[]}
@@ -0,0 +1,234 @@
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/react/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ useComments: () => useComments,
24
+ useOrg: () => useOrg,
25
+ useSayrWS: () => useSayrWS,
26
+ useTask: () => useTask,
27
+ useTasks: () => useTasks
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/react/useOrg.ts
32
+ var import_react = require("react");
33
+
34
+ // src/org.ts
35
+ var API = "https://sayr.io/api/public";
36
+ async function get(url) {
37
+ const res = await fetch(url);
38
+ const json = await res.json();
39
+ if (!json.success) throw json;
40
+ return json;
41
+ }
42
+ var org = {
43
+ async get(slug) {
44
+ const r = await get(
45
+ `${API}/organization/${slug}`
46
+ );
47
+ return r.data;
48
+ },
49
+ async labels(slug) {
50
+ const r = await get(
51
+ `${API}/organization/${slug}/labels`
52
+ );
53
+ return r.data;
54
+ },
55
+ async categories(slug, order = "desc") {
56
+ const r = await get(
57
+ `${API}/organization/${slug}/categories?order=${order}`
58
+ );
59
+ return r.data;
60
+ },
61
+ async tasks(slug, opts) {
62
+ const q = new URLSearchParams({
63
+ order: opts?.order ?? "desc",
64
+ limit: String(opts?.limit ?? 5),
65
+ page: String(opts?.page ?? 1)
66
+ });
67
+ const res = await fetch(
68
+ `${API}/organization/${slug}/tasks?${q}`
69
+ );
70
+ const json = await res.json();
71
+ if (!json.success) throw json;
72
+ return {
73
+ data: json.data,
74
+ pagination: json.pagination
75
+ };
76
+ },
77
+ async task(slug, shortId) {
78
+ const r = await get(
79
+ `${API}/organization/${slug}/tasks/${shortId}`
80
+ );
81
+ return r.data;
82
+ },
83
+ async comments(slug, shortId, opts) {
84
+ const q = new URLSearchParams({
85
+ order: opts?.order ?? "desc",
86
+ limit: String(opts?.limit ?? 5),
87
+ page: String(opts?.page ?? 1)
88
+ });
89
+ const res = await fetch(
90
+ `${API}/organization/${slug}/tasks/${shortId}/comments?${q}`
91
+ );
92
+ const json = await res.json();
93
+ if (!json.success) throw json;
94
+ return {
95
+ data: json.data,
96
+ pagination: json.pagination
97
+ };
98
+ }
99
+ };
100
+
101
+ // src/ws.ts
102
+ var wsTypes = {
103
+ CONNECTION_STATUS: "CONNECTION_STATUS",
104
+ SUBSCRIBED: "SUBSCRIBED",
105
+ ERROR: "ERROR",
106
+ PING: "PING",
107
+ PONG: "PONG",
108
+ UPDATE_ORG: "UPDATE_ORG",
109
+ CREATE_TASK: "CREATE_TASK",
110
+ UPDATE_TASK: "UPDATE_TASK",
111
+ UPDATE_TASK_COMMENTS: "UPDATE_TASK_COMMENTS",
112
+ UPDATE_TASK_VOTE: "UPDATE_TASK_VOTE",
113
+ UPDATE_LABELS: "UPDATE_LABELS",
114
+ UPDATE_VIEWS: "UPDATE_VIEWS",
115
+ UPDATE_CATEGORIES: "UPDATE_CATEGORIES",
116
+ UPDATE_ISSUE_TEMPLATES: "UPDATE_ISSUE_TEMPLATES",
117
+ DISCONNECTED: "DISCONNECTED"
118
+ };
119
+ function ws(url, handlers = {}) {
120
+ let socket;
121
+ let retry = 0;
122
+ let closed = false;
123
+ function connect() {
124
+ if (closed) return;
125
+ socket = new WebSocket(url);
126
+ socket.onmessage = (e) => {
127
+ const msg = JSON.parse(e.data);
128
+ if (msg.type === wsTypes.PING) {
129
+ socket.send(JSON.stringify({ type: wsTypes.PONG }));
130
+ return;
131
+ }
132
+ handlers[msg.type]?.(msg.data, msg);
133
+ };
134
+ socket.onclose = () => {
135
+ if (closed) return;
136
+ setTimeout(connect, Math.min(1e3 * 2 ** retry++, 3e4));
137
+ };
138
+ socket.onerror = () => socket.close();
139
+ }
140
+ connect();
141
+ return {
142
+ close() {
143
+ closed = true;
144
+ socket?.close();
145
+ }
146
+ };
147
+ }
148
+
149
+ // src/react/useOrg.ts
150
+ function useOrg(slug) {
151
+ const [data, setData] = (0, import_react.useState)(null);
152
+ const [loading, setLoading] = (0, import_react.useState)(false);
153
+ const [error, setError] = (0, import_react.useState)(null);
154
+ (0, import_react.useEffect)(() => {
155
+ if (!slug) return;
156
+ setLoading(true);
157
+ org.get(slug).then(setData).catch(setError).finally(() => setLoading(false));
158
+ }, [slug]);
159
+ return { data, loading, error };
160
+ }
161
+
162
+ // src/react/useTasks.ts
163
+ var import_react3 = require("react");
164
+
165
+ // src/react/useSayrWS.ts
166
+ var import_react2 = require("react");
167
+ function useSayrWS(wsUrl, handlers) {
168
+ const connRef = (0, import_react2.useRef)(null);
169
+ (0, import_react2.useEffect)(() => {
170
+ if (!wsUrl) return;
171
+ connRef.current = ws(wsUrl, handlers);
172
+ return () => {
173
+ connRef.current?.close();
174
+ connRef.current = null;
175
+ };
176
+ }, [wsUrl]);
177
+ return connRef;
178
+ }
179
+
180
+ // src/react/useTasks.ts
181
+ function useTasks(slug, wsUrl) {
182
+ const [tasks, setTasks] = (0, import_react3.useState)([]);
183
+ const [loading, setLoading] = (0, import_react3.useState)(false);
184
+ function fetchTasks() {
185
+ if (!slug) return;
186
+ setLoading(true);
187
+ org.tasks(slug).then((r) => setTasks(r.data)).finally(() => setLoading(false));
188
+ }
189
+ (0, import_react3.useEffect)(fetchTasks, [slug]);
190
+ useSayrWS(wsUrl, {
191
+ [wsTypes.CREATE_TASK]: fetchTasks,
192
+ [wsTypes.UPDATE_TASK]: fetchTasks
193
+ });
194
+ return { tasks, loading, refetch: fetchTasks };
195
+ }
196
+
197
+ // src/react/useTask.ts
198
+ var import_react4 = require("react");
199
+ function useTask(slug, shortId) {
200
+ const [task, setTask] = (0, import_react4.useState)(null);
201
+ const [loading, setLoading] = (0, import_react4.useState)(false);
202
+ (0, import_react4.useEffect)(() => {
203
+ if (!slug || shortId == null) return;
204
+ setLoading(true);
205
+ org.task(slug, shortId).then(setTask).finally(() => setLoading(false));
206
+ }, [slug, shortId]);
207
+ return { task, loading };
208
+ }
209
+
210
+ // src/react/useComments.ts
211
+ var import_react5 = require("react");
212
+ function useComments(slug, shortId, wsUrl) {
213
+ const [comments, setComments] = (0, import_react5.useState)([]);
214
+ const [loading, setLoading] = (0, import_react5.useState)(false);
215
+ function fetchComments() {
216
+ if (!slug || shortId == null) return;
217
+ setLoading(true);
218
+ org.comments(slug, shortId).then((r) => setComments(r.data)).finally(() => setLoading(false));
219
+ }
220
+ (0, import_react5.useEffect)(fetchComments, [slug, shortId]);
221
+ useSayrWS(wsUrl, {
222
+ [wsTypes.UPDATE_TASK_COMMENTS]: fetchComments
223
+ });
224
+ return { comments, loading, refetch: fetchComments };
225
+ }
226
+ // Annotate the CommonJS export names for ESM import in node:
227
+ 0 && (module.exports = {
228
+ useComments,
229
+ useOrg,
230
+ useSayrWS,
231
+ useTask,
232
+ useTasks
233
+ });
234
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +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"]}
@@ -0,0 +1,88 @@
1
+ import * as react from 'react';
2
+
3
+ type WSMessageType = "CONNECTION_STATUS" | "SUBSCRIBED" | "ERROR" | "PING" | "PONG" | "UPDATE_ORG" | "CREATE_TASK" | "UPDATE_TASK" | "UPDATE_TASK_COMMENTS" | "UPDATE_TASK_VOTE" | "UPDATE_LABELS" | "UPDATE_VIEWS" | "UPDATE_CATEGORIES" | "UPDATE_ISSUE_TEMPLATES" | "DISCONNECTED";
4
+
5
+ interface Organization {
6
+ id: string;
7
+ name: string;
8
+ slug: string;
9
+ logo: string | null;
10
+ bannerImg: string | null;
11
+ description: string;
12
+ createdAt: string;
13
+ updatedAt: string;
14
+ wsUrl: string;
15
+ }
16
+ type TaskStatus = "backlog" | "todo" | "in-progress" | "done" | "canceled";
17
+ type TaskPriority = "none" | "low" | "medium" | "high" | "urgent";
18
+ interface Task {
19
+ id: string;
20
+ organizationId: string;
21
+ shortId: number | null;
22
+ visible: "public" | "private";
23
+ createdAt: string;
24
+ updatedAt: string;
25
+ title: string | null;
26
+ description: unknown | null;
27
+ status: TaskStatus;
28
+ priority: TaskPriority;
29
+ createdBy: string | null;
30
+ category: string | null;
31
+ voteCount: number;
32
+ descriptionHtml: string;
33
+ descriptionMarkdown: string;
34
+ }
35
+ interface CommentUser {
36
+ name: string | null;
37
+ image: string | null;
38
+ }
39
+ interface CommentReaction {
40
+ count: number;
41
+ users: string[];
42
+ }
43
+ interface Comment {
44
+ id: string;
45
+ organizationId: string;
46
+ taskId: string | null;
47
+ createdAt: string;
48
+ updatedAt: string;
49
+ content: unknown | null;
50
+ visibility: "public" | "internal";
51
+ contentHtml: string;
52
+ contentMarkdown: string;
53
+ createdBy: CommentUser | null;
54
+ reactions?: {
55
+ total: number;
56
+ reactions: Record<string, CommentReaction>;
57
+ };
58
+ }
59
+
60
+ declare function useOrg(slug?: string): {
61
+ data: Organization | null;
62
+ loading: boolean;
63
+ error: unknown;
64
+ };
65
+
66
+ declare function useTasks(slug?: string, wsUrl?: string): {
67
+ tasks: Task[];
68
+ loading: boolean;
69
+ refetch: () => void;
70
+ };
71
+
72
+ declare function useTask(slug?: string, shortId?: number): {
73
+ task: Task | null;
74
+ loading: boolean;
75
+ };
76
+
77
+ declare function useComments(slug?: string, shortId?: number, wsUrl?: string): {
78
+ comments: Comment[];
79
+ loading: boolean;
80
+ refetch: () => void;
81
+ };
82
+
83
+ type Handlers = Partial<Record<WSMessageType, (data: any, msg: any) => void>>;
84
+ declare function useSayrWS(wsUrl?: string, handlers?: Handlers): react.RefObject<{
85
+ close(): void;
86
+ } | null>;
87
+
88
+ export { useComments, useOrg, useSayrWS, useTask, useTasks };
@@ -0,0 +1,88 @@
1
+ import * as react from 'react';
2
+
3
+ type WSMessageType = "CONNECTION_STATUS" | "SUBSCRIBED" | "ERROR" | "PING" | "PONG" | "UPDATE_ORG" | "CREATE_TASK" | "UPDATE_TASK" | "UPDATE_TASK_COMMENTS" | "UPDATE_TASK_VOTE" | "UPDATE_LABELS" | "UPDATE_VIEWS" | "UPDATE_CATEGORIES" | "UPDATE_ISSUE_TEMPLATES" | "DISCONNECTED";
4
+
5
+ interface Organization {
6
+ id: string;
7
+ name: string;
8
+ slug: string;
9
+ logo: string | null;
10
+ bannerImg: string | null;
11
+ description: string;
12
+ createdAt: string;
13
+ updatedAt: string;
14
+ wsUrl: string;
15
+ }
16
+ type TaskStatus = "backlog" | "todo" | "in-progress" | "done" | "canceled";
17
+ type TaskPriority = "none" | "low" | "medium" | "high" | "urgent";
18
+ interface Task {
19
+ id: string;
20
+ organizationId: string;
21
+ shortId: number | null;
22
+ visible: "public" | "private";
23
+ createdAt: string;
24
+ updatedAt: string;
25
+ title: string | null;
26
+ description: unknown | null;
27
+ status: TaskStatus;
28
+ priority: TaskPriority;
29
+ createdBy: string | null;
30
+ category: string | null;
31
+ voteCount: number;
32
+ descriptionHtml: string;
33
+ descriptionMarkdown: string;
34
+ }
35
+ interface CommentUser {
36
+ name: string | null;
37
+ image: string | null;
38
+ }
39
+ interface CommentReaction {
40
+ count: number;
41
+ users: string[];
42
+ }
43
+ interface Comment {
44
+ id: string;
45
+ organizationId: string;
46
+ taskId: string | null;
47
+ createdAt: string;
48
+ updatedAt: string;
49
+ content: unknown | null;
50
+ visibility: "public" | "internal";
51
+ contentHtml: string;
52
+ contentMarkdown: string;
53
+ createdBy: CommentUser | null;
54
+ reactions?: {
55
+ total: number;
56
+ reactions: Record<string, CommentReaction>;
57
+ };
58
+ }
59
+
60
+ declare function useOrg(slug?: string): {
61
+ data: Organization | null;
62
+ loading: boolean;
63
+ error: unknown;
64
+ };
65
+
66
+ declare function useTasks(slug?: string, wsUrl?: string): {
67
+ tasks: Task[];
68
+ loading: boolean;
69
+ refetch: () => void;
70
+ };
71
+
72
+ declare function useTask(slug?: string, shortId?: number): {
73
+ task: Task | null;
74
+ loading: boolean;
75
+ };
76
+
77
+ declare function useComments(slug?: string, shortId?: number, wsUrl?: string): {
78
+ comments: Comment[];
79
+ loading: boolean;
80
+ refetch: () => void;
81
+ };
82
+
83
+ type Handlers = Partial<Record<WSMessageType, (data: any, msg: any) => void>>;
84
+ declare function useSayrWS(wsUrl?: string, handlers?: Handlers): react.RefObject<{
85
+ close(): void;
86
+ } | null>;
87
+
88
+ export { useComments, useOrg, useSayrWS, useTask, useTasks };
@@ -0,0 +1,203 @@
1
+ // src/react/useOrg.ts
2
+ import { useEffect, useState } from "react";
3
+
4
+ // src/org.ts
5
+ var API = "https://sayr.io/api/public";
6
+ async function get(url) {
7
+ const res = await fetch(url);
8
+ const json = await res.json();
9
+ if (!json.success) throw json;
10
+ return json;
11
+ }
12
+ var org = {
13
+ async get(slug) {
14
+ const r = await get(
15
+ `${API}/organization/${slug}`
16
+ );
17
+ return r.data;
18
+ },
19
+ async labels(slug) {
20
+ const r = await get(
21
+ `${API}/organization/${slug}/labels`
22
+ );
23
+ return r.data;
24
+ },
25
+ async categories(slug, order = "desc") {
26
+ const r = await get(
27
+ `${API}/organization/${slug}/categories?order=${order}`
28
+ );
29
+ return r.data;
30
+ },
31
+ async tasks(slug, opts) {
32
+ const q = new URLSearchParams({
33
+ order: opts?.order ?? "desc",
34
+ limit: String(opts?.limit ?? 5),
35
+ page: String(opts?.page ?? 1)
36
+ });
37
+ const res = await fetch(
38
+ `${API}/organization/${slug}/tasks?${q}`
39
+ );
40
+ const json = await res.json();
41
+ if (!json.success) throw json;
42
+ return {
43
+ data: json.data,
44
+ pagination: json.pagination
45
+ };
46
+ },
47
+ async task(slug, shortId) {
48
+ const r = await get(
49
+ `${API}/organization/${slug}/tasks/${shortId}`
50
+ );
51
+ return r.data;
52
+ },
53
+ async comments(slug, shortId, opts) {
54
+ const q = new URLSearchParams({
55
+ order: opts?.order ?? "desc",
56
+ limit: String(opts?.limit ?? 5),
57
+ page: String(opts?.page ?? 1)
58
+ });
59
+ const res = await fetch(
60
+ `${API}/organization/${slug}/tasks/${shortId}/comments?${q}`
61
+ );
62
+ const json = await res.json();
63
+ if (!json.success) throw json;
64
+ return {
65
+ data: json.data,
66
+ pagination: json.pagination
67
+ };
68
+ }
69
+ };
70
+
71
+ // src/ws.ts
72
+ var wsTypes = {
73
+ CONNECTION_STATUS: "CONNECTION_STATUS",
74
+ SUBSCRIBED: "SUBSCRIBED",
75
+ ERROR: "ERROR",
76
+ PING: "PING",
77
+ PONG: "PONG",
78
+ UPDATE_ORG: "UPDATE_ORG",
79
+ CREATE_TASK: "CREATE_TASK",
80
+ UPDATE_TASK: "UPDATE_TASK",
81
+ UPDATE_TASK_COMMENTS: "UPDATE_TASK_COMMENTS",
82
+ UPDATE_TASK_VOTE: "UPDATE_TASK_VOTE",
83
+ UPDATE_LABELS: "UPDATE_LABELS",
84
+ UPDATE_VIEWS: "UPDATE_VIEWS",
85
+ UPDATE_CATEGORIES: "UPDATE_CATEGORIES",
86
+ UPDATE_ISSUE_TEMPLATES: "UPDATE_ISSUE_TEMPLATES",
87
+ DISCONNECTED: "DISCONNECTED"
88
+ };
89
+ function ws(url, handlers = {}) {
90
+ let socket;
91
+ let retry = 0;
92
+ let closed = false;
93
+ function connect() {
94
+ if (closed) return;
95
+ socket = new WebSocket(url);
96
+ socket.onmessage = (e) => {
97
+ const msg = JSON.parse(e.data);
98
+ if (msg.type === wsTypes.PING) {
99
+ socket.send(JSON.stringify({ type: wsTypes.PONG }));
100
+ return;
101
+ }
102
+ handlers[msg.type]?.(msg.data, msg);
103
+ };
104
+ socket.onclose = () => {
105
+ if (closed) return;
106
+ setTimeout(connect, Math.min(1e3 * 2 ** retry++, 3e4));
107
+ };
108
+ socket.onerror = () => socket.close();
109
+ }
110
+ connect();
111
+ return {
112
+ close() {
113
+ closed = true;
114
+ socket?.close();
115
+ }
116
+ };
117
+ }
118
+
119
+ // src/react/useOrg.ts
120
+ function useOrg(slug) {
121
+ const [data, setData] = useState(null);
122
+ const [loading, setLoading] = useState(false);
123
+ const [error, setError] = useState(null);
124
+ useEffect(() => {
125
+ if (!slug) return;
126
+ setLoading(true);
127
+ org.get(slug).then(setData).catch(setError).finally(() => setLoading(false));
128
+ }, [slug]);
129
+ return { data, loading, error };
130
+ }
131
+
132
+ // src/react/useTasks.ts
133
+ import { useEffect as useEffect3, useState as useState2 } from "react";
134
+
135
+ // src/react/useSayrWS.ts
136
+ import { useEffect as useEffect2, useRef } from "react";
137
+ function useSayrWS(wsUrl, handlers) {
138
+ const connRef = useRef(null);
139
+ useEffect2(() => {
140
+ if (!wsUrl) return;
141
+ connRef.current = ws(wsUrl, handlers);
142
+ return () => {
143
+ connRef.current?.close();
144
+ connRef.current = null;
145
+ };
146
+ }, [wsUrl]);
147
+ return connRef;
148
+ }
149
+
150
+ // src/react/useTasks.ts
151
+ function useTasks(slug, wsUrl) {
152
+ const [tasks, setTasks] = useState2([]);
153
+ const [loading, setLoading] = useState2(false);
154
+ function fetchTasks() {
155
+ if (!slug) return;
156
+ setLoading(true);
157
+ org.tasks(slug).then((r) => setTasks(r.data)).finally(() => setLoading(false));
158
+ }
159
+ useEffect3(fetchTasks, [slug]);
160
+ useSayrWS(wsUrl, {
161
+ [wsTypes.CREATE_TASK]: fetchTasks,
162
+ [wsTypes.UPDATE_TASK]: fetchTasks
163
+ });
164
+ return { tasks, loading, refetch: fetchTasks };
165
+ }
166
+
167
+ // src/react/useTask.ts
168
+ import { useEffect as useEffect4, useState as useState3 } from "react";
169
+ function useTask(slug, shortId) {
170
+ const [task, setTask] = useState3(null);
171
+ const [loading, setLoading] = useState3(false);
172
+ useEffect4(() => {
173
+ if (!slug || shortId == null) return;
174
+ setLoading(true);
175
+ org.task(slug, shortId).then(setTask).finally(() => setLoading(false));
176
+ }, [slug, shortId]);
177
+ return { task, loading };
178
+ }
179
+
180
+ // src/react/useComments.ts
181
+ import { useEffect as useEffect5, useState as useState4 } from "react";
182
+ function useComments(slug, shortId, wsUrl) {
183
+ const [comments, setComments] = useState4([]);
184
+ const [loading, setLoading] = useState4(false);
185
+ function fetchComments() {
186
+ if (!slug || shortId == null) return;
187
+ setLoading(true);
188
+ org.comments(slug, shortId).then((r) => setComments(r.data)).finally(() => setLoading(false));
189
+ }
190
+ useEffect5(fetchComments, [slug, shortId]);
191
+ useSayrWS(wsUrl, {
192
+ [wsTypes.UPDATE_TASK_COMMENTS]: fetchComments
193
+ });
194
+ return { comments, loading, refetch: fetchComments };
195
+ }
196
+ export {
197
+ useComments,
198
+ useOrg,
199
+ useSayrWS,
200
+ useTask,
201
+ useTasks
202
+ };
203
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"]}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@sayrio/public",
3
+ "version": "0.1.0",
4
+ "description": "Sayr.io public REST + WebSocket SDK",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "dist/index.cjs",
8
+ "module": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs",
14
+ "types": "./dist/index.d.ts"
15
+ },
16
+ "./react": {
17
+ "import": "./dist/react/index.js",
18
+ "require": "./dist/react/index.cjs",
19
+ "types": "./dist/react/index.d.ts"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "sideEffects": false,
26
+ "peerDependencies": {
27
+ "react": ">=18"
28
+ },
29
+ "publishConfig": {
30
+ "access": "public"
31
+ },
32
+ "devDependencies": {
33
+ "@types/react": "^19.2.8",
34
+ "tsup": "^8.0.0",
35
+ "typescript": "^5.3.3"
36
+ },
37
+ "scripts": {
38
+ "build": "tsup",
39
+ "dev": "tsup --watch"
40
+ }
41
+ }