@loomwork/ui-changes 0.0.0-stage → 0.1.0

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.
Files changed (46) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +8 -0
  3. package/README.md +134 -2
  4. package/dist/conformance.d.ts +18 -0
  5. package/dist/conformance.d.ts.map +1 -0
  6. package/dist/conformance.js +243 -0
  7. package/dist/conformance.js.map +1 -0
  8. package/dist/cursor.d.ts +43 -0
  9. package/dist/cursor.d.ts.map +1 -0
  10. package/dist/cursor.js +50 -0
  11. package/dist/cursor.js.map +1 -0
  12. package/dist/esm/conformance.js +239 -0
  13. package/dist/esm/conformance.js.map +1 -0
  14. package/dist/esm/cursor.js +44 -0
  15. package/dist/esm/cursor.js.map +1 -0
  16. package/dist/esm/feed.js +301 -0
  17. package/dist/esm/feed.js.map +1 -0
  18. package/dist/esm/index.js +6 -0
  19. package/dist/esm/index.js.map +1 -0
  20. package/dist/esm/signal.js +141 -0
  21. package/dist/esm/signal.js.map +1 -0
  22. package/dist/esm/untrusted.js +14 -0
  23. package/dist/esm/untrusted.js.map +1 -0
  24. package/dist/feed.d.ts +125 -0
  25. package/dist/feed.d.ts.map +1 -0
  26. package/dist/feed.js +305 -0
  27. package/dist/feed.js.map +1 -0
  28. package/dist/index.d.ts +10 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +20 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/signal.d.ts +33 -0
  33. package/dist/signal.d.ts.map +1 -0
  34. package/dist/signal.js +145 -0
  35. package/dist/signal.js.map +1 -0
  36. package/dist/untrusted.d.ts +5 -0
  37. package/dist/untrusted.d.ts.map +1 -0
  38. package/dist/untrusted.js +17 -0
  39. package/dist/untrusted.js.map +1 -0
  40. package/package.json +60 -3
  41. package/src/conformance.ts +248 -0
  42. package/src/cursor.ts +89 -0
  43. package/src/feed.ts +446 -0
  44. package/src/index.ts +18 -0
  45. package/src/signal.ts +200 -0
  46. package/src/untrusted.ts +16 -0
