diff --git a/History.md b/History.md
index ec4ea53bf9..00861142fb 100644
--- a/History.md
+++ b/History.md
@@ -29,7 +29,8 @@
* `webapp@1.12`
- npm dependencies have been updated
- - Added option to change runtime config in your app, [read more](https://github.com/meteor/meteor/pull/11506)
+ - Added hook to change runtime config delivered to the client app, [read more](https://github.com/meteor/meteor/pull/11506)
+ - Added hook to get notified when the app is updated, [read more](https://github.com/meteor/meteor/pull/11607)
- `@vlasky/whomst@0.1.7`
- Added `addUpdateNotifyHook` that gets called when runtime configuration is updated
diff --git a/packages/webapp/package.js b/packages/webapp/package.js
index f58b6b00f9..ee3e186368 100644
--- a/packages/webapp/package.js
+++ b/packages/webapp/package.js
@@ -35,6 +35,7 @@ Package.onUse(function (api) {
'boilerplate-generator',
'webapp-hashing',
'inter-process-messaging',
+ 'callback-hook'
], 'server');
// At response serving time, webapp uses browser-policy if it is loaded. If
diff --git a/packages/webapp/webapp_server.js b/packages/webapp/webapp_server.js
index df91dbaa6a..a07b9cfff4 100644
--- a/packages/webapp/webapp_server.js
+++ b/packages/webapp/webapp_server.js
@@ -345,21 +345,35 @@ function getBoilerplate(request, arch) {
return getBoilerplateAsync(request, arch).await();
}
+/**
+ * @summary Takes a runtime configuration object and
+ * returns an encoded runtime string.
+ * @locus Server
+ * @param {Object} rtimeConfig
+ * @returns {String}
+*/
WebApp.encodeRuntimeConfig = function (rtimeConfig) {
return JSON.stringify(encodeURIComponent(JSON.stringify(rtimeConfig)));
}
-WebApp.decodeRuntimeConfig = function (rtimeConfig) {
- return JSON.parse(decodeURIComponent(JSON.parse(rtimeConfig)));
+/**
+ * @summary Takes an encoded runtime string and returns
+ * a runtime configuration object.
+ * @locus Server
+ * @param {String} rtimeConfigString
+ * @returns {Object}
+*/
+WebApp.decodeRuntimeConfig = function (rtimeConfigStr) {
+ return JSON.parse(decodeURIComponent(JSON.parse(rtimeConfigStr)));
}
const runtimeConfig = {
// hooks will contain the callback functions
// set by the caller to addRuntimeConfigHook
- hooks: [],
+ hooks: new Hook(),
// updateHooks will contain the callback functions
- // set by the caller to addUpdatedConfigHook
- updateHooks: [],
+ // set by the caller to addUpdatedNotifyHook
+ updateHooks: new Hook(),
// isUpdatedByArch is an object containing fields for each arch
// that this server supports.
// - Each field will be true when the server updates the runtimeConfig for that arch.
@@ -371,9 +385,55 @@ WebApp.decodeRuntimeConfig = function (rtimeConfig) {
isUpdatedByArch: {}
};
-WebApp.addRuntimeConfigHook = function (hook) {
- if(typeof hook !== 'function') throw new Error('WebApp.addRuntimeConfigHook must be a function');
- runtimeConfig.hooks.push(hook);
+/**
+ * @name addRuntimeConfigHookCallback(options)
+ * @locus Server
+ * @isprototype true
+ * @summary Callback for `addRuntimeConfigHook`.
+ *
+ * If the handler returns a _falsy_ value the hook will not
+ * modify the runtime configuration.
+ *
+ * If the handler returns a _String_ the hook will substitute
+ * the string for the encoded configuration string.
+ *
+ * **Warning:** the hook does not check the return value at all it is
+ * the responsibility of the caller to get the formatting correct using
+ * the helper functions.
+ *
+ * `addRuntimeConfigHookCallback` takes only one `Object` argument
+ * with the following fields:
+ * @param {Object} options
+ * @param {String} options.arch The architecture of the client
+ * requesting a new runtime configuration. This can be one of
+ * `web.browser`, `web.browser.legacy` or `web.cordova`.
+ * @param {Object} options.request
+ * A NodeJs [IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage)
+ * https://nodejs.org/api/http.html#http_class_http_incomingmessage
+ * `Object` that can be used to get information about the incoming request.
+ * @param {String} options.encodedCurrentConfig The current configuration object
+ * encoded as a string for inclusion in the root html.
+ * @param {Boolean} options.updated `true` if the config for this architecture
+ * has been updated since last called, otherwise `false`. This flag can be used
+ * to cache the decoding/encoding for each architecture.
+ */
+
+/**
+ * @summary Hook that calls back when the meteor runtime configuration,
+ * `__meteor_runtime_config__` is being sent to any client.
+ *
+ * **returns**: _Object_ `{ stop: function, callback: function }`
+ * - `stop` _Function_ Call `stop()` to stop getting callbacks.
+ * - `callback` _Function_ The passed in `callback`.
+ * @locus Server
+ * @param {addRuntimeConfigHookCallback} callback
+ * See `addRuntimeConfigHookCallback` description.
+ * @returns {Object} {{ stop: function, callback: function }}
+ * Call the returned `stop()` to stop getting callbacks.
+ * The passed in `callback` is returned also.
+*/
+WebApp.addRuntimeConfigHook = function (callback) {
+ return runtimeConfig.hooks.register(callback);
}
function getBoilerplateAsync(request, arch) {
@@ -415,14 +475,32 @@ function getBoilerplateAsync(request, arch) {
}));
}
-// Notification hook whenever the runtime configuration is updated
-// hook will pass an object with the following fields:
-// - arch
-// - manifest
-// - runtimeConfig
-WebApp.addUpdatedNotifyHook = function(hook) {
- if(typeof hook !== 'function') throw new Error('WebApp.addUpdatedNotifyHook must be a function');
- runtimeConfig.updateHooks.push(hook);
+/**
+ * @name addUpdatedNotifyHookCallback(options)
+ * @summary callback handler for `addupdatedNotifyHook`
+ * @isprototype true
+ * @locus Server
+ * @param {Object} options
+ * @param {String} options.arch The architecture that is being updated.
+ * This can be one of `web.browser`, `web.browser.legacy` or `web.cordova`.
+ * @param {Object} options.manifest The new updated manifest object for
+ * this `arch`.
+ * @param {Object} options.runtimeConfig The new updated configuration
+ * object for this `arch`.
+ */
+
+
+/**
+ * @summary Hook that runs when the meteor runtime configuration
+ * is updated. Typically the configuration only changes during development mode.
+ * @locus Server
+ * @param {addUpdatedNotifyHookCallback} handler
+ * The `handler` is called on every change to an `arch` runtime configuration.
+ * See `addUpdatedNotifyHookCallback`.
+ * @returns {Object} {{ stop: function, callback: function }}
+*/
+WebApp.addUpdatedNotifyHook = function(handler) {
+ return runtimeConfig.updateHooks.register(handler);
}
WebAppInternals.generateBoilerplateInstance = function (arch,
@@ -1032,6 +1110,44 @@ function runWebAppServer() {
// other handlers added by package and application code.
app.use(WebAppInternals.meteorInternalHandlers = connect());
+
+ /**
+ * @name connectHandlersCallback(req, res, next)
+ * @locus Server
+ * @isprototype true
+ * @summary callback handler for `WebApp.connectHandlers`
+ * @param {Object} req
+ * a Node.js
+ * [IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage)
+ * object with some extra properties. This argument can be used
+ * to get information about the incoming request.
+ * @param {Object} res
+ * a Node.js
+ * [ServerResponse](http://nodejs.org/api/http.html#http_class_http_serverresponse)
+ * object. Use this to write data that should be sent in response to the
+ * request, and call `res.end()` when you are done.
+ * @param {Function} next
+ * Calling this function will pass on the handling of
+ * this request to the next relevant handler.
+ *
+ */
+
+ /**
+ * @method connectHandlers
+ * @memberof WebApp
+ * @locus Server
+ * @summary Register a handler for all HTTP requests.
+ * @param {String} [path]
+ * This handler will only be called on paths that match
+ * this string. The match has to border on a `/` or a `.`.
+ *
+ * For example, `/hello` will match `/hello/world` and
+ * `/hello.world`, but not `/hello_world`.
+ * @param {connectHandlersCallback} handler
+ * A handler function that will be called on HTTP requests.
+ * See `connectHandlersCallback`
+ *
+ */
// Packages and apps can add handlers to this via WebApp.connectHandlers.
// They are inserted before our default handler.
var packageAndAppHandlers = connect();