{"version":3,"sources":["meteor://💻app/packages/autoupdate/autoupdate_server.js","meteor://💻app/packages/autoupdate/client_versions.js"],"names":["_objectSpread","module1","link","default","v","export","Autoupdate","ClientVersions","onMessage","Future","Npm","require","__meteor_runtime_config__","autoupdate","versions","clientVersions","autoupdateVersion","autoupdateVersionRefreshable","autoupdateVersionCordova","appId","process","env","APP_ID","syncQueue","Meteor","_SynchronousQueue","updateVersions","shouldReloadClientProgram","WebAppInternals","reloadClientPrograms","AUTOUPDATE_VERSION","clientArchs","Object","keys","WebApp","clientPrograms","forEach","arch","version","calculateClientHash","versionRefreshable","calculateClientHashRefreshable","versionNonRefreshable","calculateClientHashNonRefreshable","versionReplaceable","calculateClientHashReplaceable","versionHmr","hmrVersion","generateBoilerplate","onListening","payload","assets","getRefreshableAssets","set","publish","check","Match","OneOf","String","undefined","stop","watch","isNew","added","changed","call","_id","onStop","ready","is_auto","startup","fut","queueTask","wait","return","enqueueVersionsRefresh","on","bindEnvironment","module","Tracker","constructor","_versions","Map","_watchCallbacks","Set","createStore","update","id","msg","fields","hasVersions","size","get","assign","fn","filter","skipInitial","resolved","Promise","resolve","then","callback","add","delete","newClientAvailable","currentVersion","isNewVersion","some","field","dependency","Dependency","depend"],"mappings":"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","file":"/packages/autoupdate.js","sourcesContent":["// Publish the current client versions for each client architecture\n// (web.browser, web.browser.legacy, web.cordova). When a client observes\n// a change in the versions associated with its client architecture,\n// it will refresh itself, either by swapping out CSS assets or by\n// reloading the page. Changes to the replaceable version are ignored\n// and handled by the hot-module-replacement package.\n//\n// There are four versions for any given client architecture: `version`,\n// `versionRefreshable`, `versionNonRefreshable`, and\n// `versionReplaceable`. The refreshable version is a hash of just the\n// client resources that are refreshable, such as CSS. The replaceable\n// version is a hash of files that can be updated with HMR. The\n// non-refreshable version is a hash of the rest of the client assets,\n// excluding the refreshable ones: HTML, JS that is not replaceable, and\n// static files in the `public` directory. The `version` version is a\n// combined hash of everything.\n//\n// If the environment variable `AUTOUPDATE_VERSION` is set, it will be\n// used in place of all client versions. You can use this variable to\n// control when the client reloads. For example, if you want to force a\n// reload only after major changes, use a custom AUTOUPDATE_VERSION and\n// change it only when something worth pushing to clients happens.\n//\n// The server publishes a `meteor_autoupdate_clientVersions` collection.\n// The ID of each document is the client architecture, and the fields of\n// the document are the versions described above.\n\nimport { ClientVersions } from \"./client_versions.js\";\nvar Future = Npm.require(\"fibers/future\");\n\nexport const Autoupdate = __meteor_runtime_config__.autoupdate = {\n // Map from client architectures (web.browser, web.browser.legacy,\n // web.cordova) to version fields { version, versionRefreshable,\n // versionNonRefreshable, refreshable } that will be stored in\n // ClientVersions documents (whose IDs are client architectures). This\n // data gets serialized into the boilerplate because it's stored in\n // __meteor_runtime_config__.autoupdate.versions.\n versions: {}\n};\n\n// Stores acceptable client versions.\nconst clientVersions = new ClientVersions();\n\n// The client hash includes __meteor_runtime_config__, so wait until\n// all packages have loaded and have had a chance to populate the\n// runtime config before using the client hash as our default auto\n// update version id.\n\n// Note: Tests allow people to override Autoupdate.autoupdateVersion before\n// startup.\nAutoupdate.autoupdateVersion = null;\nAutoupdate.autoupdateVersionRefreshable = null;\nAutoupdate.autoupdateVersionCordova = null;\nAutoupdate.appId = __meteor_runtime_config__.appId = process.env.APP_ID;\n\nvar syncQueue = new Meteor._SynchronousQueue();\n\nfunction updateVersions(shouldReloadClientProgram) {\n // Step 1: load the current client program on the server\n if (shouldReloadClientProgram) {\n WebAppInternals.reloadClientPrograms();\n }\n\n const {\n // If the AUTOUPDATE_VERSION environment variable is defined, it takes\n // precedence, but Autoupdate.autoupdateVersion is still supported as\n // a fallback. In most cases neither of these values will be defined.\n AUTOUPDATE_VERSION = Autoupdate.autoupdateVersion\n } = process.env;\n\n // Step 2: update __meteor_runtime_config__.autoupdate.versions.\n const clientArchs = Object.keys(WebApp.clientPrograms);\n clientArchs.forEach(arch => {\n Autoupdate.versions[arch] = {\n version: AUTOUPDATE_VERSION ||\n WebApp.calculateClientHash(arch),\n versionRefreshable: AUTOUPDATE_VERSION ||\n WebApp.calculateClientHashRefreshable(arch),\n versionNonRefreshable: AUTOUPDATE_VERSION ||\n WebApp.calculateClientHashNonRefreshable(arch),\n versionReplaceable: AUTOUPDATE_VERSION ||\n WebApp.calculateClientHashReplaceable(arch),\n versionHmr: WebApp.clientPrograms[arch].hmrVersion\n };\n });\n\n // Step 3: form the new client boilerplate which contains the updated\n // assets and __meteor_runtime_config__.\n if (shouldReloadClientProgram) {\n WebAppInternals.generateBoilerplate();\n }\n\n // Step 4: update the ClientVersions collection.\n // We use `onListening` here because we need to use\n // `WebApp.getRefreshableAssets`, which is only set after\n // `WebApp.generateBoilerplate` is called by `main` in webapp.\n WebApp.onListening(() => {\n clientArchs.forEach(arch => {\n const payload = {\n ...Autoupdate.versions[arch],\n assets: WebApp.getRefreshableAssets(arch),\n };\n\n clientVersions.set(arch, payload);\n });\n });\n}\n\nMeteor.publish(\n \"meteor_autoupdate_clientVersions\",\n function (appId) {\n // `null` happens when a client doesn't have an appId and passes\n // `undefined` to `Meteor.subscribe`. `undefined` is translated to\n // `null` as JSON doesn't have `undefined.\n check(appId, Match.OneOf(String, undefined, null));\n\n // Don't notify clients using wrong appId such as mobile apps built with a\n // different server but pointing at the same local url\n if (Autoupdate.appId && appId && Autoupdate.appId !== appId)\n return [];\n\n const stop = clientVersions.watch((version, isNew) => {\n (isNew ? this.added : this.changed)\n .call(this, \"meteor_autoupdate_clientVersions\", version._id, version);\n });\n\n this.onStop(() => stop());\n this.ready();\n },\n {is_auto: true}\n);\n\nMeteor.startup(function () {\n updateVersions(false);\n\n // Force any connected clients that are still looking for these older\n // document IDs to reload.\n [\"version\",\n \"version-refreshable\",\n \"version-cordova\",\n ].forEach(_id => {\n clientVersions.set(_id, {\n version: \"outdated\"\n });\n });\n});\n\nvar fut = new Future();\n\n// We only want 'refresh' to trigger 'updateVersions' AFTER onListen,\n// so we add a queued task that waits for onListen before 'refresh' can queue\n// tasks. Note that the `onListening` callbacks do not fire until after\n// Meteor.startup, so there is no concern that the 'updateVersions' calls from\n// 'refresh' will overlap with the `updateVersions` call from Meteor.startup.\n\nsyncQueue.queueTask(function () {\n fut.wait();\n});\n\nWebApp.onListening(function () {\n fut.return();\n});\n\nfunction enqueueVersionsRefresh() {\n syncQueue.queueTask(function () {\n updateVersions(true);\n });\n}\n\n// Listen for messages pertaining to the client-refresh topic.\nimport { onMessage } from \"meteor/inter-process-messaging\";\nonMessage(\"client-refresh\", enqueueVersionsRefresh);\n\n// Another way to tell the process to refresh: send SIGHUP signal\nprocess.on('SIGHUP', Meteor.bindEnvironment(function () {\n enqueueVersionsRefresh();\n}, \"handling SIGHUP signal for refresh\"));\n","import { Tracker } from \"meteor/tracker\";\n\nexport class ClientVersions {\n constructor() {\n this._versions = new Map();\n this._watchCallbacks = new Set();\n }\n\n // Creates a Livedata store for use with `Meteor.connection.registerStore`.\n // After the store is registered, document updates reported by Livedata are\n // merged with the documents in this `ClientVersions` instance.\n createStore() {\n return {\n update: ({ id, msg, fields }) => {\n if (msg === \"added\" || msg === \"changed\") {\n this.set(id, fields);\n }\n }\n };\n }\n\n hasVersions() {\n return this._versions.size > 0;\n }\n\n get(id) {\n return this._versions.get(id);\n }\n\n // Adds or updates a version document and invokes registered callbacks for the\n // added/updated document. If a document with the given ID already exists, its\n // fields are merged with `fields`.\n set(id, fields) {\n let version = this._versions.get(id);\n let isNew = false;\n\n if (version) {\n Object.assign(version, fields);\n } else {\n version = {\n _id: id,\n ...fields\n };\n\n isNew = true;\n this._versions.set(id, version);\n }\n\n this._watchCallbacks.forEach(({ fn, filter }) => {\n if (! filter || filter === version._id) {\n fn(version, isNew);\n }\n });\n }\n\n // Registers a callback that will be invoked when a version document is added\n // or changed. Calling the function returned by `watch` removes the callback.\n // If `skipInitial` is true, the callback isn't be invoked for existing\n // documents. If `filter` is set, the callback is only invoked for documents\n // with ID `filter`.\n watch(fn, { skipInitial, filter } = {}) {\n if (! skipInitial) {\n const resolved = Promise.resolve();\n\n this._versions.forEach((version) => {\n if (! filter || filter === version._id) {\n resolved.then(() => fn(version, true));\n }\n });\n }\n\n const callback = { fn, filter };\n this._watchCallbacks.add(callback);\n\n return () => this._watchCallbacks.delete(callback);\n }\n\n // A reactive data source for `Autoupdate.newClientAvailable`.\n newClientAvailable(id, fields, currentVersion) {\n function isNewVersion(version) {\n return (\n version._id === id &&\n fields.some((field) => version[field] !== currentVersion[field])\n );\n }\n\n const dependency = new Tracker.Dependency();\n const version = this.get(id);\n\n dependency.depend();\n\n const stop = this.watch(\n (version) => {\n if (isNewVersion(version)) {\n dependency.changed();\n stop();\n }\n },\n { skipInitial: true }\n );\n\n return !! version && isNewVersion(version);\n }\n}\n"]}