@brass-build/client 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/AGENTS.md +72 -63
  2. package/CHANGELOG.md +97 -0
  3. package/README.md +325 -235
  4. package/dist/auth-flow.d.ts +10 -1
  5. package/dist/auth-flow.d.ts.map +1 -1
  6. package/dist/auth-flow.js +64 -20
  7. package/dist/auth-flow.js.map +1 -1
  8. package/dist/client.d.ts +4 -0
  9. package/dist/client.d.ts.map +1 -1
  10. package/dist/client.js +245 -76
  11. package/dist/client.js.map +1 -1
  12. package/dist/document-checkpoints.d.ts +1 -0
  13. package/dist/document-checkpoints.d.ts.map +1 -1
  14. package/dist/document-checkpoints.js +27 -0
  15. package/dist/document-checkpoints.js.map +1 -1
  16. package/dist/document-engine.d.ts +4 -0
  17. package/dist/document-engine.d.ts.map +1 -0
  18. package/dist/document-engine.js +9 -0
  19. package/dist/document-engine.js.map +1 -0
  20. package/dist/document-signals.d.ts +31 -1
  21. package/dist/document-signals.d.ts.map +1 -1
  22. package/dist/document-signals.js +59 -0
  23. package/dist/document-signals.js.map +1 -1
  24. package/dist/document-streams.d.ts +8 -1
  25. package/dist/document-streams.d.ts.map +1 -1
  26. package/dist/document-streams.js +79 -8
  27. package/dist/document-streams.js.map +1 -1
  28. package/dist/document-thumbnail-capture.d.ts +20 -0
  29. package/dist/document-thumbnail-capture.d.ts.map +1 -1
  30. package/dist/document-thumbnail-capture.js +247 -9
  31. package/dist/document-thumbnail-capture.js.map +1 -1
  32. package/dist/documents-namespace.d.ts.map +1 -1
  33. package/dist/documents-namespace.js +22 -3
  34. package/dist/documents-namespace.js.map +1 -1
  35. package/dist/documents.d.ts +49 -16
  36. package/dist/documents.d.ts.map +1 -1
  37. package/dist/documents.js +981 -237
  38. package/dist/documents.js.map +1 -1
  39. package/dist/embed-protocol.d.ts +3 -0
  40. package/dist/embed-protocol.d.ts.map +1 -0
  41. package/dist/embed-protocol.js +26 -0
  42. package/dist/embed-protocol.js.map +1 -0
  43. package/dist/emitter.d.ts.map +1 -1
  44. package/dist/emitter.js +8 -1
  45. package/dist/emitter.js.map +1 -1
  46. package/dist/export-guest.d.ts.map +1 -1
  47. package/dist/export-guest.js +14 -6
  48. package/dist/export-guest.js.map +1 -1
  49. package/dist/fetch-deadline.d.ts +22 -0
  50. package/dist/fetch-deadline.d.ts.map +1 -0
  51. package/dist/fetch-deadline.js +96 -0
  52. package/dist/fetch-deadline.js.map +1 -0
  53. package/dist/fetch-retry.d.ts +1 -0
  54. package/dist/fetch-retry.d.ts.map +1 -1
  55. package/dist/fetch-retry.js +12 -5
  56. package/dist/fetch-retry.js.map +1 -1
  57. package/dist/handle-types.d.ts +150 -0
  58. package/dist/handle-types.d.ts.map +1 -0
  59. package/dist/handle-types.js +8 -0
  60. package/dist/handle-types.js.map +1 -0
  61. package/dist/handles.d.ts +48 -48
  62. package/dist/handles.d.ts.map +1 -1
  63. package/dist/handles.js +3122 -501
  64. package/dist/handles.js.map +1 -1
  65. package/dist/hosted-runtime.d.ts.map +1 -1
  66. package/dist/hosted-runtime.js +8 -12
  67. package/dist/hosted-runtime.js.map +1 -1
  68. package/dist/hosted-ui.d.ts +0 -1
  69. package/dist/hosted-ui.d.ts.map +1 -1
  70. package/dist/hosted-ui.js +29 -24
  71. package/dist/hosted-ui.js.map +1 -1
  72. package/dist/importer.d.ts.map +1 -1
  73. package/dist/importer.js +204 -8
  74. package/dist/importer.js.map +1 -1
  75. package/dist/index.d.ts +10 -10
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +11 -2
  78. package/dist/index.js.map +1 -1
  79. package/dist/internal.d.ts +2 -1
  80. package/dist/internal.d.ts.map +1 -1
  81. package/dist/internal.js +1 -0
  82. package/dist/internal.js.map +1 -1
  83. package/dist/join-coverage.d.ts +12 -0
  84. package/dist/join-coverage.d.ts.map +1 -0
  85. package/dist/join-coverage.js +37 -0
  86. package/dist/join-coverage.js.map +1 -0
  87. package/dist/jwt.d.ts.map +1 -1
  88. package/dist/jwt.js +5 -1
  89. package/dist/jwt.js.map +1 -1
  90. package/dist/launch-context.js +2 -2
  91. package/dist/launch-context.js.map +1 -1
  92. package/dist/mutate-batching.d.ts +25 -2
  93. package/dist/mutate-batching.d.ts.map +1 -1
  94. package/dist/mutate-batching.js +57 -10
  95. package/dist/mutate-batching.js.map +1 -1
  96. package/dist/open-document.d.ts +30 -1
  97. package/dist/open-document.d.ts.map +1 -1
  98. package/dist/open-document.js +1905 -322
  99. package/dist/open-document.js.map +1 -1
  100. package/dist/presence.d.ts +28 -0
  101. package/dist/presence.d.ts.map +1 -1
  102. package/dist/presence.js +149 -7
  103. package/dist/presence.js.map +1 -1
  104. package/dist/record-paging.d.ts +335 -71
  105. package/dist/record-paging.d.ts.map +1 -1
  106. package/dist/record-paging.js +590 -127
  107. package/dist/record-paging.js.map +1 -1
  108. package/dist/selection-persistence.d.ts.map +1 -1
  109. package/dist/selection-persistence.js +11 -1
  110. package/dist/selection-persistence.js.map +1 -1
  111. package/dist/serializer.d.ts +30 -1
  112. package/dist/serializer.d.ts.map +1 -1
  113. package/dist/session-persistence.d.ts.map +1 -1
  114. package/dist/session-persistence.js +9 -8
  115. package/dist/session-persistence.js.map +1 -1
  116. package/dist/session-toolbar.d.ts +0 -1
  117. package/dist/session-toolbar.d.ts.map +1 -1
  118. package/dist/session-toolbar.js +356 -83
  119. package/dist/session-toolbar.js.map +1 -1
  120. package/dist/session.d.ts.map +1 -1
  121. package/dist/session.js +87 -18
  122. package/dist/session.js.map +1 -1
  123. package/dist/tail-ops.d.ts +14 -4
  124. package/dist/tail-ops.d.ts.map +1 -1
  125. package/dist/tail-ops.js +75 -15
  126. package/dist/tail-ops.js.map +1 -1
  127. package/dist/topic-lifecycle.d.ts +4 -0
  128. package/dist/topic-lifecycle.d.ts.map +1 -1
  129. package/dist/topic-lifecycle.js +25 -1
  130. package/dist/topic-lifecycle.js.map +1 -1
  131. package/dist/types.d.ts +47 -1
  132. package/dist/types.d.ts.map +1 -1
  133. package/dist/undo.d.ts +2 -3
  134. package/dist/undo.d.ts.map +1 -1
  135. package/dist/undo.js +11 -0
  136. package/dist/undo.js.map +1 -1
  137. package/dist/unload-guard.d.ts +5 -0
  138. package/dist/unload-guard.d.ts.map +1 -0
  139. package/dist/unload-guard.js +88 -0
  140. package/dist/unload-guard.js.map +1 -0
  141. package/dist/version-bar.d.ts +4 -3
  142. package/dist/version-bar.d.ts.map +1 -1
  143. package/dist/version-bar.js +162 -20
  144. package/dist/version-bar.js.map +1 -1
  145. package/dist/version.d.ts +2 -2
  146. package/dist/version.js +1 -1
  147. package/dist/vite.d.ts +18 -2
  148. package/dist/vite.d.ts.map +1 -1
  149. package/dist/vite.js +133 -8
  150. package/dist/vite.js.map +1 -1
  151. package/dist/when-saved.d.ts +2 -0
  152. package/dist/when-saved.d.ts.map +1 -0
  153. package/dist/when-saved.js +70 -0
  154. package/dist/when-saved.js.map +1 -0
  155. package/dist/ws-client.d.ts +27 -3
  156. package/dist/ws-client.d.ts.map +1 -1
  157. package/dist/ws-client.js +408 -89
  158. package/dist/ws-client.js.map +1 -1
  159. package/dist/ws-frame-limit.d.ts.map +1 -1
  160. package/dist/ws-frame-limit.js +23 -17
  161. package/dist/ws-frame-limit.js.map +1 -1
  162. package/docs/app-manifest.md +7 -8
  163. package/docs/custom-chrome.md +21 -22
  164. package/docs/hosted-ui-advanced.md +14 -17
  165. package/docs/imported-documents.md +53 -197
  166. package/docs/presence.md +45 -21
  167. package/docs/schemas.md +12 -17
  168. package/docs/streams.md +55 -57
  169. package/package.json +4 -3
  170. package/src/auth-flow.ts +90 -23
  171. package/src/client.ts +262 -80
  172. package/src/document-checkpoints.ts +30 -0
  173. package/src/document-engine.ts +15 -0
  174. package/src/document-signals.ts +120 -8
  175. package/src/document-streams.ts +95 -9
  176. package/src/document-thumbnail-capture.ts +253 -9
  177. package/src/documents-namespace.ts +38 -13
  178. package/src/documents.ts +1103 -290
  179. package/src/embed-protocol.ts +25 -0
  180. package/src/emitter.ts +8 -1
  181. package/src/export-guest.ts +15 -9
  182. package/src/fetch-deadline.ts +119 -0
  183. package/src/fetch-retry.ts +14 -5
  184. package/src/handle-types.ts +303 -0
  185. package/src/handles.ts +3407 -683
  186. package/src/hosted-runtime.ts +8 -12
  187. package/src/hosted-ui.ts +28 -32
  188. package/src/importer.ts +216 -8
  189. package/src/index.ts +29 -18
  190. package/src/internal.ts +7 -0
  191. package/src/join-coverage.ts +42 -0
  192. package/src/jwt.ts +8 -1
  193. package/src/launch-context.ts +2 -2
  194. package/src/mutate-batching.ts +60 -11
  195. package/src/open-document.ts +2441 -502
  196. package/src/presence.ts +210 -11
  197. package/src/record-paging.ts +686 -137
  198. package/src/selection-persistence.ts +10 -1
  199. package/src/serializer.ts +33 -1
  200. package/src/session-persistence.ts +9 -8
  201. package/src/session-toolbar.ts +387 -89
  202. package/src/session.ts +91 -17
  203. package/src/tail-ops.ts +76 -10
  204. package/src/topic-lifecycle.ts +31 -1
  205. package/src/types.ts +56 -4
  206. package/src/undo.ts +13 -5
  207. package/src/unload-guard.ts +91 -0
  208. package/src/version-bar.ts +183 -26
  209. package/src/version.ts +1 -1
  210. package/src/vite.ts +175 -10
  211. package/src/when-saved.ts +66 -0
  212. package/src/ws-client.ts +461 -92
  213. package/src/ws-frame-limit.ts +23 -17