@@ -0,0 +1,43 @@
1
+ import type { Finding } from "@loomwork/ui-core";
2
+ import type { ChangeEvent, ChangeFeed } from "./feed";
3
+ export interface UnreadStore {
4
+ /** The viewer's cursor for the line; 0 when none was ever recorded. */
5
+ get(viewerId: string, experienceId: string): Promise<number>;
6
+ /**
7
+ * Move the cursor to `max(current, to)` and return the result. A `to` that is not a non-negative
8
+ * safe integer changes nothing. Atomic against concurrent advances: the cursor never goes back.
9
+ */
10
+ advance(viewerId: string, experienceId: string, to: number): Promise<number>;
11
+ }
12
+ export declare class InMemoryUnreadStore implements UnreadStore {
13
+ private readonly cursors;
14
+ get(viewerId: string, experienceId: string): Promise<number>;
15
+ advance(viewerId: string, experienceId: string, to: number): Promise<number>;
16
+ }
17
+ export type UnreadResult = {
18
+ readonly ok: true;
19
+ readonly events: readonly ChangeEvent[];
20
+ /** The viewer's cursor when read. */
21
+ readonly cursor: number;
22
+ /** More unread events exist past this page. */
23
+ readonly more: boolean;
24
+ } | {
25
+ readonly ok: false;
26
+ readonly finding: Finding;
27
+ };
28
+ /** The events the viewer has not acknowledged, oldest first, one page. */
29
+ export declare function unreadChanges(feed: ChangeFeed, store: UnreadStore, viewerId: string, experienceId: string, opts?: {
30
+ limit?: number;
31
+ }): Promise<UnreadResult>;
32
+ /**
33
+ * Acknowledge everything through `through` (clamped to the log's tip). Returns the new cursor, or
34
+ * the finding when the line could not be read (then nothing moves).
35
+ */
36
+ export declare function markRead(feed: ChangeFeed, store: UnreadStore, viewerId: string, experienceId: string, through: number): Promise<{
37
+ ok: true;
38
+ cursor: number;
39
+ } | {
40
+ ok: false;
41
+ finding: Finding;
42
+ }>;
43
+ //# sourceMappingURL=cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.d.ts","sourceRoot":"","sources":["../src/cursor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAUtD,MAAM,WAAW,WAAW;IAC1B,uEAAuE;IACvE,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7D;;;OAGG;IACH,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9E;AAED,qBAAa,mBAAoB,YAAW,WAAW;IAErD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0C;IAE5D,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAI5D,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAWnF;AAED,MAAM,MAAM,YAAY,GACpB;IACE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAClB,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;IACxC,qCAAqC;IACrC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;CACxB,GACD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtD,0EAA0E;AAC1E,wBAAsB,aAAa,CACjC,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5B,OAAO,CAAC,YAAY,CAAC,CAOvB;AAED;;;GAGG;AACH,wBAAsB,QAAQ,CAC5B,IAAI,EAAE,UAAU,EAChB,KAAK,EAAE,WAAW,EAClB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CASzE"}
package/dist/cursor.js ADDED
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InMemoryUnreadStore = void 0;
4
+ exports.unreadChanges = unreadChanges;
5
+ exports.markRead = markRead;
6
+ class InMemoryUnreadStore {
7
+ // Nested maps, not a joined key: no separator a viewer or experience id could contain.
8
+ cursors = new Map();
9
+ async get(viewerId, experienceId) {
10
+ return this.cursors.get(viewerId)?.get(experienceId) ?? 0;
11
+ }
12
+ async advance(viewerId, experienceId, to) {
13
+ const current = this.cursors.get(viewerId)?.get(experienceId) ?? 0;
14
+ if (!Number.isSafeInteger(to) || to <= current)
15
+ return current;
16
+ let mine = this.cursors.get(viewerId);
17
+ if (mine === undefined) {
18
+ mine = new Map();
19
+ this.cursors.set(viewerId, mine);
20
+ }
21
+ mine.set(experienceId, to);
22
+ return to;
23
+ }
24
+ }
25
+ exports.InMemoryUnreadStore = InMemoryUnreadStore;
26
+ /** The events the viewer has not acknowledged, oldest first, one page. */
27
+ async function unreadChanges(feed, store, viewerId, experienceId, opts = {}) {
28
+ const cursor = await store.get(viewerId, experienceId);
29
+ const r = await feed.read(experienceId, {
30
+ after: cursor,
31
+ ...(opts.limit !== undefined && { limit: opts.limit }),
32
+ });
33
+ return r.ok ? { ok: true, events: r.events, cursor, more: r.more } : r;
34
+ }
35
+ /**
36
+ * Acknowledge everything through `through` (clamped to the log's tip). Returns the new cursor, or
37
+ * the finding when the line could not be read (then nothing moves).
38
+ */
39
+ async function markRead(feed, store, viewerId, experienceId, through) {
40
+ const r = await feed.read(experienceId, { limit: 1, after: Number.MAX_SAFE_INTEGER });
41
+ if (!r.ok)
42
+ return r;
43
+ if (!Number.isSafeInteger(through) || through < 0)
44
+ return { ok: true, cursor: await store.get(viewerId, experienceId) };
45
+ return {
46
+ ok: true,
47
+ cursor: await store.advance(viewerId, experienceId, Math.min(through, r.tip)),
48
+ };
49
+ }
50
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../src/cursor.ts"],"names":[],"mappings":";;;AAsDA,sCAaC;AAMD,4BAeC;AAnED,MAAa,mBAAmB;IAC9B,uFAAuF;IACtE,OAAO,GAAG,IAAI,GAAG,EAA+B,CAAC;IAElE,KAAK,CAAC,GAAG,CAAC,QAAgB,EAAE,YAAoB;QAC9C,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,QAAgB,EAAE,YAAoB,EAAE,EAAU;QAC9D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACnE,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC;QAC/D,IAAI,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACtC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;YACjB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;QAC3B,OAAO,EAAE,CAAC;IACZ,CAAC;CACF;AAnBD,kDAmBC;AAaD,0EAA0E;AACnE,KAAK,UAAU,aAAa,CACjC,IAAgB,EAChB,KAAkB,EAClB,QAAgB,EAChB,YAAoB,EACpB,OAA2B,EAAE;IAE7B,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACvD,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE;QACtC,KAAK,EAAE,MAAM;QACb,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;KACvD,CAAC,CAAC;IACH,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACzE,CAAC;AAED;;;GAGG;AACI,KAAK,UAAU,QAAQ,CAC5B,IAAgB,EAChB,KAAkB,EAClB,QAAgB,EAChB,YAAoB,EACpB,OAAe;IAEf,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACtF,IAAI,CAAC,CAAC,CAAC,EAAE;QAAE,OAAO,CAAC,CAAC;IACpB,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC;QAC/C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,CAAC;IACvE,OAAO;QACL,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;KAC9E,CAAC;AACJ,CAAC"}
@@ -0,0 +1,239 @@
1
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
2
+ async function until(cond, tick, waitMs) {
3
+ const end = Date.now() + waitMs;
4
+ for (;;) {
5
+ if (tick !== undefined)
6
+ await tick();
7
+ if (cond())
8
+ return true;
9
+ if (Date.now() > end)
10
+ return false;
11
+ await sleep(5);
12
+ }
13
+ }
14
+ export async function checkChangeFeedConformance(make, opts = {}) {
15
+ const findings = [];
16
+ const fail = (code, message) => findings.push({ code, severity: "error", message });
17
+ const waitMs = opts.waitMs ?? 1500;
18
+ const h = await make();
19
+ const read = async (id, o) => {
20
+ const r = await h.feed.read(id, o);
21
+ if (!r.ok)
22
+ throw new Error(`read failed: ${r.finding.message}`);
23
+ return r;
24
+ };
25
+ const cases = [
26
+ [
27
+ "an unknown line reads empty with tip 0",
28
+ async () => {
29
+ const r = await read("f-empty");
30
+ if (r.events.length !== 0 || r.cursor !== 0 || r.tip !== 0 || r.more)
31
+ fail("read-empty", `an unknown line read ${JSON.stringify(r)}`);
32
+ },
33
+ ],
34
+ [
35
+ "events are dense, ordered, with unique ids that are stable across reads",
36
+ async () => {
37
+ for (let i = 0; i < 3; i++)
38
+ await h.compose("f-ids");
39
+ const a = await read("f-ids");
40
+ const b = await read("f-ids");
41
+ if (a.events.map((e) => e.sequence).join() !== "1,2,3")
42
+ fail("sequence", `sequences ${a.events.map((e) => e.sequence).join()} are not 1,2,3`);
43
+ if (new Set(a.events.map((e) => e.id)).size !== a.events.length)
44
+ fail("ids-unique", "two events share an id");
45
+ if (a.events.map((e) => e.id).join() !== b.events.map((e) => e.id).join())
46
+ fail("ids-stable", "re-reading the same range changed an id");
47
+ if (a.cursor !== 3 || a.tip !== 3)
48
+ fail("cursor", `cursor/tip were ${a.cursor}/${a.tip}`);
49
+ },
50
+ ],
51
+ [
52
+ "reading after a cursor returns only later events",
53
+ async () => {
54
+ for (let i = 0; i < 3; i++)
55
+ await h.compose("f-resume");
56
+ const r = await read("f-resume", { after: 1 });
57
+ if (r.events.map((e) => e.sequence).join() !== "2,3")
58
+ fail("resume", `after 1 returned ${r.events.map((e) => e.sequence).join()}`);
59
+ const none = await read("f-resume", { after: 3 });
60
+ if (none.events.length !== 0 || none.cursor !== 3)
61
+ fail("resume-end", `after the tip returned ${JSON.stringify(none)}`);
62
+ },
63
+ ],
64
+ [
65
+ "paging by limit visits every event once, in order",
66
+ async () => {
67
+ for (let i = 0; i < 5; i++)
68
+ await h.compose("f-page");
69
+ const seen = [];
70
+ let cursor = 0;
71
+ for (let guard = 0; guard < 10; guard++) {
72
+ const r = await read("f-page", { after: cursor, limit: 2 });
73
+ if (r.events.length > 2)
74
+ fail("limit", `limit 2 returned ${r.events.length}`);
75
+ seen.push(...r.events.map((e) => e.sequence));
76
+ cursor = r.cursor;
77
+ if (!r.more)
78
+ break;
79
+ }
80
+ if (seen.join() !== "1,2,3,4,5")
81
+ fail("paging", `paging saw ${seen.join()}`);
82
+ const bad = await h.feed.read("f-page", { after: -1 });
83
+ if (bad.ok)
84
+ fail("cursor-invalid", "a negative cursor was accepted");
85
+ const badLimit = await h.feed.read("f-page", { limit: 0 });
86
+ if (badLimit.ok)
87
+ fail("limit-invalid", "a zero limit was accepted");
88
+ },
89
+ ],
90
+ [
91
+ "subscribe catches up from a cursor, then follows live, in order",
92
+ async () => {
93
+ await h.compose("f-sub");
94
+ await h.compose("f-sub");
95
+ const got = [];
96
+ const off = h.feed.subscribe("f-sub", (e) => void got.push(e), { after: 1 });
97
+ const caught = await until(() => got.length >= 1, h.tick, waitMs);
98
+ if (!caught || got[0]?.sequence !== 2)
99
+ fail("sub-catchup", `catch-up from 1 delivered ${got.map((e) => e.sequence).join()}`);
100
+ await h.compose("f-sub");
101
+ const live = await until(() => got.length >= 2, h.tick, waitMs);
102
+ if (!live || got.map((e) => e.sequence).join() !== "2,3")
103
+ fail("sub-live", `live delivery gave ${got.map((e) => e.sequence).join()}`);
104
+ off();
105
+ await h.compose("f-sub");
106
+ await sleep(40);
107
+ if (h.tick)
108
+ await h.tick();
109
+ await sleep(20);
110
+ if (got.length !== 2)
111
+ fail("sub-off", "an event was delivered after unsubscribe");
112
+ },
113
+ ],
114
+ [
115
+ "resubscribing from an older cursor redelivers the SAME ids (at-least-once, idempotent)",
116
+ async () => {
117
+ await h.compose("f-redo");
118
+ await h.compose("f-redo");
119
+ const first = [];
120
+ const offA = h.feed.subscribe("f-redo", (e) => void first.push(e.id));
121
+ await until(() => first.length >= 2, h.tick, waitMs);
122
+ offA();
123
+ const again = [];
124
+ const offB = h.feed.subscribe("f-redo", (e) => void again.push(e.id), { after: 0 });
125
+ await until(() => again.length >= 2, h.tick, waitMs);
126
+ offB();
127
+ if (first.join() !== again.join() || first.length !== 2)
128
+ fail("redelivery", `first ${first.join()} vs again ${again.join()}`);
129
+ },
130
+ ],
131
+ [
132
+ "a throwing handler is reported, not advanced past, and the event is delivered again",
133
+ async () => {
134
+ await h.compose("f-throw");
135
+ const errors = [];
136
+ const ids = [];
137
+ let attempts = 0;
138
+ const off = h.feed.subscribe("f-throw", (e) => {
139
+ attempts += 1;
140
+ if (attempts === 1)
141
+ throw new Error("handler down");
142
+ ids.push(e.id);
143
+ }, { onError: (e) => void errors.push(e), retry: { baseMs: 5, maxMs: 20 } });
144
+ await until(() => errors.length >= 1, h.tick, waitMs);
145
+ if (errors.length < 1)
146
+ fail("handler-error", "a throwing handler was not reported");
147
+ // NO append and NO signal from here: a quiet line must still redeliver the failed event
148
+ // (a feed that retries only when the head moves leaves it stuck).
149
+ await until(() => ids.length >= 1, undefined, waitMs);
150
+ off();
151
+ if (ids.join() !== "f-throw#1")
152
+ fail("handler-retry", `after a throw on a quiet line the handler saw ${ids.join()}`);
153
+ },
154
+ ],
155
+ ];
156
+ for (const [name, run] of cases) {
157
+ try {
158
+ await run();
159
+ }
160
+ catch (e) {
161
+ fail("feed-threw", `${name}: ${e instanceof Error ? e.message : String(e)}`);
162
+ }
163
+ }
164
+ return findings;
165
+ }
166
+ export async function checkUnreadStoreConformance(make) {
167
+ const findings = [];
168
+ const fail = (code, message) => findings.push({ code, severity: "error", message });
169
+ const s = await make();
170
+ const cases = [
171
+ [
172
+ "an unseen (viewer, line) is 0",
173
+ async () => {
174
+ if ((await s.get("u-none", "e")) !== 0)
175
+ fail("default", "an unseen cursor was not 0");
176
+ },
177
+ ],
178
+ [
179
+ "advance moves forward, never back, and is idempotent",
180
+ async () => {
181
+ const a = await s.advance("u-fwd", "e", 5);
182
+ const b = await s.advance("u-fwd", "e", 3);
183
+ const c = await s.advance("u-fwd", "e", 5);
184
+ if (a !== 5 || b !== 5 || c !== 5 || (await s.get("u-fwd", "e")) !== 5)
185
+ fail("monotone", `advance returned ${[a, b, c].join()}`);
186
+ },
187
+ ],
188
+ [
189
+ "invalid positions change nothing",
190
+ async () => {
191
+ await s.advance("u-bad", "e", 2);
192
+ for (const bad of [
193
+ -1,
194
+ 1.5,
195
+ Number.NaN,
196
+ Number.POSITIVE_INFINITY,
197
+ "9",
198
+ ]) {
199
+ const r = await s.advance("u-bad", "e", bad);
200
+ if (r !== 2)
201
+ fail("invalid", `advance(${String(bad)}) returned ${r}`);
202
+ }
203
+ },
204
+ ],
205
+ [
206
+ "cursors are per viewer and per line, with no id collisions",
207
+ async () => {
208
+ await s.advance("u-a", "e1", 4);
209
+ await s.advance("u-c", "a:b", 9);
210
+ if ((await s.get("u-c:a", "b")) !== 0)
211
+ fail("collision", "ids that join to the same string share a cursor");
212
+ if ((await s.get("u-a", "e2")) !== 0)
213
+ fail("per-line", "another line shared a cursor");
214
+ if ((await s.get("u-b", "e1")) !== 0)
215
+ fail("per-viewer", "another viewer shared a cursor");
216
+ if ((await s.get("u-a", "e1")) !== 4)
217
+ fail("collision", "a joined-looking id disturbed a cursor");
218
+ },
219
+ ],
220
+ [
221
+ "concurrent advances land on the maximum",
222
+ async () => {
223
+ await Promise.all([1, 7, 3, 5, 2].map((n) => s.advance("u-race", "e", n)));
224
+ if ((await s.get("u-race", "e")) !== 7)
225
+ fail("race", "concurrent advances did not end at the max");
226
+ },
227
+ ],
228
+ ];
229
+ for (const [name, run] of cases) {
230
+ try {
231
+ await run();
232
+ }
233
+ catch (e) {
234
+ fail("store-threw", `${name}: ${e instanceof Error ? e.message : String(e)}`);
235
+ }
236
+ }
237
+ return findings;
238
+ }
239
+ //# sourceMappingURL=conformance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conformance.js","sourceRoot":"","sources":["../../src/conformance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ export class InMemoryUnreadStore {
2
+ // Nested maps, not a joined key: no separator a viewer or experience id could contain.
3
+ cursors = new Map();
4
+ async get(viewerId, experienceId) {
5
+ return this.cursors.get(viewerId)?.get(experienceId) ?? 0;
6
+ }
7
+ async advance(viewerId, experienceId, to) {
8
+ const current = this.cursors.get(viewerId)?.get(experienceId) ?? 0;
9
+ if (!Number.isSafeInteger(to) || to <= current)
10
+ return current;
11
+ let mine = this.cursors.get(viewerId);
12
+ if (mine === undefined) {
13
+ mine = new Map();
14
+ this.cursors.set(viewerId, mine);
15
+ }
16
+ mine.set(experienceId, to);
17
+ return to;
18
+ }
19
+ }
20
+ /** The events the viewer has not acknowledged, oldest first, one page. */
21
+ export async function unreadChanges(feed, store, viewerId, experienceId, opts = {}) {
22
+ const cursor = await store.get(viewerId, experienceId);
23
+ const r = await feed.read(experienceId, {
24
+ after: cursor,
25
+ ...(opts.limit !== undefined && { limit: opts.limit }),
26
+ });
27
+ return r.ok ? { ok: true, events: r.events, cursor, more: r.more } : r;
28
+ }
29
+ /**
30
+ * Acknowledge everything through `through` (clamped to the log's tip). Returns the new cursor, or
31
+ * the finding when the line could not be read (then nothing moves).
32
+ */
33
+ export async function markRead(feed, store, viewerId, experienceId, through) {
34
+ const r = await feed.read(experienceId, { limit: 1, after: Number.MAX_SAFE_INTEGER });
35
+ if (!r.ok)
36
+ return r;
37
+ if (!Number.isSafeInteger(through) || through < 0)
38
+ return { ok: true, cursor: await store.get(viewerId, experienceId) };
39
+ return {
40
+ ok: true,
41
+ cursor: await store.advance(viewerId, experienceId, Math.min(through, r.tip)),
42
+ };
43
+ }
44
+ //# sourceMappingURL=cursor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor.js","sourceRoot":"","sources":["../../src/cursor.ts"],"names":[],"mappings":"AAqBA,MAAM,OAAO,mBAAmB;IAC9B,uFAAuF;IACtE,OAAO,GAAG,IAAI,GAAG,EAA+B,CAAC;IAElE,KAAK,CAAC,GAAG,CAAC,QAAgB,EAAE,YAAoB;QAC9C,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,QAAgB,EAAE,YAAoB,EAAE,EAAU;QAC9D,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;QACnE,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC;QAC/D,IAAI,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACtC,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;YACjB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACnC,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;QAC3B,OAAO,EAAE,CAAC;IACZ,CAAC;CACF;AAaD,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAgB,EAChB,KAAkB,EAClB,QAAgB,EAChB,YAAoB,EACpB,OAA2B,EAAE;IAE7B,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;IACvD,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE;QACtC,KAAK,EAAE,MAAM;QACb,GAAG,CAAC,IAAI,CAAC,KAAK,KAAK,SAAS,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;KACvD,CAAC,CAAC;IACH,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACzE,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,IAAgB,EAChB,KAAkB,EAClB,QAAgB,EAChB,YAAoB,EACpB,OAAe;IAEf,MAAM,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACtF,IAAI,CAAC,CAAC,CAAC,EAAE;QAAE,OAAO,CAAC,CAAC;IACpB,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC;QAC/C,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,YAAY,CAAC,EAAE,CAAC;IACvE,OAAO;QACL,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC;KAC9E,CAAC;AACJ,CAAC"}