@brass-build/client 0.1.0 → 0.2.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 +72 -63
- package/CHANGELOG.md +86 -0
- package/README.md +317 -231
- package/dist/auth-flow.d.ts +10 -1
- package/dist/auth-flow.d.ts.map +1 -1
- package/dist/auth-flow.js +64 -20
- package/dist/auth-flow.js.map +1 -1
- package/dist/client.d.ts +3 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +217 -75
- package/dist/client.js.map +1 -1
- package/dist/document-engine.d.ts +4 -0
- package/dist/document-engine.d.ts.map +1 -0
- package/dist/document-engine.js +9 -0
- package/dist/document-engine.js.map +1 -0
- package/dist/document-streams.d.ts +8 -1
- package/dist/document-streams.d.ts.map +1 -1
- package/dist/document-streams.js +79 -8
- package/dist/document-streams.js.map +1 -1
- package/dist/documents-namespace.d.ts.map +1 -1
- package/dist/documents-namespace.js +19 -3
- package/dist/documents-namespace.js.map +1 -1
- package/dist/documents.d.ts +43 -14
- package/dist/documents.d.ts.map +1 -1
- package/dist/documents.js +871 -223
- package/dist/documents.js.map +1 -1
- package/dist/embed-protocol.d.ts +3 -0
- package/dist/embed-protocol.d.ts.map +1 -0
- package/dist/embed-protocol.js +26 -0
- package/dist/embed-protocol.js.map +1 -0
- package/dist/emitter.d.ts.map +1 -1
- package/dist/emitter.js +8 -1
- package/dist/emitter.js.map +1 -1
- package/dist/export-guest.d.ts.map +1 -1
- package/dist/export-guest.js +14 -6
- package/dist/export-guest.js.map +1 -1
- package/dist/fetch-deadline.d.ts +22 -0
- package/dist/fetch-deadline.d.ts.map +1 -0
- package/dist/fetch-deadline.js +96 -0
- package/dist/fetch-deadline.js.map +1 -0
- package/dist/fetch-retry.d.ts +1 -0
- package/dist/fetch-retry.d.ts.map +1 -1
- package/dist/fetch-retry.js +12 -5
- package/dist/fetch-retry.js.map +1 -1
- package/dist/handle-types.d.ts +150 -0
- package/dist/handle-types.d.ts.map +1 -0
- package/dist/handle-types.js +8 -0
- package/dist/handle-types.js.map +1 -0
- package/dist/handles.d.ts +48 -48
- package/dist/handles.d.ts.map +1 -1
- package/dist/handles.js +3122 -501
- package/dist/handles.js.map +1 -1
- package/dist/hosted-runtime.d.ts.map +1 -1
- package/dist/hosted-runtime.js +8 -12
- package/dist/hosted-runtime.js.map +1 -1
- package/dist/hosted-ui.d.ts +0 -1
- package/dist/hosted-ui.d.ts.map +1 -1
- package/dist/hosted-ui.js +29 -24
- package/dist/hosted-ui.js.map +1 -1
- package/dist/importer.d.ts.map +1 -1
- package/dist/importer.js +253 -8
- package/dist/importer.js.map +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +1 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js.map +1 -1
- package/dist/join-coverage.d.ts +12 -0
- package/dist/join-coverage.d.ts.map +1 -0
- package/dist/join-coverage.js +37 -0
- package/dist/join-coverage.js.map +1 -0
- package/dist/jwt.d.ts.map +1 -1
- package/dist/jwt.js +5 -1
- package/dist/jwt.js.map +1 -1
- package/dist/launch-context.js +2 -2
- package/dist/launch-context.js.map +1 -1
- package/dist/mutate-batching.d.ts +25 -2
- package/dist/mutate-batching.d.ts.map +1 -1
- package/dist/mutate-batching.js +57 -10
- package/dist/mutate-batching.js.map +1 -1
- package/dist/open-document.d.ts +15 -1
- package/dist/open-document.d.ts.map +1 -1
- package/dist/open-document.js +1851 -321
- package/dist/open-document.js.map +1 -1
- package/dist/presence.d.ts +28 -0
- package/dist/presence.d.ts.map +1 -1
- package/dist/presence.js +149 -7
- package/dist/presence.js.map +1 -1
- package/dist/record-paging.d.ts +336 -71
- package/dist/record-paging.d.ts.map +1 -1
- package/dist/record-paging.js +591 -127
- package/dist/record-paging.js.map +1 -1
- package/dist/selection-persistence.d.ts.map +1 -1
- package/dist/selection-persistence.js +11 -1
- package/dist/selection-persistence.js.map +1 -1
- package/dist/serializer.d.ts +30 -1
- package/dist/serializer.d.ts.map +1 -1
- package/dist/session-persistence.d.ts.map +1 -1
- package/dist/session-persistence.js +9 -8
- package/dist/session-persistence.js.map +1 -1
- package/dist/session-toolbar.d.ts +0 -1
- package/dist/session-toolbar.d.ts.map +1 -1
- package/dist/session-toolbar.js +226 -75
- package/dist/session-toolbar.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +87 -18
- package/dist/session.js.map +1 -1
- package/dist/tail-ops.d.ts +14 -4
- package/dist/tail-ops.d.ts.map +1 -1
- package/dist/tail-ops.js +75 -15
- package/dist/tail-ops.js.map +1 -1
- package/dist/topic-lifecycle.d.ts +4 -0
- package/dist/topic-lifecycle.d.ts.map +1 -1
- package/dist/topic-lifecycle.js +25 -1
- package/dist/topic-lifecycle.js.map +1 -1
- package/dist/types.d.ts +47 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/undo.d.ts +2 -3
- package/dist/undo.d.ts.map +1 -1
- package/dist/undo.js +11 -0
- package/dist/undo.js.map +1 -1
- package/dist/unload-guard.d.ts +5 -0
- package/dist/unload-guard.d.ts.map +1 -0
- package/dist/unload-guard.js +88 -0
- package/dist/unload-guard.js.map +1 -0
- package/dist/version-bar.d.ts +4 -3
- package/dist/version-bar.d.ts.map +1 -1
- package/dist/version-bar.js +140 -19
- package/dist/version-bar.js.map +1 -1
- package/dist/version.d.ts +2 -2
- package/dist/version.js +1 -1
- package/dist/vite.d.ts +18 -2
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +133 -8
- package/dist/vite.js.map +1 -1
- package/dist/ws-client.d.ts +23 -3
- package/dist/ws-client.d.ts.map +1 -1
- package/dist/ws-client.js +339 -89
- package/dist/ws-client.js.map +1 -1
- package/dist/ws-frame-limit.d.ts.map +1 -1
- package/dist/ws-frame-limit.js +19 -12
- package/dist/ws-frame-limit.js.map +1 -1
- package/docs/app-manifest.md +7 -8
- package/docs/custom-chrome.md +17 -20
- package/docs/hosted-ui-advanced.md +14 -17
- package/docs/imported-documents.md +53 -197
- package/docs/presence.md +45 -21
- package/docs/schemas.md +12 -17
- package/docs/streams.md +55 -57
- package/package.json +4 -3
- package/src/auth-flow.ts +90 -23
- package/src/client.ts +225 -78
- package/src/document-engine.ts +15 -0
- package/src/document-signals.ts +7 -7
- package/src/document-streams.ts +95 -9
- package/src/documents-namespace.ts +18 -3
- package/src/documents.ts +983 -277
- package/src/embed-protocol.ts +25 -0
- package/src/emitter.ts +8 -1
- package/src/export-guest.ts +15 -9
- package/src/fetch-deadline.ts +119 -0
- package/src/fetch-retry.ts +14 -5
- package/src/handle-types.ts +303 -0
- package/src/handles.ts +3406 -683
- package/src/hosted-runtime.ts +8 -12
- package/src/hosted-ui.ts +28 -32
- package/src/importer.ts +261 -8
- package/src/index.ts +29 -18
- package/src/internal.ts +1 -0
- package/src/join-coverage.ts +42 -0
- package/src/jwt.ts +8 -1
- package/src/launch-context.ts +2 -2
- package/src/mutate-batching.ts +60 -11
- package/src/open-document.ts +2355 -515
- package/src/presence.ts +210 -11
- package/src/record-paging.ts +687 -137
- package/src/selection-persistence.ts +10 -1
- package/src/serializer.ts +33 -1
- package/src/session-persistence.ts +9 -8
- package/src/session-toolbar.ts +249 -80
- package/src/session.ts +91 -17
- package/src/tail-ops.ts +76 -10
- package/src/topic-lifecycle.ts +31 -1
- package/src/types.ts +56 -4
- package/src/undo.ts +13 -5
- package/src/unload-guard.ts +91 -0
- package/src/version-bar.ts +163 -25
- package/src/version.ts +1 -1
- package/src/vite.ts +175 -10
- package/src/ws-client.ts +392 -92
- package/src/ws-frame-limit.ts +19 -12
package/src/document-streams.ts
CHANGED
|
@@ -18,7 +18,14 @@ import type {
|
|
|
18
18
|
EntityProxy,
|
|
19
19
|
} from './documents.js';
|
|
20
20
|
import type { DocumentStreamInfo } from './documents-namespace.js';
|
|
21
|
-
import type {
|
|
21
|
+
import type {
|
|
22
|
+
DocumentGoneListener,
|
|
23
|
+
DocumentGoneUnsubscribe,
|
|
24
|
+
DocumentStatusListener,
|
|
25
|
+
DocumentStatusUnsubscribe,
|
|
26
|
+
DocumentWorkStatus,
|
|
27
|
+
} from './document-signals.js';
|
|
28
|
+
import type { ListHandle, MapHandle, OrderedMapHandle } from './handle-types.js';
|
|
22
29
|
|
|
23
30
|
// The wiring `Document.stream()` runs on: list/create manifests over
|
|
24
31
|
// HTTP, and open a stream's line (join + WS subscribe) returning the
|
|
@@ -79,6 +86,50 @@ export class DocumentStream {
|
|
|
79
86
|
return this._inner.subscribe(cb);
|
|
80
87
|
}
|
|
81
88
|
|
|
89
|
+
// Whether this stream's writes are landing, which is a property of its own
|
|
90
|
+
// line: its snapshot carries its own capacity verdict, its ops are its own,
|
|
91
|
+
// and its own grant decides whether it takes writes. `subscribe` fires when
|
|
92
|
+
// `savingCount` or `atCapacity` moves, as it does on the document.
|
|
93
|
+
|
|
94
|
+
// True when this stream refuses local writes: `mutate` throws and edit
|
|
95
|
+
// affordances should be disabled.
|
|
96
|
+
get readOnly(): boolean {
|
|
97
|
+
return this._inner.readOnly;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Locally-issued ops on this stream the server has not acked yet. This
|
|
101
|
+
// stream's own line: `doc.savingCount` is the one that adds the streams up,
|
|
102
|
+
// and the default stream's inner IS the document, so reading that here would
|
|
103
|
+
// report every other stream's writes as this one's.
|
|
104
|
+
get savingCount(): number {
|
|
105
|
+
return this._inner._ownSavingCount;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// True when this stream has stopped accepting changes because it reached the
|
|
109
|
+
// largest size the platform can keep. The ceiling is per stream, so a
|
|
110
|
+
// document can hold one full stream and keep taking writes on the rest.
|
|
111
|
+
get atCapacity(): boolean {
|
|
112
|
+
return this._inner.atCapacity;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// The latest opaque work-status for this stream, or `null` when no slow
|
|
116
|
+
// server-side work is in flight. Render `message` verbatim.
|
|
117
|
+
get status(): DocumentWorkStatus | null {
|
|
118
|
+
return this._inner.status;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Subscribe to this stream's work-status updates. Fires once on attach when
|
|
122
|
+
// a status is already known.
|
|
123
|
+
onStatus(cb: DocumentStatusListener): DocumentStatusUnsubscribe {
|
|
124
|
+
return this._inner.onStatus(cb);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Fires once when the server reports this stream's state evicted. A late
|
|
128
|
+
// listener fires on registration if the eviction already happened.
|
|
129
|
+
onGone(cb: DocumentGoneListener): DocumentGoneUnsubscribe {
|
|
130
|
+
return this._inner.onGone(cb);
|
|
131
|
+
}
|
|
132
|
+
|
|
82
133
|
// Per-stream undo/redo: each stream keeps its own local-edit stack
|
|
83
134
|
// (its edits are its own CRDT commits). The doc-level `doc.undo()`
|
|
84
135
|
// covers only the default stream.
|
|
@@ -109,18 +160,40 @@ export class DocumentStream {
|
|
|
109
160
|
}
|
|
110
161
|
|
|
111
162
|
/** @internal Preview this stream at a historical line seq, or return it
|
|
112
|
-
* to its live head
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
|
|
163
|
+
* to its live head. Driven by the parent Document's version preview so a
|
|
164
|
+
* partitioned document previews consistently across streams.
|
|
165
|
+
* `parentPreviewing` says which of the two a `seq`-less call means: the
|
|
166
|
+
* parent stepping into a version that records nothing for this line, or the
|
|
167
|
+
* parent returning to its head. A no-op when the inner has no checkout
|
|
168
|
+
* fetcher (a guest line, which holds no token to fetch at-version), so such
|
|
169
|
+
* a stream stays live; a guest line refuses writes anyway. */
|
|
170
|
+
async _previewAt(seq: number | undefined, parentPreviewing = false): Promise<void> {
|
|
118
171
|
const inner = this._inner;
|
|
119
172
|
if (seq !== undefined && inner._hasCheckoutFetcher()) {
|
|
120
173
|
await inner._checkoutVersion(seq);
|
|
121
|
-
|
|
122
|
-
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (seq === undefined && parentPreviewing) {
|
|
177
|
+
// The preview's checkpoint records no seq for this line, because the
|
|
178
|
+
// stream did not exist when it was taken. There is no historical state
|
|
179
|
+
// this stream can honestly show, and returning it to its head shows two
|
|
180
|
+
// moments at once: the document's past beside this stream's present.
|
|
181
|
+
// Worse, it would leave the stream taking writes while the document
|
|
182
|
+
// reports read-only, and those writes land on the live head and outlive
|
|
183
|
+
// the preview. Hold it read-only for as long as the parent is browsing.
|
|
184
|
+
inner._setVersionBrowsing(true);
|
|
185
|
+
return;
|
|
123
186
|
}
|
|
187
|
+
inner._setVersionBrowsing(false);
|
|
188
|
+
// Returned whether or not a preview has LANDED on this line, because a
|
|
189
|
+
// checkout of it can be in flight and unapplied, and the return is what
|
|
190
|
+
// supersedes that fetch (it bumps the line's own generation before it
|
|
191
|
+
// looks at anything). Asking whether the line is checked out first is
|
|
192
|
+
// what lets an in-flight one land after the reader has already gone back
|
|
193
|
+
// to the head, leaving the stream showing a moment the document left.
|
|
194
|
+
// Throws on a line with no live head of its own, which the callers
|
|
195
|
+
// treat the same as any other per-stream failure.
|
|
196
|
+
inner.returnToHead();
|
|
124
197
|
}
|
|
125
198
|
|
|
126
199
|
/** @internal Close the stream's own transport + state. Called by the
|
|
@@ -129,4 +202,17 @@ export class DocumentStream {
|
|
|
129
202
|
_close(): void {
|
|
130
203
|
if (this._ownsInner) this._inner.close();
|
|
131
204
|
}
|
|
205
|
+
|
|
206
|
+
/** @internal Park the stream's own state alongside the parent's park, so a
|
|
207
|
+
* stream an app holds across `doc.close()` stops calling it back and refuses
|
|
208
|
+
* writes the way the parent does. A no-op for the default stream, whose
|
|
209
|
+
* inner is the parent and is already parking. */
|
|
210
|
+
_suspend(): void {
|
|
211
|
+
if (this._ownsInner) this._inner._suspend();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** @internal Pair of `_suspend`, run when the parent resumes. */
|
|
215
|
+
_resume(): void {
|
|
216
|
+
if (this._ownsInner) this._inner._resume();
|
|
217
|
+
}
|
|
132
218
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SDK surface: `Brass.documents.*`. The metadata surface: get /
|
|
2
|
-
// thumbnails
|
|
3
|
-
//
|
|
4
|
-
//
|
|
2
|
+
// thumbnails. The editable surface (snapshot + ops + mutate) is the
|
|
3
|
+
// `Document` runtime class via `Brass.openDocumentById` /
|
|
4
|
+
// `Brass.createDocument`. Access-granting is hosted-only (the share
|
|
5
5
|
// dialog), not a programmatic method here.
|
|
6
6
|
|
|
7
7
|
import type { Document } from './documents.js';
|
|
@@ -305,6 +305,11 @@ export function createDocumentsNamespace(
|
|
|
305
305
|
opts: DocumentsFactoryOptions,
|
|
306
306
|
): DocumentsNamespace & DocumentsInternalNamespace {
|
|
307
307
|
const apiFetch = createApiFetch(opts);
|
|
308
|
+
// Identifies THIS client to the import claim, so two tabs of one app
|
|
309
|
+
// signed in as one user are two claimants and only one of them imports.
|
|
310
|
+
// Minted once per namespace, so re-claiming from this client refreshes
|
|
311
|
+
// its own claim rather than taking a second one.
|
|
312
|
+
const importClientId = randomToken();
|
|
308
313
|
|
|
309
314
|
return {
|
|
310
315
|
get(docId) {
|
|
@@ -452,12 +457,14 @@ export function createDocumentsNamespace(
|
|
|
452
457
|
return apiFetch<ImportClaimResponse>({
|
|
453
458
|
method: 'POST',
|
|
454
459
|
path: `/documents/${encodeURIComponent(docId)}/import-claim`,
|
|
460
|
+
body: { client_id: importClientId },
|
|
455
461
|
});
|
|
456
462
|
},
|
|
457
463
|
completeImport(docId) {
|
|
458
464
|
return apiFetch<{ schema_ref: { app_id: string; schema_hash: string } }>({
|
|
459
465
|
method: 'POST',
|
|
460
466
|
path: `/documents/${encodeURIComponent(docId)}/import-complete`,
|
|
467
|
+
body: { client_id: importClientId },
|
|
461
468
|
});
|
|
462
469
|
},
|
|
463
470
|
async getThumbnail(docId, options) {
|
|
@@ -477,6 +484,14 @@ export function createDocumentsNamespace(
|
|
|
477
484
|
};
|
|
478
485
|
}
|
|
479
486
|
|
|
487
|
+
function randomToken(): string {
|
|
488
|
+
const g = globalThis as { crypto?: { randomUUID?: () => string } };
|
|
489
|
+
if (g.crypto?.randomUUID) return g.crypto.randomUUID();
|
|
490
|
+
let s = '';
|
|
491
|
+
for (let i = 0; i < 32; i++) s += Math.floor(Math.random() * 16).toString(16);
|
|
492
|
+
return s;
|
|
493
|
+
}
|
|
494
|
+
|
|
480
495
|
// Reads `SetThumbnailInput.image` into base64 plus a resolved MIME
|
|
481
496
|
// type. A `Blob` carries its own `type` (canvas `toBlob` sets it);
|
|
482
497
|
// raw `ArrayBuffer` / `Uint8Array` bytes need an explicit
|