@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.
Files changed (193) hide show
  1. package/AGENTS.md +72 -63
  2. package/CHANGELOG.md +86 -0
  3. package/README.md +317 -231
  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 +3 -0
  9. package/dist/client.d.ts.map +1 -1
  10. package/dist/client.js +217 -75
  11. package/dist/client.js.map +1 -1
  12. package/dist/document-engine.d.ts +4 -0
  13. package/dist/document-engine.d.ts.map +1 -0
  14. package/dist/document-engine.js +9 -0
  15. package/dist/document-engine.js.map +1 -0
  16. package/dist/document-streams.d.ts +8 -1
  17. package/dist/document-streams.d.ts.map +1 -1
  18. package/dist/document-streams.js +79 -8
  19. package/dist/document-streams.js.map +1 -1
  20. package/dist/documents-namespace.d.ts.map +1 -1
  21. package/dist/documents-namespace.js +19 -3
  22. package/dist/documents-namespace.js.map +1 -1
  23. package/dist/documents.d.ts +43 -14
  24. package/dist/documents.d.ts.map +1 -1
  25. package/dist/documents.js +871 -223
  26. package/dist/documents.js.map +1 -1
  27. package/dist/embed-protocol.d.ts +3 -0
  28. package/dist/embed-protocol.d.ts.map +1 -0
  29. package/dist/embed-protocol.js +26 -0
  30. package/dist/embed-protocol.js.map +1 -0
  31. package/dist/emitter.d.ts.map +1 -1
  32. package/dist/emitter.js +8 -1
  33. package/dist/emitter.js.map +1 -1
  34. package/dist/export-guest.d.ts.map +1 -1
  35. package/dist/export-guest.js +14 -6
  36. package/dist/export-guest.js.map +1 -1
  37. package/dist/fetch-deadline.d.ts +22 -0
  38. package/dist/fetch-deadline.d.ts.map +1 -0
  39. package/dist/fetch-deadline.js +96 -0
  40. package/dist/fetch-deadline.js.map +1 -0
  41. package/dist/fetch-retry.d.ts +1 -0
  42. package/dist/fetch-retry.d.ts.map +1 -1
  43. package/dist/fetch-retry.js +12 -5
  44. package/dist/fetch-retry.js.map +1 -1
  45. package/dist/handle-types.d.ts +150 -0
  46. package/dist/handle-types.d.ts.map +1 -0
  47. package/dist/handle-types.js +8 -0
  48. package/dist/handle-types.js.map +1 -0
  49. package/dist/handles.d.ts +48 -48
  50. package/dist/handles.d.ts.map +1 -1
  51. package/dist/handles.js +3122 -501
  52. package/dist/handles.js.map +1 -1
  53. package/dist/hosted-runtime.d.ts.map +1 -1
  54. package/dist/hosted-runtime.js +8 -12
  55. package/dist/hosted-runtime.js.map +1 -1
  56. package/dist/hosted-ui.d.ts +0 -1
  57. package/dist/hosted-ui.d.ts.map +1 -1
  58. package/dist/hosted-ui.js +29 -24
  59. package/dist/hosted-ui.js.map +1 -1
  60. package/dist/importer.d.ts.map +1 -1
  61. package/dist/importer.js +253 -8
  62. package/dist/importer.js.map +1 -1
  63. package/dist/index.d.ts +10 -10
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +11 -2
  66. package/dist/index.js.map +1 -1
  67. package/dist/internal.d.ts +1 -1
  68. package/dist/internal.d.ts.map +1 -1
  69. package/dist/internal.js.map +1 -1
  70. package/dist/join-coverage.d.ts +12 -0
  71. package/dist/join-coverage.d.ts.map +1 -0
  72. package/dist/join-coverage.js +37 -0
  73. package/dist/join-coverage.js.map +1 -0
  74. package/dist/jwt.d.ts.map +1 -1
  75. package/dist/jwt.js +5 -1
  76. package/dist/jwt.js.map +1 -1
  77. package/dist/launch-context.js +2 -2
  78. package/dist/launch-context.js.map +1 -1
  79. package/dist/mutate-batching.d.ts +25 -2
  80. package/dist/mutate-batching.d.ts.map +1 -1
  81. package/dist/mutate-batching.js +57 -10
  82. package/dist/mutate-batching.js.map +1 -1
  83. package/dist/open-document.d.ts +15 -1
  84. package/dist/open-document.d.ts.map +1 -1
  85. package/dist/open-document.js +1851 -321
  86. package/dist/open-document.js.map +1 -1
  87. package/dist/presence.d.ts +28 -0
  88. package/dist/presence.d.ts.map +1 -1
  89. package/dist/presence.js +149 -7
  90. package/dist/presence.js.map +1 -1
  91. package/dist/record-paging.d.ts +336 -71
  92. package/dist/record-paging.d.ts.map +1 -1
  93. package/dist/record-paging.js +591 -127
  94. package/dist/record-paging.js.map +1 -1
  95. package/dist/selection-persistence.d.ts.map +1 -1
  96. package/dist/selection-persistence.js +11 -1
  97. package/dist/selection-persistence.js.map +1 -1
  98. package/dist/serializer.d.ts +30 -1
  99. package/dist/serializer.d.ts.map +1 -1
  100. package/dist/session-persistence.d.ts.map +1 -1
  101. package/dist/session-persistence.js +9 -8
  102. package/dist/session-persistence.js.map +1 -1
  103. package/dist/session-toolbar.d.ts +0 -1
  104. package/dist/session-toolbar.d.ts.map +1 -1
  105. package/dist/session-toolbar.js +226 -75
  106. package/dist/session-toolbar.js.map +1 -1
  107. package/dist/session.d.ts.map +1 -1
  108. package/dist/session.js +87 -18
  109. package/dist/session.js.map +1 -1
  110. package/dist/tail-ops.d.ts +14 -4
  111. package/dist/tail-ops.d.ts.map +1 -1
  112. package/dist/tail-ops.js +75 -15
  113. package/dist/tail-ops.js.map +1 -1
  114. package/dist/topic-lifecycle.d.ts +4 -0
  115. package/dist/topic-lifecycle.d.ts.map +1 -1
  116. package/dist/topic-lifecycle.js +25 -1
  117. package/dist/topic-lifecycle.js.map +1 -1
  118. package/dist/types.d.ts +47 -1
  119. package/dist/types.d.ts.map +1 -1
  120. package/dist/undo.d.ts +2 -3
  121. package/dist/undo.d.ts.map +1 -1
  122. package/dist/undo.js +11 -0
  123. package/dist/undo.js.map +1 -1
  124. package/dist/unload-guard.d.ts +5 -0
  125. package/dist/unload-guard.d.ts.map +1 -0
  126. package/dist/unload-guard.js +88 -0
  127. package/dist/unload-guard.js.map +1 -0
  128. package/dist/version-bar.d.ts +4 -3
  129. package/dist/version-bar.d.ts.map +1 -1
  130. package/dist/version-bar.js +140 -19
  131. package/dist/version-bar.js.map +1 -1
  132. package/dist/version.d.ts +2 -2
  133. package/dist/version.js +1 -1
  134. package/dist/vite.d.ts +18 -2
  135. package/dist/vite.d.ts.map +1 -1
  136. package/dist/vite.js +133 -8
  137. package/dist/vite.js.map +1 -1
  138. package/dist/ws-client.d.ts +23 -3
  139. package/dist/ws-client.d.ts.map +1 -1
  140. package/dist/ws-client.js +339 -89
  141. package/dist/ws-client.js.map +1 -1
  142. package/dist/ws-frame-limit.d.ts.map +1 -1
  143. package/dist/ws-frame-limit.js +19 -12
  144. package/dist/ws-frame-limit.js.map +1 -1
  145. package/docs/app-manifest.md +7 -8
  146. package/docs/custom-chrome.md +17 -20
  147. package/docs/hosted-ui-advanced.md +14 -17
  148. package/docs/imported-documents.md +53 -197
  149. package/docs/presence.md +45 -21
  150. package/docs/schemas.md +12 -17
  151. package/docs/streams.md +55 -57
  152. package/package.json +4 -3
  153. package/src/auth-flow.ts +90 -23
  154. package/src/client.ts +225 -78
  155. package/src/document-engine.ts +15 -0
  156. package/src/document-signals.ts +7 -7
  157. package/src/document-streams.ts +95 -9
  158. package/src/documents-namespace.ts +18 -3
  159. package/src/documents.ts +983 -277
  160. package/src/embed-protocol.ts +25 -0
  161. package/src/emitter.ts +8 -1
  162. package/src/export-guest.ts +15 -9
  163. package/src/fetch-deadline.ts +119 -0
  164. package/src/fetch-retry.ts +14 -5
  165. package/src/handle-types.ts +303 -0
  166. package/src/handles.ts +3406 -683
  167. package/src/hosted-runtime.ts +8 -12
  168. package/src/hosted-ui.ts +28 -32
  169. package/src/importer.ts +261 -8
  170. package/src/index.ts +29 -18
  171. package/src/internal.ts +1 -0
  172. package/src/join-coverage.ts +42 -0
  173. package/src/jwt.ts +8 -1
  174. package/src/launch-context.ts +2 -2
  175. package/src/mutate-batching.ts +60 -11
  176. package/src/open-document.ts +2355 -515
  177. package/src/presence.ts +210 -11
  178. package/src/record-paging.ts +687 -137
  179. package/src/selection-persistence.ts +10 -1
  180. package/src/serializer.ts +33 -1
  181. package/src/session-persistence.ts +9 -8
  182. package/src/session-toolbar.ts +249 -80
  183. package/src/session.ts +91 -17
  184. package/src/tail-ops.ts +76 -10
  185. package/src/topic-lifecycle.ts +31 -1
  186. package/src/types.ts +56 -4
  187. package/src/undo.ts +13 -5
  188. package/src/unload-guard.ts +91 -0
  189. package/src/version-bar.ts +163 -25
  190. package/src/version.ts +1 -1
  191. package/src/vite.ts +175 -10
  192. package/src/ws-client.ts +392 -92
  193. package/src/ws-frame-limit.ts +19 -12
