snapshot of https://github.com/RocketChat/Rocket.Chat.git (develop, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
Some checks failed
Create issues from TODOs / todos (push) Failing after 3s
Close inactive issues / close-issues (push) Has been cancelled
Deploy GitHub Pages / deploy-preview (push) Has been cancelled
CI / ⚙️ Variables Setup (push) Has been cancelled
CI / ⚙️ Test Guard (push) Has been cancelled
CI / 🚀 Notify external services - draft (push) Has been cancelled
CI / 📦 Build Packages (push) Has been cancelled
CI / 📦 Meteor Build (coverage) (push) Has been cancelled
CI / 📦 Meteor Build (production) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [account-service presence-service omnichannel-transcript-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [authorization-service queue-worker-service ddp-streamer-service], fips) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Build Docker (amd64, [rocketchat], fips) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [account-service presence-service omnichannel-transcript-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [authorization-service queue-worker-service ddp-streamer-service], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], ${{ (github.event_name != 'release' && github.ref != 'refs/heads/develop') && 'coverage' || 'production' }}) (push) Has been cancelled
CI / 🚢 Build Docker (arm64, [rocketchat], coverage) (push) Has been cancelled
CI / 🚢 Publish Docker Images (ghcr.io) (push) Has been cancelled
CI / 📦 Track Image Sizes (push) Has been cancelled
CI / 🔎 Code Check - Action Lint (push) Has been cancelled
CI / 🔎 Code Check (push) Has been cancelled
CI / 🔨 Test Storybook (push) Has been cancelled
CI / 🔨 Test Unit (push) Has been cancelled
CI / 🔨 Test API (CE) (push) Has been cancelled
CI / 🔨 Test API Livechat (CE) (push) Has been cancelled
CI / 🔨 Test UI (CE) (push) Has been cancelled
CI / 🔨 Test API (EE) (push) Has been cancelled
CI / 🔨 Test API Livechat (EE) (push) Has been cancelled
CI / 🔨 Test API Apps (deno-runtime - EE) (push) Has been cancelled
CI / 🔨 Test UI (EE) (push) Has been cancelled
CI / 🔨 Test API (FIPS) (push) Has been cancelled
CI / 🔨 Test API Livechat (FIPS) (push) Has been cancelled
CI / 🔨 Test UI (FIPS) (push) Has been cancelled
CI / 🔨 Test Federation Matrix (push) Has been cancelled
CI / 📊 Report Coverage (push) Has been cancelled
CI / ✅ Tests Done (push) Has been cancelled
CI / 🚀 Publish build assets (push) Has been cancelled
CI / 🚀 Publish Docker Images (DockerHub) (push) Has been cancelled
CI / 🚀 Notify external services (push) Has been cancelled
CI / Update Version Durability (push) Has been cancelled
Release / Release (push) Has been cancelled
Auto-close duplicate issues / auto-close-duplicates (push) Failing after 1s
Code scanning - action / CodeQL-Build (push) Failing after 1s
This commit is contained in:
197
apps/meteor/packages/autoupdate/autoupdate_client.js
Normal file
197
apps/meteor/packages/autoupdate/autoupdate_client.js
Normal file
@@ -0,0 +1,197 @@
|
||||
// Subscribe to the `meteor_autoupdate_clientVersions` collection,
|
||||
// which contains the set of acceptable client versions.
|
||||
//
|
||||
// A "hard code push" occurs when the running client version is not in
|
||||
// the set of acceptable client versions (or the server updates the
|
||||
// collection, there is a published client version marked `current` and
|
||||
// the running client version is no longer in the set).
|
||||
//
|
||||
// When the `reload` package is loaded, a hard code push causes
|
||||
// the browser to reload, so that it will load the latest client
|
||||
// version from the server.
|
||||
//
|
||||
// A "soft code push" represents the situation when the running client
|
||||
// version is in the set of acceptable versions, but there is a newer
|
||||
// version available on the server.
|
||||
//
|
||||
// `Autoupdate.newClientAvailable` is a reactive data source which
|
||||
// becomes `true` if a new version of the client is available on
|
||||
// the server.
|
||||
//
|
||||
// This package doesn't implement a soft code reload process itself,
|
||||
// but `newClientAvailable` could be used for example to display a
|
||||
// "click to reload" link to the user.
|
||||
|
||||
// The client version of the client code currently running in the
|
||||
// browser.
|
||||
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { ClientVersions } from './client_versions.js';
|
||||
|
||||
const clientArch = Meteor.isCordova ? 'web.cordova' : Meteor.isModern ? 'web.browser' : 'web.browser.legacy';
|
||||
|
||||
const autoupdateVersions = ((__meteor_runtime_config__.autoupdate || {}).versions || {})[clientArch] || {
|
||||
version: 'unknown',
|
||||
versionRefreshable: 'unknown',
|
||||
versionNonRefreshable: 'unknown',
|
||||
assets: [],
|
||||
};
|
||||
|
||||
export const Autoupdate = {};
|
||||
|
||||
// Stores acceptable client versions.
|
||||
const clientVersions = (Autoupdate._clientVersions = // Used by a self-test and hot-module-replacement
|
||||
new ClientVersions());
|
||||
|
||||
Meteor.connection.registerStoreClient('meteor_autoupdate_clientVersions', clientVersions.createStore());
|
||||
|
||||
Autoupdate.newClientAvailable = function () {
|
||||
return clientVersions.newClientAvailable(clientArch, ['versionRefreshable', 'versionNonRefreshable'], autoupdateVersions);
|
||||
};
|
||||
|
||||
// Set to true if the link.onload callback ever fires for any <link> node.
|
||||
let knownToSupportCssOnLoad = false;
|
||||
|
||||
const retry = new Retry({
|
||||
// Unlike the stream reconnect use of Retry, which we want to be instant
|
||||
// in normal operation, this is a wacky failure. We don't want to retry
|
||||
// right away, we can start slowly.
|
||||
//
|
||||
// A better way than timeconstants here might be to use the knowledge
|
||||
// of when we reconnect to help trigger these retries. Typically, the
|
||||
// server fixing code will result in a restart and reconnect, but
|
||||
// potentially the subscription could have a transient error.
|
||||
minCount: 0, // don't do any immediate retries
|
||||
baseTimeout: 30 * 1000, // start with 30s
|
||||
});
|
||||
|
||||
let failures = 0;
|
||||
|
||||
Autoupdate._retrySubscription = () => {
|
||||
Meteor.subscribe('meteor_autoupdate_clientVersions', {
|
||||
onError(error) {
|
||||
Meteor._debug('autoupdate subscription failed', error);
|
||||
failures++;
|
||||
retry.retryLater(failures, function () {
|
||||
// Just retry making the subscription, don't reload the whole
|
||||
// page. While reloading would catch more cases (for example,
|
||||
// the server went back a version and is now doing old-style hot
|
||||
// code push), it would also be more prone to reload loops,
|
||||
// which look really bad to the user. Just retrying the
|
||||
// subscription over DDP means it is at least possible to fix by
|
||||
// updating the server.
|
||||
Autoupdate._retrySubscription();
|
||||
});
|
||||
},
|
||||
|
||||
onReady() {
|
||||
// Call checkNewVersionDocument with a slight delay, so that the
|
||||
// const handle declaration is guaranteed to be initialized, even if
|
||||
// the added or changed callbacks are called synchronously.
|
||||
const resolved = Promise.resolve();
|
||||
function check(doc) {
|
||||
resolved.then(() => checkNewVersionDocument(doc));
|
||||
}
|
||||
|
||||
const stop = clientVersions.watch(check);
|
||||
|
||||
const reloadDelayInSeconds = Meteor.isProduction ? 60 : 0;
|
||||
|
||||
function checkNewVersionDocument(doc) {
|
||||
if (doc._id !== clientArch) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (doc.versionNonRefreshable !== autoupdateVersions.versionNonRefreshable) {
|
||||
// Non-refreshable assets have changed, so we have to reload the
|
||||
// whole page rather than just replacing <link> tags.
|
||||
if (stop) stop();
|
||||
if (Package.reload) {
|
||||
// The reload package should be provided by ddp-client, which
|
||||
// is provided by the ddp package that autoupdate depends on.
|
||||
|
||||
// Delay reload in 60 seconds
|
||||
console.warn(
|
||||
'Client version changed from',
|
||||
autoupdateVersions.versionNonRefreshable,
|
||||
'to',
|
||||
doc.versionNonRefreshable,
|
||||
`Page will reload in ${reloadDelayInSeconds} seconds`,
|
||||
);
|
||||
document.dispatchEvent(new Event('client_changed'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (doc.versionRefreshable !== autoupdateVersions.versionRefreshable) {
|
||||
autoupdateVersions.versionRefreshable = doc.versionRefreshable;
|
||||
|
||||
// Switch out old css links for the new css links. Inspired by:
|
||||
// https://github.com/guard/guard-livereload/blob/master/js/livereload.js#L710
|
||||
var newCss = doc.assets || [];
|
||||
var oldLinks = [];
|
||||
|
||||
Array.prototype.forEach.call(document.getElementsByTagName('link'), function (link) {
|
||||
if (link.className === '__meteor-css__') {
|
||||
oldLinks.push(link);
|
||||
}
|
||||
});
|
||||
|
||||
function waitUntilCssLoads(link, callback) {
|
||||
var called;
|
||||
|
||||
link.onload = function () {
|
||||
knownToSupportCssOnLoad = true;
|
||||
if (!called) {
|
||||
called = true;
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
if (!knownToSupportCssOnLoad) {
|
||||
var id = Meteor.setInterval(function () {
|
||||
if (link.sheet) {
|
||||
if (!called) {
|
||||
called = true;
|
||||
callback();
|
||||
}
|
||||
Meteor.clearInterval(id);
|
||||
}
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
|
||||
let newLinksLeftToLoad = newCss.length;
|
||||
function removeOldLinks() {
|
||||
if (oldLinks.length > 0 && --newLinksLeftToLoad < 1) {
|
||||
oldLinks.splice(0).forEach((link) => {
|
||||
link.parentNode.removeChild(link);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (newCss.length > 0) {
|
||||
newCss.forEach((css) => {
|
||||
const newLink = document.createElement('link');
|
||||
newLink.setAttribute('rel', 'stylesheet');
|
||||
newLink.setAttribute('type', 'text/css');
|
||||
newLink.setAttribute('class', '__meteor-css__');
|
||||
newLink.setAttribute('href', css.url);
|
||||
|
||||
waitUntilCssLoads(newLink, function () {
|
||||
Meteor.setTimeout(removeOldLinks, 200);
|
||||
});
|
||||
|
||||
const head = document.getElementsByTagName('head').item(0);
|
||||
head.appendChild(newLink);
|
||||
});
|
||||
} else {
|
||||
removeOldLinks();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Autoupdate._retrySubscription();
|
||||
166
apps/meteor/packages/autoupdate/autoupdate_server.js
Normal file
166
apps/meteor/packages/autoupdate/autoupdate_server.js
Normal file
@@ -0,0 +1,166 @@
|
||||
// Publish the current client versions for each client architecture
|
||||
// (web.browser, web.browser.legacy, web.cordova). When a client observes
|
||||
// a change in the versions associated with its client architecture,
|
||||
// it will refresh itself, either by swapping out CSS assets or by
|
||||
// reloading the page. Changes to the replaceable version are ignored
|
||||
// and handled by the hot-module-replacement package.
|
||||
//
|
||||
// There are four versions for any given client architecture: `version`,
|
||||
// `versionRefreshable`, `versionNonRefreshable`, and
|
||||
// `versionReplaceable`. The refreshable version is a hash of just the
|
||||
// client resources that are refreshable, such as CSS. The replaceable
|
||||
// version is a hash of files that can be updated with HMR. The
|
||||
// non-refreshable version is a hash of the rest of the client assets,
|
||||
// excluding the refreshable ones: HTML, JS that is not replaceable, and
|
||||
// static files in the `public` directory. The `version` version is a
|
||||
// combined hash of everything.
|
||||
//
|
||||
// If the environment variable `AUTOUPDATE_VERSION` is set, it will be
|
||||
// used in place of all client versions. You can use this variable to
|
||||
// control when the client reloads. For example, if you want to force a
|
||||
// reload only after major changes, use a custom AUTOUPDATE_VERSION and
|
||||
// change it only when something worth pushing to clients happens.
|
||||
//
|
||||
// The server publishes a `meteor_autoupdate_clientVersions` collection.
|
||||
// The ID of each document is the client architecture, and the fields of
|
||||
// the document are the versions described above.
|
||||
|
||||
import { Meteor } from 'meteor/meteor';
|
||||
import { ClientVersions } from './client_versions.js';
|
||||
|
||||
export const Autoupdate = (__meteor_runtime_config__.autoupdate = {
|
||||
// Map from client architectures (web.browser, web.browser.legacy,
|
||||
// web.cordova) to version fields { version, versionRefreshable,
|
||||
// versionNonRefreshable, refreshable } that will be stored in
|
||||
// ClientVersions documents (whose IDs are client architectures). This
|
||||
// data gets serialized into the boilerplate because it's stored in
|
||||
// __meteor_runtime_config__.autoupdate.versions.
|
||||
versions: {},
|
||||
});
|
||||
|
||||
// Stores acceptable client versions.
|
||||
const clientVersions = new ClientVersions();
|
||||
|
||||
// The client hash includes __meteor_runtime_config__, so wait until
|
||||
// all packages have loaded and have had a chance to populate the
|
||||
// runtime config before using the client hash as our default auto
|
||||
// update version id.
|
||||
|
||||
// Note: Tests allow people to override Autoupdate.autoupdateVersion before
|
||||
// startup.
|
||||
Autoupdate.autoupdateVersion = null;
|
||||
Autoupdate.autoupdateVersionRefreshable = null;
|
||||
Autoupdate.autoupdateVersionCordova = null;
|
||||
Autoupdate.appId = __meteor_runtime_config__.appId = process.env.APP_ID;
|
||||
|
||||
var syncQueue = new Meteor._AsynchronousQueue();
|
||||
|
||||
async function updateVersions(shouldReloadClientProgram) {
|
||||
// Step 1: load the current client program on the server
|
||||
if (shouldReloadClientProgram) {
|
||||
await WebAppInternals.reloadClientPrograms();
|
||||
}
|
||||
|
||||
const {
|
||||
// If the AUTOUPDATE_VERSION environment variable is defined, it takes
|
||||
// precedence, but Autoupdate.autoupdateVersion is still supported as
|
||||
// a fallback. In most cases neither of these values will be defined.
|
||||
AUTOUPDATE_VERSION = Autoupdate.autoupdateVersion,
|
||||
} = process.env;
|
||||
|
||||
// Step 2: update __meteor_runtime_config__.autoupdate.versions.
|
||||
const clientArchs = Object.keys(WebApp.clientPrograms);
|
||||
clientArchs.forEach((arch) => {
|
||||
Autoupdate.versions[arch] = {
|
||||
version: AUTOUPDATE_VERSION || WebApp.calculateClientHash(arch),
|
||||
versionRefreshable: AUTOUPDATE_VERSION || WebApp.calculateClientHashRefreshable(arch),
|
||||
versionNonRefreshable: AUTOUPDATE_VERSION || WebApp.calculateClientHashNonRefreshable(arch),
|
||||
versionReplaceable: AUTOUPDATE_VERSION || WebApp.calculateClientHashReplaceable(arch),
|
||||
versionHmr: WebApp.clientPrograms[arch].hmrVersion,
|
||||
};
|
||||
});
|
||||
|
||||
// Step 3: form the new client boilerplate which contains the updated
|
||||
// assets and __meteor_runtime_config__.
|
||||
if (shouldReloadClientProgram) {
|
||||
await WebAppInternals.generateBoilerplate();
|
||||
}
|
||||
|
||||
// Step 4: update the ClientVersions collection.
|
||||
// We use `onListening` here because we need to use
|
||||
// `WebApp.getRefreshableAssets`, which is only set after
|
||||
// `WebApp.generateBoilerplate` is called by `main` in webapp.
|
||||
WebApp.onListening(() => {
|
||||
clientArchs.forEach((arch) => {
|
||||
const payload = {
|
||||
...Autoupdate.versions[arch],
|
||||
assets: WebApp.getRefreshableAssets(arch),
|
||||
};
|
||||
|
||||
clientVersions.set(arch, payload);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Meteor.publish(
|
||||
'meteor_autoupdate_clientVersions',
|
||||
function (appId) {
|
||||
// `null` happens when a client doesn't have an appId and passes
|
||||
// `undefined` to `Meteor.subscribe`. `undefined` is translated to
|
||||
// `null` as JSON doesn't have `undefined.
|
||||
check(appId, Match.OneOf(String, undefined, null));
|
||||
|
||||
// Don't notify clients using wrong appId such as mobile apps built with a
|
||||
// different server but pointing at the same local url
|
||||
if (Autoupdate.appId && appId && Autoupdate.appId !== appId) return [];
|
||||
|
||||
// Random value to delay the updates for 2-10 minutes
|
||||
const randomInterval = Meteor.isProduction ? (Math.floor(Math.random() * 8) + 2) * 1000 * 60 : 0;
|
||||
|
||||
const stop = clientVersions.watch((version, isNew) => {
|
||||
setTimeout(() => {
|
||||
(isNew ? this.added : this.changed).call(this, 'meteor_autoupdate_clientVersions', version._id, version);
|
||||
}, randomInterval);
|
||||
});
|
||||
|
||||
this.onStop(() => stop());
|
||||
this.ready();
|
||||
},
|
||||
{ is_auto: true },
|
||||
);
|
||||
|
||||
Meteor.startup(async function () {
|
||||
await updateVersions(false);
|
||||
|
||||
// Force any connected clients that are still looking for these older
|
||||
// document IDs to reload.
|
||||
['version', 'version-refreshable', 'version-cordova'].forEach((_id) => {
|
||||
clientVersions.set(_id, {
|
||||
version: 'outdated',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function enqueueVersionsRefresh() {
|
||||
syncQueue.queueTask(async function () {
|
||||
await updateVersions(true);
|
||||
});
|
||||
}
|
||||
|
||||
const setupListeners = () => {
|
||||
// Listen for messages pertaining to the client-refresh topic.
|
||||
import { onMessage } from 'meteor/inter-process-messaging';
|
||||
onMessage('client-refresh', enqueueVersionsRefresh);
|
||||
|
||||
// Another way to tell the process to refresh: send SIGHUP signal
|
||||
process.on(
|
||||
'SIGHUP',
|
||||
Meteor.bindEnvironment(function () {
|
||||
enqueueVersionsRefresh();
|
||||
}, 'handling SIGHUP signal for refresh'),
|
||||
);
|
||||
};
|
||||
|
||||
WebApp.onListening(function () {
|
||||
Promise.resolve(setupListeners());
|
||||
});
|
||||
101
apps/meteor/packages/autoupdate/client_versions.js
Normal file
101
apps/meteor/packages/autoupdate/client_versions.js
Normal file
@@ -0,0 +1,101 @@
|
||||
import { Tracker } from 'meteor/tracker';
|
||||
|
||||
export class ClientVersions {
|
||||
constructor() {
|
||||
this._versions = new Map();
|
||||
this._watchCallbacks = new Set();
|
||||
}
|
||||
|
||||
// Creates a Livedata store for use with `Meteor.connection.registerStore`.
|
||||
// After the store is registered, document updates reported by Livedata are
|
||||
// merged with the documents in this `ClientVersions` instance.
|
||||
createStore() {
|
||||
return {
|
||||
update: ({ id, msg, fields }) => {
|
||||
if (msg === 'added' || msg === 'changed') {
|
||||
this.set(id, fields);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
hasVersions() {
|
||||
return this._versions.size > 0;
|
||||
}
|
||||
|
||||
get(id) {
|
||||
return this._versions.get(id);
|
||||
}
|
||||
|
||||
// Adds or updates a version document and invokes registered callbacks for the
|
||||
// added/updated document. If a document with the given ID already exists, its
|
||||
// fields are merged with `fields`.
|
||||
set(id, fields) {
|
||||
let version = this._versions.get(id);
|
||||
let isNew = false;
|
||||
|
||||
if (version) {
|
||||
Object.assign(version, fields);
|
||||
} else {
|
||||
version = {
|
||||
_id: id,
|
||||
...fields,
|
||||
};
|
||||
|
||||
isNew = true;
|
||||
this._versions.set(id, version);
|
||||
}
|
||||
|
||||
this._watchCallbacks.forEach(({ fn, filter }) => {
|
||||
if (!filter || filter === version._id) {
|
||||
fn(version, isNew);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Registers a callback that will be invoked when a version document is added
|
||||
// or changed. Calling the function returned by `watch` removes the callback.
|
||||
// If `skipInitial` is true, the callback isn't be invoked for existing
|
||||
// documents. If `filter` is set, the callback is only invoked for documents
|
||||
// with ID `filter`.
|
||||
watch(fn, { skipInitial, filter } = {}) {
|
||||
if (!skipInitial) {
|
||||
const resolved = Promise.resolve();
|
||||
|
||||
this._versions.forEach((version) => {
|
||||
if (!filter || filter === version._id) {
|
||||
resolved.then(() => fn(version, true));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const callback = { fn, filter };
|
||||
this._watchCallbacks.add(callback);
|
||||
|
||||
return () => this._watchCallbacks.delete(callback);
|
||||
}
|
||||
|
||||
// A reactive data source for `Autoupdate.newClientAvailable`.
|
||||
newClientAvailable(id, fields, currentVersion) {
|
||||
function isNewVersion(version) {
|
||||
return version._id === id && fields.some((field) => version[field] !== currentVersion[field]);
|
||||
}
|
||||
|
||||
const dependency = new Tracker.Dependency();
|
||||
const version = this.get(id);
|
||||
|
||||
dependency.depend();
|
||||
|
||||
const stop = this.watch(
|
||||
(version) => {
|
||||
if (isNewVersion(version)) {
|
||||
dependency.changed();
|
||||
stop();
|
||||
}
|
||||
},
|
||||
{ skipInitial: true },
|
||||
);
|
||||
|
||||
return !!version && isNewVersion(version);
|
||||
}
|
||||
}
|
||||
20
apps/meteor/packages/autoupdate/package.js
Normal file
20
apps/meteor/packages/autoupdate/package.js
Normal file
@@ -0,0 +1,20 @@
|
||||
Package.describe({
|
||||
name: 'autoupdate',
|
||||
summary: 'Update the client when new client code is available',
|
||||
version: '2.0.1',
|
||||
});
|
||||
|
||||
Package.onUse(function (api) {
|
||||
api.use(['webapp', 'check', 'inter-process-messaging'], 'server');
|
||||
|
||||
api.use(['tracker', 'retry'], 'client');
|
||||
|
||||
api.use('reload', 'client', { weak: true });
|
||||
|
||||
api.use(['ecmascript', 'ddp'], ['client', 'server']);
|
||||
|
||||
api.mainModule('autoupdate_server.js', 'server');
|
||||
api.mainModule('autoupdate_client.js', 'client');
|
||||
|
||||
api.export('Autoupdate');
|
||||
});
|
||||
Reference in New Issue
Block a user