@arenahito/piggychick 0.2.1 → 0.2.2
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/assets/main.js +27 -18
- package/package.json +1 -1
- package/src/server/prd-events.ts +125 -0
- package/src/server/routes.ts +12 -1
- package/src/server/tasks.ts +6 -0
package/dist/assets/main.js
CHANGED
|
@@ -146167,7 +146167,7 @@ var fetchJson = async (url, init) => {
|
|
|
146167
146167
|
return data;
|
|
146168
146168
|
};
|
|
146169
146169
|
var fetchRoots = () => fetchJson("/api/roots?prdSort=desc");
|
|
146170
|
-
var
|
|
146170
|
+
var allEventsUrl = () => "/api/events";
|
|
146171
146171
|
var fetchPlan = (rootId, prd) => fetchJson(`/api/roots/${encodeURIComponent(rootId)}/prds/${encodeURIComponent(prd)}/plan`);
|
|
146172
146172
|
var fetchMarkdown = (rootId, prd, docId) => fetchJson(`/api/roots/${encodeURIComponent(rootId)}/prds/${encodeURIComponent(prd)}/${encodeURIComponent(docId)}`);
|
|
146173
146173
|
var fetchConfig = () => fetchJson("/api/config");
|
|
@@ -154078,7 +154078,10 @@ var normalizeRootChangedEvent = (input, fallbackRootId) => {
|
|
|
154078
154078
|
const candidate = input;
|
|
154079
154079
|
if (candidate.kind !== "changed")
|
|
154080
154080
|
return null;
|
|
154081
|
-
const
|
|
154081
|
+
const normalizedFallbackRootId = typeof fallbackRootId === "string" && fallbackRootId.trim().length > 0 ? fallbackRootId : null;
|
|
154082
|
+
const rootId = typeof candidate.rootId === "string" && candidate.rootId.trim().length > 0 ? candidate.rootId : normalizedFallbackRootId;
|
|
154083
|
+
if (!rootId)
|
|
154084
|
+
return null;
|
|
154082
154085
|
const prdId = typeof candidate.prdId === "string" && candidate.prdId.trim().length > 0 ? candidate.prdId : null;
|
|
154083
154086
|
const at2 = typeof candidate.at === "string" ? candidate.at : new Date().toISOString();
|
|
154084
154087
|
return { kind: "changed", rootId, prdId, at: at2 };
|
|
@@ -154091,16 +154094,13 @@ var parseRootChangedEvent = (raw, fallbackRootId) => {
|
|
|
154091
154094
|
return null;
|
|
154092
154095
|
}
|
|
154093
154096
|
};
|
|
154094
|
-
var
|
|
154095
|
-
|
|
154096
|
-
|
|
154097
|
-
|
|
154098
|
-
|
|
154099
|
-
|
|
154100
|
-
|
|
154101
|
-
};
|
|
154102
|
-
const handleMessage = (event4) => {
|
|
154103
|
-
const payload = parseRootChangedEvent(event4.data, rootId);
|
|
154097
|
+
var createGlobalEventsSubscription = (onChanged, onError) => {
|
|
154098
|
+
return createEventsSubscription(allEventsUrl(), onChanged, onError);
|
|
154099
|
+
};
|
|
154100
|
+
var createEventsSubscription = (url, onChanged, onError, fallbackRootId) => {
|
|
154101
|
+
const source = new EventSource(url);
|
|
154102
|
+
const handleEvent = (event4) => {
|
|
154103
|
+
const payload = parseRootChangedEvent(event4.data, fallbackRootId);
|
|
154104
154104
|
if (!payload)
|
|
154105
154105
|
return;
|
|
154106
154106
|
onChanged(payload);
|
|
@@ -154108,13 +154108,13 @@ var createRootEventsSubscription = (rootId, onChanged, onError) => {
|
|
|
154108
154108
|
const handleError2 = (event4) => {
|
|
154109
154109
|
onError?.(event4);
|
|
154110
154110
|
};
|
|
154111
|
-
source.addEventListener("changed",
|
|
154112
|
-
source.addEventListener("message",
|
|
154111
|
+
source.addEventListener("changed", handleEvent);
|
|
154112
|
+
source.addEventListener("message", handleEvent);
|
|
154113
154113
|
source.addEventListener("error", handleError2);
|
|
154114
154114
|
return {
|
|
154115
154115
|
close: () => {
|
|
154116
|
-
source.removeEventListener("changed",
|
|
154117
|
-
source.removeEventListener("message",
|
|
154116
|
+
source.removeEventListener("changed", handleEvent);
|
|
154117
|
+
source.removeEventListener("message", handleEvent);
|
|
154118
154118
|
source.removeEventListener("error", handleError2);
|
|
154119
154119
|
source.close();
|
|
154120
154120
|
}
|
|
@@ -154205,6 +154205,7 @@ var configRequest = 0;
|
|
|
154205
154205
|
var configHandle = null;
|
|
154206
154206
|
var rootEventSubscriptions = new Map;
|
|
154207
154207
|
var pendingRootChangeEvents = [];
|
|
154208
|
+
var globalEventsSubscriptionKey = "__global__";
|
|
154208
154209
|
var readBooleanRecord = (key) => {
|
|
154209
154210
|
try {
|
|
154210
154211
|
const raw = localStorage.getItem(key);
|
|
@@ -154329,6 +154330,10 @@ var findFirstSelection = () => {
|
|
|
154329
154330
|
}
|
|
154330
154331
|
return null;
|
|
154331
154332
|
};
|
|
154333
|
+
var createGlobalEventsSubscriptionKey = (roots) => {
|
|
154334
|
+
const rootIds = [...new Set(roots.map((rootEntry) => rootEntry.id))].sort((left3, right3) => left3.localeCompare(right3, "en", { sensitivity: "base", numeric: true }));
|
|
154335
|
+
return `${globalEventsSubscriptionKey}:${JSON.stringify(rootIds)}`;
|
|
154336
|
+
};
|
|
154332
154337
|
var ensureSelection = (route) => {
|
|
154333
154338
|
let updatedHash = false;
|
|
154334
154339
|
if (route?.kind === "config") {
|
|
@@ -154792,8 +154797,12 @@ var handleRootChangedEvent = (event4) => {
|
|
|
154792
154797
|
liveRefreshRunner.trigger();
|
|
154793
154798
|
};
|
|
154794
154799
|
var reconcileRootEventStreams = () => {
|
|
154795
|
-
|
|
154796
|
-
|
|
154800
|
+
if (state3.roots.length === 0) {
|
|
154801
|
+
closeAllRootEventStreams();
|
|
154802
|
+
return;
|
|
154803
|
+
}
|
|
154804
|
+
const subscriptionKey = createGlobalEventsSubscriptionKey(state3.roots);
|
|
154805
|
+
rootEventSubscriptions = reconcileRootSubscriptions(rootEventSubscriptions, [subscriptionKey], () => createGlobalEventsSubscription(handleRootChangedEvent));
|
|
154797
154806
|
};
|
|
154798
154807
|
var syncRoots = async (payload, options2 = {}) => {
|
|
154799
154808
|
const allowLoadSelection = options2.allowLoadSelection ?? true;
|
package/package.json
CHANGED
package/src/server/prd-events.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { basename, join, sep } from "node:path";
|
|
|
4
4
|
import { resolveConfigPath } from "../shared/config";
|
|
5
5
|
import {
|
|
6
6
|
inferPrdIdFromWatchPath,
|
|
7
|
+
listRootIds,
|
|
7
8
|
listRootWatchTargetsByRootId,
|
|
8
9
|
type RootWatchTarget,
|
|
9
10
|
} from "./tasks";
|
|
@@ -274,6 +275,34 @@ const getOrCreateRootEntry = async (rootId: string, configPath: string) => {
|
|
|
274
275
|
return init;
|
|
275
276
|
};
|
|
276
277
|
|
|
278
|
+
const attachSubscriberToRoots = async (
|
|
279
|
+
rootIds: string[],
|
|
280
|
+
configPath: string,
|
|
281
|
+
isClosed: () => boolean,
|
|
282
|
+
subscriber: Subscriber,
|
|
283
|
+
attachedRootIds: Set<string>,
|
|
284
|
+
) => {
|
|
285
|
+
for (const rootId of rootIds) {
|
|
286
|
+
if (isClosed()) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (attachedRootIds.has(rootId)) {
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
let entry: RootEventEntry | null = null;
|
|
293
|
+
try {
|
|
294
|
+
entry = await getOrCreateRootEntry(rootId, configPath);
|
|
295
|
+
} catch {
|
|
296
|
+
entry = null;
|
|
297
|
+
}
|
|
298
|
+
if (!entry || isClosed()) {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
entry.subscribers.add(subscriber);
|
|
302
|
+
attachedRootIds.add(rootId);
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
277
306
|
export const createRootEventsResponse = async (
|
|
278
307
|
request: Request,
|
|
279
308
|
rootId: string,
|
|
@@ -358,6 +387,102 @@ export const createRootEventsResponse = async (
|
|
|
358
387
|
});
|
|
359
388
|
};
|
|
360
389
|
|
|
390
|
+
export const createGlobalEventsResponse = async (
|
|
391
|
+
request: Request,
|
|
392
|
+
configPath = resolveConfigPath(),
|
|
393
|
+
) => {
|
|
394
|
+
const encoder = new TextEncoder();
|
|
395
|
+
let closed = false;
|
|
396
|
+
let keepalive: ReturnType<typeof setInterval> | null = null;
|
|
397
|
+
let removeAbort: (() => void) | null = null;
|
|
398
|
+
const attachedRootIds = new Set<string>();
|
|
399
|
+
let cleanup = () => {};
|
|
400
|
+
|
|
401
|
+
const stream = new ReadableStream<Uint8Array>({
|
|
402
|
+
async start(controller) {
|
|
403
|
+
const send = (chunk: string) => {
|
|
404
|
+
controller.enqueue(encoder.encode(chunk));
|
|
405
|
+
};
|
|
406
|
+
const subscriber: Subscriber = (event) => {
|
|
407
|
+
if (closed) return;
|
|
408
|
+
try {
|
|
409
|
+
send(formatChangedEvent(event));
|
|
410
|
+
} catch {
|
|
411
|
+
cleanup();
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
cleanup = () => {
|
|
415
|
+
if (closed) return;
|
|
416
|
+
closed = true;
|
|
417
|
+
if (keepalive) {
|
|
418
|
+
clearInterval(keepalive);
|
|
419
|
+
keepalive = null;
|
|
420
|
+
}
|
|
421
|
+
for (const rootId of attachedRootIds) {
|
|
422
|
+
const entry = rootEntries.get(rootId);
|
|
423
|
+
if (!entry) continue;
|
|
424
|
+
entry.subscribers.delete(subscriber);
|
|
425
|
+
closeEntryIfUnused(rootId);
|
|
426
|
+
}
|
|
427
|
+
attachedRootIds.clear();
|
|
428
|
+
if (removeAbort) {
|
|
429
|
+
removeAbort();
|
|
430
|
+
removeAbort = null;
|
|
431
|
+
}
|
|
432
|
+
try {
|
|
433
|
+
controller.close();
|
|
434
|
+
} catch {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
if (request.signal.aborted) {
|
|
440
|
+
cleanup();
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
const onAbort = () => cleanup();
|
|
445
|
+
request.signal.addEventListener("abort", onAbort, { once: true });
|
|
446
|
+
removeAbort = () => request.signal.removeEventListener("abort", onAbort);
|
|
447
|
+
|
|
448
|
+
try {
|
|
449
|
+
const rootIds = await listRootIds(configPath).catch(() => []);
|
|
450
|
+
await attachSubscriberToRoots(
|
|
451
|
+
rootIds,
|
|
452
|
+
configPath,
|
|
453
|
+
() => closed,
|
|
454
|
+
subscriber,
|
|
455
|
+
attachedRootIds,
|
|
456
|
+
);
|
|
457
|
+
if (closed) return;
|
|
458
|
+
send(": connected\n\n");
|
|
459
|
+
keepalive = setInterval(() => {
|
|
460
|
+
if (closed) return;
|
|
461
|
+
try {
|
|
462
|
+
send(": keepalive\n\n");
|
|
463
|
+
} catch {
|
|
464
|
+
cleanup();
|
|
465
|
+
}
|
|
466
|
+
}, keepaliveMs);
|
|
467
|
+
} catch {
|
|
468
|
+
cleanup();
|
|
469
|
+
}
|
|
470
|
+
},
|
|
471
|
+
cancel() {
|
|
472
|
+
cleanup();
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
return new Response(stream, {
|
|
477
|
+
headers: {
|
|
478
|
+
"Content-Type": "text/event-stream; charset=utf-8",
|
|
479
|
+
"Cache-Control": "no-cache",
|
|
480
|
+
Connection: "keep-alive",
|
|
481
|
+
"X-Accel-Buffering": "no",
|
|
482
|
+
},
|
|
483
|
+
});
|
|
484
|
+
};
|
|
485
|
+
|
|
361
486
|
export const getRootEventsDebugSnapshot = () => {
|
|
362
487
|
let watcherCount = 0;
|
|
363
488
|
let subscriberCount = 0;
|
package/src/server/routes.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
toConfigFile,
|
|
12
12
|
writeConfigText,
|
|
13
13
|
} from "../shared/config";
|
|
14
|
-
import { createRootEventsResponse } from "./prd-events";
|
|
14
|
+
import { createGlobalEventsResponse, createRootEventsResponse } from "./prd-events";
|
|
15
15
|
import {
|
|
16
16
|
listRoots,
|
|
17
17
|
readMarkdownByRoot,
|
|
@@ -70,6 +70,17 @@ export const handleApiRequest = async (request: Request, configPath = resolveCon
|
|
|
70
70
|
return jsonError(404, "not_found", "Not found");
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
if (segments[1] === "events" && segments.length === 2) {
|
|
74
|
+
if (request.method !== "GET") {
|
|
75
|
+
return jsonError(405, "method_not_allowed", "Method not allowed");
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
return await createGlobalEventsResponse(request, configPath);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
return handleTasksError(error);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
73
84
|
if (segments[1] === "roots" && segments.length === 2) {
|
|
74
85
|
if (request.method === "GET") {
|
|
75
86
|
try {
|
package/src/server/tasks.ts
CHANGED
|
@@ -685,6 +685,12 @@ export const listRoots = async (
|
|
|
685
685
|
return { roots };
|
|
686
686
|
};
|
|
687
687
|
|
|
688
|
+
export const listRootIds = async (configPath = resolveConfigPath()) => {
|
|
689
|
+
const config = await loadConfigFile(configPath);
|
|
690
|
+
const normalized = await normalizeConfig(config, { path: configPath });
|
|
691
|
+
return buildRootEntries(normalized.roots).map((entry) => entry.id);
|
|
692
|
+
};
|
|
693
|
+
|
|
688
694
|
export const resolveRootById = async (rootId: string, configPath = resolveConfigPath()) => {
|
|
689
695
|
const config = await loadConfigFile(configPath);
|
|
690
696
|
const normalized = await normalizeConfig(config, { path: configPath });
|