@tileflow/react-native 0.0.0-bootstrap.0 → 0.1.0-alpha.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 (86) hide show
  1. package/README.md +216 -4
  2. package/TileflowNativeAdmission.podspec +34 -0
  3. package/android/build.gradle +43 -0
  4. package/android/src/main/AndroidManifest.xml +3 -0
  5. package/android/src/main/java/dev/tileflow/reactnative/AdmissionBootstrap.kt +144 -0
  6. package/android/src/main/java/dev/tileflow/reactnative/AdmissionBootstrapOkHttpNetwork.kt +89 -0
  7. package/android/src/main/java/dev/tileflow/reactnative/AdmissionCatalog.kt +137 -0
  8. package/android/src/main/java/dev/tileflow/reactnative/AdmissionDocumentScope.kt +22 -0
  9. package/android/src/main/java/dev/tileflow/reactnative/AdmissionEngine.kt +335 -0
  10. package/android/src/main/java/dev/tileflow/reactnative/AdmissionModuleProvider.kt +68 -0
  11. package/android/src/main/java/dev/tileflow/reactnative/AdmissionOkHttpNetwork.kt +86 -0
  12. package/android/src/main/java/dev/tileflow/reactnative/AdmissionProviderLease.kt +56 -0
  13. package/android/src/main/java/dev/tileflow/reactnative/AdmissionTypes.kt +85 -0
  14. package/android/src/main/java/dev/tileflow/reactnative/AdmissionUrl.kt +100 -0
  15. package/android/src/main/java/dev/tileflow/reactnative/HandlerAdmissionScheduler.kt +16 -0
  16. package/android/src/main/java/dev/tileflow/reactnative/MobileConfiguration.kt +95 -0
  17. package/android/src/main/java/dev/tileflow/reactnative/MobileConfigurationResources.kt +43 -0
  18. package/android/src/main/java/dev/tileflow/reactnative/NativeDocumentOkHttpNetwork.kt +101 -0
  19. package/android/src/main/java/dev/tileflow/reactnative/NativeDocumentRegistry.kt +136 -0
  20. package/android/src/main/java/dev/tileflow/reactnative/NativeSurfaceState.kt +115 -0
  21. package/android/src/main/java/dev/tileflow/reactnative/TileflowNativeAdmissionModule.kt +234 -0
  22. package/android/src/main/java/dev/tileflow/reactnative/TileflowNativeAdmissionPackage.kt +16 -0
  23. package/android/src/main/java/dev/tileflow/reactnative/TileflowNativeConfigurationModule.kt +43 -0
  24. package/android/src/main/java/dev/tileflow/reactnative/TileflowNativeDocumentsModule.kt +80 -0
  25. package/android/src/main/java/dev/tileflow/reactnative/TileflowNativeSurfaceModule.kt +277 -0
  26. package/android/src/main/res/raw/tileflow_configuration_keep.xml +3 -0
  27. package/dist/contract-DWs-lDPo.d.ts +144 -0
  28. package/dist/index.d.ts +13 -0
  29. package/dist/index.js +6989 -0
  30. package/dist/internal/camera.d.ts +65 -0
  31. package/dist/internal/camera.js +325 -0
  32. package/dist/internal/hosted-binding.d.ts +18 -0
  33. package/dist/internal/hosted-binding.js +277 -0
  34. package/dist/internal/initial-view.d.ts +11 -0
  35. package/dist/internal/initial-view.js +8 -0
  36. package/dist/internal/interactions.d.ts +76 -0
  37. package/dist/internal/interactions.js +794 -0
  38. package/dist/internal/native-admission-bridge.d.ts +29 -0
  39. package/dist/internal/native-admission-bridge.js +351 -0
  40. package/dist/internal/native-admission.d.ts +78 -0
  41. package/dist/internal/native-admission.js +1614 -0
  42. package/dist/internal/native-appearance.d.ts +21 -0
  43. package/dist/internal/native-appearance.js +110 -0
  44. package/dist/internal/native-configuration-bridge.d.ts +9 -0
  45. package/dist/internal/native-configuration-bridge.js +343 -0
  46. package/dist/internal/session.d.ts +125 -0
  47. package/dist/internal/session.js +887 -0
  48. package/dist/internal/source-state.d.ts +11 -0
  49. package/dist/internal/source-state.js +26 -0
  50. package/dist/native-admission-contract-k7LKE1vW.d.ts +89 -0
  51. package/docs/native-admission.md +108 -0
  52. package/docs/native-configuration.md +210 -0
  53. package/docs/native-interactions.md +161 -0
  54. package/docs/native-location.md +148 -0
  55. package/ios/TFAdmissionBootstrap.h +17 -0
  56. package/ios/TFAdmissionBootstrap.mm +168 -0
  57. package/ios/TFAdmissionCatalog.h +13 -0
  58. package/ios/TFAdmissionCatalog.mm +193 -0
  59. package/ios/TFAdmissionDocumentScope.h +7 -0
  60. package/ios/TFAdmissionDocumentScope.mm +45 -0
  61. package/ios/TFAdmissionEngine.h +57 -0
  62. package/ios/TFAdmissionEngine.mm +371 -0
  63. package/ios/TFAdmissionInstallation.h +27 -0
  64. package/ios/TFAdmissionInstallation.mm +117 -0
  65. package/ios/TFAdmissionNetwork.h +16 -0
  66. package/ios/TFAdmissionNetwork.mm +268 -0
  67. package/ios/TFAdmissionURL.mm +116 -0
  68. package/ios/TFDocumentBounds.h +9 -0
  69. package/ios/TFDocumentBounds.mm +19 -0
  70. package/ios/TFMobileConfiguration.h +23 -0
  71. package/ios/TFMobileConfiguration.mm +139 -0
  72. package/ios/TFNativeAdmissionURLProtocol.mm +69 -0
  73. package/ios/TFNativeDocumentRegistry.h +28 -0
  74. package/ios/TFNativeDocumentRegistry.mm +193 -0
  75. package/ios/TFNativeDocumentURL.h +6 -0
  76. package/ios/TFNativeSurfaceState.h +28 -0
  77. package/ios/TFNativeSurfaceState.mm +128 -0
  78. package/ios/TileflowNativeAdmission.h +9 -0
  79. package/ios/TileflowNativeAdmission.mm +184 -0
  80. package/ios/TileflowNativeConfiguration.mm +41 -0
  81. package/ios/TileflowNativeDocuments.h +12 -0
  82. package/ios/TileflowNativeDocuments.mm +105 -0
  83. package/ios/TileflowNativeSurface.mm +403 -0
  84. package/ios/tileflow_native_admission.rb +45 -0
  85. package/package.json +51 -7
  86. package/react-native.config.cjs +13 -0
