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

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.
@@ -19,6 +19,11 @@ var REFRESH_MARGIN_MS = 3e4;
19
19
  var ROTATION_AGE_MS = 6 * 60 * 60 * 1e3;
20
20
  var ROTATION_REQUEST_LIMIT = 1e4;
21
21
  var FUTURE_ISSUE_SKEW_MS = 3e4;
22
+ var RETRY_INITIAL_MS = 1e3;
23
+ var RETRY_LIMIT_MS = 6e4;
24
+ var RETRY_AFTER_LIMIT_MS = 15 * 60 * 1e3;
25
+ var RETRY_AFTER_SECONDS = /^\d{1,10}$/u;
26
+ var QUOTA_CODES = /* @__PURE__ */ new Set(["COMMERCIAL_MAP_LIMIT_REACHED", "COMMERCIAL_SPENDING_LIMIT_REACHED"]);
22
27
  var MOBILE_CREDENTIAL = /^tf_public_[0-9a-f]{48}$/u;
23
28
  var MAP_ID = /^map_[A-Za-z0-9_-]{16}$/u;
24
29
  var CREDENTIAL_ID = /^key_[A-Za-z0-9_-]{8,80}$/u;
@@ -57,6 +62,13 @@ var SUCCESS_KEYS = /* @__PURE__ */ new Set([
57
62
  "tilesetIds"
58
63
  ]);
59
64
  var RESTART_KEYS = /* @__PURE__ */ new Set(["code", "error", "retryWithNewSession", "sessionId"]);
65
+ var TERMINAL_CODES = /* @__PURE__ */ new Set([
66
+ "NATIVE_SESSION_INPUT_INVALID",
67
+ "NATIVE_SESSION_CREDENTIAL_REJECTED",
68
+ "NATIVE_SESSION_MAP_MISMATCH",
69
+ "NATIVE_SESSION_MAP_NOT_FOUND",
70
+ "NATIVE_SESSION_QUOTA_EXCEEDED"
71
+ ]);
60
72
  var ERROR_MESSAGES = {
61
73
  NATIVE_SESSION_INPUT_INVALID: "Native session configuration is invalid.",
62
74
  NATIVE_SESSION_RESPONSE_TOO_LARGE: "Native session response exceeded its byte limit.",
@@ -68,6 +80,7 @@ var ERROR_MESSAGES = {
68
80
  NATIVE_SESSION_MAP_NOT_FOUND: "Native session Map was not found.",
69
81
  NATIVE_SESSION_QUOTA_EXCEEDED: "Native session quota was exceeded.",
70
82
  NATIVE_SESSION_REJECTED: "Native session request was rejected.",
83
+ NATIVE_SESSION_RATE_LIMITED: "Native session requests are rate limited.",
71
84
  NATIVE_SESSION_UNAVAILABLE: "Native session service is unavailable.",
72
85
  NATIVE_SESSION_REPLACED: "Native session binding was replaced.",
73
86
  NATIVE_SESSION_DISPOSED: "Native session controller is disposed."
@@ -189,7 +202,29 @@ function createHostedNativeSessionController(input) {
189
202
  })
190
203
  );
191
204
  }
