Ionic 2, Chrome keeps loading from disk cache - google-chrome

I'm developing a mobile app and use the following command to build and run a version that works in a browser (pulling in all necessary hooks, which ionic serve does not)
ionic build browser --desktop --testing && http-server platforms/browser/www/
Yesterday, and for weeks, this works perfectly. I stop and start that same command, it builds/compiles everything, everything is great.
Then I updated Google Chrome. Now, no matter what I do, Chrome keeps pulling all of these resources from disk cache, even after I delete and re-create them. Any ideas how I can solve? I don't want to have to clear my cache out every time I reload, and it seems this'll cause additional issues down the road.
I don't know why or how this changed, no project or config settings are different in my Ionic2 app.
Using cmd-shift-R instead of just cmd-R to reload seems to force Chrome to not load from disk cache but I'm confused and want to understand what happened here...

Chrome caches a lot but you can force it to load resources from your server instead of taking them out of cache by using cache busting:
Load templates:
var timestamp = (new Date()).getTime();
$ionicPopup.show({
scope: $scope,
title: 'Work!',
templateUrl: '/templates/roll-timer-popup.html?update='+timestamp
});
Load scripts/stylesheets:
<script src="myscripts.js?update=123..."></script>
<link rel="stylesheet" type="text/css" href="theme.css?update=123...">
For script/stylesheets you might create them along with the timestamps dynamically and insert them afterwards.
Or when your scripts-files get bundelt together, you could use a script to write timestamps into your finally index.html file for deployment by using a nodejs-script, for I made this script for one of my projects:
const fs = require('fs');
var filename = process.argv[2];
var regExp = /[.|\w]+[.]js/;
var contentToAttach = ['<!-- CONTENT ATTACHED BY '+process.argv[1]+' -->','you can put other content in here that is put into at the end of your file'];
fs.readFile(filename, {
flag : 'r',
encoding: 'utf-8'
},(err, oldFileContent) => {
if (err) throw err;
var fileContent = oldFileContent;
var oldScriptName;
var now = (new Date()).getTime();
var lastIndexPos = 0;
while (oldScriptName = oldFileContent.match(regExp)) {
var newScriptName = oldScriptName + '?update=' + now;
var newIndexPos = fileContent.indexOf(oldScriptName);
if (newIndexPos !== lastIndexPos) {
fileContent = fileContent.replace(oldScriptName, newScriptName);
lastIndexPos = newIndexPos;
}
else {
// same filename found
var fildContentSlice = fileContent.slice(newIndexPos+oldScriptName.length);
fildContentSlice = fildContentSlice.replace(oldScriptName, newScriptName);
fileContent = fileContent.slice(0,newIndexPos+oldScriptName.length)+fildContentSlice;
}
oldFileContent = oldFileContent.replace(oldScriptName, '');
}
var wholeContent = fileContent+'\n\r'+contentToAttach.join('\n\r');
fs.writeFile(filename,wholeContent, (err) => {
if (err) throw err;
console.log('File: '+filename+' is updated!');
});
});
It inserts ?update=123... in every script-tag it can find in a file.
To execute this script in the shell write:
node updateScript.js path_to_your_html_file
Hope it helps.

Related

How to deal with Access-Control-Allow-Origin for myDrive files

