@agentshouse/mdruntime 0.1.0-cde2b99d2fcbc → 0.1.0-cdf06805b9658

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 (228) hide show
  1. package/AGENTS.md +286 -292
  2. package/README.md +16 -14
  3. package/dist/adapter.d.ts +99 -36
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/attached-model.d.ts +5 -1
  6. package/dist/attached-model.d.ts.map +1 -1
  7. package/dist/attached-model.js +21 -4
  8. package/dist/attempt.d.ts.map +1 -1
  9. package/dist/attempt.js +14 -1
  10. package/dist/binary.d.ts +0 -5
  11. package/dist/binary.d.ts.map +1 -1
  12. package/dist/binary.js +1 -1
  13. package/dist/bootstrap.d.ts +27 -9
  14. package/dist/bootstrap.d.ts.map +1 -1
  15. package/dist/bootstrap.js +155 -64
  16. package/dist/chain.d.ts +11 -0
  17. package/dist/chain.d.ts.map +1 -0
  18. package/dist/chain.js +64 -0
  19. package/dist/change-set.d.ts +9 -14
  20. package/dist/change-set.d.ts.map +1 -1
  21. package/dist/change-set.js +11 -63
  22. package/dist/change.d.ts +31 -1
  23. package/dist/change.d.ts.map +1 -1
  24. package/dist/change.js +10 -1
  25. package/dist/client.d.ts +24 -12
  26. package/dist/client.d.ts.map +1 -1
  27. package/dist/client.js +49 -9
  28. package/dist/collection.d.ts +7 -6
  29. package/dist/collection.d.ts.map +1 -1
  30. package/dist/collection.js +30 -77
  31. package/dist/compile.d.ts +16 -0
  32. package/dist/compile.d.ts.map +1 -0
  33. package/dist/compile.js +721 -0
  34. package/dist/connection.d.ts +42 -15
  35. package/dist/connection.d.ts.map +1 -1
  36. package/dist/connection.js +3397 -804
  37. package/dist/conversation.d.ts +30 -9
  38. package/dist/conversation.d.ts.map +1 -1
  39. package/dist/conversation.js +549 -181
  40. package/dist/digest.d.ts +7 -0
  41. package/dist/digest.d.ts.map +1 -0
  42. package/dist/digest.js +113 -0
  43. package/dist/durability.d.ts +5 -1
  44. package/dist/durability.d.ts.map +1 -1
  45. package/dist/durability.js +5 -0
  46. package/dist/entry.d.ts +19 -0
  47. package/dist/entry.d.ts.map +1 -0
  48. package/dist/entry.js +151 -0
  49. package/dist/fence.d.ts +6 -0
  50. package/dist/fence.d.ts.map +1 -0
  51. package/dist/fence.js +10 -0
  52. package/dist/file.d.ts +2 -10
  53. package/dist/file.d.ts.map +1 -1
  54. package/dist/file.js +3 -120
  55. package/dist/fulltext.d.ts +27 -0
  56. package/dist/fulltext.d.ts.map +1 -0
  57. package/dist/fulltext.js +368 -0
  58. package/dist/host.d.ts +15 -3
  59. package/dist/host.d.ts.map +1 -1
  60. package/dist/host.js +126 -16
  61. package/dist/identity.d.ts +1 -0
  62. package/dist/identity.d.ts.map +1 -1
  63. package/dist/identity.js +3 -0
  64. package/dist/index.d.ts +15 -9
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +5 -0
  67. package/dist/live.d.ts +22 -0
  68. package/dist/live.d.ts.map +1 -0
  69. package/dist/live.js +123 -0
  70. package/dist/memory.d.ts +10 -3
  71. package/dist/memory.d.ts.map +1 -1
  72. package/dist/memory.js +54 -26
  73. package/dist/messages.d.ts +28 -0
  74. package/dist/messages.d.ts.map +1 -0
  75. package/dist/messages.js +90 -0
  76. package/dist/model.d.ts +4 -2
  77. package/dist/model.d.ts.map +1 -1
  78. package/dist/model.js +11 -2
  79. package/dist/name.d.ts +4 -0
  80. package/dist/name.d.ts.map +1 -0
  81. package/dist/name.js +122 -0
  82. package/dist/observation.d.ts +19 -2
  83. package/dist/observation.d.ts.map +1 -1
  84. package/dist/observation.js +113 -12
  85. package/dist/opened.d.ts +22 -0
  86. package/dist/opened.d.ts.map +1 -0
  87. package/dist/opened.js +107 -0
  88. package/dist/outcome.d.ts +38 -4
  89. package/dist/outcome.d.ts.map +1 -1
  90. package/dist/outcome.js +26 -0
  91. package/dist/overlay.d.ts +3 -4
  92. package/dist/overlay.d.ts.map +1 -1
  93. package/dist/overlay.js +100 -182
  94. package/dist/query.d.ts +60 -4
  95. package/dist/query.d.ts.map +1 -1
  96. package/dist/query.js +48 -7
  97. package/dist/reach.d.ts +11 -6
  98. package/dist/reach.d.ts.map +1 -1
  99. package/dist/reach.js +182 -62
  100. package/dist/react.d.ts +8 -6
  101. package/dist/react.d.ts.map +1 -1
  102. package/dist/react.js +7 -17
  103. package/dist/readiness.d.ts +64 -0
  104. package/dist/readiness.d.ts.map +1 -0
  105. package/dist/readiness.js +33 -0
  106. package/dist/reads.d.ts +11 -0
  107. package/dist/reads.d.ts.map +1 -0
  108. package/dist/reads.js +100 -0
  109. package/dist/recovery.d.ts +25 -0
  110. package/dist/recovery.d.ts.map +1 -0
  111. package/dist/recovery.js +198 -0
  112. package/dist/refresh.d.ts +16 -15
  113. package/dist/refresh.d.ts.map +1 -1
  114. package/dist/refresh.js +113 -49
  115. package/dist/replica.d.ts +56 -33
  116. package/dist/replica.d.ts.map +1 -1
  117. package/dist/replica.js +297 -162
  118. package/dist/room-draft.d.ts +17 -2
  119. package/dist/room-draft.d.ts.map +1 -1
  120. package/dist/room-draft.js +65 -25
  121. package/dist/rows.d.ts +28 -37
  122. package/dist/rows.d.ts.map +1 -1
  123. package/dist/rows.js +113 -72
  124. package/dist/save.d.ts +50 -9
  125. package/dist/save.d.ts.map +1 -1
  126. package/dist/save.js +528 -166
  127. package/dist/search.d.ts +39 -28
  128. package/dist/search.d.ts.map +1 -1
  129. package/dist/search.js +37 -105
  130. package/dist/seed.d.ts +16 -0
  131. package/dist/seed.d.ts.map +1 -0
  132. package/dist/seed.js +75 -0
  133. package/dist/shared.d.ts +16 -0
  134. package/dist/shared.d.ts.map +1 -0
  135. package/dist/shared.js +431 -0
  136. package/dist/status.d.ts +4 -0
  137. package/dist/status.d.ts.map +1 -1
  138. package/dist/store.d.ts +118 -76
  139. package/dist/store.d.ts.map +1 -1
  140. package/dist/store.js +355 -444
  141. package/dist/stored.d.ts +145 -0
  142. package/dist/stored.d.ts.map +1 -0
  143. package/dist/stored.js +567 -0
  144. package/dist/tables.d.ts +354 -0
  145. package/dist/tables.d.ts.map +1 -0
  146. package/dist/tables.js +624 -0
  147. package/dist/turn.d.ts +9 -0
  148. package/dist/turn.d.ts.map +1 -0
  149. package/dist/turn.js +75 -0
  150. package/dist/validation.d.ts +30 -0
  151. package/dist/validation.d.ts.map +1 -0
  152. package/dist/validation.js +162 -0
  153. package/dist/windows.d.ts +22 -0
  154. package/dist/windows.d.ts.map +1 -0
  155. package/dist/windows.js +89 -0
  156. package/dist/wire.d.ts +164 -32
  157. package/dist/wire.d.ts.map +1 -1
  158. package/dist/wire.js +55 -31
  159. package/dist/words.d.ts +19 -0
  160. package/dist/words.d.ts.map +1 -0
  161. package/dist/words.js +150 -0
  162. package/package.json +27 -23
  163. package/dist/chat/acts.d.ts +0 -34
  164. package/dist/chat/acts.d.ts.map +0 -1
  165. package/dist/chat/acts.js +0 -36
  166. package/dist/chat/context.d.ts +0 -3
  167. package/dist/chat/context.d.ts.map +0 -1
  168. package/dist/chat/context.js +0 -10
  169. package/dist/chat/input.d.ts +0 -9
  170. package/dist/chat/input.d.ts.map +0 -1
  171. package/dist/chat/input.js +0 -99
  172. package/dist/chat/kit.d.ts +0 -60
  173. package/dist/chat/kit.d.ts.map +0 -1
  174. package/dist/chat/kit.js +0 -123
  175. package/dist/chat/lasting.d.ts +0 -6
  176. package/dist/chat/lasting.d.ts.map +0 -1
  177. package/dist/chat/lasting.js +0 -19
  178. package/dist/chat/list.d.ts +0 -20
  179. package/dist/chat/list.d.ts.map +0 -1
  180. package/dist/chat/list.js +0 -59
  181. package/dist/chat/markdown.d.ts +0 -19
  182. package/dist/chat/markdown.d.ts.map +0 -1
  183. package/dist/chat/markdown.js +0 -187
  184. package/dist/chat/message.d.ts +0 -6
  185. package/dist/chat/message.d.ts.map +0 -1
  186. package/dist/chat/message.js +0 -192
  187. package/dist/chat/messages.d.ts +0 -21
  188. package/dist/chat/messages.d.ts.map +0 -1
  189. package/dist/chat/messages.js +0 -151
  190. package/dist/chat/name.d.ts +0 -6
  191. package/dist/chat/name.d.ts.map +0 -1
  192. package/dist/chat/name.js +0 -37
  193. package/dist/chat/opened.d.ts +0 -11
  194. package/dist/chat/opened.d.ts.map +0 -1
  195. package/dist/chat/opened.js +0 -17
  196. package/dist/chat/overlay.d.ts +0 -31
  197. package/dist/chat/overlay.d.ts.map +0 -1
  198. package/dist/chat/overlay.js +0 -97
  199. package/dist/chat/parts.d.ts +0 -57
  200. package/dist/chat/parts.d.ts.map +0 -1
  201. package/dist/chat/parts.js +0 -119
  202. package/dist/chat/process.d.ts +0 -20
  203. package/dist/chat/process.d.ts.map +0 -1
  204. package/dist/chat/process.js +0 -32
  205. package/dist/chat/provider.d.ts +0 -63
  206. package/dist/chat/provider.d.ts.map +0 -1
  207. package/dist/chat/provider.js +0 -645
  208. package/dist/chat/question.d.ts +0 -9
  209. package/dist/chat/question.d.ts.map +0 -1
  210. package/dist/chat/question.js +0 -80
  211. package/dist/chat/questions.d.ts +0 -31
  212. package/dist/chat/questions.d.ts.map +0 -1
  213. package/dist/chat/questions.js +0 -111
  214. package/dist/chat/read.d.ts +0 -5
  215. package/dist/chat/read.d.ts.map +0 -1
  216. package/dist/chat/read.js +0 -13
  217. package/dist/chat/ready.d.ts +0 -33
  218. package/dist/chat/ready.d.ts.map +0 -1
  219. package/dist/chat/ready.js +0 -91
  220. package/dist/chat/style.d.ts +0 -4
  221. package/dist/chat/style.d.ts.map +0 -1
  222. package/dist/chat/style.js +0 -302
  223. package/dist/chat.d.ts +0 -14
  224. package/dist/chat.d.ts.map +0 -1
  225. package/dist/chat.js +0 -8
  226. package/dist/read-side.d.ts +0 -18
  227. package/dist/read-side.d.ts.map +0 -1
  228. package/dist/read-side.js +0 -440
