diff --git a/API.md b/API.md index 865cbefd9..36dfac6b2 100755 --- a/API.md +++ b/API.md @@ -1,262 +1,3 @@ -# v18.4.x API Reference - - - -- [Server](#server) - - [`server([options])`](#server()) - - [Server options](#server.options) - - [`server.options.address`](#server.options.address) - - [`server.options.app`](#server.options.app) - - [`server.options.autoListen`](#server.options.autolisten) - - [`server.options.cache`](#server.options.cache) - - [`server.options.compression`](#server.options.compression) - - [`server.options.compression.minBytes`](#server.options.compression.minBytes) - - [`server.options.debug`](#server.options.debug) - - [`server.options.host`](#server.options.host) - - [`server.options.listener`](#server.options.listener) - - [`server.options.load`](#server.options.load) - - [`server.options.mime`](#server.options.mime) - - [`server.options.operations`](#server.options.operations) - - [`server.options.plugins`](#server.options.plugins) - - [`server.options.port`](#server.options.port) - - [`server.options.query`](#server.options.query) - - [`server.options.query.parser`](#server.options.query.parser) - - [`server.options.router`](#server.options.router) - - [`server.options.routes`](#server.options.routes) - - [`server.options.state`](#server.options.state) - - [`server.options.tls`](#server.options.tls) - - [`server.options.uri`](#server.options.uri) - - [Server properties](#server-properties) - - [`server.app`](#server.app) - - [`server.auth.api`](#server.auth.api) - - [`server.auth.settings.default`](#server.auth.settings.default) - - [`server.decorations`](#server.decorations) - - [`server.events`](#server.events) - - [`'log'` Event](#server.events.log) - - [`'request'` Event](#server.events.request) - - [`'response'` Event](#server.events.response) - - [`'route'` Event](#server.events.route) - - [`'start'` Event](#server.events.start) - - [`'stop'` Event](#server.events.stop) - - [`server.info`](#server.info) - - [`server.listener`](#server.listener) - - [`server.load`](#server.load) - - [`server.methods`](#server.methods) - - [`server.mime`](#server.mime) - - [`server.plugins`](#server.plugins) - - [`server.realm`](#server.realm) - - [`server.registrations`](#server.registrations) - - [`server.settings`](#server.settings) - - [`server.states`](#server.states) - - [`server.states.settings`](#server.states.settings) - - [`server.states.cookies`](#server.states.cookies) - - [`server.states.names`](#server.states.names) - - [`server.type`](#server.type) - - [`server.version`](#server.version) - - [`server.auth.default(options)`](#server.auth.default()) - - [`server.auth.scheme(name, scheme)`](#server.auth.scheme()) - - [Authentication scheme](#authentication-scheme) - - [`server.auth.strategy(name, scheme, [options])`](#server.auth.strategy()) - - [`await server.auth.test(strategy, request)`](#server.auth.test()) - - [`await server.auth.verify(request)`](#server.auth.verify()) - - [`server.bind(context)`](#server.bind()) - - [`server.cache(options)`](#server.cache()) - - [`await server.cache.provision(options)`](#server.cache.provision()) - - [`server.control(server)`](#server.control()) - - [`server.decoder(encoding, decoder)`](#server.decoder()) - - [`server.decorate(type, property, method, [options])`](#server.decorate()) - - [`server.dependency(dependencies, [after])`](#server.dependency()) - - [`server.encoder(encoding, encoder)`](#server.encoder()) - - [`server.event(events)`](#server.event()) - - [`await server.events.emit(criteria, data)`](#server.events.emit()) - - [`server.events.on(criteria, listener)`](#server.events.on()) - - [`server.events.once(criteria, listener)`](#server.events.once()) - - [`await server.events.once(criteria)`](#server.events.once.await()) - - [`server.expose(key, value)`](#server.expose()) - - [`server.expose(obj)`](#server.expose.obj()) - - [`server.ext(events)`](#server.ext()) - - [`server.ext(event, method, [options])`](#server.ext.args()) - - [`await server.initialize()`](#server.initialize()) - - [`await server.inject(options)`](#server.inject()) - - [`server.log(tags, [data, [timestamp]])`](#server.log()) - - [`server.lookup(id)`](#server.lookup()) - - [`server.match(method, path, [host])`](#server.match()) - - [`server.method(name, method, [options])`](#server.method()) - - [`server.method(methods)`](#server.method.array()) - - [`server.path(relativeTo)`](#server.path()) - - [`await server.register(plugins, [options])`](#server.register()) - - [`server.route(route)`](#server.route()) - - [Path parameters](#path-parameters) - - [Path matching order](#path-matching-order) - - [Catch all route](#catch-all-route) - - [`server.rules(processor, [options])`](#server.rules()) - - [`await server.start()`](#server.start()) - - [`server.state(name, [options])`](#server.state()) - - [`server.states.add(name, [options])`](#server.states.add()) - - [`await server.states.format(cookies)`](#server.states.format()) - - [`await server.states.parse(header)`](#server.states.parse()) - - [`await server.stop([options])`](#server.stop()) - - [`server.table([host])`](#server.table()) - - [`server.validator(validator)`](#server.validator()) -- [Route options](#route-options) - - [`route.options.app`](#route.options.app) - - [`route.options.auth`](#route.options.auth) - - [`route.options.auth.access`](#route.options.auth.access) - - [`route.options.auth.access.scope`](#route.options.auth.access.scope) - - [`route.options.auth.access.entity`](#route.options.auth.access.entity) - - [`route.options.auth.mode`](#route.options.auth.mode) - - [`route.options.auth.payload`](#route.options.auth.payload) - - [`route.options.auth.strategies`](#route.options.auth.strategies) - - [`route.options.auth.strategy`](#route.options.auth.strategy) - - [`route.options.bind`](#route.options.bind) - - [`route.options.cache`](#route.options.cache) - - [`route.options.compression`](#route.options.compression) - - [`route.options.cors`](#route.options.cors) - - [`route.options.description`](#route.options.description) - - [`route.options.ext`](#route.options.ext) - - [`route.options.files`](#route.options.files) - - [`route.options.handler`](#route.options.handler) - - [`route.options.id`](#route.options.id) - - [`route.options.isInternal`](#route.options.isInternal) - - [`route.options.json`](#route.options.json) - - [`route.options.jsonp`](#route.options.jsonp) - - [`route.options.log`](#route.options.log) - - [`route.options.notes`](#route.options.notes) - - [`route.options.payload`](#route.options.payload) - - [`route.options.payload.allow`](#route.options.payload.allow) - - [`route.options.payload.compression`](#route.options.payload.compression) - - [`route.options.payload.defaultContentType`](#route.options.payload.defaultContentType) - - [`route.options.payload.failAction`](#route.options.payload.failAction) - - [`route.options.payload.maxBytes`](#route.options.payload.maxBytes) - - [`route.options.payload.multipart`](#route.options.payload.multipart) - - [`route.options.payload.output`](#route.options.payload.output) - - [`route.options.payload.override`](#route.options.payload.override) - - [`route.options.payload.parse`](#route.options.payload.parse) - - [`route.options.payload.protoAction`](#route.options.payload.protoAction) - - [`route.options.payload.timeout`](#route.options.payload.timeout) - - [`route.options.payload.uploads`](#route.options.payload.uploads) - - [`route.options.plugins`](#route.options.plugins) - - [`route.options.pre`](#route.options.pre) - - [`route.options.response`](#route.options.response) - - [`route.options.response.disconnectStatusCode`](#route.options.response.disconnectStatusCode) - - [`route.options.response.emptyStatusCode`](#route.options.response.emptyStatusCode) - - [`route.options.response.failAction`](#route.options.response.failAction) - - [`route.options.response.modify`](#route.options.response.modify) - - [`route.options.response.options`](#route.options.response.options) - - [`route.options.response.ranges`](#route.options.response.ranges) - - [`route.options.response.sample`](#route.options.response.sample) - - [`route.options.response.schema`](#route.options.response.schema) - - [`route.options.response.status`](#route.options.response.status) - - [`route.options.rules`](#route.options.rules) - - [`route.options.security`](#route.options.security) - - [`route.options.state`](#route.options.state) - - [`route.options.tags`](#route.options.tags) - - [`route.options.timeout`](#route.options.timeout) - - [`route.options.timeout.server`](#route.options.timeout.server) - - [`route.options.timeout.socket`](#route.options.timeout.socket) - - [`route.options.validate`](#route.options.validate) - - [`route.options.validate.errorFields`](#route.options.validate.errorFields) - - [`route.options.validate.failAction`](#route.options.validate.failAction) - - [`route.options.validate.headers`](#route.options.validate.headers) - - [`route.options.validate.options`](#route.options.validate.options) - - [`route.options.validate.params`](#route.options.validate.params) - - [`route.options.validate.payload`](#route.options.validate.payload) - - [`route.options.validate.query`](#route.options.validate.query) - - [`route.options.validate.state`](#route.options.validate.state) -- [Request lifecycle](#request-lifecycle) - - [Lifecycle methods](#lifecycle-methods) - - [Lifecycle workflow](#lifecycle-workflow) - - [Takeover response](#takeover-response) - - [`failAction` configuration](#lifecycle-failAction) - - [Errors](#errors) - - [Error transformation](#error-transformation) - - [Response Toolkit](#response-toolkit) - - [Toolkit properties](#toolkit-properties) - - [`h.abandon`](#h.abandon) - - [`h.close`](#h.close) - - [`h.context`](#h.context) - - [`h.continue`](#h.continue) - - [`h.realm`](#h.realm) - - [`h.request`](#h.request) - - [`h.authenticated(data)`](#h.authenticated()) - - [`h.entity(options)`](#h.entity()) - - [`h.redirect(uri)`](#h.redirect()) - - [`h.response([value])`](#h.response()) - - [`h.state(name, value, [options])`](#h.state()) - - [`h.unauthenticated(error, [data])`](#h.unauthenticated()) - - [`h.unstate(name, [options])`](#h.unstate()) - - [Response object](#response-object) - - [Response properties](#response-properties) - - [`response.app`](#response.app) - - [`response.events`](#response.events) - - [`response.headers`](#response.headers) - - [`response.plugins`](#response.plugins) - - [`response.settings`](#response.settings) - - [`response.settings.passThrough`](#response.settings.passThrough) - - [`response.settings.stringify`](#response.settings.stringify) - - [`response.settings.ttl`](#response.settings.ttl) - - [`response.settings.varyEtag`](#response.settings.varyEtag) - - [`response.source`](#response.source) - - [`response.statusCode`](#response.statusCode) - - [`response.variety`](#response.variety) - - [`response.bytes(length)`](#response.bytes()) - - [`response.charset(charset)`](#response.charset()) - - [`response.code(statusCode)`](#response.code()) - - [`response.message(httpMessage)`](#response.message()) - - [`response.compressed(encoding)`](#response.compressed()) - - [`response.created(uri)`](#response.created()) - - [`response.encoding(encoding)`](#response.encoding()) - - [`response.etag(tag, options)`](#response.etag()) - - [`response.header(name, value, options)`](#response.header()) - - [`response.location(uri)`](#response.location()) - - [`response.redirect(uri)`](#response.redirect()) - - [`response.replacer(method)`](#response.replacer()) - - [`response.spaces(count)`](#response.spaces()) - - [`response.state(name, value, [options])`](#response.state()) - - [`response.suffix(suffix)`](#response.suffix()) - - [`response.ttl(msec)`](#response.ttl()) - - [`response.type(mimeType)`](#response.type()) - - [`response.unstate(name, [options])`](#response.unstate()) - - [`response.vary(header)`](#response.vary()) - - [`response.takeover()`](#response.takeover()) - - [`response.temporary(isTemporary)`](#response.temporary()) - - [`response.permanent(isPermanent)`](#response.permanent()) - - [`response.rewritable(isRewritable)`](#response.rewritable()) -- [Request](#request) - - [Request properties](#request-properties) - - [`request.app`](#request.app) - - [`request.auth`](#request.auth) - - [`request.events`](#request.events) - - [`request.headers`](#request.headers) - - [`request.info`](#request.info) - - [`request.logs`](#request.logs) - - [`request.method`](#request.method) - - [`request.mime`](#request.mime) - - [`request.orig`](#request.orig) - - [`request.params`](#request.params) - - [`request.paramsArray`](#request.paramsArray) - - [`request.path`](#request.path) - - [`request.payload`](#request.payload) - - [`request.plugins`](#request.plugins) - - [`request.pre`](#request.pre) - - [`request.response`](#request.response) - - [`request.preResponses`](#request.preResponses) - - [`request.query`](#request.query) - - [`request.raw`](#request.raw) - - [`request.route`](#request.route) - - [`request.server`](#request.server) - - [`request.state`](#request.state) - - [`request.url`](#request.url) - - [`request.generateResponse(source, [options])`](#request.generateResponse()) - - [`request.active()`](#request.active()) - - [`request.log(tags, [data])`](#request.log()) - - [`request.route.auth.access(request)`](#request.route.auth.access()) - - [`request.setMethod(method)`](#request.setMethod()) - - [`request.setUrl(url, [stripTrailingSlash]`](#request.setUrl()) -- [Plugins](#plugins) - - - ## Server The server object is the main application container. The server manages all incoming requests @@ -1919,12 +1660,17 @@ async function example() { } ``` -### `server.expose(key, value)` +### `server.expose(key, value, [options])` Used within a plugin to expose a property via [`server.plugins[name]`](#server.plugins) where: - `key` - the key assigned ([`server.plugins[name][key]`](#server.plugins)). - `value` - the value assigned. +- `options` - optional settings: + - `scope` - controls how to handle the presence of a plugin scope in the name (e.g. `@hapi/test`): + - `false` - the scope is removed (e.g. `@hapi/test` is changed to `test` under `server.plugins`). This is the default. + - `true` - the scope is retained as-is (e.g. `@hapi/test` is used as `server.plugins['@hapi/test']`). + - `'underscore'` - the scope is rewritten (e.g. `@hapi/test` is used as `server.plugins.hapi__test`). Return value: none. diff --git a/lib/server.js b/lib/server.js index 430affcfd..0ba7a60c6 100755 --- a/lib/server.js +++ b/lib/server.js @@ -64,7 +64,7 @@ internals.Server = class { modifiers: { route: {} }, - parent: (parent ? parent.realm : null), + parent: parent ? parent.realm : null, plugin: name, pluginOptions: {}, plugins: {}, @@ -218,11 +218,20 @@ internals.Server = class { this._core.events.registerEvent(event); } - expose(key, value) { + expose(key, value, options = {}) { Hoek.assert(this.realm.plugin, 'Cannot call expose() outside of a plugin'); - const plugin = this.realm.plugin; + let plugin = this.realm.plugin; + if (plugin[0] === '@' && + options.scope !== true) { + + plugin = plugin.replace(/^\@([^\/]+)\//, ($0, $1) => { + + return !options.scope ? '' : `${$1}__`; + }); + } + this._core.plugins[plugin] = this._core.plugins[plugin] || {}; if (typeof key === 'string') { @@ -294,7 +303,7 @@ internals.Server = class { delete settings.plugins; delete settings.allowInternals; - settings.authority = settings.authority || (this._core.info.host + ':' + this._core.info.port); + settings.authority = settings.authority || this._core.info.host + ':' + this._core.info.port; } Hoek.assert(!options.credentials, 'options.credentials no longer supported (use options.auth)'); diff --git a/test/server.js b/test/server.js index f4c66e160..81098b1c5 100755 --- a/test/server.js +++ b/test/server.js @@ -826,6 +826,78 @@ describe('Server', () => { expect(server.plugins.test1.add(1, 3)).to.equal(4); expect(server.plugins.test1.glue('1', '3')).to.equal('13'); }); + + it('exposes an api (scope without scope)', async () => { + + const server = Hapi.server(); + + const plugin = { + name: 'test1', + version: '1.0.0', + register: function (srv, options) { + + srv.expose('x', { y: 1 }, { scope: true }); + } + }; + + await server.register(plugin); + expect(server.plugins.test1.x.y).to.equal(1); + expect(server.registrations).to.equal({ test1: { version: '1.0.0', name: 'test1', options: undefined } }); + }); + + it('exposes an api (drops scope by default)', async () => { + + const server = Hapi.server(); + + const plugin = { + name: '@hapi/test1', + version: '1.0.0', + register: function (srv, options) { + + srv.expose('x', { y: 1 }); + } + }; + + await server.register(plugin); + expect(server.plugins.test1.x.y).to.equal(1); + expect(server.registrations).to.equal({ '@hapi/test1': { version: '1.0.0', name: '@hapi/test1', options: undefined } }); + }); + + it('exposes an api (keeps scope)', async () => { + + const server = Hapi.server(); + + const plugin = { + name: '@hapi/test1', + version: '1.0.0', + register: function (srv, options) { + + srv.expose('x', { y: 1 }, { scope: true }); + } + }; + + await server.register(plugin); + expect(server.plugins['@hapi/test1'].x.y).to.equal(1); + expect(server.registrations).to.equal({ '@hapi/test1': { version: '1.0.0', name: '@hapi/test1', options: undefined } }); + }); + + it('exposes an api (rewrites scope)', async () => { + + const server = Hapi.server(); + + const plugin = { + name: '@hapi/test1', + version: '1.0.0', + register: function (srv, options) { + + srv.expose('x', { y: 1 }, { scope: 'underscore' }); + } + }; + + await server.register(plugin); + expect(server.plugins.hapi__test1.x.y).to.equal(1); + expect(server.registrations).to.equal({ '@hapi/test1': { version: '1.0.0', name: '@hapi/test1', options: undefined } }); + }); }); describe('ext()', () => {