The question arises from this note:
Someone here suggested using div's. The HTML requirement is very
skeletal. The 3D display is basically canvas, but it requires seven
three.js files, ten js files of my own making to exchange parameters
and other variables with the global variable and .dae collada files
for each of the 3D models you can see. If they could be linked in like
jQuery that might be the solution but I wonder about conflicts.
on Questions on extending GAS spreadsheet usefulness
principally, if they can be linked like jQuery part
The files to be linked are on myDrive. The thinking is that if I can copy the files into GAS editor, it seems as secure and more flexible to bring them into the html directly.
code.gs
function sendUrls(){
var folder = DriveApp.getFoldersByName("___Blazer").next();
var sub = folder.getFoldersByName("assembler").next();
var contents = sub.getFiles();
var file;
var data = []
while(contents.hasNext()) {
file = contents.next();
type = file.getName().split(".")[1];
url = file.getUrl();
data.push([type,url]);
}
return data;
}
html
google.script.run.withSuccessHandler(function (files) {
$.each(files,function(i,v){
if(v[0] === "js"){
$.get(v[1])
}
})
})
.sendUrls();
The first url opens the proper script file but the origin file is not recognisable to me.
I am not sure that this is a proper answer as it relies on cors-anywhere, viz:
function importFile(name){
var myUrl = 'http://glasier.hk/cors/tba.html';
var proxy = 'https://cors-anywhere.herokuapp.com/';
var finalURL = proxy + myUrl;
$.get(finalURL,function(data) {
$("body").append(data);
importNset();
})
}
function importNset(){
google.script.run
.withSuccessHandler(function (code) {
path = "https://api.myjson.com/bins/"+code;
$.get(path)
.done((data, textStatus, jqXHR) => {
nset = data;
cfig = nset.cfig;
start();
})
})
.sendCode();
}
var nset,cfig;
$(document).ready(function(){
importFile();
});
but it works, albeit on my machine, using my own website as the resource.
I used the Gas function in gas Shop to make the eight previously tested js files into the single tba.html script only file. I swapped the workshop specific script files for those needed for google.script.run but otherwise that was it. If I could find out how to cors-enable my site, I think I might be able to demonstrate how scripts might be imported to generate different views from the same TBA and spreadsheet interfaces.

Offline Ready using Service worker