package/AGENTS.md CHANGED
@@ -1,37 +1,36 @@
1
1
  # Instructions for AI coding agents
2
2
 
3
3
  You are likely an AI coding agent building an app on
4
- `@brass-build/client`. The [README](./README.md) teaches the API;
5
- this file is addressed to you specifically and carries two things the
6
- README does not: how to verify your work, and the UX baseline a
7
- generated app is expected to meet. Apply both.
4
+ `@brass-build/client`. The [README](./README.md) teaches the API. This
5
+ page covers how to verify your work and the UX baseline a generated app
6
+ should meet. Apply both.
8
7
 
9
8
  ## Find out where the app's data comes from
10
9
 
11
10
  **The request names a format, a corpus, or an export.** Our IFC models,
12
11
  the GPX tracks off my watch, the change orders our subs email us. Ask
13
12
  for one real example as your first action, before you design anything.
14
- That file decides your record shape, and an app built ahead of it
13
+ You take your record shape from that file, and an app built ahead of it
15
14
  guesses at a shape the real files contradict.
16
15
 
17
16
  **The work has a customary shape in the user's trade.** A punch list, a
18
17
  timesheet, an RFI, a site diary: people in that line of work pass these
19
18
  around as documents whose fields they all expect, so a shape for it
20
19
  exists and the user probably holds a copy. Ask for theirs before you
