@tuongaz/seeflow 0.1.109 → 0.1.110
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/web/assets/{architectureDiagram-3BPJPVTR-CvIAMnMC.js → architectureDiagram-3BPJPVTR-CyC7OltI.js} +1 -1
- package/dist/web/assets/{blockDiagram-GPEHLZMM-C55itdvA.js → blockDiagram-GPEHLZMM-Bzp66cSV.js} +1 -1
- package/dist/web/assets/{c4Diagram-AAUBKEIU-BUJz6OcY.js → c4Diagram-AAUBKEIU-DG8Iponw.js} +1 -1
- package/dist/web/assets/channel-ByV7mN6x.js +1 -0
- package/dist/web/assets/{chart-Ck4M6xxI.js → chart-DYrOhreJ.js} +1 -1
- package/dist/web/assets/{chunk-2J33WTMH-CYHRe8M7.js → chunk-2J33WTMH-DAqVVWtt.js} +1 -1
- package/dist/web/assets/{chunk-4BX2VUAB-0G6VpCw_.js → chunk-4BX2VUAB-BLSkydjn.js} +1 -1
- package/dist/web/assets/{chunk-55IACEB6-YeyA7Efg.js → chunk-55IACEB6-DQPfPbiJ.js} +1 -1
- package/dist/web/assets/{chunk-727SXJPM-irL9oAdE.js → chunk-727SXJPM-BTf0cYfp.js} +1 -1
- package/dist/web/assets/{chunk-AQP2D5EJ-DB0ZGTqs.js → chunk-AQP2D5EJ-3Vhv9mu3.js} +1 -1
- package/dist/web/assets/{chunk-FMBD7UC4-Z48rYWhG.js → chunk-FMBD7UC4-DFLLVi48.js} +1 -1
- package/dist/web/assets/{chunk-ND2GUHAM-BgYSDKdi.js → chunk-ND2GUHAM-CxNVSIAI.js} +1 -1
- package/dist/web/assets/{chunk-QZHKN3VN-CzcjFmi-.js → chunk-QZHKN3VN-CgQH2fWj.js} +1 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-CftRaxtv.js +1 -0
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-CftRaxtv.js +1 -0
- package/dist/web/assets/{code-block-BwE8Ip6V.js → code-block-DbJK70Ez.js} +1 -1
- package/dist/web/assets/{cose-bilkent-S5V4N54A-DtufDmle.js → cose-bilkent-S5V4N54A-Byq3k4cK.js} +1 -1
- package/dist/web/assets/{dagre-BM42HDAG-DwYVzLme.js → dagre-BM42HDAG-BsD1H1iU.js} +1 -1
- package/dist/web/assets/{diagram-2AECGRRQ-DDw_qvjI.js → diagram-2AECGRRQ-CtzAGbyS.js} +1 -1
- package/dist/web/assets/{diagram-5GNKFQAL-BYG2VdwF.js → diagram-5GNKFQAL-D0UfPbiW.js} +1 -1
- package/dist/web/assets/{diagram-KO2AKTUF-Drp2zu_G.js → diagram-KO2AKTUF-9h1XRmj3.js} +1 -1
- package/dist/web/assets/{diagram-LMA3HP47-CXmAihfn.js → diagram-LMA3HP47-BrI6e364.js} +1 -1
- package/dist/web/assets/{diagram-OG6HWLK6-i6-iKnAU.js → diagram-OG6HWLK6-BFGFDRp3.js} +1 -1
- package/dist/web/assets/{erDiagram-TEJ5UH35-Bo96y9hO.js → erDiagram-TEJ5UH35-ybC8Itku.js} +1 -1
- package/dist/web/assets/{flowDiagram-I6XJVG4X-D9fneMTu.js → flowDiagram-I6XJVG4X-Dhol-kR8.js} +1 -1
- package/dist/web/assets/{ganttDiagram-6RSMTGT7-Cbefa8NE.js → ganttDiagram-6RSMTGT7-Y0-j3xBh.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-PVQCEYII-BHYd5nUs.js → gitGraphDiagram-PVQCEYII-CkmdZb3E.js} +1 -1
- package/dist/web/assets/{iconify-BuhA_8An.js → iconify-C6Z3PVFi.js} +1 -1
- package/dist/web/assets/index-D3ZH7sGq.js +8629 -0
- package/dist/web/assets/{index.es-pImjWTGX.js → index.es-D2LpuCoX.js} +1 -1
- package/dist/web/assets/{infoDiagram-5YYISTIA-Bw7MIIwj.js → infoDiagram-5YYISTIA-C349fRyb.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-CySt1i7t.js → ishikawaDiagram-YF4QCWOH-CdAxgdA0.js} +1 -1
- package/dist/web/assets/{journeyDiagram-JHISSGLW-q4YV61wv.js → journeyDiagram-JHISSGLW-Gdj93qhh.js} +1 -1
- package/dist/web/assets/{jspdf.es.min-B95ptK5L.js → jspdf.es.min-BaNs7eNe.js} +3 -3
- package/dist/web/assets/{kanban-definition-UN3LZRKU-C1oN-zoM.js → kanban-definition-UN3LZRKU-utWOpj47.js} +1 -1
- package/dist/web/assets/{linear-DPSv7VcC.js → linear-ysH3nWHa.js} +1 -1
- package/dist/web/assets/{markdown-btlM2PIA.js → markdown-DKlQMpx8.js} +1 -1
- package/dist/web/assets/{mermaid.core-Cmch2GTm.js → mermaid.core-eyZWidoa.js} +4 -4
- package/dist/web/assets/{mindmap-definition-RKZ34NQL--XOTWr6e.js → mindmap-definition-RKZ34NQL-Dhprs5tv.js} +1 -1
- package/dist/web/assets/{pieDiagram-4H26LBE5-DxZgtzul.js → pieDiagram-4H26LBE5-z_3vPWNO.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-W4KKPZXB-bbV2bAO3.js → quadrantDiagram-W4KKPZXB-BOBDCRWb.js} +1 -1
- package/dist/web/assets/{requirementDiagram-4Y6WPE33-CiJM8hio.js → requirementDiagram-4Y6WPE33-qn0up2ND.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-5OEKKPKP-DABBnuaB.js → sankeyDiagram-5OEKKPKP-CrcTPUVx.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-3UESZ5HK-BTQJk7bM.js → sequenceDiagram-3UESZ5HK-r24r7OqC.js} +1 -1
- package/dist/web/assets/{stateDiagram-AJRCARHV-yL0tAd3x.js → stateDiagram-AJRCARHV-2EJNhDf-.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-YVpQlkEY.js +1 -0
- package/dist/web/assets/{time-hCZUKGxT.js → time-CheTsYvu.js} +1 -1
- package/dist/web/assets/{timeline-definition-PNZ67QCA-CG6k16Wj.js → timeline-definition-PNZ67QCA-BNj38YIy.js} +1 -1
- package/dist/web/assets/{vennDiagram-CIIHVFJN-BVH530xf.js → vennDiagram-CIIHVFJN-C6ktujmS.js} +1 -1
- package/dist/web/assets/{wardley-L42UT6IY-BYVkFMrL.js → wardley-L42UT6IY-Bl_M5GRw.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-YWT4CUSO-BGMuHejP.js → wardleyDiagram-YWT4CUSO-B0CVz9vg.js} +1 -1
- package/dist/web/assets/{xychartDiagram-2RQKCTM6-D6MSlnJD.js → xychartDiagram-2RQKCTM6-ljSwqYiu.js} +1 -1
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/src/api.ts +0 -291
- package/src/server.ts +0 -20
- package/dist/web/assets/channel-DtcQ9fhj.js +0 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-BjYXB41E.js +0 -1
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-BjYXB41E.js +0 -1
- package/dist/web/assets/index-08hmlCqO.js +0 -8644
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-BzWj6i1l.js +0 -1
- package/src/share/sse-frame.ts +0 -85
- package/src/share/sse-outbound-queue.ts +0 -173
- package/src/share/sse-rate-limit.ts +0 -205
- package/src/share/sse-tap.ts +0 -183
- package/src/share-audit.ts +0 -267
- package/src/share-envelope.ts +0 -155
- package/src/share-file-request.ts +0 -399
- package/src/share-file-resolver.ts +0 -68
- package/src/share-file-upload.ts +0 -595
- package/src/share-files-manifest.ts +0 -232
- package/src/share-ratelimit.ts +0 -69
- package/src/share-rpc-schema.ts +0 -249
- package/src/share-transport.ts +0 -205
- package/src/share.ts +0 -1663
package/src/api.ts
CHANGED
|
@@ -47,8 +47,6 @@ import {
|
|
|
47
47
|
} from './schema-catalog.ts';
|
|
48
48
|
import type { ComponentAction, SeeflowManifest } from './schema.ts';
|
|
49
49
|
import { FlowIdPattern, FlowSchema, ResolvedFlowSchema } from './schema.ts';
|
|
50
|
-
import type { RpcOp } from './share-rpc-schema.ts';
|
|
51
|
-
import type { AttributionEvent, RpcDispatchOutcome, ShareController, ShareState } from './share.ts';
|
|
52
50
|
import { type Spawner, defaultSpawner } from './shellout.ts';
|
|
53
51
|
import { ID_TYPES, MAX_ID_COUNT, generateIds, isIdType } from './short-id.ts';
|
|
54
52
|
import type { StatusRunner } from './status-runner.ts';
|
|
@@ -238,14 +236,8 @@ export interface ApiOptions {
|
|
|
238
236
|
/** Override the icon installer fetcher. Production uses fetchWithProgress;
|
|
239
237
|
* integration tests inject a fixture-returning closure. */
|
|
240
238
|
iconFetcher?: IconFetcher;
|
|
241
|
-
/** Live Share controller. The studio bootstrap instantiates one per process
|
|
242
|
-
* (see server.ts); tests inject a fake to drive the /api/share/* routes
|
|
243
|
-
* without touching the relay or a real WebSocket. */
|
|
244
|
-
share?: ShareController;
|
|
245
239
|
}
|
|
246
240
|
|
|
247
|
-
const KickBodySchema = z.object({ peerId: z.string().min(1) });
|
|
248
|
-
|
|
249
241
|
/**
|
|
250
242
|
* Thin call-through wrapper around the proxy.ts module exports. Lets tests
|
|
251
243
|
* inject a recording fake to observe runPlay invocations — the play-run map
|
|
@@ -269,25 +261,6 @@ export function createApi(options: ApiOptions): Hono {
|
|
|
269
261
|
const ops = createOperations({ registry, watcher });
|
|
270
262
|
const api = new Hono();
|
|
271
263
|
|
|
272
|
-
// Fan-out helper for host-originated edits (US-039). Every node/connector
|
|
273
|
-
// mutation endpoint below funnels through this after a successful op so the
|
|
274
|
-
// live-share peers receive a `node-patched` envelope and can apply the diff
|
|
275
|
-
// to their local snapshot. No-op when the share controller is absent or the
|
|
276
|
-
// session is not active. Wrapped in try/catch defensively — broadcast
|
|
277
|
-
// failures must NEVER fail the local HTTP request. The share controller
|
|
278
|
-
// normalizes flat `patchNode` bodies into a peer-applicable wire shape via
|
|
279
|
-
// its own `canonicalizePatchNode` helper before broadcasting.
|
|
280
|
-
const share = options.share;
|
|
281
|
-
const broadcastEdit = (op: RpcOp, result: { kind: string; data?: unknown }): void => {
|
|
282
|
-
if (!share) return;
|
|
283
|
-
if (result.kind !== 'ok') return;
|
|
284
|
-
try {
|
|
285
|
-
share.broadcastHostEdit(op, result as RpcDispatchOutcome);
|
|
286
|
-
} catch (err) {
|
|
287
|
-
console.warn('[api] share broadcastHostEdit failed:', err);
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
|
|
291
264
|
const iconJobs = options.iconJobs ?? createJobRegistry();
|
|
292
265
|
api.route(
|
|
293
266
|
'/icons',
|
|
@@ -1690,7 +1663,6 @@ export function createApi(options: ApiOptions): Hono {
|
|
|
1690
1663
|
const result = await ops.moveNode(id, nodeId, parsed.data);
|
|
1691
1664
|
switch (result.kind) {
|
|
1692
1665
|
case 'ok':
|
|
1693
|
-
broadcastEdit({ op: 'moveNode', flowId: id, nodeId, position: parsed.data }, result);
|
|
1694
1666
|
return c.json({ ok: true, position: result.data.position });
|
|
1695
1667
|
case 'flowNotFound':
|
|
1696
1668
|
return c.json({ error: 'unknown demo' }, 404);
|
|
@@ -1734,7 +1706,6 @@ export function createApi(options: ApiOptions): Hono {
|
|
|
1734
1706
|
const result = await ops.reorderNode(id, nodeId, parsed.data);
|
|
1735
1707
|
switch (result.kind) {
|
|
1736
1708
|
case 'ok':
|
|
1737
|
-
broadcastEdit({ op: 'reorderNode', flowId: id, nodeId, reorder: parsed.data }, result);
|
|
1738
1709
|
return c.json({ ok: true });
|
|
1739
1710
|
case 'flowNotFound':
|
|
1740
1711
|
return c.json({ error: 'unknown demo' }, 404);
|
|
@@ -1778,7 +1749,6 @@ export function createApi(options: ApiOptions): Hono {
|
|
|
1778
1749
|
const result = await ops.patchNode(id, nodeId, parsed.data);
|
|
1779
1750
|
switch (result.kind) {
|
|
1780
1751
|
case 'ok':
|
|
1781
|
-
broadcastEdit({ op: 'patchNode', flowId: id, nodeId, patch: parsed.data }, result);
|
|
1782
1752
|
return c.json({ ok: true });
|
|
1783
1753
|
case 'flowNotFound':
|
|
1784
1754
|
return c.json({ error: 'unknown demo' }, 404);
|
|
@@ -1816,10 +1786,6 @@ export function createApi(options: ApiOptions): Hono {
|
|
|
1816
1786
|
const result = await ops.addNode(id, body as Record<string, unknown>);
|
|
1817
1787
|
switch (result.kind) {
|
|
1818
1788
|
case 'ok':
|
|
1819
|
-
broadcastEdit(
|
|
1820
|
-
{ op: 'addNode', flowId: id, node: result.data.node as Record<string, unknown> },
|
|
1821
|
-
result,
|
|
1822
|
-
);
|
|
1823
1789
|
return c.json({ ok: true, id: result.data.id, node: result.data.node });
|
|
1824
1790
|
case 'flowNotFound':
|
|
1825
1791
|
return c.json({ error: 'unknown demo' }, 404);
|
|
@@ -1860,15 +1826,6 @@ export function createApi(options: ApiOptions): Hono {
|
|
|
1860
1826
|
const result = await ops.addBulk(id, parsed.data);
|
|
1861
1827
|
switch (result.kind) {
|
|
1862
1828
|
case 'ok':
|
|
1863
|
-
broadcastEdit(
|
|
1864
|
-
{
|
|
1865
|
-
op: 'addBulk',
|
|
1866
|
-
flowId: id,
|
|
1867
|
-
...(parsed.data.nodes ? { nodes: parsed.data.nodes } : {}),
|
|
1868
|
-
...(parsed.data.connectors ? { connectors: parsed.data.connectors } : {}),
|
|
1869
|
-
},
|
|
1870
|
-
result,
|
|
1871
|
-
);
|
|
1872
1829
|
return c.json({
|
|
1873
1830
|
ok: true,
|
|
1874
1831
|
nodes: result.data.nodes,
|
|
@@ -1910,7 +1867,6 @@ export function createApi(options: ApiOptions): Hono {
|
|
|
1910
1867
|
const result = await ops.deleteNode(id, nodeId);
|
|
1911
1868
|
switch (result.kind) {
|
|
1912
1869
|
case 'ok':
|
|
1913
|
-
broadcastEdit({ op: 'deleteNode', flowId: id, nodeId }, result);
|
|
1914
1870
|
return c.json({ ok: true });
|
|
1915
1871
|
case 'flowNotFound':
|
|
1916
1872
|
return c.json({ error: 'unknown demo' }, 404);
|
|
@@ -1954,10 +1910,6 @@ export function createApi(options: ApiOptions): Hono {
|
|
|
1954
1910
|
const result = await ops.patchConnector(id, connId, parsed.data);
|
|
1955
1911
|
switch (result.kind) {
|
|
1956
1912
|
case 'ok':
|
|
1957
|
-
broadcastEdit(
|
|
1958
|
-
{ op: 'patchConnector', flowId: id, connectorId: connId, patch: parsed.data },
|
|
1959
|
-
result,
|
|
1960
|
-
);
|
|
1961
1913
|
return c.json({ ok: true });
|
|
1962
1914
|
case 'flowNotFound':
|
|
1963
1915
|
return c.json({ error: 'unknown demo' }, 404);
|
|
@@ -1996,14 +1948,6 @@ export function createApi(options: ApiOptions): Hono {
|
|
|
1996
1948
|
const result = await ops.addConnector(id, body as Record<string, unknown>);
|
|
1997
1949
|
switch (result.kind) {
|
|
1998
1950
|
case 'ok':
|
|
1999
|
-
broadcastEdit(
|
|
2000
|
-
{
|
|
2001
|
-
op: 'addConnector',
|
|
2002
|
-
flowId: id,
|
|
2003
|
-
connector: { ...(body as Record<string, unknown>), id: result.data.id },
|
|
2004
|
-
},
|
|
2005
|
-
result,
|
|
2006
|
-
);
|
|
2007
1951
|
return c.json({ ok: true, id: result.data.id });
|
|
2008
1952
|
case 'flowNotFound':
|
|
2009
1953
|
return c.json({ error: 'unknown demo' }, 404);
|
|
@@ -2029,7 +1973,6 @@ export function createApi(options: ApiOptions): Hono {
|
|
|
2029
1973
|
const result = await ops.deleteConnector(id, connId);
|
|
2030
1974
|
switch (result.kind) {
|
|
2031
1975
|
case 'ok':
|
|
2032
|
-
broadcastEdit({ op: 'deleteConnector', flowId: id, connectorId: connId }, result);
|
|
2033
1976
|
return c.json({ ok: true });
|
|
2034
1977
|
case 'flowNotFound':
|
|
2035
1978
|
return c.json({ error: 'unknown demo' }, 404);
|
|
@@ -2147,240 +2090,6 @@ export function createApi(options: ApiOptions): Hono {
|
|
|
2147
2090
|
});
|
|
2148
2091
|
});
|
|
2149
2092
|
|
|
2150
|
-
// Live Share local API. Five routes that delegate to the injected
|
|
2151
|
-
// ShareController. The controller is optional — when absent (e.g. tests
|
|
2152
|
-
// that don't exercise share) every endpoint returns 503 so misconfigured
|
|
2153
|
-
// deployments fail visibly instead of silently 404ing. `share` is defined
|
|
2154
|
-
// at the top of createApi together with `broadcastEdit`.
|
|
2155
|
-
|
|
2156
|
-
// Map a controller rejection to an HTTP status + error body. share-not-active
|
|
2157
|
-
// and share-peer-not-found are the two domain-specific reasons; everything
|
|
2158
|
-
// else surfaces as 500 with the raw message so misconfigurations (bad relay
|
|
2159
|
-
// URL, network failures, etc.) are debuggable from the response.
|
|
2160
|
-
const shareErrorStatus = (err: unknown): { status: 400 | 404 | 409 | 500; error: string } => {
|
|
2161
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
2162
|
-
if (msg === 'share-not-active') return { status: 409, error: msg };
|
|
2163
|
-
if (msg === 'share-peer-not-found') return { status: 404, error: msg };
|
|
2164
|
-
if (msg === 'share-already-active') return { status: 409, error: msg };
|
|
2165
|
-
return { status: 500, error: msg };
|
|
2166
|
-
};
|
|
2167
|
-
|
|
2168
|
-
api.post('/share/start', async (c) => {
|
|
2169
|
-
if (!share) return c.json({ error: 'share controller not configured' }, 503);
|
|
2170
|
-
try {
|
|
2171
|
-
const result = await share.start();
|
|
2172
|
-
return c.json(result);
|
|
2173
|
-
} catch (err) {
|
|
2174
|
-
const mapped = shareErrorStatus(err);
|
|
2175
|
-
return c.json({ error: mapped.error }, mapped.status);
|
|
2176
|
-
}
|
|
2177
|
-
});
|
|
2178
|
-
|
|
2179
|
-
api.post('/share/stop', async (c) => {
|
|
2180
|
-
if (!share) return c.json({ error: 'share controller not configured' }, 503);
|
|
2181
|
-
try {
|
|
2182
|
-
await share.stop();
|
|
2183
|
-
return c.body(null, 204);
|
|
2184
|
-
} catch (err) {
|
|
2185
|
-
const mapped = shareErrorStatus(err);
|
|
2186
|
-
return c.json({ error: mapped.error }, mapped.status);
|
|
2187
|
-
}
|
|
2188
|
-
});
|
|
2189
|
-
|
|
2190
|
-
api.post('/share/kick', async (c) => {
|
|
2191
|
-
if (!share) return c.json({ error: 'share controller not configured' }, 503);
|
|
2192
|
-
let body: unknown;
|
|
2193
|
-
try {
|
|
2194
|
-
body = await c.req.json();
|
|
2195
|
-
} catch {
|
|
2196
|
-
return c.json({ error: 'Body must be valid JSON' }, 400);
|
|
2197
|
-
}
|
|
2198
|
-
const parsed = KickBodySchema.safeParse(body);
|
|
2199
|
-
if (!parsed.success) {
|
|
2200
|
-
return c.json({ error: 'Invalid kick body', issues: parsed.error.issues }, 400);
|
|
2201
|
-
}
|
|
2202
|
-
try {
|
|
2203
|
-
await share.kick(parsed.data.peerId);
|
|
2204
|
-
return c.body(null, 204);
|
|
2205
|
-
} catch (err) {
|
|
2206
|
-
const mapped = shareErrorStatus(err);
|
|
2207
|
-
return c.json({ error: mapped.error }, mapped.status);
|
|
2208
|
-
}
|
|
2209
|
-
});
|
|
2210
|
-
|
|
2211
|
-
api.post('/share/rotate', async (c) => {
|
|
2212
|
-
if (!share) return c.json({ error: 'share controller not configured' }, 503);
|
|
2213
|
-
try {
|
|
2214
|
-
const result = await share.rotateUrl();
|
|
2215
|
-
return c.json(result);
|
|
2216
|
-
} catch (err) {
|
|
2217
|
-
const mapped = shareErrorStatus(err);
|
|
2218
|
-
return c.json({ error: mapped.error }, mapped.status);
|
|
2219
|
-
}
|
|
2220
|
-
});
|
|
2221
|
-
|
|
2222
|
-
// POST /api/share/kill-all — host kill-switch (US-081). Revokes every
|
|
2223
|
-
// session this studio has tracked in `active.json`, not just the active
|
|
2224
|
-
// one. Local-only by the cors.ts middleware. Returns the counts surfaced
|
|
2225
|
-
// by the controller so the UI toast can show "Ended N live sessions".
|
|
2226
|
-
api.post('/share/kill-all', async (c) => {
|
|
2227
|
-
if (!share) return c.json({ error: 'share controller not configured' }, 503);
|
|
2228
|
-
try {
|
|
2229
|
-
const result = await share.killAll();
|
|
2230
|
-
return c.json(result);
|
|
2231
|
-
} catch (err) {
|
|
2232
|
-
const mapped = shareErrorStatus(err);
|
|
2233
|
-
return c.json({ error: mapped.error }, mapped.status);
|
|
2234
|
-
}
|
|
2235
|
-
});
|
|
2236
|
-
|
|
2237
|
-
// GET /api/share/audit — page through the per-session AuditEntry JSONL log.
|
|
2238
|
-
// Local-only by virtue of the cors.ts middleware. Requires an active session
|
|
2239
|
-
// — when the controller is idle there is no logger to read from, so respond
|
|
2240
|
-
// 400 rather than silently returning an empty page (consumers can use the
|
|
2241
|
-
// status code to distinguish "no entries yet" from "no session").
|
|
2242
|
-
api.get('/share/audit', async (c) => {
|
|
2243
|
-
if (!share) return c.json({ error: 'share controller not configured' }, 503);
|
|
2244
|
-
if (share.state().status !== 'active') {
|
|
2245
|
-
return c.json({ error: 'share-not-active' }, 400);
|
|
2246
|
-
}
|
|
2247
|
-
const limitRaw = c.req.query('limit');
|
|
2248
|
-
const cursorRaw = c.req.query('cursor');
|
|
2249
|
-
const limit = limitRaw !== undefined ? Number.parseInt(limitRaw, 10) : undefined;
|
|
2250
|
-
const cursor = cursorRaw !== undefined ? Number.parseInt(cursorRaw, 10) : undefined;
|
|
2251
|
-
const opts: { limit?: number; cursor?: number } = {};
|
|
2252
|
-
if (typeof limit === 'number' && Number.isFinite(limit)) opts.limit = limit;
|
|
2253
|
-
if (typeof cursor === 'number' && Number.isFinite(cursor)) opts.cursor = cursor;
|
|
2254
|
-
try {
|
|
2255
|
-
const page = await share.audit.list(opts);
|
|
2256
|
-
return c.json(page);
|
|
2257
|
-
} catch (err) {
|
|
2258
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
2259
|
-
return c.json({ error: message }, 500);
|
|
2260
|
-
}
|
|
2261
|
-
});
|
|
2262
|
-
|
|
2263
|
-
// GET /api/share/state — SSE stream of ShareState transitions. Initial frame
|
|
2264
|
-
// is the current state; subsequent frames are pushed on each controller
|
|
2265
|
-
// subscribe callback. Mirrors the /api/events shape (queue + wake + heartbeat)
|
|
2266
|
-
// so the UI can use the same EventSource handling.
|
|
2267
|
-
api.get('/share/state', (c) => {
|
|
2268
|
-
if (!share) return c.json({ error: 'share controller not configured' }, 503);
|
|
2269
|
-
const controller = share;
|
|
2270
|
-
|
|
2271
|
-
return streamSSE(c, async (stream) => {
|
|
2272
|
-
let active = true;
|
|
2273
|
-
const queue: Array<{ event: string; data: string }> = [];
|
|
2274
|
-
let resume: (() => void) | null = null;
|
|
2275
|
-
|
|
2276
|
-
const wake = () => {
|
|
2277
|
-
if (resume) {
|
|
2278
|
-
const r = resume;
|
|
2279
|
-
resume = null;
|
|
2280
|
-
r();
|
|
2281
|
-
}
|
|
2282
|
-
};
|
|
2283
|
-
|
|
2284
|
-
const enqueue = (s: ShareState) => {
|
|
2285
|
-
queue.push({ event: 'state', data: JSON.stringify(s) });
|
|
2286
|
-
wake();
|
|
2287
|
-
};
|
|
2288
|
-
|
|
2289
|
-
// subscribe() invokes fn synchronously with the current state, so the
|
|
2290
|
-
// initial frame lands in the queue automatically — no explicit prologue.
|
|
2291
|
-
const unsubscribe = controller.subscribe(enqueue);
|
|
2292
|
-
|
|
2293
|
-
stream.onAbort(() => {
|
|
2294
|
-
active = false;
|
|
2295
|
-
unsubscribe();
|
|
2296
|
-
wake();
|
|
2297
|
-
});
|
|
2298
|
-
|
|
2299
|
-
try {
|
|
2300
|
-
while (active) {
|
|
2301
|
-
while (queue.length > 0) {
|
|
2302
|
-
const next = queue.shift();
|
|
2303
|
-
if (!next) break;
|
|
2304
|
-
await stream.writeSSE(next);
|
|
2305
|
-
}
|
|
2306
|
-
if (!active) break;
|
|
2307
|
-
let heartbeat: ReturnType<typeof setTimeout> | null = null;
|
|
2308
|
-
const reason = await new Promise<'event' | 'heartbeat'>((r) => {
|
|
2309
|
-
resume = () => r('event');
|
|
2310
|
-
heartbeat = setTimeout(() => r('heartbeat'), SSE_HEARTBEAT_MS);
|
|
2311
|
-
});
|
|
2312
|
-
if (heartbeat) clearTimeout(heartbeat);
|
|
2313
|
-
resume = null;
|
|
2314
|
-
if (reason === 'heartbeat' && active) {
|
|
2315
|
-
await stream.write(': ping\n\n');
|
|
2316
|
-
}
|
|
2317
|
-
}
|
|
2318
|
-
} finally {
|
|
2319
|
-
unsubscribe();
|
|
2320
|
-
}
|
|
2321
|
-
});
|
|
2322
|
-
});
|
|
2323
|
-
|
|
2324
|
-
// GET /api/share/attributions — SSE stream of `node-patched` attribution
|
|
2325
|
-
// events for the host studio's apps/web UI (US-053). Fires once per accepted
|
|
2326
|
-
// op (peer-originated AND host-originated), carrying `{flowId, op, diff,
|
|
2327
|
-
// version, attributedTo, ts}`. No initial replay — the toast stack is for
|
|
2328
|
-
// live activity only.
|
|
2329
|
-
api.get('/share/attributions', (c) => {
|
|
2330
|
-
if (!share) return c.json({ error: 'share controller not configured' }, 503);
|
|
2331
|
-
const controller = share;
|
|
2332
|
-
|
|
2333
|
-
return streamSSE(c, async (stream) => {
|
|
2334
|
-
let active = true;
|
|
2335
|
-
const queue: Array<{ event: string; data: string }> = [];
|
|
2336
|
-
let resume: (() => void) | null = null;
|
|
2337
|
-
|
|
2338
|
-
const wake = () => {
|
|
2339
|
-
if (resume) {
|
|
2340
|
-
const r = resume;
|
|
2341
|
-
resume = null;
|
|
2342
|
-
r();
|
|
2343
|
-
}
|
|
2344
|
-
};
|
|
2345
|
-
|
|
2346
|
-
const enqueue = (event: AttributionEvent) => {
|
|
2347
|
-
queue.push({ event: 'attribution', data: JSON.stringify(event) });
|
|
2348
|
-
wake();
|
|
2349
|
-
};
|
|
2350
|
-
|
|
2351
|
-
const unsubscribe = controller.subscribeAttributions(enqueue);
|
|
2352
|
-
|
|
2353
|
-
stream.onAbort(() => {
|
|
2354
|
-
active = false;
|
|
2355
|
-
unsubscribe();
|
|
2356
|
-
wake();
|
|
2357
|
-
});
|
|
2358
|
-
|
|
2359
|
-
try {
|
|
2360
|
-
while (active) {
|
|
2361
|
-
while (queue.length > 0) {
|
|
2362
|
-
const next = queue.shift();
|
|
2363
|
-
if (!next) break;
|
|
2364
|
-
await stream.writeSSE(next);
|
|
2365
|
-
}
|
|
2366
|
-
if (!active) break;
|
|
2367
|
-
let heartbeat: ReturnType<typeof setTimeout> | null = null;
|
|
2368
|
-
const reason = await new Promise<'event' | 'heartbeat'>((r) => {
|
|
2369
|
-
resume = () => r('event');
|
|
2370
|
-
heartbeat = setTimeout(() => r('heartbeat'), SSE_HEARTBEAT_MS);
|
|
2371
|
-
});
|
|
2372
|
-
if (heartbeat) clearTimeout(heartbeat);
|
|
2373
|
-
resume = null;
|
|
2374
|
-
if (reason === 'heartbeat' && active) {
|
|
2375
|
-
await stream.write(': ping\n\n');
|
|
2376
|
-
}
|
|
2377
|
-
}
|
|
2378
|
-
} finally {
|
|
2379
|
-
unsubscribe();
|
|
2380
|
-
}
|
|
2381
|
-
});
|
|
2382
|
-
});
|
|
2383
|
-
|
|
2384
2093
|
// Global registry channel — broadcasts `registry:reload` when an external
|
|
2385
2094
|
// process (e.g. the CLI) writes to ~/.seeflow/registry.json. Subscribers
|
|
2386
2095
|
// re-fetch the flow list. The channel id is the internal sentinel from
|
package/src/server.ts
CHANGED
|
@@ -14,7 +14,6 @@ import { seeflowHome } from './paths.ts';
|
|
|
14
14
|
import { type ProcessSpawner, defaultProcessSpawner } from './process-spawner.ts';
|
|
15
15
|
import { type RegistryWatcher, createRegistryWatcher } from './registry-watcher.ts';
|
|
16
16
|
import { type Registry, createRegistry, manifestOnlyEntryFilter } from './registry.ts';
|
|
17
|
-
import { type ShareController, createShareController, resolveHostDisplayName } from './share.ts';
|
|
18
17
|
import type { Spawner } from './shellout.ts';
|
|
19
18
|
import { type StatusRunner, createStatusRunner } from './status-runner.ts';
|
|
20
19
|
import { type FlowWatcher, createWatcher } from './watcher.ts';
|
|
@@ -80,16 +79,8 @@ export interface CreateAppOptions {
|
|
|
80
79
|
/** Override the icon installer's fetcher. Production uses fetchWithProgress
|
|
81
80
|
* (real network); integration tests inject a fixture-returning closure. */
|
|
82
81
|
iconFetcher?: IconFetcher;
|
|
83
|
-
/** Inject a Live Share controller. Defaults to one pointed at
|
|
84
|
-
* https://seeflow.dev (SEEFLOW_SHARE_RELAY_URL) with share URLs rooted at
|
|
85
|
-
* https://seeflow.dev/share (SEEFLOW_SHARE_URL_BASE). Tests inject a fake
|
|
86
|
-
* to exercise the /api/share/* routes without a real relay. */
|
|
87
|
-
share?: ShareController;
|
|
88
82
|
}
|
|
89
83
|
|
|
90
|
-
const DEFAULT_SHARE_RELAY_URL = 'https://seeflow.dev';
|
|
91
|
-
const DEFAULT_SHARE_URL_BASE = 'https://seeflow.dev/share';
|
|
92
|
-
|
|
93
84
|
const DEFAULT_VITE_DEV_URL = 'http://localhost:5173';
|
|
94
85
|
const DEFAULT_STATIC_ROOT = resolvePath(import.meta.dir, '../dist/web');
|
|
95
86
|
|
|
@@ -117,16 +108,6 @@ export function createApp(options: CreateAppOptions = {}): Hono {
|
|
|
117
108
|
options.statusRunner ??
|
|
118
109
|
createStatusRunner({ registry, events, spawner: defaultProcessSpawner });
|
|
119
110
|
const iconJobs = options.iconJobs ?? createJobRegistry();
|
|
120
|
-
const share =
|
|
121
|
-
options.share ??
|
|
122
|
-
createShareController({
|
|
123
|
-
relayHttpUrl: process.env.SEEFLOW_SHARE_RELAY_URL ?? DEFAULT_SHARE_RELAY_URL,
|
|
124
|
-
shareUrlBase: process.env.SEEFLOW_SHARE_URL_BASE ?? DEFAULT_SHARE_URL_BASE,
|
|
125
|
-
eventBus: events,
|
|
126
|
-
flowIdsForBroadcast: () => registry.list().map((e) => e.id),
|
|
127
|
-
hostDisplayName: resolveHostDisplayName(),
|
|
128
|
-
operationsDeps: { registry, ...(watcher ? { watcher } : {}) },
|
|
129
|
-
});
|
|
130
111
|
|
|
131
112
|
if (watcher && (options.watchAllOnBoot ?? true)) {
|
|
132
113
|
watcher.watchAll();
|
|
@@ -190,7 +171,6 @@ export function createApp(options: CreateAppOptions = {}): Hono {
|
|
|
190
171
|
iconJobs,
|
|
191
172
|
iconCacheRoot: options.iconCacheRoot,
|
|
192
173
|
iconFetcher: options.iconFetcher,
|
|
193
|
-
share,
|
|
194
174
|
}),
|
|
195
175
|
);
|
|
196
176
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{U as a,C as n}from"./mermaid.core-Cmch2GTm.js";const t=(r,o)=>a.lang.round(n.parse(r)[o]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,a as s,c as e,C as t}from"./chunk-727SXJPM-irL9oAdE.js";import{a as i}from"./mermaid.core-Cmch2GTm.js";import"./index-08hmlCqO.js";import"./chunk-FMBD7UC4-Z48rYWhG.js";import"./chunk-ND2GUHAM-BgYSDKdi.js";import"./chunk-55IACEB6-YeyA7Efg.js";import"./chunk-2J33WTMH-CYHRe8M7.js";import"./purify.es-CLGrRn1w.js";import"./step-CWvwoXpJ.js";var b={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{b as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,a as s,c as e,C as t}from"./chunk-727SXJPM-irL9oAdE.js";import{a as i}from"./mermaid.core-Cmch2GTm.js";import"./index-08hmlCqO.js";import"./chunk-FMBD7UC4-Z48rYWhG.js";import"./chunk-ND2GUHAM-BgYSDKdi.js";import"./chunk-55IACEB6-YeyA7Efg.js";import"./chunk-2J33WTMH-CYHRe8M7.js";import"./purify.es-CLGrRn1w.js";import"./step-CWvwoXpJ.js";var b={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{b as diagram};
|