I built an offline first app using the appcache a while ago and wanted to convert it to using the service-worker (my clients all use the latest chrome so I don't have any browser compatibility issues).
I'm using sw-precache to generate a service-worker that caches my local assets (specifically, my html/css/fonts and also some js) and it looks like when the service-worker installs, it does successfully add all the assets to cache storage and it does successfully start (install and activate both fire and complete successfully. And I have the self.skipWaiting() at the end of the install event to start the service-worker (which it does successfully as well)).
The issue is that the "fetch" event doesn't seem to ever fire. As such, if I go offline or open a browser (while already offline) and navigate to the site, I get the Chrome offline dinosaur. When I look at the network tab, it looks like the browser is trying to hit a server to retrieve the pages. I'm not sure what I'm doing wrong and I didn't touch the fetch method that was generated by the sw-precache utility...so I'm not sure what I'm missing. Any help would be greatly appreciated. My fetch event is below:
self.addEventListener('fetch', function(event) {
if (event.request.method === 'GET') {
var urlWithoutIgnoredParameters = stripIgnoredUrlParameters(event.request.url,
IgnoreUrlParametersMatching);
var cacheName = AbsoluteUrlToCacheName[urlWithoutIgnoredParameters];
var directoryIndex = 'index.html';
if (!cacheName && directoryIndex) {
urlWithoutIgnoredParameters = addDirectoryIndex(urlWithoutIgnoredParameters, directoryIndex);
cacheName = AbsoluteUrlToCacheName[urlWithoutIgnoredParameters];
}
var navigateFallback = '';
// Ideally, this would check for event.request.mode === 'navigate', but that is not widely
// supported yet:
// https://code.google.com/p/chromium/issues/detail?id=540967
// https://bugzilla.mozilla.org/show_bug.cgi?id=1209081
if (!cacheName && navigateFallback && event.request.headers.has('accept') &&
event.request.headers.get('accept').includes('text/html') &&
/* eslint-disable quotes, comma-spacing */
isPathWhitelisted([], event.request.url)) {
/* eslint-enable quotes, comma-spacing */
var navigateFallbackUrl = new URL(navigateFallback, self.location);
cacheName = AbsoluteUrlToCacheName[navigateFallbackUrl.toString()];
}
if (cacheName) {
event.respondWith(
// Rely on the fact that each cache we manage should only have one entry, and return that.
caches.open(cacheName).then(function(cache) {
return cache.keys().then(function(keys) {
return cache.match(keys[0]).then(function(response) {
if (response) {
return response;
}
// If for some reason the response was deleted from the cache,
// raise and exception and fall back to the fetch() triggered in the catch().
throw Error('The cache ' + cacheName + ' is empty.');
});
});
}).catch(function(e) {
console.warn('Couldn\'t serve response for "%s" from cache: %O', event.request.url, e);
return fetch(event.request);
})
);
}
}
});

Google Chrome Indexdb - redundant code

I am tryint yo understand some code from an opensource project that handles indexDB commands within a Google Chrome application.
The code is as follows :
var db = pm.indexedDB.db;
var trans = db.transaction([pm.indexedDB.TABLE_DRIVE_CHANGES], "readwrite");
var store = trans.objectStore(pm.indexedDB.TABLE_DRIVE_CHANGES);
var boundKeyRange = IDBKeyRange.only(driveChange.id);
var request = store.put(driveChange);
request.onsuccess = function (e) {
callback(driveChange);
};
request.onerror = function (e) {
console.log(e.value);
};
Although the app works, to me it seems that the following line is redundant code
var boundKeyRange = IDBKeyRange.only(driveChange.id);
Or am I missing something? The variable 'boundKeyRange' is never referenced anywhere.
Unless boundKeyRange is used later, you're not missing something. IDBKeyRange.only just creates an IDBKeyRange object, and if that object isn't used in some IndexedDB request, it does absolutely nothing.

IndexedDB can make database unreachable (getting blocked), how unblock?

UPDATE
I discovered the issue is that it's blocked. Despite the database always being created and upgraded by the same extension, it does not get closed. So now I'm getting the "onblocked" function called.
How do I "unblock" currently blocked databases? And how do I prevent this in the future? This is an app, so no tabs are using it. And since I can't open those databases to even delete them (this also gets blocked), how do I close them?
(For anyone wondering, to avoid this issue from the start, you HAVE to do the folllowing:)
mydb.onversionchange = function(event) {
mydb.close();
};
Original Post
IndexedDB dies and becomes unopenable if I (accidentally) try to open and upgrade with the wrong version. As far as I can tell, there's no way to ask indexedDB for the latest version of a DB. So if I try to run the following code twice, it destroys the database and it becomes unopenable:
And it never throws an error or calls onerror. It just sits silently
var db = null;
//Note, no version passed in, so the second time I do this, it seems to cause an error
var req = indexedDB.open( "test" );
req.onsuccess = function(event) { console.log( "suc: " + event.target.result.version ); db = event.target.result; };
req.onerror = function(event) { console.log( "err: " + event ); };
req.onupgradeneeded = function(event) { console.log( "upg: " + event.target.result.version ); };
//We're doing in interval since waiting for callback
var intv = setInterval(
function()
{
if ( db === null ) return;
clearInterval( intv );
var req2 = indexedDB.open( "test", db.version + 1 );
req2.onsuccess = function(event) { console.log( "suc: " + event.target.result.version ); };
req2.onerror = function(event) { console.log( "err: " + event ); };
req2.onupgradeneeded = function(event) { console.log( "upg: " + event.target.result.version ); };
},
50
);
All of that code is in my chrome.runtime.onInstalled.addListener. So when I update my app, it calls it again. If I call indexedDB.open( "test" ) without passing in the new version and then again run the setInterval function, it causes everything to become unusable and I'm never able to open "test" again. This would be solved if I could query indexedDB for the version of a database prior to attempting to open it. Does that exist?
Maybe this helps?
function getVersion(callback) {
var r = indexedDB.open('asdf');
r.onblocked = r.onerror = console.error;
r.onsuccess = function(event) {
event.target.result.close();
callback(event.target.result.version);
};
}
getVersion(function(version) {
console.log('The version is: %s', version);
});
Ok, based on the convo, this little util function might set you on the path:
var DATABASE_NAME_CONSTANT = 'whatever';
// Basic indexedDB connection helper
// #param callback the action to perform with the open connection
// #param version the version of the database to open or upgrade to
// #param upgradeNeeded the callback if the db should be upgraded
function connect(callback, version, upgradeNeeded) {
var r = indexedDB.open(DATABASE_NAME_CONSTANT, version);
if(upgradeNeeded) r.onupgradeneeded = updateNeeded;
r.onblocked = r.onerror = console.error;
r.onsuccess = function(event) {
console.log('Connected to %s version %s',
DATABASE_NAME_CONSTANT, version);
callback(event.target.result);
};
}
// Now let us say you needed to connect
// and need to have the version be upgraded
// and need to send in custom upgrades based on some ajax call
function fetch() {
var xhr = new XMLHttpRequest();
// ... setup the request and what not
xhr.onload = function(event) {
// if response is 200 etc
// store the json in some variable
var responseJSON = ...;
console.log('Fetched the json file successfully');
// Let's suppose you send in version and updgradeNeeded
// as properties of your fetched JSON object
var targetVersion = responseJSON.idb.targetVersion;
var upgradeNeeded = responseJSON.idb.upgradeNeeded;
// Now connect and do whatever
connect(function(db) {
// Do stuff with the locally scoped db variable
// For example, grab a prop from the fetched object
db.objectStore('asdf').put(responseJSON.recordToInsert);
// If you feel the need, but should not, close the db
db.close();
console.log('Finished doing idb stuff');
}, targetVersion, upgradeNeeded);
}
}
I think it is best to provide the version number always. If you don't how are you going to manage upgrades on the db structure? If you don't its a good chance you will get in a situation where same db versions on a client will have an other database structure, and I don't think that is the thing you want. So I would suggest to keep the version number in a variable.
Also when working with indexeddb you will have to provide an upgrade plan from al previous versions to the current. Meaning version 4 has a certain structure, but you will have to be able to get that same structure from scratch as from version 1,2 and 3

chrome indexed database setVersion request filled with exceptions

I am trying to get the following code to work on chrom by using setVersion (as onupgradeneeded is not available yet).
The IDBVersionChangeRequest is filled with IDBDatabaseException. And the onsuccess function could not be called. I need to create an ObjectStore within the onsuccess function.
specifically this line: request = browserDatabase._db.setVersion(browserDatabase._dbVersion.toString());
Below is my code. Any help would be greatly appreciated...
browserDatabase._db = null;
browserDatabase._dbVersion = 4;
browserDatabase._dbName = "mediaStorageDB";
browserDatabase._storeName = "myStore";
var request = indexedDB.open(browserDatabase._dbName);
// database exist
request.onsuccess = function(e)
{
browserDatabase._db = e.target.result;
// this is specifically for chrome, because it does not support onupgradeneeded
if (browserDatabase._dbVersion != browserDatabase._db.version)
{
request = browserDatabase._db.setVersion(browserDatabase._dbVersion.toString());
request.onerror = function(e) { alert("error") };
request.onblocked = function(e)
{
b = 11; // for some reason the code goes here...
}
request.onsuccess = function(e)
{
browserDatabase._db.createObjectStore(browserDatabase._storeName, {autoIncrement: true});
}
}
}
In your code sample you say you come in to the onblocked callback. The only way you can get in this callback is when you have still open transactions/connections to your db. (aside the one you are working in.) This means you will have to close all other transactions/connections before you can call the setVersion.
When wired things happen to IndexedDB, I "Clear data from hosted apps", quit Chrome windows and take a cup of coffee. After that everything work fine. :-D
browserDatabase._dbVersion < browserDatabase._db.version. Downgrading is not possible. dbVersion = 4 should not be consider lightly. You might open other tab with dbVersion = 5, or browser may be waining your response elsewhere or itself updating. All these are not worth to trace the reasons behind.