@agentshouse/mdruntime 0.1.0-cacb4c3f6734e → 0.1.0-caee7332d2235

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 (210) hide show
  1. package/AGENTS.md +277 -222
  2. package/README.md +20 -10
  3. package/dist/adapter.d.ts +95 -20
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/attached-model.d.ts.map +1 -1
  6. package/dist/attached-model.js +15 -2
  7. package/dist/attempt.d.ts +16 -0
  8. package/dist/attempt.d.ts.map +1 -0
  9. package/dist/attempt.js +70 -0
  10. package/dist/binary.d.ts +23 -0
  11. package/dist/binary.d.ts.map +1 -0
  12. package/dist/binary.js +8 -0
  13. package/dist/bootstrap.d.ts +18 -5
  14. package/dist/bootstrap.d.ts.map +1 -1
  15. package/dist/bootstrap.js +113 -37
  16. package/dist/change-set.d.ts +7 -2
  17. package/dist/change-set.d.ts.map +1 -1
  18. package/dist/change-set.js +54 -16
  19. package/dist/change.d.ts +15 -0
  20. package/dist/change.d.ts.map +1 -1
  21. package/dist/change.js +26 -1
  22. package/dist/chat/acts.d.ts +33 -0
  23. package/dist/chat/acts.d.ts.map +1 -0
  24. package/dist/chat/acts.js +36 -0
  25. package/dist/chat/context.d.ts +3 -0
  26. package/dist/chat/context.d.ts.map +1 -0
  27. package/dist/chat/context.js +10 -0
  28. package/dist/chat/earlier.d.ts +10 -0
  29. package/dist/chat/earlier.d.ts.map +1 -0
  30. package/dist/chat/earlier.js +54 -0
  31. package/dist/chat/input.d.ts +9 -0
  32. package/dist/chat/input.d.ts.map +1 -0
  33. package/dist/chat/input.js +99 -0
  34. package/dist/chat/kit.d.ts +61 -0
  35. package/dist/chat/kit.d.ts.map +1 -0
  36. package/dist/chat/kit.js +123 -0
  37. package/dist/chat/lasting.d.ts +6 -0
  38. package/dist/chat/lasting.d.ts.map +1 -0
  39. package/dist/chat/lasting.js +19 -0
  40. package/dist/chat/list.d.ts +27 -0
  41. package/dist/chat/list.d.ts.map +1 -0
  42. package/dist/chat/list.js +99 -0
  43. package/dist/chat/markdown.d.ts +21 -0
  44. package/dist/chat/markdown.d.ts.map +1 -0
  45. package/dist/chat/markdown.js +246 -0
  46. package/dist/chat/message.d.ts +6 -0
  47. package/dist/chat/message.d.ts.map +1 -0
  48. package/dist/chat/message.js +192 -0
  49. package/dist/chat/messages.d.ts +22 -0
  50. package/dist/chat/messages.d.ts.map +1 -0
  51. package/dist/chat/messages.js +171 -0
  52. package/dist/chat/name.d.ts +6 -0
  53. package/dist/chat/name.d.ts.map +1 -0
  54. package/dist/chat/name.js +37 -0
  55. package/dist/chat/opened.d.ts +11 -0
  56. package/dist/chat/opened.d.ts.map +1 -0
  57. package/dist/chat/opened.js +17 -0
  58. package/dist/chat/overlay.d.ts +31 -0
  59. package/dist/chat/overlay.d.ts.map +1 -0
  60. package/dist/chat/overlay.js +97 -0
  61. package/dist/chat/parts.d.ts +57 -0
  62. package/dist/chat/parts.d.ts.map +1 -0
  63. package/dist/chat/parts.js +119 -0
  64. package/dist/chat/pieces.d.ts +62 -0
  65. package/dist/chat/pieces.d.ts.map +1 -0
  66. package/dist/chat/pieces.js +1110 -0
  67. package/dist/chat/process.d.ts +20 -0
  68. package/dist/chat/process.d.ts.map +1 -0
  69. package/dist/chat/process.js +32 -0
  70. package/dist/chat/provider.d.ts +63 -0
  71. package/dist/chat/provider.d.ts.map +1 -0
  72. package/dist/chat/provider.js +630 -0
  73. package/dist/chat/question.d.ts +9 -0
  74. package/dist/chat/question.d.ts.map +1 -0
  75. package/dist/chat/question.js +69 -0
  76. package/dist/chat/questions.d.ts +31 -0
  77. package/dist/chat/questions.d.ts.map +1 -0
  78. package/dist/chat/questions.js +111 -0
  79. package/dist/chat/read.d.ts +5 -0
  80. package/dist/chat/read.d.ts.map +1 -0
  81. package/dist/chat/read.js +13 -0
  82. package/dist/chat/ready.d.ts +34 -0
  83. package/dist/chat/ready.d.ts.map +1 -0
  84. package/dist/chat/ready.js +91 -0
  85. package/dist/chat/steady.d.ts +3 -0
  86. package/dist/chat/steady.d.ts.map +1 -0
  87. package/dist/chat/steady.js +26 -0
  88. package/dist/chat/style.d.ts +4 -0
  89. package/dist/chat/style.d.ts.map +1 -0
  90. package/dist/chat/style.js +302 -0
  91. package/dist/chat.d.ts +14 -0
  92. package/dist/chat.d.ts.map +1 -0
  93. package/dist/chat.js +8 -0
  94. package/dist/client.d.ts +30 -13
  95. package/dist/client.d.ts.map +1 -1
  96. package/dist/client.js +79 -16
  97. package/dist/collection.d.ts +23 -13
  98. package/dist/collection.d.ts.map +1 -1
  99. package/dist/collection.js +349 -36
  100. package/dist/connection.d.ts +50 -16
  101. package/dist/connection.d.ts.map +1 -1
  102. package/dist/connection.js +1968 -424
  103. package/dist/conversation.d.ts +31 -10
  104. package/dist/conversation.d.ts.map +1 -1
  105. package/dist/conversation.js +521 -170
  106. package/dist/durability.d.ts +7 -1
  107. package/dist/durability.d.ts.map +1 -1
  108. package/dist/entry.d.ts +17 -0
  109. package/dist/entry.d.ts.map +1 -0
  110. package/dist/entry.js +157 -0
  111. package/dist/expectation.d.ts +21 -0
  112. package/dist/expectation.d.ts.map +1 -0
  113. package/dist/expectation.js +70 -0
  114. package/dist/failure.d.ts +13 -0
  115. package/dist/failure.d.ts.map +1 -0
  116. package/dist/failure.js +19 -0
  117. package/dist/fence.d.ts +6 -0
  118. package/dist/fence.d.ts.map +1 -0
  119. package/dist/fence.js +10 -0
  120. package/dist/host.d.ts +15 -2
  121. package/dist/host.d.ts.map +1 -1
  122. package/dist/host.js +102 -19
  123. package/dist/identity.d.ts +5 -0
  124. package/dist/identity.d.ts.map +1 -1
  125. package/dist/identity.js +19 -1
  126. package/dist/index.d.ts +16 -8
  127. package/dist/index.d.ts.map +1 -1
  128. package/dist/index.js +5 -0
  129. package/dist/memory.d.ts +11 -4
  130. package/dist/memory.d.ts.map +1 -1
  131. package/dist/memory.js +91 -24
  132. package/dist/model.d.ts +14 -4
  133. package/dist/model.d.ts.map +1 -1
  134. package/dist/model.js +12 -1
  135. package/dist/observation.d.ts +7 -2
  136. package/dist/observation.d.ts.map +1 -1
  137. package/dist/observation.js +29 -12
  138. package/dist/open.d.ts +2 -0
  139. package/dist/open.d.ts.map +1 -0
  140. package/dist/open.js +1 -0
  141. package/dist/outcome.d.ts +38 -3
  142. package/dist/outcome.d.ts.map +1 -1
  143. package/dist/outcome.js +45 -1
  144. package/dist/overlay.d.ts +18 -5
  145. package/dist/overlay.d.ts.map +1 -1
  146. package/dist/overlay.js +247 -165
  147. package/dist/query.d.ts +60 -4
  148. package/dist/query.d.ts.map +1 -1
  149. package/dist/query.js +41 -38
  150. package/dist/reach.d.ts +8 -3
  151. package/dist/reach.d.ts.map +1 -1
  152. package/dist/reach.js +73 -24
  153. package/dist/react.d.ts +31 -20
  154. package/dist/react.d.ts.map +1 -1
  155. package/dist/react.js +13 -21
  156. package/dist/read-side.d.ts +16 -7
  157. package/dist/read-side.d.ts.map +1 -1
  158. package/dist/read-side.js +495 -102
  159. package/dist/readiness.d.ts +53 -0
  160. package/dist/readiness.d.ts.map +1 -0
  161. package/dist/readiness.js +33 -0
  162. package/dist/reads.d.ts +11 -0
  163. package/dist/reads.d.ts.map +1 -0
  164. package/dist/reads.js +98 -0
  165. package/dist/recovery.d.ts +25 -0
  166. package/dist/recovery.d.ts.map +1 -0
  167. package/dist/recovery.js +197 -0
  168. package/dist/refresh.d.ts +13 -3
  169. package/dist/refresh.d.ts.map +1 -1
  170. package/dist/refresh.js +72 -42
  171. package/dist/replica.d.ts +17 -8
  172. package/dist/replica.d.ts.map +1 -1
  173. package/dist/replica.js +165 -79
  174. package/dist/room-draft.d.ts +38 -9
  175. package/dist/room-draft.d.ts.map +1 -1
  176. package/dist/room-draft.js +141 -33
  177. package/dist/rows.d.ts +10 -13
  178. package/dist/rows.d.ts.map +1 -1
  179. package/dist/rows.js +19 -14
  180. package/dist/save.d.ts +35 -3
  181. package/dist/save.d.ts.map +1 -1
  182. package/dist/save.js +522 -107
  183. package/dist/search.d.ts +52 -8
  184. package/dist/search.d.ts.map +1 -1
  185. package/dist/search.js +192 -47
  186. package/dist/seed.d.ts +16 -0
  187. package/dist/seed.d.ts.map +1 -0
  188. package/dist/seed.js +75 -0
  189. package/dist/shared.d.ts +15 -0
  190. package/dist/shared.d.ts.map +1 -0
  191. package/dist/shared.js +398 -0
  192. package/dist/status.d.ts +6 -2
  193. package/dist/status.d.ts.map +1 -1
  194. package/dist/status.js +0 -8
  195. package/dist/store.d.ts +91 -18
  196. package/dist/store.d.ts.map +1 -1
  197. package/dist/store.js +704 -283
  198. package/dist/turn.d.ts +9 -0
  199. package/dist/turn.d.ts.map +1 -0
  200. package/dist/turn.js +64 -0
  201. package/dist/windows.d.ts +26 -0
  202. package/dist/windows.d.ts.map +1 -0
  203. package/dist/windows.js +93 -0
  204. package/dist/wire.d.ts +157 -33
  205. package/dist/wire.d.ts.map +1 -1
  206. package/dist/wire.js +65 -31
  207. package/package.json +26 -14
  208. package/dist/attachment.d.ts +0 -3
  209. package/dist/attachment.d.ts.map +0 -1
  210. package/dist/attachment.js +0 -8