@@ -6,69 +6,223 @@ export declare const BODY_FIELD = "params";
6
6
  export declare const META_KEY = "meta";
7
7
  export declare const RECORDS_KEY = "instances";
8
8
  export declare const ORDER_KEY = "instance_order";
9
- /** `meta` key holding a paged store's page count; absent (or 0) means
10
- * unpaged. The read path detects pages by tag; only an in-place edit needs
11
- * the count, to hash a key to its page. */
12
- export declare const PAGE_COUNT_KEY = "page_count";
13
- /** `meta` key holding the paging codec's scheme number, stamped on a paged
14
- * write. The scheme fixes the page-key hash (`pageKeyFor`), the `p<n>` key
15
- * format, and the packed-record layout together: a reader that hashes a key
16
- * with a different scheme than the store was written under targets the wrong
17
- * page on an in-place edit, silently writing a record into a page it does not
18
- * belong to. The stamp lets a pinned reader detect a store written under a
19
- * scheme it does not implement and refuse the edit instead of corrupting it.
20
- * Absent means the current scheme (paged stores written before the stamp
21
- * existed used scheme 1's hash and layout). */
22
- export declare const PAGING_SCHEME_KEY = "paging_scheme";
23
- /** The paging scheme this build writes and can edit. Bump ONLY alongside a
24
- * change to `pageKeyFor`'s hash, the `p<n>` key format, or the packed-record
25
- * layout, so an older pinned reader trips `isPagingSchemeEditable` and fails
26
- * loud rather than editing a store it would mis-hash. */
27
- export declare const CURRENT_PAGING_SCHEME = 1;
28
- /** Whether a store stamped with `scheme` can be edited in place by this build.
29
- * Absent / non-numeric / `0` is a scheme-1 store (the stamp postdates it), so
30
- * editable; any scheme at or below the current one is editable; a higher
31
- * scheme is not (this reader would hash into the wrong page). Reads stay
32
- * best-effort and are not gated here; only an in-place edit can persist the
33
- * mis-hash, so the edit path is where the guard belongs. */
34
- export declare function isPagingSchemeEditable(scheme: unknown): boolean;
9
+ /** Prefix on the `meta` key marking ONE root map paged (`meta` holds
10
+ * `paged:instances` -> `true`). A root whose key is absent, or holds anything
11
+ * but `true`, is unpaged; a document with no paged root carries no such key.
12
+ *
13
+ * Per root rather than one flag for the document, so paging is a property of
14
+ * a collection rather than of a reserved name. One flag would force the read
15
+ * side to hardcode WHICH root it described, since a document-wide answer
16
+ * handed to every root would make `meta` and `header` read as paged the
17
+ * moment the records did. Keyed by root, a root with no key is simply
18
+ * unpaged, which is the answer those roots want anyway.
19
+ *
20
+ * A key per root rather than one entry naming them all. A map entry is
21
+ * written whole, so an entry holding every paged root is read-modify-write:
22
+ * two peers crossing the threshold on two different roots at once each write
23
+ * the whole entry, the two resolve last-writer-wins, and the root the losing
24
+ * write named is left unmarked. Nothing recovers it. Every reader then takes
25
+ * the unpaged path over a map holding nothing but pages, so the store's own
26
+ * bookkeeping keys read back as the records and the records reach nobody,
27
+ * while the raw entry count sits far below the threshold that would page the
28
+ * root again. A key per root means two roots are two keys, which merge.
29
+ *
30
+ * A flag rather than a page count. Pages are keyed by writing session and
31
+ * index (`brass:p:<peer>:<n>`) and a reader locates a record through the index
32
+ * it builds from the pages' `keys` fields, so no reader needs to know how many
33
+ * pages there are; a stored count would be a number that drifts as the store
34
+ * grows and that nothing checks.
35
+ *
36
+ * A plain value rather than a container: it is read on the hot path
37
+ * (`_isPaged` on every keyed read and edit), where one `get` of a scalar is
38
+ * the cheapest answer available. */
39
+ export declare const PAGED_ROOT_PREFIX = "paged:";
40
+ /** The `meta` key marking `rootKey` paged. */
41
+ export declare function pagedRootKey(rootKey: string): string;
42
+ /** Whether a value read from `meta` at `pagedRootKey(root)` marks that root
43
+ * paged. The one place the marker's stored form is interpreted, so the SDK's
44
+ * handle path and the store readers cannot disagree about what marks a root. */
45
+ export declare function isPagedMarker(marker: unknown): boolean;
35
46
  /** Above this many records the store is written paged; at or below it it keeps
36
- * the one-container-per-record shape. */
37
- export declare const UNPAGED_MAX_RECORDS = 50000;
38
- /** The tag a page container carries in place of a record tag, so a reader
39
- * tells a page (body = record triples) from a single record (body = one
40
- * plain value). */
47
+ * the one-container-per-record shape.
48
+ *
49
+ * Set where paging starts paying for itself on every axis but one. Measured
50
+ * on the two shapes at equal record counts (`scripts/bench/paging-threshold.mts`),
51
+ * the paged store writes ~3x faster, opens ~5x faster, and stores ~1.7x
52
+ * smaller here, against ~1.4x on a single edit, and that last penalty
53
+ * inverts by 25,000 records. Below a few hundred records the paged store is
54
+ * actually LARGER, since per-page gzip overhead outweighs what it saves. */
55
+ export declare const UNPAGED_MAX_RECORDS = 5000;
56
+ /** The tag a page container carries in place of a record tag, which labels it
57
+ * in a raw dump of the stored document. A READER never decides from it: what
58
+ * makes an entry a page is its key (`SESSION_PAGE_PREFIX`), so a record whose
59
+ * own tag happens to be this string is still a record. */
41
60
  export declare const PAGE_TAG = "__page__";
