@tileflow/core 0.1.0-alpha.3 → 0.1.0-alpha.31
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/LICENSE +201 -0
- package/README.md +273 -26
- package/THIRD_PARTY_NOTICES.md +327 -0
- package/dist/browser.d.ts +962 -0
- package/dist/browser.js +6237 -0
- package/dist/build.d.ts +574 -0
- package/dist/build.js +25423 -0
- package/dist/capture-scene-D6soWztE.d.ts +161 -0
- package/dist/capture.d.ts +9 -0
- package/dist/capture.js +276 -0
- package/dist/config-reference.json +32261 -0
- package/dist/icon-composition-BiOTwrvD.d.ts +1331 -0
- package/dist/index.d.ts +1114 -162
- package/dist/index.js +18714 -3479
- package/dist/manifest-types-CTT2eLbp.d.ts +790 -0
- package/dist/manifest.d.ts +9 -0
- package/dist/manifest.js +368 -0
- package/dist/native-profile.d.ts +188 -0
- package/dist/native-profile.js +9316 -0
- package/dist/native.d.ts +355 -0
- package/dist/native.js +5552 -0
- package/dist/runtime.d.ts +1 -0
- package/dist/runtime.js +1317 -0
- package/docs/authoring-model.md +241 -0
- package/docs/capture-scenes.md +36 -0
- package/docs/compiled-style-performance.md +263 -0
- package/docs/data-is-separate-from-design.md +345 -0
- package/docs/globe-atmosphere.md +83 -0
- package/docs/hosted-session-authorization.md +108 -0
- package/docs/map-and-terrain-examples.md +149 -0
- package/docs/map-emphasis.md +89 -0
- package/docs/native-artifact-profile.md +145 -0
- package/docs/native-resource-urls.md +367 -0
- package/docs/public-api-and-browser-subpath.md +93 -0
- package/docs/shared-visual-primitives.md +81 -0
- package/docs/theme-transitions-and-blends.md +221 -0
- package/docs/themes-and-module-styles.md +83 -0
- package/package.json +58 -8
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
# Native resource URLs, sources and initial views
|
|
2
|
+
|
|
3
|
+
`@tileflow/core/native` provides URL helpers, source coordination and renderer-neutral initial
|
|
4
|
+
views for clients without a browser origin. Its published entry bundles a private WHATWG parser
|
|
5
|
+
with IDNA processing. It does not use or replace the ambient `URL`, `TextEncoder` or `TextDecoder`,
|
|
6
|
+
and has no renderer dependency.
|
|
7
|
+
|
|
8
|
+
The synchronous URL helpers resolve references only. Tileflow manifest sources use an injected
|
|
9
|
+
bounded acquisition adapter. None of these APIs renders maps or authorizes Hosted requests.
|
|
10
|
+
A successful resolution is not a claim that a style or feature works on a particular native renderer.
|
|
11
|
+
Install `@tileflow/core@alpha` as described in the [package README](../README.md); the examples use
|
|
12
|
+
only this package's public exports.
|
|
13
|
+
|
|
14
|
+
## Resolve the manifest explicitly
|
|
15
|
+
|
|
16
|
+
A native client must provide the complete manifest URL. There is no implicit
|
|
17
|
+
`/tileflow/manifest.json`, Metro host, device address, or current application route.
|
|
18
|
+
|
|
19
|
+
<!-- docs:check -->
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import {resolveTileflowNativeManifestUrl} from '@tileflow/core/native';
|
|
23
|
+
|
|
24
|
+
const manifestUrl = resolveTileflowNativeManifestUrl(
|
|
25
|
+
'https://maps.example.com/tileflow/native/manifest.json',
|
|
26
|
+
);
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The result is the validated, absolute HTTPS manifest URL; no network request is made.
|
|
30
|
+
The default policy requires HTTPS. Both input and resolved URLs are limited to 2,048 UTF-16 code
|
|
31
|
+
units. Empty values, leading or trailing whitespace, control characters, backslashes, fragments,
|
|
32
|
+
malformed percent escapes, embedded credentials, and protocol-relative URLs are rejected.
|
|
33
|
+
Local file URLs and custom renderer protocols are outside this helper's HTTP(S) contract.
|
|
34
|
+
|
|
35
|
+
## Resolve resources from their owning document
|
|
36
|
+
|
|
37
|
+
Pass the URL of the document that declares the resource. A style URL is relative to its manifest;
|
|
38
|
+
a tile URL in TileJSON is relative to that TileJSON; a font URL in a style is relative to the style.
|
|
39
|
+
The helper uses normal URL path resolution and never guesses a deployment prefix.
|
|
40
|
+
|
|
41
|
+
<!-- docs:check -->
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import {resolveTileflowNativeResourceUrl} from '@tileflow/core/native';
|
|
45
|
+
|
|
46
|
+
const styleUrl = resolveTileflowNativeResourceUrl('styles/main/light.json', {
|
|
47
|
+
documentUrl: 'https://maps.example.com/tileflow/native/manifest.json',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const tileUrl = resolveTileflowNativeResourceUrl('./{z}/{x}/{y}.pbf', {
|
|
51
|
+
documentUrl: 'https://tiles.example.com/streets/tiles.json',
|
|
52
|
+
template: 'tile',
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`styleUrl` resolves to the `styles/main/light.json` path beneath the manifest's directory. `tileUrl`
|
|
57
|
+
resolves to the `streets/{z}/{x}/{y}.pbf` template on the tile server; no tile coordinates are chosen.
|
|
58
|
+
|
|
59
|
+
Use the actual document URL after any redirects that the application has approved. The declaring
|
|
60
|
+
URL is validated even when the resource is already absolute. External HTTPS resources retain their
|
|
61
|
+
own origin. The helper never adds delivery credentials or copies a parent's query parameters to a
|
|
62
|
+
child resource.
|
|
63
|
+
|
|
64
|
+
`template: 'tile'` preserves `{z}`, `{x}`, `{y}`, `{ratio}`, `{quadkey}`, `{bbox-epsg-3857}`, and
|
|
65
|
+
`{prefix}`. `template: 'glyphs'` preserves `{fontstack}` and `{range}`. These placeholders are allowed
|
|
66
|
+
only in the resource path or query, not the authority. They are not expanded or checked against a
|
|
67
|
+
renderer version. Unknown, mismatched, and incomplete placeholders are rejected. Without a template
|
|
68
|
+
option, literal braces are rejected; existing percent-encoded braces in paths and queries remain
|
|
69
|
+
encoded literal data. Manifest and declaring-document URLs cannot contain literal placeholders.
|
|
70
|
+
Hostname normalization must not introduce placeholders, including from percent escapes or Unicode
|
|
71
|
+
characters that normalize to braces.
|
|
72
|
+
|
|
73
|
+
## Opt into one development origin
|
|
74
|
+
|
|
75
|
+
A debug integration can explicitly permit one HTTP origin:
|
|
76
|
+
|
|
77
|
+
<!-- docs:check -->
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import {resolveTileflowNativeManifestUrl} from '@tileflow/core/native';
|
|
81
|
+
|
|
82
|
+
const manifestUrl = resolveTileflowNativeManifestUrl(
|
|
83
|
+
'http://192.168.1.5:8080/tileflow/manifest.json',
|
|
84
|
+
{developmentOrigin: 'http://192.168.1.5:8080'},
|
|
85
|
+
);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Supply the same option when resolving that document's resources. Scheme, hostname, and port must
|
|
89
|
+
match the selected origin after URL normalization. An exception for `localhost` does not permit
|
|
90
|
+
`127.0.0.1`, another port, or another device. HTTPS remains allowed. The option must name an HTTP
|
|
91
|
+
origin without a path, query, fragment, wildcard, or credentials; an optional trailing slash is
|
|
92
|
+
accepted. Wildcards and placeholders are rejected before and after hostname normalization.
|
|
93
|
+
|
|
94
|
+
Keep this option in debug-only application configuration. The helper cannot determine whether the
|
|
95
|
+
application is a debug build, does not change iOS or Android transport settings, and does not expose
|
|
96
|
+
a development server. The application still owns any narrowly scoped debug transport exception.
|
|
97
|
+
|
|
98
|
+
## Handle errors without logging the rejected URL
|
|
99
|
+
|
|
100
|
+
Failures throw `TileflowNativeUrlError`, a `TypeError` with a stable `code` and `field`.
|
|
101
|
+
|
|
102
|
+
- `NATIVE_URL_INVALID`: malformed, unsafe, unsupported-scheme, or oversized URL.
|
|
103
|
+
- `NATIVE_URL_ABSOLUTE_REQUIRED`: a document URL is missing its explicit HTTP(S) origin.
|
|
104
|
+
- `NATIVE_URL_HTTPS_REQUIRED`: HTTP does not match the selected development origin.
|
|
105
|
+
- `NATIVE_URL_DEVELOPMENT_ORIGIN_INVALID`: the development exception is not one valid HTTP origin.
|
|
106
|
+
- `NATIVE_URL_TEMPLATE_INVALID`: a template is unsupported, misplaced, or incomplete.
|
|
107
|
+
|
|
108
|
+
`field` is `manifestUrl`, `documentUrl`, `resourceUrl`, or `developmentOrigin`. Error messages do not
|
|
109
|
+
include the submitted URL or attach the original parser error. The application should preserve
|
|
110
|
+
that boundary when reporting failures: resource URLs may contain sensitive query parameters.
|
|
111
|
+
|
|
112
|
+
For JavaScript callers, missing or non-object resource options report `NATIVE_URL_INVALID` with
|
|
113
|
+
`field: 'documentUrl'`, just like an options object without its required `documentUrl`. Non-object
|
|
114
|
+
manifest options report `NATIVE_URL_DEVELOPMENT_ORIGIN_INVALID` with `field: 'developmentOrigin'`.
|
|
115
|
+
Omitting manifest options or passing `undefined` remains valid and uses the default HTTPS policy.
|
|
116
|
+
Neither function accepts `null` as an options object.
|
|
117
|
+
|
|
118
|
+
This is URL policy, not an authorization or server-side request-forgery defense. Callers still own
|
|
119
|
+
resource-origin authorization, redirect handling, response size limits, cancellation, and any
|
|
120
|
+
network request. Do not attach a credential merely because this helper accepted a URL.
|
|
121
|
+
|
|
122
|
+
## Private parser and source builds
|
|
123
|
+
|
|
124
|
+
The native entry embeds the low-level parser from `whatwg-url` 15.1.0, `tr46` 6.0.0 (Unicode 17
|
|
125
|
+
IDNA processing) and Punycode.js 2.3.1. It exposes no URL class or parser configuration. Package
|
|
126
|
+
consumers only install Core; the published `dist/native.js` has no external runtime imports.
|
|
127
|
+
The parser is a development/build dependency, not an additional runtime package to install.
|
|
128
|
+
Canonical manifest validation also embeds the existing Zod dependency; it adds no package that
|
|
129
|
+
consumers must configure. Complete notices are included in [THIRD_PARTY_NOTICES.md](../THIRD_PARTY_NOTICES.md).
|
|
130
|
+
|
|
131
|
+
A React Native URL shim can concatenate references onto a filename or append a slash to `.json`.
|
|
132
|
+
The native helpers instead resolve against the owning document with the bundled parser, including
|
|
133
|
+
host percent-decoding, IDNA mapping, default ports and encoded dot segments. This does not require
|
|
134
|
+
an application-wide polyfill. The build-time native artifact validator uses the same Tileflow URL
|
|
135
|
+
policy with its existing host parser; it does not embed the mobile parser in its own bundle.
|
|
136
|
+
|
|
137
|
+
From an SDK source checkout, run the ordinary Core build, not a direct source import in a native
|
|
138
|
+
application. `tsup.native.config.ts` builds only the native entry with `platform: 'browser'`,
|
|
139
|
+
`target: 'es2020'`, ESM and no splitting. Its plugin redirects exactly the two relative
|
|
140
|
+
`./encoding` imports in the pinned parser modules to Core's private UTF-8 codec. It rejects an
|
|
141
|
+
unexpected dependency version, an unbound codec import or external runtime imports. It does not
|
|
142
|
+
patch packages on disk, configure a global alias or change another entry's build.
|
|
143
|
+
|
|
144
|
+
The codec uses replacement semantics for unmatched UTF-16 surrogates and invalid UTF-8 bytes and
|
|
145
|
+
preserves BOMs, matching `TextEncoder` and non-fatal `TextDecoder('utf-8', {ignoreBOM: true})`.
|
|
146
|
+
It processes at most 65,536 code units for encoding or 65,536 bytes for decoding per call. Private
|
|
147
|
+
placeholder sentinels are bounded well below that ceiling; the public URL ceiling stays at 2,048
|
|
148
|
+
code units. No parser or codec error is exposed as a cause containing the rejected URL.
|
|
149
|
+
|
|
150
|
+
## Check the built package
|
|
151
|
+
|
|
152
|
+
From an installed and built SDK checkout:
|
|
153
|
+
|
|
154
|
+
```sh
|
|
155
|
+
pnpm --filter @tileflow/core exec tsx --test test/native-runtime-url.test.ts test/native-url-provider.test.ts test/native-url-utf8.test.ts test/native-url-policy.test.ts test/native-source-view-package.test.ts
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The existing runtime regression checks the Node host and a React Native URL fixture independently.
|
|
159
|
+
Another test copies the built native file into an empty directory and executes it with ambient URL
|
|
160
|
+
and codec getters that throw. Neither test is a Hermes or device qualification.
|
|
161
|
+
|
|
162
|
+
For size comparisons, build Core in two isolated checkouts and retain an `npm pack --json` receipt
|
|
163
|
+
from each package directory. Pass the already-built Core directories and optional receipt paths to:
|
|
164
|
+
|
|
165
|
+
```sh
|
|
166
|
+
node packages/core/scripts/measure-native-url.mjs /path/to/before/packages/core /path/to/after/packages/core /path/to/before-pack.json /path/to/after-pack.json
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The report separates raw native entry bytes, its runtime graph, a minified ES2020 browser consumer,
|
|
170
|
+
gzip size, compressed tarball bytes and unpacked package bytes. It performs no installation and is
|
|
171
|
+
not a Metro bundle, Hermes bytecode or device-memory measurement.
|
|
172
|
+
|
|
173
|
+
Native acceptance must install the exact tarball in an existing iOS/Android application and run
|
|
174
|
+
[`checkNativeUrlContract`](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/test/fixtures/native-url-contract.mjs)
|
|
175
|
+
with the installed `@tileflow/core/native` exports, leaving the application's URL global unchanged.
|
|
176
|
+
The [fixture procedure](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/test/fixtures/native-url-contract.md)
|
|
177
|
+
covers the two explicit development origins without network requests. Packaged Hermes execution
|
|
178
|
+
remains a separate qualification step; static parsing and Node tests do not establish it. URL-provider
|
|
179
|
+
checks do not qualify source/view behavior. This entry does not render maps, create transport
|
|
180
|
+
grants or make mobile service availability claims.
|
|
181
|
+
|
|
182
|
+
## Acquire a bounded manifest
|
|
183
|
+
|
|
184
|
+
`loadTileflowNativeManifest(manifestUrl, {acquire, signal?, developmentOrigin?})` loads only the
|
|
185
|
+
runtime manifest. The application supplies `acquire`; Core never selects an ambient `fetch` or
|
|
186
|
+
starts a request at import time. The function returns an immutable `{manifestUrl, manifest}` result.
|
|
187
|
+
`manifestUrl` is the validated final response URL, not a guessed application or Metro address.
|
|
188
|
+
|
|
189
|
+
The acquisition adapter returns a request handle immediately. Its `response` promise resolves to
|
|
190
|
+
`{url, status, reader}`, and its `cancel()` method must be usable before the response arrives.
|
|
191
|
+
`url` must be the absolute final URL after redirects that the adapter has approved. Redirect and
|
|
192
|
+
resource-origin authorization remain application responsibilities; HTTPS syntax does not authorize
|
|
193
|
+
another origin. A missing or invalid final URL is an error, not a reason to reuse the request URL.
|
|
194
|
+
|
|
195
|
+
The reader exposes `read(maximumBytes)` and `cancel()`. Each read returns either `{done: true}` or
|
|
196
|
+
`{done: false, value: Uint8Array}` with between one and the requested number of bytes. Core requests
|
|
197
|
+
at most 64 KiB and copies each measured chunk before requesting another. At the 1 MiB ceiling it
|
|
198
|
+
requests one byte to distinguish EOF from overflow. Empty chunks, strings, malformed read results,
|
|
199
|
+
and chunks that exceed the requested allowance are rejected. The adapter must release its resources
|
|
200
|
+
at EOF; cancellation and failure also call its reader/request cancellation hooks.
|
|
201
|
+
|
|
202
|
+
An adapter must enforce bounded reads at its acquisition boundary. It must not implement this
|
|
203
|
+
interface by first calling an unlimited `response.text()`, `arrayBuffer()`, or native equivalent and
|
|
204
|
+
then slicing the completed body. A platform without a bounded byte reader needs an appropriate
|
|
205
|
+
acquisition adapter; this package does not supply a network bridge or a fallback buffering path.
|
|
206
|
+
The supplied request `maximumBytes` is the canonical 1 MiB cap, not an adjustable consumer budget.
|
|
207
|
+
|
|
208
|
+
Core counts actual bytes, never relies on Content-Length, and rejects overflow before JSON/schema
|
|
209
|
+
processing. Decoding is fatal UTF-8: malformed bytes and truncated sequences fail, including those
|
|
210
|
+
split across reads. One leading BOM is removed, matching the existing runtime reader; later BOMs
|
|
211
|
+
remain data. The private URL codec retains its existing replacement behavior for URLs. A depth
|
|
212
|
+
ceiling of 64 is checked before parsing JSON, and the canonical manifest schema still imposes all
|
|
213
|
+
map/theme/font/view/identity restrictions. Canonical and resolved manifest JSON must also remain
|
|
214
|
+
within 1 MiB; URL expansion cannot turn a small response into an unbounded result.
|
|
215
|
+
|
|
216
|
+
There is one version-1 grammar, shared with `parseTileflowRuntimeManifest`. Native supplies the
|
|
217
|
+
accepted private URL provider and byte-counting primitives to that grammar, without widening its
|
|
218
|
+
wire fields or accepting previously rejected relative URL forms. Every theme's `styleUrl` resolves
|
|
219
|
+
against the final manifest URL; font declarations retain the existing style-relative ownership.
|
|
220
|
+
The loader resolves those references but does not download styles, fonts, sprites, tiles or TileJSON.
|
|
221
|
+
Declared revisions and map/style IDs remain data, not inferred authorization or a session bootstrap.
|
|
222
|
+
|
|
223
|
+
There is no implicit cache, retry, request timeout or background task. Use the adapter's bounded
|
|
224
|
+
request policy or an external AbortSignal-compatible input for a deadline. Core does not construct
|
|
225
|
+
an AbortController, inspect `signal.reason`, install a polyfill, or wait for a broken cancellation
|
|
226
|
+
hook to finish. It retires an aborted operation immediately, observes late promise rejections and
|
|
227
|
+
cancels a reader that arrives after retirement. Physical cancellation of network work still depends
|
|
228
|
+
on the adapter honoring its cancellation methods.
|
|
229
|
+
|
|
230
|
+
## Control a source
|
|
231
|
+
|
|
232
|
+
`createTileflowNativeSourceController({acquire})` coordinates a manifest-backed Tileflow source:
|
|
233
|
+
`{map, manifestUrl, developmentOrigin?}`. The map and manifest URL are required. The optional
|
|
234
|
+
development origin uses the exact HTTP exception described above and applies to manifest resources.
|
|
235
|
+
It belongs to the source descriptor, while theme, color scheme and cancellation remain replacement
|
|
236
|
+
options. Renderer discriminators and direct style inputs
|
|
237
|
+
are not part of this public source contract; the obsolete `kind` and `style` fields are rejected.
|
|
238
|
+
A completely unmanaged map uses upstream MapLibre directly, not another Tileflow source mode.
|
|
239
|
+
|
|
240
|
+
The controller's `state` is undefined until the first `replace()`. It then publishes only `loading`,
|
|
241
|
+
`ready`, or `error` snapshots, each with a monotonically increasing `generation`. The acquisition
|
|
242
|
+
adapter is required at construction. Core does not mount a renderer or subscribe to device appearance.
|
|
243
|
+
|
|
244
|
+
The following function expects an acquisition adapter implementing the bounded protocol above.
|
|
245
|
+
The example URL represents an existing manifest endpoint; no network adapter is supplied here.
|
|
246
|
+
|
|
247
|
+
<!-- docs:check -->
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
import {
|
|
251
|
+
createTileflowNativeSourceController,
|
|
252
|
+
type TileflowNativeManifestAcquire,
|
|
253
|
+
} from '@tileflow/core/native';
|
|
254
|
+
|
|
255
|
+
export async function connectNativeManifest(acquire: TileflowNativeManifestAcquire) {
|
|
256
|
+
const controller = createTileflowNativeSourceController({acquire});
|
|
257
|
+
await controller.replace(
|
|
258
|
+
{
|
|
259
|
+
map: 'streets',
|
|
260
|
+
manifestUrl: 'https://maps.example.com/tileflow/native/manifest.json',
|
|
261
|
+
},
|
|
262
|
+
{theme: 'dark'},
|
|
263
|
+
);
|
|
264
|
+
const state = controller.state;
|
|
265
|
+
if (state?.status === 'ready') {
|
|
266
|
+
console.log(state.map.name, state.theme.name, state.theme.revision);
|
|
267
|
+
} else if (state?.status === 'error') {
|
|
268
|
+
console.error(state.error.code, state.error.field);
|
|
269
|
+
}
|
|
270
|
+
return controller;
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Narrow on `state.status` to access the resolved selection. Every `ready` snapshot retains the
|
|
275
|
+
immutable requested `source`, final `manifestUrl`, resolved `manifest`, selected `map` and concrete
|
|
276
|
+
`theme` as required fields. There is no renderer discriminator or direct-style state.
|
|
277
|
+
|
|
278
|
+
Map/theme identity and revisions come from the manifest. Map-level `apiUrl` takes precedence over
|
|
279
|
+
manifest-level `apiUrl`; no identity is guessed from URL paths. The map's canonical `view` is
|
|
280
|
+
preserved as metadata. The controller does not combine camera inputs.
|
|
281
|
+
|
|
282
|
+
Omitted `theme` uses `defaultTheme`; a concrete theme selects that exact declared name. `system`
|
|
283
|
+
uses the manifest's `systemThemes` mapping only when an explicit `colorScheme: 'light' | 'dark'`
|
|
284
|
+
is supplied. The same portable resolver owns this precedence in the existing runtime. Core never
|
|
285
|
+
reads device appearance. A missing map, missing theme or unresolved system selection is an error;
|
|
286
|
+
there is no first-map or first-theme fallback.
|
|
287
|
+
|
|
288
|
+
`replace(source, options)` snapshots inputs and retires the prior generation. A failed replacement
|
|
289
|
+
cannot publish the previous source as newly ready. Only the current generation can publish success
|
|
290
|
+
or error, even when responses arrive out of order or a transport ignores cancellation. Each valid
|
|
291
|
+
replacement reacquires its manifest. There is no hidden source cache. The promise settles when that
|
|
292
|
+
generation completes or is retired; source failures are reported in `state`, not as rejected
|
|
293
|
+
replacement promises. Calling it after disposal rejects with `NATIVE_SOURCE_DISPOSED`.
|
|
294
|
+
|
|
295
|
+
`subscribe(listener)` synchronously receives the current snapshot, when present, and subsequent
|
|
296
|
+
snapshots until its returned unsubscribe function is called. Observer exceptions are isolated from
|
|
297
|
+
acquisition and from other observers. Reentrant replacement/disposal prevents remaining observers
|
|
298
|
+
from receiving an obsolete notification. `dispose()` is idempotent: it retires active work, clears
|
|
299
|
+
listeners and retains the last snapshot for inspection without publishing another state. No late
|
|
300
|
+
completion or abort can notify a disposed controller.
|
|
301
|
+
|
|
302
|
+
## Resolve an initial view without a renderer
|
|
303
|
+
|
|
304
|
+
`resolveTileflowNativeInitialView({view?, mapOptionsView?, manifestView?})` returns an immutable
|
|
305
|
+
`{center, zoom, bearing, pitch}`. All three inputs are partial renderer-neutral view objects, not
|
|
306
|
+
React Native props or MapLibre camera types. Precedence applies independently to each field:
|
|
307
|
+
`view` > `mapOptionsView` > `manifestView` > shared runtime defaults.
|
|
308
|
+
|
|
309
|
+
<!-- docs:check -->
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
import {resolveTileflowNativeInitialView} from '@tileflow/core/native';
|
|
313
|
+
|
|
314
|
+
const initialView = resolveTileflowNativeInitialView({
|
|
315
|
+
manifestView: {center: [-3.7, 40.4], zoom: 10, bearing: 12},
|
|
316
|
+
mapOptionsView: {zoom: 11, pitch: 30},
|
|
317
|
+
view: {zoom: 13},
|
|
318
|
+
});
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
The result is `{center: [-3.7, 40.4], zoom: 13, bearing: 12, pitch: 30}`. With no inputs, the defaults
|
|
322
|
+
are `{center: [0, 20], zoom: 2, bearing: 0, pitch: 0}`. Coordinates always use
|
|
323
|
+
`[longitude, latitude]`; Core neither swaps nor wraps them.
|
|
324
|
+
|
|
325
|
+
Validation reuses the manifest view grammar: longitude -180 to 180, latitude -90 to 90, zoom 0 to 24,
|
|
326
|
+
bearing -180 to 180 and pitch 0 to 85, all finite. Supplied fields with `undefined` are omitted;
|
|
327
|
+
`null`, out-of-range values, unknown fields, accessors and malformed tuples are errors. Every
|
|
328
|
+
supplied level is validated, even if a higher-precedence level would override it. Invalid explicit
|
|
329
|
+
input never silently falls back. The result owns a new frozen tuple, not a reference to an input or
|
|
330
|
+
to the shared defaults. Failures use `NATIVE_SOURCE_INVALID` with `field: 'view'` and no caller values.
|
|
331
|
+
|
|
332
|
+
The resolver is synchronous and independent of source control. Calling it again for a camera update
|
|
333
|
+
does not load a manifest, change the selected theme or increment a source generation. It does not
|
|
334
|
+
animate, read device appearance, install listeners or apply the result to a renderer.
|
|
335
|
+
|
|
336
|
+
## Handle source errors
|
|
337
|
+
|
|
338
|
+
Source operations and initial-view resolution use `TileflowNativeSourceError` with stable `code`,
|
|
339
|
+
`field` and `kind`. An active external abort produces `kind: 'cancelled'` and `NATIVE_SOURCE_ABORTED`;
|
|
340
|
+
superseded or disposed work cannot publish that error into a newer generation. Acquisition,
|
|
341
|
+
response, schema and selection failures are terminal for that generation. A later explicit
|
|
342
|
+
`replace()` can retry. Tile-resource failures after a map is created are outside this controller
|
|
343
|
+
and do not get classified as manifest failures. This diagnostic `kind` classifies failure, not a
|
|
344
|
+
renderer or source mode.
|
|
345
|
+
|
|
346
|
+
- `NATIVE_SOURCE_INVALID`: invalid source, view, acquisition configuration or cancellation input.
|
|
347
|
+
- `NATIVE_SOURCE_ABORTED`, `NATIVE_SOURCE_DISPOSED`: cancellation or use after controller disposal.
|
|
348
|
+
- `NATIVE_MANIFEST_URL_INVALID`: invalid request/final URL or development-origin policy.
|
|
349
|
+
- `NATIVE_MANIFEST_REQUEST_FAILED`: acquisition/read failure or unsuccessful response.
|
|
350
|
+
- `NATIVE_MANIFEST_RESPONSE_INVALID`: invalid response, reader or chunk protocol.
|
|
351
|
+
- `NATIVE_MANIFEST_ACCESS_DENIED`, `NATIVE_MANIFEST_NOT_FOUND`: HTTP 401/403 or 404, respectively.
|
|
352
|
+
- `NATIVE_MANIFEST_TOO_LARGE`: actual body or resolved/canonical JSON exceeds the 1 MiB cap.
|
|
353
|
+
- `NATIVE_MANIFEST_UTF8_INVALID`, `NATIVE_MANIFEST_JSON_INVALID`: malformed UTF-8 or JSON.
|
|
354
|
+
- `NATIVE_MANIFEST_INVALID`: invalid version-1 structure, relationships or excessive nesting.
|
|
355
|
+
- `NATIVE_MANIFEST_RESOURCE_INVALID`: a manifest resource fails native URL policy.
|
|
356
|
+
- `NATIVE_MAP_NOT_FOUND`, `NATIVE_THEME_INVALID`: missing map or invalid/unresolved theme selection.
|
|
357
|
+
|
|
358
|
+
Fields identify only `source`, `signal`, `manifestUrl`, `response`, `body`, `map`, `theme` or `view`.
|
|
359
|
+
Errors never carry a rejected URL, response body, credential, remote exception, schema issue list or
|
|
360
|
+
abort reason. Successful snapshots necessarily contain resource URLs; treat them as potentially
|
|
361
|
+
sensitive when logging. The synchronous URL helpers retain their existing `TileflowNativeUrlError`
|
|
362
|
+
contract. A view error is thrown by the pure resolver and does not change controller state.
|
|
363
|
+
|
|
364
|
+
These APIs acquire manifests and resolve view data. They do not load styles, validate their
|
|
365
|
+
cartographic compatibility, fetch their resources, render maps, create Hosted authorization or
|
|
366
|
+
sessions, or provide a React Native component. Source and view behavior requires its own packed
|
|
367
|
+
Hermes checks; URL-only checks, Node tests and browser captures do not establish those results.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Public API and browser subpath
|
|
2
|
+
|
|
3
|
+
Start with the [@tileflow/core guide](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/README.md) for installation and a complete first example.
|
|
4
|
+
|
|
5
|
+
Config, data, modules, compilation, validation, runtime resolution, and capture-scene APIs remain
|
|
6
|
+
available from the package root. Capture scenes, manifests, and browser-runtime surfaces also have
|
|
7
|
+
explicit `@tileflow/core/capture`, `@tileflow/core/manifest`, and `@tileflow/core/runtime`
|
|
8
|
+
subpaths. Node build integrations import the small deterministic map-identity contract from
|
|
9
|
+
`@tileflow/core/build`: `collectTileflowMapBuildLineage`, `createTileflowMapBuildManifest`,
|
|
10
|
+
`hashTileflowMapRevision`, `hashTileflowAssetSet`, and `hashTileflowAssetSetIdentities`. The latter
|
|
11
|
+
reproduces the same asset-set v1 hash from exact per-file identities after another system has
|
|
12
|
+
independently confirmed the immutable bytes; package or bundle hashes are not substitutes. The
|
|
13
|
+
previous aggregate-wrapper and recipe-selector APIs, `renderer`, top-level `tiles`/`tileset`, module
|
|
14
|
+
arrays, and top-level raw `layers` are not part of this API. Raw MapLibre layers are accepted only
|
|
15
|
+
inside a named `maplibreOverlay()` bound to a named Team source.
|
|
16
|
+
|
|
17
|
+
Advanced cartography remains in the same public language: `withRenderStack()`, `renderPass()`, and
|
|
18
|
+
`refineRenderTarget()` address owner-local semantic targets without exposing physical layer IDs,
|
|
19
|
+
sources, source layers, raw filters, or before/after anchors. Typed `field()` references and
|
|
20
|
+
`expr.*` builders stay semantic until the compiler's single lowering boundary. There is no recipe
|
|
21
|
+
subpath or alternate compiler integration API.
|
|
22
|
+
|
|
23
|
+
`mapRevisionSha256` hashes only resolved cartography: the effective design after `extends`, the
|
|
24
|
+
semantic language, compiler-owned effective contributions, and exact source icon/font identities.
|
|
25
|
+
Leaf `id`, `name`, editorial `mapVersion`, default `view`, capture `scenes`, and `delivery` policy do
|
|
26
|
+
not change that content identity. They remain explicit on the map, manifest, Style, capture receipt,
|
|
27
|
+
or Hosted deployment fingerprint that owns them. Compiler ABI, package versions, generated assets,
|
|
28
|
+
and the concrete release selected by World `current` likewise have separate identities.
|
|
29
|
+
|
|
30
|
+
Runtime manifest version 1 has one shape for local and Hosted delivery. Every map entry owns
|
|
31
|
+
`defaultTheme`, optional `systemThemes`, and a `themes` record whose leaves contain
|
|
32
|
+
`colorScheme`, `styleUrl`, and optional font/deployment identity. Hosted-only identity is additive
|
|
33
|
+
metadata on the same map entry; there is no delivery discriminator or parallel `styles` alias.
|
|
34
|
+
`parseTileflowRuntimeManifest()` accepts only this canonical shape. The strict bounded schema uses
|
|
35
|
+
the same portable map and theme IDs as authoring, rejects `system` as a published name, duplicate
|
|
36
|
+
font identities, unsafe owner-relative URLs, prototype-bearing input, unknown fields, and JSON
|
|
37
|
+
larger than 1 MiB.
|
|
38
|
+
|
|
39
|
+
```json
|
|
40
|
+
{
|
|
41
|
+
"version": 1,
|
|
42
|
+
"maps": {
|
|
43
|
+
"madrid": {
|
|
44
|
+
"defaultTheme": "light",
|
|
45
|
+
"systemThemes": {"light": "light", "dark": "dark"},
|
|
46
|
+
"themes": {
|
|
47
|
+
"light": {"colorScheme": "light", "styleUrl": "/styles/madrid/light.json"},
|
|
48
|
+
"dark": {"colorScheme": "dark", "styleUrl": "/styles/madrid/dark.json"}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Runtime fetches share a successful result for 30 seconds, never cache failures, use
|
|
56
|
+
`cache: 'no-store'`, enforce a 10-second timeout (configurable up to 60 seconds), compose an
|
|
57
|
+
external abort signal, and can be invalidated with `clearTileflowManifestCache()`.
|
|
58
|
+
|
|
59
|
+
Named map `view` values travel in the manifest. `resolveTileflowRuntimeTheme()` turns an omitted,
|
|
60
|
+
explicit, or `system` request with a supplied color scheme into one concrete published entry;
|
|
61
|
+
unknown names fail closed. `resolveTileflowRuntimeView()` defines the shared precedence as explicit
|
|
62
|
+
runtime values, then the manifest view, then the single exported `defaultTileflowRuntimeView`
|
|
63
|
+
(`[0, 20]`, zoom 2, bearing/pitch 0).
|
|
64
|
+
|
|
65
|
+
`TileflowRuntimeSource` is one object, `{map, manifestUrl?}`, selecting a named Tileflow map only
|
|
66
|
+
through its published manifest. React, Vue and Svelte share that source contract. Omitting the URL
|
|
67
|
+
means exactly `/tileflow/manifest.json`; other locations must be explicit. The native Core contract
|
|
68
|
+
uses `{map, manifestUrl}` with a required absolute URL and no discovery, as described in the
|
|
69
|
+
[native guide](native-resource-urls.md). The React Native type contract preserves that requirement.
|
|
70
|
+
No public framework Map source exposes a renderer discriminator or direct-style branch. The obsolete
|
|
71
|
+
`kind` and `style` fields are rejected; completely unmanaged maps use upstream MapLibre directly.
|
|
72
|
+
The runtime subpath does not import the config compiler, invent a localhost style URL, or change
|
|
73
|
+
image mode by environment.
|
|
74
|
+
|
|
75
|
+
Framework adapters import the browser-only lifecycle kernel explicitly:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import {
|
|
79
|
+
attachTileflowAtmosphere,
|
|
80
|
+
attachTileflowFairUseNotice,
|
|
81
|
+
attachTileflowMapLifecycle,
|
|
82
|
+
createTileflowSessionStarter,
|
|
83
|
+
createTileflowTransformRequest,
|
|
84
|
+
registerTileflowWorldRequestBridge,
|
|
85
|
+
} from '@tileflow/core/browser';
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The browser entry is not re-exported from the root. It is SSR-safe to import, has no MapLibre
|
|
89
|
+
runtime dependency, and reads no browser global during module evaluation. See the
|
|
90
|
+
[framework browser runtime contract](https://github.com/tileflow/tileflow-sdk/blob/main/docs/contracts/framework-browser-runtime.md) and the
|
|
91
|
+
[cartographic authoring contract](https://github.com/tileflow/tileflow-sdk/blob/main/docs/contracts/cartographic-authoring.md). Exact field
|
|
92
|
+
resolution and asset rules live in the
|
|
93
|
+
[map inheritance contract](https://github.com/tileflow/tileflow-sdk/blob/main/docs/contracts/map-inheritance.md).
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Shared visual primitives
|
|
2
|
+
|
|
3
|
+
Start with the [@tileflow/core guide](https://github.com/tileflow/tileflow-sdk/blob/main/packages/core/README.md) for installation and a complete first example.
|
|
4
|
+
|
|
5
|
+
The same small visual language is reused across every geographic domain. This keeps an agent from
|
|
6
|
+
having to learn different spellings for the same MapLibre behavior:
|
|
7
|
+
|
|
8
|
+
- `BackgroundStyle`: color, opacity, pattern, visibility, and zoom range.
|
|
9
|
+
- `FillStyle`: color, opacity, pattern, antialiasing, visibility, and zoom range.
|
|
10
|
+
- `LineStyle`: color, width, opacity, dash, blur, gap, offset, pattern, caps, joins, and zoom range.
|
|
11
|
+
- `LineHatchStyle`: repeated diagonal detail with color, opacity, spacing, size, angle, and zoom
|
|
12
|
+
range for a road structure.
|
|
13
|
+
- `TextStyle`: field, exact font face, exact fallback faces, size, color, halo, line-fitted
|
|
14
|
+
background, spacing metrics, transform, collision policy, rotation, fixed/variable anchoring, line
|
|
15
|
+
constraints, and zoom range.
|
|
16
|
+
- `IconStyle`: image, size, color, halo, rotation/alignment, collision policy, and zoom range.
|
|
17
|
+
- `CircleStyle`: radius, fill/stroke appearance, blur, opacity, pitch behavior, and zoom range.
|
|
18
|
+
- `ExtrusionStyle`: color, height, base, opacity, pattern, vertical gradient, and zoom range.
|
|
19
|
+
|
|
20
|
+
Compounds express common cartographic structures: `AreaStyle` has `fill` and `outline`,
|
|
21
|
+
`LineStackStyle` has `shadow`, `casing`, and `fill`, and `SymbolStyle` combines placement with
|
|
22
|
+
optional `text`, `icon`, and `marker`. Geographic selection remains owned by semantic modules;
|
|
23
|
+
ordinary visual styles intentionally do not accept raw source filters.
|
|
24
|
+
|
|
25
|
+
## Line-fitted text backgrounds
|
|
26
|
+
|
|
27
|
+
`text.background` draws one opaque strip behind each shaped line of a label, fitted to that line's
|
|
28
|
+
own width. Use it for captions whose lines differ in length, where a stretched icon plate
|
|
29
|
+
(`icon.textFit`) would cover the whole block.
|
|
30
|
+
|
|
31
|
+
<!-- docs:check -->
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import {fixed, poi, zoom} from '@tileflow/core';
|
|
35
|
+
|
|
36
|
+
export const captions = poi({
|
|
37
|
+
categories: ['arts-entertainment'],
|
|
38
|
+
styles: {
|
|
39
|
+
'arts-entertainment': {
|
|
40
|
+
text: {
|
|
41
|
+
background: {
|
|
42
|
+
color: fixed('#080E11', {reason: 'Caption strip'}),
|
|
43
|
+
fit: 'lines',
|
|
44
|
+
padding: 0.33,
|
|
45
|
+
},
|
|
46
|
+
color: fixed('#ECEEE9', {reason: 'Caption text'}),
|
|
47
|
+
font: fixed('Noto Sans Bold', {reason: 'Caption face'}),
|
|
48
|
+
letterSpacing: fixed(0.08, {reason: 'Caption tracking'}),
|
|
49
|
+
lineHeight: fixed(1.2, {reason: 'Caption leading'}),
|
|
50
|
+
size: zoom.linear([
|
|
51
|
+
[14, 11],
|
|
52
|
+
[18, 14],
|
|
53
|
+
]),
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Tileflow lowers the background to MapLibre properties only. `text-font` becomes a derived stack,
|
|
61
|
+
such as `Noto Sans Bold lines-v1-t80-l1200-p330`. Its glyphs keep the source advances and shapes and
|
|
62
|
+
add a rectangular cell, which the text halo paints. The halo width tracks `text-size / 6`, including
|
|
63
|
+
zoom curves. Shaping, wrapping, collision, variable anchors, fading and right-to-left text therefore
|
|
64
|
+
stay native, and the compiled style needs no runtime extension.
|
|
65
|
+
|
|
66
|
+
- The colour must be opaque. Neighbouring cells overlap where they join.
|
|
67
|
+
- `font` must name one stack served by the map's `glyphs` provider, without `fallbacks`. Maps that
|
|
68
|
+
use browser-rendered `fonts` reject backgrounds.
|
|
69
|
+
- `letterSpacing` and `lineHeight` must be constants. The derived stack encodes them in thousandths
|
|
70
|
+
of an em, and the layer uses those rounded values. Tracking runs from 0 to 0.5em, leading from 0.8
|
|
71
|
+
to 3 and padding from 0 to 1em.
|
|
72
|
+
- The background replaces the text halo. While a background is active, `haloWidth` and `haloBlur`
|
|
73
|
+
are derived, and `haloColor` is the strip colour. Later refinements of size, tracking, leading or
|
|
74
|
+
font re-derive the stack and halo.
|
|
75
|
+
- Collision still reserves the shaped block. Padding beyond it is drawn outside that box, so keep
|
|
76
|
+
`padding` (text padding) at least as large as the strip padding in pixels.
|
|
77
|
+
|
|
78
|
+
The map's `glyphs` provider must serve derived stacks. `deriveTileflowLineBackgroundGlyphs`
|
|
79
|
+
computes any range from the source stack's range, and `parseTileflowLineBackgroundFontStack`
|
|
80
|
+
recognises derived requests. Local previews and captures derive ranges themselves; hosted and
|
|
81
|
+
self-hosted providers must do the same.
|