@react-x11/components 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.
Files changed (219) hide show
  1. package/README.md +125 -4
  2. package/dist/charts/components.d.ts +3 -1
  3. package/dist/charts/components.d.ts.map +1 -1
  4. package/dist/charts/components.js +8 -4
  5. package/dist/charts/components.js.map +1 -1
  6. package/dist/charts/node.d.ts +13 -6
  7. package/dist/charts/node.d.ts.map +1 -1
  8. package/dist/charts/node.js +75 -24
  9. package/dist/charts/node.js.map +1 -1
  10. package/dist/charts/render.d.ts +4 -0
  11. package/dist/charts/render.d.ts.map +1 -1
  12. package/dist/charts/render.js +11 -6
  13. package/dist/charts/render.js.map +1 -1
  14. package/dist/code-editor/node.d.ts +47 -16
  15. package/dist/code-editor/node.d.ts.map +1 -1
  16. package/dist/code-editor/node.js +121 -51
  17. package/dist/code-editor/node.js.map +1 -1
  18. package/dist/flow/draw.d.ts +19 -1
  19. package/dist/flow/draw.d.ts.map +1 -1
  20. package/dist/flow/draw.js +64 -35
  21. package/dist/flow/draw.js.map +1 -1
  22. package/dist/flow/index.d.ts.map +1 -1
  23. package/dist/flow/index.js +24 -8
  24. package/dist/flow/index.js.map +1 -1
  25. package/dist/flow/model.d.ts +4 -2
  26. package/dist/flow/model.d.ts.map +1 -1
  27. package/dist/flow/model.js +4 -2
  28. package/dist/flow/model.js.map +1 -1
  29. package/dist/flow/node.d.ts +40 -9
  30. package/dist/flow/node.d.ts.map +1 -1
  31. package/dist/flow/node.js +142 -53
  32. package/dist/flow/node.js.map +1 -1
  33. package/dist/flow/types.d.ts +46 -21
  34. package/dist/flow/types.d.ts.map +1 -1
  35. package/dist/formula/index.d.ts +4 -2
  36. package/dist/formula/index.d.ts.map +1 -1
  37. package/dist/formula/index.js.map +1 -1
  38. package/dist/formula/node.d.ts +5 -1
  39. package/dist/formula/node.d.ts.map +1 -1
  40. package/dist/formula/node.js +19 -3
  41. package/dist/formula/node.js.map +1 -1
  42. package/dist/html/controls.d.ts +4 -1
  43. package/dist/html/controls.d.ts.map +1 -1
  44. package/dist/html/controls.js.map +1 -1
  45. package/dist/html/css/cascade.d.ts +11 -5
  46. package/dist/html/css/cascade.d.ts.map +1 -1
  47. package/dist/html/css/cascade.js +18 -8
  48. package/dist/html/css/cascade.js.map +1 -1
  49. package/dist/html/css/parse.d.ts +15 -10
  50. package/dist/html/css/parse.d.ts.map +1 -1
  51. package/dist/html/css/parse.js +41 -16
  52. package/dist/html/css/parse.js.map +1 -1
  53. package/dist/html/css/style.d.ts +3 -0
  54. package/dist/html/css/style.d.ts.map +1 -1
  55. package/dist/html/css/style.js +3 -1
  56. package/dist/html/css/style.js.map +1 -1
  57. package/dist/html/css/values.d.ts +14 -1
  58. package/dist/html/css/values.d.ts.map +1 -1
  59. package/dist/html/css/values.js +11 -8
  60. package/dist/html/css/values.js.map +1 -1
  61. package/dist/html/index.d.ts.map +1 -1
  62. package/dist/html/index.js +1 -0
  63. package/dist/html/index.js.map +1 -1
  64. package/dist/html/layout/boxes.d.ts +12 -5
  65. package/dist/html/layout/boxes.d.ts.map +1 -1
  66. package/dist/html/layout/boxes.js +7 -4
  67. package/dist/html/layout/boxes.js.map +1 -1
  68. package/dist/html/layout/inline.d.ts.map +1 -1
  69. package/dist/html/layout/inline.js +22 -1
  70. package/dist/html/layout/inline.js.map +1 -1
  71. package/dist/html/node.d.ts +22 -8
  72. package/dist/html/node.d.ts.map +1 -1
  73. package/dist/html/node.js +97 -15
  74. package/dist/html/node.js.map +1 -1
  75. package/dist/html/paint.d.ts +3 -0
  76. package/dist/html/paint.d.ts.map +1 -1
  77. package/dist/html/paint.js +2 -2
  78. package/dist/html/paint.js.map +1 -1
  79. package/dist/index.d.ts +7 -1
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +21 -0
  82. package/dist/index.js.map +1 -1
  83. package/dist/maps/clip.d.ts +43 -0
  84. package/dist/maps/clip.d.ts.map +1 -0
  85. package/dist/maps/clip.js +171 -0
  86. package/dist/maps/clip.js.map +1 -0
  87. package/dist/maps/gzip.d.ts +15 -0
  88. package/dist/maps/gzip.d.ts.map +1 -0
  89. package/dist/maps/gzip.js +56 -0
  90. package/dist/maps/gzip.js.map +1 -0
  91. package/dist/maps/index.d.ts +55 -0
  92. package/dist/maps/index.d.ts.map +1 -0
  93. package/dist/maps/index.js +141 -0
  94. package/dist/maps/index.js.map +1 -0
  95. package/dist/maps/labels.d.ts +123 -0
  96. package/dist/maps/labels.d.ts.map +1 -0
  97. package/dist/maps/labels.js +323 -0
  98. package/dist/maps/labels.js.map +1 -0
  99. package/dist/maps/mvt.d.ts +244 -0
  100. package/dist/maps/mvt.d.ts.map +1 -0
  101. package/dist/maps/mvt.js +820 -0
  102. package/dist/maps/mvt.js.map +1 -0
  103. package/dist/maps/node.d.ts +278 -0
  104. package/dist/maps/node.d.ts.map +1 -0
  105. package/dist/maps/node.js +1459 -0
  106. package/dist/maps/node.js.map +1 -0
  107. package/dist/maps/overlay.d.ts +157 -0
  108. package/dist/maps/overlay.d.ts.map +1 -0
  109. package/dist/maps/overlay.js +472 -0
  110. package/dist/maps/overlay.js.map +1 -0
  111. package/dist/maps/paint.d.ts +226 -0
  112. package/dist/maps/paint.d.ts.map +1 -0
  113. package/dist/maps/paint.js +705 -0
  114. package/dist/maps/paint.js.map +1 -0
  115. package/dist/maps/proj.d.ts +281 -0
  116. package/dist/maps/proj.d.ts.map +1 -0
  117. package/dist/maps/proj.js +451 -0
  118. package/dist/maps/proj.js.map +1 -0
  119. package/dist/maps/sources.d.ts +256 -0
  120. package/dist/maps/sources.d.ts.map +1 -0
  121. package/dist/maps/sources.js +210 -0
  122. package/dist/maps/sources.js.map +1 -0
  123. package/dist/maps/style.d.ts +141 -0
  124. package/dist/maps/style.d.ts.map +1 -0
  125. package/dist/maps/style.js +147 -0
  126. package/dist/maps/style.js.map +1 -0
  127. package/dist/maps/styles.d.ts +82 -0
  128. package/dist/maps/styles.d.ts.map +1 -0
  129. package/dist/maps/styles.js +898 -0
  130. package/dist/maps/styles.js.map +1 -0
  131. package/dist/maps/tiles.d.ts +283 -0
  132. package/dist/maps/tiles.d.ts.map +1 -0
  133. package/dist/maps/tiles.js +469 -0
  134. package/dist/maps/tiles.js.map +1 -0
  135. package/dist/maps/types.d.ts +244 -0
  136. package/dist/maps/types.d.ts.map +1 -0
  137. package/dist/maps/types.js +2 -0
  138. package/dist/maps/types.js.map +1 -0
  139. package/dist/reorder/hx.d.ts +18 -0
  140. package/dist/reorder/hx.d.ts.map +1 -0
  141. package/dist/reorder/hx.js +10 -0
  142. package/dist/reorder/hx.js.map +1 -0
  143. package/dist/reorder/index.d.ts +314 -0
  144. package/dist/reorder/index.d.ts.map +1 -0
  145. package/dist/reorder/index.js +1539 -0
  146. package/dist/reorder/index.js.map +1 -0
  147. package/dist/reorder/model.d.ts +107 -0
  148. package/dist/reorder/model.d.ts.map +1 -0
  149. package/dist/reorder/model.js +162 -0
  150. package/dist/reorder/model.js.map +1 -0
  151. package/dist/richtext/node.d.ts +15 -5
  152. package/dist/richtext/node.d.ts.map +1 -1
  153. package/dist/richtext/node.js +40 -11
  154. package/dist/richtext/node.js.map +1 -1
  155. package/dist/richtext/runs.d.ts +35 -13
  156. package/dist/richtext/runs.d.ts.map +1 -1
  157. package/dist/richtext/runs.js +83 -27
  158. package/dist/richtext/runs.js.map +1 -1
  159. package/dist/tabs/index.d.ts +17 -0
  160. package/dist/tabs/index.d.ts.map +1 -1
  161. package/dist/tabs/index.js +849 -186
  162. package/dist/tabs/index.js.map +1 -1
  163. package/dist/terminal/vt/fonts.d.ts +45 -15
  164. package/dist/terminal/vt/fonts.d.ts.map +1 -1
  165. package/dist/terminal/vt/fonts.js +89 -19
  166. package/dist/terminal/vt/fonts.js.map +1 -1
  167. package/dist/terminal/vt/node.d.ts +42 -0
  168. package/dist/terminal/vt/node.d.ts.map +1 -1
  169. package/dist/terminal/vt/node.js +95 -11
  170. package/dist/terminal/vt/node.js.map +1 -1
  171. package/dist/terminal/vt/renderer.d.ts.map +1 -1
  172. package/dist/terminal/vt/renderer.js +13 -3
  173. package/dist/terminal/vt/renderer.js.map +1 -1
  174. package/package.json +13 -3
  175. package/src/charts/components.ts +14 -7
  176. package/src/charts/node.ts +81 -26
  177. package/src/charts/render.ts +16 -6
  178. package/src/code-editor/node.ts +147 -53
  179. package/src/flow/draw.ts +104 -35
  180. package/src/flow/index.ts +33 -13
  181. package/src/flow/model.ts +4 -2
  182. package/src/flow/node.ts +158 -59
  183. package/src/flow/types.ts +46 -21
  184. package/src/formula/index.ts +4 -2
  185. package/src/formula/node.ts +24 -5
  186. package/src/html/controls.ts +4 -1
  187. package/src/html/css/cascade.ts +18 -7
  188. package/src/html/css/parse.ts +46 -21
  189. package/src/html/css/style.ts +6 -1
  190. package/src/html/css/values.ts +25 -9
  191. package/src/html/index.ts +1 -0
  192. package/src/html/layout/boxes.ts +19 -9
  193. package/src/html/layout/inline.ts +23 -1
  194. package/src/html/node.ts +110 -17
  195. package/src/html/paint.ts +17 -2
  196. package/src/index.ts +98 -0
  197. package/src/maps/clip.ts +184 -0
  198. package/src/maps/gzip.ts +66 -0
  199. package/src/maps/index.ts +316 -0
  200. package/src/maps/labels.ts +442 -0
  201. package/src/maps/mvt.ts +857 -0
  202. package/src/maps/node.ts +1819 -0
  203. package/src/maps/overlay.ts +639 -0
  204. package/src/maps/paint.ts +978 -0
  205. package/src/maps/proj.ts +641 -0
  206. package/src/maps/sources.ts +425 -0
  207. package/src/maps/style.ts +309 -0
  208. package/src/maps/styles.ts +1040 -0
  209. package/src/maps/tiles.ts +676 -0
  210. package/src/maps/types.ts +260 -0
  211. package/src/reorder/hx.ts +31 -0
  212. package/src/reorder/index.ts +2223 -0
  213. package/src/reorder/model.ts +232 -0
  214. package/src/richtext/node.ts +49 -14
  215. package/src/richtext/runs.ts +115 -30
  216. package/src/tabs/index.ts +1040 -204
  217. package/src/terminal/vt/fonts.ts +128 -36
  218. package/src/terminal/vt/node.ts +105 -12
  219. package/src/terminal/vt/renderer.ts +13 -3