42
- /** A record packed inside a page body: `[key, tag, body]`. The tuple form
43
- * keeps a paged snapshot compact. */
44
- export type PackedRecord<B> = [key: string, tag: string, body: B];
45
- /** Encode a page's triples as its stored body: JSON-serialised, gzipped, held
46
- * as a latin1 string. A STRING body avoids loro's superlinear per-value
47
- * conversion of a deeply nested triple array; gzipping keeps a heavy file in
48
- * memory + export budget. (Full rationale in the substrate's `encodePageBody`.) */
49
- export declare function encodePageBody<B>(triples: PackedRecord<B>[]): string;
50
- /** Encode one record's `[key, tag, body]` triple as the JSON string it
51
- * occupies inside a page body. Split from page assembly so a large write can
61
+ /** Prefix on every key the record store writes for its own bookkeeping: the
62
+ * pages, a record's own container, a delete marker, a patch.
63
+ *
64
+ * A record and the bookkeeping about it share one map, so the two key spaces
65
+ * have to be told apart by something an app cannot produce. The map handle
66
+ * refuses a write under this prefix (`assertUsableRecordKey`), which makes the
67
+ * space exclusive: every key outside it is a record key, and every key inside
68
+ * it is the store's own. Nothing here inspects an app's VALUE to make that
69
+ * call, so no record can be mistaken for a page or a tombstone whatever it
70
+ * holds. */
71
+ export declare const RESERVED_KEY_PREFIX = "brass:";
72
+ /** Whether `key` names one of the store's own entries rather than a record. */
73
+ export declare function isReservedKey(key: string): boolean;
74
+ /** Refuse a record key the store cannot carry, at the moment an app writes it:
75
+ * one inside the reserved space, and `__proto__`.
76
+ *
77
+ * Rejecting the reserved space is what lets every reader decide what an entry
78
+ * is from the key alone. The alternative, escaping, would put the escape on
79
+ * the read of every key of every map, paged or not, and a store that pages
80
+ * later cannot escape what it already holds. */
81
+ export declare function assertUsableRecordKey(key: string): void;
82
+ /** The prefix on a record written as its OWN container in a paged store,
83
+ * which shadows the page's copy on read. A page body is one value, so an
84
+ * edit that rewrote its page would make the page the merge unit; a record in
85
+ * its own container merges on its own. */
86
+ export declare const OVERLAY_PREFIX = "brass:r:";
87
+ /** Pack one stored value into a page triple. A `null` tag is what says the
88
+ * body IS the record: an app's map holds whatever the app assigned, which has
89
+ * no tag to split off. A codec's records arrive already split, and a codec
90
+ * tag is a string, so the two forms cannot be confused however a codec names
91
+ * its records. */
92
+ export declare function packWholeValue<B>(key: string, value: B): PackedRecord<B>;
93
+ /** Re-pack `value` in the same triple shape `packed` already uses, so a page
94
+ * rewritten in place keeps the form its readers expect: a codec's records
95
+ * stay a tag beside a body, and an app's stay one whole value. A streaming
96
+ * read visits a page's triples without unpacking them, so a rewrite that
97
+ * changed the shape would hand it the wrong half of the record. */
98
+ export declare function repackRecord(packed: PackedRecord<unknown>, value: unknown): PackedRecord<unknown>;
99
+ /** The stored value a page triple carries: the value itself for a whole-value
100
+ * record, else the `{ type, params }` pair a codec split it into. */
101
+ export declare function unpackRecord(packed: PackedRecord<unknown>): unknown;
102
+ /** The tag + body a page triple names, for a reader that walks the two
103
+ * separately, or null when the triple packs a whole value that is not a
104
+ * record.
105
+ *
106
+ * A page holds both forms: a codec packs a record it has already split into a
107
+ * tag and a body, and a map an app writes packs whatever the app assigned as
108
+ * one whole value. Both forms occur in ONE store, because an app's bulk add
109
+ * to a codec's record map seals its own pages, so a reader that took
110
+ * `packed[1]` for the tag would report every record those pages hold as
111
+ * having no type at all. */
112
+ export declare function unpackRecordPair(packed: PackedRecord<unknown>): {
113
+ tag: string;
114
+ body: unknown;
115
+ } | null;
116
+ /** The container key a record is written under, beside the pages. */
117
+ export declare function overlayKeyFor(recordKey: string): string;
118
+ /** The marker key standing for a record deleted out of a paged store. */
119
+ export declare function tombstoneKeyFor(recordKey: string): string;
120
+ /** The prefix on a page written by ONE editing session, which fills its pages
121
+ * in creation order.
122
+ *
123
+ * An app fills a map by appending, so a rule that scattered one mutation's
124
+ * records across every page would add a record or two to each of thousands of
125
+ * pages and rewrite every one of them again on the next mutation, which costs
126
+ * more than the per-record containers it replaces. Filling in creation order
127
+ * packs each mutation's records into its own dense pages, written complete
128
+ * and never rewritten.
129
+ *
130
+ * The token makes the key space exclusive to one writer. A page body is a
131
+ * single value, so two writers appending to the same page container resolve
132
+ * last-writer-wins and one writer's records are lost; with a per-session
133
+ * token no two writers ever address the same page. It is also what makes
134
+ * filling in creation order safe on reopen: a session never inherits, and so
135
+ * never rewrites, a page an earlier session sealed. */
136
+ export declare const SESSION_PAGE_PREFIX = "brass:p:";
137
+ /** The container key for page `index` of the session identified by `token`. */
138
+ export declare function sessionPageKey(token: string, index: number): string;
139
+ /** The `{ token, index }` a session page key names, or null for any other key.
140
+ * Used to seed a session's next page index above what the store already
141
+ * holds, so a reopened session cannot overwrite its own sealed pages. */
142
+ export declare function parseSessionPageKey(key: string): {
143
+ token: string;
144
+ index: number;
145
+ } | null;
146
+ /** A record packed inside a page: `[key, tag, body]`, where a `null` tag means
147
+ * the body is the record's WHOLE stored value (`packWholeValue`). The tuple
148
+ * form keeps a paged snapshot compact. It is how a reader HOLDS a page's
149
+ * records; a stored page splits the key off into its own field
150
+ * (`PAGE_KEYS_FIELD`). */
151
+ export type PackedRecord<B> = [key: string, tag: string | null, body: B];
152
+ /** Field on a page container holding the keys of the records its body packs,
153
+ * in the same order, so the two are zipped by index.
154
+ *
155
+ * The keys live apart from the bodies because finding a record in a page
156
+ * filled in creation order (`SESSION_PAGE_PREFIX`) means learning which page
157
+ * holds which key, and that question is answered by the keys alone. Packed
158
+ * beside the bodies, answering it costs decompressing and parsing every
159
+ * record in the store: measured at 250,000 records, 1.85 s against 0.24 s for
160
+ * the keys on their own. Splitting them costs no bytes, since the keys are
161
+ * stored once either way and compress better grouped than interleaved with
162
+ * the bodies. */
163
+ export declare const PAGE_KEYS_FIELD = "keys";
164
+ /** A page's two stored strings: the record bodies and the keys they belong
165
+ * to. */
166
+ export interface EncodedPage {
167
+ body: string;
168
+ keys: string;
169
+ /** The body's size BEFORE gzip, so a writer can charge a page in the same
170
+ * unit an ordinary map assignment is charged in. Taken here because the
171
+ * uncompressed string is built here and nothing else holds it. */
172
+ chars: number;
173
+ }
174
+ /** Encode a page's records as the two strings it stores. Each is JSON,
175
+ * gzipped, held as a latin1 string. A STRING avoids loro's superlinear
176
+ * per-value conversion of a nested array; gzipping keeps a heavy file in
177
+ * memory + export budget. (Full rationale in the substrate's page notes.) */
178
+ export declare function encodePage<B>(records: PackedRecord<B>[]): EncodedPage;
179
+ /** Encode one record's `[tag, body]` pair as the JSON string it occupies
180
+ * inside a page body. Split from page assembly so a large write can
52
181
  * stringify each record as it buckets and release the parsed body before the
53
- * gzip loop, rather than holding the whole parsed model live through it (see
54
- * `writeRecordStoreConsuming` for why that release keeps the encode fast). */
55
- export declare function encodeRecordForPage<B>(key: string, tag: string, body: B): string;
56
- /** Assemble a page body from records already JSON-encoded by
57
- * `encodeRecordForPage`: wrap in a JSON array, gzip, hold as a latin1 string.
58
- * Byte-identical to `encodePageBody` over the same triples, so the wire shape
59
- * is unchanged; the split exists only so the writer frees each parsed body
182
+ * gzip loop: holding a whole parsed model live through that loop makes GC
183
+ * marking scale with the live set and turns the encode into a wall. */
184
+ export declare function encodeRecordForPage<B>(tag: string | null, body: B): string;
185
+ /** Assemble a page from bodies already JSON-encoded by `encodeRecordForPage`
186
+ * and the keys they belong to. Byte-identical to `encodePage` over the same
187
+ * records; the split exists only so the writer frees each parsed body
60
188
  * first. */
