@catalyst-cloud/sdk 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +81 -144
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/live-sync-client.d.ts +11 -11
- package/dist/live-sync-client.d.ts.map +1 -1
- package/dist/live-sync-client.js +12 -14
- package/dist/live-sync-client.js.map +1 -1
- package/dist/types.d.ts +14 -15
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +6 -10
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,129 +1,66 @@
|
|
|
1
1
|
# @catalyst-cloud/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Keep a live local copy of your Linear and GitHub project data — pushed to you in real time, without polling rate limits or webhook tunnels.**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
Object and exposes it as a `change_log` feed. This SDK is the **push** consumer of that feed: it opens
|
|
7
|
-
an outbound WebSocket to a tenant's Mirror DO (`{baseUrl}/connect`), sends a cursor-replay request on
|
|
8
|
-
every (re)connect, and hands you each pushed change so you can land it into your own store.
|
|
5
|
+
When you run coding agents at scale — dozens or more working in parallel — they all need the same live view of your Linear and GitHub state to coordinate, and you quickly hit the rate limits of the very systems your team depends on. catalyst-cloud is a service that mirrors your Linear + GitHub state **once** and pushes every change out to your fleet; this SDK is how your code subscribes to that stream and keeps a local copy current.
|
|
9
6
|
|
|
10
|
-
|
|
11
|
-
browser, in Node (>=22), or in Bun**. It is the single push transport behind both the host replicas
|
|
12
|
-
and the web UI per [ADR-0008](https://github.com/coalesce-labs/catalyst-cloud/blob/main/docs/adr/0008-catalyst-host-local-replica-consumption.md)
|
|
13
|
-
(host reads from an SDK-managed per-host replica) and [ADR-0009](https://github.com/coalesce-labs/catalyst-cloud/blob/main/docs/adr/0009-browser-live-updates-over-websocket.md)
|
|
14
|
-
(browser live updates over a **hibernating** WebSocket, not SSE — so an idle tab can't pin the DO).
|
|
7
|
+
## Why
|
|
15
8
|
|
|
16
|
-
|
|
9
|
+
- **Real-time updates, no tunnels.** catalyst-cloud is the single webhook subscriber for your Linear and GitHub. Changes are **pushed** to you over an outbound connection — so you never stand up a public endpoint, smee/ngrok tunnel, or webhook gateway just to hear about an update locally.
|
|
10
|
+
- **One subscriber, not N.** Your whole fleet reads from the shared mirror instead of each agent polling Linear and GitHub directly — so a large fleet doesn't multiply load against those rate limits.
|
|
11
|
+
- **A local copy, not just a feed.** You don't get a firehose of events to babysit — you get your data, kept current, that you can query locally.
|
|
17
12
|
|
|
18
|
-
|
|
19
|
-
npm install @catalyst-cloud/sdk
|
|
20
|
-
```
|
|
13
|
+
## Coverage
|
|
21
14
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
- **Linear** — issues, projects, cycles, initiatives, comments, labels, history, and relations. **Mature.**
|
|
16
|
+
- **GitHub** — pull requests, checks, commit statuses, and reviews. **Maturing.**
|
|
17
|
+
- **Knowledge base** — thoughts / memories. **On the roadmap.**
|
|
25
18
|
|
|
26
|
-
##
|
|
19
|
+
## Requirements
|
|
27
20
|
|
|
28
|
-
|
|
29
|
-
(`bun:sqlite`, OPFS, in-memory) or how you authenticate — both are **injected**:
|
|
21
|
+
A catalyst-cloud account and an auth token. (In the browser, a same-origin session cookie is used instead — no token in the page.)
|
|
30
22
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
- **`reseed`** — an async callback returning the fresh cursor after a full re-seed. Called on a
|
|
35
|
-
`{type:"resync"}` underflow frame (and before the first connect if you have no cursor). A host pulls
|
|
36
|
-
`/snapshot` into SQLite here; a browser re-runs its own seed.
|
|
37
|
-
- **`getCursor` / `onChange`** — you own the cursor and the writes; the client drives the wire.
|
|
38
|
-
- **`onStatus`** — connection lifecycle (`connecting` / `live` / `reconnecting` / `resyncing` /
|
|
39
|
-
`error` / `stopped`) so a UI can show "live" vs "reconnecting".
|
|
40
|
-
|
|
41
|
-
It reconnects with **capped exponential backoff** (1 s → ×2 → 30 s ceiling) and never throws out of
|
|
42
|
-
the event loop. There is **no client-side keepalive** — re-implementing the WebSocket heartbeat would
|
|
43
|
-
re-pin the hibernating DO, defeating ADR-0009.
|
|
44
|
-
|
|
45
|
-
## Usage
|
|
46
|
-
|
|
47
|
-
### Browser (cookie auth, no token)
|
|
48
|
-
|
|
49
|
-
In the browser there is **no token**: pass `auth: { kind: "cookie" }` and the same-origin session
|
|
50
|
-
cookie rides the WebSocket upgrade automatically. The SDK appends nothing secret to the URL, so a
|
|
51
|
-
token can never leak from a browser tab. Storage is yours — here it is a trivial in-memory map; in a
|
|
52
|
-
real app you would seed from your own `/snapshot` fetch into OPFS / IndexedDB.
|
|
53
|
-
|
|
54
|
-
```ts
|
|
55
|
-
import { LiveSyncClient } from "@catalyst-cloud/sdk";
|
|
23
|
+
```sh
|
|
24
|
+
npm install @catalyst-cloud/sdk
|
|
25
|
+
```
|
|
56
26
|
|
|
57
|
-
|
|
58
|
-
let cursor: number | null = null;
|
|
27
|
+
## Today, and where this is going
|
|
59
28
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
accountId: "tenant-0",
|
|
63
|
-
auth: { kind: "cookie" }, // same-origin cookie rides the upgrade; no token ever in the URL
|
|
29
|
+
- **Today** — the SDK is the live-sync client. It keeps your local store current: it manages the connection, replays anything you missed while disconnected, and recovers automatically. You provide the storage (any SQLite, OPFS in the browser, or in-memory) and apply each change.
|
|
30
|
+
- **Coming** — a fully managed local replica: a strongly-typed SQLite database you read **directly via [Drizzle ORM](https://orm.drizzle.team)**, with the syncing handled for you. The typed read layer already exists; we're folding it into the SDK so the database is something you read, not something you assemble.
|
|
64
31
|
|
|
65
|
-
|
|
66
|
-
reseed: async () => {
|
|
67
|
-
const snap = (await (await fetch("/api/v1/snapshot?account=tenant-0")).json()) as {
|
|
68
|
-
cursor: number;
|
|
69
|
-
issues: { id: string }[];
|
|
70
|
-
};
|
|
71
|
-
issues.clear();
|
|
72
|
-
for (const row of snap.issues) issues.set(row.id, row);
|
|
73
|
-
cursor = snap.cursor;
|
|
74
|
-
return snap.cursor;
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
getCursor: () => cursor,
|
|
78
|
-
onChange: (frame) => {
|
|
79
|
-
if (frame.op === "delete") issues.delete(frame.entityId);
|
|
80
|
-
else issues.set(frame.entityId, frame.row);
|
|
81
|
-
cursor = frame.seq;
|
|
82
|
-
},
|
|
83
|
-
onStatus: (s) => setConnectionBadge(s), // "live" / "reconnecting" / …
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
void client.start(); // never await in the browser — it resolves only on stop()
|
|
87
|
-
// on teardown (component unmount, page hide):
|
|
88
|
-
client.stop();
|
|
89
|
-
```
|
|
32
|
+
## Usage
|
|
90
33
|
|
|
91
|
-
### Backend / host
|
|
34
|
+
### Backend / agent host
|
|
92
35
|
|
|
93
|
-
|
|
94
|
-
reaches the DO) and lands every frame into a local SQLite replica. On a `{type:"resync"}` underflow
|
|
95
|
-
(or the very first run with no cursor) it pulls a full `/snapshot` and rewrites the table:
|
|
36
|
+
Authenticate with a token and land every change into a local SQLite replica. On the first run (or after a long disconnect) the SDK asks you to re-seed from a snapshot; after that you just apply each pushed change.
|
|
96
37
|
|
|
97
38
|
```ts
|
|
98
|
-
import { Database } from "bun:sqlite"; //
|
|
39
|
+
import { Database } from "bun:sqlite"; // Node: better-sqlite3 with the same calls
|
|
99
40
|
import { LiveSyncClient } from "@catalyst-cloud/sdk";
|
|
100
41
|
|
|
101
42
|
const db = new Database("replica.sqlite");
|
|
102
43
|
db.run("CREATE TABLE IF NOT EXISTS issues (id TEXT PRIMARY KEY, row TEXT, seq INTEGER)");
|
|
103
44
|
db.run("CREATE TABLE IF NOT EXISTS sync_state (k TEXT PRIMARY KEY, cursor INTEGER)");
|
|
104
45
|
|
|
105
|
-
const baseUrl = process.env.CATALYST_CLOUD_BASE_URL!; // incl. /api/v1
|
|
106
|
-
const token = process.env.ADMIN_TOKEN!;
|
|
107
|
-
|
|
108
46
|
const client = new LiveSyncClient({
|
|
109
|
-
baseUrl
|
|
47
|
+
baseUrl: process.env.CATALYST_CLOUD_BASE_URL!,
|
|
110
48
|
accountId: "tenant-0",
|
|
111
|
-
auth: { kind: "token", token },
|
|
49
|
+
auth: { kind: "token", token: process.env.CATALYST_CLOUD_TOKEN! },
|
|
112
50
|
|
|
113
|
-
// Full re-seed:
|
|
114
|
-
// Must resolve to the fresh cursor (the snapshot's max change_log.seq).
|
|
51
|
+
// Full re-seed: fetch the current snapshot and rewrite the replica; resolve to the fresh cursor.
|
|
115
52
|
reseed: async () => {
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
53
|
+
const snap = (await (
|
|
54
|
+
await fetch(`${process.env.CATALYST_CLOUD_BASE_URL}/snapshot?account=tenant-0`, {
|
|
55
|
+
headers: { authorization: `Bearer ${process.env.CATALYST_CLOUD_TOKEN}` },
|
|
56
|
+
})
|
|
57
|
+
).json()) as { cursor: number; issues: { id: string }[] };
|
|
120
58
|
const tx = db.transaction(() => {
|
|
121
59
|
db.run("DELETE FROM issues");
|
|
122
60
|
const ins = db.prepare("INSERT INTO issues (id, row, seq) VALUES (?, ?, ?)");
|
|
123
61
|
for (const row of snap.issues) ins.run(row.id, JSON.stringify(row), snap.cursor);
|
|
124
62
|
db.run(
|
|
125
|
-
"INSERT INTO sync_state (k, cursor) VALUES ('replica', ?) "
|
|
126
|
-
"ON CONFLICT(k) DO UPDATE SET cursor = excluded.cursor",
|
|
63
|
+
"INSERT INTO sync_state (k, cursor) VALUES ('replica', ?) ON CONFLICT(k) DO UPDATE SET cursor = excluded.cursor",
|
|
127
64
|
[snap.cursor],
|
|
128
65
|
);
|
|
129
66
|
});
|
|
@@ -131,76 +68,76 @@ const client = new LiveSyncClient({
|
|
|
131
68
|
return snap.cursor;
|
|
132
69
|
},
|
|
133
70
|
|
|
134
|
-
// Own the cursor + the writes; the client just drives the wire.
|
|
135
71
|
getCursor: () =>
|
|
136
72
|
(db.query("SELECT cursor FROM sync_state WHERE k = 'replica'").get() as
|
|
137
73
|
| { cursor: number }
|
|
138
74
|
| undefined)?.cursor ?? null,
|
|
139
75
|
|
|
76
|
+
// Apply each pushed change to your local copy.
|
|
140
77
|
onChange: (frame) => {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
[frame.entityId, JSON.stringify(frame.row), frame.seq],
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
db.run("UPDATE sync_state SET cursor = ? WHERE k = 'replica'", [frame.seq]);
|
|
152
|
-
});
|
|
153
|
-
tx();
|
|
78
|
+
if (frame.op === "delete") db.run("DELETE FROM issues WHERE id = ?", [frame.entityId]);
|
|
79
|
+
else
|
|
80
|
+
db.run(
|
|
81
|
+
"INSERT INTO issues (id, row, seq) VALUES (?, ?, ?) ON CONFLICT(id) DO UPDATE SET row = excluded.row, seq = excluded.seq",
|
|
82
|
+
[frame.entityId, JSON.stringify(frame.row), frame.seq],
|
|
83
|
+
);
|
|
84
|
+
db.run("UPDATE sync_state SET cursor = ? WHERE k = 'replica'", [frame.seq]);
|
|
154
85
|
},
|
|
155
86
|
});
|
|
156
87
|
|
|
157
|
-
await client.start(); // resolves only when stop() is called — keeps the process alive between
|
|
88
|
+
await client.start(); // resolves only when stop() is called — keeps the process alive between changes
|
|
158
89
|
```
|
|
159
90
|
|
|
160
|
-
|
|
161
|
-
> package: `wsFactory: (url) => new WebSocket(url) as unknown as WebSocketLike`. Node 22+ and Bun
|
|
162
|
-
> expose a global `WebSocket`, so no factory is needed.
|
|
91
|
+
### Browser
|
|
163
92
|
|
|
164
|
-
|
|
93
|
+
In the browser there's no token: the same-origin session cookie authenticates the connection, and the SDK never puts anything secret in the URL. Storage is yours (here, a `Map`; in a real app, OPFS / IndexedDB).
|
|
165
94
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
| `LiveSyncClient` | The client. `new LiveSyncClient(opts)`, then `start()` / `stop()` / `connectUrl()`. |
|
|
169
|
-
| `LiveSyncClientOptions` | The full options shape (auth, reseed, getCursor, onChange, onStatus, backoff, wsFactory, log). |
|
|
170
|
-
| `AuthStrategy` | `{ kind: "token"; token }` (backend) or `{ kind: "cookie" }` (browser). |
|
|
171
|
-
| `LiveSyncStatus` | `"connecting"` · `"live"` · `"reconnecting"` · `"resyncing"` · `"error"` · `"stopped"`. |
|
|
172
|
-
| `WebSocketLike` / `WebSocketFactory` | The minimal structural WS surface + the injectable factory (for old Node / tests). |
|
|
173
|
-
| `ChangeFrame` · `ResyncFrame` · `SyncFrame` · `ServerFrame` | The wire frames (see below). |
|
|
174
|
-
| `EntityName` · `ChangeOp` · `ENTITY_NAMES` · `CHANGE_OPS` | The canonical table / op contract (types + frozen runtime arrays). |
|
|
175
|
-
| `buildConnectUrl` · `parseFrame` · `toWsOrigin` | The pure helpers the client is built from (exported for diagnostics/tests). |
|
|
95
|
+
```ts
|
|
96
|
+
import { LiveSyncClient } from "@catalyst-cloud/sdk";
|
|
176
97
|
|
|
177
|
-
|
|
178
|
-
|
|
98
|
+
const issues = new Map<string, unknown>();
|
|
99
|
+
let cursor: number | null = null;
|
|
179
100
|
|
|
180
|
-
|
|
101
|
+
const client = new LiveSyncClient({
|
|
102
|
+
baseUrl: "https://app.example.com/api/v1", // same origin as the page
|
|
103
|
+
accountId: "tenant-0",
|
|
104
|
+
auth: { kind: "cookie" },
|
|
105
|
+
reseed: async () => {
|
|
106
|
+
const snap = (await (await fetch("/api/v1/snapshot?account=tenant-0")).json()) as {
|
|
107
|
+
cursor: number;
|
|
108
|
+
issues: { id: string }[];
|
|
109
|
+
};
|
|
110
|
+
issues.clear();
|
|
111
|
+
for (const row of snap.issues) issues.set(row.id, row);
|
|
112
|
+
cursor = snap.cursor;
|
|
113
|
+
return snap.cursor;
|
|
114
|
+
},
|
|
115
|
+
getCursor: () => cursor,
|
|
116
|
+
onChange: (frame) => {
|
|
117
|
+
if (frame.op === "delete") issues.delete(frame.entityId);
|
|
118
|
+
else issues.set(frame.entityId, frame.row);
|
|
119
|
+
cursor = frame.seq;
|
|
120
|
+
},
|
|
121
|
+
onStatus: (s) => setConnectionBadge(s), // "live" / "reconnecting" / …
|
|
122
|
+
});
|
|
181
123
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
SDK has **no dependency** on that internal package. A contract test pins the literal members so drift
|
|
186
|
-
from the monorepo is caught.
|
|
124
|
+
void client.start(); // never await in the browser
|
|
125
|
+
// client.stop() on teardown
|
|
126
|
+
```
|
|
187
127
|
|
|
188
|
-
|
|
128
|
+
Requires a platform `WebSocket` (browser, Bun, or Node ≥22). On older Node, inject a `wsFactory` that wraps the [`ws`](https://www.npmjs.com/package/ws) package.
|
|
189
129
|
|
|
190
|
-
|
|
191
|
-
(the catalyst-cloud monorepo consumes it via a `file:` dependency). Workflow:
|
|
130
|
+
## API
|
|
192
131
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
132
|
+
| Export | What it is |
|
|
133
|
+
| --- | --- |
|
|
134
|
+
| `LiveSyncClient` | The client — `new LiveSyncClient(opts)`, then `start()` / `stop()`. |
|
|
135
|
+
| `LiveSyncClientOptions` | The options shape (auth, reseed, getCursor, onChange, onStatus, backoff, wsFactory, log). |
|
|
136
|
+
| `AuthStrategy` | `{ kind: "token"; token }` (backend) or `{ kind: "cookie" }` (browser). |
|
|
137
|
+
| `LiveSyncStatus` | `"connecting"` · `"live"` · `"reconnecting"` · `"resyncing"` · `"error"` · `"stopped"`. |
|
|
138
|
+
| `ChangeFrame` · `EntityName` · `ChangeOp` | The change shape + the entity/op contract. |
|
|
199
139
|
|
|
200
|
-
|
|
201
|
-
runs install + build + test on every push and PR. Releases are cut by bumping `version` and running
|
|
202
|
-
`npm publish` (the `prepublishOnly` hook rebuilds `dist/` first; the package is published with public
|
|
203
|
-
access via `publishConfig`).
|
|
140
|
+
`start()` resolves only when `stop()` is called. On a backend, `await` it to keep the process alive; in a browser, never await it — just call `stop()` on teardown.
|
|
204
141
|
|
|
205
142
|
## License
|
|
206
143
|
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// @catalyst-cloud/sdk — public entrypoint.
|
|
2
2
|
//
|
|
3
|
-
// The
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// The live-sync client for the catalyst-cloud change feed (browser + node/bun). Open a WebSocket to a
|
|
4
|
+
// tenant's mirror, send a cursor-replay request on every (re)connect, and apply each pushed change
|
|
5
|
+
// into your own store.
|
|
6
6
|
//
|
|
7
7
|
// The class is storage-agnostic and auth-injected: see {@link LiveSyncClient}.
|
|
8
8
|
export { LiveSyncClient, buildConnectUrl, parseFrame, toWsOrigin, } from "./live-sync-client.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,EAAE;AACF,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,EAAE;AACF,sGAAsG;AACtG,mGAAmG;AACnG,uBAAuB;AACvB,EAAE;AACF,+EAA+E;AAE/E,OAAO,EACL,cAAc,EACd,eAAe,EACf,UAAU,EACV,UAAU,GAOX,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EACL,YAAY,EACZ,UAAU,GAQX,MAAM,YAAY,CAAC"}
|
|
@@ -19,10 +19,10 @@ export type WebSocketFactory = (url: string) => WebSocketLike;
|
|
|
19
19
|
/**
|
|
20
20
|
* How the consumer proves it may open `/connect`.
|
|
21
21
|
*
|
|
22
|
-
* • `token` — a
|
|
23
|
-
* cannot set an Authorization header, so the token rides the URL as `?token=`. The
|
|
24
|
-
* constant-time compares it
|
|
25
|
-
*
|
|
22
|
+
* • `token` — a service bearer token. The WHATWG WebSocket constructor
|
|
23
|
+
* cannot set an Authorization header, so the token rides the URL as `?token=`. The service
|
|
24
|
+
* constant-time compares it and STRIPS it before forwarding internally. Use this ONLY on a
|
|
25
|
+
* trusted backend — never the browser.
|
|
26
26
|
* • `cookie` — append NOTHING to the URL. The browser's same-origin session cookie rides the
|
|
27
27
|
* WebSocket upgrade automatically. This makes it impossible to leak a token from the browser path.
|
|
28
28
|
*/
|
|
@@ -34,20 +34,20 @@ export type AuthStrategy = {
|
|
|
34
34
|
};
|
|
35
35
|
/** Connection lifecycle, surfaced via `onStatus` so a consumer can drive UI. */
|
|
36
36
|
export type LiveSyncStatus = "connecting" | "live" | "reconnecting" | "resyncing" | "error" | "stopped";
|
|
37
|
-
/** Structured log levels
|
|
37
|
+
/** Structured log levels. */
|
|
38
38
|
export type LogLevel = "info" | "warn" | "error";
|
|
39
39
|
/** Configuration for a {@link LiveSyncClient}. */
|
|
40
40
|
export interface LiveSyncClientOptions {
|
|
41
41
|
/**
|
|
42
|
-
* The
|
|
42
|
+
* The service's public origin, http(s), INCLUDING any versioned path prefix (e.g.
|
|
43
43
|
* "https://api.example/api/v1"). The scheme is swapped to ws(s) and `connectPath` is appended
|
|
44
44
|
* verbatim (path-preserving). A trailing slash is trimmed.
|
|
45
45
|
*/
|
|
46
46
|
baseUrl: string;
|
|
47
|
-
/** The tenant id = the
|
|
47
|
+
/** The tenant id = the mirror's name; sent as `?account=` on the connect URL. */
|
|
48
48
|
accountId: string;
|
|
49
49
|
/**
|
|
50
|
-
* The connect route. Default "/connect"; the
|
|
50
|
+
* The connect route. Default "/connect"; the service dual-serves it at "/api/v1/connect" too, so a
|
|
51
51
|
* path-prefixed `baseUrl` ("…/api/v1") with the default "/connect" resolves to "…/api/v1/connect".
|
|
52
52
|
*/
|
|
53
53
|
connectPath?: string;
|
|
@@ -61,7 +61,7 @@ export interface LiveSyncClientOptions {
|
|
|
61
61
|
*/
|
|
62
62
|
reseed: () => Promise<number>;
|
|
63
63
|
/**
|
|
64
|
-
* Read the consumer's durable cursor (the last applied
|
|
64
|
+
* Read the consumer's durable cursor (the last applied feed seq), or null/undefined if it has
|
|
65
65
|
* never seeded. Drives the `{type:"sync", after}` catch-up request. Return `null` to send
|
|
66
66
|
* `after: -1` (replay from the start).
|
|
67
67
|
*/
|
|
@@ -135,11 +135,11 @@ export declare class LiveSyncClient {
|
|
|
135
135
|
/** Detach handlers BEFORE closing so a programmatic close can't re-enter scheduleReconnect. */
|
|
136
136
|
private closeSocket;
|
|
137
137
|
private scheduleReconnect;
|
|
138
|
-
/** Ask the
|
|
138
|
+
/** Ask the service to replay everything after our durable cursor. */
|
|
139
139
|
private sendSync;
|
|
140
140
|
private handleFrame;
|
|
141
141
|
/**
|
|
142
|
-
* Cursor underflow: the deltas we need were evicted from the
|
|
142
|
+
* Cursor underflow: the deltas we need were evicted from the service's retained change buffer. Close the socket
|
|
143
143
|
* (so no live frame interleaves with the re-seed), re-seed via the injected callback, then reconnect
|
|
144
144
|
* — which re-sends {type:"sync"} from the fresh cursor. `resyncing` guards against a second resync
|
|
145
145
|
* frame and suppresses scheduleReconnect for the duration so we reopen exactly once.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live-sync-client.d.ts","sourceRoot":"","sources":["../src/live-sync-client.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"live-sync-client.d.ts","sourceRoot":"","sources":["../src/live-sync-client.ts"],"names":[],"mappings":"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"}
|
package/dist/live-sync-client.js
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
// @catalyst-cloud/sdk — LiveSyncClient: the
|
|
1
|
+
// @catalyst-cloud/sdk — LiveSyncClient: the live-sync client.
|
|
2
2
|
//
|
|
3
|
-
// The push transport for the catalyst-cloud change
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
3
|
+
// The push transport for the catalyst-cloud change feed. It opens an OUTBOUND WebSocket to the
|
|
4
|
+
// tenant's mirror (`{baseUrl}{connectPath}`) and lets the service push each change the instant it
|
|
5
|
+
// lands. On every (re)connect it sends `{type:"sync", after:<cursor>}` so the service replays — in
|
|
6
|
+
// seq order — everything the consumer missed; a `{type:"resync"}` frame (cursor underflow) triggers a
|
|
7
|
+
// full re-seed via the injected `reseed` callback. It reconnects with capped exponential backoff and
|
|
8
|
+
// never throws out of the event loop.
|
|
9
9
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// any host-only assumption. Those are INJECTED:
|
|
10
|
+
// It is deliberately transport-only and storage-agnostic: it does NOT know about a specific storage
|
|
11
|
+
// engine, snapshot endpoint, or auth token — any host-only assumption is INJECTED:
|
|
13
12
|
//
|
|
14
13
|
// • auth — {kind:"token", token} (host → ?token=) OR {kind:"cookie"} (browser → nothing; the
|
|
15
14
|
// same-origin cookie rides the upgrade). A cookie-kind client can never leak a token.
|
|
@@ -23,8 +22,7 @@
|
|
|
23
22
|
// node-only import like 'ws'.
|
|
24
23
|
//
|
|
25
24
|
// There is intentionally NO setInterval keepalive: the WHATWG WebSocket ping/pong is handled by the
|
|
26
|
-
// platform and a re-implemented heartbeat would
|
|
27
|
-
// whole point of moving off SSE.
|
|
25
|
+
// platform, and a re-implemented heartbeat would keep the connection needlessly active.
|
|
28
26
|
/** Resolve the runtime global WebSocket, or fail with an actionable message. */
|
|
29
27
|
function defaultWsFactory(url) {
|
|
30
28
|
const Ctor = globalThis.WebSocket;
|
|
@@ -207,7 +205,7 @@ export class LiveSyncClient {
|
|
|
207
205
|
this.openSocket();
|
|
208
206
|
}, delay);
|
|
209
207
|
}
|
|
210
|
-
/** Ask the
|
|
208
|
+
/** Ask the service to replay everything after our durable cursor. */
|
|
211
209
|
sendSync() {
|
|
212
210
|
const after = this.getCursor() ?? -1;
|
|
213
211
|
const frame = { type: "sync", after };
|
|
@@ -240,7 +238,7 @@ export class LiveSyncClient {
|
|
|
240
238
|
}
|
|
241
239
|
}
|
|
242
240
|
/**
|
|
243
|
-
* Cursor underflow: the deltas we need were evicted from the
|
|
241
|
+
* Cursor underflow: the deltas we need were evicted from the service's retained change buffer. Close the socket
|
|
244
242
|
* (so no live frame interleaves with the re-seed), re-seed via the injected callback, then reconnect
|
|
245
243
|
* — which re-sends {type:"sync"} from the fresh cursor. `resyncing` guards against a second resync
|
|
246
244
|
* frame and suppresses scheduleReconnect for the duration so we reopen exactly once.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live-sync-client.js","sourceRoot":"","sources":["../src/live-sync-client.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"live-sync-client.js","sourceRoot":"","sources":["../src/live-sync-client.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
/** The tenant id
|
|
1
|
+
/** The tenant id (also the mirror's name). `"tenant-0"` is an example tenant. */
|
|
2
2
|
export type AccountId = string;
|
|
3
3
|
/**
|
|
4
|
-
* Canonical mirror table names — the `entity` field
|
|
4
|
+
* Canonical mirror table names — the `entity` field on the change-feed wire.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* erases to nothing and can't be checked).
|
|
6
|
+
* The runtime list `ENTITY_NAMES` below is the same set as an array so the contract test can assert
|
|
7
|
+
* membership at runtime (a bare `type` alias erases to nothing and can't be checked).
|
|
9
8
|
*/
|
|
10
9
|
export type EntityName = "issues" | "labels" | "users" | "issue_labels" | "relations" | "issue_history" | "projects" | "cycles" | "initiatives" | "project_initiatives" | "comments" | "pull_requests" | "check_runs" | "commit_statuses" | "reviews";
|
|
11
10
|
/**
|
|
@@ -14,28 +13,28 @@ export type EntityName = "issues" | "labels" | "users" | "issue_labels" | "relat
|
|
|
14
13
|
* the contract test asserts the two agree.
|
|
15
14
|
*/
|
|
16
15
|
export declare const ENTITY_NAMES: readonly ["issues", "labels", "users", "issue_labels", "relations", "issue_history", "projects", "cycles", "initiatives", "project_initiatives", "comments", "pull_requests", "check_runs", "commit_statuses", "reviews"];
|
|
17
|
-
/**
|
|
16
|
+
/** The change op — the change-feed wire contract. */
|
|
18
17
|
export type ChangeOp = "upsert" | "delete";
|
|
19
18
|
/** The `ChangeOp` union as a runtime array (same lockstep contract as `ENTITY_NAMES`). */
|
|
20
19
|
export declare const CHANGE_OPS: readonly ["upsert", "delete"];
|
|
21
20
|
/**
|
|
22
|
-
* A live change frame off the `/connect` WebSocket — the exact shape
|
|
23
|
-
*
|
|
21
|
+
* A live change frame off the `/connect` WebSocket — the exact shape the service broadcasts and
|
|
22
|
+
* replays. One change from the feed, serialized.
|
|
24
23
|
*/
|
|
25
24
|
export interface ChangeFrame {
|
|
26
25
|
type: "change";
|
|
27
26
|
accountId: AccountId;
|
|
28
|
-
/** The
|
|
27
|
+
/** The feed sequence — the monotonic cursor the replica advances to. */
|
|
29
28
|
seq: number;
|
|
30
29
|
entity: EntityName;
|
|
31
|
-
/** The
|
|
30
|
+
/** The entity's primary key (composite PKs joined with ':'). */
|
|
32
31
|
entityId: string;
|
|
33
32
|
op: ChangeOp;
|
|
34
33
|
/** The full normalized row for "upsert"; absent / partial for "delete". */
|
|
35
34
|
row?: Record<string, unknown>;
|
|
36
35
|
}
|
|
37
36
|
/**
|
|
38
|
-
* The underflow control frame: the consumer's cursor predates the
|
|
37
|
+
* The underflow control frame: the consumer's cursor predates the service's retained change buffer →
|
|
39
38
|
* the consumer must re-seed from a full /snapshot.
|
|
40
39
|
*/
|
|
41
40
|
export interface ResyncFrame {
|
|
@@ -44,14 +43,14 @@ export interface ResyncFrame {
|
|
|
44
43
|
}
|
|
45
44
|
/**
|
|
46
45
|
* The catch-up request the consumer sends on every (re)connect: "replay everything after this
|
|
47
|
-
* cursor, in seq order". The
|
|
48
|
-
* predates the retained
|
|
46
|
+
* cursor, in seq order". The service answers with the missed `ChangeFrame`s, or a `ResyncFrame` if
|
|
47
|
+
* `after` predates the retained buffer (cursor underflow).
|
|
49
48
|
*/
|
|
50
49
|
export interface SyncFrame {
|
|
51
50
|
type: "sync";
|
|
52
|
-
/** The durable cursor: the last
|
|
51
|
+
/** The durable cursor: the last feed seq the consumer has applied (-1 if none). */
|
|
53
52
|
after: number;
|
|
54
53
|
}
|
|
55
|
-
/** Any frame the
|
|
54
|
+
/** Any frame the service can push to a consumer over `/connect`. */
|
|
56
55
|
export type ServerFrame = ChangeFrame | ResyncFrame;
|
|
57
56
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAUA,iFAAiF;AACjF,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC;AAE/B;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAClB,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,cAAc,GACd,WAAW,GACX,eAAe,GACf,UAAU,GACV,QAAQ,GACR,aAAa,GACb,qBAAqB,GACrB,UAAU,GACV,eAAe,GACf,YAAY,GACZ,iBAAiB,GACjB,SAAS,CAAC;AAEd;;;;GAIG;AACH,eAAO,MAAM,YAAY,2NAgBiB,CAAC;AAE3C,qDAAqD;AACrD,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE3C,0FAA0F;AAC1F,eAAO,MAAM,UAAU,+BAA8D,CAAC;AAEtF;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,EAAE,SAAS,CAAC;IACrB,wEAAwE;IACxE,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,UAAU,CAAC;IACnB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,EAAE,EAAE,QAAQ,CAAC;IACb,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,CAAC;IACf,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,KAAK,EAAE,MAAM,CAAC;CACf;AAED,oEAAoE;AACpE,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,WAAW,CAAC"}
|
package/dist/types.js
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
// catalyst-cloud-sdk wire types — the change-feed contract, SELF-CONTAINED.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
// catalyst-cloud
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// NOT depend on the internal @catalyst-cloud/types workspace package — instead it owns its own copy
|
|
8
|
-
// of the contract, and a contract test (test/contract.test.ts) pins the literal members so any drift
|
|
9
|
-
// from the monorepo is caught.
|
|
3
|
+
// The `EntityName` table-name union, the `ChangeOp` op union, and the change/sync/resync frame shapes
|
|
4
|
+
// the catalyst-cloud service broadcasts over `/connect`. The SDK is published standalone and owns its
|
|
5
|
+
// own copy of this contract; a contract test (test/contract.test.ts) pins the literal members so any
|
|
6
|
+
// drift is caught.
|
|
10
7
|
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// app, and a Worker alike.
|
|
8
|
+
// This module is free of any runtime-specific or DOM imports — pure type declarations + plain JS
|
|
9
|
+
// runtime constants — so it imports cleanly into a browser bundle or a node/bun app alike.
|
|
14
10
|
/**
|
|
15
11
|
* The `EntityName` union as a runtime array, in the SAME ORDER as the type above. Frozen so consumers
|
|
16
12
|
* (and the contract test) can iterate the canonical table set. Kept in lockstep with `EntityName` —
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,EAAE;AACF,
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,4EAA4E;AAC5E,EAAE;AACF,sGAAsG;AACtG,sGAAsG;AACtG,qGAAqG;AACrG,mBAAmB;AACnB,EAAE;AACF,iGAAiG;AACjG,2FAA2F;AA4B3F;;;;GAIG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,QAAQ;IACR,QAAQ;IACR,OAAO;IACP,cAAc;IACd,WAAW;IACX,eAAe;IACf,UAAU;IACV,QAAQ;IACR,aAAa;IACb,qBAAqB;IACrB,UAAU;IACV,eAAe;IACf,YAAY;IACZ,iBAAiB;IACjB,SAAS;CAC+B,CAAC;AAK3C,0FAA0F;AAC1F,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAwC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@catalyst-cloud/sdk",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Keep a live local copy of your Linear and GitHub project data — pushed in real time, without polling rate limits or webhook tunnels.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Coalesce Labs",
|
|
7
7
|
"homepage": "https://github.com/coalesce-labs/catalyst-cloud-sdk#readme",
|