21
- design a schema. A schema you invent for a shape that already exists
22
- forks it: the picker routes documents to apps by their declared schema,
23
- so each app's documents stop opening in the other, and the split
24
- stands. Ask the user rather than looking, because a shape reaches you
25
- through a document that already carries it.
20
+ design a schema. Inventing a schema for a shape that already exists
21
+ forks that shape: the picker routes documents to apps by their declared
22
+ schema, so each app's documents stop opening in the other, and the split
23
+ stands. Ask the user rather than searching for the shape yourself: it
24
+ reaches you as a document they already hold.
26
25
 
27
26
  Ask as well whenever the user brings up another application. What that
28
27
  product can hand over decides whether you adopt a shape or integrate,
29
28
  and only the user knows.
30
29
 
31
- A product you assumed rather than one they named is the other way
32
- round. Data a product holds reaches you through its API rather than as
33
- a document, so a sprint board, a sales pipeline, or a ticket queue you
34
- guess lives in some tool leaves you no shape to adopt. Build it as
30
+ A product you assumed, rather than one the user named, works the other
31
+ way round. Data a product holds reaches you through its API rather than
32
+ as a document, so a sprint board, a sales pipeline, or a ticket queue
33
+ you guess lives in some tool leaves you no shape to adopt. Build it as
35
34
  below until the user names the tool.