61
- export declare function encodePageBodyFromEncoded(encoded: string[]): string;
62
- /** Decode a page body back to its record triples. Returns [] for anything that
63
- * does not decompress to a JSON array, so a reader tolerates a corrupt or
64
- * stray page rather than throwing. */
65
- export declare function decodePageBody(body: unknown): PackedRecord<unknown>[];
66
- /** The page count for a record population: 0 (unpaged) at or below
67
- * `UNPAGED_MAX_RECORDS`, else ~`TARGET_PAGE_SIZE` records per page, capped at
68
- * `MAX_PAGES`. */
69
- export declare function pageCountFor(recordCount: number): number;
70
- /** The page key a record key lives in, for a store of `pageCount` pages. */
71
- export declare function pageKeyFor(key: string, pageCount: number): string;
189
+ export declare function encodePageFromEncoded(encoded: string[], keys: string[]): EncodedPage;
190
+ /** The record keys a page holds, from the page's own `keys` string. Reading
191
+ * this field alone is what makes learning a store's key locations cost the
192
+ * keys rather than every record body. */
193
+ export declare function decodePageKeys(keysField: unknown): string[];
194
+ /** A page's records, zipped back into `[key, tag, body]` triples from the two
195
+ * strings the page stores. A key with no body (or a body with no key) is
196
+ * dropped: the two arrays are written together and index each other, so a
197
+ * length mismatch means a page nothing can read as records. */
198
+ export declare function decodePageRecords(page: unknown): PackedRecord<unknown>[];
199
+ /** Mean JSON bytes of a record body, over a stride sample of at most
200
+ * `WIDTH_SAMPLE` records, which is what `recordsPerPage` sizes a page
201
+ * against. `bodyOf` returns the value the caller will pack as the triple's
202
+ * body: a codec's records carry a tag beside a body, and an app's map packs
203
+ * its whole stored value (`packWholeValue`), so each caller weighs the bytes
204
+ * its own pages will hold.
205
+ *
206
+ * Returns 0, which keeps `recordsPerPage`'s plain record-count rule, for an
207
+ * empty population and for a body `JSON.stringify` refuses. Such a body fails
208
+ * the write regardless (a paged write stringifies each record, and loro
209
+ * rejects a non-JSON value), so the catch is not there to rescue it; it is
210
+ * there so that a page-size ESTIMATE is never what decides a write fails, and
211
+ * the failure stays with the code that owns the body. */
212
+ export declare function sampleBodyBytes<T>(records: readonly T[], bodyOf: (record: T) => unknown): number;
213
+ /** Records one page holds: `TARGET_PAGE_SIZE`, or fewer when that many bodies
214
+ * would exceed `TARGET_PAGE_BYTES`. `avgBodyBytes` of 0 or absent (a caller
215
+ * with no estimate) keeps the count rule. */
216
+ export declare function recordsPerPage(avgBodyBytes?: number): number;
217
+ /** How many pages a record population occupies: 0 (unpaged) at or below
218
+ * `UNPAGED_MAX_RECORDS`, else one page per `recordsPerPage(avgBodyBytes)`
219
+ * records.
220
+ *
221
+ * A projection, not a decision the write path takes. Pages are filled in
222
+ * creation order and allocated as a write needs them, so this is what
223
+ * `scripts/bench` uses to price container counts and what the hash-page
224
+ * fixtures use as their bucket modulus. */
225
+ export declare function pageCountFor(recordCount: number, avgBodyBytes?: number): number;
72
226
  /** A stored container's tag (`type`), or null for any value not in the
73
227
  * `{ type, params }` shape, so a reader skips a corrupt entry. A page carries
74
228
  * `PAGE_TAG`. */