package/dist/react.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { useCallback, useLayoutEffect, useRef, useState } from 'react';
2
- import { failed, runtimeError } from './failure.js';
2
+ import { failed } from './failure.js';
3
3
  const LOADING = { state: 'loading' };
4
4
  export function useObserve(client, topic, ...input) {
5
5
  const key = JSON.stringify([topic, ...input]);
@@ -33,24 +33,12 @@ export function useObserve(client, topic, ...input) {
33
33
  return held !== null && held.client === client && held.key === key ? held.observed : LOADING;
34
34
  }
35
35
  const IDLE = { busy: false, outcome: null, failure: null };
36
- export async function saveRoomDrafts(client, room, roomDrafts) {
37
- const kept = [];
38
- for (const [path, parts] of roomDrafts) {
39
- const prior = await client.roomDraft.read(room, path);
40
- const write = await client.roomDraft.write(room, path, prior?.revision ?? null, parts);
41
- if (write.outcome !== 'written' || write.roomDraft === null) {
42
- throw runtimeError(write.outcome === 'stale' ? write.code : 'room-draft-unwritten', `the Room Draft at ${path} was not written`);
43
- }
44
- kept.push({ path, revision: write.roomDraft.revision });
45
- }
46
- return client.save(room, kept);
47
- }
48
36
  export function useSave(client) {
49
37
  const [state, setState] = useState(IDLE);
50
- const save = useCallback(async (room, roomDrafts) => {
38
+ const settled = useCallback(async (saving) => {
51
39
  setState({ busy: true, outcome: null, failure: null });
52
40
  try {
53
- const outcome = await saveRoomDrafts(client, room, roomDrafts);
41
+ const outcome = await saving();
54
42
  setState({ busy: false, outcome, failure: null });
55
43
  return outcome;
56
44
  }
@@ -59,6 +47,8 @@ export function useSave(client) {
59
47
  setState({ busy: false, outcome: null, failure });
60
48
  throw failure;
61
49
  }
62
- }, [client]);
63
- return { ...state, save };
50
+ }, []);
51
+ const save = useCallback((room, parts) => settled(() => client.saveParts(room, [...parts].map(([path, one]) => ({ path, parts: one })))), [client, settled]);
52
+ const saveWritten = useCallback((room, roomDrafts) => settled(() => client.save(room, roomDrafts)), [client, settled]);
53
+ return { ...state, save, saveWritten };
64
54
  }
@@ -0,0 +1,64 @@
1
+ import type { Open } from './open.js';
2
+ export type CodeAvailability = Open<'stored' | 'absent'>;
3
+ export type RoomPreparation = Open<'complete' | 'acquiring' | 'preparing' | 'refused' | 'unadmitted'>;
4
+ export type RoomCoverage = {
5
+ readonly room: string;
6
+ readonly state: RoomPreparation;
7
+ };
8
+ export type ConversationCoverage = Open<'complete' | 'acquiring' | 'undeclared'>;
9
+ export type SearchObligation = Open<'required' | 'unused'>;
10
+ export type TargetCoverage = {
11
+ readonly complete: boolean;
12
+ readonly listed: boolean;
13
+ readonly rooms: readonly RoomCoverage[];
14
+ readonly conversations: ConversationCoverage;
15
+ readonly search: SearchObligation;
16
+ };
17
+ export type Persistence = Open<'granted' | 'denied' | 'unavailable'>;
18
+ export type Durability = Open<'persistent' | 'best-effort'>;
19
+ export type ConnectionAvailability = Open<'connected' | 'connecting' | 'unreachable'>;
20
+ export type HouseOutcome = Open<'open' | 'write-halted' | 'refused'>;
21
+ export type Space = Open<'full'>;
22
+ export type RefusedRoom = {
23
+ readonly room: string;
24
+ readonly name: string;
25
+ readonly file: string;
26
+ readonly rule: string;
27
+ readonly location: string | null;
28
+ };
29
+ export type InstallationStep = Open<'listing' | 'downloading' | 'writing' | 'replaying'>;
30
+ export type InstallationPause = Open<'hidden'>;
31
+ export type InstallationAmount = {
32
+ readonly done: number;
33
+ readonly total: number;
34
+ };
35
+ export type Installation = {
36
+ readonly step: InstallationStep | null;
37
+ readonly paused: InstallationPause | null;
38
+ readonly rooms: InstallationAmount;
39
+ readonly parts: InstallationAmount;
40
+ readonly bytes: InstallationAmount;
41
+ readonly received: InstallationAmount;
42
+ };
43
+ export type ReadinessStatus = Open<'preparing' | 'paused' | 'refused' | 'ready' | 'lost'>;
44
+ export type Readiness = {
45
+ readonly status: ReadinessStatus;
46
+ readonly ready: boolean;
47
+ readonly usable: boolean;
48
+ readonly code: CodeAvailability;
49
+ readonly coverage: TargetCoverage;
50
+ readonly durability: Durability;
51
+ readonly persistence: Persistence;
52
+ readonly connection: ConnectionAvailability;
53
+ readonly house: HouseOutcome;
54
+ readonly space: Space | null;
55
+ readonly installation: Installation;
56
+ readonly refused: readonly RefusedRoom[];
57
+ readonly lost: boolean;
58
+ };
59
+ export type ReadinessFacts = Omit<Readiness, 'status' | 'ready' | 'usable' | 'durability' | 'coverage'> & {
60
+ readonly coverage: Omit<TargetCoverage, 'complete'>;
61
+ readonly established: boolean;
62
+ };
63
+ export declare function readinessOf(facts: ReadinessFacts): Readiness;
64
+ //# sourceMappingURL=readiness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readiness.d.ts","sourceRoot":"","sources":["../src/readiness.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ const COMPLETE_ROOMS = new Set(['complete', 'unadmitted']);
2
+ const SETTLED_ROOMS = new Set([...COMPLETE_ROOMS, 'refused']);
3
+ export function readinessOf(facts) {
4
+ const { established, coverage: held, ...reported } = facts;
5
+ const settledConversations = held.conversations === 'complete' || held.conversations === 'undeclared';
6
+ const complete = held.listed &&
7
+ settledConversations &&
8
+ held.rooms.every((room) => COMPLETE_ROOMS.has(room.state));
9
+ const settled = held.listed &&
10
+ settledConversations &&
11
+ held.rooms.every((room) => SETTLED_ROOMS.has(room.state));
12
+ const coverage = { ...held, complete };
13
+ const stored = facts.code === 'stored';
14
+ const ready = stored && complete;
15
+ const usable = stored && (ready || established);
16
+ const status = ready
17
+ ? 'ready'
18
+ : facts.space !== null || facts.installation.paused !== null
19
+ ? 'paused'
20
+ : settled && !complete
21
+ ? 'refused'
22
+ : facts.lost && !established
23
+ ? 'lost'
24
+ : 'preparing';
25
+ return {
26
+ ...reported,
27
+ coverage,
28
+ status,
29
+ ready,
30
+ usable,
31
+ durability: facts.persistence === 'granted' ? 'persistent' : 'best-effort',
32
+ };
33
+ }
@@ -0,0 +1,11 @@
1
+ import type { Adapter } from './adapter.js';
2
+ import type { Reads } from './bootstrap.js';
3
+ export type DoorReads = Reads & {
4
+ carried(locator: string): number;
5
+ finished(locator: string, turn: number): void;
6
+ opened(): void;
7
+ applied(): void;
8
+ lost(): void;
9
+ };
10
+ export declare function doorReads(adapter: Adapter, declaration: (locator: string) => unknown, generation: () => number): DoorReads;
11
+ //# sourceMappingURL=reads.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reads.d.ts","sourceRoot":"","sources":["../src/reads.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EAOR,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAE5C,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG;IAC9B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9C,MAAM,IAAI,IAAI,CAAC;IACf,OAAO,IAAI,IAAI,CAAC;IAChB,IAAI,IAAI,IAAI,CAAC;CACd,CAAC;AAkCF,wBAAgB,SAAS,CACvB,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,EACzC,UAAU,EAAE,MAAM,MAAM,GACvB,SAAS,CA4FX"}
package/dist/reads.js ADDED
@@ -0,0 +1,100 @@
1
+ function declaredAs(declaration) {
2
+ if (declaration === null || typeof declaration !== 'object')
3
+ return declaration;
4
+ const { roomVersion: _version, ...declared } = declaration;
5
+ return declared;
6
+ }
7
+ function sameHeld(one, other) {
8
+ return one === other;
9
+ }
10
+ function failed(answer) {
11
+ return answer?.kind === 'transport-uncertainty';
12
+ }
13
+ function continues(answer) {
14
+ const page = answer;
15
+ return page?.kind === 'net-advancement' && page.pageToken !== undefined;
16
+ }
17
+ export function doorReads(adapter, declaration, generation) {
18
+ const reading = new Map();
19
+ const taken = new Map();
20
+ let carrying = false;
21
+ function joined(read, authorization, request, ask) {
22
+ const key = `${read} ${request.locator} ${request.pageToken ?? ''}`;
23
+ const { held, received, ...named } = request;
24
+ const asked = JSON.stringify([declaredAs(declaration(request.locator)), named]);
25
+ const asking = generation();
26
+ const current = reading.get(key);
27
+ if (current !== undefined &&
28
+ current.authorization === authorization &&
29
+ current.asked === asked &&
30
+ sameHeld(current.held, held) &&
31
+ current.received === received &&
32
+ (!current.settled || current.holder < asking)) {
33
+ if (current.settled && !current.continued)
34
+ reading.delete(key);
35
+ else
36
+ current.holder = Math.max(current.holder, asking);
37
+ if (current.from === asking)
38
+ return current.answer;
39
+ const again = () => joined(read, authorization, request, ask);
40
+ return current.answer.then((answer) => {
41
+ if (failed(answer))
42
+ return again();
43
+ taken.set(request.locator, (taken.get(request.locator) ?? 0) + 1);
44
+ return answer;
45
+ }, again);
46
+ }
47
+ const answer = ask();
48
+ const sent = {
49
+ locator: request.locator,
50
+ authorization,
51
+ asked,
52
+ held,
53
+ received,
54
+ answer,
55
+ from: asking,
56
+ holder: asking,
57
+ settled: false,
58
+ continued: false,
59
+ };
60
+ reading.set(key, sent);
61
+ const settled = (result, kept) => {
62
+ sent.settled = true;
63
+ sent.continued = kept && read === 'refresh' && continues(result);
64
+ if (reading.get(key) !== sent)
65
+ return;
66
+ const live = sent.holder === generation();
67
+ if (!kept || (live ? !sent.continued : !carrying))
68
+ reading.delete(key);
69
+ };
70
+ answer.then((result) => settled(result, !failed(result)), () => settled(null, false));
71
+ return answer;
72
+ }
73
+ return {
74
+ bootstrap: (authorization, request) => joined('bootstrap', authorization, request, () => adapter.bootstrap(authorization, request)),
75
+ refresh: (authorization, request) => joined('refresh', authorization, request, () => adapter.refresh(authorization, request)),
76
+ reached: (locator, roomVersion, target) => adapter.reached(locator, roomVersion, target),
77
+ carried: (locator) => taken.get(locator) ?? 0,
78
+ finished(locator, turn) {
79
+ for (const [key, held] of reading) {
80
+ if (held.locator !== locator || !held.settled || held.holder !== turn)
81
+ continue;
82
+ if (turn === generation() || !carrying)
83
+ reading.delete(key);
84
+ }
85
+ },
86
+ opened() {
87
+ carrying = true;
88
+ },
89
+ applied() {
90
+ carrying = false;
91
+ for (const [key, held] of reading) {
92
+ if (held.settled && held.holder !== generation())
93
+ reading.delete(key);
94
+ }
95
+ },
96
+ lost() {
97
+ reading.clear();
98
+ },
99
+ };
100
+ }
@@ -0,0 +1,25 @@
1
+ export type Attempted = 'done' | 'again' | {
2
+ readonly at: number;
3
+ };
4
+ export type Attempt = (signal: AbortSignal) => Promise<Attempted>;
5
+ export type AttemptOptions = {
6
+ readonly at?: number;
7
+ readonly again?: boolean;
8
+ };
9
+ export type RecoveryOptions = {
10
+ readonly firstMs?: number;
11
+ readonly maxMs?: number;
12
+ readonly attempts?: number;
13
+ readonly inFlight?: number;
14
+ };
15
+ export type Recovery = {
16
+ attempt(intent: string, run: Attempt, options?: AttemptOptions): Promise<boolean>;
17
+ pending(intent: string): boolean;
18
+ resume(): void;
19
+ cancel(intent: string): void;
20
+ scope(inFlight?: number): Recovery;
21
+ retire(): Promise<void>;
22
+ };
23
+ export declare function retryDelay(tries: number, firstMs?: number, maxMs?: number): number;
24
+ export declare function createRecovery(options?: RecoveryOptions): Recovery;
25
+ //# sourceMappingURL=recovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,SAAS,CAAC,CAAC;AAElE,MAAM,MAAM,cAAc,GAAG;IAAE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEhF,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG;IACrB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAClF,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IACjC,MAAM,IAAI,IAAI,CAAC;IACf,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IACnC,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB,CAAC;AAsBF,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,SAAW,EAAE,KAAK,SAAS,GAAG,MAAM,CAGpF;AAwKD,wBAAgB,cAAc,CAAC,OAAO,GAAE,eAAoB,GAAG,QAAQ,CAStE"}
@@ -0,0 +1,198 @@
1
+ // A first retry waits about a second, so a passing failure clears before the next attempt.
2
+ const FIRST_MS = 1_000;
3
+ // Waits stop doubling at half a minute, so work resumes soon after a long failure ends.
4
+ const MAX_MS = 30_000;
5
+ const ATTEMPTS = 8;
6
+ export function retryDelay(tries, firstMs = FIRST_MS, maxMs = MAX_MS) {
7
+ const longest = Math.min(maxMs, firstMs * 2 ** tries);
8
+ return longest / 2 + (Math.random() * longest) / 2;
9
+ }
10
+ function owner(backoff, inFlight, release = () => { }) {
11
+ const intents = new Map();
12
+ const children = new Set();
13
+ const running = new Set();
14
+ const queued = [];
15
+ let free = inFlight;
16
+ let retired = false;
17
+ function finish(key, intent, done) {
18
+ if (intents.get(key) === intent)
19
+ intents.delete(key);
20
+ if (intent.timer !== null)
21
+ clearTimeout(intent.timer);
22
+ intent.timer = null;
23
+ intent.end(done);
24
+ }
25
+ function wait(key, intent, at, guided) {
26
+ if (intent.timer !== null)
27
+ clearTimeout(intent.timer);
28
+ intent.due = at;
29
+ intent.guided = guided;
30
+ intent.timer = setTimeout(() => {
31
+ intent.timer = null;
32
+ launch(key, intent);
33
+ }, Math.max(0, at - Date.now()));
34
+ }
35
+ function launch(key, intent) {
36
+ intent.due = null;
37
+ intent.guided = false;
38
+ const controller = new AbortController();
39
+ intent.controller = controller;
40
+ const run = start(key, intent, controller);
41
+ running.add(run);
42
+ void run.finally(() => running.delete(run));
43
+ }
44
+ async function start(key, intent, controller) {
45
+ while (free === 0)
46
+ await new Promise((admit) => queued.push(admit));
47
+ if (controller.signal.aborted) {
48
+ queued.shift()?.();
49
+ return;
50
+ }
51
+ free -= 1;
52
+ const abandoned = new Promise((resolve) => {
53
+ controller.signal.addEventListener('abort', () => resolve(null), { once: true });
54
+ });
55
+ let result;
56
+ try {
57
+ result = await Promise.race([intent.run(controller.signal), abandoned]);
58
+ }
59
+ catch {
60
+ result = 'again';
61
+ }
62
+ finally {
63
+ free += 1;
64
+ queued.shift()?.();
65
+ }
66
+ if (result === null || controller.signal.aborted || intents.get(key) !== intent)
67
+ return;
68
+ intent.controller = null;
69
+ const again = intent.again;
70
+ intent.again = null;
71
+ if (result === 'done') {
72
+ intent.tries = 0;
73
+ if (again === null)
74
+ finish(key, intent, true);
75
+ else if (again === 'now')
76
+ launch(key, intent);
77
+ else
78
+ wait(key, intent, again, true);
79
+ return;
80
+ }
81
+ intent.tries += 1;
82
+ if (intent.tries >= backoff.attempts) {
83
+ finish(key, intent, false);
84
+ return;
85
+ }
86
+ if (result === 'again') {
87
+ // The next attempt waits the doubling backoff, up to its maximum.
88
+ const delay = retryDelay(intent.tries - 1, backoff.firstMs, backoff.maxMs);
89
+ wait(key, intent, Date.now() + delay, false);
90
+ }
91
+ else
92
+ wait(key, intent, result.at, true);
93
+ }
94
+ const recovery = {
95
+ attempt(key, run, options = {}) {
96
+ if (retired)
97
+ return Promise.resolve(false);
98
+ const held = intents.get(key);
99
+ if (held !== undefined) {
100
+ held.run = run;
101
+ if (held.controller !== null) {
102
+ const at = options.at;
103
+ held.again =
104
+ at === undefined || held.again === 'now'
105
+ ? 'now'
106
+ : Math.min(at, held.again ?? Number.POSITIVE_INFINITY);
107
+ }
108
+ else if (held.due !== null && held.due <= Date.now()) {
109
+ if (held.timer !== null)
110
+ clearTimeout(held.timer);
111
+ held.timer = null;
112
+ launch(key, held);
113
+ }
114
+ else if (options.at !== undefined && (held.due === null || options.at < held.due)) {
115
+ wait(key, held, options.at, true);
116
+ }
117
+ return held.ended;
118
+ }
119
+ let end = () => { };
120
+ const ended = new Promise((resolve) => {
121
+ end = resolve;
122
+ });
123
+ const intent = {
124
+ run,
125
+ tries: 0,
126
+ timer: null,
127
+ due: null,
128
+ guided: false,
129
+ controller: null,
130
+ again: null,
131
+ ended,
132
+ end,
133
+ };
134
+ intents.set(key, intent);
135
+ if (options.at !== undefined)
136
+ wait(key, intent, options.at, true);
137
+ else if (options.again === true) {
138
+ intent.tries = 1;
139
+ wait(key, intent, Date.now() + retryDelay(0, backoff.firstMs, backoff.maxMs), false);
140
+ }
141
+ else
142
+ launch(key, intent);
143
+ return ended;
144
+ },
145
+ pending(key) {
146
+ const intent = intents.get(key);
147
+ return intent !== undefined && intent.controller === null;
148
+ },
149
+ resume() {
150
+ if (retired)
151
+ return;
152
+ for (const [key, intent] of [...intents]) {
153
+ if (intent.guided && intent.due !== null && intent.due > Date.now())
154
+ continue;
155
+ if (intent.timer !== null)
156
+ clearTimeout(intent.timer);
157
+ intent.timer = null;
158
+ intent.controller?.abort();
159
+ intent.again = null;
160
+ intent.tries = 0;
161
+ launch(key, intent);
162
+ }
163
+ },
164
+ cancel(key) {
165
+ const intent = intents.get(key);
166
+ if (intent === undefined)
167
+ return;
168
+ intent.controller?.abort();
169
+ finish(key, intent, false);
170
+ },
171
+ scope(bound = Infinity) {
172
+ const child = owner(backoff, bound, () => children.delete(child));
173
+ if (retired)
174
+ void child.retire();
175
+ else
176
+ children.add(child);
177
+ return child;
178
+ },
179
+ async retire() {
180
+ retired = true;
181
+ release();
182
+ for (const [key, intent] of [...intents]) {
183
+ intent.controller?.abort();
184
+ finish(key, intent, false);
185
+ }
186
+ await Promise.all([...children].map((child) => child.retire()));
187
+ await Promise.all(running);
188
+ },
189
+ };
190
+ return recovery;
191
+ }
192
+ export function createRecovery(options = {}) {
193
+ return owner({
194
+ firstMs: options.firstMs ?? FIRST_MS,
195
+ maxMs: options.maxMs ?? MAX_MS,
196
+ attempts: options.attempts ?? ATTEMPTS,
197
+ }, options.inFlight ?? Infinity);
198
+ }
package/dist/refresh.d.ts CHANGED
@@ -1,24 +1,25 @@
1
- import type { Adapter, AdapterFailure, Authorization, RetryableRefusal } from './adapter.js';
2
- import type { Remodel } from './bootstrap.js';
3
- import { type ConfirmedLayer } from './replica.js';
4
- import type { ReplicaRecord } from './store.js';
5
- import { type Refused, type Unconfirmed } from './transfer.js';
1
+ import type { AdapterFailure, Authorization, RefreshPage, RetryableRefusal } from './adapter.js';
2
+ import type { Reads, Remodel } from './bootstrap.js';
3
+ import type { ReplicaRecord } from './replica.js';
4
+ import type { RefreshedPage } from './store.js';
5
+ import type { HeldEntry, StoredEntry } from './tables.js';
6
+ import { type Unconfirmed } from './transfer.js';
6
7
  export type Advancement = {
7
8
  kind: 'still-current';
8
9
  } | {
9
10
  kind: 'advanced';
10
11
  record: ReplicaRecord;
12
+ changed: ReadonlySet<string>;
11
13
  } | {
12
14
  kind: 'beyond-horizon';
13
15
  } | RetryableRefusal | Unconfirmed | AdapterFailure;
14
- export type RefreshOutcome = {
15
- kind: 'still-current';
16
- } | {
17
- kind: 'advanced';
18
- confirmed: ConfirmedLayer;
19
- } | {
20
- kind: 'beyond-horizon';
21
- } | RetryableRefusal | Unconfirmed | Refused | AdapterFailure;
22
- export declare function advancement(adapter: Adapter, authorization: Authorization, held: ReplicaRecord, remodel: Remodel, continuing: () => boolean): Promise<Advancement>;
23
- export declare function refreshRoom(adapter: Adapter, authorization: Authorization, confirmed: ConfirmedLayer, remodel: Remodel, continuing: () => boolean): Promise<RefreshOutcome>;
16
+ export type Refreshing = {
17
+ turn(): Promise<void>;
18
+ prior(paths: readonly string[]): Promise<ReadonlyMap<string, HeldEntry>>;
19
+ stage(page: RefreshedPage): Promise<boolean>;
20
+ };
21
+ type PageContent = Pick<RefreshPage, 'documents' | 'revisions' | 'fileIds' | 'changes' | 'binaryFiles'>;
22
+ export declare function pageEntries(page: PageContent, removals: readonly string[], roomVersion: string, prior: (paths: readonly string[]) => Promise<ReadonlyMap<string, HeldEntry>>): Promise<StoredEntry[]>;
23
+ export declare function advancement(adapter: Reads, authorization: Authorization, held: ReplicaRecord, remodel: Remodel, continuing: () => boolean, staging: Refreshing): Promise<Advancement>;
24
+ export {};
24
25
  //# sourceMappingURL=refresh.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../src/refresh.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,aAAa,EAGb,gBAAgB,EACjB,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAe,KAAK,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5E,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GACzB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,aAAa,CAAA;CAAE,GAC3C;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B,gBAAgB,GAChB,WAAW,GACX,cAAc,CAAC;AAEnB,MAAM,MAAM,cAAc,GACtB;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GACzB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,cAAc,CAAA;CAAE,GAC/C;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B,gBAAgB,GAChB,WAAW,GACX,OAAO,GACP,cAAc,CAAC;AAEnB,wBAAsB,WAAW,CAC/B,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,aAAa,EAC5B,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,OAAO,GACxB,OAAO,CAAC,WAAW,CAAC,CAwEtB;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,aAAa,EAC5B,SAAS,EAAE,cAAc,EACzB,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,OAAO,GACxB,OAAO,CAAC,cAAc,CAAC,CAKzB"}
1
+ {"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../src/refresh.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACjG,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAG9D,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,eAAe,CAAA;CAAE,GACzB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAAE,GACzE;IAAE,IAAI,EAAE,gBAAgB,CAAA;CAAE,GAC1B,gBAAgB,GAChB,WAAW,GACX,cAAc,CAAC;AAEnB,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,KAAK,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IACzE,KAAK,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9C,CAAC;AASF,KAAK,WAAW,GAAG,IAAI,CACrB,WAAW,EACX,WAAW,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAClE,CAAC;AAmFF,wBAAsB,WAAW,CAC/B,IAAI,EAAE,WAAW,EACjB,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,GAC3E,OAAO,CAAC,WAAW,EAAE,CAAC,CAKxB;AAeD,wBAAsB,WAAW,CAC/B,OAAO,EAAE,KAAK,EACd,aAAa,EAAE,aAAa,EAC5B,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,OAAO,EACzB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,WAAW,CAAC,CA8CtB"}