@bendyline/docblocks 2.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (278) hide show
  1. package/README.md +2 -2
  2. package/dist/{chunk-2D7Y4IR3.js → chunk-4437YWLE.js} +123 -67
  3. package/dist/{chunk-5MYAICRE.js → chunk-45T5AFCN.js} +44 -57
  4. package/dist/{chunk-PGR7LK6D.js → chunk-6L3JRDE7.js} +14 -6
  5. package/dist/{chunk-RVQHY3XU.js → chunk-6XHOOBYF.js} +0 -1
  6. package/dist/chunk-73OOROXH.js +8 -0
  7. package/dist/{chunk-SI2LU2RP.js → chunk-AYWKIZLD.js} +1 -1
  8. package/dist/{chunk-GCPBEMMH.js → chunk-BVY7GGNV.js} +10 -21
  9. package/dist/{chunk-AEHCVWU5.js → chunk-DZB666V7.js} +0 -1
  10. package/dist/{chunk-75UUF6C4.js → chunk-HI7QEWL6.js} +18 -10
  11. package/dist/chunk-KQ6756AJ.js +28 -0
  12. package/dist/chunk-LMMOFRGQ.js +148 -0
  13. package/dist/{chunk-FNW556FN.js → chunk-M2PUNKHS.js} +0 -1
  14. package/dist/chunk-M5ZNQGR5.js +19 -0
  15. package/dist/chunk-M62ZGJ5I.js +51 -0
  16. package/dist/chunk-OQCPBOQA.js +168 -0
  17. package/dist/{chunk-EGNDADXB.js → chunk-QI6YKTQV.js} +2 -3
  18. package/dist/chunk-SJBB5OD7.js +227 -0
  19. package/dist/{chunk-EZKJZUNJ.js → chunk-XAMILHQP.js} +0 -1
  20. package/dist/{chunk-J32GU3F3.js → chunk-YM7O3PWO.js} +249 -222
  21. package/dist/{chunk-RQJRQF46.js → chunk-ZO7G53QP.js} +41 -49
  22. package/dist/document/index.d.ts +358 -6
  23. package/dist/document/index.js +24 -4
  24. package/dist/filesystem/electron.d.ts +73 -4
  25. package/dist/filesystem/electron.js +16 -4
  26. package/dist/filesystem/index.d.ts +228 -22
  27. package/dist/filesystem/index.js +133 -20
  28. package/dist/filesystem/indexeddb.d.ts +284 -5
  29. package/dist/filesystem/indexeddb.js +22 -5
  30. package/dist/filesystem/memory.d.ts +152 -4
  31. package/dist/filesystem/memory.js +12 -4
  32. package/dist/filesystem/native.d.ts +136 -4
  33. package/dist/filesystem/native.js +27 -4
  34. package/dist/host/index.d.ts +32 -9
  35. package/dist/host/index.js +26 -30
  36. package/dist/index.d.ts +14 -10
  37. package/dist/index.js +204 -10
  38. package/dist/mcp/index.d.ts +33 -4
  39. package/dist/mcp/index.js +31 -4
  40. package/dist/mcp/zod.d.ts +645 -643
  41. package/dist/mcp/zod.js +700 -885
  42. package/dist/share/index.d.ts +43 -0
  43. package/dist/share/index.js +12 -0
  44. package/dist/{types-CsX6EN2t.d.ts → types-BEg5SQ1_.d.ts} +1 -1
  45. package/dist/{types-eOlt9J7n.d.ts → types-CueK0tvU.d.ts} +34 -11
  46. package/dist/vscode/index.d.ts +225 -3
  47. package/dist/vscode/index.js +389 -3
  48. package/dist/workspace/index.d.ts +141 -4
  49. package/dist/workspace/index.js +28 -3
  50. package/package.json +10 -5
  51. package/dist/chunk-2D7Y4IR3.js.map +0 -1
  52. package/dist/chunk-5MYAICRE.js.map +0 -1
  53. package/dist/chunk-75UUF6C4.js.map +0 -1
  54. package/dist/chunk-AEHCVWU5.js.map +0 -1
  55. package/dist/chunk-AYZ4HN5M.js +0 -20
  56. package/dist/chunk-AYZ4HN5M.js.map +0 -1
  57. package/dist/chunk-EGNDADXB.js.map +0 -1
  58. package/dist/chunk-EZKJZUNJ.js.map +0 -1
  59. package/dist/chunk-FNW556FN.js.map +0 -1
  60. package/dist/chunk-GCPBEMMH.js.map +0 -1
  61. package/dist/chunk-J32GU3F3.js.map +0 -1
  62. package/dist/chunk-PGR7LK6D.js.map +0 -1
  63. package/dist/chunk-RQJRQF46.js.map +0 -1
  64. package/dist/chunk-RVQHY3XU.js.map +0 -1
  65. package/dist/chunk-SI2LU2RP.js.map +0 -1
  66. package/dist/chunk-W5UUUII5.js +0 -142
  67. package/dist/chunk-W5UUUII5.js.map +0 -1
  68. package/dist/document/document-session.d.ts +0 -127
  69. package/dist/document/document-session.d.ts.map +0 -1
  70. package/dist/document/document-session.js +0 -749
  71. package/dist/document/document-session.js.map +0 -1
  72. package/dist/document/filesystem-target.d.ts +0 -17
  73. package/dist/document/filesystem-target.d.ts.map +0 -1
  74. package/dist/document/filesystem-target.js +0 -70
  75. package/dist/document/filesystem-target.js.map +0 -1
  76. package/dist/document/index.d.ts.map +0 -1
  77. package/dist/document/index.js.map +0 -1
  78. package/dist/document/recovery-journal.d.ts +0 -125
  79. package/dist/document/recovery-journal.d.ts.map +0 -1
  80. package/dist/document/recovery-journal.js +0 -385
  81. package/dist/document/recovery-journal.js.map +0 -1
  82. package/dist/document/types.d.ts +0 -89
  83. package/dist/document/types.d.ts.map +0 -1
  84. package/dist/document/types.js +0 -9
  85. package/dist/document/types.js.map +0 -1
  86. package/dist/filesystem/commit-lock.d.ts +0 -3
  87. package/dist/filesystem/commit-lock.d.ts.map +0 -1
  88. package/dist/filesystem/commit-lock.js +0 -20
  89. package/dist/filesystem/commit-lock.js.map +0 -1
  90. package/dist/filesystem/dbk-workspace.d.ts +0 -34
  91. package/dist/filesystem/dbk-workspace.d.ts.map +0 -1
  92. package/dist/filesystem/dbk-workspace.js +0 -85
  93. package/dist/filesystem/dbk-workspace.js.map +0 -1
  94. package/dist/filesystem/electron-provider-v2.d.ts +0 -36
  95. package/dist/filesystem/electron-provider-v2.d.ts.map +0 -1
  96. package/dist/filesystem/electron-provider-v2.js +0 -503
  97. package/dist/filesystem/electron-provider-v2.js.map +0 -1
  98. package/dist/filesystem/electron-provider.d.ts +0 -35
  99. package/dist/filesystem/electron-provider.d.ts.map +0 -1
  100. package/dist/filesystem/electron-provider.js +0 -69
  101. package/dist/filesystem/electron-provider.js.map +0 -1
  102. package/dist/filesystem/electron.d.ts.map +0 -1
  103. package/dist/filesystem/electron.js.map +0 -1
  104. package/dist/filesystem/file-media-provider.d.ts +0 -23
  105. package/dist/filesystem/file-media-provider.d.ts.map +0 -1
  106. package/dist/filesystem/file-media-provider.js +0 -85
  107. package/dist/filesystem/file-media-provider.js.map +0 -1
  108. package/dist/filesystem/filesystem-content-container.d.ts +0 -24
  109. package/dist/filesystem/filesystem-content-container.d.ts.map +0 -1
  110. package/dist/filesystem/filesystem-content-container.js +0 -153
  111. package/dist/filesystem/filesystem-content-container.js.map +0 -1
  112. package/dist/filesystem/fs-error.d.ts +0 -46
  113. package/dist/filesystem/fs-error.d.ts.map +0 -1
  114. package/dist/filesystem/fs-error.js +0 -170
  115. package/dist/filesystem/fs-error.js.map +0 -1
  116. package/dist/filesystem/index.d.ts.map +0 -1
  117. package/dist/filesystem/index.js.map +0 -1
  118. package/dist/filesystem/indexeddb-content-container.d.ts +0 -22
  119. package/dist/filesystem/indexeddb-content-container.d.ts.map +0 -1
  120. package/dist/filesystem/indexeddb-content-container.js +0 -100
  121. package/dist/filesystem/indexeddb-content-container.js.map +0 -1
  122. package/dist/filesystem/indexeddb-provider-v2.d.ts +0 -96
  123. package/dist/filesystem/indexeddb-provider-v2.d.ts.map +0 -1
  124. package/dist/filesystem/indexeddb-provider-v2.js +0 -1241
  125. package/dist/filesystem/indexeddb-provider-v2.js.map +0 -1
  126. package/dist/filesystem/indexeddb-provider.d.ts +0 -39
  127. package/dist/filesystem/indexeddb-provider.d.ts.map +0 -1
  128. package/dist/filesystem/indexeddb-provider.js +0 -145
  129. package/dist/filesystem/indexeddb-provider.js.map +0 -1
  130. package/dist/filesystem/indexeddb-store.d.ts +0 -34
  131. package/dist/filesystem/indexeddb-store.d.ts.map +0 -1
  132. package/dist/filesystem/indexeddb-store.js +0 -165
  133. package/dist/filesystem/indexeddb-store.js.map +0 -1
  134. package/dist/filesystem/indexeddb.d.ts.map +0 -1
  135. package/dist/filesystem/indexeddb.js.map +0 -1
  136. package/dist/filesystem/legacy-v2-adapter.d.ts +0 -62
  137. package/dist/filesystem/legacy-v2-adapter.d.ts.map +0 -1
  138. package/dist/filesystem/legacy-v2-adapter.js +0 -499
  139. package/dist/filesystem/legacy-v2-adapter.js.map +0 -1
  140. package/dist/filesystem/memory-provider-v2.d.ts +0 -92
  141. package/dist/filesystem/memory-provider-v2.d.ts.map +0 -1
  142. package/dist/filesystem/memory-provider-v2.js +0 -615
  143. package/dist/filesystem/memory-provider-v2.js.map +0 -1
  144. package/dist/filesystem/memory-provider.d.ts +0 -54
  145. package/dist/filesystem/memory-provider.d.ts.map +0 -1
  146. package/dist/filesystem/memory-provider.js +0 -132
  147. package/dist/filesystem/memory-provider.js.map +0 -1
  148. package/dist/filesystem/memory.d.ts.map +0 -1
  149. package/dist/filesystem/memory.js.map +0 -1
  150. package/dist/filesystem/move-entry.d.ts +0 -9
  151. package/dist/filesystem/move-entry.d.ts.map +0 -1
  152. package/dist/filesystem/move-entry.js +0 -103
  153. package/dist/filesystem/move-entry.js.map +0 -1
  154. package/dist/filesystem/move-error.d.ts +0 -53
  155. package/dist/filesystem/move-error.d.ts.map +0 -1
  156. package/dist/filesystem/move-error.js +0 -99
  157. package/dist/filesystem/move-error.js.map +0 -1
  158. package/dist/filesystem/native-provider-v2.d.ts +0 -75
  159. package/dist/filesystem/native-provider-v2.d.ts.map +0 -1
  160. package/dist/filesystem/native-provider-v2.js +0 -849
  161. package/dist/filesystem/native-provider-v2.js.map +0 -1
  162. package/dist/filesystem/native-provider.d.ts +0 -53
  163. package/dist/filesystem/native-provider.d.ts.map +0 -1
  164. package/dist/filesystem/native-provider.js +0 -471
  165. package/dist/filesystem/native-provider.js.map +0 -1
  166. package/dist/filesystem/native.d.ts.map +0 -1
  167. package/dist/filesystem/native.js.map +0 -1
  168. package/dist/filesystem/types.d.ts +0 -83
  169. package/dist/filesystem/types.d.ts.map +0 -1
  170. package/dist/filesystem/types.js +0 -16
  171. package/dist/filesystem/types.js.map +0 -1
  172. package/dist/filesystem/v2.d.ts +0 -172
  173. package/dist/filesystem/v2.d.ts.map +0 -1
  174. package/dist/filesystem/v2.js +0 -16
  175. package/dist/filesystem/v2.js.map +0 -1
  176. package/dist/filesystem/workspace-path.d.ts +0 -21
  177. package/dist/filesystem/workspace-path.d.ts.map +0 -1
  178. package/dist/filesystem/workspace-path.js +0 -80
  179. package/dist/filesystem/workspace-path.js.map +0 -1
  180. package/dist/host/filesystem-v2.d.ts +0 -45
  181. package/dist/host/filesystem-v2.d.ts.map +0 -1
  182. package/dist/host/filesystem-v2.js +0 -13
  183. package/dist/host/filesystem-v2.js.map +0 -1
  184. package/dist/host/git.d.ts +0 -200
  185. package/dist/host/git.d.ts.map +0 -1
  186. package/dist/host/git.js +0 -15
  187. package/dist/host/git.js.map +0 -1
  188. package/dist/host/index.d.ts.map +0 -1
  189. package/dist/host/index.js.map +0 -1
  190. package/dist/host/types.d.ts +0 -232
  191. package/dist/host/types.d.ts.map +0 -1
  192. package/dist/host/types.js +0 -10
  193. package/dist/host/types.js.map +0 -1
  194. package/dist/host/wire-policy.d.ts +0 -22
  195. package/dist/host/wire-policy.d.ts.map +0 -1
  196. package/dist/host/wire-policy.js +0 -92
  197. package/dist/host/wire-policy.js.map +0 -1
  198. package/dist/index.d.ts.map +0 -1
  199. package/dist/index.js.map +0 -1
  200. package/dist/mcp/index.d.ts.map +0 -1
  201. package/dist/mcp/index.js.map +0 -1
  202. package/dist/mcp/types.d.ts +0 -466
  203. package/dist/mcp/types.d.ts.map +0 -1
  204. package/dist/mcp/types.js +0 -25
  205. package/dist/mcp/types.js.map +0 -1
  206. package/dist/mcp/wire.d.ts +0 -29
  207. package/dist/mcp/wire.d.ts.map +0 -1
  208. package/dist/mcp/wire.js +0 -1046
  209. package/dist/mcp/wire.js.map +0 -1
  210. package/dist/mcp/zod.d.ts.map +0 -1
  211. package/dist/mcp/zod.js.map +0 -1
  212. package/dist/vscode/index.d.ts.map +0 -1
  213. package/dist/vscode/index.js.map +0 -1
  214. package/dist/vscode/messages.d.ts +0 -204
  215. package/dist/vscode/messages.d.ts.map +0 -1
  216. package/dist/vscode/messages.js +0 -494
  217. package/dist/vscode/messages.js.map +0 -1
  218. package/dist/workspace/index.d.ts.map +0 -1
  219. package/dist/workspace/index.js.map +0 -1
  220. package/dist/workspace/reconcile-electron.d.ts +0 -19
  221. package/dist/workspace/reconcile-electron.d.ts.map +0 -1
  222. package/dist/workspace/reconcile-electron.js +0 -42
  223. package/dist/workspace/reconcile-electron.js.map +0 -1
  224. package/dist/workspace/types.d.ts +0 -69
  225. package/dist/workspace/types.d.ts.map +0 -1
  226. package/dist/workspace/types.js +0 -2
  227. package/dist/workspace/types.js.map +0 -1
  228. package/dist/workspace/workspace-manager.d.ts +0 -47
  229. package/dist/workspace/workspace-manager.d.ts.map +0 -1
  230. package/dist/workspace/workspace-manager.js +0 -126
  231. package/dist/workspace/workspace-manager.js.map +0 -1
  232. package/src/document/document-session.ts +0 -834
  233. package/src/document/filesystem-target.ts +0 -109
  234. package/src/document/index.ts +0 -41
  235. package/src/document/recovery-journal.ts +0 -536
  236. package/src/document/types.ts +0 -116
  237. package/src/filesystem/commit-lock.ts +0 -29
  238. package/src/filesystem/dbk-workspace.ts +0 -135
  239. package/src/filesystem/electron-provider-v2.ts +0 -630
  240. package/src/filesystem/electron-provider.ts +0 -99
  241. package/src/filesystem/electron.ts +0 -3
  242. package/src/filesystem/file-media-provider.ts +0 -103
  243. package/src/filesystem/filesystem-content-container.ts +0 -164
  244. package/src/filesystem/fs-error.ts +0 -234
  245. package/src/filesystem/index.ts +0 -143
  246. package/src/filesystem/indexeddb-content-container.ts +0 -112
  247. package/src/filesystem/indexeddb-provider-v2.ts +0 -1740
  248. package/src/filesystem/indexeddb-provider.ts +0 -171
  249. package/src/filesystem/indexeddb-store.ts +0 -207
  250. package/src/filesystem/indexeddb.ts +0 -12
  251. package/src/filesystem/legacy-v2-adapter.ts +0 -779
  252. package/src/filesystem/memory-provider-v2.ts +0 -896
  253. package/src/filesystem/memory-provider.ts +0 -180
  254. package/src/filesystem/memory.ts +0 -15
  255. package/src/filesystem/move-entry.ts +0 -142
  256. package/src/filesystem/move-error.ts +0 -159
  257. package/src/filesystem/native-provider-v2.ts +0 -1165
  258. package/src/filesystem/native-provider.ts +0 -537
  259. package/src/filesystem/native.ts +0 -16
  260. package/src/filesystem/types.ts +0 -114
  261. package/src/filesystem/v2.ts +0 -231
  262. package/src/filesystem/workspace-path.ts +0 -92
  263. package/src/host/filesystem-v2.ts +0 -104
  264. package/src/host/git.ts +0 -246
  265. package/src/host/index.ts +0 -93
  266. package/src/host/types.ts +0 -274
  267. package/src/host/wire-policy.ts +0 -103
  268. package/src/index.ts +0 -10
  269. package/src/mcp/index.ts +0 -3
  270. package/src/mcp/types.ts +0 -594
  271. package/src/mcp/wire.ts +0 -1280
  272. package/src/mcp/zod.ts +0 -1000
  273. package/src/vscode/index.ts +0 -2
  274. package/src/vscode/messages.ts +0 -704
  275. package/src/workspace/index.ts +0 -17
  276. package/src/workspace/reconcile-electron.ts +0 -65
  277. package/src/workspace/types.ts +0 -67
  278. package/src/workspace/workspace-manager.ts +0 -151