@@ -0,0 +1,53 @@
1
+ import type { Open } from './open.js';
2
+ export type CodeAvailability = Open<'stored' | 'absent'>;
3
+ export type RoomPreparation = Open<'complete' | 'acquiring' | 'preparing' | 'indexing' | 'refused' | 'unadmitted'>;
4
+ export type RoomCoverage = {
5
+ readonly room: string;
6
+ readonly state: RoomPreparation;
7
+ };
8
+ export type ConversationCoverage = Open<'complete' | 'acquiring' | 'indexing' | '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 CapacityCause = {
22
+ readonly cause: Open<'insufficient-space' | 'quota-exceeded'>;
23
+ readonly needed: number | null;
24
+ readonly available: number | null;
25
+ };
26
+ export type RefusedRoom = {
27
+ readonly room: string;
28
+ readonly file: string;
29
+ readonly rule: string;
30
+ readonly location: string | null;
31
+ };
32
+ export type ReadinessStatus = Open<'preparing' | 'paused' | 'refused' | 'ready' | 'lost'>;
33
+ export type Readiness = {
34
+ readonly status: ReadinessStatus;
35
+ readonly ready: boolean;
36
+ readonly usable: boolean;
37
+ readonly code: CodeAvailability;
38
+ readonly coverage: TargetCoverage;
39
+ readonly durability: Durability;
40
+ readonly persistence: Persistence;
41
+ readonly connection: ConnectionAvailability;
42
+ readonly house: HouseOutcome;
43
+ readonly capacity: CapacityCause | null;
44
+ readonly refused: readonly RefusedRoom[];
45
+ readonly lost: boolean;
46
+ };
47
+ export type ReadinessFacts = Omit<Readiness, 'status' | 'ready' | 'usable' | 'durability' | 'coverage'> & {
48
+ readonly coverage: Omit<TargetCoverage, 'complete'>;
49
+ readonly established: boolean;
50
+ };
51
+ export declare const SETTLED_ROOMS: ReadonlySet<string>;
52
+ export declare function readinessOf(facts: ReadinessFacts): Readiness;
53
+ //# 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
+ export 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.capacity !== null
19
+ ? 'paused'
20
+ : facts.lost && !established
21
+ ? 'lost'
22
+ : settled && !complete
23
+ ? 'refused'
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;AAiCF,wBAAgB,SAAS,CACvB,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,EACzC,UAAU,EAAE,MAAM,MAAM,GACvB,SAAS,CA0FX"}
package/dist/reads.js ADDED
@@ -0,0 +1,98 @@
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?.documents === other?.documents && one?.revisions === other?.revisions;
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, ...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.settled || current.holder < asking)) {
32
+ if (current.settled && !current.continued)
33
+ reading.delete(key);
34
+ else
35
+ current.holder = Math.max(current.holder, asking);
36
+ if (current.from === asking)
37
+ return current.answer;
38
+ const again = () => joined(read, authorization, request, ask);
39
+ return current.answer.then((answer) => {
40
+ if (failed(answer))
41
+ return again();
42
+ taken.set(request.locator, (taken.get(request.locator) ?? 0) + 1);
43
+ return answer;
44
+ }, again);
45
+ }
46
+ const answer = ask();
47
+ const sent = {
48
+ locator: request.locator,
49
+ authorization,
50
+ asked,
51
+ held,
52
+ answer,
53
+ from: asking,
54
+ holder: asking,
55
+ settled: false,
56
+ continued: false,
57
+ };
58
+ reading.set(key, sent);
59
+ const settled = (result, kept) => {
60
+ sent.settled = true;
61
+ sent.continued = kept && read === 'refresh' && continues(result);
62
+ if (reading.get(key) !== sent)
63
+ return;
64
+ const live = sent.holder === generation();
65
+ if (!kept || (live ? !sent.continued : !carrying))
66
+ reading.delete(key);
67
+ };
68
+ answer.then((result) => settled(result, !failed(result)), () => settled(null, false));
69
+ return answer;
70
+ }
71
+ return {
72
+ bootstrap: (authorization, request) => joined('bootstrap', authorization, request, () => adapter.bootstrap(authorization, request)),
73
+ refresh: (authorization, request) => joined('refresh', authorization, request, () => adapter.refresh(authorization, request)),
74
+ reached: (locator, roomVersion, target) => adapter.reached(locator, roomVersion, target),
75
+ carried: (locator) => taken.get(locator) ?? 0,
76
+ finished(locator, turn) {
77
+ for (const [key, held] of reading) {
78
+ if (held.locator !== locator || !held.settled || held.holder !== turn)
79
+ continue;
80
+ if (turn === generation() || !carrying)
81
+ reading.delete(key);
82
+ }
83
+ },
84
+ opened() {
85
+ carrying = true;
86
+ },
87
+ applied() {
88
+ carrying = false;
89
+ for (const [key, held] of reading) {
90
+ if (held.settled && held.holder !== generation())
91
+ reading.delete(key);
92
+ }
93
+ },
94
+ lost() {
95
+ reading.clear();
96
+ },
97
+ };
98
+ }
@@ -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 ceilingMs?: 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, ceilingMs?: 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,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,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,SAAS,SAAa,GAAG,MAAM,CAG5F;AAuKD,wBAAgB,cAAc,CAAC,OAAO,GAAE,eAAoB,GAAG,QAAQ,CAStE"}
@@ -0,0 +1,197 @@
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 CEILING_MS = 30_000;
5
+ const ATTEMPTS = 8;
6
+ export function retryDelay(tries, firstMs = FIRST_MS, ceilingMs = CEILING_MS) {
7
+ const ceiling = Math.min(ceilingMs, firstMs * 2 ** tries);
8
+ return ceiling / 2 + (Math.random() * ceiling) / 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
+ const delay = retryDelay(intent.tries - 1, backoff.firstMs, backoff.ceilingMs);
88
+ wait(key, intent, Date.now() + delay, false);
89
+ }
90
+ else
91
+ wait(key, intent, result.at, true);
92
+ }
93
+ const recovery = {
94
+ attempt(key, run, options = {}) {
95
+ if (retired)
96
+ return Promise.resolve(false);
97
+ const held = intents.get(key);
98
+ if (held !== undefined) {
99
+ held.run = run;
100
+ if (held.controller !== null) {
101
+ const at = options.at;
102
+ held.again =
103
+ at === undefined || held.again === 'now'
104
+ ? 'now'
105
+ : Math.min(at, held.again ?? Number.POSITIVE_INFINITY);
106
+ }
107
+ else if (held.due !== null && held.due <= Date.now()) {
108
+ if (held.timer !== null)
109
+ clearTimeout(held.timer);
110
+ held.timer = null;
111
+ launch(key, held);
112
+ }
113
+ else if (options.at !== undefined && (held.due === null || options.at < held.due)) {
114
+ wait(key, held, options.at, true);
115
+ }
116
+ return held.ended;
117
+ }
118
+ let end = () => { };
119
+ const ended = new Promise((resolve) => {
120
+ end = resolve;
121
+ });
122
+ const intent = {
123
+ run,
124
+ tries: 0,
125
+ timer: null,
126
+ due: null,
127
+ guided: false,
128
+ controller: null,
129
+ again: null,
130
+ ended,
131
+ end,
132
+ };
133
+ intents.set(key, intent);
134
+ if (options.at !== undefined)
135
+ wait(key, intent, options.at, true);
136
+ else if (options.again === true) {
137
+ intent.tries = 1;
138
+ wait(key, intent, Date.now() + retryDelay(0, backoff.firstMs, backoff.ceilingMs), false);
139
+ }
140
+ else
141
+ launch(key, intent);
142
+ return ended;
143
+ },
144
+ pending(key) {
145
+ const intent = intents.get(key);
146
+ return intent !== undefined && intent.controller === null;
147
+ },
148
+ resume() {
149
+ if (retired)
150
+ return;
151
+ for (const [key, intent] of [...intents]) {
152
+ if (intent.guided && intent.due !== null && intent.due > Date.now())
153
+ continue;
154
+ if (intent.timer !== null)
155
+ clearTimeout(intent.timer);
156
+ intent.timer = null;
157
+ intent.controller?.abort();
158
+ intent.again = null;
159
+ intent.tries = 0;
160
+ launch(key, intent);
161
+ }
162
+ },
163
+ cancel(key) {
164
+ const intent = intents.get(key);
165
+ if (intent === undefined)
166
+ return;
167
+ intent.controller?.abort();
168
+ finish(key, intent, false);
169
+ },
170
+ scope(bound = Infinity) {
171
+ const child = owner(backoff, bound, () => children.delete(child));
172
+ if (retired)
173
+ void child.retire();
174
+ else
175
+ children.add(child);
176
+ return child;
177
+ },
178
+ async retire() {
179
+ retired = true;
180
+ release();
181
+ for (const [key, intent] of [...intents]) {
182
+ intent.controller?.abort();
183
+ finish(key, intent, false);
184
+ }
185
+ await Promise.all([...children].map((child) => child.retire()));
186
+ await Promise.all(running);
187
+ },
188
+ };
189
+ return recovery;
190
+ }
191
+ export function createRecovery(options = {}) {
192
+ return owner({
193
+ firstMs: options.firstMs ?? FIRST_MS,
194
+ ceilingMs: options.ceilingMs ?? CEILING_MS,
195
+ attempts: options.attempts ?? ATTEMPTS,
196
+ }, options.inFlight ?? Infinity);
197
+ }
package/dist/refresh.d.ts CHANGED
@@ -1,7 +1,16 @@
1
- import type { Adapter, AdapterFailure, Authorization, RetryableRefusal } from './adapter.js';
2
- import type { Remodel } from './bootstrap.js';
1
+ import type { AdapterFailure, Authorization, RetryableRefusal } from './adapter.js';
2
+ import type { Reads, Remodel } from './bootstrap.js';
3
3
  import { type ConfirmedLayer } from './replica.js';
