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();