@@ -1,749 +0,0 @@
1
- import { isFileSystemMoveStateError } from '../filesystem/move-error.js';
2
- const DEFAULT_AUTO_SAVE_RETRY_DELAYS_MS = [1000, 3000, 10000];
3
- /**
4
- * Raised by a commit target when optimistic external-change verification
5
- * fails. The session converts it into durable conflict state.
6
- */
7
- export class DocumentCommitConflictError extends Error {
8
- constructor(message, externalContent, externalVersion = null) {
9
- super(message);
10
- this.name = 'DocumentCommitConflictError';
11
- this.externalContent = externalContent;
12
- this.externalVersion = externalVersion;
13
- }
14
- }
15
- export class DocumentSessionConflictError extends Error {
16
- constructor(conflict) {
17
- super('The document changed outside DocBlocks while local edits were pending.');
18
- this.name = 'DocumentSessionConflictError';
19
- this.conflict = conflict;
20
- }
21
- }
22
- /**
23
- * A revisioned, serialized persistence boundary for one active document.
24
- *
25
- * Invariants:
26
- * - only drain() calls target.commit();
27
- * - at most one commit is in flight;
28
- * - revisions never decrease, including across target transitions;
29
- * - destructive operations wait for an in-flight commit and run last;
30
- * - a target is never changed by a React effect cleanup.
31
- */
32
- export class DocumentSession {
33
- constructor(options = {}) {
34
- this.listeners = new Set();
35
- this.target = null;
36
- this.content = '';
37
- this.persistedContent = null;
38
- this.revision = 0;
39
- this.persistedRevision = 0;
40
- this.savingRevision = null;
41
- this.savingContent = null;
42
- this.generation = 0;
43
- this.externalSequence = -1;
44
- this.lifecycle = 'open';
45
- this.frozen = false;
46
- this.error = null;
47
- this.conflict = null;
48
- this.autoSaveTimer = null;
49
- this.autoSaveRetryAttempt = 0;
50
- this.drainPromise = null;
51
- this.haltDrain = false;
52
- this.operationTail = Promise.resolve();
53
- this.subscribe = (listener) => {
54
- this.listeners.add(listener);
55
- return () => this.listeners.delete(listener);
56
- };
57
- this.getSnapshot = () => this.snapshot;
58
- this.autoSaveDelayMs = Math.max(0, options.autoSaveDelayMs ?? 500);
59
- this.autoSaveEnabled = options.autoSaveEnabled ?? true;
60
- this.autoSaveRetryDelaysMs = normalizeRetryDelays(options.autoSaveRetryDelaysMs ?? DEFAULT_AUTO_SAVE_RETRY_DELAYS_MS);
61
- this.recoveryJournal = options.recoveryJournal ?? null;
62
- this.snapshot = this.createSnapshot();
63
- }
64
- /** Enable or disable automatic persistence without weakening explicit or lifecycle flushes. */
65
- setAutoSaveEnabled(enabled) {
66
- if (this.autoSaveEnabled === enabled)
67
- return;
68
- this.autoSaveEnabled = enabled;
69
- if (!enabled) {
70
- this.clearAutoSaveTimer();
71
- return;
72
- }
73
- if (this.target &&
74
- this.lifecycle === 'open' &&
75
- !this.frozen &&
76
- !this.conflict &&
77
- this.persistedRevision < this.revision) {
78
- this.scheduleAutoSave();
79
- }
80
- }
81
- /**
82
- * Record a complete local snapshot and schedule its persistence.
83
- * Returns the assigned monotonic revision.
84
- */
85
- edit(content, scope) {
86
- if (!this.target || this.lifecycle !== 'open' || this.frozen) {
87
- throw new Error('Cannot edit a document session that is not open.');
88
- }
89
- if (scope.targetKey !== this.target.key || scope.generation !== this.generation) {
90
- throw new Error('Cannot apply an edit from an obsolete document instance.');
91
- }
92
- if (content === this.content)
93
- return this.revision;
94
- this.content = content;
95
- this.revision += 1;
96
- this.error = null;
97
- this.autoSaveRetryAttempt = 0;
98
- if (this.conflict) {
99
- this.conflict = {
100
- ...this.conflict,
101
- localContent: content,
102
- localRevision: this.revision,
103
- };
104
- }
105
- this.writeRecoverySnapshot();
106
- if (!this.conflict)
107
- this.scheduleAutoSave();
108
- this.emit();
109
- return this.revision;
110
- }
111
- /**
112
- * Persist the revision current when flush() is called. A later edit may
113
- * advance the session again, but commits remain strictly serialized.
114
- */
115
- async flush(reason = 'manual') {
116
- this.clearAutoSaveTimer();
117
- if (!this.target)
118
- return this.snapshot;
119
- if (this.conflict)
120
- throw new DocumentSessionConflictError(this.conflict);
121
- const requiredRevision = this.revision;
122
- while (this.target && this.persistedRevision < requiredRevision) {
123
- if (this.conflict)
124
- throw new DocumentSessionConflictError(this.conflict);
125
- if (this.haltDrain) {
126
- throw new Error('Document persistence was cancelled before the requested revision saved.');
127
- }
128
- try {
129
- await this.ensureDrain(reason);
130
- }
131
- catch (error) {
132
- if (this.conflict)
133
- throw new DocumentSessionConflictError(this.conflict);
134
- throw error;
135
- }
136
- }
137
- if (this.conflict)
138
- throw new DocumentSessionConflictError(this.conflict);
139
- if (this.persistedRevision < requiredRevision) {
140
- throw new Error('The document target closed before the requested revision saved.');
141
- }
142
- return this.snapshot;
143
- }
144
- /**
145
- * Flush the old document, then replace it with a new clean baseline.
146
- * UI state should change only after this promise resolves.
147
- */
148
- transitionTo(target, content) {
149
- return this.enqueueOperation(() => this.performTransition(async () => ({ target, content })).then((snapshot) => {
150
- if (!snapshot)
151
- throw new Error('A fixed document transition cannot be cancelled.');
152
- return snapshot;
153
- }));
154
- }
155
- /**
156
- * Freeze and flush the current document before reading the next one. The
157
- * loader may return null when a navigation request was superseded, leaving
158
- * the current target active and clean.
159
- */
160
- transitionWithLoad(load) {
161
- return this.enqueueOperation(() => this.performTransition(load));
162
- }
163
- /**
164
- * Flush the current path, run the storage move, and only then swap the
165
- * commit target. No edit can enter between those steps.
166
- */
167
- retarget(nextTarget, moveStorage) {
168
- return this.enqueueOperation(async () => {
169
- this.assertNotPreparingClose();
170
- if (!this.target)
171
- throw new Error('Cannot retarget a session without a document.');
172
- this.lifecycle = 'transitioning';
173
- this.frozen = true;
174
- this.emit();
175
- try {
176
- this.haltDrain = false;
177
- await this.flush('transition');
178
- await moveStorage();
179
- this.generation += 1;
180
- this.externalSequence = -1;
181
- this.target = nextTarget;
182
- this.error = null;
183
- this.conflict = null;
184
- this.lifecycle = 'open';
185
- this.frozen = false;
186
- this.emit();
187
- return this.snapshot;
188
- }
189
- catch (error) {
190
- if (isFileSystemMoveStateError(error)) {
191
- if (error.documentLocation === 'destination') {
192
- this.generation += 1;
193
- this.externalSequence = -1;
194
- this.target = nextTarget;
195
- }
196
- this.haltDrain = true;
197
- this.error = error;
198
- this.lifecycle = 'open';
199
- // The provider could not establish one authoritative compound move.
200
- // Keep editing frozen until navigation/recovery chooses a real path.
201
- this.frozen = true;
202
- this.emit();
203
- throw error;
204
- }
205
- this.lifecycle = 'open';
206
- this.frozen = false;
207
- this.emit();
208
- throw error;
209
- }
210
- });
211
- }
212
- /**
213
- * Cancel queued writes, wait for an already-running write, then delete.
214
- * The deletion is therefore the final storage operation for this target.
215
- */
216
- delete(deleteStorage) {
217
- return this.enqueueOperation(async () => {
218
- this.assertNotPreparingClose();
219
- this.lifecycle = 'transitioning';
220
- this.frozen = true;
221
- this.haltDrain = true;
222
- this.clearAutoSaveTimer();
223
- this.emit();
224
- await this.awaitActiveDrain();
225
- try {
226
- await deleteStorage();
227
- }
228
- catch (error) {
229
- this.haltDrain = false;
230
- this.lifecycle = 'open';
231
- this.frozen = false;
232
- if (this.target && this.persistedRevision < this.revision && !this.conflict) {
233
- this.scheduleAutoSave();
234
- }
235
- this.emit();
236
- throw error;
237
- }
238
- this.discardRecoverySnapshot();
239
- this.detach();
240
- return this.snapshot;
241
- });
242
- }
243
- /**
244
- * Discard queued changes and detach without mutating storage. This is used
245
- * for explicitly abandoned sessions, never as an implicit effect cleanup.
246
- */
247
- cancel() {
248
- return this.enqueueOperation(async () => {
249
- this.lifecycle = 'transitioning';
250
- this.frozen = true;
251
- this.haltDrain = true;
252
- this.clearAutoSaveTimer();
253
- this.emit();
254
- await this.awaitActiveDrain();
255
- this.discardRecoverySnapshot();
256
- this.detach();
257
- return this.snapshot;
258
- });
259
- }
260
- /**
261
- * Observe a filesystem/host change captured for targetKey. Clean sessions
262
- * adopt it. Dirty or saving sessions preserve local text and enter conflict.
263
- */
264
- observeExternal(change) {
265
- if (!this.target || change.targetKey !== this.target.key || this.lifecycle === 'closed') {
266
- return 'ignored';
267
- }
268
- if (change.sequence !== undefined) {
269
- if (!Number.isSafeInteger(change.sequence) || change.sequence <= this.externalSequence) {
270
- return 'ignored';
271
- }
272
- this.externalSequence = change.sequence;
273
- }
274
- // While conflicted, a current observation equal to the latest local
275
- // snapshot proves that revision durable without requiring another write.
276
- // Never acknowledge while a commit is still running; its eventual result
277
- // remains authoritative for the serialized drain.
278
- if (this.conflict &&
279
- change.content === this.content &&
280
- this.savingRevision === null &&
281
- this.lifecycle === 'open') {
282
- this.clearAutoSaveTimer();
283
- this.autoSaveRetryAttempt = 0;
284
- this.persistedRevision = this.revision;
285
- this.persistedContent = this.content;
286
- this.haltDrain = false;
287
- this.error = null;
288
- this.conflict = null;
289
- this.discardRecoverySnapshot();
290
- this.emit();
291
- return 'applied';
292
- }
293
- // Once a conflict exists, every later accepted observation refreshes its
294
- // external branch before ordinary duplicate suppression. In particular,
295
- // A -> local C -> external B -> external A must offer A, not stale B, when
296
- // the user chooses Reload external.
297
- if (this.conflict) {
298
- const externalVersion = change.version ?? null;
299
- if (this.conflict.localContent === this.content &&
300
- this.conflict.localRevision === this.revision &&
301
- this.conflict.externalContent === change.content &&
302
- this.conflict.externalVersion === externalVersion) {
303
- return 'ignored';
304
- }
305
- this.clearAutoSaveTimer();
306
- this.haltDrain = true;
307
- this.conflict = {
308
- targetKey: change.targetKey,
309
- localContent: this.content,
310
- localRevision: this.revision,
311
- externalContent: change.content,
312
- externalVersion,
313
- };
314
- this.error = null;
315
- this.writeRecoverySnapshot();
316
- this.emit();
317
- return 'conflict';
318
- }
319
- if (change.content === this.content ||
320
- change.content === this.persistedContent ||
321
- (this.savingRevision !== null && change.content === this.savingContent)) {
322
- return 'ignored';
323
- }
324
- const isClean = this.persistedRevision === this.revision &&
325
- this.savingRevision === null &&
326
- this.error === null &&
327
- this.conflict === null;
328
- if (isClean && change.content !== null) {
329
- this.content = change.content;
330
- this.revision += 1;
331
- this.persistedRevision = this.revision;
332
- this.persistedContent = change.content;
333
- this.generation += 1;
334
- this.discardRecoverySnapshot();
335
- this.emit();
336
- return 'applied';
337
- }
338
- this.clearAutoSaveTimer();
339
- this.haltDrain = true;
340
- this.conflict = {
341
- targetKey: change.targetKey,
342
- localContent: this.content,
343
- localRevision: this.revision,
344
- externalContent: change.content,
345
- externalVersion: change.version ?? null,
346
- };
347
- this.error = null;
348
- this.writeRecoverySnapshot();
349
- this.emit();
350
- return 'conflict';
351
- }
352
- resolveConflict(strategy) {
353
- return this.enqueueOperation(async () => {
354
- if (!this.conflict)
355
- return this.snapshot;
356
- this.clearAutoSaveTimer();
357
- this.haltDrain = true;
358
- await this.awaitActiveDrain();
359
- // A watcher may refresh or converge the conflict while an in-flight
360
- // commit settles. Resolve the branch that is current now, never the
361
- // snapshot captured before the await.
362
- const currentConflict = this.conflict;
363
- if (!currentConflict)
364
- return this.snapshot;
365
- if (strategy === 'use-external') {
366
- this.discardRecoverySnapshot();
367
- this.generation += 1;
368
- this.revision += 1;
369
- this.conflict = null;
370
- this.error = null;
371
- this.haltDrain = false;
372
- if (currentConflict.externalContent === null) {
373
- this.target = null;
374
- this.content = '';
375
- this.persistedContent = null;
376
- this.persistedRevision = this.revision;
377
- this.lifecycle = 'closed';
378
- this.frozen = true;
379
- }
380
- else {
381
- this.content = currentConflict.externalContent;
382
- this.persistedContent = currentConflict.externalContent;
383
- this.persistedRevision = this.revision;
384
- this.lifecycle = 'open';
385
- this.frozen = false;
386
- }
387
- this.emit();
388
- return this.snapshot;
389
- }
390
- // Keep the local branch, but make the observed external snapshot the
391
- // optimistic baseline for the next conditional commit.
392
- this.persistedContent = currentConflict.externalContent;
393
- // A clean external deletion/conflict has no unsaved local revision.
394
- // Keeping local still requires a fresh commit that recreates/overwrites
395
- // the target, so make that intent explicit with a new revision.
396
- this.revision += 1;
397
- this.conflict = null;
398
- this.error = null;
399
- this.haltDrain = false;
400
- this.lifecycle = 'open';
401
- this.frozen = false;
402
- this.writeRecoverySnapshot();
403
- this.emit();
404
- return this.flush('conflict');
405
- });
406
- }
407
- /**
408
- * Freeze edits and flush through the latest revision. A successful call
409
- * remains frozen until cancelClose(); the owning host may now destroy it.
410
- */
411
- prepareClose() {
412
- return this.enqueueOperation(async () => {
413
- if (this.lifecycle === 'closing')
414
- return this.snapshot;
415
- this.lifecycle = 'closing';
416
- this.frozen = true;
417
- this.haltDrain = false;
418
- this.emit();
419
- try {
420
- await this.flush('close');
421
- return this.snapshot;
422
- }
423
- catch (error) {
424
- this.lifecycle = 'open';
425
- this.frozen = false;
426
- this.emit();
427
- throw error;
428
- }
429
- });
430
- }
431
- /** Resume editing when a host cancels a prepared close. */
432
- cancelClose() {
433
- if (this.lifecycle !== 'closing')
434
- return;
435
- this.lifecycle = 'open';
436
- this.frozen = false;
437
- this.haltDrain = false;
438
- if (this.target && this.persistedRevision < this.revision && !this.conflict) {
439
- this.scheduleAutoSave();
440
- }
441
- this.emit();
442
- }
443
- async performTransition(load) {
444
- this.assertNotPreparingClose();
445
- this.lifecycle = 'transitioning';
446
- this.frozen = true;
447
- this.emit();
448
- try {
449
- this.haltDrain = false;
450
- await this.flush('transition');
451
- this.clearAutoSaveTimer();
452
- const next = await load();
453
- if (!next) {
454
- this.lifecycle = 'open';
455
- this.frozen = false;
456
- this.emit();
457
- return null;
458
- }
459
- this.generation += 1;
460
- this.externalSequence = -1;
461
- this.target = next.target;
462
- this.content = next.content;
463
- this.revision += 1;
464
- this.persistedRevision = this.revision;
465
- this.persistedContent = next.target ? next.content : null;
466
- this.savingRevision = null;
467
- this.savingContent = null;
468
- this.error = null;
469
- this.conflict = null;
470
- this.haltDrain = false;
471
- this.lifecycle = 'open';
472
- this.frozen = false;
473
- const recoveryState = this.restoreRecoverySnapshot();
474
- if (recoveryState === 'dirty')
475
- this.scheduleAutoSave();
476
- this.emit();
477
- return this.snapshot;
478
- }
479
- catch (error) {
480
- this.lifecycle = 'open';
481
- this.frozen = false;
482
- this.emit();
483
- throw error;
484
- }
485
- }
486
- scheduleAutoSave(delayMs = this.autoSaveDelayMs) {
487
- this.clearAutoSaveTimer();
488
- if (!this.autoSaveEnabled)
489
- return;
490
- this.autoSaveTimer = setTimeout(() => {
491
- this.autoSaveTimer = null;
492
- const target = this.target;
493
- const generation = this.generation;
494
- void this.flush('autosave').then(() => {
495
- this.autoSaveRetryAttempt = 0;
496
- }, () => {
497
- // Preserve honest error/conflict state while making ordinary I/O
498
- // failures recoverable. A newer edit already owns a fresh debounce
499
- // timer, and lifecycle operations cancel this retry path.
500
- if (this.autoSaveTimer !== null ||
501
- !target ||
502
- target !== this.target ||
503
- generation !== this.generation ||
504
- this.lifecycle !== 'open' ||
505
- this.frozen ||
506
- this.haltDrain ||
507
- this.conflict ||
508
- this.persistedRevision >= this.revision) {
509
- return;
510
- }
511
- const retryDelay = this.autoSaveRetryDelaysMs[this.autoSaveRetryAttempt];
512
- if (retryDelay === undefined)
513
- return;
514
- this.autoSaveRetryAttempt += 1;
515
- this.scheduleAutoSave(retryDelay);
516
- });
517
- }, delayMs);
518
- }
519
- clearAutoSaveTimer() {
520
- if (this.autoSaveTimer === null)
521
- return;
522
- clearTimeout(this.autoSaveTimer);
523
- this.autoSaveTimer = null;
524
- }
525
- ensureDrain(reason) {
526
- if (this.drainPromise)
527
- return this.drainPromise;
528
- const running = this.drain(reason);
529
- this.drainPromise = running;
530
- const clear = () => {
531
- if (this.drainPromise === running)
532
- this.drainPromise = null;
533
- };
534
- void running.then(clear, clear);
535
- return running;
536
- }
537
- async drain(reason) {
538
- while (this.target &&
539
- !this.haltDrain &&
540
- !this.conflict &&
541
- this.persistedRevision < this.revision) {
542
- const target = this.target;
543
- const generation = this.generation;
544
- const saveRevision = this.revision;
545
- const saveContent = this.content;
546
- const previousPersistedRevision = this.persistedRevision;
547
- const previousPersistedContent = this.persistedContent;
548
- this.savingRevision = saveRevision;
549
- this.savingContent = saveContent;
550
- this.error = null;
551
- this.emit();
552
- try {
553
- await target.commit({
554
- targetKey: target.key,
555
- content: saveContent,
556
- revision: saveRevision,
557
- persistedRevision: previousPersistedRevision,
558
- persistedContent: previousPersistedContent,
559
- reason,
560
- });
561
- }
562
- catch (error) {
563
- this.savingRevision = null;
564
- this.savingContent = null;
565
- if (error instanceof DocumentCommitConflictError) {
566
- this.haltDrain = true;
567
- this.conflict = {
568
- targetKey: target.key,
569
- localContent: this.content,
570
- localRevision: this.revision,
571
- externalContent: error.externalContent,
572
- externalVersion: error.externalVersion,
573
- };
574
- this.error = null;
575
- }
576
- else {
577
- this.error = toError(error);
578
- }
579
- this.emit();
580
- throw error;
581
- }
582
- if (generation !== this.generation || target !== this.target)
583
- return;
584
- this.persistedRevision = Math.max(this.persistedRevision, saveRevision);
585
- this.persistedContent = saveContent;
586
- this.autoSaveRetryAttempt = 0;
587
- this.savingRevision = null;
588
- this.savingContent = null;
589
- this.error = null;
590
- const activeConflict = this.getSnapshot().conflict;
591
- if (activeConflict?.externalContent === saveContent && this.content === saveContent) {
592
- // An observation that matched the in-flight local snapshot could not
593
- // be acknowledged until the commit settled. Its successful return now
594
- // proves both branches converged on the same durable revision.
595
- this.conflict = null;
596
- this.haltDrain = false;
597
- }
598
- this.recoveryJournal?.acknowledge({
599
- targetKey: target.key,
600
- generation,
601
- persistedRevision: this.persistedRevision,
602
- });
603
- this.emit();
604
- }
605
- }
606
- async awaitActiveDrain() {
607
- const active = this.drainPromise;
608
- if (!active)
609
- return;
610
- try {
611
- await active;
612
- }
613
- catch {
614
- // A destructive cancellation still waits for the operation to settle,
615
- // but a failed obsolete write must not prevent the requested deletion.
616
- }
617
- }
618
- /**
619
- * Rehydrate a synchronous crash snapshot after the durable baseline has
620
- * been read. A matching baseline is safe to resume as dirty; a changed
621
- * baseline becomes an explicit conflict and is never auto-overwritten.
622
- */
623
- restoreRecoverySnapshot() {
624
- if (!this.target || !this.recoveryJournal)
625
- return 'none';
626
- const recovered = this.recoveryJournal.lookup(this.target.key);
627
- if (!recovered)
628
- return 'none';
629
- if (recovered.content === this.content) {
630
- this.recoveryJournal.acknowledge({
631
- targetKey: recovered.targetKey,
632
- generation: recovered.generation,
633
- persistedRevision: recovered.revision,
634
- });
635
- return 'none';
636
- }
637
- const externalContent = this.content;
638
- this.generation = Math.max(this.generation, recovered.generation) + 1;
639
- this.revision = Math.max(this.revision, recovered.revision) + 1;
640
- this.content = recovered.content;
641
- if (recovered.persistedContent === externalContent) {
642
- this.writeRecoverySnapshot();
643
- return 'dirty';
644
- }
645
- // Preserve the journal's prior baseline so a second crash continues to
646
- // reopen as a conflict until the user explicitly chooses a branch.
647
- this.persistedContent = recovered.persistedContent;
648
- this.haltDrain = true;
649
- this.conflict = {
650
- targetKey: this.target.key,
651
- localContent: recovered.content,
652
- localRevision: this.revision,
653
- externalContent,
654
- externalVersion: null,
655
- };
656
- this.writeRecoverySnapshot();
657
- return 'conflict';
658
- }
659
- writeRecoverySnapshot() {
660
- if (!this.target || !this.recoveryJournal)
661
- return;
662
- if (this.persistedRevision >= this.revision && !this.conflict)
663
- return;
664
- this.recoveryJournal.write({
665
- targetKey: this.target.key,
666
- generation: this.generation,
667
- revision: this.revision,
668
- content: this.content,
669
- persistedContent: this.persistedContent,
670
- });
671
- }
672
- discardRecoverySnapshot() {
673
- if (!this.target)
674
- return;
675
- this.recoveryJournal?.discard(this.target.key);
676
- }
677
- detach() {
678
- this.clearAutoSaveTimer();
679
- this.autoSaveRetryAttempt = 0;
680
- this.generation += 1;
681
- this.externalSequence = -1;
682
- this.target = null;
683
- this.persistedContent = null;
684
- this.savingRevision = null;
685
- this.savingContent = null;
686
- this.error = null;
687
- this.conflict = null;
688
- this.haltDrain = false;
689
- this.lifecycle = 'closed';
690
- this.frozen = true;
691
- this.emit();
692
- }
693
- assertNotPreparingClose() {
694
- if (this.lifecycle === 'closing') {
695
- throw new Error('The document is already prepared for close.');
696
- }
697
- }
698
- enqueueOperation(operation) {
699
- const run = this.operationTail.then(operation, operation);
700
- this.operationTail = run.then(() => undefined, () => undefined);
701
- return run;
702
- }
703
- createSnapshot() {
704
- return Object.freeze({
705
- targetKey: this.target?.key ?? null,
706
- content: this.content,
707
- revision: this.revision,
708
- persistedRevision: this.persistedRevision,
709
- persistedContent: this.persistedContent,
710
- savingRevision: this.savingRevision,
711
- generation: this.generation,
712
- status: this.computeStatus(),
713
- lifecycle: this.lifecycle,
714
- frozen: this.frozen,
715
- error: this.error,
716
- conflict: this.conflict,
717
- });
718
- }
719
- computeStatus() {
720
- if (this.lifecycle === 'closed')
721
- return 'closed';
722
- if (!this.target)
723
- return 'idle';
724
- if (this.conflict)
725
- return 'conflict';
726
- if (this.error)
727
- return 'error';
728
- if (this.savingRevision !== null)
729
- return 'saving';
730
- return this.persistedRevision < this.revision ? 'dirty' : 'saved';
731
- }
732
- emit() {
733
- this.snapshot = this.createSnapshot();
734
- for (const listener of [...this.listeners])
735
- listener();
736
- }
737
- }
738
- function toError(error) {
739
- return error instanceof Error ? error : new Error(String(error));
740
- }
741
- function normalizeRetryDelays(delays) {
742
- return delays.map((delay) => {
743
- if (!Number.isFinite(delay) || delay < 0) {
744
- throw new TypeError('Autosave retry delays must be finite, non-negative numbers.');
745
- }
746
- return Math.floor(delay);
747
- });
748
- }
749
- //# sourceMappingURL=document-session.js.map