@tileflow/react-native 0.1.0-alpha.7 → 0.1.0-alpha.8

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.
@@ -1,161 +0,0 @@
1
- # Native interaction contract
2
-
3
- The React Native `Map` accepts portable annotations, semantic interaction bindings, activation
4
- callbacks and an optional marker renderer. It exposes normalized targets and bounded data. The
5
- application owns persistent selection and its presentation. Tileflow does not render a popup,
6
- callout, tooltip, sheet, panel or modal.
7
-
8
- The mounted implementation uses a private renderer-neutral foundation. It reuses schemas, target
9
- types and diagnostics from the ordinary `@tileflow/interactions` root without
10
- importing its browser adapter, DOM implementation or `maplibre-gl`. Native map handles, style
11
- proofs, query ports and owners are not exported by `@tileflow/react-native`.
12
-
13
- ## Public Map inputs
14
-
15
- `Map` is generic over the supplied annotation type, so application data remains typed in
16
- `renderMarker` and `onInteractionEvent`. `annotations` and `interactions` are complete portable
17
- documents, not patches. The shared annotation and binding validators still accept web presentation
18
- metadata, but mobile does not use `popup` or `tooltip` fields.
19
-
20
- `renderMarker` composes marker content only. Tileflow retains the native marker host, coordinate,
21
- stable key, activation and authoritative accessibility wrapper. Without a renderer, Tileflow shows
22
- a bounded default marker. Each marker has the annotation label, a 44-by-44-point minimum touch
23
- target, button semantics and disabled accessibility state. Custom marker content does
24
- not create another accessible target.
25
-
26
- `onInteractionEvent` receives `target:activate` events for annotations and semantic POIs.
27
- `onInteractionDiagnostic` receives fixed structured diagnostics. There is no mobile selection-state
28
- prop, state callback or popup event. The application can retain the selected target in its own
29
- state and choose what, if anything, to render.
30
-
31
- ## Input ownership and validation
32
-
33
- One owner receives complete annotation and interaction-binding inputs. Omitted collections mean
34
- empty collections, not partial document patches.
35
-
36
- The owner uses the portable validators before planning any annotation changes. Invalid annotation
37
- or binding replacements preserve the entire previous valid collection. The initial fallback is an
38
- empty collection.
39
-
40
- Inputs are copied and frozen before they become committed values. The portable JSON perimeter
41
- rejects accessors, unsupported prototypes, non-finite numbers, unsafe keys, cycles, shared object
42
- references and oversized documents. The native foundation uses the same portable bounds: at most
43
- 1,000 annotations, 100 bindings, 256,000 JSON bytes, depth 64, 20,000 nodes and 50,000 properties per
44
- document. It does not expand large feature sets into annotation hosts.
45
-
46
- Diagnostics retain the portable code, level and a fixed bounded message. They do not include raw
47
- validator paths, input values, physical layer identifiers, style JSON, URLs or native error causes.
48
- Replacing callbacks alone does not change snapshots or annotation identities.
49
-
50
- ## Annotation reconciliation
51
-
52
- The private annotation plan contains `create`, `update`, `retain`, `remove` and final `order`.
53
- Planning uses stable validated annotation IDs. An existing marker ID retains its future host when
54
- coordinates, data or declarative surfaces change. Its update entry contains the old and new
55
- validated definitions. New IDs create entries; absent IDs produce removals in previous order.
56
- Unchanged IDs are retained, and final order follows the new document.
57
-
58
- An invalid replacement produces no partial creates, updates or removals. The mounted adapter uses
59
- the annotation ID as the React and native marker identity, so updates and reordering retain the
60
- compatible host while removal retires it.
61
-
62
- ## Activation without selection ownership
63
-
64
- A valid annotation activation emits one normalized `target:activate` event with annotation data,
65
- coordinate and optional binding ID. A semantic POI activation emits the same event type with a
66
- bounded POI target. No event changes or clears application selection. The application decides how
67
- to handle removed entities, style changes and dismissal.
68
-
69
- A syntactically invalid annotation or binding replacement retains the last valid collection. New
70
- touches, source/style replacement, backgrounding and unmount discard pending semantic queries, so
71
- late results cannot activate a target from a retired map or style.
72
-
73
- ## Finalized-style query port
74
-
75
- The semantic adapter receives a private lease for exactly one finalized style on one native Map.
76
- The lease supplies its style description, an ownership check and a cancelable asynchronous query
77
- operation. Its ownership check must establish the exact current native style and map, not merely
78
- compare style JSON. Style replacement must invalidate the old lease before another query can use
79
- it.
80
-
81
- Each query receives an immutable request containing the touch point, the relevant declared layer
82
- IDs and a result limit. A result envelope must echo the identical private request object. The
83
- platform adapter may construct that envelope only after rechecking native map/style ownership.
84
- Native handles never cross this neutral boundary. The adapter rejects a response associated with
85
- another request or Map even when the physical URLs or layer names happen to match.
86
-
87
- The mounted adapter uses the existing public query on the pinned MapLibre React Native Map. Because
88
- its iOS and Android serializers omit physical provenance, Tileflow queries one verified semantic
89
- layer at a time in finalized topmost order. The exact single-layer filter supplies the physical
90
- layer, source and source-layer identity that the neutral adapter verifies again. Results preserve
91
- their within-layer order and share one global 512-feature bound. This adds no Swift, Kotlin or
92
- public query API.
93
-
94
- A lease becomes current only after the private Tileflow native-surface owner accepts the matching
95
- style token. A JavaScript style-loaded callback or equal style JSON is insufficient. A newer touch
96
- rotates the query lease; style or source replacement, backgrounding and unmount permanently retire
97
- the old proof. Upstream queries have no abort handle, so cancellation is logical: Tileflow settles
98
- the old operation as stale immediately, stops issuing further layer queries and ignores late
99
- results.
100
-
101
- ## Semantic POI rules
102
-
103
- Only finalized `tileflow:interaction-manifest` metadata with exact version 2 is accepted. The
104
- adapter checks the POI identity convention, deduplication tuple, representation-priority list,
105
- field mapping, category vocabulary, rendered-topmost ordering and pointer-coordinate anchors.
106
- The artifact's `animation-frame` frequency field is its existing version-2 vocabulary; it does not
107
- introduce hover or JavaScript-frame scheduling into the native adapter.
108
-
109
- Every declared semantic layer must exist exactly once in that same style and match its declared
110
- source and source-layer. The source must exist. All declared POI representations share one
111
- source/source-layer feature-ID namespace. The adapter bounds the physical style inventory at 4,096
112
- layers and the semantic inventory at 256 declared layers. Missing or inconsistent metadata fails
113
- closed with `SEMANTIC_MANIFEST_MISMATCH`.
114
-
115
- Queries include only declared layers relevant to the current POI bindings and category filters.
116
- The port must supply rendered-topmost results. Duplicate representations of one stable feature ID
117
- use the manifest's representation priority, then layer priority, while retaining the earliest
118
- rendered position of that identity. Numeric and string IDs are distinct. Features from undeclared
119
- layers are excluded; an owned feature with a mismatched source or source-layer invalidates the
120
- query rather than being relabeled.
121
-
122
- A query processes at most 512 returned features. The port must report overflow rather than silently
123
- truncate a result whose omitted features could change representation selection. The native
124
- boundary emits only the existing semantic POI target shape, without physical layer identities.
125
- Properties are restricted to category, filter rank, size rank, icon, name and type. Category and
126
- rank ranges follow the portable contract; icon/type use bounded snake-case values and text uses the
127
- portable 4,096-character limit. Unknown fields and unsafe property accessors are not forwarded.
128
-
129
- Touch is the only activation modality in this adapter. There is no inferred hover, tooltip,
130
- long-press or keyboard behavior. An activation without a stable feature ID can still emit a target
131
- event. When present, feature IDs are safe integers or nonempty strings of at most 128 characters;
132
- no property-based fallback ID is invented.
133
-
134
- ## Retirement and observation
135
-
136
- Each mounted Map has independent inputs, annotation plans, style proofs,
137
- query tickets and subscribers. No authority, Map identity or asynchronous operation is shared
138
- between Maps. Marker and map delivery for one touch share one claim, so a marker activation cannot
139
- also activate a semantic feature underneath it.
140
-
141
- Ownership is checked again after asynchronous results and before event publication. Late results
142
- from disposed owners or retired styles are inert. Reentrant cancellation, observer replacement,
143
- exceptions and rejected observer promises cannot transfer ownership to another Map. Disposal is
144
- idempotent and exposes a final safe empty snapshot plus the annotation removal plan.
145
-
146
- Selection presentation remains outside this contract. The application owns its layout, focus,
147
- dismissal and accessibility behavior. Device location is also separate and requires an explicit
148
- application-owned permission and provider flow.
149
-
150
- ## Location composes as an ordinary application annotation
151
-
152
- Foreground device location does not extend the interaction owner or semantic query port. An
153
- application that chooses to display its latest validated fix can provide one stable-ID annotation
154
- through the existing `annotations` input, and remove that annotation when permission is revoked or
155
- the provider becomes unavailable. Tileflow does not request permission, start observation or
156
- recenter the camera when that annotation changes.
157
-
158
- See [Application-owned foreground location](./native-location.md) for the source-checkout recipe.
159
- The application remains responsible for provider lifetime, privacy policy, recenter controls and
160
- status/error UI, and the recipe does not alter the rule that all selection presentation remains
161
- application-owned.
@@ -1,148 +0,0 @@
1
- # Application-owned foreground location
2
-
3
- `@tileflow/react-native` does not own device location. A Tileflow `Map` renders normally without a
4
- location permission, provider, hook or native location module. The application decides whether to
5
- request foreground permission, which provider to use, when observation starts and stops, what fix is
6
- displayed, whether recentering is offered, and what status or error UI is appropriate.
7
-
8
- This guide describes a source-checkout recipe built only from the existing public `Map`, controlled
9
- camera and annotation contracts. It is not a new Tileflow runtime API and it does not add a location
10
- provider to the package.
11
-
12
- ## Boundary
13
-
14
- The package deliberately does not provide any of the following:
15
-
16
- - a Tileflow location component, hook, provider or permission API;
17
- - an iOS or Android location permission declaration;
18
- - a dependency on Expo Location, a geolocation package or another location provider;
19
- - a background task, background permission or location-history store;
20
- - a credential/session field for device location;
21
- - a location analytics event or precise-coordinate telemetry path;
22
- - a GeoIP fallback for device location;
23
- - implicit permission requests or tracking when `Map` mounts.
24
-
25
- The application must configure any native permission strings, manifest entries and provider setup
26
- required by the provider it chooses. Those application settings are separate from Tileflow's native
27
- Hosted credential configuration.
28
-
29
- ## Source-checkout recipe
30
-
31
- `../harness/ForegroundLocationExample.tsx` is a compile-only example owned by this package. Its
32
- provider boundary is injected:
33
-
34
- ```ts
35
- export type ApplicationForegroundLocationAdapter = Readonly<{
36
- requestPermission(): Promise<
37
- 'granted-precise' | 'granted-approximate' | 'denied' | 'unavailable'
38
- >;
39
- observe(listener: (update: ApplicationLocationObservation) => void): () => void;
40
- }>;
41
- ```
42
-
43
- No concrete provider is imported. The adapter is application code: it translates the provider's
44
- permission and foreground observation APIs into this small recipe-specific shape. The example and
45
- its state helper live under `harness/`, are absent from package exports, and are excluded from
46
- packed runtime files.
47
-
48
- ## Explicit application state
49
-
50
- The recipe models these states without unmounting the Map:
51
-
52
- | State | Meaning | Location annotation |
53
- | --------------------- | ------------------------------------------------------------ | ---------------------------- |
54
- | `idle` | The application has not requested permission. | none |
55
- | `requesting` | An explicit application action started a permission request. | none |
56
- | `granted-precise` | Foreground policy allows precise observation. | one marker after a valid fix |
57
- | `granted-approximate` | Foreground policy allows approximate observation. | one marker after a valid fix |
58
- | `denied` | The permission request was denied. | none |
59
- | `unavailable` | Permission/provider setup failed or emitted invalid data. | none |
60
- | `revoked` | A previously granted observation reported revocation. | removed immediately |
61
-
62
- Construction is inert: it neither requests permission nor starts observation. The example's **Use
63
- my location** control is the only permission-request trigger. A rejected provider promise is reduced
64
- to the application-level `unavailable` state; raw provider exceptions are not forwarded into Map
65
- props or Tileflow callbacks.
66
-
67
- ## Validate before Map props
68
-
69
- The adapter output is treated as untrusted runtime data even though the TypeScript adapter is typed.
70
- The recipe accepts only a plain record with exactly `longitude`, `latitude` and `accuracy` data
71
- properties. Longitude must be finite and within `[-180, 180]`, latitude finite and within
72
- `[-90, 90]`, and accuracy finite and nonnegative. Accessors, extra provider fields, invalid
73
- prototypes and non-finite values fail closed.
74
-
75
- Only a validated fix is converted to an annotation:
76
-
77
- ```ts
78
- {
79
- id: 'application-foreground-location',
80
- kind: 'marker',
81
- ariaLabel: 'Current location',
82
- coordinate: [longitude, latitude],
83
- data: {accuracyMeters: accuracy, precision: 'precise'},
84
- }
85
- ```
86
-
87
- Approximate permission uses the same stable ID and an `Approximate current location` label. The
88
- stable ID lets the existing annotation reconciler update one marker host instead of recreating a
89
- new marker for every fix. Revocation, denial or provider failure removes the annotation by returning
90
- an empty annotation collection.
91
-
92
- The marker uses the existing accessible Tileflow annotation wrapper. This recipe does not add a
93
- location-specific native marker component.
94
-
95
- ## Foreground observation lifetime
96
-
97
- Observation starts only when both conditions are true:
98
-
99
- 1. the application is in the foreground; and
100
- 2. its current permission state is `granted-precise` or `granted-approximate`.
101
-
102
- `AppState` is application-side orchestration in the example. Moving to background synchronously
103
- retires the current observation token and calls the provider cleanup. Returning to foreground
104
- starts a new observation only if the last application permission policy still allows it. Denied,
105
- revoked and unavailable states do not restart observation automatically.
106
-
107
- The React effect owns a replayable `mount()` epoch. Its cleanup immediately retires the current
108
- observation and any pending permission/provider callback without terminally disposing the
109
- controller. A StrictMode setup replay may therefore mount the same controller again; if the retained
110
- settled permission state is granted and the application is foregrounded, observation restarts
111
- without another permission prompt. If cleanup interrupted an in-flight permission request, that
112
- request is retired and the last settled application state is restored instead of accepting its late
113
- result. A real unmount or adapter replacement uses the same cleanup boundary, so late callbacks are
114
- inert even though no timing heuristic distinguishes them from a replay. The recipe does not persist
115
- fixes or observation history.
116
-
117
- `dispose()` remains a separate terminal operation for an owner that will never mount again. The
118
- React example does not use terminal disposal for ordinary effect cleanup.
119
-
120
- ## Recenter is explicit
121
-
122
- A new fix changes only the application-owned annotation. It does **not** change camera state.
123
-
124
- The example keeps the existing complete controlled `Map` view. An explicit **Recenter** action copies
125
- the latest validated coordinate into that controlled view while preserving zoom, bearing and pitch.
126
- The recipe uses no camera animation API or duration, so it introduces no animated movement for
127
- reduced-motion users. Applications that later add their own animated camera affordance must apply
128
- their own reduced-motion policy before doing so.
129
-
130
- Gestures continue to update the same controlled view through `onViewChange`; receiving another
131
- location fix never overwrites that user-driven camera state.
132
-
133
- ## Privacy and product scope
134
-
135
- The sample keeps only the latest in-memory fix needed for its marker. It does not store history,
136
- send coordinates to analytics, derive a device fix from GeoIP, start background tracking or attach
137
- location to Tileflow sessions/admission. Status and error presentation are application UI.
138
-
139
- Location also does not change the interaction presentation boundary. Tileflow annotations and
140
- semantic targets remain primitives and data; the application still owns any popup, callout,
141
- tooltip, sheet, panel, modal or other selection presentation.
142
-
143
- ## Qualification
144
-
145
- The source files describe the intended application contract; they are not evidence that a provider,
146
- permission prompt, simulator/device scenario, native build, package publication or Hosted service
147
- was executed. Typecheck the source-checkout harness and qualify the chosen provider in the
148
- application that owns its permission and privacy policy.