Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: generate types from JSDoc #377

Merged
merged 4 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ tmp/**/*
.idea
.idea/**/*
coverage
dist/
.tap
types/
.tap
140 changes: 0 additions & 140 deletions client.d.ts

This file was deleted.

3 changes: 0 additions & 3 deletions dist/package.json

This file was deleted.

85 changes: 68 additions & 17 deletions lib/client.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,80 @@
import EventEmitter from 'events';
import {uriStrict as validateUriStrict, name as validateName } from'@podium/schemas';
import {
uriStrict as validateUriStrict,
name as validateName,
} from '@podium/schemas';
import Metrics from '@metrics/client';
import abslog from 'abslog';
import Cache from 'ttl-mem-cache';
import http from 'http';
import https from 'https';

import Resource from './resource.js';
import State from './state.js';

const inspect = Symbol.for('nodejs.util.inspect.custom');

const HTTP_AGENT_OPTIONS = {
keepAlive: true,
maxSockets: 10,
maxFreeSockets: 10,
timeout: 60000,
keepAliveMsecs: 30000,
};

const HTTPS_AGENT_OPTIONS = {
...HTTP_AGENT_OPTIONS,
maxCachedSessions: 10,
};

const REJECT_UNAUTHORIZED = true;

const HTTP_AGENT = new http.Agent(HTTP_AGENT_OPTIONS);

const HTTPS_AGENT = new https.Agent(HTTPS_AGENT_OPTIONS);

const RETRIES = 4;

const TIMEOUT = 1000; // 1 seconds

const MAX_AGE = Infinity;

/**
* @typedef {import('./resource.js').default} PodiumClientResource
* @typedef {import('./resource.js').PodiumClientResourceOptions} PodiumClientResourceOptions
* @typedef {import('./response.js').default} PodiumClientResponse
* @typedef {import('./http-outgoing.js').PodiumRedirect} PodiumRedirect
* @typedef {import('@podium/schemas').PodletManifestSchema} PodletManifest
*/

/**
* @typedef {object} PodiumClientOptions
* @property {string} name
* @property {import('abslog').AbstractLoggerOptions} [logger]
* @property {number} [retries=4]
* @property {number} [timeout=1000] In milliseconds
* @property {number} [maxAge=Infinity]
* @property {boolean} [rejectUnauthorized=true]
* @property {number} [resolveThreshold]
* @property {number} [resolveMax]
* @property {import('http').Agent} [httpAgent]
* @property {import('https').Agent} [httpsAgent]
*/

/**
* @typedef {object} RegisterOptions
* @property {string} name A unique name for the podlet
* @property {string} uri URL to the podlet's `manifest.json`
* @property {number} [retries=4] Number of retries before serving fallback
* @property {number} [timeout=1000] In milliseconds, the amount of time to wait before serving fallback.
* @property {boolean} [throwable=false] Set to `true` and surround `fetch` in `try/catch` to serve different content in case podlet is unavailable. Will not server fallback content.
* @property {boolean} [redirectable=false] Set to `true` to allow podlet to respond with a redirect. You need to look for the redirect response from the podlet and return a redirect response to the browser yourself.
*/

export default class PodiumClient extends EventEmitter {
#resources;
#registry;
#metrics;
#histogram;
#options;
#state;

/**
* @constructor
* @param {PodiumClientOptions} options
*/
// @ts-expect-error Deliberate default empty options for better error messages
constructor(options = {}) {
super();
const log = abslog(options.logger);
Expand All @@ -69,7 +101,7 @@ export default class PodiumClient extends EventEmitter {
resolveThreshold: options.resolveThreshold,
resolveMax: options.resolveMax,
});
this.#state.on('state', state => {
this.#state.on('state', (state) => {
this.emit('state', state);
});

Expand All @@ -79,7 +111,7 @@ export default class PodiumClient extends EventEmitter {
changefeed: true,
ttl: options.maxAge,
});
this.#registry.on('error', error => {
this.#registry.on('error', (error) => {
log.error(
'Error emitted by the registry in @podium/client module',
error,
Expand All @@ -91,15 +123,15 @@ export default class PodiumClient extends EventEmitter {
});

this.#metrics = new Metrics();
this.#metrics.on('error', error => {
this.#metrics.on('error', (error) => {
log.error(
'Error emitted by metric stream in @podium/client module',
error,
);
});

this[Symbol.iterator] = () => ({
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this Symbol.iterator API is possible to type

items: Array.from(this.#resources).map(item => item[1]),
items: Array.from(this.#resources).map((item) => item[1]),
next: function next() {
return {
done: this.items.length === 0,
Expand Down Expand Up @@ -128,7 +160,21 @@ export default class PodiumClient extends EventEmitter {
return this.#state.status;
}

register(options = {}) {
/**
* Register a podlet so you can fetch its contents later with {@link PodiumClientResource.fetch}.
*
* @param {RegisterOptions} options
* @returns {PodiumClientResource}
*
* @example
* ```js
* const headerPodlet = layout.client.register({
* name: 'header',
* uri: 'http://header/manifest.json',
* });
* ```
*/
register(options) {
if (validateName(options.name).error)
throw new Error(
`The value, "${options.name}", for the required argument "name" on the .register() method is not defined or not valid.`,
Expand Down Expand Up @@ -183,12 +229,17 @@ export default class PodiumClient extends EventEmitter {
return this.#registry.load(dump);
}

/**
* Refreshes the cached podlet manifest for all {@link register}ed podlets.
*/
async refreshManifests() {
const end = this.#histogram.timer();

// Don't return this
await Promise.all(
Array.from(this.#resources).map(resource => resource[1].refresh()),
Array.from(this.#resources).map((resource) =>
resource[1].refresh(),
),
);

end();
Expand All @@ -204,4 +255,4 @@ export default class PodiumClient extends EventEmitter {
get [Symbol.toStringTag]() {
return 'PodiumClient';
}
};
}
Loading