36
35
 
37
36
  **The data starts in your app.** A canvas, a poll, a garden planner, a
@@ -42,10 +41,9 @@ Design the records from the request, declare their schema in
42
41
  has none behind it, and it reads to the user as though their request
43
42
  was incomplete.
44
43
 
45
- Either question runs alongside the work that does not depend on the
46
- answer, rather than idling: drive the sign-in, fetch the organization's
47
- instructions (next section), and scaffold the app shell while the user
48
- answers.
44
+ Ask either question and keep working on what does not depend on the
45
+ answer: drive the sign-in, fetch the organization's instructions (next
46
+ section), and scaffold the app shell while the user answers.
49
47
 
50
48
  With an example in hand: the user adds it at their Brass dashboard
51
49
  (`https://app.brass.build`) and sends you back the document's link,
@@ -79,18 +77,17 @@ npx @brass-build/cli agents pull --stdout
79
77
 
80
78
  It prints the organization's own conventions for apps built on Brass
81
79
  (its `AGENTS.md` / `CLAUDE.md`) to stdout. Read them inline and treat
82
- them as an instruction file for this task; they take precedence over
83
- the generic defaults here. Run it rather than deciding up front whether
84
- the org has any: if it has set none the command prints nothing and you
85
- proceed with the defaults below, but that is something you learn by
86
- running it, not by assuming it, so this is a step you take, not a box
87
- you skip. The pull needs a signed-in session, so if `brass agents pull`
88
- reports no credential, run the sign-in yourself first (`brass login
89
- --start`, relay the printed URL and code to the human, then `brass
90
- login --check` until it reports approved), and it is the same
91
- credential you will use to publish (see "Ship the app"), so drive that
92
- sign-in now rather than skipping the fetch. `brass agents pull` is detailed in the
93
- `@brass-build/cli` package's `AGENTS.md`.
80
+ them as an instruction file for this task. They take precedence over
81
+ the generic defaults here. Run the command rather than deciding up
82
+ front whether the org has any: an org that set none prints nothing, and
83
+ you proceed with the defaults below.
84
+
85
+ The pull needs a signed-in session, so when `brass agents pull` reports
86
+ no credential, run the sign-in yourself first (`brass login --start`,
87
+ relay the printed URL and code to the human, then `brass login --check`
88
+ until it reports approved). That is the same credential you will use to
89
+ publish (see "Ship the app"), so drive the sign-in now. `brass agents
90
+ pull` is detailed in the `@brass-build/cli` package's `AGENTS.md`.
94
91
 
95
92
  ## Have a second agent review your work
96
93
 
@@ -98,33 +95,35 @@ Brass documents are persistent and shared: a write that compiles and
98
95
  looks right on screen can still be subtly wrong in the stored
99
96
  document, and once saved it syncs to every collaborator and reopens
100
97
  later. You share the blind spot that produced any such bug, so
101
- reviewing your own work tends to reconfirm it. Build with a review
102
- loop instead of treating one as optional polish.
98
+ reviewing your own work reconfirms it. Build with a review loop.
103
99
 
104
100
  Once the integration builds, have a **separate** agent (not you)
105
101
  adversarially review it and drive it in a real browser against the
106
102
  live backend. Hold that reviewer to implementation-independent checks:
107
103
 
108
- - It should read the document back through the handles
109
- (`doc.map(...).read()` / `doc.list(...).read()`) and validate the
110
- result against the data spec itself, rather than trusting the app's
111
- own helpers.
104
+ - It should take the document's own records from a subscriber of its
105
+ own (`doc.map(...).subscribe(...)` / `doc.list(...).subscribe(...)`)
106
+ and validate them against the data spec itself, rather than trusting
107
+ the app's own helpers.
112
108
  - It should push the negative, zero, and boundary inputs of every
113
109
  operation.
114
110
  - It should confirm each edit round-trips and leaves the document
115
111
  consistent.
116
-
117
- Driving it on a touch device, not just a desktop mouse, tends to be
118
- where the surprising bugs are: interactive gestures (drags,
119
- multi-point taps, anything pointer-driven) commonly work under a mouse
120
- but break under touch, and layout looking fine at a narrow width says
121
- nothing about whether the gesture still functions there. Worth a pass
122
- on real touch when the app has any.
123
-
124
- Fix what the reviewer finds and iterate until the reviewer's own
125
- checks pass. A single build pass routinely ships writes that are wrong
126
- but plausible; this loop is what catches them, and it is worth a round
127
- before you call an integration done.
112
+ - It should open the same document as **two** signed-in readers and put
113
+ them in the same field at once: with one reader's caret sitting in a
114
+ field, the other edits it. The first reader's screen has to show that
115
+ value once their caret leaves, and a reader whose own edit goes over a
116
+ peer's has to be told so. Both halves fail silently, and a single
117
+ session never reaches either.
118
+
119
+ Have the reviewer drive any gesture on a touch device as well as a
120
+ desktop mouse. Drags, multi-point taps, and anything else
121
+ pointer-driven commonly work under a mouse and break under touch, and a
122
+ layout that looks right at a narrow width says nothing about whether
123
+ the gesture still works there.
124
+
125
+ Fix what the reviewer finds and iterate until the reviewer's own checks
126
+ pass, before you call the integration done.
128
127
 
129
128
  ## Apply these UX defaults
130
129
 
@@ -154,11 +153,24 @@ the baseline for when there is no design.
154
153
  where it bites.
155
154
  - **Make touch targets at least 40px**: `padding: 12px 16px` on
156
155
  tappable controls.
156
+ - **Draw the `<select>` arrow yourself.** A native `<select>` puts the
157
+ browser's own arrow tight against the control's right edge, at a gap
158
+ that differs per browser, so a select carrying your border and radius
159
+ still reads as a stock control beside your other inputs. Set
160
+ `appearance: none`, draw the chevron as a `background-image` at
161
+ `background-position: right 0.75rem center`, and reserve
162
+ `padding-right: 2.25rem` so a long option cannot run under it.
157
163
  - **Disable, don't hide, on read-only documents.** When
158
- `doc.readOnly` is true (a viewer-role grant, or a historical-version
159
- preview), render the editor and share controls disabled with a
160
- `title` explaining why, rather than removing them. `doc.readOnly` is
161
- the public signal; gate on it, not on a role field.
164
+ `doc.readOnly` is true, render the editor and share controls disabled
165
+ with a `title` explaining why, rather than removing them. It covers
166
+ every reason this reader cannot write: a view-only grant, a view-only
167
+ share link, a historical-version preview, and a document still
168
+ loading. Re-read it in your `doc.subscribe` handler rather than
169
+ reading it once at bind: it also flips when someone withdraws the
170
+ reader's access while the document is open, which is what closes the
171
+ editor at the moment writing stops working. Pair it with
172
+ `Brass.onError`, where the sentence explaining a refused write
173
+ arrives.
162
174
  - **Name the next action in empty states.** "No document open. Use
163
175
  Open to pick or create one."
164
176
  - **Gate edit affordances on an open document.** When
@@ -168,9 +180,10 @@ the baseline for when there is no design.
168
180
  user produce work that silently cannot be persisted and is discarded
169
181
  when the first document opens.
170
182
  - **Render the `error` status.** When `session.status` is `error` (a
171
- document the user opened could not be opened: deleted, or an access
172
- the platform collapses to a non-disclosing 404; or a document that was
173
- open being deleted out from under the user), render
183
+ document the user opened could not be opened: deleted, an access the
184
+ platform collapses to a non-disclosing 404, or a file the platform
185
+ could not prepare; or a document that was open being deleted out from
186
+ under the user), render
174
187
  `session.error.message` verbatim, surrounded by your own New / Open
175
188
  actions, not the plain empty state. Falling through to "No document
176
189
  open" reads as if the user asked for nothing and hides the failure.
@@ -210,13 +223,10 @@ npx @brass-build/cli status
210
223
 
211
224
  It inspects the credential and app state for this environment and ends
212
225
  in a `Next:` line naming the one command to run. Follow that line, then
213
- run `brass status` again. It is the control surface for the rest of this
214
- section: rather than reasoning about publishing from prose, you run it
215
- and do what it says.
226
+ run `brass status` again, for the rest of this section.
216
227
 
217
228
  Two steps need the human's Brass account, and you cannot stand in for
218
- it. Both are steps you drive to completion, not blockers you note and
219
- stop at:
229
+ it. Drive both to completion rather than noting them and stopping:
220
230
 
221
231
  - **Getting an app id.** `Brass.init` needs the `app_id` of a
222
232
  registered app. Run the first `brass publish` (below): it creates the
@@ -227,11 +237,10 @@ stop at:
227
237
  on the dashboard and gives you the id.)
228
238
  - **A publish credential.** When `brass status` reports no credential,
229
239
  drive the sign-in: run `brass login`. In a local session it opens the
230
- human's browser to approve and completes on approval; on a headless
240
+ human's browser to approve and completes on approval. On a headless
231
241
  box it prints a URL and short code the human approves from any device.
232
242
  Running it and getting the human through approval is your step. For CI
233
- the human supplies `BRASS_SERVICE_TOKEN` instead. You cannot mint
234
- either, but neither is a wall you stop at.
243
+ the human supplies `BRASS_SERVICE_TOKEN` instead.
235
244
 
236
245
  Once `brass status` reports a credential, publish:
237
246
 
package/CHANGELOG.md CHANGED
@@ -6,6 +6,103 @@ and the package follows [semantic versioning](https://semver.org/spec/v2.0.0.htm
6
6
  While the version is below `1.0.0`, minor releases may introduce
7
7
  breaking changes.
8
8
 
9
+ ## 0.3.0
10
+
11
+ ### Added
12
+
13
+ - Imported documents generate a thumbnail on first open. Apps already calling
14
+ `useCanvas` need no change.
15
+ - Documents can be published into a connected project's drive, creating the
16
+ file there. Apps already mounting `Brass.mountDriveSync` get the action.
17
+ - `doc.driveStatus.sourceUnreadable` carries the drive's own words when it
18
+ received a file it cannot read.
19
+
20
+ ## 0.2.0
21
+
22
+ ### Added
23
+
24
+ - Large collections page themselves. A map an app fills past 5,000 records
25
+ stores them in compressed pages instead of one container per record, which
26
+ keeps a bulk write and a reload viable at hundreds of thousands of records.
27
+ `get()`, `has()` and `mutate()` read and edit a paged map exactly as they do
28
+ a small one.
29
+ - A map keeps its own bookkeeping beside your records, under keys beginning
30
+ with `brass:`, so a write under one of those keys is refused. Any other key
31
+ is yours.
32
+ - `doc.atCapacity` (and `stream.atCapacity`) reports that the platform has
33
+ refused a write because the document reached its ceiling. A document gets
34
+ there through a band where ordinary editing still lands and a bulk write is
35
+ refused, so this is the signal to stop offering the bulk work (an import, a
36
+ generate pass) while leaving the rest of the app alone. Edits keep applying
37
+ to the local copy while it reads `true`, so an app can show the reader what
38
+ they did alongside the fact that it is not saved. `doc.subscribe` fires when
39
+ it flips, once rather than once per refused change.
40
+ - `onSettled(cb)` on the map, list and ordered-map handles fires when a
41
+ delivery of the whole collection finishes, so the work that follows on the
42
+ collection (framing the camera on what arrived, a first pass over it) has
43
+ somewhere to live other than a single change.
44
+ - `orderedMap.insert(key, value, at)` and `orderedMap.move(key, to)` place and
45
+ reposition an entry against a neighbour, through the new `OrderedMapPosition`
46
+ type.
47
+ - A list draft carries `move(from, to)`. Two people repositioning the same
48
+ entry with it converge on one entry, where a removal paired with an
49
+ insertion keeps both insertions and leaves the entry in the list twice.
50
+ - Presence layers carry a 2,048-byte cap (`PRESENCE_LAYER_MAX_BYTES`). Watch
51
+ `presence.onStatus` to know when Brass is withholding a layer that is over
52
+ it; a `selection` crosses the cap at roughly 130 to 140 typical entity ids,
53
+ so publish a bounded summary when a selection can get large.
54
+ - The browser's own "leave site?" prompt appears while a document holds edits
55
+ the server has not acknowledged, so a reload cannot silently drop them.
56
+
57
+ ### Changed
58
+
59
+ - `session.status` reaches `error` for a file Brass could not prepare, on top
60
+ of a deleted or inaccessible document. One error screen still covers them
61
+ all.
62
+ - `doc.readOnly` now also reads `true` for a document that could not be
63
+ loaded, so edit affordances gated on it stay disabled there as well as
64
+ during loading.
65
+ - A store pages at 5,000 records rather than 50,000, where paging starts
66
+ paying for itself: it writes about 3x faster, opens about 5x faster and
67
+ stores about 1.7x smaller. What it costs is merge granularity. Below the
68
+ threshold two people editing different fields of one record both keep their
69
+ edit; above it the record is the merge unit, so one of the two wins.
70
+ - Two tabs of one app are two claimants for an import, so only one of them
71
+ parses the file.
72
+ - A list subscriber receives the splices its change made, applied in the order
73
+ they arrive, so a render costs what the change carried rather than what the
74
+ list holds.
75
+ - `SyntheticSubscribeEvent` names a move through version history `history`
76
+ rather than `checkout`. `ChangeSource` no longer lists it: a version move
77
+ swaps the whole container instead of writing into it, so it reaches a handle
78
+ subscriber as a whole-container fire with no origin, and `doc.subscribe` is
79
+ where that move is reported.
80
+ - A `Document` is reached through `Brass.session`, `Brass.openDocumentById`, or
81
+ `Brass.createDocument`. The class no longer constructs.
82
+ - The capability manifest names the files an app imports under `imports`,
83
+ which is the word the platform and the dashboard use for them.
84
+
85
+ ### Removed
86
+
87
+ - `read()` on the map, list and ordered-map handles. A handle feeds an app
88
+ through `subscribe`, which costs what a change carried rather than what the
89
+ document holds, so a collection large enough to page never pays an
90
+ O(document) walk to render. A registered serializer runs detached with no
91
+ subscription behind it, and is handed a `StoredMap` / `StoredList` that
92
+ keeps `read()`.
93
+ - The barrel no longer exports plumbing types that no `Brass`, `Document`, or
94
+ `DocumentSession` member names: `AcquireOrganizationResult`,
95
+ `HostedUiLauncher`, `HostedUiLauncherOptions`, `DocumentSessionDeps`,
96
+ `StreamRuntime`, `CreateDocumentFn`, `DocumentFactory`,
97
+ `DocumentAtVersionResponse`, `DocumentSnapshotResponse`,
98
+ `DocumentDriveSummaryWire`, `OpenDocumentFactoryOptions`,
99
+ `OpenDocumentByIdFn`, and `OpenDocumentWsClient`. What autocomplete offers
100
+ is now what an app can call.
101
+ - `doc.pushToDrive` and `doc.resolveDrive` are internal. Publishing a document
102
+ back to its drive source, and resolving a diverged one, are the hosted
103
+ toolbar's controls. `doc.driveStatus` still reports the sync state, so an
104
+ app can tell the reader they have unpublished edits.
105
+
9
106
  ## 0.1.0
10
107
 
11
108
  Initial public release. The SDK turns a document into a live,