diff --git a/CHANGELOG.md b/CHANGELOG.md index f1facc1cd..28cb409ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ fix: [disable code signing for release builds](<[#450](https://github.com/maplib fix: [disable library code signing](<[#447](https://github.com/maplibre/maplibre-react-native/pull/447)>) feat: [feat: yarn monorepo](<[#441](https://github.com/maplibre/maplibre-react-native/pull/441)>) +fix: Call requestProgress when getting pack status on IOS + example improvement [#445](https://github.com/maplibre/maplibre-react-native/pull/445) ## 10.0.0-alpha.13 diff --git a/__tests__/interface.test.js b/__tests__/interface.test.js index 24daa9e7c..eb2e47a93 100644 --- a/__tests__/interface.test.js +++ b/__tests__/interface.test.js @@ -19,6 +19,7 @@ describe('Public Interface', () => { // modules 'offlineManager', 'OfflineCreatePackOptions', + 'OfflinePack', 'snapshotManager', 'locationManager', diff --git a/ios/RCTMLN/MLNOfflineModule.m b/ios/RCTMLN/MLNOfflineModule.m index d7edfd423..69f883849 100644 --- a/ios/RCTMLN/MLNOfflineModule.m +++ b/ios/RCTMLN/MLNOfflineModule.m @@ -48,12 +48,12 @@ - (instancetype)init packRequestQueue = [NSMutableArray new]; eventThrottle = 300; lastPackState = -1; - + NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter]; [defaultCenter addObserver:self selector:@selector(offlinePackProgressDidChange:) name:MLNOfflinePackProgressChangedNotification object:nil]; [defaultCenter addObserver:self selector:@selector(offlinePackDidReceiveError:) name:MLNOfflinePackErrorNotification object:nil]; [defaultCenter addObserver:self selector:@selector(offlinePackDidReceiveMaxAllowedMapboxTiles:) name:MLNOfflinePackMaximumMapboxTilesReachedNotification object:nil]; - + [[MLNOfflineStorage sharedOfflineStorage] addObserver:self forKeyPath:@"packs" options:NSKeyValueObservingOptionInitial context:NULL]; } return self; @@ -72,15 +72,21 @@ - (void)dealloc - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + if ([keyPath isEqualToString:@"state"] && [object isKindOfClass:[MLNOfflinePack class]]) { + MLNOfflinePack* pack = (MLNOfflinePack*)object; + [self observerStateForPack:pack context:context]; + return; + } + if (packRequestQueue.count == 0) { return; } - + NSArray *packs = [[MLNOfflineStorage sharedOfflineStorage] packs]; if (packs == nil) { return; } - + while (packRequestQueue.count > 0) { RCTPromiseResolveBlock resolve = [packRequestQueue objectAtIndex:0]; resolve([self _convertPacksToJson:packs]); @@ -94,13 +100,13 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N { NSString *styleURL = options[@"styleURL"]; MLNCoordinateBounds bounds = [RCTMLNUtils fromFeatureCollection:options[@"bounds"]]; - + id offlineRegion = [[MLNTilePyramidOfflineRegion alloc] initWithStyleURL:[NSURL URLWithString:styleURL] bounds:bounds fromZoomLevel:[options[@"minZoom"] doubleValue] toZoomLevel:[options[@"maxZoom"] doubleValue]]; NSData *context = [self _archiveMetadata:options[@"metadata"]]; - + [[MLNOfflineStorage sharedOfflineStorage] addPackForRegion:offlineRegion withContext:context completionHandler:^(MLNOfflinePack *pack, NSError *error) { @@ -129,7 +135,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N return reject(@"asset_does_not_exist", [NSString stringWithFormat:@"The given assetName, %@, can't be found in the app's bundle.", path], nil); } } - + [[MLNOfflineStorage sharedOfflineStorage] addContentsOfFile:absolutePath withCompletionHandler:^(NSURL *fileURL, NSArray *packs, NSError *error) { if (error != nil) { reject(@"mergeOfflineRegions", error.description, error); @@ -143,7 +149,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N { dispatch_async(dispatch_get_main_queue(), ^{ NSArray *packs = [[MLNOfflineStorage sharedOfflineStorage] packs]; - + if (packs == nil) { // packs have not loaded yet [self->packRequestQueue addObject:resolve]; @@ -187,7 +193,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N } resolve(nil); }]; - + } RCT_EXPORT_METHOD(resetDatabase:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) @@ -199,7 +205,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N } resolve(nil); }]; - + } RCT_EXPORT_METHOD(getPackStatus:(NSString *)name @@ -207,14 +213,29 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N rejecter:(RCTPromiseRejectBlock)reject) { MLNOfflinePack *pack = [self _getPackFromName:name]; - + if (pack == nil) { resolve(nil); NSLog(@"getPackStatus - Unknown offline region"); return; } - - resolve([self _makeRegionStatusPayload:name pack:pack]); + + if (pack.state == MLNOfflinePackStateUnknown) { + [pack addObserver:self forKeyPath:@"state" options:NSKeyValueObservingOptionNew context:(__bridge_retained void*)resolve]; + [pack requestProgress]; + } else { + resolve([self _makeRegionStatusPayload:name pack:pack]); + } +} + +-(void)observerStateForPack:(MLNOfflinePack*)pack context:(nullable void*) context { + RCTPromiseResolveBlock resolve = (__bridge_transfer RCTPromiseResolveBlock)context; + dispatch_async(dispatch_get_main_queue(), ^{ + NSDictionary* metadata = [self _unarchiveMetadata:pack]; + NSString* name = metadata[@"name"]; + resolve([self _makeRegionStatusPayload:name pack:pack]); + }); + [pack removeObserver:self forKeyPath:@"state" context:context]; } RCT_EXPORT_METHOD(invalidatePack:(NSString *)name @@ -241,7 +262,7 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N rejecter:(RCTPromiseRejectBlock)reject) { MLNOfflinePack *pack = [self _getPackFromName:name]; - + if (pack == nil) { resolve(nil); return; @@ -265,17 +286,17 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N rejecter:(RCTPromiseRejectBlock)reject) { MLNOfflinePack *pack = [self _getPackFromName:name]; - + if (pack == nil) { reject(@"pausePackDownload", @"Unknown offline region", nil); return; } - + if (pack.state == MLNOfflinePackStateInactive || pack.state == MLNOfflinePackStateComplete) { resolve(nil); return; } - + [pack suspend]; resolve(nil); } @@ -285,17 +306,17 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N rejecter:(RCTPromiseRejectBlock)reject) { MLNOfflinePack *pack = [self _getPackFromName:name]; - + if (pack == nil) { reject(@"resumePack", @"Unknown offline region", nil); return; } - + if (pack.state == MLNOfflinePackStateActive || pack.state == MLNOfflinePackStateComplete) { resolve(nil); return; } - + [pack resume]; resolve(nil); } @@ -313,18 +334,18 @@ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(N - (void)offlinePackProgressDidChange:(NSNotification *)notification { MLNOfflinePack *pack = notification.object; - + if (pack.state == MLNOfflinePackStateInvalid) { return; // Avoid invalid offline pack exception } - + if ([self _shouldSendProgressEvent:[self _getCurrentTimestamp] pack:pack]) { NSDictionary *metadata = [self _unarchiveMetadata:pack]; RCTMLNEvent *event = [self _makeProgressEvent:metadata[@"name"] pack:pack]; [self _sendEvent:RCT_MAPBOX_OFFLINE_CALLBACK_PROGRESS event:event]; lastPackTimestamp = [self _getCurrentTimestamp]; } - + lastPackState = pack.state; } @@ -335,7 +356,7 @@ - (void)offlinePackDidReceiveError:(NSNotification *)notification return; // Avoid invalid offline pack exception } NSDictionary *metadata = [self _unarchiveMetadata:pack]; - + NSString *name = metadata[@"name"]; if (name != nil) { NSError *error = notification.userInfo[MLNOfflinePackUserInfoKeyError]; @@ -350,7 +371,7 @@ - (void)offlinePackDidReceiveMaxAllowedMapboxTiles:(NSNotification *)notificatio { MLNOfflinePack *pack = notification.object; NSDictionary *metadata = [self _unarchiveMetadata:pack]; - + NSString *name = metadata[@"name"]; if (name != nil) { RCTMLNEvent *event = [self _makeErrorEvent:name @@ -386,11 +407,11 @@ - (NSDictionary *)_unarchiveMetadata:(MLNOfflinePack *)pack if ([data isKindOfClass:[NSDictionary class]]) { return data; } - + if (data == nil) { return @{}; } - + return [NSJSONSerialization JSONObjectWithData:[data dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:nil]; @@ -406,7 +427,7 @@ - (NSDictionary *)_makeRegionStatusPayload:(NSString *)name pack:(MLNOfflinePack if(expectedResources == 0) { progressPercentage = 0; } - + return @{ @"state": @(pack.state), @"name": name, @@ -433,15 +454,15 @@ - (RCTMLNEvent *)_makeErrorEvent:(NSString *)name type:(NSString *)type message: - (NSArray *)_convertPacksToJson:(NSArray *)packs { NSMutableArray *jsonPacks = [NSMutableArray new]; - + if (packs == nil) { return jsonPacks; } - + for (MLNOfflinePack *pack in packs) { [jsonPacks addObject:[self _convertPackToDict:pack]]; } - + return jsonPacks; } @@ -452,12 +473,12 @@ - (NSDictionary *)_convertPackToDict:(MLNOfflinePack *)pack if (region == nil) { return nil; } - + NSArray *jsonBounds = @[ @[@(region.bounds.ne.longitude), @(region.bounds.ne.latitude)], @[@(region.bounds.sw.longitude), @(region.bounds.sw.latitude)] ]; - + // format metadata NSDictionary *metadata = [self _unarchiveMetadata:pack]; NSData *jsonMetadata = [NSJSONSerialization dataWithJSONObject:metadata @@ -472,19 +493,19 @@ - (NSDictionary *)_convertPackToDict:(MLNOfflinePack *)pack - (MLNOfflinePack *)_getPackFromName:(NSString *)name { NSArray *packs = [[MLNOfflineStorage sharedOfflineStorage] packs]; - + if (packs == nil) { return nil; } - + for (MLNOfflinePack *pack in packs) { NSDictionary *metadata = [self _unarchiveMetadata:pack]; - + if ([name isEqualToString:metadata[@"name"]]) { return pack; } } - + return nil; } @@ -501,15 +522,15 @@ - (BOOL)_shouldSendProgressEvent:(double)currentTimestamp pack:(MLNOfflinePack * if (lastPackState == -1) { return YES; } - + if (lastPackState != currentPack.state) { return YES; } - + if (currentTimestamp - lastPackTimestamp > eventThrottle) { return YES; } - + return NO; } diff --git a/javascript/Maplibre.ts b/javascript/Maplibre.ts index ee4ee08ed..e589a53cb 100644 --- a/javascript/Maplibre.ts +++ b/javascript/Maplibre.ts @@ -40,6 +40,12 @@ export { type Location, } from "./modules/location/locationManager"; export { default as offlineManager } from "./modules/offline/offlineManager"; +export type { + OfflineProgressStatus, + OfflinePackError, +} from "./modules/offline/offlineManager"; +export type { OfflinePackStatus } from "./modules/offline/OfflinePack"; +export { default as OfflinePack } from "./modules/offline/OfflinePack"; export { default as OfflineCreatePackOptions } from "./modules/offline/OfflineCreatePackOptions"; export { default as snapshotManager } from "./modules/snapshot/snapshotManager"; export type { SnapshotInputOptions } from "./modules/snapshot/SnapshotOptions"; diff --git a/javascript/modules/offline/OfflinePack.ts b/javascript/modules/offline/OfflinePack.ts index f5348dadf..c041d1e62 100644 --- a/javascript/modules/offline/OfflinePack.ts +++ b/javascript/modules/offline/OfflinePack.ts @@ -4,7 +4,7 @@ import OfflineCreatePackOptions from "./OfflineCreatePackOptions"; const MapLibreGLOfflineManager = NativeModules.MLNOfflineModule; -type OfflinePackStatus = { +export type OfflinePackStatus = { name: string; state: number; percentage: number; diff --git a/packages/examples/src/examples/Map/CreateOfflineRegion.js b/packages/examples/src/examples/Map/CreateOfflineRegion.js deleted file mode 100755 index 7b9cc5c0c..000000000 --- a/packages/examples/src/examples/Map/CreateOfflineRegion.js +++ /dev/null @@ -1,197 +0,0 @@ -import React from 'react'; -import { - Alert, - Text, - View, - TouchableOpacity, - Dimensions, - StyleSheet, -} from 'react-native'; -import MapLibreGL from '@maplibre/maplibre-react-native'; -import geoViewport from '@mapbox/geo-viewport'; - -import sheet from '../../styles/sheet'; -import Page from '../common/Page'; -import Bubble from '../common/Bubble'; - -const CENTER_COORD = [-73.970895, 40.723279]; -const MVT_SIZE = 512; - -const styles = StyleSheet.create({ - bubble: {flex: 1}, - button: { - alignItems: 'center', - backgroundColor: 'blue', - borderRadius: 3, - flex: 0.4, - justifyContent: 'center', - padding: 8, - }, - buttonCnt: { - alignItems: 'center', - flexDirection: 'row', - justifyContent: 'space-between', - }, - buttonTxt: { - color: 'white', - }, -}); - -class CreateOfflineRegion extends React.Component { - constructor(props) { - super(props); - - this.state = { - name: `test-${Date.now()}`, - offlineRegion: null, - offlineRegionStatus: null, - }; - - this.onDownloadProgress = this.onDownloadProgress.bind(this); - this.onDidFinishLoadingStyle = this.onDidFinishLoadingStyle.bind(this); - - this.onResume = this.onResume.bind(this); - this.onPause = this.onPause.bind(this); - this.onStatusRequest = this.onStatusRequest.bind(this); - } - - componentWillUnmount() { - // avoid setState warnings if we back out before we finishing downloading - MapLibreGL.offlineManager.deletePack(this.state.name); - MapLibreGL.offlineManager.unsubscribe('test'); - } - - async onDidFinishLoadingStyle() { - const {width, height} = Dimensions.get('window'); - const bounds = geoViewport.bounds( - CENTER_COORD, - 12, - [width, height], - MVT_SIZE, - ); - - const options = { - name: this.state.name, - styleURL: MapLibreGL.StyleURL.Default, - bounds: [ - [bounds[0], bounds[1]], - [bounds[2], bounds[3]], - ], - minZoom: 10, - maxZoom: 20, - }; - - // start download - MapLibreGL.offlineManager.createPack(options, this.onDownloadProgress); - } - - onDownloadProgress(offlineRegion, offlineRegionStatus) { - this.setState({ - name: offlineRegion.name, - offlineRegion, - offlineRegionStatus, - }); - } - - onResume() { - if (this.state.offlineRegion) { - this.state.offlineRegion.resume(); - } - } - - onPause() { - if (this.state.offlineRegion) { - this.state.offlineRegion.pause(); - } - } - - async onStatusRequest() { - if (this.state.offlineRegion) { - const offlineRegionStatus = await this.state.offlineRegion.status(); - Alert.alert('Get Status', JSON.stringify(offlineRegionStatus, null, 2)); - } - } - - _formatPercent() { - if (!this.state.offlineRegionStatus) { - return '0%'; - } - return Math.round(this.state.offlineRegionStatus.percentage / 10) / 10; - } - - _getRegionDownloadState(downloadState) { - switch (downloadState) { - case MapLibreGL.OfflinePackDownloadState.Active: - return 'Active'; - case MapLibreGL.OfflinePackDownloadState.Complete: - return 'Complete'; - default: - return 'Inactive'; - } - } - - render() { - const {offlineRegionStatus} = this.state; - - return ( - - (this._map = c)} - onPress={this.onPress} - onDidFinishLoadingMap={this.onDidFinishLoadingStyle} - style={sheet.matchParent}> - - - - {offlineRegionStatus !== null ? ( - - - - Download State:{' '} - {this._getRegionDownloadState(offlineRegionStatus.state)} - - Download Percent: {offlineRegionStatus.percentage} - - Completed Resource Count:{' '} - {offlineRegionStatus.completedResourceCount} - - - Completed Resource Size:{' '} - {offlineRegionStatus.completedResourceSize} - - - Completed Tile Count: {offlineRegionStatus.completedTileCount} - - - Required Resource Count:{' '} - {offlineRegionStatus.requiredResourceCount} - - - - - - Resume - - - - - - Status - - - - - - Pause - - - - - - ) : null} - - ); - } -} - -export default CreateOfflineRegion; diff --git a/packages/examples/src/examples/Map/CreateOfflineRegion.tsx b/packages/examples/src/examples/Map/CreateOfflineRegion.tsx new file mode 100755 index 000000000..7b2e8de2c --- /dev/null +++ b/packages/examples/src/examples/Map/CreateOfflineRegion.tsx @@ -0,0 +1,268 @@ +import geoViewport from "@mapbox/geo-viewport"; +import MapLibreGL, { + OfflinePack, + OfflineProgressStatus, + OfflinePackError, +} from "@maplibre/maplibre-react-native"; +import React, { useEffect, useState } from "react"; +import { + Alert, + Text, + View, + TouchableOpacity, + Dimensions, + StyleSheet, +} from "react-native"; + +import sheet from "../../styles/sheet"; +import Bubble from "../common/Bubble"; +import Page from "../common/Page"; +import { AMERICANA_STYLE } from "../mapStyles"; + +const CENTER_COORD: [number, number] = [18.6466, 54.352]; +const MVT_SIZE = 512; +const PACK_NAME = "test"; + +const styles = StyleSheet.create({ + bubble: { + flex: 1, + left: 18, + right: 18, + borderRadius: 8, + }, + button: { + alignItems: "center", + backgroundColor: "blue", + borderRadius: 3, + flex: 0.4, + justifyContent: "center", + padding: 8, + }, + buttonCnt: { + marginTop: 16, + paddingHorizontal: 16, + alignItems: "center", + flexDirection: "row", + justifyContent: "space-between", + width: "100%", + }, + buttonTxt: { + color: "white", + }, +}); + +type OfflinePackDownloadState = + (typeof MapLibreGL.OfflinePackDownloadState)[keyof typeof MapLibreGL.OfflinePackDownloadState]; + +function getRegionDownloadState(downloadState: OfflinePackDownloadState) { + switch (downloadState) { + case MapLibreGL.OfflinePackDownloadState.Active: + return "Active"; + case MapLibreGL.OfflinePackDownloadState.Complete: + return "Complete"; + + case MapLibreGL.OfflinePackDownloadState.Inactive: + return "Inactive"; + default: + return "UNKNOWN"; + } +} + +export default function CreateOfflineRegion() { + const [offlineRegionStatus, setOfflineRegionStatus] = + useState(null); + const [offlinePack, setOfflinePack] = useState(null); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + return () => { + MapLibreGL.offlineManager.unsubscribe(PACK_NAME); + }; + }, []); + + function onDownloadProgress( + pack: OfflinePack, + status: OfflineProgressStatus, + ) { + setOfflinePack(pack); + setOfflineRegionStatus(status); + } + + function onDownloadError(pack: OfflinePack, err: OfflinePackError) { + console.log("onDownloadError", pack, err); + } + + function createPack() { + const { width, height } = Dimensions.get("window"); + const viewportBounds = geoViewport.bounds( + CENTER_COORD, + 12, + [width, height], + MVT_SIZE, + ); + + const bounds: [GeoJSON.Position, GeoJSON.Position] = [ + [viewportBounds[0], viewportBounds[1]], + [viewportBounds[2], viewportBounds[3]], + ]; + + const options = { + name: PACK_NAME, + // demotiles are crashing the app when used with offline manager + styleURL: AMERICANA_STYLE, + bounds, + minZoom: 12, + maxZoom: 14, + }; + + // start download + MapLibreGL.offlineManager.createPack( + options, + onDownloadProgress, + onDownloadError, + ); + } + + async function onDidFinishLoadingStyle() { + try { + const pack = await MapLibreGL.offlineManager.getPack(PACK_NAME); + + if (!pack) { + return; + } + + const status = await pack.status(); + + setOfflinePack(pack); + setOfflineRegionStatus(status); + } finally { + setIsLoading(false); + } + } + + function onDownload() { + createPack(); + } + + function onResume() { + if (offlinePack) { + offlinePack.resume(); + } + } + + function onPause() { + if (offlinePack) { + offlinePack.pause(); + } + } + + async function onDelete() { + if (!offlinePack) { + return; + } + + await MapLibreGL.offlineManager.deletePack(PACK_NAME); + + setOfflinePack(null); + setOfflineRegionStatus(null); + } + + async function onStatusRequest() { + if (!offlinePack) { + return; + } + + const status = await offlinePack.status(); + + if (status) { + Alert.alert("Get Status", JSON.stringify(status, null, 2)); + } else { + Alert.alert("Get Status", "Could not get status"); + } + } + + return ( + + + + + + {isLoading === false && ( + + {offlineRegionStatus === null && ( + + + Download + + + )} + + {offlineRegionStatus !== null && ( + <> + + Download State:{" "} + {getRegionDownloadState(offlineRegionStatus.state)} + + Download Percent: {offlineRegionStatus.percentage} % + + Completed Resource Count:{" "} + {offlineRegionStatus.completedResourceCount} + + + Completed Resource Size:{" "} + {Math.round( + (offlineRegionStatus.completedResourceSize * 100) / + 1024 / + 1024, + ) / 100}{" "} + MB + + + Completed Tile Count: {offlineRegionStatus.completedTileCount} + + + Required Resource Count:{" "} + {offlineRegionStatus.requiredResourceCount} + + + + + + Resume + + + + + + Status + + + + + + Pause + + + + + + Delete + + + + + )} + + )} + + ); +} diff --git a/packages/examples/src/examples/common/Page.tsx b/packages/examples/src/examples/common/Page.tsx index 6922eb934..8f29e5fe0 100755 --- a/packages/examples/src/examples/common/Page.tsx +++ b/packages/examples/src/examples/common/Page.tsx @@ -1,5 +1,5 @@ import { useNavigation, useRoute } from "@react-navigation/native"; -import React, { ReactElement } from "react"; +import React, { ReactElement, ReactNode } from "react"; import { View } from "react-native"; import MapHeader from "./MapHeader"; @@ -7,7 +7,7 @@ import colors from "../../styles/colors"; import sheet from "../../styles/sheet"; interface PageProps { - children: ReactElement | ReactElement[]; + children: ReactNode; } const Page = ({ children }: PageProps): ReactElement => { diff --git a/packages/examples/src/examples/mapStyles.ts b/packages/examples/src/examples/mapStyles.ts new file mode 100644 index 000000000..ca49c0728 --- /dev/null +++ b/packages/examples/src/examples/mapStyles.ts @@ -0,0 +1 @@ +export const AMERICANA_STYLE = "https://americanamap.org/style.json"; diff --git a/packages/react-native-app/tsconfig.json b/packages/react-native-app/tsconfig.json index 0827aa2cb..e91508e2c 100644 --- a/packages/react-native-app/tsconfig.json +++ b/packages/react-native-app/tsconfig.json @@ -9,4 +9,4 @@ }, }, "include": ["./src/**/*", "index.js", "e2e/firstTest.e2e.js"], - } \ No newline at end of file + } diff --git a/tsconfig.json b/tsconfig.json index 49a846a64..9cf78eaed 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,4 +21,3 @@ "javascript/**/*", "index.ts" ], } -