@@ -0,0 +1,857 @@
1
+ // Mapbox Vector Tiles, decoded.
2
+ //
3
+ // MVT 2.1 is the format. Not one format among several: it is what Mapbox,
4
+ // MapTiler, Protomaps, Esri, TomTom, Azure Maps and OpenStreetMap's own
5
+ // tile server all serve, so a decoder for it is a decoder for the whole
6
+ // open half of the industry. (`docs/prd-maps.md` surveys who serves what,
7
+ // and which providers are closed.)
8
+ //
9
+ // Written out rather than taken from `@mapbox/vector-tile` + `pbf`, and the
10
+ // reason is not weight — those two are small — but the hot loop. Both
11
+ // libraries hand back a fresh array-of-arrays of `{x, y}` objects per
12
+ // feature, which for a dense city tile is on the order of 100,000 object
13
+ // allocations that exist for one rasterization and are then garbage. The
14
+ // decoder here reads geometry into a **caller-owned buffer of typed
15
+ // arrays** ({@link readGeometry}), so a tile is rasterized with no
16
+ // allocation at all in the per-vertex path, and the convenience shape
17
+ // ({@link VectorTileFeature.geometry}) is a wrapper over the same code for
18
+ // the callers that want one — hit tests, a GeoJSON export.
19
+ //
20
+ // Everything else is a straight reading of the specification, and the parts
21
+ // that are easy to get subtly wrong carry the spec's own words:
22
+ //
23
+ // - a `CommandInteger` is `(id & 0x7) | (count << 3)`;
24
+ // - `MoveTo` is 1 and `LineTo` is 2, each taking two parameters, and
25
+ // `ClosePath` is 7 and takes none;
26
+ // - a `ParameterInteger` is zigzag: `(n >> 1) ^ -(n & 1)`;
27
+ // - coordinates are **deltas from a cursor**, which starts at (0, 0) and
28
+ // persists across commands *and across the parts of one feature*;
29
+ // - a polygon's exterior ring is the one with **positive** area by the
30
+ // surveyor's formula in tile coordinates, where y increases downward —
31
+ // so an exterior ring is clockwise on screen and an interior one is
32
+ // not;
33
+ // - a ring's last point is not repeated before `ClosePath`;
34
+ // - `extent` defaults to 4096, and geometry **may leave** `[0, extent]`,
35
+ // because producers carry a buffer past the edge so a road's join is
36
+ // right where two tiles meet.
37
+
38
+ /** What a feature's geometry is. The numbers are the specification's. */
39
+ export const enum GeomType {
40
+ Unknown = 0,
41
+ Point = 1,
42
+ LineString = 2,
43
+ Polygon = 3,
44
+ }
45
+
46
+ /** A tag value. MVT's seven value types collapse to these three in
47
+ * JavaScript; which of the four numeric ones it was is not information a
48
+ * style can use. */
49
+ export type FeatureValue = string | number | boolean;
50
+
51
+ /**
52
+ * Decoded geometry, as flat typed arrays.
53
+ *
54
+ * `coords` is `[x0, y0, x1, y1, …]` in **tile-local integers** — the space
55
+ * `extent` divides, not pixels and not degrees. `starts` indexes it in
56
+ * *points*: part `i` covers `coords[2 * starts[i] .. 2 * starts[i + 1])`,
57
+ * and `starts` has `parts + 1` entries so the last part needs no special
58
+ * case.
59
+ *
60
+ * `areas[i]` is part `i`'s signed area by the surveyor's formula, and is
61
+ * meaningful for {@link GeomType.Polygon} only: positive is an exterior
62
+ * ring, negative an interior one. It is filled during the same pass that
63
+ * reads the vertices, because a second pass over 4,000 points to learn one
64
+ * sign is the sort of thing that turns a 6 ms tile into an 11 ms one.
65
+ */
66
+ export interface GeometryData {
67
+ coords: Int32Array;
68
+ starts: Uint32Array;
69
+ areas: Float64Array;
70
+ parts: number;
71
+ points: number;
72
+ }
73
+
74
+ /**
75
+ * A growable {@link GeometryData} a caller keeps between features.
76
+ *
77
+ * The rasterizer allocates one of these per tile — or per element, since it
78
+ * is only scratch — and every feature reads into it. Capacity ratchets up
79
+ * to the largest feature seen and then stops moving, which for a tile means
80
+ * two or three grows in total rather than two per feature.
81
+ */
82
+ export class GeometryBuffer implements GeometryData {
83
+ coords: Int32Array;
84
+ starts: Uint32Array;
85
+ areas: Float64Array;
86
+ parts = 0;
87
+ points = 0;
88
+
89
+ constructor(points = 1024, parts = 32) {
90
+ this.coords = new Int32Array(points * 2);
91
+ this.starts = new Uint32Array(parts + 1);
92
+ this.areas = new Float64Array(parts);
93
+ this.partBounds = new Float64Array(parts * 4);
94
+ }
95
+
96
+ /** Room for `points` vertices, keeping what is already written. */
97
+ reserveCoords(points: number): void {
98
+ if (this.coords.length >= points * 2) return;
99
+ let size = this.coords.length || 2;
100
+ while (size < points * 2) size *= 2;
101
+ const next = new Int32Array(size);
102
+ next.set(this.coords);
103
+ this.coords = next;
104
+ }
105
+
106
+ /**
107
+ * Per-part bounding boxes, four numbers each: `minX, minY, maxX, maxY`.
108
+ *
109
+ * The feature-wide box is not enough, and the case that proves it is the
110
+ * one that dominates a low-zoom frame: OSM's `land` layer at zoom 8 is a
111
+ * **single feature** whose geometry is thousands of separate rings, most
112
+ * of them a fraction of a pixel across. Culled as one feature it is never
113
+ * culled; culled per part, almost all of it goes.
114
+ */
115
+ partBounds: Float64Array;
116
+
117
+ /** Room for `parts` parts, keeping what is already written. */
118
+ reserveParts(parts: number): void {
119
+ if (this.areas.length >= parts) return;
120
+ let size = this.areas.length || 1;
121
+ while (size < parts) size *= 2;
122
+ const starts = new Uint32Array(size + 1);
123
+ starts.set(this.starts);
124
+ this.starts = starts;
125
+ const areas = new Float64Array(size);
126
+ areas.set(this.areas);
127
+ this.areas = areas;
128
+ const bounds = new Float64Array(size * 4);
129
+ bounds.set(this.partBounds);
130
+ this.partBounds = bounds;
131
+ }
132
+
133
+ /** The bounding box of everything written, in tile-local integers.
134
+ * Free here — the read already touches every vertex — and what lets a
135
+ * painter skip a building that is a third of a pixel across, which at a
136
+ * country-wide zoom is most of what a tile contains. */
137
+ minX = 0;
138
+ minY = 0;
139
+ maxX = 0;
140
+ maxY = 0;
141
+
142
+ reset(): void {
143
+ this.parts = 0;
144
+ this.points = 0;
145
+ this.starts[0] = 0;
146
+ this.minX = Infinity;
147
+ this.minY = Infinity;
148
+ this.maxX = -Infinity;
149
+ this.maxY = -Infinity;
150
+ }
151
+ }
152
+
153
+ // --- the protobuf wire format ----------------------------------------------
154
+ //
155
+ // A reader rather than a dependency: MVT uses four of protobuf's features
156
+ // (varints, zigzag, length-delimited submessages, packed repeated fields)
157
+ // and none of the rest, so the whole of what a `pbf` dependency would bring
158
+ // is below, at a size where it can be read in one sitting.
159
+
160
+ const WIRE_VARINT = 0;
161
+ const WIRE_FIXED64 = 1;
162
+ const WIRE_BYTES = 2;
163
+ const WIRE_FIXED32 = 5;
164
+
165
+ /** `TextDecoder` through `globalThis`: `src/` compiles with `types: []` and
166
+ * no DOM lib, so naming the global directly would make this package depend
167
+ * on one of them. It exists on every runtime this can run on. */
168
+ const decoderOf = (): { decode(input: Uint8Array): string } | null => {
169
+ const g = globalThis as {
170
+ TextDecoder?: new (label?: string) => { decode(input: Uint8Array): string };
171
+ };
172
+ return g.TextDecoder ? new g.TextDecoder('utf-8') : null;
173
+ };
174
+ let utf8: { decode(input: Uint8Array): string } | null | undefined;
175
+
176
+ /** A malformed tile. Thrown rather than tolerated: a truncated protobuf is
177
+ * a transport bug, and silently rendering the half of a tile that parsed
178
+ * is how a map ends up missing a river nobody can explain. */
179
+ export class MvtError extends Error {
180
+ constructor(message: string) {
181
+ super(message);
182
+ this.name = 'MvtError';
183
+ }
184
+ }
185
+
186
+ class Reader {
187
+ buf: Uint8Array;
188
+ pos: number;
189
+ end: number;
190
+
191
+ constructor(buf: Uint8Array, pos = 0, end = buf.length) {
192
+ this.buf = buf;
193
+ this.pos = pos;
194
+ this.end = end;
195
+ }
196
+
197
+ /**
198
+ * A varint, as a JavaScript number.
199
+ *
200
+ * Unrolled to seven bytes — 2^49, which covers every field MVT puts in a
201
+ * varint except a feature `id` that used the full 64 bits. Past that the
202
+ * slow path accumulates in floating point and loses the low bits above
203
+ * 2^53; the alternative is a BigInt per id, which costs every tile
204
+ * something to be exact about a case that does not occur in tiles cut
205
+ * from OSM (whose largest ids are ~2^33).
206
+ */
207
+ varint(): number {
208
+ const b = this.buf;
209
+ let p = this.pos;
210
+ let byte = b[p++];
211
+ let value = byte & 0x7f;
212
+ if (byte < 0x80) {
213
+ this.pos = p;
214
+ return value;
215
+ }
216
+ byte = b[p++];
217
+ value |= (byte & 0x7f) << 7;
218
+ if (byte < 0x80) {
219
+ this.pos = p;
220
+ return value;
221
+ }
222
+ byte = b[p++];
223
+ value |= (byte & 0x7f) << 14;
224
+ if (byte < 0x80) {
225
+ this.pos = p;
226
+ return value;
227
+ }
228
+ byte = b[p++];
229
+ value |= (byte & 0x7f) << 21;
230
+ if (byte < 0x80) {
231
+ this.pos = p;
232
+ return value >>> 0;
233
+ }
234
+ // Four bytes carry bits 0-27, so from here the shift would leave int32
235
+ // and the rest has to be *added* in floating point: `1 << 35` is 8, not
236
+ // 34359738368. Above 2^53 the low bits are gone — see the note above.
237
+ let out = value >>> 0;
238
+ for (let shift = 28; shift < 64; shift += 7) {
239
+ byte = b[p++];
240
+ if (byte === undefined) throw new MvtError('varint ran past the end');
241
+ out += (byte & 0x7f) * Math.pow(2, shift);
242
+ if (byte < 0x80) break;
243
+ }
244
+ this.pos = p;
245
+ return out;
246
+ }
247
+
248
+ /** A zigzag-encoded signed varint. */
249
+ svarint(): number {
250
+ const n = this.varint();
251
+ return (n >>> 1) ^ -(n & 1);
252
+ }
253
+
254
+ skip(wire: number): void {
255
+ if (wire === WIRE_VARINT) {
256
+ while (this.buf[this.pos++] >= 0x80) {
257
+ if (this.pos > this.end) throw new MvtError('varint ran past the end');
258
+ }
259
+ } else if (wire === WIRE_BYTES) {
260
+ this.pos += this.varint();
261
+ } else if (wire === WIRE_FIXED32) {
262
+ this.pos += 4;
263
+ } else if (wire === WIRE_FIXED64) {
264
+ this.pos += 8;
265
+ } else {
266
+ throw new MvtError(`unknown wire type ${wire}`);
267
+ }
268
+ if (this.pos > this.end) throw new MvtError('field ran past the end');
269
+ }
270
+
271
+ /** One view over the whole buffer rather than one per read: only a
272
+ * layer's `values` use these, but a dense tile has thousands of them. */
273
+ private _view: DataView | null = null;
274
+ private view(): DataView {
275
+ if (!this._view) {
276
+ this._view = new DataView(
277
+ this.buf.buffer,
278
+ this.buf.byteOffset,
279
+ this.buf.byteLength,
280
+ );
281
+ }
282
+ return this._view;
283
+ }
284
+
285
+ float(): number {
286
+ const at = this.pos;
287
+ this.pos += 4;
288
+ return this.view().getFloat32(at, true);
289
+ }
290
+
291
+ double(): number {
292
+ const at = this.pos;
293
+ this.pos += 8;
294
+ return this.view().getFloat64(at, true);
295
+ }
296
+
297
+ string(): string {
298
+ const length = this.varint();
299
+ const start = this.pos;
300
+ this.pos += length;
301
+ if (this.pos > this.end) throw new MvtError('string ran past the end');
302
+ const buf = this.buf;
303
+ // Tag keys and most tag values are ASCII, and building those directly
304
+ // is several times quicker than a `TextDecoder` call whose fixed cost
305
+ // dominates a nine-byte string. Anything with a high bit set — a place
306
+ // name, which is most of them outside the anglosphere — goes the
307
+ // correct way.
308
+ let ascii = true;
309
+ for (let i = start; i < this.pos; i++) {
310
+ if (buf[i] >= 0x80) {
311
+ ascii = false;
312
+ break;
313
+ }
314
+ }
315
+ if (ascii) {
316
+ let out = '';
317
+ for (let i = start; i < this.pos; i += 1024) {
318
+ out += String.fromCharCode.apply(
319
+ null,
320
+ buf.subarray(i, Math.min(i + 1024, this.pos)) as unknown as number[],
321
+ );
322
+ }
323
+ return out;
324
+ }
325
+ if (utf8 === undefined) utf8 = decoderOf();
326
+ if (utf8) return utf8.decode(buf.subarray(start, this.pos));
327
+ return decodeUtf8(buf, start, this.pos);
328
+ }
329
+ }
330
+
331
+ /** The fallback for a runtime with no `TextDecoder`. Correct, not quick —
332
+ * and never reached on Node, Bun or a browser. */
333
+ function decodeUtf8(buf: Uint8Array, start: number, end: number): string {
334
+ let out = '';
335
+ let i = start;
336
+ while (i < end) {
337
+ const b0 = buf[i++];
338
+ if (b0 < 0x80) {
339
+ out += String.fromCharCode(b0);
340
+ } else if (b0 < 0xe0) {
341
+ out += String.fromCharCode(((b0 & 0x1f) << 6) | (buf[i++] & 0x3f));
342
+ } else if (b0 < 0xf0) {
343
+ out += String.fromCharCode(
344
+ ((b0 & 0x0f) << 12) | ((buf[i++] & 0x3f) << 6) | (buf[i++] & 0x3f),
345
+ );
346
+ } else {
347
+ const cp =
348
+ ((b0 & 0x07) << 18) |
349
+ ((buf[i++] & 0x3f) << 12) |
350
+ ((buf[i++] & 0x3f) << 6) |
351
+ (buf[i++] & 0x3f);
352
+ out += String.fromCodePoint(cp);
353
+ }
354
+ }
355
+ return out;
356
+ }
357
+
358
+ // --- the schema ------------------------------------------------------------
359
+
360
+ /** A decoded tile: its layers, by name. Layers are ordered as the tile
361
+ * ordered them, which is the order a producer means them to be drawn in
362
+ * when a style says nothing. */
363
+ export interface VectorTile {
364
+ layers: Map<string, VectorTileLayer>;
365
+ /** Layer names in tile order. */
366
+ order: string[];
367
+ }
368
+
369
+ /**
370
+ * One layer of a tile.
371
+ *
372
+ * `keys` and `values` are decoded eagerly because MVT **deduplicates**
373
+ * them — a tile with 12,000 features has perhaps 40 keys and 2,000 distinct
374
+ * values, so decoding them once is cheaper than decoding a feature's tags
375
+ * lazily would be. Features are the other way round: their byte ranges are
376
+ * collected on construction and nothing inside one is touched until it is
377
+ * asked for.
378
+ */
379
+ export class VectorTileLayer {
380
+ readonly name: string;
381
+ readonly version: number;
382
+ /** Tile-local coordinate units across the tile's square. 4096 unless the
383
+ * producer said otherwise. */
384
+ readonly extent: number;
385
+ readonly keys: string[];
386
+ readonly values: FeatureValue[];
387
+ /** How many features. */
388
+ readonly length: number;
389
+
390
+ private readonly _buf: Uint8Array;
391
+ /** Byte offset of feature `i`, with a sentinel: `[starts[i], starts[i+1])`
392
+ * is not a valid span (features are not contiguous), so both ends are
393
+ * stored. */
394
+ private readonly _at: Uint32Array;
395
+ private readonly _to: Uint32Array;
396
+ private _keyIndex: Map<string, number> | null = null;
397
+
398
+ constructor(reader: Reader, end: number) {
399
+ this._buf = reader.buf;
400
+ let name = '';
401
+ let version = 1;
402
+ let extent = 4096;
403
+ const keys: string[] = [];
404
+ const values: FeatureValue[] = [];
405
+ const at: number[] = [];
406
+ const to: number[] = [];
407
+ while (reader.pos < end) {
408
+ const tag = reader.varint();
409
+ const field = tag >> 3;
410
+ const wire = tag & 0x7;
411
+ if (field === 1 && wire === WIRE_BYTES) {
412
+ name = reader.string();
413
+ } else if (field === 15 && wire === WIRE_VARINT) {
414
+ version = reader.varint();
415
+ } else if (field === 5 && wire === WIRE_VARINT) {
416
+ extent = reader.varint();
417
+ } else if (field === 3 && wire === WIRE_BYTES) {
418
+ keys.push(reader.string());
419
+ } else if (field === 4 && wire === WIRE_BYTES) {
420
+ const length = reader.varint();
421
+ // `readValue` leaves `pos` at the value's end, so nothing here has
422
+ // to move it.
423
+ values.push(readValue(reader, reader.pos + length));
424
+ } else if (field === 2 && wire === WIRE_BYTES) {
425
+ const length = reader.varint();
426
+ at.push(reader.pos);
427
+ reader.pos += length;
428
+ to.push(reader.pos);
429
+ } else {
430
+ reader.skip(wire);
431
+ }
432
+ if (reader.pos > end) throw new MvtError(`layer "${name}" overran`);
433
+ }
434
+ if (extent <= 0) throw new MvtError(`layer "${name}" has extent ${extent}`);
435
+ this.name = name;
436
+ this.version = version;
437
+ this.extent = extent;
438
+ this.keys = keys;
439
+ this.values = values;
440
+ this._at = new Uint32Array(at);
441
+ this._to = new Uint32Array(to);
442
+ this.length = at.length;
443
+ }
444
+
445
+ /** Which tag index a key name has, or -1. Built on first use, because a
446
+ * style usually reads two or three of a layer's forty keys. */
447
+ keyIndex(key: string): number {
448
+ if (!this._keyIndex) {
449
+ this._keyIndex = new Map();
450
+ for (let i = 0; i < this.keys.length; i++) {
451
+ this._keyIndex.set(this.keys[i], i);
452
+ }
453
+ }
454
+ return this._keyIndex.get(key) ?? -1;
455
+ }
456
+
457
+ /** Feature `i`, as a fresh cursor. */
458
+ feature(i: number): FeatureCursor {
459
+ const cursor = new FeatureCursor();
460
+ this.seek(i, cursor);
461
+ return cursor;
462
+ }
463
+
464
+ /**
465
+ * Point an existing cursor at feature `i`.
466
+ *
467
+ * The whole reason the cursor is a separate object: a rasterizer walks
468
+ * twelve thousand features and wants none of them to be an allocation.
469
+ */
470
+ seek(i: number, cursor: FeatureCursor): FeatureCursor {
471
+ if (i < 0 || i >= this.length) {
472
+ throw new MvtError(`feature ${i} of ${this.length}`);
473
+ }
474
+ cursor._bind(this, this._buf, this._at[i], this._to[i]);
475
+ return cursor;
476
+ }
477
+
478
+ /** Every feature, as fresh cursors. Convenience — the allocation-free
479
+ * path is {@link seek}. */
480
+ *features(): Generator<FeatureCursor> {
481
+ const cursor = new FeatureCursor();
482
+ for (let i = 0; i < this.length; i++) yield this.seek(i, cursor);
483
+ }
484
+ }
485
+
486
+ function readValue(reader: Reader, end: number): FeatureValue {
487
+ let out: FeatureValue = '';
488
+ while (reader.pos < end) {
489
+ const tag = reader.varint();
490
+ const field = tag >> 3;
491
+ const wire = tag & 0x7;
492
+ if (field === 1 && wire === WIRE_BYTES) out = reader.string();
493
+ else if (field === 2 && wire === WIRE_FIXED32) out = reader.float();
494
+ else if (field === 3 && wire === WIRE_FIXED64) out = reader.double();
495
+ else if (field === 4 && wire === WIRE_VARINT) out = reader.varint();
496
+ else if (field === 5 && wire === WIRE_VARINT) out = reader.varint();
497
+ else if (field === 6 && wire === WIRE_VARINT) out = reader.svarint();
498
+ else if (field === 7 && wire === WIRE_VARINT) out = reader.varint() !== 0;
499
+ else reader.skip(wire);
500
+ }
501
+ reader.pos = end;
502
+ return out;
503
+ }
504
+
505
+ /** A position on a cursor's feature. */
506
+ export interface TilePoint {
507
+ x: number;
508
+ y: number;
509
+ }
510
+
511
+ /**
512
+ * One feature, as a movable cursor over the tile's bytes.
513
+ *
514
+ * Nothing inside the feature is decoded until it is asked for, and a
515
+ * cursor's own state is four numbers, so `layer.seek(i, cursor)` in a loop
516
+ * allocates nothing.
517
+ */
518
+ export class FeatureCursor {
519
+ /** The feature's `id`, or undefined when it carries none. Ids above 2^53
520
+ * lose their low bits — see {@link Reader.varint}. */
521
+ id: number | undefined = undefined;
522
+ type: GeomType = GeomType.Unknown;
523
+ layer: VectorTileLayer | null = null;
524
+
525
+ private _buf: Uint8Array = EMPTY;
526
+ private _tagAt = 0;
527
+ private _tagTo = 0;
528
+ private _geomAt = 0;
529
+ private _geomTo = 0;
530
+
531
+ /**
532
+ * The feature's tags as key/value **indices**, decoded on the first
533
+ * {@link get} and reused for every one after it.
534
+ *
535
+ * The reason this exists rather than a scan of the bytes per call: a
536
+ * style asks the same feature about the same two or three keys once per
537
+ * style layer over that source layer, and a road network is fourteen
538
+ * such layers (seven classes, a casing pass and a fill pass). Re-decoding
539
+ * a feature's varints fourteen times over is, on the corpus, most of the
540
+ * cost of a zoom-12 tile. Decoded once, a lookup is a scan over a dozen
541
+ * integers.
542
+ */
543
+ private _tagKeys = EMPTY_INTS;
544
+ private _tagValues = EMPTY_INTS;
545
+ private _tagCount = -1;
546
+
547
+ /** @internal */
548
+ _bind(layer: VectorTileLayer, buf: Uint8Array, at: number, to: number): void {
549
+ this.layer = layer;
550
+ this._buf = buf;
551
+ this.id = undefined;
552
+ this.type = GeomType.Unknown;
553
+ this._tagAt = 0;
554
+ this._tagTo = 0;
555
+ this._geomAt = 0;
556
+ this._geomTo = 0;
557
+ this._tagCount = -1;
558
+ const reader = new Reader(buf, at, to);
559
+ while (reader.pos < to) {
560
+ const tag = reader.varint();
561
+ const field = tag >> 3;
562
+ const wire = tag & 0x7;
563
+ if (field === 1 && wire === WIRE_VARINT) {
564
+ this.id = reader.varint();
565
+ } else if (field === 3 && wire === WIRE_VARINT) {
566
+ this.type = reader.varint() as GeomType;
567
+ } else if (field === 2 && wire === WIRE_BYTES) {
568
+ const length = reader.varint();
569
+ this._tagAt = reader.pos;
570
+ this._tagTo = reader.pos + length;
571
+ reader.pos = this._tagTo;
572
+ } else if (field === 4 && wire === WIRE_BYTES) {
573
+ const length = reader.varint();
574
+ this._geomAt = reader.pos;
575
+ this._geomTo = reader.pos + length;
576
+ reader.pos = this._geomTo;
577
+ } else {
578
+ reader.skip(wire);
579
+ }
580
+ if (reader.pos > to) throw new MvtError('feature overran');
581
+ }
582
+ }
583
+
584
+ /** Tile-local units across this feature's tile. */
585
+ get extent(): number {
586
+ return this.layer?.extent ?? 4096;
587
+ }
588
+
589
+ /**
590
+ * One tag, by key name.
591
+ *
592
+ * A linear scan of the tag pairs rather than a materialized object: a
593
+ * feature has five to fifteen tags and a style filter reads one or two of
594
+ * them, so the scan is quicker than the object it would have built and
595
+ * costs nothing to throw away.
596
+ */
597
+ get(key: string): FeatureValue | undefined {
598
+ const layer = this.layer;
599
+ if (!layer || this._tagTo === this._tagAt) return undefined;
600
+ const wanted = layer.keyIndex(key);
601
+ if (wanted < 0) return undefined;
602
+ if (this._tagCount < 0) this._loadTags();
603
+ const keys = this._tagKeys;
604
+ for (let i = 0; i < this._tagCount; i++) {
605
+ if (keys[i] === wanted) return layer.values[this._tagValues[i]];
606
+ }
607
+ return undefined;
608
+ }
609
+
610
+ /** Decode the tag pairs into the cursor's own arrays. Grown, never
611
+ * shrunk, so a cursor walking a layer settles on one allocation. */
612
+ private _loadTags(): void {
613
+ const reader = new Reader(this._buf, this._tagAt, this._tagTo);
614
+ let count = 0;
615
+ let keys = this._tagKeys;
616
+ let values = this._tagValues;
617
+ while (reader.pos < this._tagTo) {
618
+ const k = reader.varint();
619
+ if (reader.pos >= this._tagTo) break; // a key with no value: malformed
620
+ const v = reader.varint();
621
+ if (count >= keys.length) {
622
+ const size = Math.max(8, keys.length * 2);
623
+ const nextKeys = new Int32Array(size);
624
+ nextKeys.set(keys);
625
+ keys = nextKeys;
626
+ const nextValues = new Int32Array(size);
627
+ nextValues.set(values);
628
+ values = nextValues;
629
+ }
630
+ keys[count] = k;
631
+ values[count] = v;
632
+ count++;
633
+ }
634
+ this._tagKeys = keys;
635
+ this._tagValues = values;
636
+ this._tagCount = count;
637
+ }
638
+
639
+ has(key: string): boolean {
640
+ return this.get(key) !== undefined;
641
+ }
642
+
643
+ /** Every tag, as an object. For a hover readout or a click payload — not
644
+ * for a filter, which should use {@link get}. */
645
+ properties(): Record<string, FeatureValue> {
646
+ const layer = this.layer;
647
+ const out: Record<string, FeatureValue> = {};
648
+ if (!layer || this._tagTo === this._tagAt) return out;
649
+ if (this._tagCount < 0) this._loadTags();
650
+ for (let i = 0; i < this._tagCount; i++) {
651
+ const key = layer.keys[this._tagKeys[i]];
652
+ const index = this._tagValues[i];
653
+ if (key !== undefined && index < layer.values.length) {
654
+ out[key] = layer.values[index];
655
+ }
656
+ }
657
+ return out;
658
+ }
659
+
660
+ /**
661
+ * Read this feature's geometry into `into`, in tile-local integers.
662
+ *
663
+ * The one hot loop in the decoder. Everything about its shape is about
664
+ * not allocating: the cursor deltas accumulate in two locals, the
665
+ * vertices go into a typed array the caller owns, and a polygon's ring
666
+ * areas and the whole feature's bounding box are accumulated in the same
667
+ * pass rather than in two more over the same points.
668
+ */
669
+ readGeometry(into: GeometryBuffer): GeometryData {
670
+ into.reset();
671
+ if (this._geomTo === this._geomAt) {
672
+ into.starts[0] = 0;
673
+ return into;
674
+ }
675
+ const reader = new Reader(this._buf, this._geomAt, this._geomTo);
676
+ const end = this._geomTo;
677
+ const isPolygon = this.type === GeomType.Polygon;
678
+ const isPoint = this.type === GeomType.Point;
679
+ let x = 0;
680
+ let y = 0;
681
+ let cmd = 0;
682
+ let left = 0;
683
+ let points = 0;
684
+ let parts = 0;
685
+ // The open part's first vertex and its running shoelace sum.
686
+ let firstX = 0;
687
+ let firstY = 0;
688
+ let prevX = 0;
689
+ let prevY = 0;
690
+ let sum = 0;
691
+ let open = false;
692
+ let minX = Infinity;
693
+ let minY = Infinity;
694
+ let maxX = -Infinity;
695
+ let maxY = -Infinity;
696
+
697
+ // The open part's own box, closed out into `partBounds` beside its
698
+ // area. Tracked per part rather than per feature because the feature
699
+ // box cannot cull a multipolygon — see `GeometryBuffer.partBounds`.
700
+ let pMinX = Infinity;
701
+ let pMinY = Infinity;
702
+ let pMaxX = -Infinity;
703
+ let pMaxY = -Infinity;
704
+
705
+ const closePart = (): void => {
706
+ if (!open) return;
707
+ into.reserveParts(parts + 1);
708
+ // A ring's last point is not repeated before `ClosePath`, so the
709
+ // closing edge — last back to first — is added here and nowhere else.
710
+ into.areas[parts] = isPolygon
711
+ ? (sum + (prevX * firstY - firstX * prevY)) / 2
712
+ : 0;
713
+ const at = parts * 4;
714
+ into.partBounds[at] = pMinX;
715
+ into.partBounds[at + 1] = pMinY;
716
+ into.partBounds[at + 2] = pMaxX;
717
+ into.partBounds[at + 3] = pMaxY;
718
+ if (pMinX < minX) minX = pMinX;
719
+ if (pMinY < minY) minY = pMinY;
720
+ if (pMaxX > maxX) maxX = pMaxX;
721
+ if (pMaxY > maxY) maxY = pMaxY;
722
+ pMinX = Infinity;
723
+ pMinY = Infinity;
724
+ pMaxX = -Infinity;
725
+ pMaxY = -Infinity;
726
+ parts++;
727
+ into.starts[parts] = points;
728
+ open = false;
729
+ };
730
+
731
+ while (reader.pos < end) {
732
+ if (left <= 0) {
733
+ const command = reader.varint();
734
+ cmd = command & 0x7;
735
+ left = command >> 3;
736
+ if (left <= 0) continue; // a zero-count command: nothing to do
737
+ }
738
+ left--;
739
+ if (cmd === 1) {
740
+ // MoveTo. For a line or a polygon it begins a part; for a point
741
+ // feature every step of one MoveTo is another point of the *same*
742
+ // multipoint, and giving each its own part would make a 500-point
743
+ // cluster 500 parts for no reader's benefit.
744
+ if (!isPoint) closePart();
745
+ x += reader.svarint();
746
+ y += reader.svarint();
747
+ if (!open) {
748
+ firstX = x;
749
+ firstY = y;
750
+ sum = 0;
751
+ open = true;
752
+ into.starts[parts] = points;
753
+ }
754
+ into.reserveCoords(points + 1);
755
+ into.coords[points * 2] = x;
756
+ into.coords[points * 2 + 1] = y;
757
+ points++;
758
+ prevX = x;
759
+ prevY = y;
760
+ } else if (cmd === 2) {
761
+ x += reader.svarint();
762
+ y += reader.svarint();
763
+ if (!open) {
764
+ // A LineTo with no MoveTo before it. Malformed, and tolerated as
765
+ // the start of a part rather than dropped: the vertex is real
766
+ // data and the alternative is a road with a gap in it.
767
+ firstX = x;
768
+ firstY = y;
769
+ sum = 0;
770
+ open = true;
771
+ into.starts[parts] = points;
772
+ } else if (isPolygon) {
773
+ sum += prevX * y - x * prevY;
774
+ }
775
+ into.reserveCoords(points + 1);
776
+ into.coords[points * 2] = x;
777
+ into.coords[points * 2 + 1] = y;
778
+ points++;
779
+ prevX = x;
780
+ prevY = y;
781
+ } else if (cmd === 7) {
782
+ closePart();
783
+ } else {
784
+ throw new MvtError(`unknown geometry command ${cmd}`);
785
+ }
786
+ if (x < pMinX) pMinX = x;
787
+ if (x > pMaxX) pMaxX = x;
788
+ if (y < pMinY) pMinY = y;
789
+ if (y > pMaxY) pMaxY = y;
790
+ }
791
+ closePart();
792
+ into.points = points;
793
+ into.parts = parts;
794
+ into.minX = minX;
795
+ into.minY = minY;
796
+ into.maxX = maxX;
797
+ into.maxY = maxY;
798
+ return into;
799
+ }
800
+
801
+ /** Geometry as arrays of its own — the convenience form. */
802
+ geometry(): GeometryData {
803
+ const buffer = new GeometryBuffer(64, 4);
804
+ this.readGeometry(buffer);
805
+ // Trimmed, since this shape is the one that gets kept.
806
+ return {
807
+ coords: buffer.coords.slice(0, buffer.points * 2),
808
+ starts: buffer.starts.slice(0, buffer.parts + 1),
809
+ areas: buffer.areas.slice(0, buffer.parts),
810
+ parts: buffer.parts,
811
+ points: buffer.points,
812
+ };
813
+ }
814
+ }
815
+
816
+ const EMPTY = new Uint8Array(0);
817
+ const EMPTY_INTS = new Int32Array(0);
818
+
819
+ /**
820
+ * Decode a tile.
821
+ *
822
+ * Cheap: it walks the tile's top-level fields and each layer's, which is
823
+ * the layer names, their key and value tables, and one byte range per
824
+ * feature. No geometry is touched, so a style that draws four of a tile's
825
+ * fourteen layers pays for four.
826
+ *
827
+ * The bytes must be the raw protobuf. Tiles are almost always served
828
+ * `Content-Encoding: gzip` and sometimes gzipped *inside* that, which is a
829
+ * transport concern and deliberately not this function's — a source adapter
830
+ * un-gzips (`src/maps/sources.ts` does).
831
+ */
832
+ export function parseTile(bytes: Uint8Array): VectorTile {
833
+ const layers = new Map<string, VectorTileLayer>();
834
+ const order: string[] = [];
835
+ const reader = new Reader(bytes);
836
+ while (reader.pos < reader.end) {
837
+ const tag = reader.varint();
838
+ const field = tag >> 3;
839
+ const wire = tag & 0x7;
840
+ if (field === 3 && wire === WIRE_BYTES) {
841
+ const length = reader.varint();
842
+ const end = reader.pos + length;
843
+ if (end > reader.end) throw new MvtError('layer ran past the end');
844
+ const layer = new VectorTileLayer(reader, end);
845
+ reader.pos = end;
846
+ // A tile with two layers of one name is malformed; the first wins,
847
+ // which is what every other decoder does.
848
+ if (!layers.has(layer.name)) {
849
+ layers.set(layer.name, layer);
850
+ order.push(layer.name);
851
+ }
852
+ } else {
853
+ reader.skip(wire);
854
+ }
855
+ }
856
+ return { layers, order };
857
+ }