@@ -0,0 +1,105 @@
1
+ #import "TileflowNativeDocuments.h"
2
+ #import "TileflowNativeAdmission.h"
3
+ #import "TFNativeDocumentRegistry.h"
4
+ #import "TFAdmissionNetwork.h"
5
+ #import <React/RCTBridge.h>
6
+ #import <UIKit/UIKit.h>
7
+ #import <atomic>
8
+ #import <cmath>
9
+
10
+ @interface TileflowNativeDocuments () {
11
+ std::atomic_bool _invalidated;
12
+ }
13
+ @property (nonatomic, strong, nullable) TFNativeDocumentRegistry *registry;
14
+ @property (nonatomic, strong, nullable) TFAdmissionURLSessionNetwork *network;
15
+ @end
16
+
17
+ @implementation TileflowNativeDocuments
18
+ RCT_EXPORT_MODULE(TileflowNativeDocuments)
19
+ @synthesize bridge = _bridge;
20
+ + (BOOL)requiresMainQueueSetup { return YES; }
21
+ - (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); }
22
+ - (instancetype)init {
23
+ if ((self = [super init])) {
24
+ _invalidated.store(false);
25
+ NSNotificationCenter *center = NSNotificationCenter.defaultCenter;
26
+ [center addObserver:self selector:@selector(background:) name:UIApplicationWillResignActiveNotification object:nil];
27
+ [center addObserver:self selector:@selector(foreground:) name:UIApplicationDidBecomeActiveNotification object:nil];
28
+ }
29
+ return self;
30
+ }
31
+ - (void)dealloc { [NSNotificationCenter.defaultCenter removeObserver:self]; }
32
+ - (TFNativeDocumentRegistry *)ensureRegistry {
33
+ if (_invalidated.load()) [self invalid];
34
+ if (!self.registry) {
35
+ NSURLSessionConfiguration *configuration = NSURLSessionConfiguration.ephemeralSessionConfiguration;
36
+ configuration.protocolClasses = @[];
37
+ configuration.URLCache = nil;
38
+ configuration.HTTPCookieStorage = nil;
39
+ configuration.URLCredentialStorage = nil;
40
+ configuration.HTTPShouldSetCookies = NO;
41
+ configuration.HTTPAdditionalHeaders = nil;
42
+ configuration.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
43
+ TFAdmissionURLSessionNetwork *network = [[TFAdmissionURLSessionNetwork alloc] initWithConfiguration:configuration followsRedirects:NO responseByteLimit:8388608 queueDepth:16 concurrency:4];
44
+ self.network = network;
45
+ self.registry = [[TFNativeDocumentRegistry alloc] initWithScheduler:[TFContinuousAdmissionScheduler new] load:^id<TFAdmissionCancel>(NSURLRequest *request, TFNativeDocumentScope *scope, TFAdmissionStartGuard guard, TFAdmissionNetworkCompletion completion) {
46
+ if (scope) return scope.load(request, completion);
47
+ return [network start:request mayStart:guard completion:completion];
48
+ }];
49
+ [self.registry lifecycle:UIApplication.sharedApplication.applicationState == UIApplicationStateActive];
50
+ }
51
+ return self.registry;
52
+ }
53
+ RCT_REMAP_METHOD(openDocument, openDocumentURL:(NSString *)url maximumBytes:(double)maximumBytes installation:(NSString *)installation context:(NSString *)context developmentOrigin:(NSString *)developmentOrigin resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
54
+ @try {
55
+ if (_invalidated.load() || !std::isfinite(maximumBytes) || std::floor(maximumBytes) != maximumBytes || maximumBytes < 1 || maximumBytes > 8388608 ||
56
+ (installation == nil) != (context == nil)) [self invalid];
57
+ TFNativeDocumentScope *scope = nil;
58
+ if (installation) {
59
+ if (!TFAdmissionValidToken(installation) || !TFAdmissionValidToken(context)) [self invalid];
60
+ TileflowNativeAdmission *admission = [self.bridge moduleForClass:TileflowNativeAdmission.class];
61
+ if (!admission) [self invalid];
62
+ scope = [admission documentScopeForInstallation:installation context:context documents:self];
63
+ if (!scope) [self invalid];
64
+ }
65
+ NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
66
+ NSString *identifier = [[self ensureRegistry] open:request maximumBytes:(NSUInteger)maximumBytes scope:scope developmentOrigin:developmentOrigin];
67
+ resolve(@{@"document": identifier});
68
+ } @catch (NSException *exception) { [self reject:reject]; }
69
+ }
70
+ RCT_REMAP_METHOD(documentResponse, responseDocument:(NSString *)document resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
71
+ @try {
72
+ if (_invalidated.load() || !TFAdmissionValidToken(document) || !self.registry) [self invalid];
73
+ [self.registry response:document completion:^(NSDictionary *header) {
74
+ if (!header || self->_invalidated.load()) [self reject:reject];
75
+ else resolve(header);
76
+ }];
77
+ } @catch (NSException *exception) { [self reject:reject]; }
78
+ }
79
+ RCT_REMAP_METHOD(documentChunk, chunkDocument:(NSString *)document maximumBytes:(double)maximumBytes resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
80
+ @try {
81
+ if (_invalidated.load() || !TFAdmissionValidToken(document) || !self.registry || !std::isfinite(maximumBytes) ||
82
+ std::floor(maximumBytes) != maximumBytes || maximumBytes < 1 || maximumBytes > 65536) [self invalid];
83
+ resolve([self.registry chunk:document maximumBytes:(NSUInteger)maximumBytes]);
84
+ } @catch (NSException *exception) { [self reject:reject]; }
85
+ }
86
+ RCT_REMAP_METHOD(cancelDocument, cancelDocumentID:(NSString *)document resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
87
+ @try {
88
+ if (!TFAdmissionValidToken(document)) [self invalid];
89
+ [self.registry cancel:document];
90
+ resolve(@{@"cancelled": @YES});
91
+ } @catch (NSException *exception) { [self reject:reject]; }
92
+ }
93
+ - (void)retireNativeContext:(NSString *)context { [self.registry retireContext:context]; }
94
+ - (void)retireProtectedDocuments { [self.registry retireProtected]; }
95
+ - (void)background:(NSNotification *)notification { [self.registry lifecycle:NO]; }
96
+ - (void)foreground:(NSNotification *)notification { if (!_invalidated.load()) [self.registry lifecycle:YES]; }
97
+ - (void)invalidate {
98
+ _invalidated.store(true);
99
+ [NSNotificationCenter.defaultCenter removeObserver:self];
100
+ dispatch_async(dispatch_get_main_queue(), ^{ [self.registry close]; [self.network close]; });
101
+ }
102
+ - (void)reject:(RCTPromiseRejectBlock)reject { reject(@"NATIVE_DOCUMENT_UNAVAILABLE", @"Native document acquisition failed.", nil); }
103
+ - (void)invalid { @throw [NSException exceptionWithName:@"TFNativeDocumentInvalid" reason:@"Native document acquisition failed." userInfo:nil]; }
104
+ - (NSString *)description { return @"TileflowNativeDocuments(redacted)"; }
105
+ @end
@@ -0,0 +1,403 @@
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTEventEmitter.h>
3
+ #import <React/RCTInvalidating.h>
4
+ #import <MapLibre/MapLibre.h>
5
+ #import <MapLibreReactNative/MLRNMapView.h>
6
+ #import <MapLibreReactNative/MLRNCamera.h>
7
+ #import <QuartzCore/QuartzCore.h>
8
+ #import "TFAdmissionEngine.h"
9
+ #import "TFNativeSurfaceState.h"
10
+ #import <cmath>
11
+
12
+ static void TFSurfaceInvalid(void) {
13
+ @throw [NSException exceptionWithName:@"TFNativeSurfaceInvalid" reason:@"Native surface operation failed." userInfo:nil];
14
+ }
15
+ static void TFSurfaceReject(RCTPromiseRejectBlock reject) { reject(@"NATIVE_SURFACE_UNAVAILABLE", @"Native surface operation failed.", nil); }
16
+ static NSUInteger TFSurfaceInteger(id value) {
17
+ if (![value isKindOfClass:NSNumber.class] || CFGetTypeID((__bridge CFTypeRef)value) == CFBooleanGetTypeID()) TFSurfaceInvalid();
18
+ double number = [value doubleValue];
19
+ if (!std::isfinite(number) || number < 1 || number > 9007199254740991.0 || std::floor(number) != number) TFSurfaceInvalid();
20
+ return (NSUInteger)number;
21
+ }
22
+ static double TFSurfaceNumber(id value, double minimum, double maximum) {
23
+ if (![value isKindOfClass:NSNumber.class] || CFGetTypeID((__bridge CFTypeRef)value) == CFBooleanGetTypeID()) TFSurfaceInvalid();
24
+ double number = [value doubleValue];
25
+ if (!std::isfinite(number) || number < minimum || number > maximum) TFSurfaceInvalid();
26
+ return number;
27
+ }
28
+ static NSDictionary *TFSurfaceView(id value) {
29
+ if (![value isKindOfClass:NSDictionary.class] || [value count] != 4) TFSurfaceInvalid();
30
+ for (id key in value) if (![@[@"center", @"zoom", @"bearing", @"pitch"] containsObject:key]) TFSurfaceInvalid();
31
+ id center = value[@"center"];
32
+ if (![center isKindOfClass:NSArray.class] || [center count] != 2) TFSurfaceInvalid();
33
+ return @{@"center": @[@(TFSurfaceNumber(center[0], -180, 180)), @(TFSurfaceNumber(center[1], -90, 90))],
34
+ @"zoom": @(TFSurfaceNumber(value[@"zoom"], 0, 24)), @"bearing": @(TFSurfaceNumber(value[@"bearing"], -180, 180)),
35
+ @"pitch": @(TFSurfaceNumber(value[@"pitch"], 0, 85))};
36
+ }
37
+ static MLRNMapView *TFSurfaceMap(UIView *root) {
38
+ NSMutableArray<UIView *> *queue = [NSMutableArray arrayWithObject:root];
39
+ MLRNMapView *found = nil; NSUInteger visited = 0;
40
+ while (queue.count) {
41
+ if (++visited > 1024) TFSurfaceInvalid();
42
+ UIView *view = queue.firstObject; [queue removeObjectAtIndex:0];
43
+ if ([view isKindOfClass:MLRNMapView.class]) {
44
+ if (found) TFSurfaceInvalid(); found = (MLRNMapView *)view;
45
+ } else {
46
+ if (queue.count + view.subviews.count > 1024) TFSurfaceInvalid();
47
+ [queue addObjectsFromArray:view.subviews];
48
+ }
49
+ }
50
+ if (!found) TFSurfaceInvalid();
51
+ return found;
52
+ }
53
+ static MLRNCamera *TFSurfaceCamera(UIView *root, MLRNMapView *map) {
54
+ NSMutableArray<UIView *> *queue = [NSMutableArray arrayWithObject:root];
55
+ MLRNCamera *found = nil; NSUInteger visited = 0;
56
+ while (queue.count) {
57
+ if (++visited > 1024) TFSurfaceInvalid();
58
+ UIView *view = queue.firstObject; [queue removeObjectAtIndex:0];
59
+ if ([view isKindOfClass:MLRNCamera.class]) {
60
+ if (found) TFSurfaceInvalid(); found = (MLRNCamera *)view;
61
+ }
62
+ if (queue.count + view.subviews.count > 1024) TFSurfaceInvalid();
63
+ [queue addObjectsFromArray:view.subviews];
64
+ }
65
+ id registered = map.reactCamera;
66
+ if (!found || ![registered isKindOfClass:MLRNCamera.class] || registered != found || ((MLRNCamera *)registered).map != map) TFSurfaceInvalid();
67
+ return found;
68
+ }
69
+
70
+ @interface TFSurfaceLayoutProbe : UIView
71
+ @property (nonatomic, copy) dispatch_block_t changed;
72
+ @end
73
+ @implementation TFSurfaceLayoutProbe
74
+ - (void)layoutSubviews { [super layoutSubviews]; if (self.changed) self.changed(); }
75
+ - (void)didMoveToWindow { [super didMoveToWindow]; if (self.changed) self.changed(); }
76
+ @end
77
+
78
+ @interface TFSurfaceAttachment : NSObject <MLNMapViewDelegate>
79
+ @property (nonatomic, copy) NSString *identifier;
80
+ @property (nonatomic, strong) UIView *root;
81
+ @property (nonatomic, strong) MLRNMapView *map;
82
+ @property (nonatomic, weak) id<MLNMapViewDelegate> previous;
83
+ @property (nonatomic, strong) TFSurfaceLayoutProbe *probe;
84
+ @property (nonatomic, strong) TFNativeSurfaceState *state;
85
+ @property (nonatomic, copy) NSString *token;
86
+ @property (nonatomic, strong, nullable) NSArray *layoutSnapshot;
87
+ @property (nonatomic, strong) NSMutableArray<dispatch_block_t> *detached;
88
+ @property (nonatomic, copy, nullable) dispatch_block_t deadline;
89
+ @property (nonatomic, copy, nullable) dispatch_block_t layoutCancel;
90
+ @property (nonatomic) BOOL retiring;
91
+ @property (nonatomic) BOOL foreground;
92
+ @property (nonatomic) BOOL observing;
93
+ - (void)attach;
94
+ - (void)retire;
95
+ - (BOOL)owns;
96
+ - (MLNStyle *)style;
97
+ - (NSDictionary *)view;
98
+ - (void)sampleLayout;
99
+ @end
100
+
101
+ @implementation TFSurfaceAttachment
102
+ - (BOOL)owns {
103
+ if (self.retiring || self.map.delegate != self) return NO;
104
+ @try { return TFSurfaceMap(self.root) == self.map; }
105
+ @catch (__unused NSException *exception) { return NO; }
106
+ }
107
+ - (NSString *)marker { return [@"__tileflow_native_style_" stringByAppendingString:self.token ?: @""]; }
108
+ - (MLNStyle *)style { MLNStyle *style = self.map.style; return [style layerWithIdentifier:self.marker] ? style : nil; }
109
+ - (NSDictionary *)view {
110
+ CLLocationCoordinate2D center = self.map.centerCoordinate;
111
+ double longitude = std::fmod(center.longitude + 540, 360) - 180;
112
+ double bearing = std::fmod(self.map.direction + 540, 360) - 180;
113
+ return @{@"center": @[@(longitude), @(center.latitude)], @"zoom": @(self.map.zoomLevel), @"bearing": @(bearing), @"pitch": @(self.map.camera.pitch)};
114
+ }
115
+ - (void)attach {
116
+ self.token = @""; self.detached = [NSMutableArray array]; self.previous = self.map.delegate;
117
+ if ([self.previous isKindOfClass:TFSurfaceAttachment.class]) TFSurfaceInvalid();
118
+ self.map.delegate = self;
119
+ [self.map addObserver:self forKeyPath:@"delegate" options:0 context:(__bridge void *)self]; self.observing = YES;
120
+ TFSurfaceLayoutProbe *probe = [[TFSurfaceLayoutProbe alloc] initWithFrame:self.root.bounds];
121
+ probe.userInteractionEnabled = NO; probe.accessibilityElementsHidden = YES; probe.alpha = 0;
122
+ probe.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
123
+ __weak TFSurfaceAttachment *weakSelf = self;
124
+ probe.changed = ^{
125
+ TFSurfaceAttachment *owner = weakSelf;
126
+ if (!owner) return;
127
+ if (owner.retiring && !owner.root.window) {
128
+ NSArray *callbacks = [owner.detached copy]; [owner.detached removeAllObjects];
129
+ owner.probe.changed = nil; [owner.probe removeFromSuperview];
130
+ for (dispatch_block_t callback in callbacks) callback();
131
+ } else [owner sampleLayout];
132
+ };
133
+ self.probe = probe; [self.root addSubview:probe]; [self sampleLayout];
134
+ }
135
+ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
136
+ if (context == (__bridge void *)self && object == self.map) {
137
+ if (!self.retiring && self.map.delegate != self) [self.state fail];
138
+ return;
139
+ }
140
+ [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
141
+ }
142
+ - (void)sampleLayout {
143
+ if (self.retiring) return;
144
+ NSArray *snapshot = @[@(self.root.bounds.size.width), @(self.root.bounds.size.height), @(self.map.bounds.size.width), @(self.map.bounds.size.height),
145
+ @(self.root.frame.origin.x), @(self.root.frame.origin.y), @(self.root.window && self.map.window && self.foreground)];
146
+ if ([snapshot isEqual:self.layoutSnapshot]) return;
147
+ self.layoutSnapshot = snapshot;
148
+ BOOL visible = [snapshot.lastObject boolValue];
149
+ for (NSUInteger index = 0; index < 4; index++) visible = visible && [snapshot[index] doubleValue] > 0;
150
+ [self.state layout:visible];
151
+ }
152
+ - (void)retire {
153
+ if (self.retiring) return;
154
+ self.retiring = YES; [self.state close];
155
+ if (self.deadline) dispatch_block_cancel(self.deadline); self.deadline = nil;
156
+ if (self.layoutCancel) self.layoutCancel(); self.layoutCancel = nil;
157
+ if (self.observing) { [self.map removeObserver:self forKeyPath:@"delegate" context:(__bridge void *)self]; self.observing = NO; }
158
+ if (self.map.delegate == self) self.map.delegate = self.previous;
159
+ if (!self.root.window && self.probe.changed) self.probe.changed();
160
+ }
161
+ - (BOOL)respondsToSelector:(SEL)selector { return [super respondsToSelector:selector] || [self.previous respondsToSelector:selector]; }
162
+ - (id)forwardingTargetForSelector:(SEL)selector { return [self.previous respondsToSelector:selector] ? self.previous : [super forwardingTargetForSelector:selector]; }
163
+ - (void)mapView:(MLNMapView *)mapView didFinishLoadingStyle:(MLNStyle *)style {
164
+ if ([self owns] && style == self.style) [self.state loaded:self.token identity:style];
165
+ if ([self.previous respondsToSelector:_cmd]) [self.previous mapView:mapView didFinishLoadingStyle:style];
166
+ }
167
+ - (void)mapViewWillStartRenderingFrame:(MLNMapView *)mapView {
168
+ if ([self owns]) { [self sampleLayout]; [self.state frameStart:self.style]; }
169
+ if ([self.previous respondsToSelector:_cmd]) [self.previous mapViewWillStartRenderingFrame:mapView];
170
+ }
171
+ - (void)mapViewDidFinishRenderingMap:(MLNMapView *)mapView fullyRendered:(BOOL)fully {
172
+ if ([self.previous respondsToSelector:_cmd]) [self.previous mapViewDidFinishRenderingMap:mapView fullyRendered:fully];
173
+ }
174
+ - (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView fullyRendered:(BOOL)fully {
175
+ if ([self owns]) [self.state frameEnd:self.style fully:fully];
176
+ if ([self.previous respondsToSelector:_cmd]) [self.previous mapViewDidFinishRenderingFrame:mapView fullyRendered:fully];
177
+ }
178
+ - (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView fullyRendered:(BOOL)fully frameEncodingTime:(double)encoding frameRenderingTime:(double)rendering {
179
+ if ([self owns]) [self.state frameEnd:self.style fully:fully];
180
+ if ([self.previous respondsToSelector:_cmd]) [self.previous mapViewDidFinishRenderingFrame:mapView fullyRendered:fully frameEncodingTime:encoding frameRenderingTime:rendering];
181
+ else if ([self.previous respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)]) [self.previous mapViewDidFinishRenderingFrame:mapView fullyRendered:fully];
182
+ }
183
+ - (void)mapViewDidFinishRenderingFrame:(MLNMapView *)mapView fullyRendered:(BOOL)fully renderingStats:(MLNRenderingStats *)stats {
184
+ if ([self owns]) [self.state frameEnd:self.style fully:fully];
185
+ if ([self.previous respondsToSelector:_cmd]) [self.previous mapViewDidFinishRenderingFrame:mapView fullyRendered:fully renderingStats:stats];
186
+ else if ([self.previous respondsToSelector:@selector(mapViewDidFinishRenderingFrame:fullyRendered:)]) [self.previous mapViewDidFinishRenderingFrame:mapView fullyRendered:fully];
187
+ }
188
+ - (void)mapView:(MLNMapView *)mapView regionWillChangeWithReason:(MLNCameraChangeReason)reason animated:(BOOL)animated {
189
+ MLNCameraChangeReason gestures = MLNCameraChangeReasonResetNorth | MLNCameraChangeReasonGesturePan | MLNCameraChangeReasonGesturePinch |
190
+ MLNCameraChangeReasonGestureRotate | MLNCameraChangeReasonGestureZoomIn | MLNCameraChangeReasonGestureZoomOut |
191
+ MLNCameraChangeReasonGestureOneFingerZoom | MLNCameraChangeReasonGestureTilt;
192
+ if ([self owns]) { if (reason & gestures) [self.state gestureStart:self.view]; else [self.state gestureEnd:self.view]; }
193
+ if ([self.previous respondsToSelector:_cmd]) [self.previous mapView:mapView regionWillChangeWithReason:reason animated:animated];
194
+ else if ([self.previous respondsToSelector:@selector(mapView:regionWillChangeAnimated:)]) [self.previous mapView:mapView regionWillChangeAnimated:animated];
195
+ }
196
+ - (void)mapView:(MLNMapView *)mapView regionIsChangingWithReason:(MLNCameraChangeReason)reason {
197
+ if ([self owns]) [self.state gestureChange:self.view];
198
+ if ([self.previous respondsToSelector:_cmd]) [self.previous mapView:mapView regionIsChangingWithReason:reason];
199
+ else if ([self.previous respondsToSelector:@selector(mapViewRegionIsChanging:)]) [self.previous mapViewRegionIsChanging:mapView];
200
+ }
201
+ - (void)mapView:(MLNMapView *)mapView regionDidChangeWithReason:(MLNCameraChangeReason)reason animated:(BOOL)animated {
202
+ if ([self owns]) [self.state gestureEnd:self.view];
203
+ if ([self.previous respondsToSelector:_cmd]) [self.previous mapView:mapView regionDidChangeWithReason:reason animated:animated];
204
+ else if ([self.previous respondsToSelector:@selector(mapView:regionDidChangeAnimated:)]) [self.previous mapView:mapView regionDidChangeAnimated:animated];
205
+ }
206
+ - (NSString *)description { return @"TFSurfaceAttachment"; }
207
+ @end
208
+
209
+ @interface TileflowNativeSurface : RCTEventEmitter <RCTBridgeModule, RCTInvalidating>
210
+ @property (nonatomic, strong) NSMutableDictionary<NSString *, TFSurfaceAttachment *> *surfaces;
211
+ @property (nonatomic, strong) NSMutableDictionary<NSNumber *, TFSurfaceLayoutProbe *> *rootRetirements;
212
+ @property (nonatomic) BOOL invalidated;
213
+ @property (nonatomic) BOOL observing;
214
+ @end
215
+ @implementation TileflowNativeSurface
216
+ RCT_EXPORT_MODULE(TileflowNativeSurface)
217
+ @synthesize viewRegistry_DEPRECATED = _viewRegistry_DEPRECATED;
218
+ + (BOOL)requiresMainQueueSetup { return YES; }
219
+ - (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); }
220
+ - (NSArray<NSString *> *)supportedEvents { return @[@"TileflowNativeSurfaceEvent"]; }
221
+ - (void)startObserving { self.observing = YES; }
222
+ - (void)stopObserving { self.observing = NO; }
223
+ - (instancetype)init {
224
+ if ((self = [super init])) {
225
+ _surfaces = [NSMutableDictionary dictionary]; _rootRetirements = [NSMutableDictionary dictionary];
226
+ [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(background:) name:UIApplicationWillResignActiveNotification object:nil];
227
+ [NSNotificationCenter.defaultCenter addObserver:self selector:@selector(foreground:) name:UIApplicationDidBecomeActiveNotification object:nil];
228
+ }
229
+ return self;
230
+ }
231
+ - (void)dealloc { [NSNotificationCenter.defaultCenter removeObserver:self]; }
232
+ - (TFSurfaceAttachment *)current:(NSString *)identifier allowFailed:(BOOL)allowFailed {
233
+ TFSurfaceAttachment *surface = self.surfaces[identifier];
234
+ if (self.invalidated || !surface || ![surface owns] || (!allowFailed && !surface.state.active)) TFSurfaceInvalid();
235
+ return surface;
236
+ }
237
+ RCT_REMAP_METHOD(attachSurface, attachSurfaceWithTag:(NSNumber *)tag resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
238
+ @try {
239
+ TFSurfaceInteger(tag);
240
+ if (self.invalidated || !self.observing || !self.viewRegistry_DEPRECATED) TFSurfaceInvalid();
241
+ [self.viewRegistry_DEPRECATED addUIBlock:^(RCTViewRegistry *registry) {
242
+ @try {
243
+ if (self.invalidated || self.surfaces.count >= 16) TFSurfaceInvalid();
244
+ UIView *root = [registry viewForReactTag:tag];
245
+ if (!root || !root.window) TFSurfaceInvalid();
246
+ MLRNMapView *map = TFSurfaceMap(root);
247
+ for (TFSurfaceAttachment *existing in self.surfaces.allValues) if (existing.map == map) TFSurfaceInvalid();
248
+ TFSurfaceAttachment *surface = [TFSurfaceAttachment new];
249
+ surface.identifier = NSUUID.UUID.UUIDString; surface.root = root; surface.map = map;
250
+ surface.foreground = UIApplication.sharedApplication.applicationState == UIApplicationStateActive;
251
+ __weak TileflowNativeSurface *weakSelf = self;
252
+ __weak TFSurfaceAttachment *weakSurface = surface;
253
+ surface.state = [[TFNativeSurfaceState alloc] initWithSurface:surface.identifier emit:^(NSDictionary *event) {
254
+ TileflowNativeSurface *owner = weakSelf; TFSurfaceAttachment *attachment = weakSurface;
255
+ if (!attachment) return;
256
+ if ([event[@"kind"] isEqual:@"render"] && attachment.deadline) { dispatch_block_cancel(attachment.deadline); attachment.deadline = nil; }
257
+ if (!owner || owner.invalidated || !owner.observing) { [attachment.state close]; return; }
258
+ [owner sendEventWithName:@"TileflowNativeSurfaceEvent" body:event];
259
+ }];
260
+ self.surfaces[surface.identifier] = surface;
261
+ @try { [surface attach]; } @catch (NSException *exception) { [surface retire]; @throw; }
262
+ resolve(@{@"surface": surface.identifier});
263
+ } @catch (NSException *exception) { TFSurfaceReject(reject); }
264
+ }];
265
+ } @catch (NSException *exception) { TFSurfaceReject(reject); }
266
+ }
267
+ RCT_REMAP_METHOD(expectStyle, expectSurface:(NSString *)identifier style:(NSString *)token resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
268
+ @try {
269
+ TFSurfaceAttachment *surface = [self current:identifier allowFailed:YES];
270
+ [surface.state expect:token]; surface.token = token;
271
+ if (surface.deadline) dispatch_block_cancel(surface.deadline);
272
+ __weak TFSurfaceAttachment *weakSurface = surface;
273
+ dispatch_block_t deadline = dispatch_block_create((dispatch_block_flags_t)0, ^{
274
+ TFSurfaceAttachment *current = weakSurface;
275
+ if (current && !current.retiring && [current.token isEqual:token]) [current.state fail];
276
+ });
277
+ surface.deadline = deadline;
278
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC), dispatch_get_main_queue(), deadline);
279
+ resolve(@{@"accepted": @YES});
280
+ } @catch (NSException *exception) { TFSurfaceReject(reject); }
281
+ }
282
+ RCT_REMAP_METHOD(acknowledgeSurface, acknowledgeSurface:(NSString *)identifier sequence:(NSNumber *)sequence resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
283
+ @try { if (!TFAdmissionValidToken(identifier)) TFSurfaceInvalid(); [self.surfaces[identifier].state acknowledge:TFSurfaceInteger(sequence)]; resolve(@{@"acknowledged": @YES}); }
284
+ @catch (NSException *exception) { TFSurfaceReject(reject); }
285
+ }
286
+ RCT_REMAP_METHOD(commitLayout, commitSurface:(NSString *)identifier style:(NSString *)token resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
287
+ @try {
288
+ TFSurfaceAttachment *surface = [self current:identifier allowFailed:NO];
289
+ if (surface.layoutCancel) TFSurfaceInvalid();
290
+ __block BOOL finished = NO;
291
+ void (^complete)(BOOL) = ^(BOOL success) {
292
+ if (finished) return; finished = YES; surface.layoutCancel = nil;
293
+ @try {
294
+ if (!success || [self current:identifier allowFailed:NO] != surface || ![surface.token isEqual:token]) TFSurfaceInvalid();
295
+ [surface sampleLayout]; resolve(@{@"layout": @([surface.state commit:token])});
296
+ } @catch (NSException *exception) { TFSurfaceReject(reject); }
297
+ };
298
+ surface.layoutCancel = ^{ complete(NO); };
299
+ [CATransaction begin];
300
+ [CATransaction setCompletionBlock:^{ complete(YES); }];
301
+ [surface.root setNeedsLayout]; [surface.root layoutIfNeeded];
302
+ [CATransaction commit];
303
+ } @catch (NSException *exception) { TFSurfaceReject(reject); }
304
+ }
305
+ RCT_REMAP_METHOD(requestFrame, requestFrameForSurface:(NSString *)identifier style:(NSString *)token resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
306
+ @try {
307
+ TFSurfaceAttachment *surface = [self current:identifier allowFailed:NO];
308
+ if (![surface.token isEqual:token]) TFSurfaceInvalid();
309
+ if (!surface.style) TFSurfaceInvalid();
310
+ [surface.state request:token];
311
+ [surface.map triggerRepaint];
312
+ resolve(@{@"requested": @YES});
313
+ } @catch (NSException *exception) { TFSurfaceReject(reject); }
314
+ }
315
+ RCT_REMAP_METHOD(applyCamera, applyCameraForSurface:(NSString *)identifier sequence:(NSNumber *)sequence view:(NSDictionary *)input resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
316
+ @try {
317
+ TFSurfaceAttachment *surface = [self current:identifier allowFailed:NO];
318
+ NSDictionary *target = TFSurfaceView(input); NSUInteger command = TFSurfaceInteger(sequence);
319
+ NSUInteger invalidation = [surface.state beginCommand:command];
320
+ if (!invalidation) TFSurfaceInvalid();
321
+ MLRNCamera *camera = TFSurfaceCamera(surface.root, surface.map);
322
+ // The pinned iOS CameraUpdateItem computes altitude before applying a changed pitch.
323
+ // Apply center/bearing/pitch first, then compute the exact zoom at that pitch.
324
+ NSMutableDictionary *pitchStop = [target mutableCopy];
325
+ [pitchStop removeObjectForKey:@"zoom"]; pitchStop[@"duration"] = @0;
326
+ [camera handleImperativeStop:pitchStop];
327
+ NSMutableDictionary *stop = [target mutableCopy]; stop[@"duration"] = @0;
328
+ [camera handleImperativeStop:stop];
329
+ NSDictionary *actual = surface.view;
330
+ for (NSString *key in @[@"zoom", @"pitch"]) if (std::abs([actual[key] doubleValue] - [target[key] doubleValue]) > 0.000001) TFSurfaceInvalid();
331
+ for (NSString *key in @[@"bearing"]) if (std::abs(std::fmod([actual[key] doubleValue] - [target[key] doubleValue] + 540, 360) - 180) > 0.000001) TFSurfaceInvalid();
332
+ if (std::abs(std::fmod([actual[@"center"][0] doubleValue] - [target[@"center"][0] doubleValue] + 540, 360) - 180) > 0.000001 ||
333
+ std::abs([actual[@"center"][1] doubleValue] - [target[@"center"][1] doubleValue]) > 0.000001 || [self current:identifier allowFailed:NO] != surface) TFSurfaceInvalid();
334
+ resolve(@{@"command": @(command), @"invalidation": @(invalidation), @"view": target});
335
+ } @catch (NSException *exception) { TFSurfaceReject(reject); }
336
+ }
337
+ RCT_REMAP_METHOD(cancelCamera, cancelCameraForSurface:(NSString *)identifier sequence:(NSNumber *)sequence resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
338
+ @try { if (!TFAdmissionValidToken(identifier)) TFSurfaceInvalid(); [self.surfaces[identifier].state cancelCommand:TFSurfaceInteger(sequence)]; resolve(@{@"cancelled": @YES}); }
339
+ @catch (NSException *exception) { TFSurfaceReject(reject); }
340
+ }
341
+ - (void)retire:(TFSurfaceAttachment *)surface resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject {
342
+ [surface retire];
343
+ if (!surface.root.window) { [self.surfaces removeObjectForKey:surface.identifier]; resolve(@{@"detached": @YES}); return; }
344
+ if (surface.detached.count >= 16) TFSurfaceInvalid();
345
+ __block BOOL completed = NO;
346
+ NSString *identifier = [surface.identifier copy];
347
+ __weak TileflowNativeSurface *weakSelf = self;
348
+ __weak TFSurfaceAttachment *weakSurface = surface;
349
+ [surface.detached addObject:^{
350
+ TileflowNativeSurface *owner = weakSelf;
351
+ TFSurfaceAttachment *attachment = weakSurface;
352
+ if (!owner || !attachment) return;
353
+ TFSurfaceAttachment *registered = owner.surfaces[identifier];
354
+ if (registered && registered != attachment) return;
355
+ if (registered == attachment) [owner.surfaces removeObjectForKey:identifier];
356
+ if (!completed) { completed = YES; resolve(@{@"detached": @YES}); }
357
+ }];
358
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
359
+ if (!completed) { completed = YES; TFSurfaceReject(reject); }
360
+ });
361
+ }
362
+ RCT_REMAP_METHOD(retireSurface, retireSurfaceWithIdentifier:(NSString *)identifier resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
363
+ @try {
364
+ if (!TFAdmissionValidToken(identifier)) TFSurfaceInvalid();
365
+ TFSurfaceAttachment *surface = self.surfaces[identifier];
366
+ if (!surface) { resolve(@{@"detached": @YES}); return; }
367
+ [self retire:surface resolver:resolve rejecter:reject];
368
+ } @catch (NSException *exception) { TFSurfaceReject(reject); }
369
+ }
370
+ RCT_REMAP_METHOD(retireRoot, retireRootWithTag:(NSNumber *)tag resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) {
371
+ @try {
372
+ TFSurfaceInteger(tag);
373
+ if (!self.viewRegistry_DEPRECATED) TFSurfaceInvalid();
374
+ [self.viewRegistry_DEPRECATED addUIBlock:^(RCTViewRegistry *registry) {
375
+ @try {
376
+ UIView *root = [registry viewForReactTag:tag];
377
+ if (!root || !root.window) { resolve(@{@"detached": @YES}); return; }
378
+ for (TFSurfaceAttachment *surface in self.surfaces.allValues) if (surface.root == root) { [self retire:surface resolver:resolve rejecter:reject]; return; }
379
+ if (self.rootRetirements.count >= 16 || self.rootRetirements[tag]) TFSurfaceInvalid();
380
+ TFSurfaceLayoutProbe *probe = [[TFSurfaceLayoutProbe alloc] initWithFrame:CGRectZero];
381
+ probe.userInteractionEnabled = NO; probe.alpha = 0;
382
+ __block BOOL completed = NO;
383
+ __weak TileflowNativeSurface *weakSelf = self;
384
+ __weak TFSurfaceLayoutProbe *weakProbe = probe;
385
+ probe.changed = ^{
386
+ if (root.window) return;
387
+ [weakSelf.rootRetirements removeObjectForKey:tag]; weakProbe.changed = nil; [weakProbe removeFromSuperview];
388
+ if (!completed) { completed = YES; resolve(@{@"detached": @YES}); }
389
+ };
390
+ self.rootRetirements[tag] = probe; [root addSubview:probe];
391
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{ if (!completed) { completed = YES; TFSurfaceReject(reject); } });
392
+ } @catch (NSException *exception) { TFSurfaceReject(reject); }
393
+ }];
394
+ } @catch (NSException *exception) { TFSurfaceReject(reject); }
395
+ }
396
+ - (void)background:(NSNotification *)notification { for (TFSurfaceAttachment *surface in self.surfaces.allValues) { surface.foreground = NO; [surface sampleLayout]; } }
397
+ - (void)foreground:(NSNotification *)notification { for (TFSurfaceAttachment *surface in self.surfaces.allValues) { surface.foreground = YES; [surface sampleLayout]; } }
398
+ - (void)invalidate {
399
+ [NSNotificationCenter.defaultCenter removeObserver:self];
400
+ dispatch_async(dispatch_get_main_queue(), ^{ self.invalidated = YES; for (TFSurfaceAttachment *surface in self.surfaces.allValues) [surface retire]; });
401
+ [super invalidate];
402
+ }
403
+ @end
@@ -0,0 +1,45 @@
1
+ # Call after $MLRN.post_install(installer) in the existing local Podfile.
2
+ # This only links the already configured pinned SPM product to this pod and
3
+ # its local test targets. It never installs a shell phase or changes a pin.
4
+ def tileflow_native_admission_post_install(installer)
5
+ project = installer.pods_project
6
+ url = 'https://github.com/maplibre/maplibre-gl-native-distribution'
7
+ version = '6.26.0'
8
+ refs = project.root_object.package_references.select do |reference|
9
+ reference.respond_to?(:repositoryURL) && reference.repositoryURL == url
10
+ end
11
+ raise 'Run MapLibre React Native post_install before Tileflow native admission' unless refs.length == 1
12
+ reference = refs.first
13
+ requirement = reference.requirement.transform_keys(&:to_s)
14
+ unless requirement == { 'kind' => 'exactVersion', 'version' => version }
15
+ raise 'Tileflow native admission requires the exact MapLibre Native 6.26.0 SPM product'
16
+ end
17
+ if defined?($MLRN_NATIVE_VERSION) && $MLRN_NATIVE_VERSION.to_s != version
18
+ raise 'Conflicting MapLibre Native version for Tileflow native admission'
19
+ end
20
+
21
+ targets = project.targets.select do |target|
22
+ target.is_a?(Xcodeproj::Project::Object::PBXNativeTarget) &&
23
+ (target.name == 'TileflowNativeAdmission' || target.name.start_with?('TileflowNativeAdmission-', 'AppHost-TileflowNativeAdmission-'))
24
+ end
25
+ raise 'TileflowNativeAdmission pod target is missing' unless targets.any? { |target| target.name == 'TileflowNativeAdmission' }
26
+ targets.each do |target|
27
+ products = target.package_product_dependencies.select { |product| product.product_name == 'MapLibre' }
28
+ if products.any? { |product| product.package != reference } || products.length > 1
29
+ raise 'Conflicting MapLibre product on Tileflow native admission target'
30
+ end
31
+ product = products.first
32
+ unless product
33
+ product = project.new(Xcodeproj::Project::Object::XCSwiftPackageProductDependency)
34
+ product.package = reference
35
+ product.product_name = 'MapLibre'
36
+ target.package_product_dependencies << product
37
+ end
38
+ phase = target.frameworks_build_phase
39
+ unless phase.files.any? { |file| file.respond_to?(:product_ref) && file.product_ref == product }
40
+ file = project.new(Xcodeproj::Project::Object::PBXBuildFile)
41
+ file.product_ref = product
42
+ phase.files << file
43
+ end
44
+ end
45
+ end
package/package.json CHANGED
@@ -1,19 +1,63 @@
1
1
  {
2
2
  "name": "@tileflow/react-native",
3
- "version": "0.0.0-bootstrap.0",
4
- "description": "Package registration for Tileflow React Native. Runtime releases use the alpha tag.",
3
+ "version": "0.1.0-alpha.0",
5
4
  "license": "Apache-2.0",
5
+ "description": "Tileflow maps, camera control and semantic interactions for React Native.",
6
+ "type": "module",
7
+ "sideEffects": false,
6
8
  "homepage": "https://tileflow.dev",
7
9
  "repository": {
8
10
  "type": "git",
9
- "url": "git+https://github.com/tileflow/tileflow-sdk.git"
11
+ "url": "git+https://github.com/tileflow/tileflow-sdk.git",
12
+ "directory": "packages/react-native"
10
13
  },
11
14
  "bugs": {
12
15
  "url": "https://github.com/tileflow/tileflow-sdk/issues"
13
16
  },
14
- "files": ["README.md", "LICENSE"],
17
+ "main": "./dist/index.js",
18
+ "types": "./dist/index.d.ts",
19
+ "files": [
20
+ "dist",
21
+ "docs",
22
+ "android/build.gradle",
23
+ "android/src/main",
24
+ "ios/*.h",
25
+ "ios/*.mm",
26
+ "ios/*.rb",
27
+ "TileflowNativeAdmission.podspec",
28
+ "react-native.config.cjs",
29
+ "LICENSE"
30
+ ],
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/index.js",
35
+ "default": "./dist/index.js"
36
+ }
37
+ },
38
+ "dependencies": {
39
+ "@tileflow/core": ">=0.1.0-alpha.27 <0.1.0-beta.0",
40
+ "@tileflow/interactions": ">=0.1.0-alpha.4 <0.1.0-beta.0"
41
+ },
42
+ "peerDependencies": {
43
+ "@maplibre/maplibre-react-native": "11.3.10",
44
+ "react": "19.2.0",
45
+ "react-native": "0.83.10"
46
+ },
47
+ "devDependencies": {
48
+ "@maplibre/maplibre-react-native": "11.3.10",
49
+ "@types/react": "^19.2.17",
50
+ "react": "19.2.0",
51
+ "react-native": "0.83.10"
52
+ },
15
53
  "publishConfig": {
16
- "access": "public",
17
- "tag": "bootstrap"
54
+ "access": "public"
55
+ },
56
+ "scripts": {
57
+ "build": "tsup",
58
+ "test": "tsx --test test/*.test.ts",
59
+ "verify": "pnpm run build && pnpm run typecheck && pnpm run test",
60
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.contract.json",
61
+ "lint": "tsc -p tsconfig.json --noEmit"
18
62
  }
19
- }
63
+ }
@@ -0,0 +1,13 @@
1
+ module.exports = {
2
+ dependency: {
3
+ platforms: {
4
+ android: {
5
+ sourceDir: 'android',
6
+ packageImportPath: 'import dev.tileflow.reactnative.TileflowNativeAdmissionPackage;',
7
+ packageInstance: 'new TileflowNativeAdmissionPackage()',
8
+ },
9
+ // The CLI discovers the sole podspec at the package root.
10
+ ios: {},
11
+ },
12
+ },
13
+ };