Skip to content

Commit

Permalink
fix: generate types from JSDoc (#240)
Browse files Browse the repository at this point in the history
  • Loading branch information
wkillerud authored May 13, 2024
1 parent 4272686 commit 015537a
Show file tree
Hide file tree
Showing 21 changed files with 237 additions and 104 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ node_modules/
.vscode
package-lock.json
dist/
.tap
.tap
types/
55 changes: 0 additions & 55 deletions benchmark/benchmark.js

This file was deleted.

118 changes: 97 additions & 21 deletions lib/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,73 @@ import Debug from './get-debug.js';

const PREFIX = 'podium';

/**
* Options passed on to the built-in context parsers.
*
* @typedef {object} PodiumContextOptions
* @property {string} name
* @property {object} [logger]
* @property {import('./get-public-pathname.js').PodiumContextPublicPathnameParserOptions} [publicPathname={}]
* @property {import('./get-mount-pathname.js').PodiumContextMountPathnameParserOptions} [mountPathname={}]
* @property {import('./get-mount-origin.js').PodiumContextMountOriginParserOptions} [mountOrigin={}]
* @property {import('./get-device-type.js').PodiumContextDeviceTypeParserOptions} [deviceType={}]
* @property {import('./get-locale.js').PodiumContextLocaleParserOptions} [locale={}]
* @property {import('./get-debug.js').PodiumContextDebugParserOptions} [debug={}]
*/

/**
* @typedef {object} PodiumContextParser
* @property {(incoming?: import('@podium/utils').PodiumHttpIncoming) => string | ((name: string) => string) | Promise<string> | ((name: string) => Promise<string>)} parse
*/

export default class PodiumContext {
constructor({
name = undefined,
logger = undefined,
publicPathname = {},
mountPathname = {},
mountOrigin = {},
deviceType = {},
locale = {},
debug = {},
} = {}) {
if (schemas.name(name).error)
/**
* @readonly
* @type {import('abslog').ValidLogger}
*/
log;

/**
* @readonly
* @type {import('@metrics/client')}
*/
metrics;

/**
* @readonly
* @type {import('@metrics/client').MetricsHistogram}
*/
histogram;

/**
* @readonly
* @type {Map<string, PodiumContextParser>}
*/
parsers;

/**
* @constructor
* @param {PodiumContextOptions} options
*/
constructor(
options = {
name: undefined,
logger: undefined,
publicPathname: {},
mountPathname: {},
mountOrigin: {},
deviceType: {},
locale: {},
debug: {},
},
) {
if (schemas.name(options.name).error)
throw new Error(
`The value, "${name}", for the required argument "name" on the Context constructor is not defined or not valid.`,
`The value, "${options.name}", for the required argument "name" on the Context constructor is not defined or not valid.`,
);

Object.defineProperty(this, 'log', {
value: abslog(logger),
value: abslog(options.logger),
});

Object.defineProperty(this, 'parsers', {
Expand All @@ -57,19 +106,31 @@ export default class PodiumContext {
buckets: [0.001, 0.01, 0.1, 0.5, 1],
});

this.register('publicPathname', new PublicPathname(publicPathname));
this.register('mountPathname', new MountPathname(mountPathname));
this.register('mountOrigin', new MountOrigin(mountOrigin));
this.register('requestedBy', new RequestedBy({ name }));
this.register('deviceType', new DeviceType(deviceType));
this.register('locale', new Locale(locale));
this.register('debug', new Debug(debug));
this.register(
'publicPathname',
new PublicPathname(options.publicPathname),
);
this.register(
'mountPathname',
new MountPathname(options.mountPathname),
);
this.register('mountOrigin', new MountOrigin(options.mountOrigin));
this.register('requestedBy', new RequestedBy({ name: options.name }));
this.register('deviceType', new DeviceType(options.deviceType));
this.register('locale', new Locale(options.locale));
this.register('debug', new Debug(options.debug));
}

get [Symbol.toStringTag]() {
return 'PodiumContext';
}

/**
* Register a context parser that will run on each incoming request.
*
* @param {string} name
* @param {PodiumContextParser} parser
*/
register(name, parser) {
assert(name, 'You must provide a value to "name".');
assert(parser, 'You must provide a value to "parser".');
Expand All @@ -90,6 +151,12 @@ export default class PodiumContext {
this.parsers.set(name, parser);
}

/**
* Process an incoming request. This will run all registered context parsers.
*
* @param {import('@podium/utils').HttpIncoming} incoming
* @returns {Promise<import('@podium/utils').HttpIncoming>} enriched with the result of each context parser
*/
process(incoming) {
if (
Object.prototype.toString.call(incoming) !==
Expand All @@ -116,15 +183,24 @@ export default class PodiumContext {
});
}

/**
* @param {object} [headers]
* @param {object} [context]
* @param {unknown} [name]
* @returns {object}
*/
static serialize(headers, context, name) {
return utils.serializeContext(headers, context, name);
}

/**
* @returns {(req: { headers: Record<string, string | string[]>}, res: object, next: () => void) => void}
*/
static deserialize() {
return (req, res, next) => {
const context = utils.deserializeContext(req.headers, PREFIX);
utils.setAtLocalsPodium(res, 'context', context);
next();
};
}
};
}
12 changes: 11 additions & 1 deletion lib/get-debug.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import assert from 'assert';

/**
* @typedef {object} PodiumContextDebugParserOptions
* @property {boolean} [enabled=false]
*/

export default class PodiumContextDebugParser {
/**
* @constructor
* @param {PodiumContextDebugParserOptions} [options]
*/
constructor({ enabled = false } = {}) {
assert.strictEqual(
typeof enabled,
Expand All @@ -18,6 +27,7 @@ export default class PodiumContextDebugParser {
}

parse() {
// @ts-expect-error because of Object.defineProperty
return this.default;
}
};
}
20 changes: 19 additions & 1 deletion lib/get-device-type.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,22 @@ import { LRUCache } from 'lru-cache';

const symCapabilitiesToType = Symbol('_capabilitiesToType');

/**
* @typedef {object} PodiumContextDeviceTypeParserOptions
* @property {number} [cacheSize=10000]
*/

export default class PodiumContextDeviceTypeParser {
/**
* @readonly
* @type {import('lru-cache').LRUCache}
*/
cache;

/**
* @constructor
* @param {PodiumContextDeviceTypeParserOptions} [options]
*/
constructor({ cacheSize = 10000 } = {}) {
Object.defineProperty(this, 'default', {
value: 'desktop',
Expand All @@ -15,6 +30,7 @@ export default class PodiumContextDeviceTypeParser {

Object.defineProperty(this, 'cache', {
value: new LRUCache({
// @ts-expect-error because of Object.defineProperty
max: this.cacheSize,
}),
});
Expand All @@ -31,6 +47,7 @@ export default class PodiumContextDeviceTypeParser {
if (platformType === 'tablet') {
return 'tablet';
}
// @ts-expect-error because of Object.defineProperty
return this.default;
}

Expand All @@ -40,6 +57,7 @@ export default class PodiumContextDeviceTypeParser {
: '';

if (!userAgent || userAgent === '') {
// @ts-expect-error because of Object.defineProperty
return this.default;
}

Expand All @@ -62,4 +80,4 @@ export default class PodiumContextDeviceTypeParser {
cacheItems: this.cache.size,
};
}
};
}
12 changes: 11 additions & 1 deletion lib/get-locale.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import assert from 'assert';
import bcp47 from 'bcp47-validate';

/**
* @typedef {object} PodiumContextLocaleParserOptions
* @property {string} [locale='en-US']
*/

export default class PodiumContextLocaleParser {
/**
* @constructor
* @param {PodiumContextLocaleParserOptions} options
*/
constructor({ locale = 'en-US' } = {}) {
assert.strictEqual(
bcp47.validate(locale),
Expand All @@ -23,6 +32,7 @@ export default class PodiumContextLocaleParser {
return incoming.params.locale;
}

// @ts-expect-error because of Object.defineProperty
return this.locale;
}
};
}
13 changes: 12 additions & 1 deletion lib/get-mount-origin.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { URL } from 'url';

/**
* @typedef {object} PodiumContextMountOriginParserOptions
* @property {string | null} [origin=null]
*/

export default class PodiumContextMountOriginParser {
/**
* @constructor
* @param {PodiumContextMountOriginParserOptions} options
*/
constructor({ origin = null } = {}) {
Object.defineProperty(this, 'defaultOrigin', {
value: origin ? new URL(origin) : {},
Expand All @@ -15,7 +24,9 @@ export default class PodiumContextMountOriginParser {
let { protocol, hostname } = incoming.url;
let port = incoming.url.port ? incoming.url.port.toString() : '';

// @ts-expect-error because of Object.defineProperty
if (this.defaultOrigin.hostname) {
// @ts-expect-error because of Object.defineProperty
({ hostname, protocol, port } = this.defaultOrigin);
}

Expand All @@ -29,4 +40,4 @@ export default class PodiumContextMountOriginParser {

return `${protocol}//${hostname}${port}`;
}
};
}
12 changes: 11 additions & 1 deletion lib/get-mount-pathname.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
import { pathnameBuilder } from '@podium/utils';

/**
* @typedef {object} PodiumContextMountPathnameParserOptions
* @property {string} [pathname='/']
*/

export default class PodiumContextMountPathnameParser {
/**
* @constructor
* @param {PodiumContextMountPathnameParserOptions} options
*/
constructor({ pathname = '/' } = {}) {
Object.defineProperty(this, 'pathname', {
value: pathname,
Expand All @@ -12,6 +21,7 @@ export default class PodiumContextMountPathnameParser {
}

parse() {
// @ts-expect-error because of Object.defineProperty
return pathnameBuilder(this.pathname);
}
};
}
Loading

0 comments on commit 015537a

Please sign in to comment.