@dxos/feed 0.0.0 → 0.8.4-main.59c2e9b

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.
@@ -0,0 +1,342 @@
1
+ // src/feed-store.ts
2
+ import * as SqlClient from "@effect/sql/SqlClient";
3
+ import * as Effect from "effect/Effect";
4
+ import { Event } from "@dxos/async";
5
+
6
+ // src/protocol.ts
7
+ import * as Schema from "effect/Schema";
8
+ var FeedCursor = Schema.String.pipe(Schema.brand("@dxos/feed/FeedCursor"));
9
+ var Block = Schema.Struct({
10
+ /**
11
+ * Only set on query.
12
+ */
13
+ feedId: Schema.UndefinedOr(Schema.String),
14
+ actorId: Schema.String,
15
+ sequence: Schema.Number,
16
+ predActorId: Schema.NullOr(Schema.String),
17
+ predSequence: Schema.NullOr(Schema.Number),
18
+ position: Schema.NullOr(Schema.Number),
19
+ timestamp: Schema.Number,
20
+ data: Schema.Uint8Array,
21
+ /**
22
+ * Local insertion ID.
23
+ * Not replicated.
24
+ */
25
+ // TODO(dmaretskyi): Remove. Use cursors.
26
+ insertionId: Schema.optional(Schema.Number)
27
+ });
28
+ var QueryRequest = Schema.Struct({
29
+ requestId: Schema.optional(Schema.String),
30
+ // TODO(dmaretskyi): Make required.
31
+ spaceId: Schema.optional(Schema.String),
32
+ query: Schema.Union(Schema.Struct({
33
+ feedIds: Schema.Array(Schema.String)
34
+ }), Schema.Struct({
35
+ subscriptionId: Schema.String
36
+ }), Schema.Struct({
37
+ feedNamespace: Schema.optional(Schema.String)
38
+ })),
39
+ /**
40
+ * Get changes following this cursor (exclusive).
41
+ *
42
+ * Must not be used with `position`.
43
+ */
44
+ cursor: Schema.optional(FeedCursor),
45
+ /**
46
+ * Get changes following this position.
47
+ *
48
+ * Must not be used with `cursor`.
49
+ */
50
+ position: Schema.optional(Schema.Number),
51
+ limit: Schema.optional(Schema.Number)
52
+ });
53
+ var QueryResponse = Schema.Struct({
54
+ requestId: Schema.optional(Schema.String),
55
+ nextCursor: FeedCursor,
56
+ blocks: Schema.Array(Block)
57
+ });
58
+ var SubscribeRequest = Schema.Struct({
59
+ requestId: Schema.optional(Schema.String),
60
+ feedIds: Schema.Array(Schema.String),
61
+ spaceId: Schema.optional(Schema.String)
62
+ });
63
+ var SubscribeResponse = Schema.Struct({
64
+ requestId: Schema.optional(Schema.String),
65
+ subscriptionId: Schema.String,
66
+ expiresAt: Schema.Number
67
+ });
68
+ var AppendRequest = Schema.Struct({
69
+ requestId: Schema.optional(Schema.String),
70
+ spaceId: Schema.String,
71
+ namespace: Schema.String,
72
+ feedId: Schema.String,
73
+ blocks: Schema.Array(Block)
74
+ });
75
+ var AppendResponse = Schema.Struct({
76
+ requestId: Schema.optional(Schema.String),
77
+ positions: Schema.Array(Schema.Number)
78
+ });
79
+
80
+ // src/feed-store.ts
81
+ var FeedStore = class {
82
+ _options;
83
+ constructor(_options) {
84
+ this._options = _options;
85
+ }
86
+ onNewBlocks = new Event();
87
+ migrate = Effect.fn("FeedStore.migrate")(function* () {
88
+ const sql = yield* SqlClient.SqlClient;
89
+ yield* sql`CREATE TABLE IF NOT EXISTS feeds (
90
+ feedPrivateId INTEGER PRIMARY KEY AUTOINCREMENT,
91
+ spaceId TEXT NOT NULL,
92
+ feedId TEXT NOT NULL,
93
+ feedNamespace TEXT
94
+ )`;
95
+ yield* sql`CREATE UNIQUE INDEX IF NOT EXISTS idx_feeds_spaceId_feedId ON feeds(spaceId, feedId)`;
96
+ yield* sql`CREATE TABLE IF NOT EXISTS blocks (
97
+ insertionId INTEGER PRIMARY KEY AUTOINCREMENT,
98
+ feedPrivateId INTEGER NOT NULL,
99
+ position INTEGER,
100
+ sequence INTEGER NOT NULL,
101
+ actorId TEXT NOT NULL,
102
+ predSequence INTEGER,
103
+ predActorId TEXT,
104
+ timestamp INTEGER NOT NULL,
105
+ data BLOB NOT NULL,
106
+ FOREIGN KEY(feedPrivateId) REFERENCES feeds(feedPrivateId)
107
+ )`;
108
+ yield* sql`CREATE UNIQUE INDEX IF NOT EXISTS idx_blocks_feedPrivateId_position ON blocks(feedPrivateId, position)`;
109
+ yield* sql`CREATE UNIQUE INDEX IF NOT EXISTS idx_blocks_feedPrivateId_sequence_actorId ON blocks(feedPrivateId, sequence, actorId)`;
110
+ yield* sql`CREATE TABLE IF NOT EXISTS subscriptions (
111
+ subscriptionId TEXT PRIMARY KEY,
112
+ expiresAt INTEGER NOT NULL,
113
+ feedPrivateIds TEXT NOT NULL -- JSON array
114
+ )`;
115
+ yield* sql`CREATE TABLE IF NOT EXISTS cursor_tokens (
116
+ spaceId TEXT PRIMARY KEY,
117
+ token TEXT NOT NULL
118
+ )`;
119
+ });
120
+ // Internal Logic
121
+ _ensureFeed = Effect.fn("Feed.ensureFeed")((spaceId, feedId, namespace) => Effect.gen(this, function* () {
122
+ const sql = yield* SqlClient.SqlClient;
123
+ const rows = yield* sql`
124
+ SELECT feedPrivateId FROM feeds WHERE spaceId = ${spaceId} AND feedId = ${feedId}
125
+ `;
126
+ if (rows.length > 0) return rows[0].feedPrivateId;
127
+ const newRows = yield* sql`
128
+ INSERT INTO feeds (spaceId, feedId, feedNamespace) VALUES (${spaceId}, ${feedId}, ${namespace}) RETURNING feedPrivateId
129
+ `;
130
+ return newRows[0].feedPrivateId;
131
+ }));
132
+ _ensureCursorToken = Effect.fn("Feed.ensureCursorToken")((spaceId) => Effect.gen(this, function* () {
133
+ const sql = yield* SqlClient.SqlClient;
134
+ const rows = yield* sql`SELECT token FROM cursor_tokens WHERE spaceId = ${spaceId}`;
135
+ if (rows.length > 0) return rows[0].token;
136
+ const token = crypto.randomUUID().replace(/-/g, "").slice(0, 6);
137
+ yield* sql`INSERT INTO cursor_tokens (spaceId, token) VALUES (${spaceId}, ${token})`;
138
+ return token;
139
+ }));
140
+ // RPCs
141
+ query = Effect.fn("Feed.query")((request) => Effect.gen(this, function* () {
142
+ const sql = yield* SqlClient.SqlClient;
143
+ let feedIds = [];
144
+ let cursorInsertionId = -1;
145
+ let cursorToken;
146
+ if (!request.spaceId) {
147
+ return yield* Effect.die(new Error("spaceId is required"));
148
+ }
149
+ if (request.cursor) {
150
+ const { token, insertionId } = decodeCursor(request.cursor);
151
+ if (!token || insertionId === void 0 || isNaN(insertionId)) {
152
+ return yield* Effect.die(new Error(`Invalid cursor format`));
153
+ }
154
+ cursorToken = token;
155
+ cursorInsertionId = insertionId;
156
+ }
157
+ const validCursorToken = yield* this._ensureCursorToken(request.spaceId);
158
+ if (request.cursor && cursorToken !== validCursorToken) {
159
+ return yield* Effect.die(new Error(`Cursor token mismatch`));
160
+ }
161
+ const position = request.position ?? -1;
162
+ if ("subscriptionId" in request.query) {
163
+ const rows2 = yield* sql`
164
+ SELECT feedPrivateIds, expiresAt FROM subscriptions WHERE subscriptionId = ${request.query.subscriptionId}
165
+ `;
166
+ if (rows2.length > 0) {
167
+ const { feedPrivateIds, expiresAt } = rows2[0];
168
+ if (Date.now() <= expiresAt) {
169
+ const privateIds = JSON.parse(feedPrivateIds);
170
+ if (privateIds.length > 0) {
171
+ const feedRows = yield* sql`
172
+ SELECT feedId FROM feeds WHERE feedPrivateId IN ${sql.in(privateIds)}
173
+ `;
174
+ feedIds = feedRows.map((r) => r.feedId);
175
+ }
176
+ }
177
+ }
178
+ } else if ("feedIds" in request.query) {
179
+ feedIds = [
180
+ ...request.query.feedIds
181
+ ];
182
+ } else {
183
+ feedIds = void 0;
184
+ }
185
+ if (feedIds !== void 0 && feedIds.length === 0) {
186
+ return {
187
+ requestId: request.requestId,
188
+ blocks: [],
189
+ nextCursor: encodeCursor(validCursorToken, -1)
190
+ };
191
+ }
192
+ const query = sql`
193
+ SELECT blocks.*, feeds.feedId
194
+ FROM blocks
195
+ JOIN feeds ON blocks.feedPrivateId = feeds.feedPrivateId
196
+ WHERE 1=1
197
+ ${feedIds !== void 0 ? sql`AND feeds.feedId IN ${sql.in(feedIds)}` : sql``}
198
+ ${request.spaceId ? sql`AND feeds.spaceId = ${request.spaceId}` : sql``}
199
+ ${"feedNamespace" in request.query && request.query.feedNamespace ? sql`AND feeds.feedNamespace = ${request.query.feedNamespace}` : sql``}
200
+ `;
201
+ const filter = request.cursor ? sql`AND blocks.insertionId > ${cursorInsertionId}` : sql`AND (blocks.position > ${position} OR blocks.position IS NULL)`;
202
+ const orderBy = request.cursor ? sql`ORDER BY blocks.insertionId ASC` : sql`ORDER BY blocks.position ASC NULLS LAST`;
203
+ const rows = yield* sql`
204
+ ${query}
205
+ ${filter}
206
+ ${orderBy}
207
+ ${request.limit ? sql`LIMIT ${request.limit}` : sql``}
208
+ `;
209
+ const blocks = rows.map((row) => ({
210
+ ...row,
211
+ // Have to buffer otherwise we get empty Uint8Array.
212
+ data: new Uint8Array(row.data)
213
+ }));
214
+ let nextCursor = request.cursor ?? encodeCursor(validCursorToken, -1);
215
+ if (blocks.length > 0 && request.spaceId) {
216
+ const lastBlock = blocks[blocks.length - 1];
217
+ if (lastBlock.insertionId !== void 0) {
218
+ nextCursor = encodeCursor(validCursorToken, lastBlock.insertionId);
219
+ }
220
+ }
221
+ return {
222
+ requestId: request.requestId,
223
+ blocks,
224
+ nextCursor
225
+ };
226
+ }));
227
+ subscribe = Effect.fn("Feed.subscribe")((request) => Effect.gen(this, function* () {
228
+ const sql = yield* SqlClient.SqlClient;
229
+ const ttl = 60 * 60 * 1e3;
230
+ const subscriptionId = crypto.randomUUID();
231
+ const expiresAt = Date.now() + ttl;
232
+ if (!request.spaceId) {
233
+ return yield* Effect.die(new Error("spaceId required for subscribe"));
234
+ }
235
+ const feedPrivateIds = yield* Effect.forEach(request.feedIds, (feedId) => this._ensureFeed(request.spaceId, feedId), {
236
+ concurrency: "unbounded"
237
+ });
238
+ yield* sql`
239
+ INSERT INTO subscriptions (subscriptionId, expiresAt, feedPrivateIds)
240
+ VALUES (${subscriptionId}, ${expiresAt}, ${JSON.stringify(feedPrivateIds)})
241
+ `;
242
+ return {
243
+ requestId: request.requestId,
244
+ subscriptionId,
245
+ expiresAt
246
+ };
247
+ }));
248
+ append = Effect.fn("Feed.append")((request) => Effect.gen(this, function* () {
249
+ const sql = yield* SqlClient.SqlClient;
250
+ const positions = [];
251
+ if (!request.spaceId) {
252
+ return yield* Effect.die(new Error("spaceId required for append"));
253
+ }
254
+ for (const block of request.blocks) {
255
+ const feedId = request.feedId ?? block.actorId;
256
+ const feedPrivateId = yield* this._ensureFeed(request.spaceId, feedId, request.namespace);
257
+ let nextPos = null;
258
+ if (this._options.assignPositions) {
259
+ const maxPosResult = yield* sql`
260
+ SELECT MAX(position) as maxPos
261
+ FROM blocks
262
+ JOIN feeds ON blocks.feedPrivateId = feeds.feedPrivateId
263
+ WHERE feeds.spaceId = ${request.spaceId}
264
+ `;
265
+ nextPos = (maxPosResult[0]?.maxPos ?? -1) + 1;
266
+ positions.push(nextPos);
267
+ }
268
+ yield* sql`
269
+ INSERT INTO blocks (
270
+ feedPrivateId, position, sequence, actorId,
271
+ predSequence, predActorId, timestamp, data
272
+ ) VALUES (
273
+ ${feedPrivateId}, ${nextPos}, ${block.sequence}, ${block.actorId},
274
+ ${block.predSequence}, ${block.predActorId}, ${block.timestamp}, ${block.data}
275
+ ) ON CONFLICT DO NOTHING
276
+ `;
277
+ }
278
+ this.onNewBlocks.emit();
279
+ return {
280
+ requestId: request.requestId,
281
+ positions
282
+ };
283
+ }));
284
+ appendLocal = Effect.fn("Feed.appendLocal")((messages) => Effect.gen(this, function* () {
285
+ const sql = yield* SqlClient.SqlClient;
286
+ const blocks = [];
287
+ for (const msg of messages) {
288
+ const feedPrivateId = yield* this._ensureFeed(msg.spaceId, msg.feedId, msg.feedNamespace);
289
+ const lastBlockResult = yield* sql`
290
+ SELECT sequence, actorId FROM blocks
291
+ WHERE feedPrivateId = ${feedPrivateId}
292
+ ORDER BY sequence DESC
293
+ LIMIT 1
294
+ `;
295
+ const lastBlock = lastBlockResult[0];
296
+ const sequence = (lastBlock?.sequence ?? -1) + 1;
297
+ const predSequence = lastBlock?.sequence ?? null;
298
+ const predActorId = lastBlock?.actorId ?? null;
299
+ const block = {
300
+ feedId: void 0,
301
+ actorId: this._options.localActorId,
302
+ sequence,
303
+ predActorId,
304
+ predSequence,
305
+ timestamp: Date.now(),
306
+ data: msg.data,
307
+ position: null
308
+ };
309
+ blocks.push(block);
310
+ yield* this.append({
311
+ requestId: "local-append",
312
+ namespace: msg.feedNamespace,
313
+ blocks: [
314
+ block
315
+ ],
316
+ spaceId: msg.spaceId,
317
+ feedId: msg.feedId
318
+ });
319
+ }
320
+ return blocks;
321
+ }));
322
+ };
323
+ var encodeCursor = (token, insertionId) => FeedCursor.make(`${token}|${insertionId}`);
324
+ var decodeCursor = (cursor) => {
325
+ const [token, insertionId] = cursor.split("|");
326
+ return {
327
+ token,
328
+ insertionId: Number(insertionId)
329
+ };
330
+ };
331
+ export {
332
+ AppendRequest,
333
+ AppendResponse,
334
+ Block,
335
+ FeedCursor,
336
+ FeedStore,
337
+ QueryRequest,
338
+ QueryResponse,
339
+ SubscribeRequest,
340
+ SubscribeResponse
341
+ };
342
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/feed-store.ts", "../../../src/protocol.ts"],
4
+ "sourcesContent": ["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as SqlClient from '@effect/sql/SqlClient';\nimport type * as SqlError from '@effect/sql/SqlError';\nimport * as Effect from 'effect/Effect';\n\nimport { Event } from '@dxos/async';\n\nimport {\n type AppendRequest,\n type AppendResponse,\n type Block,\n FeedCursor,\n type QueryRequest,\n type QueryResponse,\n type SubscribeRequest,\n type SubscribeResponse,\n} from './protocol';\n\nexport interface FeedStoreOptions {\n localActorId: string;\n assignPositions: boolean;\n}\n\nexport class FeedStore {\n constructor(private readonly _options: FeedStoreOptions) {}\n\n readonly onNewBlocks = new Event<void>();\n\n migrate = Effect.fn('FeedStore.migrate')(function* () {\n const sql = yield* SqlClient.SqlClient;\n\n // Feeds Table\n yield* sql`CREATE TABLE IF NOT EXISTS feeds (\n feedPrivateId INTEGER PRIMARY KEY AUTOINCREMENT,\n spaceId TEXT NOT NULL,\n feedId TEXT NOT NULL,\n feedNamespace TEXT\n )`;\n yield* sql`CREATE UNIQUE INDEX IF NOT EXISTS idx_feeds_spaceId_feedId ON feeds(spaceId, feedId)`;\n\n // Blocks Table\n yield* sql`CREATE TABLE IF NOT EXISTS blocks (\n insertionId INTEGER PRIMARY KEY AUTOINCREMENT,\n feedPrivateId INTEGER NOT NULL,\n position INTEGER,\n sequence INTEGER NOT NULL,\n actorId TEXT NOT NULL,\n predSequence INTEGER,\n predActorId TEXT,\n timestamp INTEGER NOT NULL,\n data BLOB NOT NULL,\n FOREIGN KEY(feedPrivateId) REFERENCES feeds(feedPrivateId)\n )`;\n yield* sql`CREATE UNIQUE INDEX IF NOT EXISTS idx_blocks_feedPrivateId_position ON blocks(feedPrivateId, position)`;\n yield* sql`CREATE UNIQUE INDEX IF NOT EXISTS idx_blocks_feedPrivateId_sequence_actorId ON blocks(feedPrivateId, sequence, actorId)`;\n\n // Subscriptions Table\n yield* sql`CREATE TABLE IF NOT EXISTS subscriptions (\n subscriptionId TEXT PRIMARY KEY,\n expiresAt INTEGER NOT NULL,\n feedPrivateIds TEXT NOT NULL -- JSON array\n )`;\n\n // Cursor Tokens Table\n yield* sql`CREATE TABLE IF NOT EXISTS cursor_tokens (\n spaceId TEXT PRIMARY KEY,\n token TEXT NOT NULL\n )`;\n });\n\n // Internal Logic\n\n private _ensureFeed = Effect.fn('Feed.ensureFeed')(\n (\n spaceId: string,\n feedId: string,\n namespace?: string,\n ): Effect.Effect<number, SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(this, function* () {\n const sql = yield* SqlClient.SqlClient;\n\n const rows = yield* sql<{ feedPrivateId: number }>`\n SELECT feedPrivateId FROM feeds WHERE spaceId = ${spaceId} AND feedId = ${feedId}\n `;\n if (rows.length > 0) return rows[0].feedPrivateId;\n\n const newRows = yield* sql<{ feedPrivateId: number }>`\n INSERT INTO feeds (spaceId, feedId, feedNamespace) VALUES (${spaceId}, ${feedId}, ${namespace}) RETURNING feedPrivateId\n `;\n return newRows[0].feedPrivateId;\n }),\n );\n\n private _ensureCursorToken = Effect.fn('Feed.ensureCursorToken')(\n (spaceId: string): Effect.Effect<string, SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(this, function* () {\n const sql = yield* SqlClient.SqlClient;\n const rows = yield* sql<{ token: string }>`SELECT token FROM cursor_tokens WHERE spaceId = ${spaceId}`;\n if (rows.length > 0) return rows[0].token;\n\n const token = crypto.randomUUID().replace(/-/g, '').slice(0, 6);\n yield* sql`INSERT INTO cursor_tokens (spaceId, token) VALUES (${spaceId}, ${token})`;\n return token;\n }),\n );\n\n // RPCs\n\n query = Effect.fn('Feed.query')(\n (request: QueryRequest): Effect.Effect<QueryResponse, SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(this, function* () {\n const sql = yield* SqlClient.SqlClient;\n let feedIds: string[] | undefined = [];\n let cursorInsertionId = -1;\n let cursorToken: string | undefined;\n\n if (!request.spaceId) {\n return yield* Effect.die(new Error('spaceId is required'));\n }\n\n if (request.cursor) {\n const { token, insertionId } = decodeCursor(request.cursor as FeedCursor);\n if (!token || insertionId === undefined || isNaN(insertionId)) {\n return yield* Effect.die(new Error(`Invalid cursor format`));\n }\n cursorToken = token;\n cursorInsertionId = insertionId;\n }\n\n // Validate Token if cursor used\n const validCursorToken = yield* this._ensureCursorToken(request.spaceId);\n if (request.cursor && cursorToken !== validCursorToken) {\n return yield* Effect.die(new Error(`Cursor token mismatch`));\n }\n\n // If cursor is provided, we must validate it against the space token.\n // If spaceId is not provided in request (e.g. feedIds query), we can't easily validate token unless we look up spaceId for feedIds.\n // Ideally spaceId should be required for token validation.\n\n /* \n Logic:\n 1. If `cursor` is present, it's `token|insertionId`.\n 2. If `position` is present, it's `position` (legacy/manual).\n \n We prioritize `cursor`.\n */\n\n const position = request.position ?? -1;\n\n // Resolve Subscriptions or FeedIds\n if ('subscriptionId' in request.query) {\n const rows = yield* sql<{ feedPrivateIds: string; expiresAt: number }>`\n SELECT feedPrivateIds, expiresAt FROM subscriptions WHERE subscriptionId = ${request.query.subscriptionId}\n `;\n if (rows.length > 0) {\n const { feedPrivateIds, expiresAt } = rows[0];\n if (Date.now() <= expiresAt) {\n const privateIds = JSON.parse(feedPrivateIds) as number[];\n if (privateIds.length > 0) {\n const feedRows = yield* sql<{ feedId: string }>`\n SELECT feedId FROM feeds WHERE feedPrivateId IN ${sql.in(privateIds)}\n `;\n feedIds = feedRows.map((r) => r.feedId);\n }\n }\n }\n } else if ('feedIds' in request.query) {\n feedIds = [...(request.query as any).feedIds];\n } else {\n feedIds = undefined;\n }\n\n if (feedIds !== undefined && feedIds.length === 0) {\n return { requestId: request.requestId, blocks: [], nextCursor: encodeCursor(validCursorToken, -1) };\n }\n\n // Fetch Blocks\n const query = sql<Block>`\n SELECT blocks.*, feeds.feedId\n FROM blocks\n JOIN feeds ON blocks.feedPrivateId = feeds.feedPrivateId\n WHERE 1=1\n ${feedIds !== undefined ? sql`AND feeds.feedId IN ${sql.in(feedIds)}` : sql``}\n ${request.spaceId ? sql`AND feeds.spaceId = ${request.spaceId}` : sql``}\n ${\n 'feedNamespace' in request.query && request.query.feedNamespace\n ? sql`AND feeds.feedNamespace = ${request.query.feedNamespace}`\n : sql``\n }\n `;\n\n // Add filter based on cursor or position\n const filter = request.cursor\n ? sql`AND blocks.insertionId > ${cursorInsertionId}`\n : sql`AND (blocks.position > ${position} OR blocks.position IS NULL)`;\n\n const orderBy = request.cursor\n ? sql`ORDER BY blocks.insertionId ASC`\n : sql`ORDER BY blocks.position ASC NULLS LAST`;\n\n const rows = yield* sql<Block>`\n ${query}\n ${filter}\n ${orderBy}\n ${request.limit ? sql`LIMIT ${request.limit}` : sql``}\n `;\n\n const blocks = rows.map((row) => ({\n ...row,\n // Have to buffer otherwise we get empty Uint8Array.\n data: new Uint8Array(row.data),\n }));\n\n let nextCursor: FeedCursor = request.cursor ?? encodeCursor(validCursorToken, -1);\n if (blocks.length > 0 && request.spaceId) {\n const lastBlock = blocks[blocks.length - 1];\n if (lastBlock.insertionId !== undefined) {\n nextCursor = encodeCursor(validCursorToken, lastBlock.insertionId);\n }\n }\n\n return { requestId: request.requestId, blocks, nextCursor } satisfies QueryResponse;\n }),\n );\n\n subscribe = Effect.fn('Feed.subscribe')(\n (request: SubscribeRequest): Effect.Effect<SubscribeResponse, SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(this, function* () {\n const sql = yield* SqlClient.SqlClient;\n const ttl = 60 * 60 * 1000;\n const subscriptionId = crypto.randomUUID();\n const expiresAt = Date.now() + ttl;\n\n if (!request.spaceId) {\n // TODO(dmaretskyi): Define error type.\n return yield* Effect.die(new Error('spaceId required for subscribe'));\n }\n\n const feedPrivateIds = yield* Effect.forEach(\n request.feedIds,\n (feedId) => this._ensureFeed(request.spaceId!, feedId),\n { concurrency: 'unbounded' },\n );\n\n yield* sql`\n INSERT INTO subscriptions (subscriptionId, expiresAt, feedPrivateIds)\n VALUES (${subscriptionId}, ${expiresAt}, ${JSON.stringify(feedPrivateIds)})\n `;\n\n return {\n requestId: request.requestId,\n subscriptionId,\n expiresAt,\n };\n }),\n );\n\n append = Effect.fn('Feed.append')(\n (request: AppendRequest): Effect.Effect<AppendResponse, SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(this, function* () {\n const sql = yield* SqlClient.SqlClient;\n const positions: number[] = [];\n\n if (!request.spaceId) {\n return yield* Effect.die(new Error('spaceId required for append'));\n }\n\n for (const block of request.blocks) {\n const feedId = request.feedId ?? block.actorId;\n const feedPrivateId = yield* this._ensureFeed(request.spaceId, feedId, request.namespace);\n\n let nextPos = null;\n if (this._options.assignPositions) {\n const maxPosResult = yield* sql<{ maxPos: number | null }>`\n SELECT MAX(position) as maxPos \n FROM blocks \n JOIN feeds ON blocks.feedPrivateId = feeds.feedPrivateId\n WHERE feeds.spaceId = ${request.spaceId}\n `;\n nextPos = (maxPosResult[0]?.maxPos ?? -1) + 1;\n positions.push(nextPos);\n }\n\n yield* sql`\n INSERT INTO blocks (\n feedPrivateId, position, sequence, actorId, \n predSequence, predActorId, timestamp, data\n ) VALUES (\n ${feedPrivateId}, ${nextPos}, ${block.sequence}, ${block.actorId},\n ${block.predSequence}, ${block.predActorId}, ${block.timestamp}, ${block.data}\n ) ON CONFLICT DO NOTHING\n `;\n }\n\n this.onNewBlocks.emit();\n\n return { requestId: request.requestId, positions };\n }),\n );\n\n appendLocal = Effect.fn('Feed.appendLocal')(\n (\n messages: { spaceId: string; feedId: string; feedNamespace: string; data: Uint8Array }[],\n ): Effect.Effect<Block[], SqlError.SqlError, SqlClient.SqlClient> =>\n Effect.gen(this, function* () {\n const sql = yield* SqlClient.SqlClient;\n const blocks: Block[] = [];\n\n // Group by spaceId for efficient processing? Or just iterate.\n // Assuming small batch.\n\n for (const msg of messages) {\n // Get last block for this feed to determine sequence and predecessor.\n // Assumes we are the writer (localActorId).\n // If we are not localActorId, we shouldn't be creating blocks this way?\n // The user said: \"uses local actorId\". So feedId in message might be ignored?\n // Or message provides feedId?\n // User said: \"client provides (only data + spaceId, feedId, feedNamespace)\".\n // If client provides feedId, does it match localActorId?\n // If localActorId is fixed for the device, and we are appending to \"my\" feed, feedId should be localActorId.\n // If the client explicitly passes feedId, maybe they want to write to a specific feed (e.g. if they have multiple identities?).\n // But spec says \"uses local actorId\". I will use `this._options.localActorId`.\n // But `messages` arg has `feedId`.\n // I'll assume `msg.feedId` must match `localActorId` or we just use `localActorId` and ignore `msg.feedId`?\n // User: \"client provides ... feedId\".\n // I will use passed feedId but it should probably match localActorId if we are signing? (No signing here yet).\n // I'll use passed `feedId`.\n\n const feedPrivateId = yield* this._ensureFeed(msg.spaceId, msg.feedId, msg.feedNamespace);\n\n // Get last block to determine sequence.\n const lastBlockResult = yield* sql<{ sequence: number; actorId: string }>`\n SELECT sequence, actorId FROM blocks \n WHERE feedPrivateId = ${feedPrivateId} \n ORDER BY sequence DESC \n LIMIT 1\n `; // This gets checks only THIS feed.\n\n const lastBlock = lastBlockResult[0];\n const sequence = (lastBlock?.sequence ?? -1) + 1;\n const predSequence = lastBlock?.sequence ?? null;\n const predActorId = lastBlock?.actorId ?? null;\n\n const block: Block = {\n feedId: undefined,\n actorId: this._options.localActorId,\n sequence,\n predActorId,\n predSequence,\n timestamp: Date.now(),\n data: msg.data,\n position: null, // assigned by append\n };\n blocks.push(block);\n\n // Call append to persist (and assign position if allowed).\n yield* this.append({\n requestId: 'local-append',\n namespace: msg.feedNamespace,\n blocks: [block],\n spaceId: msg.spaceId,\n feedId: msg.feedId,\n });\n }\n return blocks;\n }),\n );\n}\n\nconst encodeCursor = (token: string, insertionId: number) => FeedCursor.make(`${token}|${insertionId}`);\nconst decodeCursor = (cursor: FeedCursor) => {\n const [token, insertionId] = cursor.split('|');\n return { token, insertionId: Number(insertionId) };\n};\n", "//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\nexport const FeedCursor = Schema.String.pipe(Schema.brand('@dxos/feed/FeedCursor'));\nexport type FeedCursor = Schema.Schema.Type<typeof FeedCursor>;\n\nexport const Block = Schema.Struct({\n /**\n * Only set on query.\n */\n feedId: Schema.UndefinedOr(Schema.String),\n\n actorId: Schema.String,\n sequence: Schema.Number,\n predActorId: Schema.NullOr(Schema.String),\n predSequence: Schema.NullOr(Schema.Number),\n position: Schema.NullOr(Schema.Number),\n timestamp: Schema.Number,\n data: Schema.Uint8Array,\n\n /**\n * Local insertion ID.\n * Not replicated.\n */\n // TODO(dmaretskyi): Remove. Use cursors.\n insertionId: Schema.optional(Schema.Number),\n});\nexport interface Block extends Schema.Schema.Type<typeof Block> {}\n\n//\n// RPC Schemas\n//\n\n// Request is a union of query by subscription or query by feedIds\nexport const QueryRequest = Schema.Struct({\n requestId: Schema.optional(Schema.String),\n\n // TODO(dmaretskyi): Make required.\n spaceId: Schema.optional(Schema.String),\n\n query: Schema.Union(\n Schema.Struct({\n feedIds: Schema.Array(Schema.String),\n }),\n Schema.Struct({\n subscriptionId: Schema.String,\n }),\n Schema.Struct({\n feedNamespace: Schema.optional(Schema.String),\n }),\n ),\n /**\n * Get changes following this cursor (exclusive).\n *\n * Must not be used with `position`.\n */\n cursor: Schema.optional(FeedCursor),\n\n /**\n * Get changes following this position.\n *\n * Must not be used with `cursor`.\n */\n position: Schema.optional(Schema.Number),\n\n limit: Schema.optional(Schema.Number),\n});\nexport interface QueryRequest extends Schema.Schema.Type<typeof QueryRequest> {}\n\n// Response is a stream/array of blocks\nexport const QueryResponse = Schema.Struct({\n requestId: Schema.optional(Schema.String),\n nextCursor: FeedCursor,\n blocks: Schema.Array(Block),\n});\nexport interface QueryResponse extends Schema.Schema.Type<typeof QueryResponse> {}\n\nexport const SubscribeRequest = Schema.Struct({\n requestId: Schema.optional(Schema.String),\n feedIds: Schema.Array(Schema.String),\n spaceId: Schema.optional(Schema.String),\n});\nexport interface SubscribeRequest extends Schema.Schema.Type<typeof SubscribeRequest> {}\n\nexport const SubscribeResponse = Schema.Struct({\n requestId: Schema.optional(Schema.String),\n subscriptionId: Schema.String,\n expiresAt: Schema.Number,\n});\nexport interface SubscribeResponse extends Schema.Schema.Type<typeof SubscribeResponse> {}\n\nexport const AppendRequest = Schema.Struct({\n requestId: Schema.optional(Schema.String),\n\n spaceId: Schema.String,\n namespace: Schema.String,\n feedId: Schema.String,\n\n blocks: Schema.Array(Block),\n});\nexport interface AppendRequest extends Schema.Schema.Type<typeof AppendRequest> {}\n\nexport const AppendResponse = Schema.Struct({\n requestId: Schema.optional(Schema.String),\n positions: Schema.Array(Schema.Number),\n});\nexport interface AppendResponse extends Schema.Schema.Type<typeof AppendResponse> {}\n"],
5
+ "mappings": "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",
6
+ "names": ["SqlClient", "Effect", "Event", "Schema", "FeedCursor", "String", "pipe", "brand", "Block", "Struct", "feedId", "UndefinedOr", "actorId", "sequence", "Number", "predActorId", "NullOr", "predSequence", "position", "timestamp", "data", "Uint8Array", "insertionId", "optional", "QueryRequest", "requestId", "spaceId", "query", "Union", "feedIds", "Array", "subscriptionId", "feedNamespace", "cursor", "limit", "QueryResponse", "nextCursor", "blocks", "SubscribeRequest", "SubscribeResponse", "expiresAt", "AppendRequest", "namespace", "AppendResponse", "positions", "FeedStore", "_options", "onNewBlocks", "Event", "migrate", "fn", "sql", "SqlClient", "_ensureFeed", "spaceId", "feedId", "namespace", "gen", "rows", "length", "feedPrivateId", "newRows", "_ensureCursorToken", "token", "crypto", "randomUUID", "replace", "slice", "query", "request", "feedIds", "cursorInsertionId", "cursorToken", "die", "Error", "cursor", "insertionId", "decodeCursor", "undefined", "isNaN", "validCursorToken", "position", "subscriptionId", "feedPrivateIds", "expiresAt", "Date", "now", "privateIds", "JSON", "parse", "feedRows", "in", "map", "r", "requestId", "blocks", "nextCursor", "encodeCursor", "feedNamespace", "filter", "orderBy", "limit", "row", "data", "Uint8Array", "lastBlock", "subscribe", "ttl", "forEach", "concurrency", "stringify", "append", "positions", "block", "actorId", "nextPos", "assignPositions", "maxPosResult", "maxPos", "push", "sequence", "predSequence", "predActorId", "timestamp", "emit", "appendLocal", "messages", "msg", "lastBlockResult", "localActorId", "FeedCursor", "make", "split", "Number"]
7
+ }
@@ -0,0 +1 @@
1
+ {"inputs":{"src/protocol.ts":{"bytes":10099,"imports":[{"path":"effect/Schema","kind":"import-statement","external":true}],"format":"esm"},"src/feed-store.ts":{"bytes":44789,"imports":[{"path":"@effect/sql/SqlClient","kind":"import-statement","external":true},{"path":"effect/Effect","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"src/protocol.ts","kind":"import-statement","original":"./protocol"}],"format":"esm"},"src/index.ts":{"bytes":544,"imports":[{"path":"src/feed-store.ts","kind":"import-statement","original":"./feed-store"},{"path":"src/protocol.ts","kind":"import-statement","original":"./protocol"}],"format":"esm"},"src/testing/index.ts":{"bytes":414,"imports":[],"format":"esm"}},"outputs":{"dist/lib/browser/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":25987},"dist/lib/browser/index.mjs":{"imports":[{"path":"@effect/sql/SqlClient","kind":"import-statement","external":true},{"path":"effect/Effect","kind":"import-statement","external":true},{"path":"@dxos/async","kind":"import-statement","external":true},{"path":"effect/Schema","kind":"import-statement","external":true}],"exports":["AppendRequest","AppendResponse","Block","FeedCursor","FeedStore","QueryRequest","QueryResponse","SubscribeRequest","SubscribeResponse"],"entryPoint":"src/index.ts","inputs":{"src/feed-store.ts":{"bytesInOutput":9944},"src/protocol.ts":{"bytesInOutput":2093},"src/index.ts":{"bytesInOutput":0}},"bytes":12291},"dist/lib/browser/testing/index.mjs.map":{"imports":[],"exports":[],"inputs":{},"bytes":93},"dist/lib/browser/testing/index.mjs":{"imports":[],"exports":[],"entryPoint":"src/testing/index.ts","inputs":{"src/testing/index.ts":{"bytesInOutput":0}},"bytes":35}}}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }