@brass-build/client 0.4.0 → 0.5.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.
- package/AGENTS.md +142 -41
- package/CHANGELOG.md +93 -4
- package/README.md +666 -179
- package/dist/acting-organization.d.ts.map +1 -1
- package/dist/acting-organization.js +6 -11
- package/dist/acting-organization.js.map +1 -1
- package/dist/api-fetch.d.ts +8 -0
- package/dist/api-fetch.d.ts.map +1 -1
- package/dist/api-fetch.js +35 -8
- package/dist/api-fetch.js.map +1 -1
- package/dist/batched-get.d.ts +11 -0
- package/dist/batched-get.d.ts.map +1 -0
- package/dist/batched-get.js +92 -0
- package/dist/batched-get.js.map +1 -0
- package/dist/call-service.d.ts +4 -0
- package/dist/call-service.d.ts.map +1 -1
- package/dist/call-service.js +66 -3
- package/dist/call-service.js.map +1 -1
- package/dist/client.d.ts +51 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +982 -124
- package/dist/client.js.map +1 -1
- package/dist/document-checkpoints.d.ts +25 -2
- package/dist/document-checkpoints.d.ts.map +1 -1
- package/dist/document-checkpoints.js +258 -61
- package/dist/document-checkpoints.js.map +1 -1
- package/dist/document-commands.d.ts.map +1 -1
- package/dist/document-commands.js +5 -3
- package/dist/document-commands.js.map +1 -1
- package/dist/document-contents.d.ts +10 -0
- package/dist/document-contents.d.ts.map +1 -1
- package/dist/document-contents.js +23 -1
- package/dist/document-contents.js.map +1 -1
- package/dist/document-set-subscription.d.ts +56 -0
- package/dist/document-set-subscription.d.ts.map +1 -0
- package/dist/document-set-subscription.js +240 -0
- package/dist/document-set-subscription.js.map +1 -0
- package/dist/document-signals.d.ts +6 -0
- package/dist/document-signals.d.ts.map +1 -1
- package/dist/document-signals.js +31 -1
- package/dist/document-signals.js.map +1 -1
- package/dist/document-streams.d.ts +36 -2
- package/dist/document-streams.d.ts.map +1 -1
- package/dist/document-streams.js +69 -18
- package/dist/document-streams.js.map +1 -1
- package/dist/document-thumbnail-capture.d.ts +12 -2
- package/dist/document-thumbnail-capture.d.ts.map +1 -1
- package/dist/document-thumbnail-capture.js +103 -27
- package/dist/document-thumbnail-capture.js.map +1 -1
- package/dist/documents-namespace.d.ts +92 -10
- package/dist/documents-namespace.d.ts.map +1 -1
- package/dist/documents-namespace.js +285 -39
- package/dist/documents-namespace.js.map +1 -1
- package/dist/documents.d.ts +173 -8
- package/dist/documents.d.ts.map +1 -1
- package/dist/documents.js +1778 -310
- package/dist/documents.js.map +1 -1
- package/dist/errors.d.ts +3 -3
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js.map +1 -1
- package/dist/fetch-retry.d.ts +1 -0
- package/dist/fetch-retry.d.ts.map +1 -1
- package/dist/fetch-retry.js +2 -0
- package/dist/fetch-retry.js.map +1 -1
- package/dist/file-choice.d.ts +2 -0
- package/dist/file-choice.d.ts.map +1 -0
- package/dist/file-choice.js +27 -0
- package/dist/file-choice.js.map +1 -0
- package/dist/guest-link.d.ts +18 -1
- package/dist/guest-link.d.ts.map +1 -1
- package/dist/guest-link.js +329 -1
- package/dist/guest-link.js.map +1 -1
- package/dist/handle-types.d.ts +67 -5
- package/dist/handle-types.d.ts.map +1 -1
- package/dist/handles.d.ts +15 -19
- package/dist/handles.d.ts.map +1 -1
- package/dist/handles.js +231 -76
- package/dist/handles.js.map +1 -1
- package/dist/hosted-ui.d.ts +61 -0
- package/dist/hosted-ui.d.ts.map +1 -1
- package/dist/hosted-ui.js +94 -42
- package/dist/hosted-ui.js.map +1 -1
- package/dist/import-contract.d.ts +74 -0
- package/dist/import-contract.d.ts.map +1 -0
- package/dist/import-contract.js +163 -0
- package/dist/import-contract.js.map +1 -0
- package/dist/import-guest.d.ts +9 -0
- package/dist/import-guest.d.ts.map +1 -0
- package/dist/import-guest.js +155 -0
- package/dist/import-guest.js.map +1 -0
- package/dist/import-report.d.ts +17 -0
- package/dist/import-report.d.ts.map +1 -0
- package/dist/import-report.js +7 -0
- package/dist/import-report.js.map +1 -0
- package/dist/importer-wait.d.ts +2 -0
- package/dist/importer-wait.d.ts.map +1 -0
- package/dist/importer-wait.js +5 -0
- package/dist/importer-wait.js.map +1 -0
- package/dist/importer.d.ts +18 -4
- package/dist/importer.d.ts.map +1 -1
- package/dist/importer.js +322 -105
- package/dist/importer.js.map +1 -1
- package/dist/in-development.d.ts +2 -0
- package/dist/in-development.d.ts.map +1 -0
- package/dist/in-development.js +10 -0
- package/dist/in-development.js.map +1 -0
- package/dist/index.d.ts +16 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -8
- package/dist/index.js.map +1 -1
- package/dist/init-keys.d.ts +1 -1
- package/dist/init-keys.d.ts.map +1 -1
- package/dist/init-keys.js +6 -1
- package/dist/init-keys.js.map +1 -1
- package/dist/input-documents.d.ts +67 -0
- package/dist/input-documents.d.ts.map +1 -0
- package/dist/input-documents.js +693 -0
- package/dist/input-documents.js.map +1 -0
- package/dist/inputs-status.d.ts +80 -0
- package/dist/inputs-status.d.ts.map +1 -0
- package/dist/inputs-status.js +460 -0
- package/dist/inputs-status.js.map +1 -0
- package/dist/inputs.d.ts +2 -0
- package/dist/inputs.d.ts.map +1 -0
- package/dist/inputs.js +793 -0
- package/dist/inputs.js.map +1 -0
- package/dist/internal.d.ts +6 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +4 -1
- package/dist/internal.js.map +1 -1
- package/dist/join-content.d.ts +33 -0
- package/dist/join-content.d.ts.map +1 -0
- package/dist/join-content.js +114 -0
- package/dist/join-content.js.map +1 -0
- package/dist/join-stamp.d.ts +3 -0
- package/dist/join-stamp.d.ts.map +1 -0
- package/dist/join-stamp.js +12 -0
- package/dist/join-stamp.js.map +1 -0
- package/dist/manifest-drift.d.ts +9 -0
- package/dist/manifest-drift.d.ts.map +1 -0
- package/dist/manifest-drift.js +46 -0
- package/dist/manifest-drift.js.map +1 -0
- package/dist/open-document.d.ts +109 -2
- package/dist/open-document.d.ts.map +1 -1
- package/dist/open-document.js +1822 -811
- package/dist/open-document.js.map +1 -1
- package/dist/page-restore.d.ts +12 -0
- package/dist/page-restore.d.ts.map +1 -0
- package/dist/page-restore.js +23 -0
- package/dist/page-restore.js.map +1 -0
- package/dist/placement.d.ts +13 -0
- package/dist/placement.d.ts.map +1 -0
- package/dist/placement.js +34 -0
- package/dist/placement.js.map +1 -0
- package/dist/presence.d.ts +1 -0
- package/dist/presence.d.ts.map +1 -1
- package/dist/presence.js +16 -4
- package/dist/presence.js.map +1 -1
- package/dist/read-only-document.d.ts +87 -0
- package/dist/read-only-document.d.ts.map +1 -0
- package/dist/read-only-document.js +4 -0
- package/dist/read-only-document.js.map +1 -0
- package/dist/record-paging.d.ts +63 -6
- package/dist/record-paging.d.ts.map +1 -1
- package/dist/record-paging.js +122 -29
- package/dist/record-paging.js.map +1 -1
- package/dist/references.d.ts +40 -0
- package/dist/references.d.ts.map +1 -0
- package/dist/references.js +135 -0
- package/dist/references.js.map +1 -0
- package/dist/request-lane.d.ts +5 -0
- package/dist/request-lane.d.ts.map +1 -0
- package/dist/request-lane.js +48 -0
- package/dist/request-lane.js.map +1 -0
- package/dist/sealed-arrow.d.ts +170 -0
- package/dist/sealed-arrow.d.ts.map +1 -0
- package/dist/sealed-arrow.js +769 -0
- package/dist/sealed-arrow.js.map +1 -0
- package/dist/sealed-cache.d.ts +21 -0
- package/dist/sealed-cache.d.ts.map +1 -0
- package/dist/sealed-cache.js +247 -0
- package/dist/sealed-cache.js.map +1 -0
- package/dist/sealed-encode.d.ts +87 -0
- package/dist/sealed-encode.d.ts.map +1 -0
- package/dist/sealed-encode.js +876 -0
- package/dist/sealed-encode.js.map +1 -0
- package/dist/sealed-handles.d.ts +45 -0
- package/dist/sealed-handles.d.ts.map +1 -0
- package/dist/sealed-handles.js +408 -0
- package/dist/sealed-handles.js.map +1 -0
- package/dist/sealed-store.d.ts +242 -0
- package/dist/sealed-store.d.ts.map +1 -0
- package/dist/sealed-store.js +1155 -0
- package/dist/sealed-store.js.map +1 -0
- package/dist/serializer.d.ts +6 -15
- package/dist/serializer.d.ts.map +1 -1
- package/dist/session-toolbar.d.ts +20 -5
- package/dist/session-toolbar.d.ts.map +1 -1
- package/dist/session-toolbar.js +419 -67
- package/dist/session-toolbar.js.map +1 -1
- package/dist/session.d.ts +38 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +562 -79
- package/dist/session.js.map +1 -1
- package/dist/sha256.d.ts +3 -0
- package/dist/sha256.d.ts.map +1 -0
- package/dist/sha256.js +74 -0
- package/dist/sha256.js.map +1 -0
- package/dist/stable-handles.d.ts +19 -0
- package/dist/stable-handles.d.ts.map +1 -0
- package/dist/stable-handles.js +193 -0
- package/dist/stable-handles.js.map +1 -0
- package/dist/staged-seal.d.ts +2 -0
- package/dist/staged-seal.d.ts.map +1 -0
- package/dist/staged-seal.js +76 -0
- package/dist/staged-seal.js.map +1 -0
- package/dist/subscriber-delivery.d.ts +8 -0
- package/dist/subscriber-delivery.d.ts.map +1 -0
- package/dist/subscriber-delivery.js +31 -0
- package/dist/subscriber-delivery.js.map +1 -0
- package/dist/tail-op-import.d.ts +10 -0
- package/dist/tail-op-import.d.ts.map +1 -0
- package/dist/tail-op-import.js +33 -0
- package/dist/tail-op-import.js.map +1 -0
- package/dist/topic-lifecycle.d.ts +6 -0
- package/dist/topic-lifecycle.d.ts.map +1 -1
- package/dist/topic-lifecycle.js +32 -2
- package/dist/topic-lifecycle.js.map +1 -1
- package/dist/trial-replace-dialog.d.ts +10 -0
- package/dist/trial-replace-dialog.d.ts.map +1 -0
- package/dist/trial-replace-dialog.js +153 -0
- package/dist/trial-replace-dialog.js.map +1 -0
- package/dist/types.d.ts +8 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/undo.d.ts +60 -5
- package/dist/undo.d.ts.map +1 -1
- package/dist/undo.js +344 -75
- package/dist/undo.js.map +1 -1
- package/dist/version-bar.d.ts +1 -0
- package/dist/version-bar.d.ts.map +1 -1
- package/dist/version-bar.js +2 -1
- package/dist/version-bar.js.map +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.js +1 -1
- package/dist/ws-client.d.ts +21 -0
- package/dist/ws-client.d.ts.map +1 -1
- package/dist/ws-client.js +192 -8
- package/dist/ws-client.js.map +1 -1
- package/dist/ws-frame-limit.d.ts +1 -0
- package/dist/ws-frame-limit.d.ts.map +1 -1
- package/dist/ws-frame-limit.js.map +1 -1
- package/docs/app-manifest.md +121 -17
- package/docs/custom-chrome.md +24 -16
- package/docs/hosted-ui-advanced.md +38 -6
- package/docs/imported-documents.md +104 -51
- package/docs/presence.md +16 -4
- package/docs/schemas.md +8 -5
- package/docs/services.md +6 -6
- package/docs/streams.md +139 -150
- package/docs/text-fields.md +26 -19
- package/package.json +5 -2
- package/src/acting-organization.ts +6 -12
- package/src/api-fetch.ts +53 -3
- package/src/auth-flow.ts +1 -1
- package/src/batched-get.ts +117 -0
- package/src/call-service.ts +78 -3
- package/src/client.ts +1092 -121
- package/src/document-checkpoints.ts +307 -65
- package/src/document-commands.ts +5 -5
- package/src/document-contents.ts +27 -1
- package/src/document-set-subscription.ts +274 -0
- package/src/document-signals.ts +47 -5
- package/src/document-streams.ts +85 -16
- package/src/document-thumbnail-capture.ts +118 -32
- package/src/documents-namespace.ts +539 -84
- package/src/documents.ts +2017 -348
- package/src/errors.ts +17 -6
- package/src/fetch-retry.ts +5 -0
- package/src/file-choice.ts +26 -0
- package/src/guest-link.ts +362 -1
- package/src/handle-types.ts +73 -47
- package/src/handles.ts +253 -83
- package/src/hosted-ui.ts +211 -50
- package/src/import-contract.ts +227 -0
- package/src/import-guest.ts +193 -0
- package/src/import-report.ts +24 -0
- package/src/importer-wait.ts +4 -0
- package/src/importer.ts +386 -131
- package/src/in-development.ts +10 -0
- package/src/index.ts +76 -16
- package/src/init-keys.ts +6 -1
- package/src/input-documents.ts +819 -0
- package/src/inputs-status.ts +598 -0
- package/src/inputs.ts +1090 -0
- package/src/internal.ts +7 -0
- package/src/join-content.ts +135 -0
- package/src/join-stamp.ts +12 -0
- package/src/manifest-drift.ts +54 -0
- package/src/open-document.ts +2346 -930
- package/src/page-restore.ts +38 -0
- package/src/placement.ts +54 -0
- package/src/presence.ts +21 -5
- package/src/read-only-document.ts +133 -0
- package/src/record-paging.ts +147 -25
- package/src/references.ts +207 -0
- package/src/request-lane.ts +59 -0
- package/src/sealed-arrow.ts +843 -0
- package/src/sealed-cache.ts +267 -0
- package/src/sealed-encode.ts +1015 -0
- package/src/sealed-handles.ts +486 -0
- package/src/sealed-store.ts +1330 -0
- package/src/serializer.ts +8 -20
- package/src/session-toolbar.ts +463 -72
- package/src/session.ts +740 -99
- package/src/sha256.ts +65 -0
- package/src/stable-handles.ts +235 -0
- package/src/staged-seal.ts +90 -0
- package/src/subscriber-delivery.ts +37 -0
- package/src/tail-op-import.ts +30 -0
- package/src/topic-lifecycle.ts +38 -2
- package/src/trial-replace-dialog.ts +163 -0
- package/src/types.ts +6 -6
- package/src/undo.ts +386 -73
- package/src/version-bar.ts +5 -1
- package/src/version.ts +1 -1
- package/src/ws-client.ts +208 -16
- package/src/ws-frame-limit.ts +1 -0
package/docs/schemas.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Schemas
|
|
2
2
|
|
|
3
|
-
A schema declares what your app expects a document to
|
|
3
|
+
A schema declares what your app expects a document to contain. Declaring one
|
|
4
4
|
in your `brass-app.json` routes matching documents to your app, and lets
|
|
5
5
|
any developer who can open one of those documents build against the same
|
|
6
6
|
shape.
|
|
@@ -21,13 +21,16 @@ stream's root collections and records.
|
|
|
21
21
|
so a namespaced `acme.roster` works and a URL does not. Brass does not
|
|
22
22
|
record a declaration whose family is missing or not a word, and reports
|
|
23
23
|
that on your app's page in the dashboard.
|
|
24
|
-
- **`streams`** maps each stream name your app expects a document to
|
|
24
|
+
- **`streams`** maps each stream name your app expects a document to have
|
|
25
25
|
to that stream's contract. `default` is the stream a document opens
|
|
26
26
|
without asking for one, and takes an entry like any other name. Two
|
|
27
27
|
streams that carry the same shape carry the same body.
|
|
28
28
|
- **A collection of records** is an object whose `additionalProperties`
|
|
29
29
|
is the record schema, so the keys are record ids. An ordered collection
|
|
30
30
|
is an array, or an array of keys alongside the map.
|
|
31
|
+
- **A text root** (`doc.text(key)`) reads as a string wherever the
|
|
32
|
+
document is read as data, `brass contents` included, so describe it as a
|
|
33
|
+
string property of the stream: `{ "type": "string" }`.
|
|
31
34
|
- **Foreign keys** ride as an `x-brass-ref` extension on the referring
|
|
32
35
|
field, an object whose `to` names the collection it points at.
|
|
33
36
|
Standard validators ignore an `x-` keyword.
|
|
@@ -54,8 +57,8 @@ and an approximation reads as a different contract.
|
|
|
54
57
|
Streams let one document load in parts, so an app pays for what it opens
|
|
55
58
|
rather than the document's total size. `doc.getOrCreateStream(name)`
|
|
56
59
|
opens the document's stream of that name and makes it when the document
|
|
57
|
-
does not
|
|
58
|
-
|
|
60
|
+
does not have it yet. `doc.stream(name)` opens one the document already
|
|
61
|
+
has and rejects any other name.
|
|
59
62
|
|
|
60
63
|
```ts
|
|
61
64
|
const stream = await doc.getOrCreateStream(name);
|
|
@@ -67,7 +70,7 @@ stream.map('records').mutate((m) => {
|
|
|
67
70
|
`getOrCreateStream` is idempotent, and concurrent callers converge on one
|
|
68
71
|
stream, so an app reaches for the stream it expects on every open without
|
|
69
72
|
checking first. Only that call writes, so a read-only open reads through
|
|
70
|
-
`doc.stream` and handles the rejection when the document does not
|
|
73
|
+
`doc.stream` and handles the rejection when the document does not have
|
|
71
74
|
the stream. Names follow the same rule a family does: `a-z 0-9 . _ -`, 1 to
|
|
72
75
|
64 characters, starting with a letter or digit.
|
|
73
76
|
|
package/docs/services.md
CHANGED
|
@@ -43,7 +43,7 @@ the host needs, and tells anyone who cannot answer who to ask.
|
|
|
43
43
|
|
|
44
44
|
| `code` | What it means |
|
|
45
45
|
| --------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
46
|
-
| `credential_required` | No credential is connected for this host, or the one connected is not approved for your app, no longer covers what your app sends, or carries no value under a name your app reads back. One code, because the dialog is the answer to all of them. The body's sentence says which, for a
|
|
46
|
+
| `credential_required` | No credential is connected for this host, or the one connected is not approved for your app, no longer covers what your app sends, or carries no value under a name your app reads back. One code, because the dialog is the answer to all of them. The body's sentence says which, for a user to read. |
|
|
47
47
|
|
|
48
48
|
**Your own key is what is missing.** These are yours to settle on your app's
|
|
49
49
|
settings page, so there is nothing to raise on the page: report the feature as
|
|
@@ -78,7 +78,7 @@ on their own.
|
|
|
78
78
|
| `code` | What it means |
|
|
79
79
|
| -------------------- | -------------------------------------------------------------------------------------- |
|
|
80
80
|
| `cap_exceeded` | A cap has been reached. Try again later. |
|
|
81
|
-
| `rate_limited` | Over Brass's own rate limit.
|
|
81
|
+
| `rate_limited` | Over Brass's own rate limit. The SDK resends a call that limit turns away for only a moment, so wait before calling again. |
|
|
82
82
|
| `calls_paused` | Whoever owns the account being spent set its cap to zero. Waiting will not clear it. |
|
|
83
83
|
| `vault_unavailable` | Brass could not read the stored key just now. Nothing is wrong with it; make the same call again. |
|
|
84
84
|
|
|
@@ -112,7 +112,7 @@ signed in, the app has no organization, or it is running in one that has not
|
|
|
112
112
|
installed it.
|
|
113
113
|
|
|
114
114
|
Each entry also carries `connectable`, which says whether the dialog has
|
|
115
|
-
anything for the
|
|
115
|
+
anything for the user on the page to do at that host. Together the two are
|
|
116
116
|
the whole branch:
|
|
117
117
|
|
|
118
118
|
```js
|
|
@@ -134,7 +134,7 @@ if (await Brass.connectService('api.anthropic.com')) showSummariseButton();
|
|
|
134
134
|
|
|
135
135
|
Passing `requestMissingCredentials: true` to `callService` opens the same
|
|
136
136
|
dialog from the call, which then goes through once the user answers. It covers
|
|
137
|
-
every refusal
|
|
137
|
+
every refusal your app's user can answer: `credential_required`, plus
|
|
138
138
|
`credential_rejected` and `credential_scheme_unusable` when the account that
|
|
139
139
|
failed is one they own.
|
|
140
140
|
|
|
@@ -157,7 +157,7 @@ same one: you name a host, and Brass attaches whichever credential is in
|
|
|
157
157
|
force.
|
|
158
158
|
|
|
159
159
|
Your app reads `connectable` rather than the supplier, because that is the
|
|
160
|
-
split it can act on. Where it is true,
|
|
160
|
+
split it can act on. Where it is true, your app's user can
|
|
161
161
|
settle it, so a Connect button leads somewhere; where it is false, the host is
|
|
162
162
|
waiting on you, so there is nothing to raise. Leave the wording about who to
|
|
163
163
|
ask to the dialog, which knows both the supplier and who is reading, where
|
|
@@ -205,7 +205,7 @@ if (cf?.ready && account) {
|
|
|
205
205
|
|
|
206
206
|
Brass shows your setup instructions on the form that collects a credential,
|
|
207
207
|
and you are strongly encouraged to provide them. Without them the form shows
|
|
208
|
-
your field labels alone, and the
|
|
208
|
+
your field labels alone, and the user filling it in has to work out what
|
|
209
209
|
to create at the vendor and where.
|
|
210
210
|
|
|
211
211
|
Declare a `setup` beside the host:
|
package/docs/streams.md
CHANGED
|
@@ -1,19 +1,29 @@
|
|
|
1
|
-
# Streams:
|
|
1
|
+
# Streams: loading a document in parts
|
|
2
2
|
|
|
3
3
|
A document is a container of **streams**: named units of document
|
|
4
4
|
state that load independently. Opening a document costs only the
|
|
5
5
|
streams your app touches, so download, load, and memory scale with what
|
|
6
|
-
|
|
7
|
-
multi-discipline design into architectural, plumbing, HVAC, and
|
|
8
|
-
structural streams, and a plumbing tool opens one of them. Every stream
|
|
6
|
+
the app reads rather than with the document's total size. Every stream
|
|
9
7
|
is live, collaborative state that merges the same way the rest of the
|
|
10
8
|
document does.
|
|
11
9
|
|
|
10
|
+
The app or importer that writes a document decides its streams. Your
|
|
11
|
+
app can put a heavy part of its content (a terrain mesh, a point-cloud
|
|
12
|
+
index, a layer only some views draw) in a stream of its own, and an open
|
|
13
|
+
that does not ask for that stream never downloads it. Your app can also
|
|
14
|
+
keep records about a document (clash findings, review notes, a cache of
|
|
15
|
+
derived results) in a stream beside the document's content, so they sync
|
|
16
|
+
and version with it and stay out of an exported file unless the exporter
|
|
17
|
+
declares that stream. An
|
|
18
|
+
importer can split a model the same way: with its geometry in a stream
|
|
19
|
+
of its own, a property editor opens only the default stream, and a 3D
|
|
20
|
+
viewer opens only the geometry.
|
|
21
|
+
|
|
12
22
|
Every document has a stream named **`default`**, and an open that names
|
|
13
23
|
no stream opens that one, so the doc-level handles (`doc.map(...)`,
|
|
14
|
-
`doc.orderedMap(...)`) read it.
|
|
15
|
-
content
|
|
16
|
-
|
|
24
|
+
`doc.orderedMap(...)`) read it. A document with one stream keeps all of
|
|
25
|
+
its content there. Name another stream on the open (below) and the
|
|
26
|
+
doc-level handles read that one instead.
|
|
17
27
|
|
|
18
28
|
## Opening and editing a stream
|
|
19
29
|
|
|
@@ -28,25 +38,23 @@ records.subscribe((change) => render(change));
|
|
|
28
38
|
|
|
29
39
|
`doc.stream(name)` resolves once per name: the first call downloads the
|
|
30
40
|
stream's snapshot and subscribes it live, and repeat calls return the same
|
|
31
|
-
open. It only reads, so a name the document does not
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
surface scoped to that stream: `map` / `list` / `orderedMap` handles,
|
|
37
|
-
`mutate`, `subscribe`, and `undo` / `redo`.
|
|
41
|
+
open. It only reads, so it rejects a name the document does not have.
|
|
42
|
+
To make a stream, see [Adding a stream](#adding-a-stream). A
|
|
43
|
+
`DocumentStream` has the same read and write methods as the document,
|
|
44
|
+
scoped to that stream: `map` / `list` / `orderedMap` handles, `mutate`,
|
|
45
|
+
and `subscribe`.
|
|
38
46
|
|
|
39
47
|
## Opening at a stream
|
|
40
48
|
|
|
41
49
|
`Brass.openDocumentById(docId, { stream })` opens the document with that
|
|
42
50
|
stream as its own state, so `doc.map(...)` hands you its records and the rest
|
|
43
|
-
of the document is never downloaded.
|
|
44
|
-
|
|
51
|
+
of the document is never downloaded. A 3D viewer of an imported model
|
|
52
|
+
opens at `geometry` and downloads nothing else:
|
|
45
53
|
|
|
46
54
|
```ts
|
|
47
|
-
const doc = await Brass.openDocumentById(docId, { stream: '
|
|
55
|
+
const doc = await Brass.openDocumentById(docId, { stream: 'geometry' });
|
|
48
56
|
doc.orderedMap('instances', 'instance_order').subscribe((change) => {
|
|
49
|
-
for (const [key, record] of change.set)
|
|
57
|
+
for (const [key, record] of change.set) draw(key, record); // geometry records
|
|
50
58
|
});
|
|
51
59
|
```
|
|
52
60
|
|
|
@@ -63,165 +71,142 @@ for (const info of await doc.listStreams()) {
|
|
|
63
71
|
}
|
|
64
72
|
```
|
|
65
73
|
|
|
66
|
-
|
|
67
|
-
digit, the same rule a schema family follows. `default` is one of them:
|
|
68
|
-
every document already holds it, so `getOrCreateStream` resolves it
|
|
69
|
-
without a request, and a loop over the names your schema declares needs
|
|
70
|
-
no case for it. `getOrCreateStream` writes, so a read-only open reads
|
|
71
|
-
with `doc.stream` instead and shows a degraded view when the document
|
|
72
|
-
does not hold the stream.
|
|
73
|
-
|
|
74
|
-
## Undo and editing model
|
|
74
|
+
## Adding a stream
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
operation completes. An app that never seals gets one giant step per
|
|
85
|
-
session.
|
|
86
|
-
|
|
87
|
-
### Undoing an operation that spans streams
|
|
88
|
-
|
|
89
|
-
When one user action writes to several streams (move a wall in
|
|
90
|
-
`architectural`, reroute its duct in `hvac`), keep an app-level
|
|
91
|
-
operation journal and undo each touched stream once per journal entry.
|
|
92
|
-
Two rules make the stacks line up:
|
|
93
|
-
|
|
94
|
-
1. **Seal every touched stream when the operation completes.** The
|
|
95
|
-
seal is what makes the operation exactly one undo step in each
|
|
96
|
-
stream, however many mutates it took.
|
|
97
|
-
2. **Route every local edit through the journal.** The alignment
|
|
98
|
-
argument is LIFO: if every operation is journaled and sealed, each
|
|
99
|
-
stream's stack top always belongs to the most recent journaled
|
|
100
|
-
operation that touched it. An edit that bypasses the journal
|
|
101
|
-
breaks that.
|
|
76
|
+
`doc.getOrCreateStream(name)` opens the document's stream of that name,
|
|
77
|
+
and creates it if the document does not have it yet. A stream your app
|
|
78
|
+
adds syncs and versions with the rest of the document, and only the
|
|
79
|
+
opens that ask for it download it. Your app can add a stream to any
|
|
80
|
+
document the user can edit, including a file an importer or another app
|
|
81
|
+
made. The call is idempotent, and
|
|
82
|
+
concurrent callers get the same stream, so your app can call it on
|
|
83
|
+
every open without checking first:
|
|
102
84
|
|
|
103
85
|
```ts
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const undoJournal: Op[] = [];
|
|
108
|
-
const redoJournal: Op[] = [];
|
|
109
|
-
|
|
110
|
-
// Wrap every user operation: run the writes, then seal + journal.
|
|
111
|
-
async function operation(
|
|
112
|
-
label: string,
|
|
113
|
-
streams: string[],
|
|
114
|
-
run: (open: Map<string, DocumentStream>) => void,
|
|
115
|
-
) {
|
|
116
|
-
const open = new Map(
|
|
117
|
-
await Promise.all(
|
|
118
|
-
streams.map(async (n) => [n, await doc.stream(n)] as const),
|
|
119
|
-
),
|
|
120
|
-
);
|
|
121
|
-
run(open);
|
|
122
|
-
for (const s of open.values()) s.sealUndoStep();
|
|
123
|
-
undoJournal.push({ label, streams });
|
|
124
|
-
redoJournal.length = 0; // a fresh edit clears redo, as usual
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
await operation('Move wall', ['architectural', 'hvac'], (open) => {
|
|
128
|
-
open.get('architectural')!.map('instances').mutate((m) => {
|
|
129
|
-
/* move the wall */
|
|
130
|
-
});
|
|
131
|
-
open.get('hvac')!.map('instances').mutate((m) => {
|
|
132
|
-
/* reroute the attached duct */
|
|
133
|
-
});
|
|
86
|
+
const findings = await doc.getOrCreateStream('clash-findings');
|
|
87
|
+
findings.map('findings').mutate((m) => {
|
|
88
|
+
m[id] = { elements: [a, b], status: 'open' };
|
|
134
89
|
});
|
|
90
|
+
```
|
|
135
91
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
92
|
+
Your app can add a stream to any document it can edit, including one
|
|
93
|
+
that another app created or imported. When the document already has a
|
|
94
|
+
stream of that name, such as one its importer wrote, `getOrCreateStream`
|
|
95
|
+
opens that stream instead of making a new one. Name the streams your app
|
|
96
|
+
adds after your app (`acme-clash-findings`) rather than after their
|
|
97
|
+
content, so your app does not write into a stream that another writer
|
|
98
|
+
named first.
|
|
99
|
+
|
|
100
|
+
Names are `a-z 0-9 . _ -`, 1 to 64 characters, starting with a letter or
|
|
101
|
+
digit, the same rule a schema family follows. `default` is a valid name
|
|
102
|
+
too: every document already has it, so `getOrCreateStream` resolves it
|
|
103
|
+
like any existing name, and a loop over the names your schema declares
|
|
104
|
+
needs no case for it. `getOrCreateStream` writes, so on a read-only
|
|
105
|
+
open, read with `doc.stream` instead, and render a reduced view when the
|
|
106
|
+
document does not have the stream.
|
|
145
107
|
|
|
146
|
-
|
|
147
|
-
const op = redoJournal.pop();
|
|
148
|
-
if (!op) return;
|
|
149
|
-
for (const name of op.streams) {
|
|
150
|
-
(await doc.stream(name)).redo();
|
|
151
|
-
}
|
|
152
|
-
undoJournal.push(op);
|
|
153
|
-
}
|
|
154
|
-
```
|
|
108
|
+
## Undo and editing model
|
|
155
109
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
110
|
+
Each stream is its own collaborative unit: concurrent edits merge per
|
|
111
|
+
stream. One Undo reverts the whole step even while a collaborator edits
|
|
112
|
+
the same stream, and a map or text handle's `subscribe` fires once for
|
|
113
|
+
each commit the step took, each with `origin.source` `'undo'` (or
|
|
114
|
+
`'redo'`). A write your app makes on its own, such as the results of a
|
|
115
|
+
pass over other documents, passes `{ undoable: false }` to `mutate` so
|
|
116
|
+
the user's Undo skips it.
|
|
117
|
+
|
|
118
|
+
A stream that reaches the largest size Brass keeps stops taking writes on
|
|
119
|
+
its own: `stream.readOnly` and `stream.atCapacity` read `true`, and the
|
|
120
|
+
document's other streams keep taking writes. The edit Brass refused stays
|
|
121
|
+
on screen until your app opens the stream again, which loads what Brass
|
|
122
|
+
saved. Read `stream.atCapacity` to stop offering bulk work on that stream.
|
|
123
|
+
|
|
124
|
+
## When to add a stream
|
|
125
|
+
|
|
126
|
+
Opening a stream incurs a snapshot request, and opening the first stream
|
|
127
|
+
on a document also retrieves the stream manifest. Add a stream when
|
|
128
|
+
specific workflows can skip its content (such as a model's geometry, a heavy
|
|
129
|
+
layer, or tool-specific data). Partition content by what each workflow loads,
|
|
130
|
+
rather than by conceptual divisions. Aim for a handful of streams
|
|
131
|
+
carrying several megabytes each. Opening dozens of tiny streams is slower than
|
|
132
|
+
loading a single stream, and data required across every workflow belongs in
|
|
133
|
+
the default stream.
|
|
180
134
|
|
|
181
135
|
## Reading a document spread across streams
|
|
182
136
|
|
|
183
137
|
Your schema names the streams it expects a document to be spread over,
|
|
184
138
|
under `streams`. A small document keeps every record in its default
|
|
185
|
-
stream; a large one moves the bulk (
|
|
139
|
+
stream; a large one moves the bulk (a model's geometry, an index most opens
|
|
186
140
|
skip) onto the streams the schema names, so an app that reads only part of
|
|
187
141
|
the content loads only that part. The declaration is the same either way,
|
|
188
142
|
because it says which streams your app expects, not what any one document
|
|
189
|
-
turned out to
|
|
143
|
+
turned out to contain.
|
|
144
|
+
|
|
145
|
+
Open only the streams that contain the records your app needs. The SDK
|
|
146
|
+
downloads and decodes each stream you open in full, so a property editor
|
|
147
|
+
over a model whose geometry has its own stream opens the default stream
|
|
148
|
+
alone.
|
|
190
149
|
|
|
191
|
-
If your app follows references
|
|
192
|
-
|
|
193
|
-
stream
|
|
150
|
+
If your app follows references into records in another stream (a
|
|
151
|
+
viewer resolving an element's shape, a clash check reading bodies), open
|
|
152
|
+
every stream the document has, because a referenced record can be in any
|
|
153
|
+
of them. `doc.listStreams()` lists them, so open them together and merge the
|
|
154
|
+
records by id:
|
|
194
155
|
|
|
195
156
|
```ts
|
|
196
157
|
import type { OrderedMapHandle } from '@brass-build/client';
|
|
197
158
|
|
|
198
159
|
type StoredRecord = { type: string; params: unknown };
|
|
199
160
|
const store: Record<string, StoredRecord> = {};
|
|
200
|
-
|
|
161
|
+
|
|
162
|
+
const streams = await Promise.all(
|
|
163
|
+
(await doc.listStreams()).map((info) => doc.stream(info.name)),
|
|
164
|
+
);
|
|
165
|
+
const handles: OrderedMapHandle<StoredRecord>[] = streams.map((s) =>
|
|
166
|
+
s.orderedMap<StoredRecord>('instances', 'instance_order'),
|
|
167
|
+
);
|
|
168
|
+
for (const records of handles) {
|
|
201
169
|
records.subscribe((change) => {
|
|
202
170
|
for (const [key, record] of change.set) store[key] = record;
|
|
203
171
|
for (const key of change.deleted) delete store[key];
|
|
204
172
|
});
|
|
205
|
-
|
|
206
|
-
fold(doc.orderedMap<StoredRecord>('instances', 'instance_order'));
|
|
207
|
-
for (const info of await doc.listStreams()) {
|
|
208
|
-
const stream = await doc.stream(info.name);
|
|
209
|
-
fold(stream.orderedMap<StoredRecord>('instances', 'instance_order'));
|
|
210
173
|
}
|
|
211
174
|
// store['#137'] resolves wherever #137 lives.
|
|
212
175
|
```
|
|
213
176
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
177
|
+
This reads a document with one stream the same way it reads one with
|
|
178
|
+
several, so it needs no special case.
|
|
179
|
+
|
|
180
|
+
### Knowing when every stream has arrived
|
|
181
|
+
|
|
182
|
+
A handle's `onSettled` callback fires when that one container has arrived
|
|
183
|
+
whole. A conclusion drawn across several streams (a model with no
|
|
184
|
+
geometry, a count, "no run yet") is true only after every stream it reads
|
|
185
|
+
has settled. Until your app opens a stream, its records are unknown, not
|
|
186
|
+
empty. Render such a conclusion once every handle has settled, and show
|
|
187
|
+
the loading state until then:
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
const unsettled = new Set(handles);
|
|
191
|
+
for (const records of handles) {
|
|
192
|
+
records.onSettled(() => {
|
|
193
|
+
unsettled.delete(records);
|
|
194
|
+
if (unsettled.size === 0) render(store);
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
`onSettled` fires again when a version checkout replaces a container, so
|
|
200
|
+
`render` runs again for the version the user switched to. The same
|
|
201
|
+
applies to streams your app adds (findings, review notes). Open the ones
|
|
202
|
+
the document has alongside the rest, and treat their records as unknown
|
|
203
|
+
until their handles settle.
|
|
219
204
|
|
|
220
|
-
A document can
|
|
221
|
-
review notes, a cache)
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
does not
|
|
205
|
+
A document can have streams your schema does not name (computed results,
|
|
206
|
+
review notes, a cache). An exporter writes nothing from a stream it does
|
|
207
|
+
not declare. Read the streams your schema names, and the others only
|
|
208
|
+
when you follow references into them. `doc.getOrCreateStream` makes a
|
|
209
|
+
stream your schema names when the document does not have it yet.
|
|
225
210
|
|
|
226
211
|
## Exporting across streams
|
|
227
212
|
|
|
@@ -231,11 +216,15 @@ lives:
|
|
|
231
216
|
|
|
232
217
|
```ts
|
|
233
218
|
Brass.registerSerializer(async (doc) => {
|
|
234
|
-
const
|
|
235
|
-
const
|
|
219
|
+
const names = new Set((await doc.listStreams()).map((s) => s.name));
|
|
220
|
+
const geometry = names.has('geometry')
|
|
221
|
+
? (await doc.stream('geometry'))
|
|
222
|
+
.orderedMap('instances', 'instance_order')
|
|
223
|
+
.entries()
|
|
224
|
+
: [];
|
|
236
225
|
return encodeMyFormat(
|
|
237
|
-
|
|
238
|
-
|
|
226
|
+
doc.orderedMap('instances', 'instance_order').entries(),
|
|
227
|
+
geometry,
|
|
239
228
|
);
|
|
240
229
|
});
|
|
241
230
|
```
|
package/docs/text-fields.md
CHANGED
|
@@ -9,7 +9,7 @@ what a keystroke costs, and where a text container belongs in a document.
|
|
|
9
9
|
## Paint a peer's edit without losing the caret
|
|
10
10
|
|
|
11
11
|
Assigning `field.value` moves the caret to the end, so an editor that
|
|
12
|
-
assigns unconditionally throws away the
|
|
12
|
+
assigns unconditionally throws away the user's place on every peer
|
|
13
13
|
keystroke, and one that skips a focused field never shows a peer's typing
|
|
14
14
|
to anyone who is typing themselves. Find the span the incoming value
|
|
15
15
|
replaced, and move the caret across it:
|
|
@@ -29,10 +29,12 @@ report.subscribe((value, origin) => {
|
|
|
29
29
|
const max = Math.min(painted.length, value.length);
|
|
30
30
|
let start = 0;
|
|
31
31
|
while (start < max && painted.charCodeAt(start) === value.charCodeAt(start)) start += 1;
|
|
32
|
+
if (start > 0 && isHighHalf(value.charCodeAt(start - 1))) start -= 1;
|
|
32
33
|
let tail = 0;
|
|
33
34
|
while (tail < max - start
|
|
34
35
|
&& painted.charCodeAt(painted.length - 1 - tail)
|
|
35
36
|
=== value.charCodeAt(value.length - 1 - tail)) tail += 1;
|
|
37
|
+
if (tail > 0 && isLowHalf(value.charCodeAt(value.length - tail))) tail -= 1;
|
|
36
38
|
const removed = painted.length - tail - start;
|
|
37
39
|
const inserted = value.length - tail - start;
|
|
38
40
|
|
|
@@ -45,13 +47,13 @@ report.subscribe((value, origin) => {
|
|
|
45
47
|
: caret >= start + removed ? caret + inserted - removed
|
|
46
48
|
: start + Math.min(caret - start, inserted);
|
|
47
49
|
|
|
48
|
-
// A
|
|
50
|
+
// A user with text selected has two offsets, and `selectionDirection`
|
|
49
51
|
// says which of them their next keystroke extends from.
|
|
50
52
|
const focused = document.activeElement === field;
|
|
51
53
|
const anchor = carry(field.selectionStart ?? 0);
|
|
52
54
|
const head = carry(field.selectionEnd ?? field.selectionStart ?? 0);
|
|
53
55
|
const direction = field.selectionDirection;
|
|
54
|
-
// Assigning `value` resets the scroll, which throws a
|
|
56
|
+
// Assigning `value` resets the scroll, which throws a user who is
|
|
55
57
|
// partway down a long field back to the top on every peer keystroke.
|
|
56
58
|
const scroll = field.scrollTop;
|
|
57
59
|
|
|
@@ -65,19 +67,24 @@ report.subscribe((value, origin) => {
|
|
|
65
67
|
```
|
|
66
68
|
|
|
67
69
|
Offsets count UTF-16 code units, which is what a text field's
|
|
68
|
-
`selectionStart` reports, so the two agree with no conversion.
|
|
69
|
-
units are why an offset needs one more check before it is used: a character
|
|
70
|
+
`selectionStart` reports, so the two agree with no conversion. A character
|
|
70
71
|
outside the Basic Multilingual Plane, an emoji above all, is two code units,
|
|
71
|
-
and
|
|
72
|
-
|
|
72
|
+
and two emoji can share their first unit (😀 and 🙂 both start with
|
|
73
|
+
`0xd83d`), so the `charCodeAt` walks can stop between the halves of a
|
|
74
|
+
character. Keep the two checks after the walks: they move each end of the
|
|
75
|
+
span back onto a whole character, so a caret where a peer types an emoji
|
|
76
|
+
ends up after it, since the caret binds to the text on its right. A carried
|
|
77
|
+
offset can still land inside a character the peer inserted, and
|
|
78
|
+
`nudgeOffPair` moves it back to that character's start:
|
|
73
79
|
|
|
74
80
|
```ts
|
|
81
|
+
const isHighHalf = (unit: number) => unit >= 0xd800 && unit <= 0xdbff;
|
|
82
|
+
const isLowHalf = (unit: number) => unit >= 0xdc00 && unit <= 0xdfff;
|
|
83
|
+
|
|
75
84
|
function nudgeOffPair(text: string, at: number): number {
|
|
76
85
|
const bounded = Math.max(0, Math.min(at, text.length));
|
|
77
86
|
if (bounded <= 0 || bounded >= text.length) return bounded;
|
|
78
|
-
const
|
|
79
|
-
const after = text.charCodeAt(bounded);
|
|
80
|
-
const splits = before >= 0xd800 && before <= 0xdbff && after >= 0xdc00 && after <= 0xdfff;
|
|
87
|
+
const splits = isHighHalf(text.charCodeAt(bounded - 1)) && isLowHalf(text.charCodeAt(bounded));
|
|
81
88
|
return splits ? bounded - 1 : bounded;
|
|
82
89
|
}
|
|
83
90
|
```
|
|
@@ -94,8 +101,8 @@ path, where it would otherwise run on every keystroke.
|
|
|
94
101
|
|
|
95
102
|
## Defer the paint, not the write, while an IME is open
|
|
96
103
|
|
|
97
|
-
Assigning to the field while an IME is open discards
|
|
98
|
-
which costs a
|
|
104
|
+
Assigning to the field while an IME is open discards its unfinished input,
|
|
105
|
+
which costs a user typing Japanese, Chinese, or Korean the word they were
|
|
99
106
|
in the middle of. So track `compositionstart` / `compositionend` and skip
|
|
100
107
|
the paint in between.
|
|
101
108
|
|
|
@@ -113,7 +120,7 @@ field.addEventListener('compositionend', () => {
|
|
|
113
120
|
painted = field.value;
|
|
114
121
|
report.update(painted);
|
|
115
122
|
// What arrived while the composition was open is older than what the
|
|
116
|
-
//
|
|
123
|
+
// user just committed, so paint the container's own value, which
|
|
117
124
|
// carries both.
|
|
118
125
|
if (missed) {
|
|
119
126
|
missed = false;
|
|
@@ -123,9 +130,9 @@ field.addEventListener('compositionend', () => {
|
|
|
123
130
|
});
|
|
124
131
|
```
|
|
125
132
|
|
|
126
|
-
|
|
133
|
+
Keeping the incoming value aside and applying it at `compositionend` is the
|
|
127
134
|
version that looks right and loses data: that value predates the
|
|
128
|
-
composition, so painting it discards the word the
|
|
135
|
+
composition, so painting it discards the word the user just committed.
|
|
129
136
|
Deferring the write instead has the same shape of bug one layer down, since
|
|
130
137
|
the field's value then has no record of the peer's edit and `update` diffs
|
|
131
138
|
it back out.
|
|
@@ -152,7 +159,7 @@ revision pattern:
|
|
|
152
159
|
| 300 KB | 4.09 |
|
|
153
160
|
|
|
154
161
|
One paragraph, one description, or one report is far inside a frame at
|
|
155
|
-
typing speed. A field
|
|
162
|
+
typing speed. A field containing a book is not, and an app whose document
|
|
156
163
|
grows that large wants its prose split across containers, one per section,
|
|
157
164
|
so an edit compares against a section rather than the book.
|
|
158
165
|
|
|
@@ -167,9 +174,9 @@ per-session, since a snapshot carries the history.
|
|
|
167
174
|
|
|
168
175
|
A text container is a top-level root, and no nested-text surface exists, so
|
|
169
176
|
per-element prose that merges costs one root per element. Loro prices a
|
|
170
|
-
container against how many the document
|
|
171
|
-
the way the record store does, where paging
|
|
172
|
-
|
|
177
|
+
container against how many the document contains, so that does not stay flat
|
|
178
|
+
the way the record store does, where paging keeps container count down once
|
|
179
|
+
a map contains many records. Creating one text root per element against the same
|
|
173
180
|
elements written as records:
|
|
174
181
|
|
|
175
182
|
| elements | a root each | as records | roots heap | records heap |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brass-build/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Client SDK for Brass",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -39,8 +39,11 @@
|
|
|
39
39
|
"prepublishOnly": "tsc -b --force"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
+
"apache-arrow": "^21.2.0",
|
|
42
43
|
"fflate": "^0.8.3",
|
|
43
|
-
"
|
|
44
|
+
"flatbuffers": "^25.1.24",
|
|
45
|
+
"fzstd": "^0.1.1",
|
|
46
|
+
"loro-crdt": "1.13.9"
|
|
44
47
|
},
|
|
45
48
|
"devDependencies": {
|
|
46
49
|
"typescript": "~6.0.0"
|
|
@@ -154,18 +154,12 @@ export async function resolveActingOrganization(
|
|
|
154
154
|
options: { ask?: boolean } = {},
|
|
155
155
|
): Promise<string | null> {
|
|
156
156
|
const fromDocument = await deps.activeDocumentOrganizationId();
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
// can live in an organization the reader reaches through a shared
|
|
164
|
-
// project and is not a member of, which is a place to act from while it
|
|
165
|
-
// is open rather than a choice to carry.
|
|
166
|
-
deps.remember(fromDocument, '', false);
|
|
167
|
-
return fromDocument;
|
|
168
|
-
}
|
|
157
|
+
// Answered, not recorded. A document can live in an organization the
|
|
158
|
+
// reader reaches through a shared project and is not a member of, which is
|
|
159
|
+
// a place to act from while it is open and not one to keep once it closes.
|
|
160
|
+
// The caller records a document's organization only after the server
|
|
161
|
+
// confirms membership and the install.
|
|
162
|
+
if (fromDocument) return fromDocument;
|
|
169
163
|
const cached = deps.cachedResolution();
|
|
170
164
|
if (cached) return cached;
|
|
171
165
|
|