├── .gitignore ├── .gitmodules ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── DEVELOPMENT.md ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── plugin.xml ├── scripts └── iosAddBridgingHeader.js ├── src ├── android │ ├── AssetBundle.java │ ├── AssetBundleDownloader.java │ ├── AssetBundleManager.java │ ├── AssetManagerCache.java │ ├── AssetManifest.java │ ├── DownloadFailureException.java │ ├── IOUtils.java │ ├── WebAppConfiguration.java │ ├── WebAppLocalServer.java │ ├── WebResourceHandler.java │ └── build-extras.gradle └── ios │ ├── Asset.swift │ ├── AssetBundle.swift │ ├── AssetBundleDownloader.swift │ ├── AssetBundleManager.swift │ ├── AssetManifest.swift │ ├── Errors.swift │ ├── METNetworkReachabilityManager.h │ ├── METNetworkReachabilityManager.m │ ├── METPlugin.h │ ├── METPlugin.m │ ├── METRandomValueGenerator.h │ ├── METRandomValueGenerator.m │ ├── METRetryStrategy.h │ ├── METRetryStrategy.m │ ├── METTimer.h │ ├── METTimer.m │ ├── Utility.swift │ ├── WebAppConfiguration.swift │ ├── WebAppLocalServer.swift │ └── cordova-plugin-meteor-webapp-Bridging-Header.h ├── tests ├── fixtures │ ├── bundled_www │ │ ├── application │ │ │ ├── app │ │ │ │ ├── mobileapp.js │ │ │ │ ├── mobileapp.js.map │ │ │ │ ├── some-data.json │ │ │ │ ├── some-file │ │ │ │ ├── some-font.woff │ │ │ │ ├── some-image.jpg │ │ │ │ ├── some-image.png │ │ │ │ ├── some-javascript.js │ │ │ │ ├── some-page.html │ │ │ │ ├── some-stylesheet.css │ │ │ │ ├── some-text.txt │ │ │ │ ├── some-video.mp4 │ │ │ │ ├── template.mobileapp.js │ │ │ │ └── template.mobileapp.js.map │ │ │ ├── head.html │ │ │ ├── index.html │ │ │ ├── merged-stylesheets.css │ │ │ ├── merged-stylesheets.css.map │ │ │ ├── not-in-manifest │ │ │ ├── packages │ │ │ │ ├── meteor.js │ │ │ │ └── meteor.js.map │ │ │ └── program.json │ │ └── cordova_plugins.js │ ├── downloadable_versions │ │ ├── 127.0.0.1_root_url │ │ │ ├── index.html │ │ │ └── manifest.json │ │ ├── different_cordova_compatibility_version │ │ │ ├── index.html │ │ │ └── manifest.json │ │ ├── missing_app_id │ │ │ ├── index.html │ │ │ └── manifest.json │ │ ├── missing_cordova_compatibility_version │ │ │ ├── index.html │ │ │ └── manifest.json │ │ ├── missing_root_url │ │ │ ├── index.html │ │ │ └── manifest.json │ │ ├── version1 │ │ │ ├── app │ │ │ │ ├── 6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map │ │ │ │ ├── 979b20f66caf126704c250fbd29ce253c6cb490e.map │ │ │ │ ├── mobileapp.js │ │ │ │ └── template.mobileapp.js │ │ │ ├── head.html │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ ├── merged-stylesheets.css │ │ │ ├── merged-stylesheets.css.map │ │ │ ├── not-in-manifest │ │ │ ├── packages │ │ │ │ ├── 57d11a30155349aa5106f8150cee35eac5f4764c.map │ │ │ │ └── meteor.js │ │ │ ├── some-data.json │ │ │ ├── some-file │ │ │ ├── some-font.woff │ │ │ ├── some-image.jpg │ │ │ ├── some-image.png │ │ │ ├── some-javascript.js │ │ │ ├── some-page.html │ │ │ ├── some-stylesheet.css │ │ │ ├── some-text.txt │ │ │ └── some-video.mp4 │ │ ├── version2 │ │ │ ├── 20ae2c8d51b2507244e598844414ecdec2615ce3.map │ │ │ ├── app │ │ │ │ ├── 3f6275657e6db3a21acb37d0f6c207cf83871e90.map │ │ │ │ ├── 6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map │ │ │ │ ├── mobileapp.js │ │ │ │ └── template.mobileapp.js │ │ │ ├── head.html │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ ├── merged-stylesheets.css │ │ │ ├── packages │ │ │ │ ├── 57d11a30155349aa5106f8150cee35eac5f4764c.map │ │ │ │ └── meteor.js │ │ │ ├── some-data.json │ │ │ ├── some-file │ │ │ ├── some-font.woff │ │ │ ├── some-image.jpg │ │ │ ├── some-image.png │ │ │ ├── some-javascript.js │ │ │ ├── some-other-file │ │ │ ├── some-page.html │ │ │ ├── some-stylesheet.css │ │ │ ├── some-text.txt │ │ │ └── some-video.mp4 │ │ ├── version2_with_invalid_asset │ │ │ ├── 20ae2c8d51b2507244e598844414ecdec2615ce3.map │ │ │ ├── app │ │ │ │ ├── 3f6275657e6db3a21acb37d0f6c207cf83871e90.map │ │ │ │ ├── 6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map │ │ │ │ ├── mobileapp.js │ │ │ │ └── template.mobileapp.js │ │ │ ├── head.html │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ ├── merged-stylesheets.css │ │ │ ├── packages │ │ │ │ ├── 57d11a30155349aa5106f8150cee35eac5f4764c.map │ │ │ │ └── meteor.js │ │ │ ├── some-data.json │ │ │ ├── some-file │ │ │ ├── some-font.woff │ │ │ ├── some-image.jpg │ │ │ ├── some-image.png │ │ │ ├── some-javascript.js │ │ │ ├── some-other-file │ │ │ ├── some-page.html │ │ │ ├── some-stylesheet.css │ │ │ ├── some-text.txt │ │ │ └── some-video.mp4 │ │ ├── version2_with_missing_asset │ │ │ ├── 20ae2c8d51b2507244e598844414ecdec2615ce3.map │ │ │ ├── app │ │ │ │ ├── 3f6275657e6db3a21acb37d0f6c207cf83871e90.map │ │ │ │ ├── 6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map │ │ │ │ └── mobileapp.js │ │ │ ├── head.html │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ ├── merged-stylesheets.css │ │ │ ├── packages │ │ │ │ ├── 57d11a30155349aa5106f8150cee35eac5f4764c.map │ │ │ │ └── meteor.js │ │ │ ├── some-data.json │ │ │ ├── some-file │ │ │ ├── some-font.woff │ │ │ ├── some-image.jpg │ │ │ ├── some-image.png │ │ │ ├── some-javascript.js │ │ │ ├── some-other-file │ │ │ ├── some-page.html │ │ │ ├── some-stylesheet.css │ │ │ ├── some-text.txt │ │ │ └── some-video.mp4 │ │ ├── version2_with_version_mismatch │ │ │ ├── 20ae2c8d51b2507244e598844414ecdec2615ce3.map │ │ │ ├── app │ │ │ │ ├── 3f6275657e6db3a21acb37d0f6c207cf83871e90.map │ │ │ │ ├── 6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map │ │ │ │ ├── mobileapp.js │ │ │ │ └── template.mobileapp.js │ │ │ ├── head.html │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ ├── merged-stylesheets.css │ │ │ ├── packages │ │ │ │ ├── 57d11a30155349aa5106f8150cee35eac5f4764c.map │ │ │ │ └── meteor.js │ │ │ ├── some-data.json │ │ │ ├── some-file │ │ │ ├── some-font.woff │ │ │ ├── some-image.jpg │ │ │ ├── some-image.png │ │ │ ├── some-javascript.js │ │ │ ├── some-other-file │ │ │ ├── some-page.html │ │ │ ├── some-stylesheet.css │ │ │ ├── some-text.txt │ │ │ └── some-video.mp4 │ │ ├── version3 │ │ │ ├── 20ae2c8d51b2507244e598844414ecdec2615ce3.map │ │ │ ├── app │ │ │ │ ├── 36e96c1d40459ae12164569599c9c0a203b36db7.map │ │ │ │ ├── 6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map │ │ │ │ ├── mobileapp.js │ │ │ │ └── template.mobileapp.js │ │ │ ├── head.html │ │ │ ├── index.html │ │ │ ├── manifest.json │ │ │ ├── merged-stylesheets.css │ │ │ ├── packages │ │ │ │ ├── 57d11a30155349aa5106f8150cee35eac5f4764c.map │ │ │ │ └── meteor.js │ │ │ ├── some-data.json │ │ │ ├── some-file │ │ │ ├── some-font.woff │ │ │ ├── some-image.jpg │ │ │ ├── some-image.png │ │ │ ├── some-javascript.js │ │ │ ├── some-other-file │ │ │ ├── some-page.html │ │ │ ├── some-stylesheet.css │ │ │ ├── some-text.txt │ │ │ └── some-video.mp4 │ │ ├── wrong_app_id │ │ │ ├── index.html │ │ │ └── manifest.json │ │ └── wrong_root_url │ │ │ ├── index.html │ │ │ └── manifest.json │ └── partially_downloaded_versions │ │ └── version2 │ │ ├── app │ │ ├── some-file │ │ └── some-other-file │ │ ├── index.html │ │ └── program.json ├── package.json ├── plugin.xml ├── src │ ├── android │ │ └── WebAppMockRemoteServer.java │ └── ios │ │ ├── GCDWebServer+Testing.h │ │ ├── GCDWebServer+Testing.m │ │ ├── WebAppLocalServer+Testing.swift │ │ ├── WebAppMockRemoteServer.swift │ │ └── cordova-plugin-meteor-webapp-tests-Bridging-Header.h └── www │ ├── fetch.js │ ├── tests.js │ ├── underscore.js │ ├── webapp_local_server_testing.js │ └── webapp_mock_remote_server.js └── www └── webapp_local_server.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/ios/GCDWebServer"] 2 | path = src/ios/GCDWebServer 3 | url = https://github.com/meteor/GCDWebServer.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ios/GCDWebServer/Tests/ 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: osx 2 | osx_image: xcode11.2 3 | git: 4 | depth: 2 5 | node_js: 6 | - 12 7 | install: 8 | - npm install 9 | script: 10 | - npm test 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## v2.0.0, 2020-10-02 4 | Use WebViewAssetLoader on Android with newest cordova AndroidX webview usage 5 | 6 | ## v1.9.1, 2020-03-05 7 | Removes hook to set Swift version 8 | 9 | ## v1.9.0, 2020-03-04 10 | Migrates Swift code to be compatible with Swift version 5 11 | 12 | ## v1.8.0, 2020-01-16 13 | It makes cordova-plugin-meteor-webapp ready for Cordova 9. 14 | - changes context.requireCordovaModule to require for non-Cordova modules 15 | - removes .woff content type test because it never worked 16 | - updates travis test to use recent versions 17 | - removes .paramedic.config.js and use options directly on package.json 18 | - declares xcode as npm dependency 19 | - updates dev dependencies 20 | - updates DEVELOPMENT.md 21 | 22 | This version should be used for apps running Meteor 1.10 forward. 23 | 24 | ## v1.7.4, 2020-01-16 25 | We didn't had a tag for 1.7.0 that was the last version before the updates for 26 | Cordova 9 then we published 1.7.4 from this revision d5a7377c. 27 | 28 | This version should be used for apps running Meteor 1.9 or previous versions. 29 | -------------------------------------------------------------------------------- /DEVELOPMENT.md: -------------------------------------------------------------------------------- 1 | # `cordova-plugin-meteor-webapp` Development 2 | 3 | ## Setup 4 | 5 | 1) Start with a cloned copy of the `cordova-plugin-meteor-webapp` repo: 6 | 7 | ``` 8 | cd ~ 9 | git clone https://github.com/meteor/cordova-plugin-meteor-webapp.git 10 | ``` 11 | 12 | 2) Make sure the `GCDWebServer` submodule is pulled in: 13 | 14 | ``` 15 | cd cordova-plugin-meteor-webapp 16 | git submodule update --init --recursive 17 | ``` 18 | 19 | ## Running npm Tests 20 | 21 | 1) Install dependencies 22 | ``` 23 | npm install 24 | ``` 25 | 26 | 2) Install devDependencies from package.json globally one by one 27 | ``` 28 | npm install -g xxx 29 | ``` 30 | 31 | Filipe: I'm not sure why it's only working when installed globally 32 | 33 | 3) Run the tests 34 | ``` 35 | npm test 36 | ``` 37 | 38 | ## Running iOS Tests 39 | 40 | 1) Create a new test Cordova app: 41 | 42 | ``` 43 | cd ~ 44 | cordova create test-app 45 | ``` 46 | 47 | 2) Add the `cordova-plugin-meteor-webapp`, `cordova-plugin-meteor-webapp-tests`, and `cordova-plugin-test-framework` plugins: 48 | 49 | ``` 50 | cd test-app 51 | cordova plugin add https://github.com/apache/cordova-plugin-test-framework.git 52 | cordova plugin add ../cordova-plugin-meteor-webapp/ 53 | cordova plugin add ../cordova-plugin-meteor-webapp/tests 54 | ``` 55 | 56 | 3) Add the `ios` platform: 57 | 58 | ``` 59 | cordova platform add ios 60 | ``` 61 | 62 | 4) Add a [`build.json`](https://cordova.apache.org/docs/en/latest/guide/platforms/ios/#using-buildjson) file to the root of your `test-app`, that includes your Apple Developer Team ID: 63 | 64 | ```json 65 | { 66 | "ios": { 67 | "debug": { 68 | "developmentTeam": "ABC123DEF456" 69 | }, 70 | "release": { 71 | "developmentTeam": "ABC123DEF456", 72 | "codeSignIdentity": "iPhone Developer", 73 | "packageType": "ad-hoc" 74 | } 75 | } 76 | } 77 | ``` 78 | 79 | 5) Update the `test-app`'s `config.xml` to point to the test runner: 80 | 81 | Change 82 | 83 | ```xml 84 | 85 | ``` 86 | 87 | to 88 | 89 | ```xml 90 | 91 | ``` 92 | 93 | 6) Run the tests on a device or using the iOS emulator: 94 | 95 | ``` 96 | cordova emulate ios 97 | ``` 98 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Meteor Development Group 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-meteor-webapp", 3 | "version": "2.0.0", 4 | "description": "Cordova plugin that serves a Meteor web app through a local server and implements hot code push", 5 | "cordova": { 6 | "id": "cordova-plugin-meteor-webapp", 7 | "platforms": [ 8 | "android", 9 | "ios" 10 | ] 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/meteor/cordova-plugin-meteor-webapp" 15 | }, 16 | "keywords": [ 17 | "cordova", 18 | "meteor", 19 | "ecosystem:cordova", 20 | "cordova-android", 21 | "cordova-ios" 22 | ], 23 | "author": "Meteor Development Group", 24 | "license": "MIT", 25 | "scripts": { 26 | "pretest": "ios-sim start --devicetypeid=iPhone-11-Pro-Max", 27 | "test": "cordova-paramedic --plugin . --platform ios --target 'iPhone-11-Pro-Max' --args=--buildFlag='-UseModernBuildSystem=0' --verbose" 28 | }, 29 | "dependencies": { 30 | "xcode": "^2.0.0" 31 | }, 32 | "devDependencies": { 33 | "cordova": "^9.0.0", 34 | "cordova-paramedic": "github:meteor/cordova-paramedic#40df66c3efc2f0db4d66b8c172174a68c031c114", 35 | "ios-deploy": "^1.10.0-beta.3", 36 | "ios-sim": "^8.0.2" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | Meteor Webapp 6 | Cordova plugin that serves a Meteor web app through a local server and implements hot code push 7 | cordova,meteor 8 | Meteor Development Group 9 | MIT 10 | https://github.com/meteor/cordova-plugin-meteor-webapp.git 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /scripts/iosAddBridgingHeader.js: -------------------------------------------------------------------------------- 1 | module.exports = function(context) { 2 | var fs = require('fs'); 3 | var path = require('path'); 4 | var cordova_util = context.requireCordovaModule('cordova-lib/src/cordova/util.js'); 5 | var ConfigParser = context.requireCordovaModule('cordova-common').ConfigParser; 6 | 7 | var projectRoot = context.opts.projectRoot; 8 | 9 | var configXml = cordova_util.projectConfig(projectRoot); 10 | var config = new ConfigParser(configXml); 11 | var projectName = config.name(); 12 | 13 | var platformRoot = path.join(context.opts.projectRoot, 'platforms/ios'); 14 | var projectBridgingHeaderPath = path.join(platformRoot, projectName, 15 | 'Bridging-Header.h'); 16 | 17 | var pluginId = context.opts.plugin.id; 18 | var pluginBridgingHeaderFilename = pluginId + '-Bridging-Header.h'; 19 | var importDirective = '#import "' + pluginBridgingHeaderFilename + '"'; 20 | 21 | var data = fs.readFileSync(projectBridgingHeaderPath, {'encoding': 'utf8'}); 22 | 23 | var regExp = new RegExp("^" + importDirective + "$", "m"); 24 | 25 | if (!regExp.test(data)) { 26 | fs.appendFileSync(projectBridgingHeaderPath, importDirective + "\n"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/android/AssetManagerCache.java: -------------------------------------------------------------------------------- 1 | package com.meteor.webapp; 2 | 3 | import android.content.res.AssetManager; 4 | 5 | import java.io.IOException; 6 | import java.io.ObjectInputStream; 7 | import java.util.Map; 8 | 9 | class AssetManagerCache { 10 | private static final String LOG_TAG = "MeteorWebApp"; 11 | 12 | private AssetManager assetManager; 13 | private Map listCache; 14 | 15 | public AssetManagerCache(AssetManager assetManager) throws IOException { 16 | this.assetManager = assetManager; 17 | 18 | ObjectInputStream inputStream = null; 19 | try { 20 | inputStream = new ObjectInputStream(assetManager.open("cdvasset.manifest")); 21 | listCache = (Map) inputStream.readObject(); 22 | } catch (ClassNotFoundException e) { 23 | } finally { 24 | if (inputStream != null) { 25 | try { 26 | inputStream.close(); 27 | } catch (IOException e) { 28 | } 29 | } 30 | } 31 | } 32 | 33 | public final String[] list(String path) { 34 | if (path.startsWith("/")) { 35 | path = path.substring(1); 36 | } 37 | 38 | if (path.endsWith("/")) { 39 | path = path.substring(0, path.length() - 1); 40 | } 41 | 42 | String[] children = listCache.get(path); 43 | return children; 44 | } 45 | 46 | public boolean exists(String path) { 47 | String parentPath; 48 | String filename; 49 | 50 | int parentEndIndex = path.lastIndexOf("/"); 51 | if (parentEndIndex == -1) { 52 | parentPath = ""; 53 | filename = path; 54 | } else { 55 | parentPath = path.substring(0, parentEndIndex); 56 | filename = path.substring(parentEndIndex + 1); 57 | } 58 | 59 | String[] children = list(parentPath); 60 | 61 | if (children == null) return false; 62 | 63 | for (String child : children) { 64 | if (child.equals(filename)) { 65 | return true; 66 | } 67 | } 68 | 69 | return false; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/android/AssetManifest.java: -------------------------------------------------------------------------------- 1 | package com.meteor.webapp; 2 | 3 | import org.json.JSONArray; 4 | import org.json.JSONException; 5 | import org.json.JSONObject; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | final class AssetManifest { 11 | private static final String LOG_TAG = "MeteorWebApp"; 12 | 13 | static final class Entry { 14 | final String filePath; 15 | final String urlPath; 16 | final String fileType; 17 | final boolean cacheable; 18 | final String hash; 19 | final String sourceMapFilePath; 20 | final String sourceMapUrlPath; 21 | 22 | Entry(String filePath, String urlPath, String fileType, boolean cacheable, String hash, String sourceMapFilePath, String sourceMapUrlPath) { 23 | this.filePath = filePath; 24 | this.urlPath = urlPath; 25 | this.fileType = fileType; 26 | this.cacheable = cacheable; 27 | this.hash = hash; 28 | this.sourceMapFilePath = sourceMapFilePath; 29 | this.sourceMapUrlPath = sourceMapUrlPath; 30 | } 31 | } 32 | 33 | final String version; 34 | final String cordovaCompatibilityVersion; 35 | final List entries; 36 | 37 | public AssetManifest(String string) throws WebAppException { 38 | try { 39 | JSONObject json = new JSONObject(string); 40 | String format = json.optString("format", null); 41 | if (format != null && !format.equals("web-program-pre1")) { 42 | throw new WebAppException("The asset manifest format is incompatible: " + format); 43 | } 44 | 45 | try { 46 | version = json.getString("version"); 47 | } catch (JSONException e) { 48 | throw new WebAppException("Asset manifest does not have a version", e); 49 | } 50 | 51 | try { 52 | JSONObject cordovaCompatibilityVersions = json.getJSONObject("cordovaCompatibilityVersions"); 53 | cordovaCompatibilityVersion = cordovaCompatibilityVersions.getString("android"); 54 | } catch (JSONException e) { 55 | throw new WebAppException("Asset manifest does not have a cordovaCompatibilityVersion", e); 56 | } 57 | 58 | JSONArray entriesJSON = json.getJSONArray("manifest"); 59 | 60 | entries = new ArrayList(entriesJSON.length()); 61 | 62 | for (int i = 0; i < entriesJSON.length(); i++) { 63 | JSONObject entryJSON = entriesJSON.getJSONObject(i); 64 | 65 | if (!entryJSON.getString("where").equals("client")) continue; 66 | 67 | String filePath = entryJSON.getString("path"); 68 | String urlPath = entryJSON.getString("url"); 69 | 70 | String fileType = entryJSON.getString("type"); 71 | boolean cacheable = entryJSON.getBoolean("cacheable"); 72 | String hash = entryJSON.optString("hash", null); 73 | String sourceMapFilePath = entryJSON.optString("sourceMap", null); 74 | String sourceMapUrlPath = entryJSON.optString("sourceMapUrl", null); 75 | 76 | Entry entry = new Entry(filePath, urlPath, fileType, cacheable, hash, sourceMapFilePath, sourceMapUrlPath); 77 | entries.add(entry); 78 | } 79 | } catch (JSONException e) { 80 | throw new WebAppException("Error parsing asset manifest", e); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/android/DownloadFailureException.java: -------------------------------------------------------------------------------- 1 | package com.meteor.webapp; 2 | 3 | class WebAppException extends Exception { 4 | public WebAppException(String detailMessage) { 5 | super(detailMessage); 6 | } 7 | 8 | public WebAppException(String detailMessage, Throwable throwable) { 9 | super(detailMessage, throwable); 10 | } 11 | 12 | public WebAppException(Throwable throwable) { 13 | super(throwable); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/android/IOUtils.java: -------------------------------------------------------------------------------- 1 | package com.meteor.webapp; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.io.InputStream; 7 | import java.io.InputStreamReader; 8 | 9 | import okio.BufferedSink; 10 | import okio.Okio; 11 | import okio.Source; 12 | 13 | class IOUtils { 14 | private static final String LOG_TAG = IOUtils.class.getSimpleName(); 15 | 16 | public static String stringFromInputStream(InputStream inputStream) throws IOException { 17 | assert (inputStream != null); 18 | 19 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); 20 | StringBuilder stringBuilder = new StringBuilder(); 21 | String line; 22 | while ((line = reader.readLine()) != null) { 23 | stringBuilder.append(line); 24 | stringBuilder.append("\n"); 25 | } 26 | return stringBuilder.toString(); 27 | } 28 | 29 | public static File writeToFile(Source source, File file) throws IOException { 30 | BufferedSink sink = null; 31 | try { 32 | sink = Okio.buffer(Okio.sink(file)); 33 | sink.writeAll(source); 34 | } finally { 35 | source.close(); 36 | if (sink != null) { 37 | sink.close(); 38 | } 39 | } 40 | return file; 41 | } 42 | 43 | public static void writeToFile(byte[] bytes, File file) throws IOException { 44 | BufferedSink sink = null; 45 | try { 46 | sink = Okio.buffer(Okio.sink(file)); 47 | sink.write(bytes); 48 | } finally { 49 | if (sink != null) { 50 | sink.close(); 51 | } 52 | } 53 | } 54 | 55 | public static boolean deleteRecursively(File file) { 56 | if (file.isDirectory()) { 57 | for (File child : file.listFiles()) { 58 | if (!deleteRecursively(child)) { 59 | return false; 60 | } 61 | } 62 | } 63 | return file.delete(); 64 | } 65 | } -------------------------------------------------------------------------------- /src/android/WebAppConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.meteor.webapp; 2 | 3 | import android.content.SharedPreferences; 4 | import android.util.Log; 5 | 6 | import java.util.Collections; 7 | import java.util.HashSet; 8 | import java.util.Set; 9 | 10 | class WebAppConfiguration { 11 | private SharedPreferences preferences; 12 | 13 | public WebAppConfiguration(SharedPreferences preferences) { 14 | this.preferences = preferences; 15 | } 16 | 17 | public String getAppId() { 18 | return preferences.getString("appId", null); 19 | } 20 | 21 | public void setAppId(String appId) { 22 | preferences.edit().putString("appId", appId).commit(); 23 | } 24 | 25 | public String getRootUrlString() { 26 | return preferences.getString("rootUrl", null); 27 | } 28 | 29 | public void setRootUrlString(String rootUrlString) { 30 | preferences.edit().putString("rootUrl", rootUrlString).commit(); 31 | } 32 | 33 | public String getCordovaCompatibilityVersion() { 34 | return preferences.getString("cordovaCompatibilityVersion", null); 35 | } 36 | 37 | public void setCordovaCompatibilityVersion(String version) { 38 | preferences.edit().putString("cordovaCompatibilityVersion", version).commit(); 39 | } 40 | 41 | public String getLastDownloadedVersion() { 42 | return preferences.getString("lastDownloadedVersion", null); 43 | } 44 | 45 | public void setLastDownloadedVersion(String version) { 46 | preferences.edit().putString("lastDownloadedVersion", version).commit(); 47 | } 48 | 49 | public String getLastSeenInitialVersion() { 50 | return preferences.getString("lastSeenInitialVersion", null); 51 | } 52 | 53 | public void setLastSeenInitialVersion(String version) { 54 | preferences.edit().putString("lastSeenInitialVersion", version).commit(); 55 | } 56 | 57 | public String getLastKnownGoodVersion() { 58 | return preferences.getString("lastKnownGoodVersion", null); 59 | } 60 | 61 | public void setLastKnownGoodVersion(String version) { 62 | preferences.edit().putString("lastKnownGoodVersion", version).commit(); 63 | } 64 | 65 | public Set getBlacklistedVersions() { 66 | Set blacklistedVersions = preferences.getStringSet("blacklistedVersions", Collections.EMPTY_SET); 67 | Log.d("BLACKLIST", "getBlacklistedVersions: " + blacklistedVersions); 68 | return blacklistedVersions; 69 | } 70 | 71 | public void addBlacklistedVersion(String version) { 72 | Set versionsForRetry = new HashSet(preferences.getStringSet("versionsForRetry", Collections.EMPTY_SET)); 73 | Set blacklistedVersions = new HashSet(getBlacklistedVersions()); 74 | Log.d("BLACKLIST", "versionsForRetry: " + versionsForRetry); 75 | 76 | if (!versionsForRetry.contains(version) && !blacklistedVersions.contains(version)) { 77 | Log.d("BLACKLIST", "adding faulty version for retry: " + version); 78 | versionsForRetry.add(version); 79 | preferences.edit().putStringSet("versionsForRetry", versionsForRetry).commit(); 80 | } else { 81 | versionsForRetry.remove(version); 82 | blacklistedVersions.add(version); 83 | Log.d("BLACKLIST", "blacklisting version: " + version); 84 | preferences.edit().putStringSet("versionsForRetry", versionsForRetry).commit(); 85 | preferences.edit().putStringSet("blacklistedVersions", blacklistedVersions).commit(); 86 | } 87 | } 88 | 89 | public void reset() { 90 | preferences.edit().clear().commit(); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/android/WebResourceHandler.java: -------------------------------------------------------------------------------- 1 | package com.meteor.webapp; 2 | 3 | import android.net.Uri; 4 | 5 | interface WebResourceHandler { 6 | Uri remapUri(Uri uri); 7 | } 8 | -------------------------------------------------------------------------------- /src/android/build-extras.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed to the Apache Software Foundation (ASF) under one 3 | or more contributor license agreements. See the NOTICE file 4 | distributed with this work for additional information 5 | regarding copyright ownership. The ASF licenses this file 6 | to you under the Apache License, Version 2.0 (the 7 | "License"); you may not use this file except in compliance 8 | with the License. You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, 13 | software distributed under the License is distributed on an 14 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | KIND, either express or implied. See the License for the 16 | specific language governing permissions and limitations 17 | under the License. 18 | */ 19 | cdvPluginPostBuildExtras.add({ 20 | def inAssetsDir = file("src/main/assets") 21 | if (!inAssetsDir.exists()) { 22 | // Backwards compatibility for cordova-android < 7.0.0: 23 | inAssetsDir = file("assets") 24 | } 25 | def outAssetsDir = inAssetsDir 26 | def outFile = new File(outAssetsDir, "cdvasset.manifest") 27 | 28 | def newTask = task("cdvCreateAssetManifest") { 29 | doLast { 30 | def contents = new HashMap() 31 | def sizes = new HashMap() 32 | contents[""] = inAssetsDir.list() 33 | def tree = fileTree(dir: inAssetsDir) 34 | tree.visit { fileDetails -> 35 | if (fileDetails.isDirectory()) { 36 | contents[fileDetails.relativePath.toString()] = fileDetails.file.list() 37 | } else { 38 | sizes[fileDetails.relativePath.toString()] = fileDetails.file.length() 39 | } 40 | } 41 | 42 | outAssetsDir.mkdirs() 43 | outFile.withObjectOutputStream { oos -> 44 | oos.writeObject(contents) 45 | oos.writeObject(sizes) 46 | } 47 | } 48 | } 49 | newTask.inputs.dir inAssetsDir 50 | newTask.outputs.file outFile 51 | def preBuildTask = tasks["preBuild"] 52 | preBuildTask.dependsOn(newTask) 53 | }) 54 | -------------------------------------------------------------------------------- /src/ios/Asset.swift: -------------------------------------------------------------------------------- 1 | struct Asset { 2 | let bundle: AssetBundle 3 | let filePath: String 4 | var fileURL: URL { 5 | return bundle.directoryURL.appendingPathComponent(filePath, isDirectory: false) 6 | } 7 | let urlPath: String 8 | let fileType: String? 9 | let cacheable: Bool 10 | let hash: String? 11 | let sourceMapURLPath: String? 12 | 13 | init(bundle: AssetBundle, filePath: String, urlPath: String, fileType: String? = nil, 14 | cacheable: Bool, hash: String? = nil, sourceMapURLPath: String? = nil) { 15 | self.bundle = bundle 16 | self.filePath = filePath 17 | self.urlPath = urlPath 18 | self.fileType = fileType 19 | self.cacheable = cacheable 20 | self.hash = hash 21 | self.sourceMapURLPath = sourceMapURLPath 22 | } 23 | } 24 | 25 | extension Asset: CustomStringConvertible { 26 | var description: String { 27 | return urlPath 28 | } 29 | } 30 | 31 | extension Asset: Hashable, Equatable { 32 | var hashValue: Int { return ObjectIdentifier(bundle).hashValue ^ urlPath.hashValue } 33 | } 34 | 35 | func ==(lhs: Asset, rhs: Asset) -> Bool { 36 | return ObjectIdentifier(lhs.bundle) == ObjectIdentifier(rhs.bundle) 37 | && lhs.urlPath == rhs.urlPath 38 | } 39 | -------------------------------------------------------------------------------- /src/ios/AssetBundle.swift: -------------------------------------------------------------------------------- 1 | /// Regex used to extract __meteor_runtime_config__ from index.html 2 | private let configJSONRegEx = try! NSRegularExpression( 3 | pattern: "__meteor_runtime_config__ = JSON.parse\\(decodeURIComponent\\(\"([^\"]*)\"\\)\\)", 4 | options: []) 5 | 6 | /// Load the runtime config by extracting and parsing 7 | /// `__meteor_runtime_config__` from index.html 8 | func loadRuntimeConfigFromIndexFileAtURL(_ fileURL: URL) throws -> AssetBundle.RuntimeConfig { 9 | do { 10 | let indexFileString = try NSString(contentsOf: fileURL, encoding: String.Encoding.utf8.rawValue) 11 | guard 12 | let match = configJSONRegEx.firstMatchInString(indexFileString as String), 13 | let configString = (indexFileString.substring(with: match.range(at: 1)) as NSString).removingPercentEncoding, 14 | let configData = configString.data(using: String.Encoding.utf8) 15 | else { throw WebAppError.unsuitableAssetBundle(reason: "Couldn't load runtime config from index file", underlyingError: nil) } 16 | return AssetBundle.RuntimeConfig(json: try JSONSerialization.jsonObject(with: configData, options: []) as! JSONObject) 17 | } catch { 18 | throw WebAppError.unsuitableAssetBundle(reason: "Couldn't load runtime config from index file", underlyingError: error) 19 | } 20 | } 21 | 22 | final class AssetBundle { 23 | private(set) var directoryURL: URL 24 | 25 | let version: String 26 | let cordovaCompatibilityVersion: String 27 | 28 | private var parentAssetBundle: AssetBundle? 29 | private var ownAssetsByURLPath: [String: Asset] = [:] 30 | private(set) var indexFile: Asset? 31 | 32 | var ownAssets: [Asset] { 33 | return Array(ownAssetsByURLPath.values) 34 | } 35 | 36 | convenience init(directoryURL: URL, parentAssetBundle: AssetBundle? = nil) throws { 37 | let manifestURL = directoryURL.appendingPathComponent("program.json") 38 | let manifest = try AssetManifest(fileURL: manifestURL) 39 | try self.init(directoryURL: directoryURL, manifest: manifest, parentAssetBundle: parentAssetBundle) 40 | } 41 | 42 | init(directoryURL: URL, manifest: AssetManifest, parentAssetBundle: AssetBundle? = nil) throws { 43 | self.directoryURL = directoryURL 44 | self.parentAssetBundle = parentAssetBundle 45 | 46 | self.version = manifest.version 47 | self.cordovaCompatibilityVersion = manifest.cordovaCompatibilityVersion 48 | 49 | for entry in manifest.entries { 50 | let URLPath = URLPathByRemovingQueryString(entry.URLPath) 51 | 52 | if parentAssetBundle?.cachedAssetForURLPath(URLPath, hash: entry.hash) == nil { 53 | let asset = Asset( 54 | bundle: self, 55 | filePath: entry.filePath, 56 | urlPath: URLPath, 57 | fileType: entry.fileType, 58 | cacheable: entry.cacheable, 59 | hash: entry.hash, 60 | sourceMapURLPath: entry.sourceMapURLPath) 61 | addAsset(asset) 62 | } 63 | 64 | if let sourceMapPath = entry.sourceMapPath, 65 | let sourceMapURLPath = entry.sourceMapURLPath { 66 | if parentAssetBundle?.cachedAssetForURLPath(sourceMapURLPath) == nil { 67 | let sourceMap = Asset( 68 | bundle: self, 69 | filePath: sourceMapPath, 70 | urlPath: sourceMapURLPath, 71 | fileType: "json", 72 | cacheable: true) 73 | addAsset(sourceMap) 74 | } 75 | } 76 | } 77 | 78 | let indexFile = Asset(bundle: self, filePath: "index.html", urlPath: "/", fileType: "html", cacheable: false, hash: nil) 79 | addAsset(indexFile) 80 | self.indexFile = indexFile 81 | } 82 | 83 | func addAsset(_ asset: Asset) { 84 | ownAssetsByURLPath[asset.urlPath] = asset 85 | } 86 | 87 | func assetForURLPath(_ URLPath: String) -> Asset? { 88 | return ownAssetsByURLPath[URLPath] ?? parentAssetBundle?.assetForURLPath(URLPath) 89 | } 90 | 91 | func assetExistsInBundle(_ URLPath: String) -> Bool { 92 | return ownAssetsByURLPath[URLPath] != nil 93 | } 94 | 95 | func cachedAssetForURLPath(_ URLPath: String, hash: String? = nil) -> Asset? { 96 | if let asset = ownAssetsByURLPath[URLPath], 97 | // If the asset is not cacheable, we require a matching hash 98 | (asset.cacheable || asset.hash != nil) && asset.hash == hash { 99 | return asset 100 | } else { 101 | return nil 102 | } 103 | } 104 | 105 | struct RuntimeConfig { 106 | private let json: JSONObject 107 | 108 | init(json: JSONObject) { 109 | self.json = json 110 | } 111 | 112 | var appId: String? { 113 | return json["appId"] as? String 114 | } 115 | 116 | var rootURL: URL? { 117 | if let rootURLString = json["ROOT_URL"] as? String { 118 | return URL(string: rootURLString) 119 | } else { 120 | return nil 121 | } 122 | } 123 | 124 | var autoupdateVersionCordova: String? { 125 | return json["autoupdateVersionCordova"] as? String 126 | } 127 | } 128 | 129 | /// The runtime config is lazily initialized by loading it from the index.html 130 | lazy var runtimeConfig: RuntimeConfig? = { 131 | guard let indexFile = self.indexFile else { return nil } 132 | 133 | do { 134 | return try loadRuntimeConfigFromIndexFileAtURL(indexFile.fileURL as URL) 135 | } catch { 136 | NSLog("\(error)") 137 | return nil 138 | } 139 | }() 140 | 141 | var appId: String? { 142 | return runtimeConfig?.appId 143 | } 144 | 145 | var rootURL: URL? { 146 | return runtimeConfig?.rootURL 147 | } 148 | 149 | func didMoveToDirectoryAtURL(_ directoryURL: URL) { 150 | self.directoryURL = directoryURL 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/ios/AssetManifest.swift: -------------------------------------------------------------------------------- 1 | struct AssetManifest { 2 | struct Entry { 3 | let filePath: String 4 | let URLPath: String 5 | let fileType: String 6 | let cacheable: Bool 7 | let hash: String? 8 | let sourceMapPath: String? 9 | let sourceMapURLPath: String? 10 | } 11 | 12 | let version: String 13 | let cordovaCompatibilityVersion: String 14 | var entries: [Entry] 15 | 16 | init(fileURL: URL) throws { 17 | try self.init(data: try Data(contentsOf: fileURL, options: [])) 18 | } 19 | 20 | init(data: Data) throws { 21 | let JSON: JSONObject 22 | do { 23 | JSON = try JSONSerialization.jsonObject(with: data, options: []) as! JSONObject 24 | } catch { 25 | throw WebAppError.invalidAssetManifest(reason: "Error parsing asset manifest", underlyingError: error) 26 | } 27 | 28 | if let format = JSON["format"] as? String, format != "web-program-pre1" { 29 | throw WebAppError.invalidAssetManifest(reason: "The asset manifest format is incompatible: \(format)", underlyingError: nil) 30 | } 31 | 32 | guard let version = JSON["version"] as? String else { 33 | throw WebAppError.invalidAssetManifest(reason: "Asset manifest does not have a version", underlyingError: nil) 34 | } 35 | 36 | self.version = version 37 | 38 | guard let cordovaCompatibilityVersions = JSON["cordovaCompatibilityVersions"] as? JSONObject, 39 | let cordovaCompatibilityVersion = cordovaCompatibilityVersions["ios"] as? String else { 40 | throw WebAppError.invalidAssetManifest(reason: "Asset manifest does not have a cordovaCompatibilityVersion", underlyingError: nil) 41 | } 42 | 43 | self.cordovaCompatibilityVersion = cordovaCompatibilityVersion 44 | 45 | let entriesJSON = JSON["manifest"] as? [JSONObject] ?? [] 46 | entries = [] 47 | for entryJSON in entriesJSON { 48 | if entryJSON["where"] as? String != "client" { continue } 49 | 50 | if let URLPath = entryJSON["url"] as? String, 51 | let filePath = entryJSON["path"] as? String, 52 | let fileType = entryJSON["type"] as? String, 53 | let hash = entryJSON["hash"] as? String, 54 | let cacheable = entryJSON["cacheable"] as? Bool { 55 | let sourceMapPath = entryJSON["sourceMap"] as? String 56 | let sourceMapURLPath = entryJSON["sourceMapUrl"] as? String 57 | 58 | let entry = Entry(filePath: filePath, URLPath: URLPath, 59 | fileType: fileType, cacheable: cacheable, hash: hash, 60 | sourceMapPath: sourceMapPath, sourceMapURLPath: sourceMapURLPath) 61 | entries.append(entry) 62 | } 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/ios/Errors.swift: -------------------------------------------------------------------------------- 1 | enum WebAppError: Error, CustomStringConvertible { 2 | case invalidAssetManifest(reason: String, underlyingError: Error?) 3 | case fileSystemFailure(reason: String, underlyingError: Error?) 4 | case downloadFailure(reason: String, underlyingError: Error?) 5 | case unsuitableAssetBundle(reason: String, underlyingError: Error?) 6 | 7 | var description: String { 8 | switch self { 9 | case .invalidAssetManifest(let reason, let underlyingError): 10 | return errorMessageWithReason(reason, underlyingError: underlyingError) 11 | case .fileSystemFailure(let reason, let underlyingError): 12 | return errorMessageWithReason(reason, underlyingError: underlyingError) 13 | case .downloadFailure(let reason, let underlyingError): 14 | return errorMessageWithReason(reason, underlyingError: underlyingError) 15 | case .unsuitableAssetBundle(let reason, let underlyingError): 16 | return errorMessageWithReason(reason, underlyingError: underlyingError) 17 | } 18 | } 19 | } 20 | 21 | func errorMessageWithReason(_ reason: String, underlyingError: Error?) -> String { 22 | if let underlyingError = underlyingError { 23 | return "\(reason): \(underlyingError)" 24 | } else { 25 | return reason 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/ios/METNetworkReachabilityManager.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2015 Martijn Walraven 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #import 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | typedef NS_ENUM(NSInteger, METNetworkReachabilityStatus) { 26 | METNetworkReachabilityStatusUnknown = 0, 27 | METNetworkReachabilityStatusNotReachable, 28 | METNetworkReachabilityStatusReachable 29 | }; 30 | 31 | @protocol METNetworkReachabilityManagerDelegate; 32 | 33 | @interface METNetworkReachabilityManager : NSObject 34 | 35 | - (instancetype)initWithHostName:(NSString *)hostName NS_DESIGNATED_INITIALIZER; 36 | - (instancetype)init NS_UNAVAILABLE; 37 | 38 | @property (nullable, weak, nonatomic) id delegate; 39 | @property (nullable, strong, nonatomic) dispatch_queue_t delegateQueue; 40 | 41 | @property (assign, nonatomic) METNetworkReachabilityStatus reachabilityStatus; 42 | 43 | - (BOOL)startMonitoring; 44 | - (void)stopMonitoring; 45 | 46 | @end 47 | 48 | @protocol METNetworkReachabilityManagerDelegate 49 | 50 | - (void)networkReachabilityManager:(METNetworkReachabilityManager *)reachabilityManager didDetectReachabilityStatusChange:(METNetworkReachabilityStatus)reachabilityStatus; 51 | 52 | @end 53 | 54 | NS_ASSUME_NONNULL_END 55 | -------------------------------------------------------------------------------- /src/ios/METNetworkReachabilityManager.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2015 Martijn Walraven 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #import "METNetworkReachabilityManager.h" 22 | 23 | @import SystemConfiguration; 24 | 25 | @interface METNetworkReachabilityManager () 26 | 27 | - (void)didReceiveCallbackWithFlags:(SCNetworkReachabilityFlags)flags; 28 | 29 | @end 30 | 31 | static void METNetworkReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) { 32 | METNetworkReachabilityManager *reachabilityManager = (__bridge METNetworkReachabilityManager *)info; 33 | [reachabilityManager didReceiveCallbackWithFlags:flags]; 34 | } 35 | 36 | @implementation METNetworkReachabilityManager { 37 | SCNetworkReachabilityRef _reachabilityRef; 38 | } 39 | 40 | #pragma mark - Lifecycle 41 | 42 | - (instancetype)initWithHostName:(NSString *)hostName { 43 | self = [super init]; 44 | if (self) { 45 | _reachabilityRef = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]); 46 | if (_reachabilityRef == NULL) { 47 | self = nil; 48 | } 49 | } 50 | return self; 51 | } 52 | 53 | - (void)dealloc { 54 | [self stopMonitoring]; 55 | if (_reachabilityRef != NULL) { 56 | CFRelease(_reachabilityRef); 57 | } 58 | } 59 | 60 | #pragma mark - Monitoring Reachability State 61 | 62 | - (BOOL)startMonitoring { 63 | NSAssert(_delegateQueue != nil, @"Delegate queue should be set before calling startMonitoring"); 64 | 65 | SCNetworkReachabilityContext context = {0, (__bridge void *)(self), NULL, NULL, NULL}; 66 | 67 | if (SCNetworkReachabilitySetCallback(_reachabilityRef, METNetworkReachabilityCallback, &context)) { 68 | return SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, _delegateQueue); 69 | } 70 | 71 | return NO; 72 | } 73 | 74 | - (void)stopMonitoring { 75 | if (_reachabilityRef != NULL) { 76 | SCNetworkReachabilitySetDispatchQueue(_reachabilityRef, NULL); 77 | } 78 | } 79 | 80 | - (void)didReceiveCallbackWithFlags:(SCNetworkReachabilityFlags)flags { 81 | BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0); 82 | BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0); 83 | BOOL canConnectAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)); 84 | BOOL canConnectWithoutUserInteraction = (canConnectAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0); 85 | BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction)); 86 | 87 | if (isNetworkReachable) { 88 | self.reachabilityStatus = METNetworkReachabilityStatusReachable; 89 | } else { 90 | self.reachabilityStatus = METNetworkReachabilityStatusNotReachable; 91 | } 92 | 93 | [_delegate networkReachabilityManager:self didDetectReachabilityStatusChange:_reachabilityStatus]; 94 | } 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /src/ios/METPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface CDVPlugin () 4 | 5 | - (instancetype)initWithWebViewEngine:(id )theWebViewEngine NS_DESIGNATED_INITIALIZER; 6 | 7 | @end 8 | 9 | @interface METPlugin : CDVPlugin 10 | 11 | @end 12 | -------------------------------------------------------------------------------- /src/ios/METPlugin.m: -------------------------------------------------------------------------------- 1 | #import "METPlugin.h" 2 | 3 | @implementation METPlugin 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /src/ios/METRandomValueGenerator.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2015 Martijn Walraven 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #import 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @interface METRandomValueGenerator : NSObject 26 | 27 | + (METRandomValueGenerator *)defaultRandomValueGenerator; 28 | 29 | - (double)randomFraction; 30 | 31 | - (NSString *)randomStringWithCharactersFromString:(NSString *)characters length:(NSUInteger)length; 32 | - (NSString *)randomHexStringWithLength:(NSUInteger)length; 33 | - (NSString *)randomSeed; 34 | - (NSString *)randomIdentifier; 35 | 36 | @end 37 | 38 | NS_ASSUME_NONNULL_END 39 | -------------------------------------------------------------------------------- /src/ios/METRandomValueGenerator.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2015 Martijn Walraven 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #import "METRandomValueGenerator.h" 22 | 23 | @implementation METRandomValueGenerator 24 | 25 | + (METRandomValueGenerator *)defaultRandomValueGenerator { 26 | static METRandomValueGenerator *defaultRandomValueGenerator; 27 | static dispatch_once_t onceToken; 28 | 29 | dispatch_once(&onceToken, ^{ 30 | defaultRandomValueGenerator = [[self alloc] init]; 31 | }); 32 | 33 | return defaultRandomValueGenerator; 34 | } 35 | 36 | - (instancetype)init { 37 | self = [super init]; 38 | if (self) { 39 | // rand48 functions require an initial value to be seeded 40 | srand48(time(0)); 41 | } 42 | return self; 43 | } 44 | 45 | - (double)randomFraction { 46 | return drand48(); 47 | } 48 | 49 | - (NSUInteger)randomUnsignedInteger { 50 | return arc4random(); 51 | } 52 | 53 | - (NSUInteger)randomIntegerLessThanInteger:(NSUInteger)upperBound { 54 | return arc4random_uniform((u_int32_t)upperBound); 55 | } 56 | 57 | - (NSString *)randomStringWithCharactersFromString:(NSString *)characters length:(NSUInteger)length { 58 | NSMutableString *string = [NSMutableString stringWithCapacity:length]; 59 | for (NSUInteger i = 0; i < length; i++) { 60 | NSUInteger index = [self randomIntegerLessThanInteger:[characters length]]; 61 | unichar character = [characters characterAtIndex:index]; 62 | [string appendFormat:@"%c", character]; 63 | } 64 | return [string copy]; 65 | } 66 | 67 | - (NSString *)randomHexStringWithLength:(NSUInteger)length { 68 | return [self randomStringWithCharactersFromString:@"0123456789abcdef" length:length]; 69 | } 70 | 71 | - (NSString *)randomSeed { 72 | return [self randomHexStringWithLength:20]; 73 | } 74 | 75 | - (NSString *)randomIdentifier { 76 | return [self randomStringWithCharactersFromString:@"23456789ABCDEFGHJKLMNPQRSTWXYZabcdefghijkmnopqrstuvwxyz" length:17]; 77 | } 78 | 79 | @end 80 | -------------------------------------------------------------------------------- /src/ios/METRetryStrategy.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2015 Martijn Walraven 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #import 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @interface METRetryStrategy : NSObject 26 | 27 | @property (assign, nonatomic) NSTimeInterval minimumTimeInterval; 28 | @property (assign, nonatomic) NSTimeInterval maximumTimeInterval; 29 | @property (assign, nonatomic) NSUInteger numberOfAttemptsAtMinimumTimeInterval; 30 | @property (assign, nonatomic) NSTimeInterval baseTimeInterval; 31 | @property (assign, nonatomic) double exponent; 32 | @property (assign, nonatomic) double randomizationFactor; 33 | 34 | - (NSTimeInterval)retryIntervalForNumberOfAttempts:(NSUInteger)numberOfAttempts; 35 | 36 | @end 37 | 38 | NS_ASSUME_NONNULL_END 39 | -------------------------------------------------------------------------------- /src/ios/METRetryStrategy.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2015 Martijn Walraven 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #import "METRetryStrategy.h" 22 | 23 | #import "METRandomValueGenerator.h" 24 | 25 | @implementation METRetryStrategy 26 | 27 | - (instancetype)init { 28 | self = [super init]; 29 | if (self) { 30 | _maximumTimeInterval = DBL_MAX; 31 | } 32 | return self; 33 | } 34 | 35 | - (NSTimeInterval)retryIntervalForNumberOfAttempts:(NSUInteger)numberOfAttempts { 36 | if (numberOfAttempts < _numberOfAttemptsAtMinimumTimeInterval) { 37 | return _minimumTimeInterval; 38 | } else { 39 | NSTimeInterval timeInterval = fmin(_maximumTimeInterval, _baseTimeInterval * pow(_exponent, numberOfAttempts)); 40 | if (_randomizationFactor > 0) { 41 | // 1 + Math.random() * this.randomisationFactor_ 42 | timeInterval *= ([[METRandomValueGenerator defaultRandomValueGenerator] randomFraction] * _randomizationFactor) + (1 - _randomizationFactor / 2); 43 | } 44 | return timeInterval; 45 | } 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /src/ios/METTimer.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2015 Martijn Walraven 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #import 22 | 23 | NS_ASSUME_NONNULL_BEGIN 24 | 25 | @interface METTimer : NSObject 26 | 27 | - (instancetype)initWithQueue:(dispatch_queue_t)queue block:(void (^)())block NS_DESIGNATED_INITIALIZER; 28 | - (instancetype)init NS_UNAVAILABLE; 29 | 30 | @property (assign, nonatomic) NSTimeInterval tolerance; 31 | 32 | - (void)startWithTimeInterval:(NSTimeInterval)timeInterval; 33 | - (void)stop; 34 | 35 | @end 36 | 37 | NS_ASSUME_NONNULL_END 38 | -------------------------------------------------------------------------------- /src/ios/METTimer.m: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014-2015 Martijn Walraven 2 | // 3 | // Permission is hereby granted, free of charge, to any person obtaining a copy 4 | // of this software and associated documentation files (the "Software"), to deal 5 | // in the Software without restriction, including without limitation the rights 6 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | // copies of the Software, and to permit persons to whom the Software is 8 | // furnished to do so, subject to the following conditions: 9 | // 10 | // The above copyright notice and this permission notice shall be included in 11 | // all copies or substantial portions of the Software. 12 | // 13 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | // THE SOFTWARE. 20 | 21 | #import "METTimer.h" 22 | 23 | @implementation METTimer { 24 | dispatch_queue_t _queue; 25 | void (^_block)(); 26 | 27 | dispatch_source_t _timer_source; 28 | BOOL _started; 29 | } 30 | 31 | - (instancetype)initWithQueue:(dispatch_queue_t)queue block:(void (^)())block { 32 | self = [super init]; 33 | if (self) { 34 | _queue = queue; 35 | _block = [block copy]; 36 | _tolerance = 0.1; 37 | _timer_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _queue); 38 | dispatch_source_set_event_handler(_timer_source, ^{ 39 | if (_started) { 40 | dispatch_suspend(_timer_source); 41 | _started = NO; 42 | } 43 | _block(); 44 | }); 45 | } 46 | return self; 47 | } 48 | 49 | - (void)startWithTimeInterval:(NSTimeInterval)timeInterval { 50 | dispatch_source_set_timer(_timer_source, dispatch_time(DISPATCH_TIME_NOW, timeInterval * NSEC_PER_SEC), 0, _tolerance * NSEC_PER_MSEC); 51 | if (!_started) { 52 | dispatch_resume(_timer_source); 53 | _started = YES; 54 | } 55 | } 56 | 57 | - (void)stop { 58 | if (_started) { 59 | dispatch_suspend(_timer_source); 60 | _started = NO; 61 | } 62 | } 63 | 64 | @end 65 | -------------------------------------------------------------------------------- /src/ios/Utility.swift: -------------------------------------------------------------------------------- 1 | extension Collection { 2 | func find(_ predicate: (Self.Iterator.Element) throws -> Bool) rethrows -> Self.Iterator.Element? { 3 | return try index(where: predicate).map({self[$0]}) 4 | } 5 | } 6 | 7 | typealias JSONObject = [String:AnyObject] 8 | 9 | // Regex that matches the query string part of a URL 10 | let queryStringRegEx = try! NSRegularExpression(pattern: "(/[^?]+).*", options: []) 11 | 12 | func URLPathByRemovingQueryString(_ URLString: String) -> String { 13 | guard let match = queryStringRegEx.firstMatchInString(URLString) else { 14 | return URLString 15 | } 16 | return (URLString as NSString).substring(with: match.range(at: 1)) 17 | } 18 | 19 | // Regex that matches a SHA1 hash 20 | let sha1HashRegEx = try! NSRegularExpression(pattern: "[0-9a-f]{40}", options: []) 21 | 22 | // Regex that matches an ETag with a SHA1 hash 23 | let ETagWithSha1HashRegEx = try! NSRegularExpression(pattern: "\"([0-9a-f]{40})\"", options: []) 24 | 25 | func SHA1HashFromETag(_ ETag: String) -> String? { 26 | guard let match = ETagWithSha1HashRegEx.firstMatchInString(ETag) else { 27 | return nil 28 | } 29 | 30 | return (ETag as NSString).substring(with: match.range(at: 1)) 31 | } 32 | 33 | extension NSRegularExpression { 34 | func firstMatchInString(_ string: String) -> NSTextCheckingResult? { 35 | return firstMatch(in: string, options: [], 36 | range: NSRange(location: 0, length: string.utf16.count)) 37 | } 38 | 39 | func matches(_ string: String) -> Bool { 40 | return firstMatchInString(string) != nil 41 | } 42 | } 43 | 44 | extension URL { 45 | var isDirectory: Bool? { 46 | let values = try? self.resourceValues(forKeys: [.isDirectoryKey]) 47 | return values?.isDirectory 48 | } 49 | 50 | var isRegularFile: Bool? { 51 | let values = try? self.resourceValues(forKeys: [.isRegularFileKey]) 52 | return values?.isRegularFile 53 | } 54 | } 55 | 56 | extension HTTPURLResponse { 57 | var isSuccessful: Bool { 58 | return (200..<300).contains(statusCode) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/ios/WebAppConfiguration.swift: -------------------------------------------------------------------------------- 1 | final class WebAppConfiguration { 2 | let userDefaults = UserDefaults.standard 3 | 4 | /// The appId as defined in the runtime config 5 | var appId: String? { 6 | get { 7 | return userDefaults.string(forKey: "MeteorWebAppId") 8 | } 9 | set { 10 | let oldValue = appId 11 | if newValue != oldValue && newValue != nil { 12 | if oldValue != nil { 13 | NSLog("appId seems to have changed, new: \(newValue!), old: \(oldValue!)") 14 | } 15 | 16 | userDefaults.set(newValue, forKey: "MeteorWebAppId") 17 | userDefaults.synchronize() 18 | } 19 | } 20 | } 21 | 22 | /// The rootURL as defined in the runtime config 23 | var rootURL: URL? { 24 | get { 25 | return userDefaults.url(forKey: "MeteorWebAppRootURL") 26 | } 27 | set { 28 | let oldValue = rootURL 29 | if newValue != oldValue && newValue != nil { 30 | if oldValue != nil { 31 | NSLog("ROOT_URL seems to have changed, new: \(newValue!), old: \(oldValue!)") 32 | } 33 | 34 | userDefaults.set(newValue, forKey: "MeteorWebAppRootURL") 35 | userDefaults.synchronize() 36 | } 37 | } 38 | } 39 | 40 | /// The Cordova compatibility version as specified in the asset manifest 41 | var cordovaCompatibilityVersion: String? { 42 | get { 43 | return userDefaults.string(forKey: "MeteorWebAppCordovaCompatibilityVersion") 44 | } 45 | 46 | set { 47 | if newValue != cordovaCompatibilityVersion { 48 | if newValue == nil { 49 | userDefaults.removeObject(forKey: "MeteorWebAppCordovaCompatibilityVersion") 50 | } else { 51 | userDefaults.set(newValue, forKey: "MeteorWebAppCordovaCompatibilityVersion") 52 | } 53 | userDefaults.synchronize() 54 | } 55 | } 56 | } 57 | 58 | /// The last seen initial version of the asset bundle 59 | var lastSeenInitialVersion: String? { 60 | get { 61 | return userDefaults.string(forKey: "MeteorWebAppLastSeenInitialVersion") 62 | } 63 | 64 | set { 65 | if newValue != lastSeenInitialVersion { 66 | if newValue == nil { 67 | userDefaults.removeObject(forKey: "MeteorWebAppLastSeenInitialVersion") 68 | } else { 69 | userDefaults.set(newValue, forKey: "MeteorWebAppLastSeenInitialVersion") 70 | } 71 | userDefaults.synchronize() 72 | } 73 | } 74 | } 75 | 76 | /// The last downloaded version of the asset bundle 77 | var lastDownloadedVersion: String? { 78 | get { 79 | return userDefaults.string(forKey: "MeteorWebAppLastDownloadedVersion") 80 | } 81 | 82 | set { 83 | if newValue != lastDownloadedVersion { 84 | if newValue == nil { 85 | userDefaults.removeObject(forKey: "MeteorWebAppLastDownloadedVersion") 86 | } else { 87 | userDefaults.set(newValue, forKey: "MeteorWebAppLastDownloadedVersion") 88 | } 89 | userDefaults.synchronize() 90 | } 91 | } 92 | } 93 | 94 | /// The last kwown good version of the asset bundle 95 | var lastKnownGoodVersion: String? { 96 | get { 97 | return userDefaults.string(forKey: "MeteorWebAppLastKnownGoodVersion") 98 | } 99 | 100 | set { 101 | if newValue != lastKnownGoodVersion { 102 | let userDefaults = UserDefaults.standard 103 | if newValue == nil { 104 | userDefaults.removeObject(forKey: "MeteorWebAppLastKnownGoodVersion") 105 | } else { 106 | userDefaults.set(newValue, forKey: "MeteorWebAppLastKnownGoodVersion") 107 | } 108 | userDefaults.synchronize() 109 | } 110 | } 111 | } 112 | 113 | /// Blacklisted asset bundle versions 114 | var blacklistedVersions: [String] { 115 | get { 116 | let versions = userDefaults.array(forKey: "MeteorWebAppBlacklistedVersions") as? [String] ?? [] 117 | NSLog("BLACKLIST - blacklistedVersions: \(versions)") 118 | return versions 119 | } 120 | 121 | set { 122 | if newValue != blacklistedVersions { 123 | if newValue.isEmpty { 124 | NSLog("BLACKLIST - removing blacklisted versions"); 125 | userDefaults.removeObject(forKey: "MeteorWebAppBlacklistedVersions") 126 | } else { 127 | userDefaults.set(newValue, forKey: "MeteorWebAppBlacklistedVersions") 128 | } 129 | userDefaults.synchronize() 130 | } 131 | } 132 | } 133 | 134 | var versionsToRetry: [String] { 135 | get { 136 | let versions = userDefaults.array(forKey: "MeteorWebAppVersionsToRetry") as? [String] ?? [] 137 | NSLog("BLACKLIST - versionsToRetry: \(versions)") 138 | return versions 139 | } 140 | set { 141 | if newValue != versionsToRetry { 142 | if newValue.isEmpty { 143 | NSLog("BLACKLIST - removing versions to retry") 144 | userDefaults.removeObject(forKey: "MeteorWebAppVersionsToRetry") 145 | } else { 146 | userDefaults.set(newValue, forKey: "MeteorWebAppVersionsToRetry") 147 | } 148 | userDefaults.synchronize() 149 | } 150 | } 151 | } 152 | 153 | func addBlacklistedVersion(_ version: String) { 154 | var blacklistedVersions = self.blacklistedVersions 155 | var versionsToRetry = self.versionsToRetry 156 | 157 | if (!versionsToRetry.contains(version) && !blacklistedVersions.contains(version)) { 158 | NSLog("BLACKLIST - adding faulty version to retry: \(version)") 159 | versionsToRetry.append(version) 160 | self.versionsToRetry = versionsToRetry 161 | } else { 162 | if let i = versionsToRetry.index(of: version) { 163 | versionsToRetry.remove(at: i) 164 | self.versionsToRetry = versionsToRetry 165 | } 166 | if (!blacklistedVersions.contains(version)) { 167 | blacklistedVersions.append(version) 168 | NSLog("BLACKLIST - blacklisting version: \(version)") 169 | self.blacklistedVersions = blacklistedVersions 170 | } 171 | } 172 | } 173 | 174 | func reset() { 175 | cordovaCompatibilityVersion = nil 176 | lastSeenInitialVersion = nil 177 | lastDownloadedVersion = nil 178 | lastKnownGoodVersion = nil 179 | blacklistedVersions = [] 180 | versionsToRetry = [] 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/ios/cordova-plugin-meteor-webapp-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "METPlugin.h" 2 | #import "METTimer.h" 3 | #import "METRetryStrategy.h" 4 | #import "METNetworkReachabilityManager.h" 5 | #import "METRandomValueGenerator.h" 6 | 7 | #import "GCDWebServer.h" 8 | #import "GCDWebServerPrivate.h" 9 | -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/app/mobileapp.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | ///////////////////////////////////////////////////////////////////////// 4 | // // 5 | // mobileapp.js // 6 | // // 7 | ///////////////////////////////////////////////////////////////////////// 8 | // 9 | if (Meteor.isClient) { // 1 10 | // counter starts at 0 // 11 | Session.setDefault('counter', 0); // 3 12 | // 13 | Template.hello.helpers({ // 5 14 | counter: function () { // 6 15 | return Session.get('counter'); // 7 16 | } // 17 | }); // 18 | // 19 | Template.hello.events({ // 11 20 | 'click button': function () { // 12 21 | // increment the counter when button is clicked // 22 | Session.set('counter', Session.get('counter') + 1); // 14 23 | } // 24 | }); // 25 | } // 26 | // 27 | if (Meteor.isServer) { // 19 28 | Meteor.startup(function () { // 20 29 | // code to run on server at startup // 30 | }); // 31 | } // 32 | ///////////////////////////////////////////////////////////////////////// 33 | 34 | }).call(this); 35 | -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/app/mobileapp.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/mobileapp.js"],"names":[],"mappings":";;;;;;;;AAAA,IAAI,MAAM,CAAC,QAAQ,EAAE;;AAEnB,SAAO,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;;AAEjC,UAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;AACrB,WAAO,EAAE,YAAY;AACnB,aAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;KAC/B;GACF,CAAC,CAAC;;AAEH,UAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;AACpB,kBAAc,EAAE,YAAY;;AAE1B,aAAO,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;KACpD;GACF,CAAC,CAAC;CACJ;;AAED,IAAI,MAAM,CAAC,QAAQ,EAAE;AACnB,QAAM,CAAC,OAAO,CAAC,YAAY;;GAE1B,CAAC,CAAC;CACJ,wE","file":"/mobileapp.js","sourcesContent":["if (Meteor.isClient) {\n // counter starts at 0\n Session.setDefault('counter', 0);\n\n Template.hello.helpers({\n counter: function () {\n return Session.get('counter');\n }\n });\n\n Template.hello.events({\n 'click button': function () {\n // increment the counter when button is clicked\n Session.set('counter', Session.get('counter') + 1);\n }\n });\n}\n\nif (Meteor.isServer) {\n Meteor.startup(function () {\n // code to run on server at startup\n });\n}\n"]} -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/app/some-data.json: -------------------------------------------------------------------------------- 1 | some-data.json 2 | -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/app/some-file: -------------------------------------------------------------------------------- 1 | some-file 2 | -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/app/some-font.woff: -------------------------------------------------------------------------------- 1 | some-font.woff 2 | -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/app/some-image.jpg: -------------------------------------------------------------------------------- 1 | some-image.jpg 2 | -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/app/some-image.png: -------------------------------------------------------------------------------- 1 | some-image.png 2 | -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/app/some-javascript.js: -------------------------------------------------------------------------------- 1 | some-javascript.js 2 | -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/app/some-page.html: -------------------------------------------------------------------------------- 1 | some-page.html 2 | -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/app/some-stylesheet.css: -------------------------------------------------------------------------------- 1 | some-stylesheet.css 2 | -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/app/some-text.txt: -------------------------------------------------------------------------------- 1 | some-text.txt 2 | -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/app/some-video.mp4: -------------------------------------------------------------------------------- 1 | some-video.mp4 2 | -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/app/template.mobileapp.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | Template.body.addContent((function() { 3 | var view = this; 4 | return [ HTML.Raw("

Welcome to Meteor!

\n\n "), Spacebars.include(view.lookupTemplate("hello")) ]; 5 | })); 6 | Meteor.startup(Template.body.renderToDocument); 7 | 8 | Template.__checkName("hello"); 9 | Template["hello"] = new Template("Template.hello", (function() { 10 | var view = this; 11 | return [ HTML.Raw("\n "), HTML.P("You've pressed the button ", Blaze.View("lookup:counter", function() { 12 | return Spacebars.mustache(view.lookup("counter")); 13 | }), " times.") ]; 14 | })); 15 | 16 | }).call(this); 17 | -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/app/template.mobileapp.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/template.mobileapp.js"],"names":[],"mappings":"YAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/template.mobileapp.js","sourcesContent":["\nTemplate.body.addContent((function() {\n var view = this;\n return [ HTML.Raw(\"

Welcome to Meteor!

\\n\\n \"), Spacebars.include(view.lookupTemplate(\"hello\")) ];\n}));\nMeteor.startup(Template.body.renderToDocument);\n\nTemplate.__checkName(\"hello\");\nTemplate[\"hello\"] = new Template(\"Template.hello\", (function() {\n var view = this;\n return [ HTML.Raw(\"\\n \"), HTML.P(\"You've pressed the button \", Blaze.View(\"lookup:counter\", function() {\n return Spacebars.mustache(view.lookup(\"counter\"));\n }), \" times.\") ];\n}));\n"]} -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/head.html: -------------------------------------------------------------------------------- 1 | mobileapp -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | mobileapp 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/merged-stylesheets.css: -------------------------------------------------------------------------------- 1 | /* CSS declarations go here */ -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/merged-stylesheets.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/app/mobileapp.css"],"names":[],"mappings":"AAAA","sourcesContent":["/* CSS declarations go here */\n"]} -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/not-in-manifest: -------------------------------------------------------------------------------- 1 | not-in-manifest 2 | -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/application/program.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": "web-program-pre1", 3 | "version": "version1", 4 | "cordovaCompatibilityVersions": { 5 | "android": "4017747ca6b4f460f33b121e439b7a11a070205a", 6 | "ios": "0abe549f2adbcf6cd295428aefea628ebe69666a" 7 | }, 8 | "manifest": [ 9 | { 10 | "path": "packages/meteor.js", 11 | "where": "client", 12 | "type": "js", 13 | "cacheable": true, 14 | "url": "/packages/meteor.js?57d11a30155349aa5106f8150cee35eac5f4764c", 15 | "sourceMap": "packages/meteor.js.map", 16 | "sourceMapUrl": "/packages/57d11a30155349aa5106f8150cee35eac5f4764c.map", 17 | "size": 113991, 18 | "hash": "57d11a30155349aa5106f8150cee35eac5f4764c" 19 | }, 20 | { 21 | "path": "app/template.mobileapp.js", 22 | "where": "client", 23 | "type": "js", 24 | "cacheable": true, 25 | "url": "/app/template.mobileapp.js?979b20f66caf126704c250fbd29ce253c6cb490e", 26 | "sourceMap": "app/template.mobileapp.js.map", 27 | "sourceMapUrl": "/app/979b20f66caf126704c250fbd29ce253c6cb490e.map", 28 | "size": 578, 29 | "hash": "979b20f66caf126704c250fbd29ce253c6cb490e" 30 | }, 31 | { 32 | "path": "app/mobileapp.js", 33 | "where": "client", 34 | "type": "js", 35 | "cacheable": true, 36 | "url": "/app/mobileapp.js?6db9763f3e0f4e4cbf78111f73823043ab08e3e7", 37 | "sourceMap": "app/mobileapp.js.map", 38 | "sourceMapUrl": "/app/6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map", 39 | "size": 2275, 40 | "hash": "6db9763f3e0f4e4cbf78111f73823043ab08e3e7" 41 | }, 42 | { 43 | "path": "merged-stylesheets.css", 44 | "where": "client", 45 | "type": "css", 46 | "cacheable": true, 47 | "url": "/merged-stylesheets.css?20ae2c8d51b2507244e598844414ecdec2615ce3", 48 | "sourceMap": "merged-stylesheets.css.map", 49 | "sourceMapUrl": "/20ae2c8d51b2507244e598844414ecdec2615ce3.map", 50 | "size": 30, 51 | "hash": "20ae2c8d51b2507244e598844414ecdec2615ce3" 52 | }, 53 | { 54 | "path": "app/some-data.json", 55 | "where": "client", 56 | "type": "asset", 57 | "cacheable": false, 58 | "url": "/some-data.json", 59 | "size": 15, 60 | "hash": "3edc8875bc0dd76d9f5fce5e823dca6f17a26da7" 61 | }, 62 | { 63 | "path": "app/some-file", 64 | "where": "client", 65 | "type": "asset", 66 | "cacheable": false, 67 | "url": "/some-file", 68 | "size": 10, 69 | "hash": "23300652a57f3e819038d9a268ba36af0e25d33b" 70 | }, 71 | { 72 | "path": "app/some-font.woff", 73 | "where": "client", 74 | "type": "asset", 75 | "cacheable": false, 76 | "url": "/some-font.woff", 77 | "size": 15, 78 | "hash": "6ec7e1e1c0199bfb5bcd6877de9fe7abefd26df8" 79 | }, 80 | { 81 | "path": "app/some-image.jpg", 82 | "where": "client", 83 | "type": "asset", 84 | "cacheable": false, 85 | "url": "/some-image.jpg", 86 | "size": 15, 87 | "hash": "13f1d459365d5604dbf2b64b203fa583c1c7fc3f" 88 | }, 89 | { 90 | "path": "app/some-image.png", 91 | "where": "client", 92 | "type": "asset", 93 | "cacheable": false, 94 | "url": "/some-image.png", 95 | "size": 15, 96 | "hash": "06b05b4c2720cd9ff733d21c594eac4e865a6e73" 97 | }, 98 | { 99 | "path": "app/some-javascript.js", 100 | "where": "client", 101 | "type": "asset", 102 | "cacheable": false, 103 | "url": "/some-javascript.js", 104 | "size": 19, 105 | "hash": "51a3422f25ddf466a35e00e327d5f4ca90eee8f4" 106 | }, 107 | { 108 | "path": "app/some-page.html", 109 | "where": "client", 110 | "type": "asset", 111 | "cacheable": false, 112 | "url": "/some-page.html", 113 | "size": 15, 114 | "hash": "5dc6878863a1fd4f7f69713b4c072280932255af" 115 | }, 116 | { 117 | "path": "app/some-stylesheet.css", 118 | "where": "client", 119 | "type": "asset", 120 | "cacheable": false, 121 | "url": "/some-stylesheet.css", 122 | "size": 20, 123 | "hash": "b33cc1bdaa963ae1cec9afd4c833d80caf7641a2" 124 | }, 125 | { 126 | "path": "app/some-text.txt", 127 | "where": "client", 128 | "type": "asset", 129 | "cacheable": false, 130 | "url": "/some-text.txt", 131 | "size": 14, 132 | "hash": "bb874a02400d28518a3d0f7a4c7fd8970735bea1" 133 | }, 134 | { 135 | "path": "app/some-video.mp4", 136 | "where": "client", 137 | "type": "asset", 138 | "cacheable": false, 139 | "url": "/some-video.mp4", 140 | "size": 15, 141 | "hash": "45e892d4c7ce693f5cd551fcd671cf227ff1ae3a" 142 | }, 143 | { 144 | "path": "head.html", 145 | "where": "internal", 146 | "type": "head", 147 | "hash": "2ce23f770b76d2f1cb0d71f4a43fbbb61afb25be" 148 | } 149 | ] 150 | } 151 | -------------------------------------------------------------------------------- /tests/fixtures/bundled_www/cordova_plugins.js: -------------------------------------------------------------------------------- 1 | cordova.define('cordova/plugin_list', function(require, exports, module) { 2 | module.exports = [ 3 | { 4 | "file": "plugins/cordova-plugin-statusbar/www/statusbar.js", 5 | "id": "cordova-plugin-statusbar.statusbar", 6 | "pluginId": "cordova-plugin-statusbar", 7 | "clobbers": [ 8 | "window.StatusBar" 9 | ] 10 | }, 11 | { 12 | "file": "plugins/cordova-plugin-splashscreen/www/splashscreen.js", 13 | "id": "cordova-plugin-splashscreen.SplashScreen", 14 | "pluginId": "cordova-plugin-splashscreen", 15 | "clobbers": [ 16 | "navigator.splashscreen" 17 | ] 18 | }, 19 | { 20 | "file": "plugins/cordova-plugin-wkwebview-engine/src/www/ios/ios-wkwebview-exec.js", 21 | "id": "cordova-plugin-wkwebview-engine.ios-wkwebview-exec", 22 | "pluginId": "cordova-plugin-wkwebview-engine", 23 | "clobbers": [ 24 | "cordova.exec" 25 | ] 26 | }, 27 | { 28 | "file": "plugins/cordova-plugin-meteor-webapp/www/webapp_cordova.js", 29 | "id": "cordova-plugin-meteor-webapp.WebAppLocalServer", 30 | "pluginId": "cordova-plugin-meteor-webapp", 31 | "merges": [ 32 | "WebAppLocalServer" 33 | ] 34 | } 35 | ]; 36 | module.exports.metadata = 37 | // TOP OF METADATA 38 | {} 39 | // BOTTOM OF METADATA 40 | }); -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/127.0.0.1_root_url/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": "web-program-pre1", 3 | "version": "127.0.0.1_root_url", 4 | "cordovaCompatibilityVersions": { 5 | "android": "4017747ca6b4f460f33b121e439b7a11a070205a", 6 | "ios": "0abe549f2adbcf6cd295428aefea628ebe69666a" 7 | }, 8 | "manifest": [] 9 | } 10 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/different_cordova_compatibility_version/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": "web-program-pre1", 3 | "version": "different_cordova_compatibility_version", 4 | "cordovaCompatibilityVersions": { 5 | "android": "f9d1e65c341c1b68740d41d35250e6a8e9503236", 6 | "ios": "9a02a7998ece3cf17379eec42582b3618825dc91" 7 | }, 8 | "manifest": [ 9 | { 10 | "path": "packages/meteor.js", 11 | "where": "client", 12 | "type": "js", 13 | "cacheable": true, 14 | "url": "/packages/meteor.js?57d11a30155349aa5106f8150cee35eac5f4764c", 15 | "sourceMap": "packages/meteor.js.map", 16 | "sourceMapUrl": "/packages/57d11a30155349aa5106f8150cee35eac5f4764c.map", 17 | "size": 113991, 18 | "hash": "57d11a30155349aa5106f8150cee35eac5f4764c" 19 | }, 20 | { 21 | "path": "app/template.mobileapp.js", 22 | "where": "client", 23 | "type": "js", 24 | "cacheable": true, 25 | "url": "/app/template.mobileapp.js?979b20f66caf126704c250fbd29ce253c6cb490e", 26 | "sourceMap": "app/template.mobileapp.js.map", 27 | "sourceMapUrl": "/app/979b20f66caf126704c250fbd29ce253c6cb490e.map", 28 | "size": 578, 29 | "hash": "979b20f66caf126704c250fbd29ce253c6cb490e" 30 | }, 31 | { 32 | "path": "app/mobileapp.js", 33 | "where": "client", 34 | "type": "js", 35 | "cacheable": true, 36 | "url": "/app/mobileapp.js?6db9763f3e0f4e4cbf78111f73823043ab08e3e7", 37 | "sourceMap": "app/mobileapp.js.map", 38 | "sourceMapUrl": "/app/6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map", 39 | "size": 2275, 40 | "hash": "6db9763f3e0f4e4cbf78111f73823043ab08e3e7" 41 | }, 42 | { 43 | "path": "merged-stylesheets.css", 44 | "where": "client", 45 | "type": "css", 46 | "cacheable": true, 47 | "url": "/merged-stylesheets.css?20ae2c8d51b2507244e598844414ecdec2615ce3", 48 | "sourceMap": "merged-stylesheets.css.map", 49 | "sourceMapUrl": "/20ae2c8d51b2507244e598844414ecdec2615ce3.map", 50 | "size": 30, 51 | "hash": "20ae2c8d51b2507244e598844414ecdec2615ce3" 52 | }, 53 | { 54 | "path": "app/some-data.json", 55 | "where": "client", 56 | "type": "asset", 57 | "cacheable": false, 58 | "url": "/some-data.json", 59 | "size": 15, 60 | "hash": "3edc8875bc0dd76d9f5fce5e823dca6f17a26da7" 61 | }, 62 | { 63 | "path": "app/some-file", 64 | "where": "client", 65 | "type": "asset", 66 | "cacheable": false, 67 | "url": "/some-file", 68 | "size": 10, 69 | "hash": "23300652a57f3e819038d9a268ba36af0e25d33b" 70 | }, 71 | { 72 | "path": "app/some-font.woff", 73 | "where": "client", 74 | "type": "asset", 75 | "cacheable": false, 76 | "url": "/some-font.woff", 77 | "size": 15, 78 | "hash": "6ec7e1e1c0199bfb5bcd6877de9fe7abefd26df8" 79 | }, 80 | { 81 | "path": "app/some-image.jpg", 82 | "where": "client", 83 | "type": "asset", 84 | "cacheable": false, 85 | "url": "/some-image.jpg", 86 | "size": 15, 87 | "hash": "13f1d459365d5604dbf2b64b203fa583c1c7fc3f" 88 | }, 89 | { 90 | "path": "app/some-image.png", 91 | "where": "client", 92 | "type": "asset", 93 | "cacheable": false, 94 | "url": "/some-image.png", 95 | "size": 15, 96 | "hash": "06b05b4c2720cd9ff733d21c594eac4e865a6e73" 97 | }, 98 | { 99 | "path": "app/some-javascript.js", 100 | "where": "client", 101 | "type": "asset", 102 | "cacheable": false, 103 | "url": "/some-javascript.js", 104 | "size": 19, 105 | "hash": "51a3422f25ddf466a35e00e327d5f4ca90eee8f4" 106 | }, 107 | { 108 | "path": "app/some-page.html", 109 | "where": "client", 110 | "type": "asset", 111 | "cacheable": false, 112 | "url": "/some-page.html", 113 | "size": 15, 114 | "hash": "5dc6878863a1fd4f7f69713b4c072280932255af" 115 | }, 116 | { 117 | "path": "app/some-stylesheet.css", 118 | "where": "client", 119 | "type": "asset", 120 | "cacheable": false, 121 | "url": "/some-stylesheet.css", 122 | "size": 20, 123 | "hash": "b33cc1bdaa963ae1cec9afd4c833d80caf7641a2" 124 | }, 125 | { 126 | "path": "app/some-text.txt", 127 | "where": "client", 128 | "type": "asset", 129 | "cacheable": false, 130 | "url": "/some-text.txt", 131 | "size": 14, 132 | "hash": "bb874a02400d28518a3d0f7a4c7fd8970735bea1" 133 | }, 134 | { 135 | "path": "app/some-video.mp4", 136 | "where": "client", 137 | "type": "asset", 138 | "cacheable": false, 139 | "url": "/some-video.mp4", 140 | "size": 15, 141 | "hash": "45e892d4c7ce693f5cd551fcd671cf227ff1ae3a" 142 | }, 143 | { 144 | "path": "head.html", 145 | "where": "internal", 146 | "type": "head", 147 | "hash": "2ce23f770b76d2f1cb0d71f4a43fbbb61afb25be" 148 | } 149 | ] 150 | } 151 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/missing_app_id/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | mobileapp 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/missing_app_id/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": "web-program-pre1", 3 | "version": "missing_app_id", 4 | "cordovaCompatibilityVersions": { 5 | "android": "4017747ca6b4f460f33b121e439b7a11a070205a", 6 | "ios": "0abe549f2adbcf6cd295428aefea628ebe69666a" 7 | }, 8 | "manifest": [] 9 | } 10 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/missing_cordova_compatibility_version/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": "web-program-pre1", 3 | "version": "missing_cordova_compatibility_version", 4 | "manifest": [ 5 | { 6 | "path": "packages/meteor.js", 7 | "where": "client", 8 | "type": "js", 9 | "cacheable": true, 10 | "url": "/packages/meteor.js?57d11a30155349aa5106f8150cee35eac5f4764c", 11 | "sourceMap": "packages/meteor.js.map", 12 | "sourceMapUrl": "/packages/57d11a30155349aa5106f8150cee35eac5f4764c.map", 13 | "size": 113991, 14 | "hash": "57d11a30155349aa5106f8150cee35eac5f4764c" 15 | }, 16 | { 17 | "path": "app/template.mobileapp.js", 18 | "where": "client", 19 | "type": "js", 20 | "cacheable": true, 21 | "url": "/app/template.mobileapp.js?979b20f66caf126704c250fbd29ce253c6cb490e", 22 | "sourceMap": "app/template.mobileapp.js.map", 23 | "sourceMapUrl": "/app/979b20f66caf126704c250fbd29ce253c6cb490e.map", 24 | "size": 578, 25 | "hash": "979b20f66caf126704c250fbd29ce253c6cb490e" 26 | }, 27 | { 28 | "path": "app/mobileapp.js", 29 | "where": "client", 30 | "type": "js", 31 | "cacheable": true, 32 | "url": "/app/mobileapp.js?6db9763f3e0f4e4cbf78111f73823043ab08e3e7", 33 | "sourceMap": "app/mobileapp.js.map", 34 | "sourceMapUrl": "/app/6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map", 35 | "size": 2275, 36 | "hash": "6db9763f3e0f4e4cbf78111f73823043ab08e3e7" 37 | }, 38 | { 39 | "path": "merged-stylesheets.css", 40 | "where": "client", 41 | "type": "css", 42 | "cacheable": true, 43 | "url": "/merged-stylesheets.css?20ae2c8d51b2507244e598844414ecdec2615ce3", 44 | "sourceMap": "merged-stylesheets.css.map", 45 | "sourceMapUrl": "/20ae2c8d51b2507244e598844414ecdec2615ce3.map", 46 | "size": 30, 47 | "hash": "20ae2c8d51b2507244e598844414ecdec2615ce3" 48 | }, 49 | { 50 | "path": "app/some-data.json", 51 | "where": "client", 52 | "type": "asset", 53 | "cacheable": false, 54 | "url": "/some-data.json", 55 | "size": 15, 56 | "hash": "3edc8875bc0dd76d9f5fce5e823dca6f17a26da7" 57 | }, 58 | { 59 | "path": "app/some-file", 60 | "where": "client", 61 | "type": "asset", 62 | "cacheable": false, 63 | "url": "/some-file", 64 | "size": 10, 65 | "hash": "23300652a57f3e819038d9a268ba36af0e25d33b" 66 | }, 67 | { 68 | "path": "app/some-font.woff", 69 | "where": "client", 70 | "type": "asset", 71 | "cacheable": false, 72 | "url": "/some-font.woff", 73 | "size": 15, 74 | "hash": "6ec7e1e1c0199bfb5bcd6877de9fe7abefd26df8" 75 | }, 76 | { 77 | "path": "app/some-image.jpg", 78 | "where": "client", 79 | "type": "asset", 80 | "cacheable": false, 81 | "url": "/some-image.jpg", 82 | "size": 15, 83 | "hash": "13f1d459365d5604dbf2b64b203fa583c1c7fc3f" 84 | }, 85 | { 86 | "path": "app/some-image.png", 87 | "where": "client", 88 | "type": "asset", 89 | "cacheable": false, 90 | "url": "/some-image.png", 91 | "size": 15, 92 | "hash": "06b05b4c2720cd9ff733d21c594eac4e865a6e73" 93 | }, 94 | { 95 | "path": "app/some-javascript.js", 96 | "where": "client", 97 | "type": "asset", 98 | "cacheable": false, 99 | "url": "/some-javascript.js", 100 | "size": 19, 101 | "hash": "51a3422f25ddf466a35e00e327d5f4ca90eee8f4" 102 | }, 103 | { 104 | "path": "app/some-page.html", 105 | "where": "client", 106 | "type": "asset", 107 | "cacheable": false, 108 | "url": "/some-page.html", 109 | "size": 15, 110 | "hash": "5dc6878863a1fd4f7f69713b4c072280932255af" 111 | }, 112 | { 113 | "path": "app/some-stylesheet.css", 114 | "where": "client", 115 | "type": "asset", 116 | "cacheable": false, 117 | "url": "/some-stylesheet.css", 118 | "size": 20, 119 | "hash": "b33cc1bdaa963ae1cec9afd4c833d80caf7641a2" 120 | }, 121 | { 122 | "path": "app/some-text.txt", 123 | "where": "client", 124 | "type": "asset", 125 | "cacheable": false, 126 | "url": "/some-text.txt", 127 | "size": 14, 128 | "hash": "bb874a02400d28518a3d0f7a4c7fd8970735bea1" 129 | }, 130 | { 131 | "path": "app/some-video.mp4", 132 | "where": "client", 133 | "type": "asset", 134 | "cacheable": false, 135 | "url": "/some-video.mp4", 136 | "size": 15, 137 | "hash": "45e892d4c7ce693f5cd551fcd671cf227ff1ae3a" 138 | }, 139 | { 140 | "path": "head.html", 141 | "where": "internal", 142 | "type": "head", 143 | "hash": "2ce23f770b76d2f1cb0d71f4a43fbbb61afb25be" 144 | } 145 | ] 146 | } 147 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/missing_root_url/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | mobileapp 84 | 85 | 86 | 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/missing_root_url/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": "web-program-pre1", 3 | "version": "missing_root_url", 4 | "cordovaCompatibilityVersions": { 5 | "android": "4017747ca6b4f460f33b121e439b7a11a070205a", 6 | "ios": "0abe549f2adbcf6cd295428aefea628ebe69666a" 7 | }, 8 | "manifest": [] 9 | } 10 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/app/6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/mobileapp.js"],"names":[],"mappings":";;;;;;;;AAAA,IAAI,MAAM,CAAC,QAAQ,EAAE;;AAEnB,SAAO,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;;AAEjC,UAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;AACrB,WAAO,EAAE,YAAY;AACnB,aAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;KAC/B;GACF,CAAC,CAAC;;AAEH,UAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;AACpB,kBAAc,EAAE,YAAY;;AAE1B,aAAO,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;KACpD;GACF,CAAC,CAAC;CACJ;;AAED,IAAI,MAAM,CAAC,QAAQ,EAAE;AACnB,QAAM,CAAC,OAAO,CAAC,YAAY;;GAE1B,CAAC,CAAC;CACJ,wE","file":"/mobileapp.js","sourcesContent":["if (Meteor.isClient) {\n // counter starts at 0\n Session.setDefault('counter', 0);\n\n Template.hello.helpers({\n counter: function () {\n return Session.get('counter');\n }\n });\n\n Template.hello.events({\n 'click button': function () {\n // increment the counter when button is clicked\n Session.set('counter', Session.get('counter') + 1);\n }\n });\n}\n\nif (Meteor.isServer) {\n Meteor.startup(function () {\n // code to run on server at startup\n });\n}\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/app/979b20f66caf126704c250fbd29ce253c6cb490e.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/template.mobileapp.js"],"names":[],"mappings":"YAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/template.mobileapp.js","sourcesContent":["\nTemplate.body.addContent((function() {\n var view = this;\n return [ HTML.Raw(\"

Welcome to Meteor!

\\n\\n \"), Spacebars.include(view.lookupTemplate(\"hello\")) ];\n}));\nMeteor.startup(Template.body.renderToDocument);\n\nTemplate.__checkName(\"hello\");\nTemplate[\"hello\"] = new Template(\"Template.hello\", (function() {\n var view = this;\n return [ HTML.Raw(\"\\n \"), HTML.P(\"You've pressed the button \", Blaze.View(\"lookup:counter\", function() {\n return Spacebars.mustache(view.lookup(\"counter\"));\n }), \" times.\") ];\n}));\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/app/mobileapp.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | ///////////////////////////////////////////////////////////////////////// 4 | // // 5 | // mobileapp.js // 6 | // // 7 | ///////////////////////////////////////////////////////////////////////// 8 | // 9 | if (Meteor.isClient) { // 1 10 | // counter starts at 0 // 11 | Session.setDefault('counter', 0); // 3 12 | // 13 | Template.hello.helpers({ // 5 14 | counter: function () { // 6 15 | return Session.get('counter'); // 7 16 | } // 17 | }); // 18 | // 19 | Template.hello.events({ // 11 20 | 'click button': function () { // 12 21 | // increment the counter when button is clicked // 22 | Session.set('counter', Session.get('counter') + 1); // 14 23 | } // 24 | }); // 25 | } // 26 | // 27 | if (Meteor.isServer) { // 19 28 | Meteor.startup(function () { // 20 29 | // code to run on server at startup // 30 | }); // 31 | } // 32 | ///////////////////////////////////////////////////////////////////////// 33 | 34 | }).call(this); 35 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/app/template.mobileapp.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | Template.body.addContent((function() { 3 | var view = this; 4 | return [ HTML.Raw("

Welcome to Meteor!

\n\n "), Spacebars.include(view.lookupTemplate("hello")) ]; 5 | })); 6 | Meteor.startup(Template.body.renderToDocument); 7 | 8 | Template.__checkName("hello"); 9 | Template["hello"] = new Template("Template.hello", (function() { 10 | var view = this; 11 | return [ HTML.Raw("\n "), HTML.P("You've pressed the button ", Blaze.View("lookup:counter", function() { 12 | return Spacebars.mustache(view.lookup("counter")); 13 | }), " times.") ]; 14 | })); 15 | 16 | }).call(this); 17 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/head.html: -------------------------------------------------------------------------------- 1 | mobileapp -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": "web-program-pre1", 3 | "version": "version1", 4 | "cordovaCompatibilityVersions": { 5 | "android": "4017747ca6b4f460f33b121e439b7a11a070205a", 6 | "ios": "0abe549f2adbcf6cd295428aefea628ebe69666a" 7 | }, 8 | "manifest": [ 9 | { 10 | "path": "packages/meteor.js", 11 | "where": "client", 12 | "type": "js", 13 | "cacheable": true, 14 | "url": "/packages/meteor.js?57d11a30155349aa5106f8150cee35eac5f4764c", 15 | "sourceMap": "packages/meteor.js.map", 16 | "sourceMapUrl": "/packages/57d11a30155349aa5106f8150cee35eac5f4764c.map", 17 | "size": 113991, 18 | "hash": "57d11a30155349aa5106f8150cee35eac5f4764c" 19 | }, 20 | { 21 | "path": "app/template.mobileapp.js", 22 | "where": "client", 23 | "type": "js", 24 | "cacheable": true, 25 | "url": "/app/template.mobileapp.js?979b20f66caf126704c250fbd29ce253c6cb490e", 26 | "sourceMap": "app/template.mobileapp.js.map", 27 | "sourceMapUrl": "/app/979b20f66caf126704c250fbd29ce253c6cb490e.map", 28 | "size": 578, 29 | "hash": "979b20f66caf126704c250fbd29ce253c6cb490e" 30 | }, 31 | { 32 | "path": "app/mobileapp.js", 33 | "where": "client", 34 | "type": "js", 35 | "cacheable": true, 36 | "url": "/app/mobileapp.js?6db9763f3e0f4e4cbf78111f73823043ab08e3e7", 37 | "sourceMap": "app/mobileapp.js.map", 38 | "sourceMapUrl": "/app/6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map", 39 | "size": 2275, 40 | "hash": "6db9763f3e0f4e4cbf78111f73823043ab08e3e7" 41 | }, 42 | { 43 | "path": "merged-stylesheets.css", 44 | "where": "client", 45 | "type": "css", 46 | "cacheable": true, 47 | "url": "/merged-stylesheets.css?20ae2c8d51b2507244e598844414ecdec2615ce3", 48 | "sourceMap": "merged-stylesheets.css.map", 49 | "sourceMapUrl": "/20ae2c8d51b2507244e598844414ecdec2615ce3.map", 50 | "size": 30, 51 | "hash": "20ae2c8d51b2507244e598844414ecdec2615ce3" 52 | }, 53 | { 54 | "path": "app/some-data.json", 55 | "where": "client", 56 | "type": "asset", 57 | "cacheable": false, 58 | "url": "/some-data.json", 59 | "size": 15, 60 | "hash": "3edc8875bc0dd76d9f5fce5e823dca6f17a26da7" 61 | }, 62 | { 63 | "path": "app/some-file", 64 | "where": "client", 65 | "type": "asset", 66 | "cacheable": false, 67 | "url": "/some-file", 68 | "size": 10, 69 | "hash": "23300652a57f3e819038d9a268ba36af0e25d33b" 70 | }, 71 | { 72 | "path": "app/some-font.woff", 73 | "where": "client", 74 | "type": "asset", 75 | "cacheable": false, 76 | "url": "/some-font.woff", 77 | "size": 15, 78 | "hash": "6ec7e1e1c0199bfb5bcd6877de9fe7abefd26df8" 79 | }, 80 | { 81 | "path": "app/some-image.jpg", 82 | "where": "client", 83 | "type": "asset", 84 | "cacheable": false, 85 | "url": "/some-image.jpg", 86 | "size": 15, 87 | "hash": "13f1d459365d5604dbf2b64b203fa583c1c7fc3f" 88 | }, 89 | { 90 | "path": "app/some-image.png", 91 | "where": "client", 92 | "type": "asset", 93 | "cacheable": false, 94 | "url": "/some-image.png", 95 | "size": 15, 96 | "hash": "06b05b4c2720cd9ff733d21c594eac4e865a6e73" 97 | }, 98 | { 99 | "path": "app/some-javascript.js", 100 | "where": "client", 101 | "type": "asset", 102 | "cacheable": false, 103 | "url": "/some-javascript.js", 104 | "size": 19, 105 | "hash": "51a3422f25ddf466a35e00e327d5f4ca90eee8f4" 106 | }, 107 | { 108 | "path": "app/some-page.html", 109 | "where": "client", 110 | "type": "asset", 111 | "cacheable": false, 112 | "url": "/some-page.html", 113 | "size": 15, 114 | "hash": "5dc6878863a1fd4f7f69713b4c072280932255af" 115 | }, 116 | { 117 | "path": "app/some-stylesheet.css", 118 | "where": "client", 119 | "type": "asset", 120 | "cacheable": false, 121 | "url": "/some-stylesheet.css", 122 | "size": 20, 123 | "hash": "b33cc1bdaa963ae1cec9afd4c833d80caf7641a2" 124 | }, 125 | { 126 | "path": "app/some-text.txt", 127 | "where": "client", 128 | "type": "asset", 129 | "cacheable": false, 130 | "url": "/some-text.txt", 131 | "size": 14, 132 | "hash": "bb874a02400d28518a3d0f7a4c7fd8970735bea1" 133 | }, 134 | { 135 | "path": "app/some-video.mp4", 136 | "where": "client", 137 | "type": "asset", 138 | "cacheable": false, 139 | "url": "/some-video.mp4", 140 | "size": 15, 141 | "hash": "45e892d4c7ce693f5cd551fcd671cf227ff1ae3a" 142 | }, 143 | { 144 | "path": "head.html", 145 | "where": "internal", 146 | "type": "head", 147 | "hash": "2ce23f770b76d2f1cb0d71f4a43fbbb61afb25be" 148 | } 149 | ] 150 | } 151 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/merged-stylesheets.css: -------------------------------------------------------------------------------- 1 | /* CSS declarations go here */ -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/merged-stylesheets.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/app/mobileapp.css"],"names":[],"mappings":"AAAA","sourcesContent":["/* CSS declarations go here */\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/not-in-manifest: -------------------------------------------------------------------------------- 1 | not-in-manifest 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/some-data.json: -------------------------------------------------------------------------------- 1 | some-data.json 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/some-file: -------------------------------------------------------------------------------- 1 | some-file 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/some-font.woff: -------------------------------------------------------------------------------- 1 | some-font.woff 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/some-image.jpg: -------------------------------------------------------------------------------- 1 | some-image.jpg 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/some-image.png: -------------------------------------------------------------------------------- 1 | some-image.png 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/some-javascript.js: -------------------------------------------------------------------------------- 1 | some-javascript.js 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/some-page.html: -------------------------------------------------------------------------------- 1 | some-page.html 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/some-stylesheet.css: -------------------------------------------------------------------------------- 1 | some-stylesheet.css 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/some-text.txt: -------------------------------------------------------------------------------- 1 | some-text.txt 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version1/some-video.mp4: -------------------------------------------------------------------------------- 1 | some-video.mp4 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/20ae2c8d51b2507244e598844414ecdec2615ce3.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/app/mobileapp.css"],"names":[],"mappings":"AAAA","sourcesContent":["/* CSS declarations go here */\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/app/3f6275657e6db3a21acb37d0f6c207cf83871e90.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/template.mobileapp.js"],"names":[],"mappings":"YAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/template.mobileapp.js","sourcesContent":["\nTemplate.body.addContent((function() {\n var view = this;\n return [ HTML.Raw(\"

Welcome to Meteor (again)!

\\n \"), Spacebars.include(view.lookupTemplate(\"hello\")) ];\n}));\nMeteor.startup(Template.body.renderToDocument);\n\nTemplate.__checkName(\"hello\");\nTemplate[\"hello\"] = new Template(\"Template.hello\", (function() {\n var view = this;\n return [ HTML.Raw(\"\\n \"), HTML.P(\"You've pressed the button \", Blaze.View(\"lookup:counter\", function() {\n return Spacebars.mustache(view.lookup(\"counter\"));\n }), \" times.\") ];\n}));\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/app/6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/mobileapp.js"],"names":[],"mappings":";;;;;;;;AAAA,IAAI,MAAM,CAAC,QAAQ,EAAE;;AAEnB,SAAO,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;;AAEjC,UAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;AACrB,WAAO,EAAE,YAAY;AACnB,aAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;KAC/B;GACF,CAAC,CAAC;;AAEH,UAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;AACpB,kBAAc,EAAE,YAAY;;AAE1B,aAAO,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;KACpD;GACF,CAAC,CAAC;CACJ;;AAED,IAAI,MAAM,CAAC,QAAQ,EAAE;AACnB,QAAM,CAAC,OAAO,CAAC,YAAY;;GAE1B,CAAC,CAAC;CACJ,wE","file":"/mobileapp.js","sourcesContent":["if (Meteor.isClient) {\n // counter starts at 0\n Session.setDefault('counter', 0);\n\n Template.hello.helpers({\n counter: function () {\n return Session.get('counter');\n }\n });\n\n Template.hello.events({\n 'click button': function () {\n // increment the counter when button is clicked\n Session.set('counter', Session.get('counter') + 1);\n }\n });\n}\n\nif (Meteor.isServer) {\n Meteor.startup(function () {\n // code to run on server at startup\n });\n}\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/app/mobileapp.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | ///////////////////////////////////////////////////////////////////////// 4 | // // 5 | // mobileapp.js // 6 | // // 7 | ///////////////////////////////////////////////////////////////////////// 8 | // 9 | if (Meteor.isClient) { // 1 10 | // counter starts at 0 // 11 | Session.setDefault('counter', 0); // 3 12 | // 13 | Template.hello.helpers({ // 5 14 | counter: function () { // 6 15 | return Session.get('counter'); // 7 16 | } // 17 | }); // 18 | // 19 | Template.hello.events({ // 11 20 | 'click button': function () { // 12 21 | // increment the counter when button is clicked // 22 | Session.set('counter', Session.get('counter') + 1); // 14 23 | } // 24 | }); // 25 | } // 26 | // 27 | if (Meteor.isServer) { // 19 28 | Meteor.startup(function () { // 20 29 | // code to run on server at startup // 30 | }); // 31 | } // 32 | ///////////////////////////////////////////////////////////////////////// 33 | 34 | }).call(this); 35 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/app/template.mobileapp.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | Template.body.addContent((function() { 3 | var view = this; 4 | return [ HTML.Raw("

Welcome to Meteor (again)!

\n "), Spacebars.include(view.lookupTemplate("hello")) ]; 5 | })); 6 | Meteor.startup(Template.body.renderToDocument); 7 | 8 | Template.__checkName("hello"); 9 | Template["hello"] = new Template("Template.hello", (function() { 10 | var view = this; 11 | return [ HTML.Raw("\n "), HTML.P("You've pressed the button ", Blaze.View("lookup:counter", function() { 12 | return Spacebars.mustache(view.lookup("counter")); 13 | }), " times.") ]; 14 | })); 15 | 16 | }).call(this); 17 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/head.html: -------------------------------------------------------------------------------- 1 | mobileapp -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": "web-program-pre1", 3 | "version": "version2", 4 | "cordovaCompatibilityVersions": { 5 | "android": "4017747ca6b4f460f33b121e439b7a11a070205a", 6 | "ios": "0abe549f2adbcf6cd295428aefea628ebe69666a" 7 | }, 8 | "manifest": [ 9 | { 10 | "path": "packages/meteor.js", 11 | "where": "client", 12 | "type": "js", 13 | "cacheable": true, 14 | "url": "/packages/meteor.js?57d11a30155349aa5106f8150cee35eac5f4764c", 15 | "sourceMap": "packages/meteor.js.map", 16 | "sourceMapUrl": "/packages/57d11a30155349aa5106f8150cee35eac5f4764c.map", 17 | "size": 113991, 18 | "hash": "57d11a30155349aa5106f8150cee35eac5f4764c" 19 | }, 20 | { 21 | "path": "app/template.mobileapp.js", 22 | "where": "client", 23 | "type": "js", 24 | "cacheable": true, 25 | "url": "/app/template.mobileapp.js?3f6275657e6db3a21acb37d0f6c207cf83871e90", 26 | "sourceMap": "app/template.mobileapp.js.map", 27 | "sourceMapUrl": "/app/3f6275657e6db3a21acb37d0f6c207cf83871e90.map", 28 | "size": 584, 29 | "hash": "3f6275657e6db3a21acb37d0f6c207cf83871e90" 30 | }, 31 | { 32 | "path": "app/mobileapp.js", 33 | "where": "client", 34 | "type": "js", 35 | "cacheable": true, 36 | "url": "/app/mobileapp.js?6db9763f3e0f4e4cbf78111f73823043ab08e3e7", 37 | "sourceMap": "app/mobileapp.js.map", 38 | "sourceMapUrl": "/app/6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map", 39 | "size": 2275, 40 | "hash": "6db9763f3e0f4e4cbf78111f73823043ab08e3e7" 41 | }, 42 | { 43 | "path": "merged-stylesheets.css", 44 | "where": "client", 45 | "type": "css", 46 | "cacheable": true, 47 | "url": "/merged-stylesheets.css?20ae2c8d51b2507244e598844414ecdec2615ce3", 48 | "sourceMap": "merged-stylesheets.css.map", 49 | "sourceMapUrl": "/20ae2c8d51b2507244e598844414ecdec2615ce3.map", 50 | "size": 30, 51 | "hash": "20ae2c8d51b2507244e598844414ecdec2615ce3" 52 | }, 53 | { 54 | "path": "app/some-data.json", 55 | "where": "client", 56 | "type": "asset", 57 | "cacheable": false, 58 | "url": "/some-data.json", 59 | "size": 15, 60 | "hash": "3edc8875bc0dd76d9f5fce5e823dca6f17a26da7" 61 | }, 62 | { 63 | "path": "app/some-file", 64 | "where": "client", 65 | "type": "asset", 66 | "cacheable": false, 67 | "url": "/some-file", 68 | "size": 20, 69 | "hash": "20242aa2ac9c728ca21c7cbbee841fd87e8277aa" 70 | }, 71 | { 72 | "path": "app/some-other-file", 73 | "where": "client", 74 | "type": "asset", 75 | "cacheable": false, 76 | "url": "/some-other-file", 77 | "size": 16, 78 | "hash": "aa4405bb6a2eb7d79af8488b44d91e5c66c123a5" 79 | }, 80 | { 81 | "path": "app/some-font.woff", 82 | "where": "client", 83 | "type": "asset", 84 | "cacheable": false, 85 | "url": "/some-font.woff", 86 | "size": 15, 87 | "hash": "6ec7e1e1c0199bfb5bcd6877de9fe7abefd26df8" 88 | }, 89 | { 90 | "path": "app/some-image.jpg", 91 | "where": "client", 92 | "type": "asset", 93 | "cacheable": false, 94 | "url": "/some-image.jpg", 95 | "size": 15, 96 | "hash": "13f1d459365d5604dbf2b64b203fa583c1c7fc3f" 97 | }, 98 | { 99 | "path": "app/some-image.png", 100 | "where": "client", 101 | "type": "asset", 102 | "cacheable": false, 103 | "url": "/some-image.png", 104 | "size": 15, 105 | "hash": "06b05b4c2720cd9ff733d21c594eac4e865a6e73" 106 | }, 107 | { 108 | "path": "app/some-javascript.js", 109 | "where": "client", 110 | "type": "asset", 111 | "cacheable": false, 112 | "url": "/some-javascript.js", 113 | "size": 19, 114 | "hash": "51a3422f25ddf466a35e00e327d5f4ca90eee8f4" 115 | }, 116 | { 117 | "path": "app/some-page.html", 118 | "where": "client", 119 | "type": "asset", 120 | "cacheable": false, 121 | "url": "/some-page.html", 122 | "size": 15, 123 | "hash": "5dc6878863a1fd4f7f69713b4c072280932255af" 124 | }, 125 | { 126 | "path": "app/some-stylesheet.css", 127 | "where": "client", 128 | "type": "asset", 129 | "cacheable": false, 130 | "url": "/some-stylesheet.css", 131 | "size": 20, 132 | "hash": "b33cc1bdaa963ae1cec9afd4c833d80caf7641a2" 133 | }, 134 | { 135 | "path": "app/some-text.txt", 136 | "where": "client", 137 | "type": "asset", 138 | "cacheable": false, 139 | "url": "/some-text.txt", 140 | "size": 14, 141 | "hash": "bb874a02400d28518a3d0f7a4c7fd8970735bea1" 142 | }, 143 | { 144 | "path": "app/some-video.mp4", 145 | "where": "client", 146 | "type": "asset", 147 | "cacheable": false, 148 | "url": "/some-video.mp4", 149 | "size": 15, 150 | "hash": "45e892d4c7ce693f5cd551fcd671cf227ff1ae3a" 151 | }, 152 | { 153 | "path": "head.html", 154 | "where": "internal", 155 | "type": "head", 156 | "hash": "2ce23f770b76d2f1cb0d71f4a43fbbb61afb25be" 157 | } 158 | ] 159 | } 160 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/merged-stylesheets.css: -------------------------------------------------------------------------------- 1 | /* CSS declarations go here */ -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/some-data.json: -------------------------------------------------------------------------------- 1 | some-data.json 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/some-file: -------------------------------------------------------------------------------- 1 | some-file (changed) 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/some-font.woff: -------------------------------------------------------------------------------- 1 | some-font.woff 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/some-image.jpg: -------------------------------------------------------------------------------- 1 | some-image.jpg 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/some-image.png: -------------------------------------------------------------------------------- 1 | some-image.png 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/some-javascript.js: -------------------------------------------------------------------------------- 1 | some-javascript.js 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/some-other-file: -------------------------------------------------------------------------------- 1 | some-other-file 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/some-page.html: -------------------------------------------------------------------------------- 1 | some-page.html 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/some-stylesheet.css: -------------------------------------------------------------------------------- 1 | some-stylesheet.css 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/some-text.txt: -------------------------------------------------------------------------------- 1 | some-text.txt 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2/some-video.mp4: -------------------------------------------------------------------------------- 1 | some-video.mp4 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/20ae2c8d51b2507244e598844414ecdec2615ce3.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/app/mobileapp.css"],"names":[],"mappings":"AAAA","sourcesContent":["/* CSS declarations go here */\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/app/3f6275657e6db3a21acb37d0f6c207cf83871e90.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/template.mobileapp.js"],"names":[],"mappings":"YAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/template.mobileapp.js","sourcesContent":["\nTemplate.body.addContent((function() {\n var view = this;\n return [ HTML.Raw(\"

Welcome to Meteor (again)!

\\n \"), Spacebars.include(view.lookupTemplate(\"hello\")) ];\n}));\nMeteor.startup(Template.body.renderToDocument);\n\nTemplate.__checkName(\"hello\");\nTemplate[\"hello\"] = new Template(\"Template.hello\", (function() {\n var view = this;\n return [ HTML.Raw(\"\\n \"), HTML.P(\"You've pressed the button \", Blaze.View(\"lookup:counter\", function() {\n return Spacebars.mustache(view.lookup(\"counter\"));\n }), \" times.\") ];\n}));\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/app/6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/mobileapp.js"],"names":[],"mappings":";;;;;;;;AAAA,IAAI,MAAM,CAAC,QAAQ,EAAE;;AAEnB,SAAO,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;;AAEjC,UAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;AACrB,WAAO,EAAE,YAAY;AACnB,aAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;KAC/B;GACF,CAAC,CAAC;;AAEH,UAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;AACpB,kBAAc,EAAE,YAAY;;AAE1B,aAAO,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;KACpD;GACF,CAAC,CAAC;CACJ;;AAED,IAAI,MAAM,CAAC,QAAQ,EAAE;AACnB,QAAM,CAAC,OAAO,CAAC,YAAY;;GAE1B,CAAC,CAAC;CACJ,wE","file":"/mobileapp.js","sourcesContent":["if (Meteor.isClient) {\n // counter starts at 0\n Session.setDefault('counter', 0);\n\n Template.hello.helpers({\n counter: function () {\n return Session.get('counter');\n }\n });\n\n Template.hello.events({\n 'click button': function () {\n // increment the counter when button is clicked\n Session.set('counter', Session.get('counter') + 1);\n }\n });\n}\n\nif (Meteor.isServer) {\n Meteor.startup(function () {\n // code to run on server at startup\n });\n}\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/app/mobileapp.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | ///////////////////////////////////////////////////////////////////////// 4 | // // 5 | // mobileapp.js // 6 | // // 7 | ///////////////////////////////////////////////////////////////////////// 8 | // 9 | if (Meteor.isClient) { // 1 10 | // counter starts at 0 // 11 | Session.setDefault('counter', 0); // 3 12 | // 13 | Template.hello.helpers({ // 5 14 | counter: function () { // 6 15 | return Session.get('counter'); // 7 16 | } // 17 | }); // 18 | // 19 | Template.hello.events({ // 11 20 | 'click button': function () { // 12 21 | // increment the counter when button is clicked // 22 | Session.set('counter', Session.get('counter') + 1); // 14 23 | } // 24 | }); // 25 | } // 26 | // 27 | if (Meteor.isServer) { // 19 28 | Meteor.startup(function () { // 20 29 | // code to run on server at startup // 30 | }); // 31 | } // 32 | ///////////////////////////////////////////////////////////////////////// 33 | 34 | }).call(this); 35 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/app/template.mobileapp.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | Template.body.addContent((function() { 3 | var view = this; 4 | return [ HTML.Raw("

Welcome to Meteor (one more time)!

\n "), Spacebars.include(view.lookupTemplate("hello")) ]; 5 | })); 6 | Meteor.startup(Template.body.renderToDocument); 7 | 8 | Template.__checkName("hello"); 9 | Template["hello"] = new Template("Template.hello", (function() { 10 | var view = this; 11 | return [ HTML.Raw("\n "), HTML.P("You've pressed the button ", Blaze.View("lookup:counter", function() { 12 | return Spacebars.mustache(view.lookup("counter")); 13 | }), " times.") ]; 14 | })); 15 | 16 | }).call(this); 17 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/head.html: -------------------------------------------------------------------------------- 1 | mobileapp -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": "web-program-pre1", 3 | "version": "version2", 4 | "cordovaCompatibilityVersions": { 5 | "android": "4017747ca6b4f460f33b121e439b7a11a070205a", 6 | "ios": "0abe549f2adbcf6cd295428aefea628ebe69666a" 7 | }, 8 | "manifest": [ 9 | { 10 | "path": "packages/meteor.js", 11 | "where": "client", 12 | "type": "js", 13 | "cacheable": true, 14 | "url": "/packages/meteor.js?57d11a30155349aa5106f8150cee35eac5f4764c", 15 | "sourceMap": "packages/meteor.js.map", 16 | "sourceMapUrl": "/packages/57d11a30155349aa5106f8150cee35eac5f4764c.map", 17 | "size": 113991, 18 | "hash": "57d11a30155349aa5106f8150cee35eac5f4764c" 19 | }, 20 | { 21 | "path": "app/template.mobileapp.js", 22 | "where": "client", 23 | "type": "js", 24 | "cacheable": true, 25 | "url": "/app/template.mobileapp.js?3f6275657e6db3a21acb37d0f6c207cf83871e90", 26 | "sourceMap": "app/template.mobileapp.js.map", 27 | "sourceMapUrl": "/app/3f6275657e6db3a21acb37d0f6c207cf83871e90.map", 28 | "size": 584, 29 | "hash": "3f6275657e6db3a21acb37d0f6c207cf83871e90" 30 | }, 31 | { 32 | "path": "app/mobileapp.js", 33 | "where": "client", 34 | "type": "js", 35 | "cacheable": true, 36 | "url": "/app/mobileapp.js?6db9763f3e0f4e4cbf78111f73823043ab08e3e7", 37 | "sourceMap": "app/mobileapp.js.map", 38 | "sourceMapUrl": "/app/6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map", 39 | "size": 2275, 40 | "hash": "6db9763f3e0f4e4cbf78111f73823043ab08e3e7" 41 | }, 42 | { 43 | "path": "merged-stylesheets.css", 44 | "where": "client", 45 | "type": "css", 46 | "cacheable": true, 47 | "url": "/merged-stylesheets.css?20ae2c8d51b2507244e598844414ecdec2615ce3", 48 | "sourceMap": "merged-stylesheets.css.map", 49 | "sourceMapUrl": "/20ae2c8d51b2507244e598844414ecdec2615ce3.map", 50 | "size": 30, 51 | "hash": "20ae2c8d51b2507244e598844414ecdec2615ce3" 52 | }, 53 | { 54 | "path": "app/some-data.json", 55 | "where": "client", 56 | "type": "asset", 57 | "cacheable": false, 58 | "url": "/some-data.json", 59 | "size": 15, 60 | "hash": "3edc8875bc0dd76d9f5fce5e823dca6f17a26da7" 61 | }, 62 | { 63 | "path": "app/some-file", 64 | "where": "client", 65 | "type": "asset", 66 | "cacheable": false, 67 | "url": "/some-file", 68 | "size": 20, 69 | "hash": "20242aa2ac9c728ca21c7cbbee841fd87e8277aa" 70 | }, 71 | { 72 | "path": "app/some-other-file", 73 | "where": "client", 74 | "type": "asset", 75 | "cacheable": false, 76 | "url": "/some-other-file", 77 | "size": 16, 78 | "hash": "aa4405bb6a2eb7d79af8488b44d91e5c66c123a5" 79 | }, 80 | { 81 | "path": "app/some-font.woff", 82 | "where": "client", 83 | "type": "asset", 84 | "cacheable": false, 85 | "url": "/some-font.woff", 86 | "size": 15, 87 | "hash": "6ec7e1e1c0199bfb5bcd6877de9fe7abefd26df8" 88 | }, 89 | { 90 | "path": "app/some-image.jpg", 91 | "where": "client", 92 | "type": "asset", 93 | "cacheable": false, 94 | "url": "/some-image.jpg", 95 | "size": 15, 96 | "hash": "13f1d459365d5604dbf2b64b203fa583c1c7fc3f" 97 | }, 98 | { 99 | "path": "app/some-image.png", 100 | "where": "client", 101 | "type": "asset", 102 | "cacheable": false, 103 | "url": "/some-image.png", 104 | "size": 15, 105 | "hash": "06b05b4c2720cd9ff733d21c594eac4e865a6e73" 106 | }, 107 | { 108 | "path": "app/some-javascript.js", 109 | "where": "client", 110 | "type": "asset", 111 | "cacheable": false, 112 | "url": "/some-javascript.js", 113 | "size": 19, 114 | "hash": "51a3422f25ddf466a35e00e327d5f4ca90eee8f4" 115 | }, 116 | { 117 | "path": "app/some-page.html", 118 | "where": "client", 119 | "type": "asset", 120 | "cacheable": false, 121 | "url": "/some-page.html", 122 | "size": 15, 123 | "hash": "5dc6878863a1fd4f7f69713b4c072280932255af" 124 | }, 125 | { 126 | "path": "app/some-stylesheet.css", 127 | "where": "client", 128 | "type": "asset", 129 | "cacheable": false, 130 | "url": "/some-stylesheet.css", 131 | "size": 20, 132 | "hash": "b33cc1bdaa963ae1cec9afd4c833d80caf7641a2" 133 | }, 134 | { 135 | "path": "app/some-text.txt", 136 | "where": "client", 137 | "type": "asset", 138 | "cacheable": false, 139 | "url": "/some-text.txt", 140 | "size": 14, 141 | "hash": "bb874a02400d28518a3d0f7a4c7fd8970735bea1" 142 | }, 143 | { 144 | "path": "app/some-video.mp4", 145 | "where": "client", 146 | "type": "asset", 147 | "cacheable": false, 148 | "url": "/some-video.mp4", 149 | "size": 15, 150 | "hash": "45e892d4c7ce693f5cd551fcd671cf227ff1ae3a" 151 | }, 152 | { 153 | "path": "head.html", 154 | "where": "internal", 155 | "type": "head", 156 | "hash": "2ce23f770b76d2f1cb0d71f4a43fbbb61afb25be" 157 | } 158 | ] 159 | } 160 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/merged-stylesheets.css: -------------------------------------------------------------------------------- 1 | /* CSS declarations go here */ -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/some-data.json: -------------------------------------------------------------------------------- 1 | some-data.json 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/some-file: -------------------------------------------------------------------------------- 1 | some-file (changed) 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/some-font.woff: -------------------------------------------------------------------------------- 1 | some-font.woff 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/some-image.jpg: -------------------------------------------------------------------------------- 1 | some-image.jpg 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/some-image.png: -------------------------------------------------------------------------------- 1 | some-image.png 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/some-javascript.js: -------------------------------------------------------------------------------- 1 | some-javascript.js 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/some-other-file: -------------------------------------------------------------------------------- 1 | some-other-file 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/some-page.html: -------------------------------------------------------------------------------- 1 | some-page.html 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/some-stylesheet.css: -------------------------------------------------------------------------------- 1 | some-stylesheet.css 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/some-text.txt: -------------------------------------------------------------------------------- 1 | some-text.txt 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_invalid_asset/some-video.mp4: -------------------------------------------------------------------------------- 1 | some-video.mp4 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/20ae2c8d51b2507244e598844414ecdec2615ce3.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/app/mobileapp.css"],"names":[],"mappings":"AAAA","sourcesContent":["/* CSS declarations go here */\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/app/3f6275657e6db3a21acb37d0f6c207cf83871e90.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/template.mobileapp.js"],"names":[],"mappings":"YAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/template.mobileapp.js","sourcesContent":["\nTemplate.body.addContent((function() {\n var view = this;\n return [ HTML.Raw(\"

Welcome to Meteor (again)!

\\n \"), Spacebars.include(view.lookupTemplate(\"hello\")) ];\n}));\nMeteor.startup(Template.body.renderToDocument);\n\nTemplate.__checkName(\"hello\");\nTemplate[\"hello\"] = new Template(\"Template.hello\", (function() {\n var view = this;\n return [ HTML.Raw(\"\\n \"), HTML.P(\"You've pressed the button \", Blaze.View(\"lookup:counter\", function() {\n return Spacebars.mustache(view.lookup(\"counter\"));\n }), \" times.\") ];\n}));\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/app/6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/mobileapp.js"],"names":[],"mappings":";;;;;;;;AAAA,IAAI,MAAM,CAAC,QAAQ,EAAE;;AAEnB,SAAO,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;;AAEjC,UAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;AACrB,WAAO,EAAE,YAAY;AACnB,aAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;KAC/B;GACF,CAAC,CAAC;;AAEH,UAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;AACpB,kBAAc,EAAE,YAAY;;AAE1B,aAAO,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;KACpD;GACF,CAAC,CAAC;CACJ;;AAED,IAAI,MAAM,CAAC,QAAQ,EAAE;AACnB,QAAM,CAAC,OAAO,CAAC,YAAY;;GAE1B,CAAC,CAAC;CACJ,wE","file":"/mobileapp.js","sourcesContent":["if (Meteor.isClient) {\n // counter starts at 0\n Session.setDefault('counter', 0);\n\n Template.hello.helpers({\n counter: function () {\n return Session.get('counter');\n }\n });\n\n Template.hello.events({\n 'click button': function () {\n // increment the counter when button is clicked\n Session.set('counter', Session.get('counter') + 1);\n }\n });\n}\n\nif (Meteor.isServer) {\n Meteor.startup(function () {\n // code to run on server at startup\n });\n}\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/app/mobileapp.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | ///////////////////////////////////////////////////////////////////////// 4 | // // 5 | // mobileapp.js // 6 | // // 7 | ///////////////////////////////////////////////////////////////////////// 8 | // 9 | if (Meteor.isClient) { // 1 10 | // counter starts at 0 // 11 | Session.setDefault('counter', 0); // 3 12 | // 13 | Template.hello.helpers({ // 5 14 | counter: function () { // 6 15 | return Session.get('counter'); // 7 16 | } // 17 | }); // 18 | // 19 | Template.hello.events({ // 11 20 | 'click button': function () { // 12 21 | // increment the counter when button is clicked // 22 | Session.set('counter', Session.get('counter') + 1); // 14 23 | } // 24 | }); // 25 | } // 26 | // 27 | if (Meteor.isServer) { // 19 28 | Meteor.startup(function () { // 20 29 | // code to run on server at startup // 30 | }); // 31 | } // 32 | ///////////////////////////////////////////////////////////////////////// 33 | 34 | }).call(this); 35 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/head.html: -------------------------------------------------------------------------------- 1 | mobileapp -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": "web-program-pre1", 3 | "version": "version2", 4 | "cordovaCompatibilityVersions": { 5 | "android": "4017747ca6b4f460f33b121e439b7a11a070205a", 6 | "ios": "0abe549f2adbcf6cd295428aefea628ebe69666a" 7 | }, 8 | "manifest": [ 9 | { 10 | "path": "packages/meteor.js", 11 | "where": "client", 12 | "type": "js", 13 | "cacheable": true, 14 | "url": "/packages/meteor.js?57d11a30155349aa5106f8150cee35eac5f4764c", 15 | "sourceMap": "packages/meteor.js.map", 16 | "sourceMapUrl": "/packages/57d11a30155349aa5106f8150cee35eac5f4764c.map", 17 | "size": 113991, 18 | "hash": "57d11a30155349aa5106f8150cee35eac5f4764c" 19 | }, 20 | { 21 | "path": "app/template.mobileapp.js", 22 | "where": "client", 23 | "type": "js", 24 | "cacheable": true, 25 | "url": "/app/template.mobileapp.js?3f6275657e6db3a21acb37d0f6c207cf83871e90", 26 | "sourceMap": "app/template.mobileapp.js.map", 27 | "sourceMapUrl": "/app/3f6275657e6db3a21acb37d0f6c207cf83871e90.map", 28 | "size": 584, 29 | "hash": "3f6275657e6db3a21acb37d0f6c207cf83871e90" 30 | }, 31 | { 32 | "path": "app/mobileapp.js", 33 | "where": "client", 34 | "type": "js", 35 | "cacheable": true, 36 | "url": "/app/mobileapp.js?6db9763f3e0f4e4cbf78111f73823043ab08e3e7", 37 | "sourceMap": "app/mobileapp.js.map", 38 | "sourceMapUrl": "/app/6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map", 39 | "size": 2275, 40 | "hash": "6db9763f3e0f4e4cbf78111f73823043ab08e3e7" 41 | }, 42 | { 43 | "path": "merged-stylesheets.css", 44 | "where": "client", 45 | "type": "css", 46 | "cacheable": true, 47 | "url": "/merged-stylesheets.css?20ae2c8d51b2507244e598844414ecdec2615ce3", 48 | "sourceMap": "merged-stylesheets.css.map", 49 | "sourceMapUrl": "/20ae2c8d51b2507244e598844414ecdec2615ce3.map", 50 | "size": 30, 51 | "hash": "20ae2c8d51b2507244e598844414ecdec2615ce3" 52 | }, 53 | { 54 | "path": "app/some-data.json", 55 | "where": "client", 56 | "type": "asset", 57 | "cacheable": false, 58 | "url": "/some-data.json", 59 | "size": 15, 60 | "hash": "3edc8875bc0dd76d9f5fce5e823dca6f17a26da7" 61 | }, 62 | { 63 | "path": "app/some-file", 64 | "where": "client", 65 | "type": "asset", 66 | "cacheable": false, 67 | "url": "/some-file", 68 | "size": 20, 69 | "hash": "20242aa2ac9c728ca21c7cbbee841fd87e8277aa" 70 | }, 71 | { 72 | "path": "app/some-other-file", 73 | "where": "client", 74 | "type": "asset", 75 | "cacheable": false, 76 | "url": "/some-other-file", 77 | "size": 16, 78 | "hash": "aa4405bb6a2eb7d79af8488b44d91e5c66c123a5" 79 | }, 80 | { 81 | "path": "app/some-font.woff", 82 | "where": "client", 83 | "type": "asset", 84 | "cacheable": false, 85 | "url": "/some-font.woff", 86 | "size": 15, 87 | "hash": "6ec7e1e1c0199bfb5bcd6877de9fe7abefd26df8" 88 | }, 89 | { 90 | "path": "app/some-image.jpg", 91 | "where": "client", 92 | "type": "asset", 93 | "cacheable": false, 94 | "url": "/some-image.jpg", 95 | "size": 15, 96 | "hash": "13f1d459365d5604dbf2b64b203fa583c1c7fc3f" 97 | }, 98 | { 99 | "path": "app/some-image.png", 100 | "where": "client", 101 | "type": "asset", 102 | "cacheable": false, 103 | "url": "/some-image.png", 104 | "size": 15, 105 | "hash": "06b05b4c2720cd9ff733d21c594eac4e865a6e73" 106 | }, 107 | { 108 | "path": "app/some-javascript.js", 109 | "where": "client", 110 | "type": "asset", 111 | "cacheable": false, 112 | "url": "/some-javascript.js", 113 | "size": 19, 114 | "hash": "51a3422f25ddf466a35e00e327d5f4ca90eee8f4" 115 | }, 116 | { 117 | "path": "app/some-page.html", 118 | "where": "client", 119 | "type": "asset", 120 | "cacheable": false, 121 | "url": "/some-page.html", 122 | "size": 15, 123 | "hash": "5dc6878863a1fd4f7f69713b4c072280932255af" 124 | }, 125 | { 126 | "path": "app/some-stylesheet.css", 127 | "where": "client", 128 | "type": "asset", 129 | "cacheable": false, 130 | "url": "/some-stylesheet.css", 131 | "size": 20, 132 | "hash": "b33cc1bdaa963ae1cec9afd4c833d80caf7641a2" 133 | }, 134 | { 135 | "path": "app/some-text.txt", 136 | "where": "client", 137 | "type": "asset", 138 | "cacheable": false, 139 | "url": "/some-text.txt", 140 | "size": 14, 141 | "hash": "bb874a02400d28518a3d0f7a4c7fd8970735bea1" 142 | }, 143 | { 144 | "path": "app/some-video.mp4", 145 | "where": "client", 146 | "type": "asset", 147 | "cacheable": false, 148 | "url": "/some-video.mp4", 149 | "size": 15, 150 | "hash": "45e892d4c7ce693f5cd551fcd671cf227ff1ae3a" 151 | }, 152 | { 153 | "path": "head.html", 154 | "where": "internal", 155 | "type": "head", 156 | "hash": "2ce23f770b76d2f1cb0d71f4a43fbbb61afb25be" 157 | } 158 | ] 159 | } 160 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/merged-stylesheets.css: -------------------------------------------------------------------------------- 1 | /* CSS declarations go here */ -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/some-data.json: -------------------------------------------------------------------------------- 1 | some-data.json 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/some-file: -------------------------------------------------------------------------------- 1 | some-file (changed) 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/some-font.woff: -------------------------------------------------------------------------------- 1 | some-font.woff 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/some-image.jpg: -------------------------------------------------------------------------------- 1 | some-image.jpg 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/some-image.png: -------------------------------------------------------------------------------- 1 | some-image.png 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/some-javascript.js: -------------------------------------------------------------------------------- 1 | some-javascript.js 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/some-other-file: -------------------------------------------------------------------------------- 1 | some-other-file 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/some-page.html: -------------------------------------------------------------------------------- 1 | some-page.html 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/some-stylesheet.css: -------------------------------------------------------------------------------- 1 | some-stylesheet.css 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/some-text.txt: -------------------------------------------------------------------------------- 1 | some-text.txt 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_missing_asset/some-video.mp4: -------------------------------------------------------------------------------- 1 | some-video.mp4 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/20ae2c8d51b2507244e598844414ecdec2615ce3.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/app/mobileapp.css"],"names":[],"mappings":"AAAA","sourcesContent":["/* CSS declarations go here */\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/app/3f6275657e6db3a21acb37d0f6c207cf83871e90.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/template.mobileapp.js"],"names":[],"mappings":"YAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/template.mobileapp.js","sourcesContent":["\nTemplate.body.addContent((function() {\n var view = this;\n return [ HTML.Raw(\"

Welcome to Meteor (again)!

\\n \"), Spacebars.include(view.lookupTemplate(\"hello\")) ];\n}));\nMeteor.startup(Template.body.renderToDocument);\n\nTemplate.__checkName(\"hello\");\nTemplate[\"hello\"] = new Template(\"Template.hello\", (function() {\n var view = this;\n return [ HTML.Raw(\"\\n \"), HTML.P(\"You've pressed the button \", Blaze.View(\"lookup:counter\", function() {\n return Spacebars.mustache(view.lookup(\"counter\"));\n }), \" times.\") ];\n}));\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/app/6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/mobileapp.js"],"names":[],"mappings":";;;;;;;;AAAA,IAAI,MAAM,CAAC,QAAQ,EAAE;;AAEnB,SAAO,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;;AAEjC,UAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;AACrB,WAAO,EAAE,YAAY;AACnB,aAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;KAC/B;GACF,CAAC,CAAC;;AAEH,UAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;AACpB,kBAAc,EAAE,YAAY;;AAE1B,aAAO,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;KACpD;GACF,CAAC,CAAC;CACJ;;AAED,IAAI,MAAM,CAAC,QAAQ,EAAE;AACnB,QAAM,CAAC,OAAO,CAAC,YAAY;;GAE1B,CAAC,CAAC;CACJ,wE","file":"/mobileapp.js","sourcesContent":["if (Meteor.isClient) {\n // counter starts at 0\n Session.setDefault('counter', 0);\n\n Template.hello.helpers({\n counter: function () {\n return Session.get('counter');\n }\n });\n\n Template.hello.events({\n 'click button': function () {\n // increment the counter when button is clicked\n Session.set('counter', Session.get('counter') + 1);\n }\n });\n}\n\nif (Meteor.isServer) {\n Meteor.startup(function () {\n // code to run on server at startup\n });\n}\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/app/mobileapp.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | ///////////////////////////////////////////////////////////////////////// 4 | // // 5 | // mobileapp.js // 6 | // // 7 | ///////////////////////////////////////////////////////////////////////// 8 | // 9 | if (Meteor.isClient) { // 1 10 | // counter starts at 0 // 11 | Session.setDefault('counter', 0); // 3 12 | // 13 | Template.hello.helpers({ // 5 14 | counter: function () { // 6 15 | return Session.get('counter'); // 7 16 | } // 17 | }); // 18 | // 19 | Template.hello.events({ // 11 20 | 'click button': function () { // 12 21 | // increment the counter when button is clicked // 22 | Session.set('counter', Session.get('counter') + 1); // 14 23 | } // 24 | }); // 25 | } // 26 | // 27 | if (Meteor.isServer) { // 19 28 | Meteor.startup(function () { // 20 29 | // code to run on server at startup // 30 | }); // 31 | } // 32 | ///////////////////////////////////////////////////////////////////////// 33 | 34 | }).call(this); 35 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/app/template.mobileapp.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | Template.body.addContent((function() { 3 | var view = this; 4 | return [ HTML.Raw("

Welcome to Meteor (again)!

\n "), Spacebars.include(view.lookupTemplate("hello")) ]; 5 | })); 6 | Meteor.startup(Template.body.renderToDocument); 7 | 8 | Template.__checkName("hello"); 9 | Template["hello"] = new Template("Template.hello", (function() { 10 | var view = this; 11 | return [ HTML.Raw("\n "), HTML.P("You've pressed the button ", Blaze.View("lookup:counter", function() { 12 | return Spacebars.mustache(view.lookup("counter")); 13 | }), " times.") ]; 14 | })); 15 | 16 | }).call(this); 17 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/head.html: -------------------------------------------------------------------------------- 1 | mobileapp -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": "web-program-pre1", 3 | "version": "version2", 4 | "cordovaCompatibilityVersions": { 5 | "android": "4017747ca6b4f460f33b121e439b7a11a070205a", 6 | "ios": "0abe549f2adbcf6cd295428aefea628ebe69666a" 7 | }, 8 | "manifest": [ 9 | { 10 | "path": "packages/meteor.js", 11 | "where": "client", 12 | "type": "js", 13 | "cacheable": true, 14 | "url": "/packages/meteor.js?57d11a30155349aa5106f8150cee35eac5f4764c", 15 | "sourceMap": "packages/meteor.js.map", 16 | "sourceMapUrl": "/packages/57d11a30155349aa5106f8150cee35eac5f4764c.map", 17 | "size": 113991, 18 | "hash": "57d11a30155349aa5106f8150cee35eac5f4764c" 19 | }, 20 | { 21 | "path": "app/template.mobileapp.js", 22 | "where": "client", 23 | "type": "js", 24 | "cacheable": true, 25 | "url": "/app/template.mobileapp.js?3f6275657e6db3a21acb37d0f6c207cf83871e90", 26 | "sourceMap": "app/template.mobileapp.js.map", 27 | "sourceMapUrl": "/app/3f6275657e6db3a21acb37d0f6c207cf83871e90.map", 28 | "size": 584, 29 | "hash": "3f6275657e6db3a21acb37d0f6c207cf83871e90" 30 | }, 31 | { 32 | "path": "app/mobileapp.js", 33 | "where": "client", 34 | "type": "js", 35 | "cacheable": true, 36 | "url": "/app/mobileapp.js?6db9763f3e0f4e4cbf78111f73823043ab08e3e7", 37 | "sourceMap": "app/mobileapp.js.map", 38 | "sourceMapUrl": "/app/6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map", 39 | "size": 2275, 40 | "hash": "6db9763f3e0f4e4cbf78111f73823043ab08e3e7" 41 | }, 42 | { 43 | "path": "merged-stylesheets.css", 44 | "where": "client", 45 | "type": "css", 46 | "cacheable": true, 47 | "url": "/merged-stylesheets.css?20ae2c8d51b2507244e598844414ecdec2615ce3", 48 | "sourceMap": "merged-stylesheets.css.map", 49 | "sourceMapUrl": "/20ae2c8d51b2507244e598844414ecdec2615ce3.map", 50 | "size": 30, 51 | "hash": "20ae2c8d51b2507244e598844414ecdec2615ce3" 52 | }, 53 | { 54 | "path": "app/some-data.json", 55 | "where": "client", 56 | "type": "asset", 57 | "cacheable": false, 58 | "url": "/some-data.json", 59 | "size": 15, 60 | "hash": "3edc8875bc0dd76d9f5fce5e823dca6f17a26da7" 61 | }, 62 | { 63 | "path": "app/some-file", 64 | "where": "client", 65 | "type": "asset", 66 | "cacheable": false, 67 | "url": "/some-file", 68 | "size": 20, 69 | "hash": "20242aa2ac9c728ca21c7cbbee841fd87e8277aa" 70 | }, 71 | { 72 | "path": "app/some-other-file", 73 | "where": "client", 74 | "type": "asset", 75 | "cacheable": false, 76 | "url": "/some-other-file", 77 | "size": 16, 78 | "hash": "aa4405bb6a2eb7d79af8488b44d91e5c66c123a5" 79 | }, 80 | { 81 | "path": "app/some-font.woff", 82 | "where": "client", 83 | "type": "asset", 84 | "cacheable": false, 85 | "url": "/some-font.woff", 86 | "size": 15, 87 | "hash": "6ec7e1e1c0199bfb5bcd6877de9fe7abefd26df8" 88 | }, 89 | { 90 | "path": "app/some-image.jpg", 91 | "where": "client", 92 | "type": "asset", 93 | "cacheable": false, 94 | "url": "/some-image.jpg", 95 | "size": 15, 96 | "hash": "13f1d459365d5604dbf2b64b203fa583c1c7fc3f" 97 | }, 98 | { 99 | "path": "app/some-image.png", 100 | "where": "client", 101 | "type": "asset", 102 | "cacheable": false, 103 | "url": "/some-image.png", 104 | "size": 15, 105 | "hash": "06b05b4c2720cd9ff733d21c594eac4e865a6e73" 106 | }, 107 | { 108 | "path": "app/some-javascript.js", 109 | "where": "client", 110 | "type": "asset", 111 | "cacheable": false, 112 | "url": "/some-javascript.js", 113 | "size": 19, 114 | "hash": "51a3422f25ddf466a35e00e327d5f4ca90eee8f4" 115 | }, 116 | { 117 | "path": "app/some-page.html", 118 | "where": "client", 119 | "type": "asset", 120 | "cacheable": false, 121 | "url": "/some-page.html", 122 | "size": 15, 123 | "hash": "5dc6878863a1fd4f7f69713b4c072280932255af" 124 | }, 125 | { 126 | "path": "app/some-stylesheet.css", 127 | "where": "client", 128 | "type": "asset", 129 | "cacheable": false, 130 | "url": "/some-stylesheet.css", 131 | "size": 20, 132 | "hash": "b33cc1bdaa963ae1cec9afd4c833d80caf7641a2" 133 | }, 134 | { 135 | "path": "app/some-text.txt", 136 | "where": "client", 137 | "type": "asset", 138 | "cacheable": false, 139 | "url": "/some-text.txt", 140 | "size": 14, 141 | "hash": "bb874a02400d28518a3d0f7a4c7fd8970735bea1" 142 | }, 143 | { 144 | "path": "app/some-video.mp4", 145 | "where": "client", 146 | "type": "asset", 147 | "cacheable": false, 148 | "url": "/some-video.mp4", 149 | "size": 15, 150 | "hash": "45e892d4c7ce693f5cd551fcd671cf227ff1ae3a" 151 | }, 152 | { 153 | "path": "head.html", 154 | "where": "internal", 155 | "type": "head", 156 | "hash": "2ce23f770b76d2f1cb0d71f4a43fbbb61afb25be" 157 | } 158 | ] 159 | } 160 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/merged-stylesheets.css: -------------------------------------------------------------------------------- 1 | /* CSS declarations go here */ -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/some-data.json: -------------------------------------------------------------------------------- 1 | some-data.json 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/some-file: -------------------------------------------------------------------------------- 1 | some-file (changed) 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/some-font.woff: -------------------------------------------------------------------------------- 1 | some-font.woff 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/some-image.jpg: -------------------------------------------------------------------------------- 1 | some-image.jpg 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/some-image.png: -------------------------------------------------------------------------------- 1 | some-image.png 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/some-javascript.js: -------------------------------------------------------------------------------- 1 | some-javascript.js 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/some-other-file: -------------------------------------------------------------------------------- 1 | some-other-file 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/some-page.html: -------------------------------------------------------------------------------- 1 | some-page.html 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/some-stylesheet.css: -------------------------------------------------------------------------------- 1 | some-stylesheet.css 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/some-text.txt: -------------------------------------------------------------------------------- 1 | some-text.txt 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version2_with_version_mismatch/some-video.mp4: -------------------------------------------------------------------------------- 1 | some-video.mp4 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/20ae2c8d51b2507244e598844414ecdec2615ce3.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/app/mobileapp.css"],"names":[],"mappings":"AAAA","sourcesContent":["/* CSS declarations go here */\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/app/36e96c1d40459ae12164569599c9c0a203b36db7.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/template.mobileapp.js"],"names":[],"mappings":"YAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/template.mobileapp.js","sourcesContent":["\nTemplate.body.addContent((function() {\n var view = this;\n return [ HTML.Raw(\"

Welcome to Meteor (one more time)!

\\n \"), Spacebars.include(view.lookupTemplate(\"hello\")) ];\n}));\nMeteor.startup(Template.body.renderToDocument);\n\nTemplate.__checkName(\"hello\");\nTemplate[\"hello\"] = new Template(\"Template.hello\", (function() {\n var view = this;\n return [ HTML.Raw(\"\\n \"), HTML.P(\"You've pressed the button \", Blaze.View(\"lookup:counter\", function() {\n return Spacebars.mustache(view.lookup(\"counter\"));\n }), \" times.\") ];\n}));\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/app/6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["meteor://💻app/mobileapp.js"],"names":[],"mappings":";;;;;;;;AAAA,IAAI,MAAM,CAAC,QAAQ,EAAE;;AAEnB,SAAO,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;;AAEjC,UAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;AACrB,WAAO,EAAE,YAAY;AACnB,aAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;KAC/B;GACF,CAAC,CAAC;;AAEH,UAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;AACpB,kBAAc,EAAE,YAAY;;AAE1B,aAAO,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;KACpD;GACF,CAAC,CAAC;CACJ;;AAED,IAAI,MAAM,CAAC,QAAQ,EAAE;AACnB,QAAM,CAAC,OAAO,CAAC,YAAY;;GAE1B,CAAC,CAAC;CACJ,wE","file":"/mobileapp.js","sourcesContent":["if (Meteor.isClient) {\n // counter starts at 0\n Session.setDefault('counter', 0);\n\n Template.hello.helpers({\n counter: function () {\n return Session.get('counter');\n }\n });\n\n Template.hello.events({\n 'click button': function () {\n // increment the counter when button is clicked\n Session.set('counter', Session.get('counter') + 1);\n }\n });\n}\n\nif (Meteor.isServer) {\n Meteor.startup(function () {\n // code to run on server at startup\n });\n}\n"]} -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/app/mobileapp.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | 3 | ///////////////////////////////////////////////////////////////////////// 4 | // // 5 | // mobileapp.js // 6 | // // 7 | ///////////////////////////////////////////////////////////////////////// 8 | // 9 | if (Meteor.isClient) { // 1 10 | // counter starts at 0 // 11 | Session.setDefault('counter', 0); // 3 12 | // 13 | Template.hello.helpers({ // 5 14 | counter: function () { // 6 15 | return Session.get('counter'); // 7 16 | } // 17 | }); // 18 | // 19 | Template.hello.events({ // 11 20 | 'click button': function () { // 12 21 | // increment the counter when button is clicked // 22 | Session.set('counter', Session.get('counter') + 1); // 14 23 | } // 24 | }); // 25 | } // 26 | // 27 | if (Meteor.isServer) { // 19 28 | Meteor.startup(function () { // 20 29 | // code to run on server at startup // 30 | }); // 31 | } // 32 | ///////////////////////////////////////////////////////////////////////// 33 | 34 | }).call(this); 35 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/app/template.mobileapp.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | Template.body.addContent((function() { 3 | var view = this; 4 | return [ HTML.Raw("

Welcome to Meteor (one more time)!

\n "), Spacebars.include(view.lookupTemplate("hello")) ]; 5 | })); 6 | Meteor.startup(Template.body.renderToDocument); 7 | 8 | Template.__checkName("hello"); 9 | Template["hello"] = new Template("Template.hello", (function() { 10 | var view = this; 11 | return [ HTML.Raw("\n "), HTML.P("You've pressed the button ", Blaze.View("lookup:counter", function() { 12 | return Spacebars.mustache(view.lookup("counter")); 13 | }), " times.") ]; 14 | })); 15 | 16 | }).call(this); 17 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/head.html: -------------------------------------------------------------------------------- 1 | mobileapp -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": "web-program-pre1", 3 | "version": "version3", 4 | "cordovaCompatibilityVersions": { 5 | "android": "4017747ca6b4f460f33b121e439b7a11a070205a", 6 | "ios": "0abe549f2adbcf6cd295428aefea628ebe69666a" 7 | }, 8 | "manifest": [ 9 | { 10 | "path": "packages/meteor.js", 11 | "where": "client", 12 | "type": "js", 13 | "cacheable": true, 14 | "url": "/packages/meteor.js?57d11a30155349aa5106f8150cee35eac5f4764c", 15 | "sourceMap": "packages/meteor.js.map", 16 | "sourceMapUrl": "/packages/57d11a30155349aa5106f8150cee35eac5f4764c.map", 17 | "size": 113991, 18 | "hash": "57d11a30155349aa5106f8150cee35eac5f4764c" 19 | }, 20 | { 21 | "path": "app/template.mobileapp.js", 22 | "where": "client", 23 | "type": "js", 24 | "cacheable": true, 25 | "url": "/app/template.mobileapp.js?36e96c1d40459ae12164569599c9c0a203b36db7", 26 | "sourceMap": "app/template.mobileapp.js.map", 27 | "sourceMapUrl": "/app/36e96c1d40459ae12164569599c9c0a203b36db7.map", 28 | "size": 592, 29 | "hash": "36e96c1d40459ae12164569599c9c0a203b36db7" 30 | }, 31 | { 32 | "path": "app/mobileapp.js", 33 | "where": "client", 34 | "type": "js", 35 | "cacheable": true, 36 | "url": "/app/mobileapp.js?6db9763f3e0f4e4cbf78111f73823043ab08e3e7", 37 | "sourceMap": "app/mobileapp.js.map", 38 | "sourceMapUrl": "/app/6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map", 39 | "size": 2275, 40 | "hash": "6db9763f3e0f4e4cbf78111f73823043ab08e3e7" 41 | }, 42 | { 43 | "path": "merged-stylesheets.css", 44 | "where": "client", 45 | "type": "css", 46 | "cacheable": true, 47 | "url": "/merged-stylesheets.css?20ae2c8d51b2507244e598844414ecdec2615ce3", 48 | "sourceMap": "merged-stylesheets.css.map", 49 | "sourceMapUrl": "/20ae2c8d51b2507244e598844414ecdec2615ce3.map", 50 | "size": 30, 51 | "hash": "20ae2c8d51b2507244e598844414ecdec2615ce3" 52 | }, 53 | { 54 | "path": "app/some-data.json", 55 | "where": "client", 56 | "type": "asset", 57 | "cacheable": false, 58 | "url": "/some-data.json", 59 | "size": 15, 60 | "hash": "3edc8875bc0dd76d9f5fce5e823dca6f17a26da7" 61 | }, 62 | { 63 | "path": "app/some-file", 64 | "where": "client", 65 | "type": "asset", 66 | "cacheable": false, 67 | "url": "/some-file", 68 | "size": 26, 69 | "hash": "eb436b5a9ae39f7b8faef931024a86b73729da9e" 70 | }, 71 | { 72 | "path": "app/some-other-file", 73 | "where": "client", 74 | "type": "asset", 75 | "cacheable": false, 76 | "url": "/some-other-file", 77 | "size": 16, 78 | "hash": "aa4405bb6a2eb7d79af8488b44d91e5c66c123a5" 79 | }, 80 | { 81 | "path": "app/some-font.woff", 82 | "where": "client", 83 | "type": "asset", 84 | "cacheable": false, 85 | "url": "/some-font.woff", 86 | "size": 15, 87 | "hash": "6ec7e1e1c0199bfb5bcd6877de9fe7abefd26df8" 88 | }, 89 | { 90 | "path": "app/some-image.jpg", 91 | "where": "client", 92 | "type": "asset", 93 | "cacheable": false, 94 | "url": "/some-image.jpg", 95 | "size": 15, 96 | "hash": "13f1d459365d5604dbf2b64b203fa583c1c7fc3f" 97 | }, 98 | { 99 | "path": "app/some-image.png", 100 | "where": "client", 101 | "type": "asset", 102 | "cacheable": false, 103 | "url": "/some-image.png", 104 | "size": 15, 105 | "hash": "06b05b4c2720cd9ff733d21c594eac4e865a6e73" 106 | }, 107 | { 108 | "path": "app/some-javascript.js", 109 | "where": "client", 110 | "type": "asset", 111 | "cacheable": false, 112 | "url": "/some-javascript.js", 113 | "size": 19, 114 | "hash": "51a3422f25ddf466a35e00e327d5f4ca90eee8f4" 115 | }, 116 | { 117 | "path": "app/some-page.html", 118 | "where": "client", 119 | "type": "asset", 120 | "cacheable": false, 121 | "url": "/some-page.html", 122 | "size": 15, 123 | "hash": "5dc6878863a1fd4f7f69713b4c072280932255af" 124 | }, 125 | { 126 | "path": "app/some-stylesheet.css", 127 | "where": "client", 128 | "type": "asset", 129 | "cacheable": false, 130 | "url": "/some-stylesheet.css", 131 | "size": 20, 132 | "hash": "b33cc1bdaa963ae1cec9afd4c833d80caf7641a2" 133 | }, 134 | { 135 | "path": "app/some-text.txt", 136 | "where": "client", 137 | "type": "asset", 138 | "cacheable": false, 139 | "url": "/some-text.txt", 140 | "size": 14, 141 | "hash": "bb874a02400d28518a3d0f7a4c7fd8970735bea1" 142 | }, 143 | { 144 | "path": "app/some-video.mp4", 145 | "where": "client", 146 | "type": "asset", 147 | "cacheable": false, 148 | "url": "/some-video.mp4", 149 | "size": 15, 150 | "hash": "45e892d4c7ce693f5cd551fcd671cf227ff1ae3a" 151 | }, 152 | { 153 | "path": "head.html", 154 | "where": "internal", 155 | "type": "head", 156 | "hash": "2ce23f770b76d2f1cb0d71f4a43fbbb61afb25be" 157 | } 158 | ] 159 | } 160 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/merged-stylesheets.css: -------------------------------------------------------------------------------- 1 | /* CSS declarations go here */ -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/some-data.json: -------------------------------------------------------------------------------- 1 | some-data.json 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/some-file: -------------------------------------------------------------------------------- 1 | some-file (changed again) 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/some-font.woff: -------------------------------------------------------------------------------- 1 | some-font.woff 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/some-image.jpg: -------------------------------------------------------------------------------- 1 | some-image.jpg 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/some-image.png: -------------------------------------------------------------------------------- 1 | some-image.png 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/some-javascript.js: -------------------------------------------------------------------------------- 1 | some-javascript.js 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/some-other-file: -------------------------------------------------------------------------------- 1 | some-other-file 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/some-page.html: -------------------------------------------------------------------------------- 1 | some-page.html 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/some-stylesheet.css: -------------------------------------------------------------------------------- 1 | some-stylesheet.css 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/some-text.txt: -------------------------------------------------------------------------------- 1 | some-text.txt 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/version3/some-video.mp4: -------------------------------------------------------------------------------- 1 | some-video.mp4 2 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/wrong_app_id/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": "web-program-pre1", 3 | "version": "wrong_app_id", 4 | "cordovaCompatibilityVersions": { 5 | "android": "4017747ca6b4f460f33b121e439b7a11a070205a", 6 | "ios": "0abe549f2adbcf6cd295428aefea628ebe69666a" 7 | }, 8 | "manifest": [] 9 | } 10 | -------------------------------------------------------------------------------- /tests/fixtures/downloadable_versions/wrong_root_url/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": "web-program-pre1", 3 | "version": "wrong_root_url", 4 | "cordovaCompatibilityVersions": { 5 | "android": "4017747ca6b4f460f33b121e439b7a11a070205a", 6 | "ios": "0abe549f2adbcf6cd295428aefea628ebe69666a" 7 | }, 8 | "manifest": [] 9 | } 10 | -------------------------------------------------------------------------------- /tests/fixtures/partially_downloaded_versions/version2/app/some-file: -------------------------------------------------------------------------------- 1 | some-file (changed) 2 | -------------------------------------------------------------------------------- /tests/fixtures/partially_downloaded_versions/version2/app/some-other-file: -------------------------------------------------------------------------------- 1 | some-other-file 2 | -------------------------------------------------------------------------------- /tests/fixtures/partially_downloaded_versions/version2/program.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": "web-program-pre1", 3 | "version": "version2", 4 | "cordovaCompatibilityVersions": { 5 | "android": "4017747ca6b4f460f33b121e439b7a11a070205a", 6 | "ios": "0abe549f2adbcf6cd295428aefea628ebe69666a" 7 | }, 8 | "manifest": [ 9 | { 10 | "path": "packages/meteor.js", 11 | "where": "client", 12 | "type": "js", 13 | "cacheable": true, 14 | "url": "/packages/meteor.js?57d11a30155349aa5106f8150cee35eac5f4764c", 15 | "sourceMap": "packages/meteor.js.map", 16 | "sourceMapUrl": "/packages/57d11a30155349aa5106f8150cee35eac5f4764c.map", 17 | "size": 113991, 18 | "hash": "57d11a30155349aa5106f8150cee35eac5f4764c" 19 | }, 20 | { 21 | "path": "app/template.mobileapp.js", 22 | "where": "client", 23 | "type": "js", 24 | "cacheable": true, 25 | "url": "/app/template.mobileapp.js?3f6275657e6db3a21acb37d0f6c207cf83871e90", 26 | "sourceMap": "app/template.mobileapp.js.map", 27 | "sourceMapUrl": "/app/3f6275657e6db3a21acb37d0f6c207cf83871e90.map", 28 | "size": 584, 29 | "hash": "3f6275657e6db3a21acb37d0f6c207cf83871e90" 30 | }, 31 | { 32 | "path": "app/mobileapp.js", 33 | "where": "client", 34 | "type": "js", 35 | "cacheable": true, 36 | "url": "/app/mobileapp.js?6db9763f3e0f4e4cbf78111f73823043ab08e3e7", 37 | "sourceMap": "app/mobileapp.js.map", 38 | "sourceMapUrl": "/app/6db9763f3e0f4e4cbf78111f73823043ab08e3e7.map", 39 | "size": 2275, 40 | "hash": "6db9763f3e0f4e4cbf78111f73823043ab08e3e7" 41 | }, 42 | { 43 | "path": "merged-stylesheets.css", 44 | "where": "client", 45 | "type": "css", 46 | "cacheable": true, 47 | "url": "/merged-stylesheets.css?20ae2c8d51b2507244e598844414ecdec2615ce3", 48 | "sourceMap": "merged-stylesheets.css.map", 49 | "sourceMapUrl": "/20ae2c8d51b2507244e598844414ecdec2615ce3.map", 50 | "size": 30, 51 | "hash": "20ae2c8d51b2507244e598844414ecdec2615ce3" 52 | }, 53 | { 54 | "path": "app/some-data.json", 55 | "where": "client", 56 | "type": "asset", 57 | "cacheable": false, 58 | "url": "/some-data.json", 59 | "size": 15, 60 | "hash": "3edc8875bc0dd76d9f5fce5e823dca6f17a26da7" 61 | }, 62 | { 63 | "path": "app/some-file", 64 | "where": "client", 65 | "type": "asset", 66 | "cacheable": false, 67 | "url": "/some-file", 68 | "size": 20, 69 | "hash": "20242aa2ac9c728ca21c7cbbee841fd87e8277aa" 70 | }, 71 | { 72 | "path": "app/some-other-file", 73 | "where": "client", 74 | "type": "asset", 75 | "cacheable": false, 76 | "url": "/some-other-file", 77 | "size": 16, 78 | "hash": "aa4405bb6a2eb7d79af8488b44d91e5c66c123a5" 79 | }, 80 | { 81 | "path": "app/some-font.woff", 82 | "where": "client", 83 | "type": "asset", 84 | "cacheable": false, 85 | "url": "/some-font.woff", 86 | "size": 15, 87 | "hash": "6ec7e1e1c0199bfb5bcd6877de9fe7abefd26df8" 88 | }, 89 | { 90 | "path": "app/some-image.jpg", 91 | "where": "client", 92 | "type": "asset", 93 | "cacheable": false, 94 | "url": "/some-image.jpg", 95 | "size": 15, 96 | "hash": "13f1d459365d5604dbf2b64b203fa583c1c7fc3f" 97 | }, 98 | { 99 | "path": "app/some-image.png", 100 | "where": "client", 101 | "type": "asset", 102 | "cacheable": false, 103 | "url": "/some-image.png", 104 | "size": 15, 105 | "hash": "06b05b4c2720cd9ff733d21c594eac4e865a6e73" 106 | }, 107 | { 108 | "path": "app/some-javascript.js", 109 | "where": "client", 110 | "type": "asset", 111 | "cacheable": false, 112 | "url": "/some-javascript.js", 113 | "size": 19, 114 | "hash": "51a3422f25ddf466a35e00e327d5f4ca90eee8f4" 115 | }, 116 | { 117 | "path": "app/some-page.html", 118 | "where": "client", 119 | "type": "asset", 120 | "cacheable": false, 121 | "url": "/some-page.html", 122 | "size": 15, 123 | "hash": "5dc6878863a1fd4f7f69713b4c072280932255af" 124 | }, 125 | { 126 | "path": "app/some-stylesheet.css", 127 | "where": "client", 128 | "type": "asset", 129 | "cacheable": false, 130 | "url": "/some-stylesheet.css", 131 | "size": 20, 132 | "hash": "b33cc1bdaa963ae1cec9afd4c833d80caf7641a2" 133 | }, 134 | { 135 | "path": "app/some-text.txt", 136 | "where": "client", 137 | "type": "asset", 138 | "cacheable": false, 139 | "url": "/some-text.txt", 140 | "size": 14, 141 | "hash": "bb874a02400d28518a3d0f7a4c7fd8970735bea1" 142 | }, 143 | { 144 | "path": "app/some-video.mp4", 145 | "where": "client", 146 | "type": "asset", 147 | "cacheable": false, 148 | "url": "/some-video.mp4", 149 | "size": 15, 150 | "hash": "45e892d4c7ce693f5cd551fcd671cf227ff1ae3a" 151 | }, 152 | { 153 | "path": "head.html", 154 | "where": "internal", 155 | "type": "head", 156 | "hash": "2ce23f770b76d2f1cb0d71f4a43fbbb61afb25be" 157 | } 158 | ] 159 | } 160 | -------------------------------------------------------------------------------- /tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cordova-plugin-meteor-webapptests", 3 | "version": "1.3.1", 4 | "cordova": { 5 | "id": "cordova-plugin-meteor-webapp-tests", 6 | "platforms": [ 7 | "android", 8 | "ios" 9 | ] 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "https://github.com/meteor/cordova-plugin-meteor-webapp" 14 | }, 15 | "keywords": [ 16 | "cordova", 17 | "meteor", 18 | "ecosystem:cordova", 19 | "cordova-android", 20 | "cordova-ios" 21 | ], 22 | "author": "Meteor Development Group", 23 | "license": "MIT" 24 | } 25 | -------------------------------------------------------------------------------- /tests/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | Meteor Webapp Tests 6 | Meteor Development Group 7 | MIT 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /tests/src/ios/GCDWebServer+Testing.h: -------------------------------------------------------------------------------- 1 | #import "GCDWebServer.h" 2 | 3 | @protocol GCDWebServerTestingDelegate 4 | - (void)webServer:(GCDWebServer *)server didReceiveRequest:(GCDWebServerRequest *)request; 5 | @end 6 | 7 | @interface GCDWebServer (Testing) 8 | 9 | @end 10 | -------------------------------------------------------------------------------- /tests/src/ios/GCDWebServer+Testing.m: -------------------------------------------------------------------------------- 1 | #import "GCDWebServer+Testing.h" 2 | #import "GCDWebServerPrivate.h" 3 | #import 4 | 5 | @implementation GCDWebServer (Testing) 6 | 7 | + (void)load { 8 | static dispatch_once_t onceToken; 9 | dispatch_once(&onceToken, ^{ 10 | Class class = [self class]; 11 | 12 | SEL originalSelector = @selector(addHandlerWithMatchBlock:asyncProcessBlock:); 13 | SEL swizzledSelector = @selector(testing_addHandlerWithMatchBlock:asyncProcessBlock:); 14 | 15 | Method originalMethod = class_getInstanceMethod(class, originalSelector); 16 | Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); 17 | 18 | method_exchangeImplementations(originalMethod, swizzledMethod); 19 | }); 20 | } 21 | 22 | - (void)testing_addHandlerWithMatchBlock:(GCDWebServerMatchBlock)matchBlock asyncProcessBlock:(GCDWebServerAsyncProcessBlock)processBlock { 23 | __weak __typeof__(self) weakSelf = self; 24 | [self testing_addHandlerWithMatchBlock:matchBlock asyncProcessBlock:^(GCDWebServerRequest *request, GCDWebServerCompletionBlock completionBlock) { 25 | __strong __typeof(weakSelf) strongSelf = weakSelf; 26 | id delegate = strongSelf.delegate; 27 | if ([delegate respondsToSelector:@selector(webServer:didReceiveRequest:)]) { 28 | dispatch_async(dispatch_get_main_queue(), ^{ 29 | [((id)delegate) webServer:strongSelf didReceiveRequest:request]; 30 | }); 31 | } 32 | 33 | processBlock(request, completionBlock); 34 | }]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /tests/src/ios/WebAppLocalServer+Testing.swift: -------------------------------------------------------------------------------- 1 | extension WebAppLocalServer { 2 | @objc func simulatePageReload(_ command: CDVInvokedUrlCommand) { 3 | onReset() 4 | 5 | let result = CDVPluginResult(status: CDVCommandStatus_OK) 6 | commandDelegate?.send(result, callbackId:command.callbackId) 7 | } 8 | 9 | @objc func simulateAppRestart(_ command: CDVInvokedUrlCommand) { 10 | initializeAssetBundles() 11 | onReset() 12 | 13 | let result = CDVPluginResult(status: CDVCommandStatus_OK) 14 | commandDelegate?.send(result, callbackId:command.callbackId) 15 | } 16 | 17 | @objc func resetToInitialState(_ command: CDVInvokedUrlCommand) { 18 | commandDelegate?.run() { 19 | self.configuration.reset() 20 | self.initializeAssetBundles() 21 | self.onReset() 22 | 23 | let result = CDVPluginResult(status: CDVCommandStatus_OK) 24 | self.commandDelegate?.send(result, callbackId:command.callbackId) 25 | } 26 | } 27 | 28 | @objc func getAuthTokenKeyValuePair(_ command: CDVInvokedUrlCommand) { 29 | let result = CDVPluginResult(status: CDVCommandStatus_OK, messageAs: authTokenKeyValuePair) 30 | commandDelegate?.send(result, callbackId:command.callbackId) 31 | } 32 | 33 | @objc func downloadedVersionExists(_ command: CDVInvokedUrlCommand) { 34 | guard let version = command.argument(at: 0) as? String else { 35 | let errorMessage = "'version' argument required" 36 | let result = CDVPluginResult(status: CDVCommandStatus_ERROR, messageAs: errorMessage) 37 | commandDelegate?.send(result, callbackId: command.callbackId) 38 | return 39 | } 40 | 41 | let versionExists = assetBundleManager.downloadedAssetBundleWithVersion(version) != nil 42 | 43 | let result = CDVPluginResult(status: CDVCommandStatus_OK, messageAs: versionExists) 44 | commandDelegate?.send(result, callbackId:command.callbackId) 45 | } 46 | 47 | @objc func simulatePartialDownload(_ command: CDVInvokedUrlCommand) { 48 | guard let version = command.argument(at: 0) as? String else { 49 | let errorMessage = "'version' argument required" 50 | let result = CDVPluginResult(status: CDVCommandStatus_ERROR, messageAs: errorMessage) 51 | commandDelegate?.send(result, callbackId: command.callbackId) 52 | return 53 | } 54 | 55 | commandDelegate?.run() { 56 | let wwwDirectoryURL = Bundle.main.resourceURL!.appendingPathComponent("www") 57 | let versionDirectoryURL = wwwDirectoryURL.appendingPathComponent("partially_downloaded_versions/\(version)") 58 | 59 | let versionsDirectoryURL = self.assetBundleManager.versionsDirectoryURL 60 | let downloadDirectoryURL = versionsDirectoryURL.appendingPathComponent("Downloading") 61 | 62 | let fileManager = FileManager.default 63 | 64 | if fileManager.fileExists(atPath: downloadDirectoryURL.path) { 65 | try! fileManager.removeItem(at: downloadDirectoryURL) 66 | } 67 | 68 | try! fileManager.copyItem(at: versionDirectoryURL, to: downloadDirectoryURL) 69 | 70 | let result = CDVPluginResult(status: CDVCommandStatus_OK) 71 | self.commandDelegate?.send(result, callbackId:command.callbackId) 72 | }; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/src/ios/WebAppMockRemoteServer.swift: -------------------------------------------------------------------------------- 1 | extension Data { 2 | func SHA1() -> String { 3 | var digest = [UInt8](repeating: 0, count: Int(CC_SHA1_DIGEST_LENGTH)) 4 | 5 | withUnsafeBytes { (bytes) -> Void in 6 | CC_SHA1(bytes, CC_LONG(count), &digest) 7 | } 8 | 9 | var hexString = "" 10 | for index in 0.. GCDWebServerRequest! in 75 | if requestMethod != "GET" { return nil } 76 | if !(URLPath.hasPrefix(basePath)) { return nil } 77 | 78 | let request = GCDWebServerRequest(method: requestMethod, url: requestURL, headers: requestHeaders, path: URLPath, query: URLQuery) 79 | return request 80 | }) { (request) -> GCDWebServerResponse! in 81 | let URLPath = request.path.substring(from: basePath.endIndex) 82 | let fileURL = self.versionDirectoryURL.appendingPathComponent(URLPath) 83 | 84 | var response: GCDWebServerResponse 85 | 86 | var isDirectory = ObjCBool(false) 87 | if fileManager.fileExists(atPath: fileURL.path, isDirectory: &isDirectory) 88 | && !isDirectory.boolValue { 89 | response = GCDWebServerFileResponse(file: fileURL.path)! 90 | let fileHash = (try! Data(contentsOf: fileURL)).SHA1() 91 | response.eTag = "\"\(fileHash)\"" 92 | } else if request.query!["meteor_dont_serve_index"] == nil { 93 | let indexFileURL = self.versionDirectoryURL.appendingPathComponent("index.html") 94 | response = GCDWebServerFileResponse(file: indexFileURL.path)! 95 | } else { 96 | response = GCDWebServerResponse(statusCode: GCDWebServerClientErrorHTTPStatusCode.httpStatusCode_NotFound.rawValue) 97 | } 98 | 99 | response.cacheControlMaxAge = 0 100 | response.lastModifiedDate = nil 101 | return response 102 | } 103 | } 104 | 105 | @objc func receivedRequests(_ command: CDVInvokedUrlCommand) { 106 | let receivedRequestURLs = receivedRequests!.map { 107 | ["path": $0.path, "query": $0.query, "headers": $0.headers] 108 | } 109 | 110 | let result = CDVPluginResult(status: CDVCommandStatus_OK, messageAs: receivedRequestURLs) 111 | commandDelegate?.send(result, callbackId:command.callbackId) 112 | } 113 | 114 | // MARK: GCDWebServerTestingDelegate 115 | 116 | func webServerDidStart(_ server: GCDWebServer!) { 117 | receivedRequests = [GCDWebServerRequest]() 118 | } 119 | 120 | func webServer(_ server: GCDWebServer!, didReceive request: GCDWebServerRequest!) { 121 | receivedRequests?.append(request) 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /tests/src/ios/cordova-plugin-meteor-webapp-tests-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "GCDWebServer+Testing.h" 4 | -------------------------------------------------------------------------------- /tests/www/webapp_local_server_testing.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | resetToInitialState: function(callback) { 3 | cordova.exec( 4 | callback, 5 | console.error, 6 | "WebAppLocalServer", 7 | "resetToInitialState", 8 | []); 9 | }, 10 | 11 | simulatePageReload: function(callback) { 12 | cordova.exec( 13 | callback, 14 | console.error, 15 | "WebAppLocalServer", 16 | "simulatePageReload", 17 | []); 18 | }, 19 | 20 | simulateAppRestart: function(callback) { 21 | cordova.exec( 22 | callback, 23 | console.error, 24 | "WebAppLocalServer", 25 | "simulateAppRestart", 26 | []); 27 | }, 28 | 29 | getAuthTokenKeyValuePair: function(callback) { 30 | cordova.exec( 31 | callback, 32 | console.error, 33 | "WebAppLocalServer", 34 | "getAuthTokenKeyValuePair", 35 | []); 36 | }, 37 | 38 | downloadedVersionExists: function(version, callback) { 39 | cordova.exec( 40 | callback, 41 | console.error, 42 | "WebAppLocalServer", 43 | "downloadedVersionExists", 44 | [version]); 45 | }, 46 | 47 | simulatePartialDownload: function(version, callback) { 48 | cordova.exec( 49 | callback, 50 | console.error, 51 | "WebAppLocalServer", 52 | "simulatePartialDownload", 53 | [version]); 54 | } 55 | }; 56 | -------------------------------------------------------------------------------- /tests/www/webapp_mock_remote_server.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | serveVersion: function(version, callback) { 3 | cordova.exec( 4 | callback, 5 | console.error, 6 | "WebAppMockRemoteServer", 7 | "serveVersion", 8 | [version]); 9 | }, 10 | 11 | receivedRequests: function(callback) { 12 | cordova.exec( 13 | callback, 14 | console.error, 15 | "WebAppMockRemoteServer", 16 | "receivedRequests", 17 | []); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /www/webapp_local_server.js: -------------------------------------------------------------------------------- 1 | var fileUrlRegEx = /^file:\/\/(.*)/; 2 | 3 | module.exports = { 4 | startupDidComplete: function(callback) { 5 | cordova.exec( 6 | callback, 7 | console.error, 8 | "WebAppLocalServer", 9 | "startupDidComplete", 10 | []); 11 | }, 12 | 13 | checkForUpdates: function(callback) { 14 | cordova.exec( 15 | callback, 16 | console.error, 17 | "WebAppLocalServer", 18 | "checkForUpdates", 19 | []); 20 | }, 21 | 22 | onNewVersionReady: function(callback) { 23 | cordova.exec( 24 | callback, 25 | console.error, 26 | "WebAppLocalServer", 27 | "onNewVersionReady", 28 | []); 29 | }, 30 | 31 | switchToPendingVersion: function(callback, errorCallback) { 32 | cordova.exec( 33 | callback, 34 | function(error) { 35 | console.error(error); 36 | if (typeof errorCallback === "function") { 37 | errorCallback(error); 38 | } 39 | }, 40 | "WebAppLocalServer", 41 | "switchPendingVersion", 42 | [] 43 | ); 44 | }, 45 | 46 | onError: function(callback) { 47 | cordova.exec( 48 | function(errorMessage) { 49 | // Convert error message to a proper error object 50 | var error = new Error(errorMessage); 51 | callback(error); 52 | }, 53 | console.error, 54 | "WebAppLocalServer", 55 | "onError", 56 | []); 57 | }, 58 | 59 | localFileSystemUrl: function(fileUrl) { 60 | var match = fileUrlRegEx.exec(fileUrl); 61 | if (!match) return fileUrl; 62 | 63 | var path = match[1]; 64 | return "/local-filesystem" + path; 65 | } 66 | }; 67 | --------------------------------------------------------------------------------