gitxp 0.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/.output/nitro.json +17 -0
- package/.output/public/assets/index-BGHvBNtP.js +11 -0
- package/.output/public/assets/routes-DuE8S6pX.js +60 -0
- package/.output/public/assets/styles-D5xbQNYu.css +1 -0
- package/.output/server/_chunks/ssr-renderer.mjs +26 -0
- package/.output/server/_libs/@floating-ui/core+[...].mjs +671 -0
- package/.output/server/_libs/@floating-ui/dom+[...].mjs +649 -0
- package/.output/server/_libs/@floating-ui/react-dom+[...].mjs +856 -0
- package/.output/server/_libs/@radix-ui/react-arrow+[...].mjs +258 -0
- package/.output/server/_libs/@radix-ui/react-dialog+[...].mjs +1862 -0
- package/.output/server/_libs/@radix-ui/react-popper+[...].mjs +320 -0
- package/.output/server/_libs/@radix-ui/react-tooltip+[...].mjs +534 -0
- package/.output/server/_libs/@tanstack/db+[...].mjs +14680 -0
- package/.output/server/_libs/@tanstack/react-router+[...].mjs +14563 -0
- package/.output/server/_libs/@tanstack/react-router-ssr-query+[...].mjs +126 -0
- package/.output/server/_libs/@tanstack/router-core+[...].mjs +3636 -0
- package/.output/server/_libs/class-variance-authority+clsx.mjs +69 -0
- package/.output/server/_libs/cmdk.mjs +504 -0
- package/.output/server/_libs/h3+rou3+srvx.mjs +1361 -0
- package/.output/server/_libs/h3-v2.mjs +285 -0
- package/.output/server/_libs/lucide-react.mjs +158 -0
- package/.output/server/_libs/radix-ui__primitive.mjs +44 -0
- package/.output/server/_libs/radix-ui__react-context.mjs +108 -0
- package/.output/server/_libs/tailwind-merge.mjs +3380 -0
- package/.output/server/_libs/tanstack__history.mjs +384 -0
- package/.output/server/_libs/tanstack__query-core.mjs +2225 -0
- package/.output/server/_libs/tanstack__react-db.mjs +242 -0
- package/.output/server/_libs/tanstack__react-query.mjs +140 -0
- package/.output/server/_libs/ufo.mjs +64 -0
- package/.output/server/_runtime.mjs +35 -0
- package/.output/server/_ssr/empty-plugin-adapters-D9UWiqvJ.mjs +5 -0
- package/.output/server/_ssr/events-BNrmOvgU.mjs +13 -0
- package/.output/server/_ssr/notifications-DUoENv6E.mjs +514 -0
- package/.output/server/_ssr/router-DsUHD4bT.mjs +179 -0
- package/.output/server/_ssr/routes-DIFfr242.mjs +982 -0
- package/.output/server/_ssr/ssr.mjs +1854 -0
- package/.output/server/_ssr/start-5Z2QO8AU.mjs +4 -0
- package/.output/server/_tanstack-start-manifest_v-CrD0YpZr.mjs +20 -0
- package/.output/server/index.mjs +310 -0
- package/.output/server/node_modules/tslib/modules/index.js +70 -0
- package/.output/server/node_modules/tslib/modules/package.json +3 -0
- package/.output/server/node_modules/tslib/package.json +47 -0
- package/.output/server/node_modules/tslib/tslib.js +484 -0
- package/.output/server/package.json +9 -0
- package/LICENSE +21 -0
- package/README.md +325 -0
- package/bin/gitxp.js +85 -0
- package/package.json +92 -0
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
import { n as TSS_SERVER_FUNCTION, t as createServerFn } from "./ssr.mjs";
|
|
2
|
+
import { t as emitChange } from "./events-BNrmOvgU.mjs";
|
|
3
|
+
import { mkdirSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import { DatabaseSync } from "node:sqlite";
|
|
7
|
+
import { execFile } from "node:child_process";
|
|
8
|
+
import { promisify } from "node:util";
|
|
9
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/notifications-DUoENv6E.js
|
|
10
|
+
var createServerRpc = (serverFnMeta, splitImportFn) => {
|
|
11
|
+
const url = "/_serverFn/" + serverFnMeta.id;
|
|
12
|
+
return Object.assign(splitImportFn, {
|
|
13
|
+
url,
|
|
14
|
+
serverFnMeta,
|
|
15
|
+
[TSS_SERVER_FUNCTION]: true
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
var migrations = [{
|
|
19
|
+
id: 1,
|
|
20
|
+
up: (db) => {
|
|
21
|
+
db.exec(`
|
|
22
|
+
CREATE TABLE settings (
|
|
23
|
+
key TEXT PRIMARY KEY,
|
|
24
|
+
value TEXT NOT NULL
|
|
25
|
+
) STRICT;
|
|
26
|
+
|
|
27
|
+
CREATE TABLE mutation_queue (
|
|
28
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
29
|
+
kind TEXT NOT NULL,
|
|
30
|
+
payload TEXT NOT NULL,
|
|
31
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
32
|
+
attempts INTEGER NOT NULL DEFAULT 0,
|
|
33
|
+
last_error TEXT,
|
|
34
|
+
created_at TEXT NOT NULL,
|
|
35
|
+
updated_at TEXT NOT NULL
|
|
36
|
+
) STRICT;
|
|
37
|
+
|
|
38
|
+
CREATE INDEX mutation_queue_pending ON mutation_queue (status, id);
|
|
39
|
+
|
|
40
|
+
CREATE TABLE sync_state (
|
|
41
|
+
resource TEXT PRIMARY KEY,
|
|
42
|
+
etag TEXT,
|
|
43
|
+
last_modified TEXT,
|
|
44
|
+
poll_interval INTEGER,
|
|
45
|
+
last_polled_at TEXT,
|
|
46
|
+
last_status INTEGER
|
|
47
|
+
) STRICT;
|
|
48
|
+
|
|
49
|
+
CREATE TABLE notifications (
|
|
50
|
+
thread_id TEXT PRIMARY KEY,
|
|
51
|
+
raw TEXT NOT NULL,
|
|
52
|
+
reason TEXT NOT NULL,
|
|
53
|
+
unread INTEGER NOT NULL,
|
|
54
|
+
updated_at TEXT NOT NULL,
|
|
55
|
+
last_read_at TEXT,
|
|
56
|
+
subject_title TEXT NOT NULL,
|
|
57
|
+
subject_type TEXT NOT NULL,
|
|
58
|
+
subject_url TEXT,
|
|
59
|
+
repo_full_name TEXT NOT NULL
|
|
60
|
+
) STRICT;
|
|
61
|
+
|
|
62
|
+
CREATE INDEX notifications_recent ON notifications (updated_at DESC);
|
|
63
|
+
`);
|
|
64
|
+
}
|
|
65
|
+
}, {
|
|
66
|
+
id: 2,
|
|
67
|
+
up: (db) => {
|
|
68
|
+
db.exec("ALTER TABLE sync_state ADD COLUMN request_url TEXT");
|
|
69
|
+
}
|
|
70
|
+
}];
|
|
71
|
+
function migrate(db) {
|
|
72
|
+
for (const migration of migrations) {
|
|
73
|
+
if (migration.id <= readVersion(db)) continue;
|
|
74
|
+
db.exec("BEGIN");
|
|
75
|
+
try {
|
|
76
|
+
migration.up(db);
|
|
77
|
+
db.exec(`PRAGMA user_version = ${migration.id}`);
|
|
78
|
+
db.exec("COMMIT");
|
|
79
|
+
} catch (error) {
|
|
80
|
+
db.exec("ROLLBACK");
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function readVersion(db) {
|
|
86
|
+
return db.prepare("PRAGMA user_version").get().user_version;
|
|
87
|
+
}
|
|
88
|
+
var handle;
|
|
89
|
+
function gitxpHome() {
|
|
90
|
+
return process.env.GITXP_HOME ?? join(homedir(), ".gitxp");
|
|
91
|
+
}
|
|
92
|
+
function getDb() {
|
|
93
|
+
if (handle) return handle;
|
|
94
|
+
const home = gitxpHome();
|
|
95
|
+
mkdirSync(home, { recursive: true });
|
|
96
|
+
const db = new DatabaseSync(join(home, "gitxp.db"));
|
|
97
|
+
db.exec("PRAGMA journal_mode = WAL");
|
|
98
|
+
db.exec("PRAGMA foreign_keys = ON");
|
|
99
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
100
|
+
migrate(db);
|
|
101
|
+
handle = db;
|
|
102
|
+
return handle;
|
|
103
|
+
}
|
|
104
|
+
var upsert = `
|
|
105
|
+
INSERT INTO notifications (
|
|
106
|
+
thread_id, raw, reason, unread, updated_at, last_read_at,
|
|
107
|
+
subject_title, subject_type, subject_url, repo_full_name
|
|
108
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
109
|
+
ON CONFLICT (thread_id) DO UPDATE SET
|
|
110
|
+
raw = excluded.raw,
|
|
111
|
+
reason = excluded.reason,
|
|
112
|
+
unread = excluded.unread,
|
|
113
|
+
updated_at = excluded.updated_at,
|
|
114
|
+
last_read_at = excluded.last_read_at,
|
|
115
|
+
subject_title = excluded.subject_title,
|
|
116
|
+
subject_type = excluded.subject_type,
|
|
117
|
+
subject_url = excluded.subject_url,
|
|
118
|
+
repo_full_name = excluded.repo_full_name
|
|
119
|
+
`;
|
|
120
|
+
function upsertNotifications(threads) {
|
|
121
|
+
if (threads.length === 0) return [];
|
|
122
|
+
const db = getDb();
|
|
123
|
+
const statement = db.prepare(upsert);
|
|
124
|
+
const seen = [];
|
|
125
|
+
db.exec("BEGIN");
|
|
126
|
+
try {
|
|
127
|
+
for (const thread of threads) {
|
|
128
|
+
statement.run(thread.id, JSON.stringify(thread), thread.reason, thread.unread ? 1 : 0, thread.updated_at, thread.last_read_at, thread.subject.title, thread.subject.type, thread.subject.url, thread.repository.full_name);
|
|
129
|
+
seen.push(thread.id);
|
|
130
|
+
}
|
|
131
|
+
db.exec("COMMIT");
|
|
132
|
+
} catch (error) {
|
|
133
|
+
db.exec("ROLLBACK");
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
return seen;
|
|
137
|
+
}
|
|
138
|
+
function listNotifications() {
|
|
139
|
+
return getDb().prepare(`SELECT thread_id, reason, unread, updated_at, last_read_at,
|
|
140
|
+
subject_title, subject_type, subject_url, repo_full_name
|
|
141
|
+
FROM notifications
|
|
142
|
+
ORDER BY updated_at DESC`).all();
|
|
143
|
+
}
|
|
144
|
+
function newestUpdatedAt() {
|
|
145
|
+
return getDb().prepare("SELECT max(updated_at) AS newest FROM notifications").get().newest;
|
|
146
|
+
}
|
|
147
|
+
function setNotificationRead(threadId) {
|
|
148
|
+
getDb().prepare(`UPDATE notifications SET unread = 0, last_read_at = ? WHERE thread_id = ?`).run((/* @__PURE__ */ new Date()).toISOString(), threadId);
|
|
149
|
+
}
|
|
150
|
+
function deleteNotification(threadId) {
|
|
151
|
+
getDb().prepare("DELETE FROM notifications WHERE thread_id = ?").run(threadId);
|
|
152
|
+
}
|
|
153
|
+
function readSyncState(resource) {
|
|
154
|
+
return getDb().prepare("SELECT * FROM sync_state WHERE resource = ?").get(resource);
|
|
155
|
+
}
|
|
156
|
+
function writeSyncState(state) {
|
|
157
|
+
getDb().prepare(`INSERT INTO sync_state
|
|
158
|
+
(resource, etag, last_modified, poll_interval, last_polled_at,
|
|
159
|
+
last_status, request_url)
|
|
160
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
161
|
+
ON CONFLICT (resource) DO UPDATE SET
|
|
162
|
+
etag = coalesce(excluded.etag, sync_state.etag),
|
|
163
|
+
last_modified = coalesce(excluded.last_modified, sync_state.last_modified),
|
|
164
|
+
poll_interval = coalesce(excluded.poll_interval, sync_state.poll_interval),
|
|
165
|
+
last_polled_at = excluded.last_polled_at,
|
|
166
|
+
last_status = excluded.last_status,
|
|
167
|
+
request_url = excluded.request_url`).run(state.resource, state.etag ?? null, state.lastModified ?? null, state.pollInterval ?? null, (/* @__PURE__ */ new Date()).toISOString(), state.status, state.requestUrl);
|
|
168
|
+
}
|
|
169
|
+
var run = promisify(execFile);
|
|
170
|
+
var pending;
|
|
171
|
+
function getAuth() {
|
|
172
|
+
if (!pending) pending = readAuth().catch((error) => {
|
|
173
|
+
pending = void 0;
|
|
174
|
+
throw error;
|
|
175
|
+
});
|
|
176
|
+
return pending;
|
|
177
|
+
}
|
|
178
|
+
function forgetAuth() {
|
|
179
|
+
pending = void 0;
|
|
180
|
+
}
|
|
181
|
+
async function readAuth() {
|
|
182
|
+
const host = await readActiveHost();
|
|
183
|
+
const { stdout } = await run("gh", [
|
|
184
|
+
"auth",
|
|
185
|
+
"token",
|
|
186
|
+
"--hostname",
|
|
187
|
+
host
|
|
188
|
+
]);
|
|
189
|
+
const token = stdout.trim();
|
|
190
|
+
if (!token) throw new Error(`gh returned no token for ${host}. Run \`gh auth login\`.`);
|
|
191
|
+
return {
|
|
192
|
+
host,
|
|
193
|
+
token,
|
|
194
|
+
apiBase: apiBaseFor(host)
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
async function readActiveHost() {
|
|
198
|
+
if (process.env.GH_HOST) return process.env.GH_HOST;
|
|
199
|
+
const { stdout } = await run("gh", [
|
|
200
|
+
"auth",
|
|
201
|
+
"status",
|
|
202
|
+
"--json",
|
|
203
|
+
"hosts"
|
|
204
|
+
]);
|
|
205
|
+
const hosts = JSON.parse(stdout).hosts;
|
|
206
|
+
const active = Object.entries(hosts).find(([, accounts]) => accounts.some((account) => account.active));
|
|
207
|
+
if (!active) throw new Error("gh has no active account. Run `gh auth login`.");
|
|
208
|
+
return active[0];
|
|
209
|
+
}
|
|
210
|
+
function apiBaseFor(host) {
|
|
211
|
+
return host === "github.com" ? "https://api.github.com" : `https://${host}/api/v3`;
|
|
212
|
+
}
|
|
213
|
+
var apiVersion = "2022-11-28";
|
|
214
|
+
var GithubError = class extends Error {
|
|
215
|
+
path;
|
|
216
|
+
status;
|
|
217
|
+
body;
|
|
218
|
+
constructor(path, status, body) {
|
|
219
|
+
super(`GitHub responded ${status} for ${path}`);
|
|
220
|
+
this.name = "GithubError";
|
|
221
|
+
this.path = path;
|
|
222
|
+
this.status = status;
|
|
223
|
+
this.body = body;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
async function githubRequest(path, options = {}) {
|
|
227
|
+
let response = await send(path, options);
|
|
228
|
+
if (response.status === 401) {
|
|
229
|
+
forgetAuth();
|
|
230
|
+
response = await send(path, options);
|
|
231
|
+
}
|
|
232
|
+
recordRateLimit(response);
|
|
233
|
+
if (response.status === 304) return {
|
|
234
|
+
status: 304,
|
|
235
|
+
notModified: true,
|
|
236
|
+
data: void 0,
|
|
237
|
+
...cacheHeaders(response)
|
|
238
|
+
};
|
|
239
|
+
if (!response.ok) throw new GithubError(path, response.status, await response.text());
|
|
240
|
+
return {
|
|
241
|
+
status: response.status,
|
|
242
|
+
notModified: false,
|
|
243
|
+
data: hasBody(response) ? await response.json() : void 0,
|
|
244
|
+
...cacheHeaders(response)
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
async function send(path, options) {
|
|
248
|
+
const auth = await getAuth();
|
|
249
|
+
const headers = {
|
|
250
|
+
accept: "application/vnd.github+json",
|
|
251
|
+
authorization: `Bearer ${auth.token}`,
|
|
252
|
+
"user-agent": "gitxp",
|
|
253
|
+
"x-github-api-version": apiVersion,
|
|
254
|
+
...options.headers
|
|
255
|
+
};
|
|
256
|
+
if (options.etag) headers["if-none-match"] = options.etag;
|
|
257
|
+
if (options.lastModified) headers["if-modified-since"] = options.lastModified;
|
|
258
|
+
if (options.body !== void 0) headers["content-type"] = "application/json";
|
|
259
|
+
return fetch(resolve$1(auth.apiBase, path), {
|
|
260
|
+
method: options.method ?? "GET",
|
|
261
|
+
headers,
|
|
262
|
+
body: options.body === void 0 ? void 0 : JSON.stringify(options.body)
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
function resolve$1(apiBase, path) {
|
|
266
|
+
if (path.startsWith("https://")) return path;
|
|
267
|
+
return `${apiBase.replace(/\/$/, "")}/${path.replace(/^\//, "")}`;
|
|
268
|
+
}
|
|
269
|
+
function hasBody(response) {
|
|
270
|
+
if (response.status === 204 || response.status === 205) return false;
|
|
271
|
+
return response.headers.get("content-length") !== "0";
|
|
272
|
+
}
|
|
273
|
+
function cacheHeaders(response) {
|
|
274
|
+
const pollInterval = response.headers.get("x-poll-interval");
|
|
275
|
+
return {
|
|
276
|
+
etag: response.headers.get("etag") ?? void 0,
|
|
277
|
+
lastModified: response.headers.get("last-modified") ?? void 0,
|
|
278
|
+
pollInterval: pollInterval === null ? void 0 : Number(pollInterval),
|
|
279
|
+
nextPage: nextPageFrom(response.headers.get("link"))
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function nextPageFrom(link) {
|
|
283
|
+
if (link === null) return void 0;
|
|
284
|
+
return /<([^>]+)>;\s*rel="next"/.exec(link)?.[1];
|
|
285
|
+
}
|
|
286
|
+
function recordRateLimit(response) {
|
|
287
|
+
const limit = response.headers.get("x-ratelimit-limit");
|
|
288
|
+
if (limit === null) return;
|
|
289
|
+
Number(limit), Number(response.headers.get("x-ratelimit-remaining")), Number(response.headers.get("x-ratelimit-used")), Number(response.headers.get("x-ratelimit-reset")), response.headers.get("x-ratelimit-resource");
|
|
290
|
+
}
|
|
291
|
+
var resource = "notifications";
|
|
292
|
+
var maxPages = 10;
|
|
293
|
+
async function syncNotifications() {
|
|
294
|
+
const state = readSyncState(resource);
|
|
295
|
+
const since = newestUpdatedAt();
|
|
296
|
+
const url = pathFor(since);
|
|
297
|
+
const first = await githubRequest(url, { etag: state?.request_url === url ? state.etag : void 0 });
|
|
298
|
+
writeSyncState({
|
|
299
|
+
resource,
|
|
300
|
+
etag: first.etag,
|
|
301
|
+
lastModified: first.lastModified,
|
|
302
|
+
pollInterval: first.pollInterval,
|
|
303
|
+
status: first.status,
|
|
304
|
+
requestUrl: url
|
|
305
|
+
});
|
|
306
|
+
if (first.notModified) return {
|
|
307
|
+
notModified: true,
|
|
308
|
+
written: 0,
|
|
309
|
+
pages: 0,
|
|
310
|
+
truncated: false,
|
|
311
|
+
incremental: since !== null,
|
|
312
|
+
pollInterval: first.pollInterval ?? state?.poll_interval ?? void 0
|
|
313
|
+
};
|
|
314
|
+
let written = upsertNotifications(first.data ?? []).length;
|
|
315
|
+
let next = first.nextPage;
|
|
316
|
+
let pages = 1;
|
|
317
|
+
while (next !== void 0 && pages < maxPages) {
|
|
318
|
+
const page = await githubRequest(next);
|
|
319
|
+
written += upsertNotifications(page.data ?? []).length;
|
|
320
|
+
next = page.nextPage;
|
|
321
|
+
pages += 1;
|
|
322
|
+
}
|
|
323
|
+
if (written > 0) emitChange(resource);
|
|
324
|
+
return {
|
|
325
|
+
notModified: false,
|
|
326
|
+
written,
|
|
327
|
+
pages,
|
|
328
|
+
truncated: next !== void 0,
|
|
329
|
+
incremental: since !== null,
|
|
330
|
+
pollInterval: first.pollInterval
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
function pathFor(since) {
|
|
334
|
+
const params = new URLSearchParams({
|
|
335
|
+
all: "true",
|
|
336
|
+
per_page: "50"
|
|
337
|
+
});
|
|
338
|
+
if (since !== null) params.set("since", since);
|
|
339
|
+
return `/notifications?${params.toString()}`;
|
|
340
|
+
}
|
|
341
|
+
function enqueueMutation(kind, payload) {
|
|
342
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
343
|
+
const result = getDb().prepare(`INSERT INTO mutation_queue (kind, payload, status, attempts, created_at, updated_at)
|
|
344
|
+
VALUES (?, ?, 'pending', 0, ?, ?)`).run(kind, JSON.stringify(payload), now, now);
|
|
345
|
+
return Number(result.lastInsertRowid);
|
|
346
|
+
}
|
|
347
|
+
function pendingMutations(limit = 50) {
|
|
348
|
+
return getDb().prepare(`SELECT * FROM mutation_queue WHERE status = 'pending' ORDER BY id LIMIT ?`).all(limit);
|
|
349
|
+
}
|
|
350
|
+
function failedMutations() {
|
|
351
|
+
return getDb().prepare(`SELECT * FROM mutation_queue WHERE status = 'failed' ORDER BY id`).all();
|
|
352
|
+
}
|
|
353
|
+
function completeMutation(id) {
|
|
354
|
+
getDb().prepare(`UPDATE mutation_queue SET status = 'done', updated_at = ? WHERE id = ?`).run((/* @__PURE__ */ new Date()).toISOString(), id);
|
|
355
|
+
}
|
|
356
|
+
function failMutation(id, message) {
|
|
357
|
+
getDb().prepare(`UPDATE mutation_queue
|
|
358
|
+
SET status = 'failed', attempts = attempts + 1, last_error = ?, updated_at = ?
|
|
359
|
+
WHERE id = ?`).run(message, (/* @__PURE__ */ new Date()).toISOString(), id);
|
|
360
|
+
}
|
|
361
|
+
function retryMutation(id) {
|
|
362
|
+
getDb().prepare(`UPDATE mutation_queue SET status = 'pending', last_error = NULL, updated_at = ?
|
|
363
|
+
WHERE id = ? AND status = 'failed'`).run((/* @__PURE__ */ new Date()).toISOString(), id);
|
|
364
|
+
}
|
|
365
|
+
var handlers = {
|
|
366
|
+
"notification.done": async ({ threadId }) => {
|
|
367
|
+
await githubRequest(`/notifications/threads/${threadId}`, { method: "DELETE" });
|
|
368
|
+
},
|
|
369
|
+
"notification.read": async ({ threadId }) => {
|
|
370
|
+
await githubRequest(`/notifications/threads/${threadId}`, { method: "PATCH" });
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
async function runMutation(kind, payload) {
|
|
374
|
+
const id = enqueueMutation(kind, payload);
|
|
375
|
+
try {
|
|
376
|
+
await handlers[kind](payload);
|
|
377
|
+
completeMutation(id);
|
|
378
|
+
} catch (error) {
|
|
379
|
+
failMutation(id, error instanceof Error ? error.message : String(error));
|
|
380
|
+
throw error;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
async function drainPendingMutations() {
|
|
384
|
+
const rows = pendingMutations();
|
|
385
|
+
if (rows.length === 0) return 0;
|
|
386
|
+
let applied = 0;
|
|
387
|
+
for (const row of rows) if (await applyRow(row)) applied += 1;
|
|
388
|
+
if (applied > 0) emitChange("notifications");
|
|
389
|
+
return applied;
|
|
390
|
+
}
|
|
391
|
+
async function applyRow(row) {
|
|
392
|
+
try {
|
|
393
|
+
await handlers[row.kind](JSON.parse(row.payload));
|
|
394
|
+
completeMutation(row.id);
|
|
395
|
+
return true;
|
|
396
|
+
} catch (error) {
|
|
397
|
+
failMutation(row.id, error instanceof Error ? error.message : String(error));
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
var jobs = [{
|
|
402
|
+
resource: "notifications",
|
|
403
|
+
defaultInterval: 60,
|
|
404
|
+
run: syncNotifications
|
|
405
|
+
}];
|
|
406
|
+
var timers = /* @__PURE__ */ new Map();
|
|
407
|
+
var failures = /* @__PURE__ */ new Map();
|
|
408
|
+
var running = false;
|
|
409
|
+
function startSync() {
|
|
410
|
+
if (running) return;
|
|
411
|
+
running = true;
|
|
412
|
+
drainPendingMutations().catch((error) => {
|
|
413
|
+
console.error("could not drain pending mutations", error);
|
|
414
|
+
});
|
|
415
|
+
for (const job of jobs) schedule(job, 0);
|
|
416
|
+
}
|
|
417
|
+
function schedule(job, delayMs) {
|
|
418
|
+
if (!running) return;
|
|
419
|
+
const timer = setTimeout(() => {
|
|
420
|
+
tick(job);
|
|
421
|
+
}, delayMs);
|
|
422
|
+
timer.unref();
|
|
423
|
+
timers.set(job.resource, timer);
|
|
424
|
+
}
|
|
425
|
+
async function tick(job) {
|
|
426
|
+
try {
|
|
427
|
+
const result = await job.run();
|
|
428
|
+
failures.delete(job.resource);
|
|
429
|
+
schedule(job, (result.pollInterval ?? job.defaultInterval) * 1e3);
|
|
430
|
+
} catch (error) {
|
|
431
|
+
const attempts = (failures.get(job.resource) ?? 0) + 1;
|
|
432
|
+
failures.set(job.resource, attempts);
|
|
433
|
+
console.error(`sync failed for ${job.resource} (attempt ${attempts})`, error);
|
|
434
|
+
schedule(job, backoff(attempts));
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
function backoff(attempts) {
|
|
438
|
+
return Math.min(2 ** attempts * 1e3, 3e5);
|
|
439
|
+
}
|
|
440
|
+
var getNotifications_createServerFn_handler = createServerRpc({
|
|
441
|
+
id: "5cfec4c6dee804d3573d611211f1ac5f56628842c036a21f55c9ae92c73dfff0",
|
|
442
|
+
name: "getNotifications",
|
|
443
|
+
filename: "src/functions/notifications.ts"
|
|
444
|
+
}, (opts) => getNotifications.__executeServer(opts));
|
|
445
|
+
var getNotifications = createServerFn({ method: "GET" }).handler(getNotifications_createServerFn_handler, async () => {
|
|
446
|
+
startSync();
|
|
447
|
+
return listNotifications();
|
|
448
|
+
});
|
|
449
|
+
var syncNow_createServerFn_handler = createServerRpc({
|
|
450
|
+
id: "0babd28c77cb1e09ec4d58a288fb1a4e4ea676ad4fb655c65b2949da4de21e51",
|
|
451
|
+
name: "syncNow",
|
|
452
|
+
filename: "src/functions/notifications.ts"
|
|
453
|
+
}, (opts) => syncNow.__executeServer(opts));
|
|
454
|
+
var syncNow = createServerFn({ method: "POST" }).handler(syncNow_createServerFn_handler, async () => {
|
|
455
|
+
return syncNotifications();
|
|
456
|
+
});
|
|
457
|
+
var getSyncStatus_createServerFn_handler = createServerRpc({
|
|
458
|
+
id: "c037beb8cda5c53572baa3bcea8e77650282ca11237e45b8703f106284575801",
|
|
459
|
+
name: "getSyncStatus",
|
|
460
|
+
filename: "src/functions/notifications.ts"
|
|
461
|
+
}, (opts) => getSyncStatus.__executeServer(opts));
|
|
462
|
+
var getSyncStatus = createServerFn({ method: "GET" }).handler(getSyncStatus_createServerFn_handler, async () => {
|
|
463
|
+
const state = readSyncState("notifications");
|
|
464
|
+
return {
|
|
465
|
+
hasSynced: state?.last_polled_at != null,
|
|
466
|
+
lastPolledAt: state?.last_polled_at ?? null,
|
|
467
|
+
lastStatus: state?.last_status ?? null
|
|
468
|
+
};
|
|
469
|
+
});
|
|
470
|
+
var markNotificationDone_createServerFn_handler = createServerRpc({
|
|
471
|
+
id: "9c3c7c75152987d0c0eb6e93398f5ac49bc91adf254a6cb7e7582da1ab9c7b69",
|
|
472
|
+
name: "markNotificationDone",
|
|
473
|
+
filename: "src/functions/notifications.ts"
|
|
474
|
+
}, (opts) => markNotificationDone.__executeServer(opts));
|
|
475
|
+
var markNotificationDone = createServerFn({ method: "POST" }).validator((threadId) => threadId).handler(markNotificationDone_createServerFn_handler, async ({ data }) => {
|
|
476
|
+
await runMutation("notification.done", { threadId: data });
|
|
477
|
+
deleteNotification(data);
|
|
478
|
+
emitChange("notifications");
|
|
479
|
+
});
|
|
480
|
+
var markNotificationRead_createServerFn_handler = createServerRpc({
|
|
481
|
+
id: "ec7cb03fde2349fdcee3000ccfc2f527248df2b7c89c3db9ad0c3ce452363f15",
|
|
482
|
+
name: "markNotificationRead",
|
|
483
|
+
filename: "src/functions/notifications.ts"
|
|
484
|
+
}, (opts) => markNotificationRead.__executeServer(opts));
|
|
485
|
+
var markNotificationRead = createServerFn({ method: "POST" }).validator((threadId) => threadId).handler(markNotificationRead_createServerFn_handler, async ({ data }) => {
|
|
486
|
+
await runMutation("notification.read", { threadId: data });
|
|
487
|
+
setNotificationRead(data);
|
|
488
|
+
emitChange("notifications");
|
|
489
|
+
});
|
|
490
|
+
var getFailedMutations_createServerFn_handler = createServerRpc({
|
|
491
|
+
id: "2360b4abbe0e8d19207ee28b76c335507d4577387d16ff7c5e69d2d1ccf2900b",
|
|
492
|
+
name: "getFailedMutations",
|
|
493
|
+
filename: "src/functions/notifications.ts"
|
|
494
|
+
}, (opts) => getFailedMutations.__executeServer(opts));
|
|
495
|
+
var getFailedMutations = createServerFn({ method: "GET" }).handler(getFailedMutations_createServerFn_handler, async () => {
|
|
496
|
+
return failedMutations().map((row) => ({
|
|
497
|
+
id: row.id,
|
|
498
|
+
kind: row.kind,
|
|
499
|
+
attempts: row.attempts,
|
|
500
|
+
lastError: row.last_error,
|
|
501
|
+
updatedAt: row.updated_at
|
|
502
|
+
}));
|
|
503
|
+
});
|
|
504
|
+
var retryFailedMutations_createServerFn_handler = createServerRpc({
|
|
505
|
+
id: "6e5e9052f4c44bc48d2cacaa624ebc266d94ad8c4f5211de50e7760446323376",
|
|
506
|
+
name: "retryFailedMutations",
|
|
507
|
+
filename: "src/functions/notifications.ts"
|
|
508
|
+
}, (opts) => retryFailedMutations.__executeServer(opts));
|
|
509
|
+
var retryFailedMutations = createServerFn({ method: "POST" }).handler(retryFailedMutations_createServerFn_handler, async () => {
|
|
510
|
+
for (const row of failedMutations()) retryMutation(row.id);
|
|
511
|
+
return drainPendingMutations();
|
|
512
|
+
});
|
|
513
|
+
//#endregion
|
|
514
|
+
export { getFailedMutations_createServerFn_handler, getNotifications_createServerFn_handler, getSyncStatus_createServerFn_handler, markNotificationDone_createServerFn_handler, markNotificationRead_createServerFn_handler, retryFailedMutations_createServerFn_handler, syncNow_createServerFn_handler };
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { f as createRouter, h as createRootRouteWithContext, l as Scripts, m as createFileRoute, p as lazyRouteComponent, u as HeadContent } from "../_libs/@tanstack/react-router+[...].mjs";
|
|
2
|
+
import { l as require_jsx_runtime } from "../_libs/@radix-ui/react-arrow+[...].mjs";
|
|
3
|
+
import { n as TSS_SERVER_FUNCTION, r as getServerFnById, t as createServerFn } from "./ssr.mjs";
|
|
4
|
+
import { n as onChange } from "./events-BNrmOvgU.mjs";
|
|
5
|
+
import { t as QueryClient } from "../_libs/tanstack__query-core.mjs";
|
|
6
|
+
import { t as setupRouterSsrQueryIntegration } from "../_libs/@tanstack/react-router-ssr-query+[...].mjs";
|
|
7
|
+
//#region node_modules/.nitro/vite/services/ssr/assets/router-DsUHD4bT.js
|
|
8
|
+
var import_jsx_runtime = require_jsx_runtime();
|
|
9
|
+
var __defProp = Object.defineProperty;
|
|
10
|
+
var __exportAll = (all, no_symbols) => {
|
|
11
|
+
let target = {};
|
|
12
|
+
for (var name in all) __defProp(target, name, {
|
|
13
|
+
get: all[name],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
17
|
+
return target;
|
|
18
|
+
};
|
|
19
|
+
var styles_default = "/assets/styles-D5xbQNYu.css";
|
|
20
|
+
var Route$2 = createRootRouteWithContext()({
|
|
21
|
+
head: () => ({
|
|
22
|
+
meta: [
|
|
23
|
+
{ charSet: "utf-8" },
|
|
24
|
+
{
|
|
25
|
+
name: "viewport",
|
|
26
|
+
content: "width=device-width, initial-scale=1"
|
|
27
|
+
},
|
|
28
|
+
{ title: "gitxp" }
|
|
29
|
+
],
|
|
30
|
+
links: [{
|
|
31
|
+
rel: "stylesheet",
|
|
32
|
+
href: styles_default
|
|
33
|
+
}]
|
|
34
|
+
}),
|
|
35
|
+
shellComponent: RootDocument
|
|
36
|
+
});
|
|
37
|
+
function RootDocument({ children }) {
|
|
38
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("html", {
|
|
39
|
+
lang: "en",
|
|
40
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("head", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HeadContent, {}) }), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("body", { children: [children, /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Scripts, {})] })]
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
var createSsrRpc = (functionId) => {
|
|
44
|
+
const url = "/_serverFn/" + functionId;
|
|
45
|
+
const serverFnMeta = { id: functionId };
|
|
46
|
+
const fn = async (...args) => {
|
|
47
|
+
return (await getServerFnById(functionId, { origin: "server" }))(...args);
|
|
48
|
+
};
|
|
49
|
+
return Object.assign(fn, {
|
|
50
|
+
url,
|
|
51
|
+
serverFnMeta,
|
|
52
|
+
[TSS_SERVER_FUNCTION]: true
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
var getNotifications = createServerFn({ method: "GET" }).handler(createSsrRpc("5cfec4c6dee804d3573d611211f1ac5f56628842c036a21f55c9ae92c73dfff0"));
|
|
56
|
+
var syncNow = createServerFn({ method: "POST" }).handler(createSsrRpc("0babd28c77cb1e09ec4d58a288fb1a4e4ea676ad4fb655c65b2949da4de21e51"));
|
|
57
|
+
var getSyncStatus = createServerFn({ method: "GET" }).handler(createSsrRpc("c037beb8cda5c53572baa3bcea8e77650282ca11237e45b8703f106284575801"));
|
|
58
|
+
var markNotificationDone = createServerFn({ method: "POST" }).validator((threadId) => threadId).handler(createSsrRpc("9c3c7c75152987d0c0eb6e93398f5ac49bc91adf254a6cb7e7582da1ab9c7b69"));
|
|
59
|
+
var markNotificationRead = createServerFn({ method: "POST" }).validator((threadId) => threadId).handler(createSsrRpc("ec7cb03fde2349fdcee3000ccfc2f527248df2b7c89c3db9ad0c3ce452363f15"));
|
|
60
|
+
var getFailedMutations = createServerFn({ method: "GET" }).handler(createSsrRpc("2360b4abbe0e8d19207ee28b76c335507d4577387d16ff7c5e69d2d1ccf2900b"));
|
|
61
|
+
var retryFailedMutations = createServerFn({ method: "POST" }).handler(createSsrRpc("6e5e9052f4c44bc48d2cacaa624ebc266d94ad8c4f5211de50e7760446323376"));
|
|
62
|
+
var viewIds = [
|
|
63
|
+
"inbox",
|
|
64
|
+
"review",
|
|
65
|
+
"mentions",
|
|
66
|
+
"yours",
|
|
67
|
+
"all"
|
|
68
|
+
];
|
|
69
|
+
var views = [
|
|
70
|
+
{
|
|
71
|
+
id: "inbox",
|
|
72
|
+
label: "Inbox",
|
|
73
|
+
match: (notification) => notification.unread === 1
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: "review",
|
|
77
|
+
label: "Review requests",
|
|
78
|
+
match: (notification) => notification.unread === 1 && notification.reason === "review_requested"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "mentions",
|
|
82
|
+
label: "Mentions",
|
|
83
|
+
match: (notification) => notification.unread === 1 && (notification.reason === "mention" || notification.reason === "assign")
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
id: "yours",
|
|
87
|
+
label: "Yours",
|
|
88
|
+
match: (notification) => notification.reason === "author"
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
id: "all",
|
|
92
|
+
label: "All",
|
|
93
|
+
match: () => true
|
|
94
|
+
}
|
|
95
|
+
];
|
|
96
|
+
function isViewId(value) {
|
|
97
|
+
return viewIds.includes(value);
|
|
98
|
+
}
|
|
99
|
+
function webUrlFor(notification) {
|
|
100
|
+
const repoUrl = `https://github.com/${notification.repo_full_name}`;
|
|
101
|
+
if (notification.subject_url === null) return repoUrl;
|
|
102
|
+
return notification.subject_url.startsWith("https://api.github.com/repos/") ? notification.subject_url.replace("https://api.github.com/repos/", "https://github.com/").replace("/pulls/", "/pull/") : repoUrl;
|
|
103
|
+
}
|
|
104
|
+
function reasonLabel(reason) {
|
|
105
|
+
return reason.replace(/_/g, " ");
|
|
106
|
+
}
|
|
107
|
+
var $$splitComponentImporter = () => import("./routes-DIFfr242.mjs");
|
|
108
|
+
var Route$1 = createFileRoute("/")({
|
|
109
|
+
component: lazyRouteComponent($$splitComponentImporter, "component"),
|
|
110
|
+
loader: () => getNotifications(),
|
|
111
|
+
validateSearch: (search) => ({
|
|
112
|
+
view: isViewId(search.view) ? search.view : "inbox",
|
|
113
|
+
thread: typeof search.thread === "string" ? search.thread : void 0
|
|
114
|
+
})
|
|
115
|
+
});
|
|
116
|
+
var heartbeatMs = 3e4;
|
|
117
|
+
var Route = createFileRoute("/api/events")({ server: { handlers: { GET: () => {
|
|
118
|
+
const encoder = new TextEncoder();
|
|
119
|
+
let unsubscribe;
|
|
120
|
+
let heartbeat;
|
|
121
|
+
const stream = new ReadableStream({
|
|
122
|
+
start(controller) {
|
|
123
|
+
const send = (chunk) => {
|
|
124
|
+
controller.enqueue(encoder.encode(chunk));
|
|
125
|
+
};
|
|
126
|
+
send(": connected\n\n");
|
|
127
|
+
unsubscribe = onChange((resource) => {
|
|
128
|
+
send(`event: change\ndata: ${JSON.stringify({ resource })}\n\n`);
|
|
129
|
+
});
|
|
130
|
+
heartbeat = setInterval(() => {
|
|
131
|
+
send(": ping\n\n");
|
|
132
|
+
}, heartbeatMs);
|
|
133
|
+
heartbeat.unref();
|
|
134
|
+
},
|
|
135
|
+
cancel() {
|
|
136
|
+
unsubscribe?.();
|
|
137
|
+
if (heartbeat) clearInterval(heartbeat);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
return new Response(stream, { headers: {
|
|
141
|
+
"content-type": "text/event-stream",
|
|
142
|
+
"cache-control": "no-cache",
|
|
143
|
+
connection: "keep-alive"
|
|
144
|
+
} });
|
|
145
|
+
} } } });
|
|
146
|
+
var rootRouteChildren = {
|
|
147
|
+
IndexRoute: Route$1.update({
|
|
148
|
+
id: "/",
|
|
149
|
+
path: "/",
|
|
150
|
+
getParentRoute: () => Route$2
|
|
151
|
+
}),
|
|
152
|
+
ApiEventsRoute: Route.update({
|
|
153
|
+
id: "/api/events",
|
|
154
|
+
path: "/api/events",
|
|
155
|
+
getParentRoute: () => Route$2
|
|
156
|
+
})
|
|
157
|
+
};
|
|
158
|
+
var routeTree = Route$2._addFileChildren(rootRouteChildren)._addFileTypes();
|
|
159
|
+
function getContext() {
|
|
160
|
+
return { queryClient: new QueryClient() };
|
|
161
|
+
}
|
|
162
|
+
var router_exports = /* @__PURE__ */ __exportAll({ getRouter: () => getRouter });
|
|
163
|
+
function getRouter() {
|
|
164
|
+
const context = getContext();
|
|
165
|
+
const router = createRouter({
|
|
166
|
+
routeTree,
|
|
167
|
+
context,
|
|
168
|
+
scrollRestoration: true,
|
|
169
|
+
defaultPreload: "intent",
|
|
170
|
+
defaultPreloadStaleTime: 0
|
|
171
|
+
});
|
|
172
|
+
setupRouterSsrQueryIntegration({
|
|
173
|
+
router,
|
|
174
|
+
queryClient: context.queryClient
|
|
175
|
+
});
|
|
176
|
+
return router;
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
export { webUrlFor as a, getSyncStatus as c, retryFailedMutations as d, syncNow as f, views as i, markNotificationDone as l, Route$1 as n, getFailedMutations as o, reasonLabel as r, getNotifications as s, router_exports as t, markNotificationRead as u };
|