4
+ import type { ReplicaRecord } from './store.js';
4
5
  import { type Refused, type Unconfirmed } from './transfer.js';
6
+ export type Advancement = {
7
+ kind: 'still-current';
8
+ } | {
9
+ kind: 'advanced';
10
+ record: ReplicaRecord;
11
+ } | {
12
+ kind: 'beyond-horizon';
13
+ } | RetryableRefusal | Unconfirmed | AdapterFailure;
5
14
  export type RefreshOutcome = {
6
15
  kind: 'still-current';
7
16
  } | {
@@ -10,5 +19,6 @@ export type RefreshOutcome = {
10
19
  } | {
11
20
  kind: 'beyond-horizon';
12
21
  } | RetryableRefusal | Unconfirmed | Refused | AdapterFailure;
13
- export declare function refreshRoom(adapter: Adapter, authorization: Authorization, confirmed: ConfirmedLayer, remodel: Remodel, continuing: () => boolean): Promise<RefreshOutcome>;
22
+ export declare function advancement(adapter: Reads, authorization: Authorization, held: ReplicaRecord, remodel: Remodel, continuing: () => boolean): Promise<Advancement>;
23
+ export declare function refreshRoom(adapter: Reads, authorization: Authorization, confirmed: ConfirmedLayer, remodel: Remodel, continuing: () => boolean): Promise<RefreshOutcome>;
14
24
  //# 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,EAEjB,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAe,KAAK,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5E,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,SAAS,EAAE,cAAc,EACzB,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,OAAO,GACxB,OAAO,CAAC,cAAc,CAAC,CA8EzB"}
1
+ {"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../src/refresh.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAe,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEjG,OAAO,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACrD,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAE/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,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,KAAK,EACd,aAAa,EAAE,aAAa,EAC5B,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,OAAO,GACxB,OAAO,CAAC,WAAW,CAAC,CAgGtB;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,KAAK,EACd,aAAa,EAAE,aAAa,EAC5B,SAAS,EAAE,cAAc,EACzB,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,OAAO,GACxB,OAAO,CAAC,cAAc,CAAC,CAKzB"}
package/dist/refresh.js CHANGED
@@ -1,61 +1,82 @@
1
- import { declaredAttachments } from './attachment.js';
1
+ import { acceptedBinaries } from './overlay.js';
2
2
  import { readStaged } from './replica.js';
3
+ import { drafted, settling } from './shared.js';
4
+ import { ROW_WEIGHT, stepped } from './turn.js';
3
5
  import { unconfirmed } from './transfer.js';
4
- export async function refreshRoom(adapter, authorization, confirmed, remodel, continuing) {
5
- const locator = confirmed.record.locator;
6
- const fromRoomVersion = confirmed.record.roomVersion;
6
+ export async function advancement(adapter, authorization, held, remodel, continuing) {
7
+ const locator = held.locator;
8
+ const fromRoomVersion = held.roomVersion;
7
9
  const first = await adapter.refresh(authorization, {
8
10
  locator,
9
- selection: confirmed.record.replica.selection,
11
+ selection: held.replica.selection,
10
12
  fromRoomVersion,
11
13
  });
12
14
  if (first.kind !== 'net-advancement')
13
15
  return first;
14
16
  const roomVersion = first.roomVersion;
15
17
  const models = remodel(first.models, {
16
- collection: confirmed.record.model,
17
- selection: confirmed.record.selectionModel,
18
+ collection: held.model,
19
+ selection: held.selectionModel,
18
20
  });
19
- const documents = { ...confirmed.record.documents };
20
- const revisions = { ...confirmed.record.revisions };
21
- const changes = { ...confirmed.record.changes };
22
- const written = { ...confirmed.record.written };
23
- const binaryTargets = new Set(confirmed.record.binaryTargets);
24
- const attachments = [];
25
- const stage = (page) => {
26
- if (page.attachments !== undefined)
27
- attachments.push(page.attachments);
28
- Object.assign(documents, page.documents);
29
- Object.assign(revisions, page.revisions);
21
+ const documents = drafted(held.documents);
22
+ const revisions = drafted(held.revisions);
23
+ const fileIds = drafted(held.fileIds);
24
+ const changes = drafted(held.changes);
25
+ const written = drafted(held.written);
26
+ const binaries = drafted(held.binaries);
27
+ const stage = function* (page) {
30
28
  for (const path of Object.keys(page.documents)) {
31
- binaryTargets.delete(path);
29
+ delete binaries[path];
30
+ delete fileIds[path];
32
31
  delete changes[path];
33
32
  written[path] = roomVersion;
33
+ yield ROW_WEIGHT;
34
+ }
35
+ for (const path of Object.keys(page.binaryFiles)) {
36
+ delete documents[path];
37
+ delete fileIds[path];
38
+ delete changes[path];
39
+ delete written[path];
40
+ yield ROW_WEIGHT;
41
+ }
42
+ for (const [path, content] of Object.entries(page.documents)) {
43
+ documents[path] = content;
44
+ yield ROW_WEIGHT + content.length;
34
45
  }
35
- for (const target of page.binaryTargets) {
36
- delete documents[target];
37
- delete revisions[target];
38
- delete changes[target];
39
- delete written[target];
40
- binaryTargets.add(target);
46
+ for (const [path, binary] of Object.entries(acceptedBinaries(page.binaryFiles))) {
47
+ binaries[path] = binary;
48
+ yield ROW_WEIGHT;
49
+ }
50
+ for (const [path, revision] of Object.entries(page.revisions)) {
51
+ revisions[path] = revision;
52
+ yield ROW_WEIGHT;
53
+ }
54
+ for (const [path, fileId] of Object.entries(page.fileIds ?? {})) {
55
+ fileIds[path] = fileId;
56
+ yield ROW_WEIGHT;
57
+ }
58
+ for (const [path, change] of Object.entries(page.changes)) {
59
+ changes[path] = change;
60
+ yield ROW_WEIGHT;
41
61
  }
42
- Object.assign(changes, page.changes);
43
62
  for (const path of page.removals) {
44
63
  delete documents[path];
45
64
  delete revisions[path];
65
+ delete fileIds[path];
46
66
  delete changes[path];
47
67
  delete written[path];
48
- binaryTargets.delete(path);
68
+ delete binaries[path];
69
+ yield ROW_WEIGHT;
49
70
  }
50
71
  };
51
- stage(first);
72
+ await stepped(stage(first));
52
73
  let pageToken = first.pageToken;
53
74
  while (pageToken !== undefined) {
54
75
  if (!continuing())
55
76
  return unconfirmed;
56
77
  const page = await adapter.refresh(authorization, {
57
78
  locator,
58
- selection: confirmed.record.replica.selection,
79
+ selection: held.replica.selection,
59
80
  fromRoomVersion,
60
81
  pageToken,
61
82
  });
@@ -65,20 +86,29 @@ export async function refreshRoom(adapter, authorization, confirmed, remodel, co
65
86
  return page;
66
87
  if (page.roomVersion !== roomVersion)
67
88
  return unconfirmed;
68
- stage(page);
89
+ await stepped(stage(page));
69
90
  pageToken = page.pageToken;
70
91
  }
71
- const staged = readStaged({
72
- ...confirmed.record,
73
- model: models.collection,
74
- selectionModel: models.selection,
75
- roomVersion,
76
- documents,
77
- binaryTargets: [...binaryTargets].sort(),
78
- revisions,
79
- changes,
80
- written,
81
- attachments: attachments.length === 0 ? confirmed.record.attachments : declaredAttachments(attachments),
82
- }, confirmed);
92
+ return {
93
+ kind: 'advanced',
94
+ record: {
95
+ ...held,
96
+ model: models.collection,
97
+ selectionModel: models.selection,
98
+ roomVersion,
99
+ documents: await stepped(settling(documents)),
100
+ binaries: await stepped(settling(binaries)),
101
+ revisions: await stepped(settling(revisions)),
102
+ fileIds: await stepped(settling(fileIds)),
103
+ changes: await stepped(settling(changes)),
104
+ written: await stepped(settling(written)),
105
+ },
106
+ };
107
+ }
108
+ export async function refreshRoom(adapter, authorization, confirmed, remodel, continuing) {
109
+ const advanced = await advancement(adapter, authorization, confirmed.record, remodel, continuing);
110
+ if (advanced.kind !== 'advanced')
111
+ return advanced;
112
+ const staged = await readStaged(advanced.record, confirmed);
83
113
  return staged.kind === 'staged' ? { kind: 'advanced', confirmed: staged.confirmed } : staged;
84
114
  }