Skip to content

Commit

Permalink
Enhance plugin types (#4400)
Browse files Browse the repository at this point in the history
* Add types from DT

* misc

* Restore log in example

* Add missing info.remote flag

* Split definition file into smaller files

* Split server definition into smaller files

* Add mime type

* Support customizing server.app per server

* Small cleanup of types

* Add return value to server.register()

* Remove undefined as value for optional props

* Remove jsonp

* Support plugin server type annotation

* Allow typing event data

* Add custom request.app test

* Fix request.server

* Allow custom typing of requests

* Fix bad merge

* Revert removal of | undefined (more)

* Update lib/types/index.d.ts

Co-authored-by: Nicolas Morel <nicolas@morel.io>

* Update lib/types/index.d.ts

* Revert removal of | undefined

* Remove plugin decoration defaults (WIP)

* Temporary solution to plugin template match

* Update lib/types/plugin.d.ts

Co-authored-by: Nicolas Morel <nicolas@morel.io>

Co-authored-by: Nicolas Morel <nicolas@morel.io>
  • Loading branch information
hueniverse and Marsup authored Dec 22, 2022
1 parent 2878456 commit 9db3662
Show file tree
Hide file tree
Showing 18 changed files with 247 additions and 210 deletions.
2 changes: 1 addition & 1 deletion API.md
Original file line number Diff line number Diff line change
Expand Up @@ -2224,7 +2224,7 @@ Registers a plugin where:
- `vhost` - virtual host string (or array of strings) applied to every route. The
outer-most `vhost` overrides the any nested configuration.

Return value: none.
Return value: a reference to the `server`.

```js
async function example() {
Expand Down
2 changes: 2 additions & 0 deletions lib/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,8 @@ internals.Server = class {
finally {
--this._core.registring;
}

return this;
}

route(options) {
Expand Down
14 changes: 14 additions & 0 deletions lib/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,17 @@ export * from './request';
export * from './route';
export * from './server';
export * from './utils';

// Kept for backwards compatibility only (remove in next major)

export namespace Utils {
interface Dictionary<T> {
[key: string]: T;
}

type HTTP_METHODS_PARTIAL_LOWERCASE = 'get' | 'post' | 'put' | 'patch' | 'delete' | 'options';

type HTTP_METHODS_PARTIAL = Uppercase<HTTP_METHODS_PARTIAL_LOWERCASE> | HTTP_METHODS_PARTIAL_LOWERCASE;

type HTTP_METHODS = 'HEAD' | 'head' | HTTP_METHODS_PARTIAL;
}
77 changes: 49 additions & 28 deletions lib/types/plugin.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ import { Lifecycle } from './utils';
* {@link https://www.npmjs.com/package/semver version range string} which must match the registered
* plugin version.
*/
export type Dependencies = string | string[] | {
[key: string]: string;
};
export type Dependencies = string | string[] | Record<string, string>;

/**
* [See docs](https://github.com/hapijs/hapi/blob/master/API.md#-serverregistrations)
Expand Down Expand Up @@ -71,19 +69,10 @@ export interface PluginPackage {
/**
* Alternatively, the name and version can be included via the pkg property containing the 'package.json' file for the module which already has the name and version included
*/
pkg: any;
pkg: PluginNameVersion;
}

/**
* Plugins provide a way to organize application code by splitting the server logic into smaller components. Each
* plugin can manipulate the server through the standard server interface, but with the added ability to sandbox
* certain properties. For example, setting a file path in one plugin doesn't affect the file path set
* in another plugin.
* [See docs](https://github.com/hapijs/hapi/blob/master/API.md#plugins)
*
* The type T is the type of the plugin options.
*/
export interface PluginBase<T> {
export interface PluginBase<T, D> {
/**
* (required) the registration function with the signature async function(server, options) where:
* * server - the server object with a plugin-specific server.realm.
Expand All @@ -108,9 +97,26 @@ export interface PluginBase<T> {

/** once - (optional) if true, will only register the plugin once per server. If set, overrides the once option passed to server.register(). Defaults to no override. */
once?: boolean | undefined;

/**
* We need to use D within the PluginBase type to be able to infer it later on,
* but this property has no concrete existence in the code.
*
* See https://github.com/Microsoft/TypeScript/wiki/FAQ#why-doesnt-type-inference-work-on-this-interface-interface-foot-- for details.
*/
___$type_of_plugin_decorations$___?: D;
}

export type Plugin<T> = PluginBase<T> & (PluginNameVersion | PluginPackage);
/**
* Plugins provide a way to organize application code by splitting the server logic into smaller components. Each
* plugin can manipulate the server through the standard server interface, but with the added ability to sandbox
* certain properties. For example, setting a file path in one plugin doesn't affect the file path set
* in another plugin.
* [See docs](https://github.com/hapijs/hapi/blob/master/API.md#plugins)
*
* The type T is the type of the plugin options.
*/
export type Plugin<T, D = void> = PluginBase<T, D> & (PluginNameVersion | PluginPackage);

/**
* The realm object contains sandboxed server settings specific to each plugin or authentication strategy. When registering a plugin or an authentication scheme, a server object reference is provided
Expand Down Expand Up @@ -182,9 +188,31 @@ export interface ServerRegisterOptions {
} | undefined;
}

export interface ServerRegisterPluginObjectDirect<T, D> extends ServerRegisterOptions {
/**
* a plugin object.
*/
plugin: Plugin<T, D>;
/**
* options passed to the plugin during registration.
*/
options?: T | undefined;
}

export interface ServerRegisterPluginObjectWrapped<T, D> extends ServerRegisterOptions {
/**
* a plugin object.
*/
plugin: { plugin: Plugin<T, D> };
/**
* options passed to the plugin during registration.
*/
options?: T | undefined;
}

/**
* An object with the following:
* * plugin - a plugin object.
* * plugin - a plugin object or a wrapped plugin loaded module.
* * options - (optional) options passed to the plugin during registration.
* * once - if true, subsequent registrations of the same plugin are skipped without error. Cannot be used with plugin options. Defaults to false. If not set to true, an error will be thrown the
* second time a plugin is registered on the server.
Expand All @@ -196,26 +224,19 @@ export interface ServerRegisterOptions {
*
* The type parameter T is the type of the plugin configuration options.
*/
export interface ServerRegisterPluginObject<T> extends ServerRegisterOptions {
/**
* a plugin object.
*/
plugin: Plugin<T>;
/**
* options passed to the plugin during registration.
*/
options?: T | undefined;
}
export type ServerRegisterPluginObject<T, D = void> =
ServerRegisterPluginObjectDirect<T, D> |
ServerRegisterPluginObjectWrapped<T, D>;

export type ServerRegisterPluginObjectArray<T, U, V, W, X, Y, Z> = Array<
export type ServerRegisterPluginObjectArray<T, U, V, W, X, Y, Z> = (
ServerRegisterPluginObject<T> |
ServerRegisterPluginObject<U> |
ServerRegisterPluginObject<V> |
ServerRegisterPluginObject<W> |
ServerRegisterPluginObject<X> |
ServerRegisterPluginObject<Y> |
ServerRegisterPluginObject<Z>
>;
)[];

/**
* The method function can have a defaults object or function property. If the property is set to an object, that object is used as the default route config for routes using this handler.
Expand Down
48 changes: 31 additions & 17 deletions lib/types/request.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import { PluginsStates, ServerRealm } from './plugin';
import { ResponseObject } from '@hapi/shot';
import { ResponseValue } from './response';
import { RouteRules, RouteSettings } from './route';
import { ServerAuthSchemeObjectApi } from './server';
import { PeekListener, RequestApplicationState, Utils } from './utils';
import { Server, ServerAuthSchemeObjectApi } from './server';
import { HTTP_METHODS_PARTIAL, HTTP_METHODS_PARTIAL_LOWERCASE, PeekListener } from './utils';

/**
* User extensible types user credentials.
Expand Down Expand Up @@ -191,9 +191,9 @@ export interface RequestInfo {
* * fingerprint - the route internal normalized string representing the normalized path.
* [See docs](https://github.com/hapijs/hapi/blob/master/API.md#-requestroute)
*/
export interface RequestRoute {
export interface RequestRoute<Refs extends ReqRef = ReqRefDefaults> {
/** the route HTTP method. */
method: Utils.HTTP_METHODS_PARTIAL;
method: HTTP_METHODS_PARTIAL;

/** the route path. */
path: string;
Expand All @@ -205,7 +205,7 @@ export interface RequestRoute {
realm: ServerRealm;

/** the route options object with all defaults applied. */
settings: RouteSettings;
settings: RouteSettings<Refs>;

/** the route internal normalized string representing the normalized path. */
fingerprint: string;
Expand Down Expand Up @@ -247,12 +247,25 @@ export interface RequestQuery {
[key: string]: any;
}

/**
* Empty interface to allow for user-defined augmentations.
*/
export interface RouteOptionsApp {}

/**
* User-extensible type for application specific state on requests (`request.app`).
*/
export interface RequestApplicationState {
}

export interface InternalRequestDefaults {
Server: Server;

Payload: stream.Readable | Buffer | string | object;
Query: RequestQuery;
Params: Utils.Dictionary<any>;
Pres: Utils.Dictionary<any>;
Headers: Utils.Dictionary<any>;
Params: Record<string, any>;
Pres: Record<string, any>;
Headers: Record<string, any>;
RequestApp: RequestApplicationState;

AuthUser: UserCredentials;
Expand All @@ -263,6 +276,7 @@ export interface InternalRequestDefaults {

Rules: RouteRules;
Bind: object | null;
RouteApp: RouteOptionsApp;
}

/**
Expand Down Expand Up @@ -361,7 +375,7 @@ export interface Request<Refs extends ReqRef = ReqRefDefaults> extends Podium {
/**
* The request method in lower case (e.g. 'get', 'post').
*/
readonly method: Utils.HTTP_METHODS_PARTIAL_LOWERCASE;
readonly method: HTTP_METHODS_PARTIAL_LOWERCASE;

/**
* The parsed content-type header. Only available when payload parsing enabled and no payload error occurred.
Expand Down Expand Up @@ -416,7 +430,7 @@ export interface Request<Refs extends ReqRef = ReqRefDefaults> extends Podium {
/**
* Same as pre but represented as the response object created by the pre method.
*/
readonly preResponses: Utils.Dictionary<any>;
readonly preResponses: Record<string, any>;

/**
* By default the object outputted from node's URL parse() method.
Expand All @@ -438,18 +452,18 @@ export interface Request<Refs extends ReqRef = ReqRefDefaults> extends Podium {
* [See docs](https://github.com/hapijs/hapi/blob/master/API.md#-requestroute)
* [See docs](https://github.com/hapijs/hapi/blob/master/API.md#-requestrouteauthaccessrequest)
*/
readonly route: RequestRoute;
readonly route: RequestRoute<Refs>;

/**
* Access: read only and the public server interface.
* The server object.
*/
server: http.Server;
readonly server: MergeRefs<Refs>['Server'];

/**
* An object containing parsed HTTP state information (cookies) where each key is the cookie name and value is the matching cookie content after processing using any registered cookie definition.
*/
readonly state: Utils.Dictionary<any>;
readonly state: Record<string, any>;

/**
* The parsed request URI.
Expand All @@ -472,7 +486,7 @@ export interface Request<Refs extends ReqRef = ReqRefDefaults> extends Podium {
* [See docs](https://github.com/hapijs/hapi/blob/master/API.md#-requestgenerateresponsesource-options)
*/
/* tslint:disable-next-line:max-line-length */
generateResponse(source: string | object | null, options?: { variety?: string | undefined; prepare?: ((response: ResponseObject) => Promise<ResponseObject>) | undefined; marshal?: ((response: ResponseObject) => Promise<ResponseValue>) | undefined; close?: ((response: ResponseObject) => void | undefined); }): ResponseObject;
generateResponse(source: string | object | null, options?: { variety?: string | undefined; prepare?: ((response: ResponseObject) => Promise<ResponseObject>) | undefined; marshal?: ((response: ResponseObject) => Promise<ResponseValue>) | undefined; close?: ((response: ResponseObject) => void) | undefined; } | undefined): ResponseObject;

/**
* Logs request-specific events. When called, the server emits a 'request' event which can be used by other listeners or plugins. The arguments are:
Expand All @@ -484,7 +498,7 @@ export interface Request<Refs extends ReqRef = ReqRefDefaults> extends Podium {
* @return void
* [See docs](https://github.com/hapijs/hapi/blob/master/API.md#-requestlogtags-data)
*/
log(tags: string | string[], data?: string | object | (() => string | object)): void;
log(tags: string | string[], data?: string | object | (() => string | object) | undefined): void;

/**
* Changes the request method before the router begins processing the request where:
Expand All @@ -493,7 +507,7 @@ export interface Request<Refs extends ReqRef = ReqRefDefaults> extends Podium {
* Can only be called from an 'onRequest' extension method.
* [See docs](https://hapijs.com/api/17.0.1#-requestsetmethodmethod)
*/
setMethod(method: Utils.HTTP_METHODS_PARTIAL): void;
setMethod(method: HTTP_METHODS_PARTIAL): void;

/**
* Changes the request URI before the router begins processing the request where:
Expand All @@ -504,5 +518,5 @@ export interface Request<Refs extends ReqRef = ReqRefDefaults> extends Podium {
* @return void
* [See docs](https://hapijs.com/api/17.0.1#-requestseturlurl-striptrailingslash)
*/
setUrl(url: string | url.URL, stripTrailingSlash?: boolean): void;
setUrl(url: string | url.URL, stripTrailingSlash?: boolean | undefined): void;
}
Loading

0 comments on commit 9db3662

Please sign in to comment.