Compare commits

...

6 commits

Author SHA1 Message Date
e3a345347e
chore: update license years 2024-08-09 16:39:51 -05:00
c3be93e6c4
chore: build latest 2024-08-09 16:37:12 -05:00
4cde62af13 chore: update dependencies 2024-08-09 16:35:18 -05:00
250fb4b4e9 chore: updating dependencies and node versions
* Updated supported Node.js versions

Dropped support for 14 and 16 in favor of the current LTS versions 18
and 20.

* Updated Node.js dependencies

Bumped nearly all dependencies to the latest. The exception being
`node-fetch` which made the jump to using ESM which is still causing
folks a lot of grief.

* Shored code coverage back up to 100%

Had some remnants from when this library did sanity checks pre-flight to
the API that required additional tests to cover them, or just adjusting
the argument defaults.

* Removed the "build" badge from the README

Seems there were some gotchas with that badge that makes it kind of a
pain, as it has to be hard coded a certain way. I messed with it and
couldn't get it working, seems like more trouble than it's worth.

Further details: https://github.com/badges/shields/issues/8671

* Added .nvmrc file

Pretty self explanatory.

* Bumped version number

Lucky number 7, y'all!
2023-10-26 17:19:54 -05:00
5a62ac8b5f fix: forgot to rebuild 2022-04-06 08:50:54 -05:00
046aadeea4 feat: get previous workday
This started as a feature request for our upstream API (closes #33) that
led to a bit of rework to help make the library a bit more flexible in
terms of not needing modification when something on the API changes.

This update also includes your typical round of dependency updates and a
bit of preemptive planning by way of dropping support for Node.js as
we're on the cusp of it being out of maintenance and the release of
Node.js v18.
2022-04-05 17:48:55 -05:00
12 changed files with 3064 additions and 9793 deletions

View file

@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: ['12', '14', '16']
node-version: ['18', '20', '22']
steps:
- name: Checkout Code
uses: actions/checkout@v2
@ -25,7 +25,7 @@ jobs:
- name: Run Tests
run: npm run test:coverage
- name: Upload Coverage
if: ${{ matrix.node-version == '16' }}
if: ${{ matrix.node-version == '20' }}
uses: codecov/codecov-action@v1
with:
file: ./coverage/lcov.info

1
.nvmrc Normal file
View file

@ -0,0 +1 @@
v20

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2016, 2017, 2018, 2019, 2020 Gravity Boulevard, LLC
Copyright (c) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024 Gravity Boulevard, LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -2,7 +2,6 @@
[![License](https://img.shields.io/npm/l/holidayapi-node?style=for-the-badge)](https://github.com/holidayapi/holidayapi-node/blob/master/LICENSE)
![Node Version](https://img.shields.io/node/v/holidayapi?style=for-the-badge)
[![Test Status](https://img.shields.io/github/workflow/status/holidayapi/holidayapi-node/Test?style=for-the-badge)](https://github.com/holidayapi/holidayapi-node/actions)
[![Code Coverage](https://img.shields.io/codecov/c/github/holidayapi/holidayapi-node?style=for-the-badge)](https://codecov.io/gh/holidayapi/holidayapi-node)
Official Node.js library for [Holiday API](https://holidayapi.com) providing

48
dist/holidayapi.js vendored
View file

@ -25,7 +25,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
@ -49,28 +49,27 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
Object.defineProperty(exports, "__esModule", { value: true });
exports.HolidayAPI = void 0;
var node_fetch_1 = require("node-fetch");
var url_1 = require("url");
var HolidayAPI = (function () {
function HolidayAPI(_a) {
var key = _a.key, _b = _a.version, version = _b === void 0 ? 1 : _b;
var getYours = 'get yours at HolidayAPI.com';
var uuidRegExp = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/;
if (!key) {
throw new Error("Missing API key, " + getYours);
throw new Error("Missing API key, ".concat(getYours));
}
if (!uuidRegExp.test(key)) {
throw new Error("Invalid API key, " + getYours);
throw new Error("Invalid API key, ".concat(getYours));
}
if (version !== 1) {
throw new Error('Invalid version number, expected "1"');
}
this.baseUrl = "https://holidayapi.com/v" + version + "/";
this.baseUrl = "https://holidayapi.com/v".concat(version, "/");
this.key = key;
}
HolidayAPI.prototype.createUrl = function (endpoint, request) {
var parameters = __assign({ key: this.key }, request);
var url = new url_1.URL(endpoint, this.baseUrl);
url.search = new url_1.URLSearchParams(parameters).toString();
var url = new URL(endpoint, this.baseUrl);
url.search = new URLSearchParams(parameters).toString();
return url.toString();
};
HolidayAPI.prototype.request = function (endpoint, request) {
@ -78,7 +77,7 @@ var HolidayAPI = (function () {
var response, payload, err_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4, node_fetch_1.default(this.createUrl(endpoint, request))];
case 0: return [4, (0, node_fetch_1.default)(this.createUrl(endpoint, request))];
case 1:
response = _a.sent();
_a.label = 2;
@ -109,18 +108,8 @@ var HolidayAPI = (function () {
});
};
HolidayAPI.prototype.holidays = function (request) {
if (request === void 0) { request = {}; }
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
if (!request.country) {
throw new Error('Missing country');
}
else if (!request.year) {
throw new Error('Missing year');
}
else if (request.previous && request.upcoming) {
throw new Error('Previous and upcoming are mutually exclusive');
}
return [2, this.request('holidays', request)];
});
});
@ -133,38 +122,15 @@ var HolidayAPI = (function () {
});
};
HolidayAPI.prototype.workday = function (request) {
if (request === void 0) { request = {}; }
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
if (!request.country) {
throw new Error('Missing country');
}
else if (!request.start) {
throw new Error('Missing start date');
}
else if (!request.days) {
throw new Error('Missing days');
}
else if (request.days < 1) {
throw new Error('Days must be 1 or more');
}
return [2, this.request('workday', request)];
});
});
};
HolidayAPI.prototype.workdays = function (request) {
if (request === void 0) { request = {}; }
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
if (!request.country) {
throw new Error('Missing country');
}
else if (!request.start) {
throw new Error('Missing start date');
}
else if (!request.end) {
throw new Error('Missing end date');
}
return [2, this.request('workdays', request)];
});
});

6
dist/index.js vendored
View file

@ -1,7 +1,11 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];