@@ -79,21 +233,132 @@ export declare function decodeRecordValue(value: unknown): {
79
233
  tag: string;
80
234
  body: unknown;
81
235
  } | null;
82
- /** Whether a `meta.page_count` value denotes a PAGED store. The writer sets
83
- * it to a positive count only when it pages, and never on an unpaged write,
84
- * so a positive finite number is the authoritative "is paged" signal. */
85
- export declare function isPagedByPageCount(pageCount: unknown): boolean;
236
+ /** Prefix of a RECORD PATCH: the fields one edit changed, applied over the
237
+ * record's page copy on read.
238
+ *
239
+ * Giving an edited record a container of its own instead restores the
240
+ * per-container cost paging exists to remove, so a store whose records are
241
+ * all eventually edited ends up slower and larger than one that never paged.
242
+ * A patch keeps the record packed and costs one entry per edit rather than a
243
+ * container per edited record.
244
+ *
245
+ * The key carries the writing peer and a counter. The peer is what lets the
246
+ * patches be FOLDED back into their pages later without a lock: a CRDT delete
247
+ * only defeats a CONCURRENT write to the same key, and no two writers can
248
+ * name the same key while the peer is in it, so a peer editing during
249
+ * another's fold writes a key that fold never saw. A writer that re-mints a
250
+ * key it folded away itself is ordered after its own delete, so that write is
251
+ * not the race either.
252
+ *
253
+ * The counter orders the patches on ONE record, and is minted one above every
254
+ * counter that record already carries. That is what makes the fold causal: a
255
+ * patch written by someone who had already merged an earlier patch sorts
256
+ * above it, whoever wrote which. A counter that instead counted the WRITER's
257
+ * edits would put a busy session's first-ever edit of a record above a later
258
+ * session's, and the later edit would vanish as it was made.
259
+ *
260
+ * The record key goes last because it may contain a colon; the peer and
261
+ * counter may not.
262
+ *
263
+ * Each reserved space differs from its siblings at the character after
264
+ * `RESERVED_KEY_PREFIX`, so `classifyStoreKey` decides between them with one
265
+ * test each and no ordering to get right. */
266
+ export declare const PATCH_PREFIX = "brass:q:";
267
+ export declare function patchKeyFor(recordKey: string, peer: string, counter: number): string;
268
+ export interface ParsedPatchKey {
269
+ recordKey: string;
270
+ peer: string;
271
+ counter: string;
272
+ }
273
+ /** What one entry of a paged map is, decided from its key alone.
274
+ *
275
+ * `record` is the ordinary case: every key outside the reserved space names a
276
+ * record, whatever its value holds. `unknown` is a reserved key this build
277
+ * does not recognise, which a reader skips rather than reporting as a record,
278
+ * so a store written by a later build degrades to missing entries instead of
279
+ * entries named after the store's own bookkeeping. */
280
+ export type StoreEntryKind = {
281
+ kind: 'record';
282
+ recordKey: string;
283
+ } | {
284
+ kind: 'page';
285
+ } | {
286
+ kind: 'overlay';
287
+ recordKey: string;
288
+ } | {
289
+ kind: 'tombstone';
290
+ recordKey: string;
291
+ } | {
292
+ kind: 'patch';
293
+ parsed: ParsedPatchKey;
294
+ } | {
295
+ kind: 'unknown';
296
+ };
297
+ /** Classify one key of a paged map. The single place the key spaces are told
298
+ * apart, so a keyed read, a whole-store read, a streaming read, and the page
299
+ * index cannot disagree about which entry is which. */
300
+ export declare function classifyStoreKey(key: string): StoreEntryKind;
301
+ /** The parts of a patch key, or null when `key` is not one.
302
+ *
303
+ * The record key runs to the end of the string and may be empty, which is a
304
+ * record key like any other: the two colons after the prefix delimit the peer
305
+ * and the counter, so the tail needs no length of its own to be
306
+ * unambiguous. Reading an empty tail as "not a patch" instead leaves the
307
+ * entry classified `unknown`, which every reader skips, so the edit stays in
308
+ * the document and reaches nobody. */
309
+ export declare function parsePatchKey(key: string): ParsedPatchKey | null;
310
+ /** One patch: the fields an edit changed, plus the pair that orders it. */
311
+ export interface RecordPatch {
312
+ peer: string;
313
+ counter: string;
314
+ fields: unknown;
315
+ }
316
+ /** Apply `patches` over `base`, in the order every reader must agree on: by
317
+ * counter, then by peer id. The counter is per record and minted one above
318
+ * what that record already carries, so a patch whose writer had merged an
319
+ * earlier one applies over it. Peer id breaks the tie between two writers who
320
+ * had not seen each other, so both reach the same answer. */
321
+ export declare function applyRecordPatches(base: unknown, patches: RecordPatch[]): unknown;
322
+ /**
323
+ * Apply patches to a record a streaming read carries both as its whole stored
324
+ * value and as the `(tag, body)` pair split out of it, and return all three
325
+ * after the fold.
326
+ *
327
+ * A patch names the record's own fields, which for a codec record are
328
+ * `type` and `params` together, so a reader that walks tag and body
329
+ * separately has to re-split the result. Both readers of a paged store
330
+ * (the flattening one and the streaming one) resolve patches through this
331
+ * module, so an edit cannot read one way in the editor and another in a
332
+ * re-emitted file. The fold runs over the whole value for the same reason the
333
+ * streaming read carries one: a field beyond the pair is a field of the record
334
+ * and an edit may name it.
335
+ */
336
+ export declare function applyPatchesToRecord(record: unknown, tag: string, body: unknown, patches: RecordPatch[]): {
337
+ tag: string;
338
+ body: unknown;
339
+ record: unknown;
340
+ };
341
+ /** Where a paged store keeps each record, reported as a flatten resolves it.
342
+ * A reader that has to locate records by key (a keyed read over pages filled
343
+ * in creation order) takes its index from the walk that decodes the pages
344
+ * anyway, rather than decoding them a second time of its own. */
345
+ export interface RecordLocationSink {
346
+ /** `recordKey` is packed in the body of page `pageKey`. */
347
+ page(recordKey: string, pageKey: string): void;
348
+ /** A container of its own answers for `recordKey`, outranking any page. */
349
+ contained(recordKey: string): void;
350
+ /** A tombstone removes `recordKey`, whatever a page still holds. */
351
+ removed(recordKey: string): void;
352
+ }
86
353
  /** Flatten a possibly-PAGED `instances` lookup to the flat
87
354
  * `key -> { type, params }` lookup a reader consumes. An unpaged lookup is
88
355
  * returned unchanged (no allocation). The single place paging is undone on the
89
356
  * read side.
90
357
  *
91
- * `paged` is the AUTHORITATIVE page state, read from `meta.page_count` by the
92
- * caller (`isPagedByPageCount`). Page interpretation MUST be gated on it, not
93
- * on whether some entry is `PAGE_TAG`-tagged: a genuine record whose codec tag
94
- * happens to equal `PAGE_TAG` in an UNPAGED store is a real record, not a
95
- * page. Treating it as a page decodes its plain body as `decodePageBody`
96
- * (which returns `[]` for a non-page body), silently DROPPING the record, and
97
- * a crafted page-shaped body could forge records into the lookup. */
98
- export declare function flattenPagedLookup(lookup: Record<string, unknown>, paged: boolean): Record<string, unknown>;
358
+ * `paged` is the AUTHORITATIVE page state, read from the root's own `meta`
359
+ * key by the caller (`pagedRootKey` / `isPagedMarker`). Page interpretation
360
+ * MUST be gated on it: in an UNPAGED store every key is a record key,
361
+ * including one that looks reserved, because nothing refused it before the
362
+ * store was written. */
363
+ export declare function flattenPagedLookup(lookup: Record<string, unknown>, paged: boolean, into?: RecordLocationSink): Record<string, unknown>;
99
364
  //# sourceMappingURL=record-paging.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"record-paging.d.ts","sourceRoot":"","sources":["../src/record-paging.ts"],"names":[],"mappings":"AAmEA;wEACwE;AACxE,eAAO,MAAM,SAAS,SAAS,CAAC;AAChC,eAAO,MAAM,UAAU,WAAW,CAAC;AAEnC,mEAAmE;AACnE,eAAO,MAAM,QAAQ,SAAS,CAAC;AAC/B,eAAO,MAAM,WAAW,cAAc,CAAC;AACvC,eAAO,MAAM,SAAS,mBAAmB,CAAC;AAE1C;;4CAE4C;AAC5C,eAAO,MAAM,cAAc,eAAe,CAAC;AAE3C;;;;;;;;gDAQgD;AAChD,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AAEjD;;;0DAG0D;AAC1D,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC;;;;;6DAK6D;AAC7D,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAK/D;AAED;0CAC0C;AAC1C,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAQ1C;;oBAEoB;AACpB,eAAO,MAAM,QAAQ,aAAa,CAAC;AAEnC;sCACsC;AACtC,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAElE;;;oFAGoF;AACpF,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,CAEpE;AAED;;;;+EAI+E;AAC/E,wBAAgB,mBAAmB,CAAC,CAAC,EACnC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,CAAC,GACN,MAAM,CAER;AAED;;;;aAIa;AACb,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAEnE;AAED;;uCAEuC;AACvC,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,YAAY,CAAC,OAAO,CAAC,EAAE,CASrE;AAED;;mBAEmB;AACnB,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAGxD;AAeD,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEjE;AAED;;kBAEkB;AAClB,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAI3D;AAED;kDACkD;AAClD,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,OAAO,GACb;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAMvC;AAED;;0EAE0E;AAC1E,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAE9D;AAED;;;;;;;;;;;sEAWsE;AACtE,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,KAAK,EAAE,OAAO,GACb,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAyBzB"}
1
+ {"version":3,"file":"record-paging.d.ts","sourceRoot":"","sources":["../src/record-paging.ts"],"names":[],"mappings":"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"}