205
+ let terminal = null;
206
+ let failures = 0;
207
+ let retryAt = null;
208
+ let retryError = null;
209
+ function clearRetry() {
210
+ failures = 0;
211
+ retryAt = null;
212
+ retryError = null;
213
+ }
214
+ function delayRetry(error) {
215
+ let at;
216
+ try {
217
+ at = readClock();
218
+ } catch {
219
+ return;
220
+ }
221
+ if (retryAt === null || at >= retryAt) failures = Math.min(failures + 1, 16);
222
+ const backoff = Math.min(RETRY_INITIAL_MS * 2 ** (failures - 1), RETRY_LIMIT_MS);
223
+ retryAt = Math.max(retryAt ?? at, at + Math.max(backoff, retryAfterDelays.get(error) ?? 0));
224
+ retryError = Object.freeze({ code: error.code, kind: error.kind });
225
+ }
192
226
  function rotateIfRequired(at) {
227
+ if (terminal) throw new HostedNativeSessionError(terminal.code);
193
228
  if (!activeSession || binding.kind !== "hosted") return;
194
229
  if (at - activeSession.startedAt < ROTATION_AGE_MS && activeSession.requestCount < ROTATION_REQUEST_LIMIT) {
195
230
  return;
@@ -223,6 +258,7 @@ function createHostedNativeSessionController(input) {
223
258
  return current;
224
259
  }
225
260
  async function ensureAuthority(session, at) {
261
+ if (terminal) throw new HostedNativeSessionError(terminal.code);
226
262
  if (session.authority && session.authority.clockEpoch === clock.epoch && session.authority.validUntil - at > REFRESH_MARGIN_MS) {
227
263
  return { authority: session.authority, session };
228
264
  }
@@ -230,20 +266,28 @@ function createHostedNativeSessionController(input) {
230
266
  if (binding.kind !== "hosted") {
231
267
  throw new HostedNativeSessionError("NATIVE_SESSION_REPLACED");
232
268
  }
269
+ if (retryAt !== null && retryError && at < retryAt) {
270
+ if (session.authority && session.authority.clockEpoch === clock.epoch && session.authority.validUntil > at) {
271
+ return { authority: session.authority, session };
272
+ }
273
+ throw new HostedNativeSessionError(retryError.code);
274
+ }
233
275
  const operationBinding = binding;
234
276
  const abort = new SessionAbortController();
235
277
  operations.add(abort);
236
278
  const operation = {
237
279
  abort,
238
280
  promise: Promise.resolve().then(async () => {
239
- session.error = null;
240
- publishSession(session, "loading");
241
- if (abort.signal.aborted) throw abort.error();
242
281
  try {
282
+ if (abort.signal.aborted) throw abort.error();
283
+ session.error = null;
284
+ publishSession(session, "loading");
285
+ if (abort.signal.aborted) throw abort.error();
243
286
  const result = await bootstrap(session, operationBinding, abort, true, true);
244
287
  if (abort.signal.aborted) throw abort.error();
245
288
  result.session.authority = result.authority;
246
289
  result.session.error = null;
290
+ if (binding === operationBinding) clearRetry();
247
291
  if (activeSession === result.session) {
248
292
  publishSession(result.session, "ready");
249
293
  if (abort.signal.aborted) throw abort.error();
@@ -252,6 +296,22 @@ function createHostedNativeSessionController(input) {
252
296
  } catch (error) {
253
297
  const normalized = abort.signal.aborted ? abort.error() : normalizeError(error);
254
298
  const target = latestSession(session);
299
+ if (normalized.kind === "terminal" && binding === operationBinding && !terminal) {
300
+ const failure = Object.freeze({ code: normalized.code, kind: normalized.kind });
301
+ terminal = failure;
302
+ const rejectedSession = activeSession;
303
+ if (rejectedSession) {
304
+ rejectedSession.authority = null;
305
+ rejectedSession.error = failure;
306
+ }
307
+ for (const pending of [...operations]) pending.abort(failure.code);
308
+ if (binding === operationBinding && rejectedSession) {
309
+ publishSession(rejectedSession, "error", failure);
310
+ }
311
+ }
312
+ if (normalized.kind === "recoverable" && binding === operationBinding && !terminal) {
313
+ delayRetry(normalized);
314
+ }
255
315
  if (normalized.code !== "NATIVE_SESSION_REPLACED" && normalized.code !== "NATIVE_SESSION_DISPOSED") {
256
316
  target.error = Object.freeze({ code: normalized.code, kind: normalized.kind });
257
317
  publishSession(target, "error", target.error);
@@ -284,7 +344,10 @@ function createHostedNativeSessionController(input) {
284
344
  try {
285
345
  body = source.length ? JSON.parse(source) : null;
286
346
  } catch {
287
- throw new HostedNativeSessionError("NATIVE_SESSION_RESPONSE_JSON_INVALID");
347
+ if (response.status === 201) {
348
+ throw new HostedNativeSessionError("NATIVE_SESSION_RESPONSE_JSON_INVALID");
349
+ }
350
+ body = null;
288
351
  }
289
352
  if (response.status === 409 && allowRestart && isExactRestart(body, session.sessionId)) {
290
353
  assertCurrentSession(session, hostedBinding);
@@ -292,7 +355,12 @@ function createHostedNativeSessionController(input) {
292
355
  if (abort.signal.aborted) throw abort.error();
293
356
  return bootstrap(replacement, hostedBinding, abort, false, false);
294
357
  }
295
- if (response.status !== 201) throw statusError(response.status);
358
+ if (response.status !== 201) {
359
+ const failure = statusError(response.status, body);
360
+ const delay = retryAfterMs(response.headers, now);
361
+ if (delay !== null) retryAfterDelays.set(failure, delay);
362
+ throw failure;
363
+ }
296
364
  if (!hasNoStore(response.headers.get("cache-control"))) {
297
365
  throw new HostedNativeSessionError("NATIVE_SESSION_RESPONSE_INVALID");
298
366
  }
@@ -365,6 +433,16 @@ function createHostedNativeSessionController(input) {
365
433
  if (binding.kind === "direct" || !activeSession) {
366
434
  return Object.freeze({ status: "idle", source: "direct", lifecycle });
367
435
  }
436
+ if (terminal) {
437
+ return Object.freeze({
438
+ status: "error",
439
+ source: "hosted",
440
+ lifecycle,
441
+ mapId: binding.mapId,
442
+ sessionId: activeSession.sessionId,
443
+ error: terminal
444
+ });
445
+ }
368
446
  if (activeSession.operation) {
369
447
  return Object.freeze({
370
448
  status: "loading",
@@ -416,6 +494,7 @@ function createHostedNativeSessionController(input) {
416
494
  if (bindingGeneration !== expectedGeneration) {
417
495
  throw new HostedNativeSessionError("NATIVE_SESSION_REPLACED");
418
496
  }
497
+ if (terminal) throw new HostedNativeSessionError(terminal.code);
419
498
  checkedAt = readClock();
420
499
  if (resolved.authority.clockEpoch === clock.epoch && resolved.authority.validUntil > checkedAt) {
421
500
  admittedAuthorities.set(resolved.authority.public, resolved.authority);
@@ -470,7 +549,7 @@ function createHostedNativeSessionController(input) {
470
549
  }
471
550
  },
472
551
  transportBudget(authority) {
473
- if (disposed || lifecycle !== "foreground") return 0;
552
+ if (disposed || terminal || lifecycle !== "foreground") return 0;
474
553
  const stored = admittedAuthorities.get(authority);
475
554
  if (!stored || stored.bindingGeneration !== bindingGeneration) return 0;
476
555
  try {
@@ -489,6 +568,11 @@ function createHostedNativeSessionController(input) {
489
568
  async resume() {
490
569
  if (disposed) throw new HostedNativeSessionError("NATIVE_SESSION_DISPOSED");
491
570
  lifecycle = "foreground";
571
+ if (terminal) {
572
+ const error = new HostedNativeSessionError(terminal.code);
573
+ publish(currentSnapshot());
574
+ throw error;
575
+ }
492
576
  if (binding.kind === "direct" || !activeSession) {
493
577
  publish(currentSnapshot());
494
578
  return;
@@ -511,9 +595,27 @@ function createHostedNativeSessionController(input) {
511
595
  bindingGeneration += 1;
512
596
  for (const operation of operations) operation.abort("NATIVE_SESSION_REPLACED");
513
597
  binding = next;
598
+ terminal = null;
599
+ clearRetry();
514
600
  activeSession = next.kind === "hosted" ? createSession(next.surfaceId, readClock()) : null;
515
601
  publish(snapshotIdle(binding, activeSession, lifecycle));
516
602
  },
603
+ retry() {
604
+ if (disposed) throw new HostedNativeSessionError("NATIVE_SESSION_DISPOSED");
605
+ if (!terminal && retryAt === null) return;
606
+ terminal = null;
607
+ clearRetry();
608
+ if (activeSession && !activeSession.operation) activeSession.error = null;
609
+ publish(currentSnapshot());
610
+ },
611
+ retryDelay() {
612
+ if (disposed || terminal || retryAt === null) return null;
613
+ try {
614
+ return Math.max(0, Math.ceil(retryAt - readClock()));
615
+ } catch {
616
+ return null;
617
+ }
618
+ },
517
619
  subscribe(listener) {
518
620
  if (typeof listener !== "function") {
519
621
  throw new HostedNativeSessionError("NATIVE_SESSION_INPUT_INVALID");
@@ -608,7 +710,8 @@ function parseSuccess(value, binding, expectedSessionId, expectedSurfaceId) {
608
710
  const issuedAtMs = parseExactIso(value.issuedAt);
609
711
  const serverTimeMs = parseExactIso(value.serverTime);
610
712
  const expiresAtMs = parseExactIso(value.expiresAt);
611
- if (issuedAtMs > serverTimeMs + FUTURE_ISSUE_SKEW_MS || expiresAtMs <= serverTimeMs || expiresAtMs <= issuedAtMs || expiresAtMs - issuedAtMs > MAX_GRANT_LIFETIME_MS || expiresAtMs - issuedAtMs > MAX_GRANT_LIFETIME_MS) {
713
+ if (issuedAtMs > serverTimeMs + FUTURE_ISSUE_SKEW_MS || expiresAtMs <= serverTimeMs || expiresAtMs <= issuedAtMs || expiresAtMs - issuedAtMs > MAX_GRANT_LIFETIME_MS || // The local deadline is derived from serverTime, so this also bounds the transport budget.
714
+ expiresAtMs - serverTimeMs > MAX_GRANT_LIFETIME_MS) {
612
715
  throw new HostedNativeSessionError("NATIVE_SESSION_RESPONSE_INVALID");
613
716
  }
614
717
  const authority = {
@@ -787,18 +890,46 @@ function decodeUtf8(bytes) {
787
890
  function hasNoStore(value) {
788
891
  return typeof value === "string" && value.split(",").some((part) => part.trim().toLowerCase() === "no-store");
789
892
  }
790
- function statusError(status) {
893
+ function statusError(status, body) {
791
894
  if (status === 401) return new HostedNativeSessionError("NATIVE_SESSION_CREDENTIAL_REJECTED");
792
895
  if (status === 403) return new HostedNativeSessionError("NATIVE_SESSION_MAP_MISMATCH");
793
896
  if (status === 404) return new HostedNativeSessionError("NATIVE_SESSION_MAP_NOT_FOUND");
794
- if (status === 429) return new HostedNativeSessionError("NATIVE_SESSION_QUOTA_EXCEEDED");
795
- if (status === 503) return new HostedNativeSessionError("NATIVE_SESSION_UNAVAILABLE");
897
+ if (status === 429) {
898
+ return new HostedNativeSessionError(
899
+ isRecord(body) && typeof body.code === "string" && QUOTA_CODES.has(body.code) ? "NATIVE_SESSION_QUOTA_EXCEEDED" : "NATIVE_SESSION_RATE_LIMITED"
900
+ );
901
+ }
902
+ if (status === 408 || status >= 500 && status <= 599) {
903
+ return new HostedNativeSessionError("NATIVE_SESSION_UNAVAILABLE");
904
+ }
796
905
  return new HostedNativeSessionError("NATIVE_SESSION_REJECTED");
797
906
  }
907
+ var retryAfterDelays = /* @__PURE__ */ new WeakMap();
908
+ function retryAfterMs(headers, now) {
909
+ let value;
910
+ try {
911
+ value = headers.get("retry-after");
912
+ } catch {
913
+ return null;
914
+ }
915
+ if (typeof value !== "string" || value.length > 64) return null;
916
+ const text = value.trim();
917
+ let delay;
918
+ if (RETRY_AFTER_SECONDS.test(text)) delay = Number(text) * 1e3;
919
+ else {
920
+ const date = Date.parse(text);
921
+ if (!Number.isFinite(date)) return null;
922
+ try {
923
+ delay = date - readWallClock(now);
924
+ } catch {
925
+ return null;
926
+ }
927
+ }
928
+ return Math.min(Math.max(0, delay), RETRY_AFTER_LIMIT_MS);
929
+ }
798
930
  function errorKind(code) {
799
931
  if (code === "NATIVE_SESSION_DISPOSED" || code === "NATIVE_SESSION_REPLACED") return "cancelled";
800
- if (code === "NATIVE_SESSION_UNAVAILABLE") return "recoverable";
801
- return "terminal";
932
+ return TERMINAL_CODES.has(code) ? "terminal" : "recoverable";
802
933
  }
803
934
  function normalizeError(error, fallback = "NATIVE_SESSION_RESPONSE_INVALID") {
804
935
  return error instanceof HostedNativeSessionError ? error : new HostedNativeSessionError(fallback);
@@ -1,5 +1,5 @@
1
1
  import { TileflowNativeSourceState } from '@tileflow/core/native';
2
- import { v as MapSourceState } from '../contract-Bp-Im7tj.js';
2
+ import { x as MapSourceState } from '../contract-BaK24QZD.js';
3
3
  import '@maplibre/maplibre-react-native';
4
4
  import 'react';
5
5
  import 'react-native';
@@ -59,6 +59,22 @@ Bootstrap uses an independent OkHttp client or ephemeral URLSession channel. It
59
59
 
60
60
  Each context binds its validated bootstrap endpoint/credential. Retiring that context cancels its bootstrap without cancelling another Map. TypeScript alone parses session authority and makes admission decisions.
61
61
 
62
+ A bootstrap ends after 10 seconds, well inside the 30-second protected transport deadline. A stalled renewal therefore fails its bridge batch, and the tickets waiting on it, instead of outliving them. A ticket whose own deadline passes while JavaScript is still answering its batch fails alone; only a batch that JavaScript does not answer within 30 seconds retires the context. When admission retires the context of a mounted Map, the Map reports `renderer-error` and opens a new context for the same source, up to three times without a successful load in between.
63
+
64
+ ## Session failures
65
+
66
+ The controller classifies each bootstrap or renewal failure:
67
+
68
+ | Response | Result |
69
+ | ------------------------------------------------------------------------------ | ------------------------------------------------------- |
70
+ | 401, 403, 404 | Stops the session until `ref.retry()` or another source |
71
+ | 429 with `COMMERCIAL_MAP_LIMIT_REACHED` or `COMMERCIAL_SPENDING_LIMIT_REACHED` | Stops the session until `ref.retry()` or another source |
72
+ | Any other 429 | Rate limited: retries after a delay |
73
+ | 408, 5xx, network failure or timeout | Unavailable: retries after a delay |
74
+ | Another status, or a response that breaks the session contract | Retries after a delay |
75
+
76
+ A stopped session reports `session-error` through `onError`, including when a renewal is refused after the first load. A delay starts at one second and doubles up to one minute; a `Retry-After` in seconds or as an HTTP date lengthens it, up to 15 minutes. During the delay no bootstrap leaves the device, and a grant that is still valid keeps admitting protected requests. A successful bootstrap or `ref.retry()` clears the delay.
77
+
62
78
  The controller's private transport budget remains anchored to its existing local `validUntil`. Native never reconstructs a fresh lifetime from server timestamps. The budget is bounded by the earlier ticket-enqueue time, the native wait deadline and a safety margin, and is rechecked immediately before network start and redirects. Backgrounding, clock rollback, source replacement, disposal and expiry invalidate stale authority.
63
79
 
64
80
  No synchronous network hook, main thread or OkHttp interceptor waits for JavaScript. Suspended JavaScript therefore cannot authorize stale protected work.
@@ -93,7 +109,7 @@ Camera commands carry private monotonically correlated tokens. A successful publ
93
109
  | Protected response body | 8,388,608 bytes |
94
110
  | Pending bootstrap bridge work | 32 |
95
111
  | Bootstrap request / response | 2,048 / 65,536 bytes |
96
- | Bootstrap deadline | 30 seconds |
112
+ | Bootstrap deadline | 10 seconds |
97
113
 
98
114
  Cancellation retains transport capacity until the corresponding native cleanup boundary. Capacity bounds are transport safety limits, not product quotas or billing counters.
99
115
 
@@ -102,11 +102,13 @@ static NSTextCheckingResult *TFBootstrapMatch(NSString *value, NSString *pattern
102
102
  context.endpoint = clean; context.credential = credential; context.lastRequest = sequence;
103
103
  TFBootstrapWork *work = [TFBootstrapWork new];
104
104
  work.identifier = requestId; work.context = context; work.alive = [TFBootstrapFlag new];
105
- work.deadline = [self.scheduler nowMs] + 30000; work.completion = completion;
105
+ // Well inside the 30 s ticket and batch deadlines, so a stalled renewal fails its batch
106
+ // instead of letting the context retire under it.
107
+ work.deadline = [self.scheduler nowMs] + 10000; work.completion = completion;
106
108
  context.pending[requestId] = work; self.pendingCount++;
107
- work.timer = [self.scheduler after:30000 perform:^{ [self finish:work reply:nil]; }];
109
+ work.timer = [self.scheduler after:10000 perform:^{ [self finish:work reply:nil]; }];
108
110
  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:clean]
109
- cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:30];
111
+ cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:10];
110
112
  request.HTTPMethod = @"POST"; request.HTTPBody = [body dataUsingEncoding:NSUTF8StringEncoding];
111
113
  request.HTTPShouldHandleCookies = NO;
112
114
  [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
@@ -115,13 +117,17 @@ static NSTextCheckingResult *TFBootstrapMatch(NSString *value, NSString *pattern
115
117
  @try {
116
118
  work.network = [self.network start:request mayStart:^BOOL { return [self mayStart:work]; } completion:^(NSHTTPURLResponse *response, NSData *bytes) {
117
119
  [self.scheduler enqueue:^{
118
- NSString *cacheControl = @"";
120
+ NSString *cacheControl = @"", *retryAfter = @"";
119
121
  for (id name in response.allHeaderFields) {
120
- if ([name isKindOfClass:NSString.class] && [(NSString *)name caseInsensitiveCompare:@"Cache-Control"] == NSOrderedSame) cacheControl = response.allHeaderFields[name];
122
+ if (![name isKindOfClass:NSString.class]) continue;
123
+ if ([(NSString *)name caseInsensitiveCompare:@"Cache-Control"] == NSOrderedSame) cacheControl = response.allHeaderFields[name];
124
+ if ([(NSString *)name caseInsensitiveCompare:@"Retry-After"] == NSOrderedSame) retryAfter = response.allHeaderFields[name];
121
125
  }
126
+ // An oversized Retry-After is ignored rather than failing the reply.
127
+ if (!TFBootstrapString(retryAfter, 64)) retryAfter = @"";
122
128
  BOOL valid = [self mayStart:work] && [response isKindOfClass:NSHTTPURLResponse.class] && response.statusCode >= 100 && response.statusCode <= 599 &&
123
129
  TFBootstrapString(cacheControl, 1024) && [bytes isKindOfClass:NSData.class] && bytes.length <= 65536;
124
- NSDictionary *reply = valid ? @{@"status": @(response.statusCode), @"cacheControl": cacheControl, @"bodyBase64": [bytes base64EncodedStringWithOptions:0]} : nil;
130
+ NSDictionary *reply = valid ? @{@"status": @(response.statusCode), @"cacheControl": cacheControl, @"retryAfter": retryAfter, @"bodyBase64": [bytes base64EncodedStringWithOptions:0]} : nil;
125
131
  [self finish:work reply:reply];
126
132
  }];
127
133
  }];
@@ -168,8 +168,10 @@ static BOOL TFGrantShape(NSString *value) {
168
168
  work.response = response; work.failure = failure; work.delegate = delegate; work.releaseReservation = release;
169
169
  context.work[work.identifier] = work; [context.order addObject:work.identifier];
170
170
  work.timer = [self.scheduler after:30000 perform:^{
171
- if ([context.batchTickets containsObject:work.identifier]) [self retire:context.identifier];
172
- else [self fail:work];
171
+ // A slow renewal fails only this request. JavaScript still answers its batch, and the
172
+ // batch deadline alone retires a context whose JavaScript stopped answering.
173
+ if ([context.batchTickets containsObject:work.identifier]) [self event:context kind:@"cancel" fields:@{@"tickets": @[work.identifier]}];
174
+ [self fail:work];
173
175
  }];
174
176
  [self.scheduler enqueue:^{ [self drain:context]; }];
175
177
  }];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tileflow/react-native",
3
- "version": "0.1.0-alpha.5",
3
+ "version": "0.1.0-alpha.7",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Tileflow maps, camera control and semantic interactions for React Native.",
6
6
  "type": "module",
@@ -36,7 +36,7 @@
36
36
  }
37
37
  },
38
38
  "dependencies": {
39
- "@tileflow/core": ">=0.1.0-alpha.38 <0.1.0-beta.0",
39
+ "@tileflow/core": ">=0.1.0-alpha.42 <0.1.0-beta.0",
40
40
  "@tileflow/interactions": ">=0.1.0-alpha.6 <0.1.0-beta.0"
41
41
  },
42
42
  "peerDependencies": {