32
dist/types.d.ts vendored
View file

@ -1,19 +1,19 @@
export declare type Endpoint = 'countries' | 'holidays' | 'languages' | 'workday' | 'workdays';
export declare type Weekday = {
export type Endpoint = 'countries' | 'holidays' | 'languages' | 'workday' | 'workdays';
export type Weekday = {
name: 'Monday' | 'Tuesday' | 'Wednesday' | 'Thursday' | 'Friday' | 'Saturday' | 'Sunday';
numeric: 1 | 2 | 3 | 4 | 5 | 6 | 7;
};
declare type Request = {
type Request = {
format?: 'csv' | 'json' | 'php' | 'tsv' | 'xml' | 'yaml';
key?: string;
pretty?: boolean;
search?: string;
};
export declare type CountriesRequest = Request & {
export type CountriesRequest = Request & {
country?: string;
public?: boolean;
};
export declare type HolidaysRequest = Request & {
export type HolidaysRequest = Request & {
country?: string;
day?: number;
language?: string;
@ -24,20 +24,20 @@ export declare type HolidaysRequest = Request & {
upcoming?: boolean;
year?: number;
};
export declare type LanguagesRequest = Request & {
export type LanguagesRequest = Request & {
language?: string;
};
export declare type WorkdayRequest = Request & {
export type WorkdayRequest = Request & {
country?: string;
start?: string;
days?: number;
};
export declare type WorkdaysRequest = Request & {
export type WorkdaysRequest = Request & {
country?: string;
start?: string;
end?: string;
};
export declare type Response = {
export type Response = {
requests: {
available: number;
resets: string;
@ -46,7 +46,7 @@ export declare type Response = {
status: number;
error?: string;
};
export declare type CountriesResponse = Response & {
export type CountriesResponse = Response & {
countries?: {
code: string;
codes: {
@ -68,7 +68,7 @@ export declare type CountriesResponse = Response & {
};
}[];
};
export declare type HolidaysResponse = Response & {
export type HolidaysResponse = Response & {
holidays?: {
country: string;
date: string;
@ -79,21 +79,21 @@ export declare type HolidaysResponse = Response & {
subdivisions?: string[];
}[];
};
export declare type LanguagesResponse = Response & {
export type LanguagesResponse = Response & {
languages?: {
code: string;
name: string;
}[];
};
export declare type WorkdayResponse = Response & {
export type WorkdayResponse = Response & {
workday?: {
date: string;
weekday: Weekday;
};
};
export declare type WorkdaysResponse = Response & {
export type WorkdaysResponse = Response & {
workdays?: number;
};
export declare type Requests = (CountriesRequest | HolidaysRequest | LanguagesRequest | WorkdayRequest | WorkdaysRequest);
export declare type Responses = (CountriesResponse | HolidaysResponse | LanguagesResponse | WorkdayResponse | WorkdaysResponse);
export type Requests = (CountriesRequest | HolidaysRequest | LanguagesRequest | WorkdayRequest | WorkdaysRequest);
export type Responses = (CountriesResponse | HolidaysResponse | LanguagesResponse | WorkdayResponse | WorkdaysResponse);
export {};

12377
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "holidayapi",
"version": "5.1.0",
"version": "7.1.0",
"description": "Official Node.js library for Holiday API",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@ -21,26 +21,26 @@
},
"homepage": "https://holidayapi.com",
"engines": {
"node": ">= 12.0.0"
"node": ">= 18.0.0"
},
"devDependencies": {
"@types/jest": "^26.0.19",
"@types/jest": "^29.5.6",
"@types/nock": "^11.1.0",
"@types/node": "^15.12.2",
"@types/node-fetch": "^2.5.7",
"@typescript-eslint/eslint-plugin": "^4.11.1",
"@typescript-eslint/parser": "^4.11.1",
"eslint": "^7.16.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.1.3",
"jest": "^27.0.4",
"nock": "^13.0.5",
"ts-jest": "^27.0.3",
"typescript": "^4.1.3"
"@types/node": "^22.2.0",
"@types/node-fetch": "^2.6.7",
"@typescript-eslint/eslint-plugin": "^6.9.0",
"@typescript-eslint/parser": "^6.9.0",
"eslint": "^8.12.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "^27.4.3",
"jest": "^29.7.0",
"nock": "^13.2.4",
"ts-jest": "^29.1.1",
"typescript": "^5.2.2"
},
"dependencies": {
"node-fetch": "^2.6.1"
"node-fetch": "^2.7.0"
},
"scripts": {
"build": "tsc",

View file

@ -6,7 +6,6 @@
*/
import fetch from 'node-fetch';
import { URL, URLSearchParams } from 'url';
import {
CountriesRequest,
CountriesResponse,
@ -56,7 +55,7 @@ export class HolidayAPI {
private async request(endpoint: Endpoint, request?: Requests): Promise<Responses> {
const response = await fetch(this.createUrl(endpoint, request));
let payload;
let payload: any;
try {
payload = await response.json();
@ -75,15 +74,7 @@ export class HolidayAPI {
return this.request('countries', request);
}
async holidays(request: HolidaysRequest = {}): Promise<HolidaysResponse> {
if (!request.country) {
throw new Error('Missing country');
} else if (!request.year) {
throw new Error('Missing year');
} else if (request.previous && request.upcoming) {
throw new Error('Previous and upcoming are mutually exclusive');
}
async holidays(request?: HolidaysRequest): Promise<HolidaysResponse> {
return this.request('holidays', request);
}
@ -91,29 +82,11 @@ export class HolidayAPI {
return this.request('languages', request);
}
async workday(request: WorkdayRequest = {}): Promise<WorkdayResponse> {
if (!request.country) {
throw new Error('Missing country');
} else if (!request.start) {
throw new Error('Missing start date');
} else if (!request.days) {
throw new Error('Missing days');
} else if (request.days < 1) {
throw new Error('Days must be 1 or more');
}
async workday(request?: WorkdayRequest): Promise<WorkdayResponse> {
return this.request('workday', request);
}
async workdays(request: WorkdaysRequest = {}): Promise<WorkdaysResponse> {
if (!request.country) {
throw new Error('Missing country');
} else if (!request.start) {
throw new Error('Missing start date');
} else if (!request.end) {
throw new Error('Missing end date');
}
async workdays(request?: WorkdaysRequest): Promise<WorkdaysResponse> {
return this.request('workdays', request);
}
}

View file

@ -93,129 +93,6 @@ describe('holidayapi', () => {
expect(await holidayapi.countries()).toStrictEqual(expectedResponse);
});
it('should return only countries with public holidays', async () => {
const expectedResponse = {
status: 200,
requests: {
used: 1000,
available: 9000,
resets: '2019-10-01 00:00:00',
},
countries: [
{
code: 'ST',
name: 'Sao Tome and Principe',
languages: ['pt'],
codes: {
'alpha-2': 'ST',
'alpha-3': 'STP',
numeric: 678,
},
flag: 'https://www.countryflags.io/ST/flat/64.png',
subdivisions: [
{
code: 'ST-P',
name: 'Príncipe',
languages: ['pt'],
},
{
code: 'ST-S',
name: 'São Tomé',
languages: ['pt'],
},
],
},
],
};
mockRequest.get(`${basePath}&public=true`).reply(200, expectedResponse);
expect(await holidayapi.countries({
public: true,
})).toStrictEqual(expectedResponse);
});
it('should return one country', async () => {
const expectedResponse = {
status: 200,
requests: {
used: 1000,
available: 9000,
resets: '2019-10-01 00:00:00',
},
countries: [
{
code: 'ST',
name: 'Sao Tome and Principe',
languages: ['pt'],
codes: {
'alpha-2': 'ST',
'alpha-3': 'STP',
numeric: 678,
},
flag: 'https://www.countryflags.io/ST/flat/64.png',
subdivisions: [
{
code: 'ST-P',
name: 'Príncipe',
languages: ['pt'],
},
{
code: 'ST-S',
name: 'São Tomé',
languages: ['pt'],
},
],
},
],
};
mockRequest.get(`${basePath}&country=ST`).reply(200, expectedResponse);
expect(await holidayapi.countries({
country: 'ST',
})).toStrictEqual(expectedResponse);
});
it('should search countries', async () => {
const expectedResponse = {
status: 200,
requests: {
used: 1000,
available: 9000,
resets: '2019-10-01 00:00:00',
},
countries: [
{
code: 'ST',
name: 'Sao Tome and Principe',
languages: ['pt'],
codes: {
'alpha-2': 'ST',
'alpha-3': 'STP',
numeric: 678,
},
flag: 'https://www.countryflags.io/ST/flat/64.png',
subdivisions: [
{
code: 'ST-P',
name: 'Príncipe',
languages: ['pt'],
},
{
code: 'ST-S',
name: 'São Tomé',
languages: ['pt'],
},
],
},
],
};
mockRequest.get(`${basePath}&search=Sao`).reply(200, expectedResponse);
expect(await holidayapi.countries({
search: 'Sao',
})).toStrictEqual(expectedResponse);
});
it('should raise 4xx errors', async () => {
const expectedResponse = {
status: 429,
@ -288,81 +165,6 @@ describe('holidayapi', () => {
})).toStrictEqual(expectedResponse);
});
it('should search holidays', async () => {
const expectedResponse = {
status: 200,
requests: {
used: 1000,
available: 9000,
resets: '2019-10-01 00:00:00',
},
holidays: [
{
name: 'Independence Day',
date: '2015-07-04',
observed: '2015-07-03',
public: true,
country: 'US',
uuid: '88268759-9b90-468c-804f-b729b8418e7c',
weekday: {
date: {
name: 'Saturday',
numeric: '6',
},
observed: {
name: 'Friday',
numeric: '5',
},
},
},
],
};
mockRequest.get(`${basePath}&country=US&year=2019&search=Independence`)
.reply(200, expectedResponse);
expect(await holidayapi.holidays({
country: 'US',
year: 2019,
search: 'Independence',
})).toStrictEqual(expectedResponse);
});
it('should error when country is missing', async () => {
expect.assertions(1);
try {
await holidayapi.holidays();
} catch (err) {
expect(err.message).toMatch(/missing country/i);
}
});
it('should error when year is missing', async () => {
expect.assertions(1);
try {
await holidayapi.holidays({ country: 'US' });
} catch (err) {
expect(err.message).toMatch(/missing year/i);
}
});
it('should error when both previous and upcoming', async () => {
expect.assertions(1);
try {
await holidayapi.holidays({
country: 'US',
year: 2019,
previous: true,
upcoming: true,
});
} catch (err) {
expect(err.message).toMatch(/previous and upcoming/i);
}
});
it('should raise 4xx errors', async () => {
const expectedResponse = {
status: 429,
@ -431,54 +233,6 @@ describe('holidayapi', () => {
expect(await holidayapi.languages()).toStrictEqual(expectedResponse);
});
it('should return one language', async () => {
const expectedResponse = {
status: 200,
requests: {
used: 1000,
available: 9000,
resets: '2019-10-01 00:00:00',
},
languages: [
{
code: 'hi',
name: 'Hindi',
},
],
};
mockRequest.get(`${basePath}&language=hi`).reply(200, expectedResponse);
expect(await holidayapi.languages({
language: 'hi',
})).toStrictEqual(expectedResponse);
});
it('should search languages', async () => {
const expectedResponse = {
status: 200,
requests: {
used: 1000,
available: 9000,
resets: '2019-10-01 00:00:00',
},
languages: [
{
code: 'hi',
name: 'Hindi',
},
{
code: 'zh',
name: 'Chinese (Simplified)',
},
],
};
mockRequest.get(`${basePath}&search=in`).reply(200, expectedResponse);
expect(await holidayapi.languages({
search: 'in',
})).toStrictEqual(expectedResponse);
});
it('should raise 4xx errors', async () => {
const expectedResponse = {
status: 429,
@ -539,46 +293,6 @@ describe('holidayapi', () => {
})).toStrictEqual(expectedResponse);
});
it('should error when country is missing', async () => {
expect.assertions(1);
try {
await holidayapi.workday();
} catch (err) {
expect(err.message).toMatch(/missing country/i);
}
});
it('should error when start is missing', async () => {
expect.assertions(1);
try {
await holidayapi.workday({ country: 'US' });
} catch (err) {
expect(err.message).toMatch(/missing start date/i);
}
});
it('should error when days is missing', async () => {
expect.assertions(1);
try {
await holidayapi.workday({ country: 'US', start: '2019-07-01' });
} catch (err) {
expect(err.message).toMatch(/missing days/i);
}
});
it('should error when days is negative', async () => {
expect.assertions(1);
try {
await holidayapi.workday({ country: 'US', start: '2019-07-01', days: -10 });
} catch (err) {
expect(err.message).toMatch(/days must be 1 or more/i);
}
});
it('should raise 4xx errors', async () => {
const expectedResponse = {
status: 429,
@ -632,36 +346,6 @@ describe('holidayapi', () => {
})).toStrictEqual(expectedResponse);
});
it('should error when country is missing', async () => {
expect.assertions(1);
try {
await holidayapi.workdays();
} catch (err) {
expect(err.message).toMatch(/missing country/i);
}
});
it('should error when start is missing', async () => {
expect.assertions(1);
try {
await holidayapi.workdays({ country: 'US' });
} catch (err) {
expect(err.message).toMatch(/missing start date/i);
}
});
it('should error when end is missing', async () => {
expect.assertions(1);
try {
await holidayapi.workdays({ country: 'US', start: '2019-07-01' });
} catch (err) {
expect(err.message).toMatch(/missing end date/i);
}
});
it('should raise 4xx errors', async () => {
const expectedResponse = {
status: 429,

View file

@ -7,7 +7,8 @@
"strict": true,
"removeComments": true,
"noUnusedLocals": true,
"noUnusedParameters": true
"noUnusedParameters": true,
"useUnknownInCatchVariables": false
},
"include": [
"src/**/*"