@holidays-rest/sdk-ts 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,213 @@
1
+ # holidays.rest TypeScript SDK
2
+
3
+ [![Codacy Badge](https://app.codacy.com/project/badge/Grade/32fc7ef7bedd4819a7b34a6ec409888d)](https://app.codacy.com/gh/holidays-rest/sdk-ts/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
4
+ [![Codacy Badge](https://app.codacy.com/project/badge/Coverage/32fc7ef7bedd4819a7b34a6ec409888d)](https://app.codacy.com/gh/holidays-rest/sdk-ts/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage)
5
+
6
+ Official TypeScript SDK for the [holidays.rest](https://www.holidays.rest) API.
7
+
8
+ ## Requirements
9
+
10
+ - Node.js ≥ 18 (uses native `fetch`)
11
+ - TypeScript 5+ (for consumers using TypeScript)
12
+ - Zero runtime dependencies
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install holidays.rest
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```ts
23
+ import { HolidaysClient } from "holidays.rest";
24
+
25
+ const client = new HolidaysClient({ apiKey: "YOUR_API_KEY" });
26
+
27
+ const holidays = await client.getHolidays({ country: "US", year: 2024 });
28
+ holidays.forEach((h) => console.log(`${h.date} — ${h.name.en}`));
29
+ ```
30
+
31
+ Get an API key at [holidays.rest/dashboard](https://www.holidays.rest/dashboard).
32
+
33
+ ---
34
+
35
+ ## API
36
+
37
+ ### `new HolidaysClient(options)`
38
+
39
+ ```ts
40
+ interface ClientOptions {
41
+ apiKey: string; // required — Bearer token from dashboard
42
+ baseUrl?: string; // optional — override for testing
43
+ }
44
+ ```
45
+
46
+ ---
47
+
48
+ ### `getHolidays(params)` → `Promise<Holiday[]>`
49
+
50
+ ```ts
51
+ interface HolidaysParams {
52
+ country: string; // required — ISO 3166 alpha-2 (e.g. "US")
53
+ year: number | string; // required — e.g. 2024
54
+
55
+ month?: number | string; // optional — 1–12
56
+ day?: number | string; // optional — 1–31
57
+ type?: string | string[]; // "religious" | "national" | "local"
58
+ religion?: number | number[]; // religion codes 1–11
59
+ region?: string | string[]; // subdivision codes from getCountry()
60
+ lang?: string | string[]; // language codes from getLanguages()
61
+ response?: "json" | "xml" | "yaml" | "csv"; // default: "json"
62
+ }
63
+ ```
64
+
65
+ ```ts
66
+ // All US holidays in 2024
67
+ await client.getHolidays({ country: "US", year: 2024 });
68
+
69
+ // National holidays only
70
+ await client.getHolidays({ country: "DE", year: 2024, type: "national" });
71
+
72
+ // Multiple types
73
+ await client.getHolidays({ country: "TR", year: 2024, type: ["national", "religious"] });
74
+
75
+ // Filter by month and day
76
+ await client.getHolidays({ country: "GB", year: 2024, month: 12, day: 25 });
77
+
78
+ // Specific region
79
+ await client.getHolidays({ country: "US", year: 2024, region: "US-CA" });
80
+
81
+ // Multiple regions
82
+ await client.getHolidays({ country: "US", year: 2024, region: ["US-CA", "US-NY"] });
83
+ ```
84
+
85
+ ---
86
+
87
+ ### `getCountries()` → `Promise<Country[]>`
88
+
89
+ ```ts
90
+ const countries = await client.getCountries();
91
+ countries.forEach((c) => console.log(`${c.alpha2} — ${c.name}`));
92
+ ```
93
+
94
+ ---
95
+
96
+ ### `getCountry(countryCode)` → `Promise<Country>`
97
+
98
+ Returns country details including subdivision codes usable as `region` filters.
99
+
100
+ ```ts
101
+ const us = await client.getCountry("US");
102
+ us.subdivisions?.forEach((s) => console.log(`${s.code} — ${s.name}`));
103
+ ```
104
+
105
+ ---
106
+
107
+ ### `getLanguages()` → `Promise<Language[]>`
108
+
109
+ ```ts
110
+ const languages = await client.getLanguages();
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Types
116
+
117
+ All request and response types are exported:
118
+
119
+ ```ts
120
+ import type {
121
+ Holiday,
122
+ HolidayDay,
123
+ HolidayName,
124
+ Country,
125
+ Subdivision,
126
+ Language,
127
+ HolidaysParams,
128
+ ClientOptions,
129
+ } from "holidays.rest";
130
+ ```
131
+
132
+ ```ts
133
+ interface HolidayName {
134
+ [lang: string]: string; // e.g. { en: "New Year's Day", de: "Neujahr" }
135
+ }
136
+
137
+ interface HolidayDay {
138
+ actual: string; // weekday the holiday falls on, e.g. "Thursday"
139
+ observed: string; // weekday legally observed, e.g. "Monday"
140
+ }
141
+
142
+ interface Holiday {
143
+ country_code: string; // ISO 3166 alpha-2, e.g. "DE"
144
+ country_name: string; // e.g. "Germany"
145
+ date: string; // ISO 8601, e.g. "2026-01-01"
146
+ name: HolidayName;
147
+ isNational: boolean;
148
+ isReligious: boolean;
149
+ isLocal: boolean;
150
+ isEstimate: boolean;
151
+ day: HolidayDay;
152
+ religion: string; // e.g. "Christianity" or ""
153
+ regions: string[]; // subdivision codes, e.g. ["BW", "BY"], or []
154
+ }
155
+
156
+ interface Country {
157
+ name: string; alpha2: string; subdivisions?: Subdivision[];
158
+ }
159
+
160
+ interface Subdivision { code: string; name: string; }
161
+ interface Language { code: string; name: string; }
162
+ ```
163
+
164
+ ---
165
+
166
+ ## Error Handling
167
+
168
+ Non-2xx responses throw `HolidaysApiError`:
169
+
170
+ ```ts
171
+ import { HolidaysClient, HolidaysApiError } from "holidays.rest";
172
+
173
+ try {
174
+ await client.getHolidays({ country: "US", year: 2024 });
175
+ } catch (err) {
176
+ if (err instanceof HolidaysApiError) {
177
+ console.log(err.status); // HTTP status code (number)
178
+ console.log(err.message); // Error message (string)
179
+ console.log(err.body); // Raw response body (unknown)
180
+ }
181
+ }
182
+ ```
183
+
184
+ | Status | Meaning |
185
+ |--------|---------------------|
186
+ | 400 | Bad request |
187
+ | 401 | Invalid API key |
188
+ | 404 | Not found |
189
+ | 500 | Server error |
190
+ | 503 | Service unavailable |
191
+
192
+ ---
193
+
194
+ ## Building
195
+
196
+ ```bash
197
+ npm run build # outputs ESM + CJS + .d.ts to dist/
198
+ npm run typecheck # type-check without emitting
199
+ ```
200
+
201
+ The build outputs:
202
+
203
+ | File | Format | Used by |
204
+ |--------------------|--------|-----------------------------|
205
+ | `dist/index.js` | ESM | `import` / bundlers |
206
+ | `dist/index.cjs` | CJS | `require()` / older tooling |
207
+ | `dist/index.d.ts` | Types | TypeScript consumers |
208
+
209
+ ---
210
+
211
+ ## License
212
+
213
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ HolidaysApiError: () => HolidaysApiError,
24
+ HolidaysClient: () => HolidaysClient
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/errors.ts
29
+ var HolidaysApiError = class extends Error {
30
+ status;
31
+ body;
32
+ constructor(message, status, body) {
33
+ super(message);
34
+ this.name = "HolidaysApiError";
35
+ this.status = status;
36
+ this.body = body;
37
+ }
38
+ };
39
+
40
+ // src/client.ts
41
+ var DEFAULT_BASE_URL = "https://api.holidays.rest/v1";
42
+ function toCommaSeparated(value) {
43
+ return Array.isArray(value) ? value.join(",") : String(value);
44
+ }
45
+ function buildQuery(params) {
46
+ const query = new URLSearchParams();
47
+ for (const [key, value] of Object.entries(params)) {
48
+ if (value === void 0 || value === null) continue;
49
+ query.set(key, toCommaSeparated(value));
50
+ }
51
+ const qs = query.toString();
52
+ return qs ? `?${qs}` : "";
53
+ }
54
+ var HolidaysClient = class {
55
+ apiKey;
56
+ baseUrl;
57
+ constructor({ apiKey, baseUrl = DEFAULT_BASE_URL }) {
58
+ if (!apiKey) throw new Error("HolidaysClient: apiKey is required");
59
+ this.apiKey = apiKey;
60
+ this.baseUrl = baseUrl.replace(/\/$/, "");
61
+ }
62
+ // ── internal ──────────────────────────────────────────────────────────────
63
+ async request(path, query = "") {
64
+ const url = `${this.baseUrl}${path}${query}`;
65
+ const response = await fetch(url, {
66
+ method: "GET",
67
+ headers: {
68
+ Authorization: `Bearer ${this.apiKey}`,
69
+ Accept: "application/json"
70
+ }
71
+ });
72
+ let body;
73
+ const contentType = response.headers.get("content-type") ?? "";
74
+ if (contentType.includes("application/json")) {
75
+ body = await response.json();
76
+ } else {
77
+ body = await response.text();
78
+ }
79
+ if (!response.ok) {
80
+ const message = typeof body === "object" && body !== null && "message" in body && typeof body.message === "string" ? body.message : response.statusText;
81
+ throw new HolidaysApiError(message, response.status, body);
82
+ }
83
+ return body;
84
+ }
85
+ // ── public API ────────────────────────────────────────────────────────────
86
+ /**
87
+ * Fetches public holidays matching the given parameters.
88
+ *
89
+ * @example
90
+ * await client.getHolidays({ country: "US", year: 2024 });
91
+ * await client.getHolidays({ country: "TR", year: 2024, type: ["national", "religious"] });
92
+ */
93
+ async getHolidays(params) {
94
+ if (!params.country) throw new Error("getHolidays: country is required");
95
+ if (!params.year) throw new Error("getHolidays: year is required");
96
+ const query = buildQuery({
97
+ country: params.country,
98
+ year: params.year,
99
+ month: params.month,
100
+ day: params.day,
101
+ type: params.type,
102
+ religion: params.religion,
103
+ region: params.region,
104
+ lang: params.lang,
105
+ response: params.response
106
+ });
107
+ return this.request("/holidays", query);
108
+ }
109
+ /** Returns all supported countries. */
110
+ async getCountries() {
111
+ return this.request("/countries");
112
+ }
113
+ /**
114
+ * Returns details for one country, including subdivision codes
115
+ * usable as `region` filters in {@link getHolidays}.
116
+ *
117
+ * @param countryCode ISO 3166 alpha-2 code, e.g. `"US"`.
118
+ */
119
+ async getCountry(countryCode) {
120
+ if (!countryCode) throw new Error("getCountry: countryCode is required");
121
+ return this.request(`/country/${encodeURIComponent(countryCode)}`);
122
+ }
123
+ /** Returns all supported language codes. */
124
+ async getLanguages() {
125
+ return this.request("/languages");
126
+ }
127
+ };
128
+ // Annotate the CommonJS export names for ESM import in node:
129
+ 0 && (module.exports = {
130
+ HolidaysApiError,
131
+ HolidaysClient
132
+ });
133
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/client.ts"],"sourcesContent":["export { HolidaysClient } from \"./client.js\";\nexport { HolidaysApiError } from \"./errors.js\";\nexport type {\n ClientOptions,\n Country,\n Holiday,\n HolidayDay,\n HolidayName,\n HolidaysParams,\n Language,\n Subdivision,\n} from \"./types.js\";\n","export class HolidaysApiError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body: unknown) {\n super(message);\n this.name = \"HolidaysApiError\";\n this.status = status;\n this.body = body;\n }\n}\n","import { HolidaysApiError } from \"./errors.js\";\nimport type {\n ClientOptions,\n Country,\n Holiday,\n HolidaysParams,\n Language,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.holidays.rest/v1\";\n\nfunction toCommaSeparated(value: string | string[] | number | number[]): string {\n return Array.isArray(value) ? value.join(\",\") : String(value);\n}\n\nfunction buildQuery(params: Record<string, string | number | string[] | number[] | undefined>): string {\n const query = new URLSearchParams();\n\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined || value === null) continue;\n query.set(key, toCommaSeparated(value as string | string[] | number | number[]));\n }\n\n const qs = query.toString();\n return qs ? `?${qs}` : \"\";\n}\n\nexport class HolidaysClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n\n constructor({ apiKey, baseUrl = DEFAULT_BASE_URL }: ClientOptions) {\n if (!apiKey) throw new Error(\"HolidaysClient: apiKey is required\");\n this.apiKey = apiKey;\n this.baseUrl = baseUrl.replace(/\\/$/, \"\");\n }\n\n // ── internal ──────────────────────────────────────────────────────────────\n\n private async request<T>(path: string, query = \"\"): Promise<T> {\n const url = `${this.baseUrl}${path}${query}`;\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n Accept: \"application/json\",\n },\n });\n\n let body: unknown;\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n\n if (contentType.includes(\"application/json\")) {\n body = await response.json();\n } else {\n body = await response.text();\n }\n\n if (!response.ok) {\n const message =\n typeof body === \"object\" &&\n body !== null &&\n \"message\" in body &&\n typeof (body as Record<string, unknown>).message === \"string\"\n ? (body as { message: string }).message\n : response.statusText;\n\n throw new HolidaysApiError(message, response.status, body);\n }\n\n return body as T;\n }\n\n // ── public API ────────────────────────────────────────────────────────────\n\n /**\n * Fetches public holidays matching the given parameters.\n *\n * @example\n * await client.getHolidays({ country: \"US\", year: 2024 });\n * await client.getHolidays({ country: \"TR\", year: 2024, type: [\"national\", \"religious\"] });\n */\n async getHolidays(params: HolidaysParams): Promise<Holiday[]> {\n if (!params.country) throw new Error(\"getHolidays: country is required\");\n if (!params.year) throw new Error(\"getHolidays: year is required\");\n\n const query = buildQuery({\n country: params.country,\n year: params.year,\n month: params.month,\n day: params.day,\n type: params.type,\n religion: params.religion,\n region: params.region,\n lang: params.lang,\n response: params.response,\n });\n\n return this.request<Holiday[]>(\"/holidays\", query);\n }\n\n /** Returns all supported countries. */\n async getCountries(): Promise<Country[]> {\n return this.request<Country[]>(\"/countries\");\n }\n\n /**\n * Returns details for one country, including subdivision codes\n * usable as `region` filters in {@link getHolidays}.\n *\n * @param countryCode ISO 3166 alpha-2 code, e.g. `\"US\"`.\n */\n async getCountry(countryCode: string): Promise<Country> {\n if (!countryCode) throw new Error(\"getCountry: countryCode is required\");\n return this.request<Country>(`/country/${encodeURIComponent(countryCode)}`);\n }\n\n /** Returns all supported language codes. */\n async getLanguages(): Promise<Language[]> {\n return this.request<Language[]>(\"/languages\");\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Holiday name keyed by BCP 47 language code, e.g. `{ en: "New Year's Day", de: "Neujahr" }`.
3
+ * The `en` key is always present.
4
+ */
5
+ interface HolidayName {
6
+ [lang: string]: string;
7
+ }
8
+ /** Actual and observed weekday names for a holiday date. */
9
+ interface HolidayDay {
10
+ /** Weekday the holiday falls on, e.g. `"Thursday"`. */
11
+ actual: string;
12
+ /** Weekday the holiday is legally observed, e.g. `"Monday"` (for rolled-over holidays). */
13
+ observed: string;
14
+ }
15
+ interface Holiday {
16
+ /** ISO 3166 alpha-2 country code, e.g. `"DE"`. */
17
+ country_code: string;
18
+ /** Full country name, e.g. `"Germany"`. */
19
+ country_name: string;
20
+ /** ISO 8601 date string, e.g. `"2026-01-01"`. */
21
+ date: string;
22
+ /** Holiday name in one or more languages. */
23
+ name: HolidayName;
24
+ /** `true` when the holiday applies nationwide. */
25
+ isNational: boolean;
26
+ /** `true` when the holiday has a religious origin. */
27
+ isReligious: boolean;
28
+ /** `true` when the holiday applies to specific regions only. */
29
+ isLocal: boolean;
30
+ /** `true` when the date is an estimate (e.g. lunar-calendar holidays). */
31
+ isEstimate: boolean;
32
+ /** Actual and observed weekday information. */
33
+ day: HolidayDay;
34
+ /** Religion associated with the holiday, e.g. `"Christianity"`. Empty string when not applicable. */
35
+ religion: string;
36
+ /** Subdivision codes where the holiday applies, e.g. `["BW", "BY"]`. Empty array for nationwide holidays. */
37
+ regions: string[];
38
+ }
39
+ interface Subdivision {
40
+ code: string;
41
+ name: string;
42
+ }
43
+ interface Country {
44
+ name: string;
45
+ alpha2: string;
46
+ subdivisions?: Subdivision[];
47
+ }
48
+ interface Language {
49
+ code: string;
50
+ name: string;
51
+ }
52
+ interface HolidaysParams {
53
+ /** ISO 3166 alpha-2 country code, e.g. `"US"`. Required. */
54
+ country: string;
55
+ /** Four-digit year, e.g. `2024`. Required. */
56
+ year: number | string;
57
+ /** Month filter (1–12). Optional. */
58
+ month?: number | string;
59
+ /** Day filter (1–31). Optional. */
60
+ day?: number | string;
61
+ /** Holiday type(s): `"religious"` | `"national"` | `"local"`. Optional. */
62
+ type?: string | string[];
63
+ /** Religion code(s) 1–11. Optional. */
64
+ religion?: number | number[];
65
+ /** Region/subdivision code(s) — from `getCountry()`. Optional. */
66
+ region?: string | string[];
67
+ /** Language code(s) — from `getLanguages()`. Optional. */
68
+ lang?: string | string[];
69
+ /** Response format: `"json"` (default) | `"xml"` | `"yaml"` | `"csv"`. Optional. */
70
+ response?: "json" | "xml" | "yaml" | "csv";
71
+ }
72
+ interface ClientOptions {
73
+ /** Bearer token from https://www.holidays.rest/dashboard. Required. */
74
+ apiKey: string;
75
+ /** Override base URL. Useful for testing. */
76
+ baseUrl?: string;
77
+ }
78
+
79
+ declare class HolidaysClient {
80
+ private readonly apiKey;
81
+ private readonly baseUrl;
82
+ constructor({ apiKey, baseUrl }: ClientOptions);
83
+ private request;
84
+ /**
85
+ * Fetches public holidays matching the given parameters.
86
+ *
87
+ * @example
88
+ * await client.getHolidays({ country: "US", year: 2024 });
89
+ * await client.getHolidays({ country: "TR", year: 2024, type: ["national", "religious"] });
90
+ */
91
+ getHolidays(params: HolidaysParams): Promise<Holiday[]>;
92
+ /** Returns all supported countries. */
93
+ getCountries(): Promise<Country[]>;
94
+ /**
95
+ * Returns details for one country, including subdivision codes
96
+ * usable as `region` filters in {@link getHolidays}.
97
+ *
98
+ * @param countryCode ISO 3166 alpha-2 code, e.g. `"US"`.
99
+ */
100
+ getCountry(countryCode: string): Promise<Country>;
101
+ /** Returns all supported language codes. */
102
+ getLanguages(): Promise<Language[]>;
103
+ }
104
+
105
+ declare class HolidaysApiError extends Error {
106
+ readonly status: number;
107
+ readonly body: unknown;
108
+ constructor(message: string, status: number, body: unknown);
109
+ }
110
+
111
+ export { type ClientOptions, type Country, type Holiday, type HolidayDay, type HolidayName, HolidaysApiError, HolidaysClient, type HolidaysParams, type Language, type Subdivision };
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Holiday name keyed by BCP 47 language code, e.g. `{ en: "New Year's Day", de: "Neujahr" }`.
3
+ * The `en` key is always present.
4
+ */
5
+ interface HolidayName {
6
+ [lang: string]: string;
7
+ }
8
+ /** Actual and observed weekday names for a holiday date. */
9
+ interface HolidayDay {
10
+ /** Weekday the holiday falls on, e.g. `"Thursday"`. */
11
+ actual: string;
12
+ /** Weekday the holiday is legally observed, e.g. `"Monday"` (for rolled-over holidays). */
13
+ observed: string;
14
+ }
15
+ interface Holiday {
16
+ /** ISO 3166 alpha-2 country code, e.g. `"DE"`. */
17
+ country_code: string;
18
+ /** Full country name, e.g. `"Germany"`. */
19
+ country_name: string;
20
+ /** ISO 8601 date string, e.g. `"2026-01-01"`. */
21
+ date: string;
22
+ /** Holiday name in one or more languages. */
23
+ name: HolidayName;
24
+ /** `true` when the holiday applies nationwide. */
25
+ isNational: boolean;
26
+ /** `true` when the holiday has a religious origin. */
27
+ isReligious: boolean;
28
+ /** `true` when the holiday applies to specific regions only. */
29
+ isLocal: boolean;
30
+ /** `true` when the date is an estimate (e.g. lunar-calendar holidays). */
31
+ isEstimate: boolean;
32
+ /** Actual and observed weekday information. */
33
+ day: HolidayDay;
34
+ /** Religion associated with the holiday, e.g. `"Christianity"`. Empty string when not applicable. */
35
+ religion: string;
36
+ /** Subdivision codes where the holiday applies, e.g. `["BW", "BY"]`. Empty array for nationwide holidays. */
37
+ regions: string[];
38
+ }
39
+ interface Subdivision {
40
+ code: string;
41
+ name: string;
42
+ }
43
+ interface Country {
44
+ name: string;
45
+ alpha2: string;
46
+ subdivisions?: Subdivision[];
47
+ }
48
+ interface Language {
49
+ code: string;
50
+ name: string;
51
+ }
52
+ interface HolidaysParams {
53
+ /** ISO 3166 alpha-2 country code, e.g. `"US"`. Required. */
54
+ country: string;
55
+ /** Four-digit year, e.g. `2024`. Required. */
56
+ year: number | string;
57
+ /** Month filter (1–12). Optional. */
58
+ month?: number | string;
59
+ /** Day filter (1–31). Optional. */
60
+ day?: number | string;
61
+ /** Holiday type(s): `"religious"` | `"national"` | `"local"`. Optional. */
62
+ type?: string | string[];
63
+ /** Religion code(s) 1–11. Optional. */
64
+ religion?: number | number[];
65
+ /** Region/subdivision code(s) — from `getCountry()`. Optional. */
66
+ region?: string | string[];
67
+ /** Language code(s) — from `getLanguages()`. Optional. */
68
+ lang?: string | string[];
69
+ /** Response format: `"json"` (default) | `"xml"` | `"yaml"` | `"csv"`. Optional. */
70
+ response?: "json" | "xml" | "yaml" | "csv";
71
+ }
72
+ interface ClientOptions {
73
+ /** Bearer token from https://www.holidays.rest/dashboard. Required. */
74
+ apiKey: string;
75
+ /** Override base URL. Useful for testing. */
76
+ baseUrl?: string;
77
+ }
78
+
79
+ declare class HolidaysClient {
80
+ private readonly apiKey;
81
+ private readonly baseUrl;
82
+ constructor({ apiKey, baseUrl }: ClientOptions);
83
+ private request;
84
+ /**
85
+ * Fetches public holidays matching the given parameters.
86
+ *
87
+ * @example
88
+ * await client.getHolidays({ country: "US", year: 2024 });
89
+ * await client.getHolidays({ country: "TR", year: 2024, type: ["national", "religious"] });
90
+ */
91
+ getHolidays(params: HolidaysParams): Promise<Holiday[]>;
92
+ /** Returns all supported countries. */
93
+ getCountries(): Promise<Country[]>;
94
+ /**
95
+ * Returns details for one country, including subdivision codes
96
+ * usable as `region` filters in {@link getHolidays}.
97
+ *
98
+ * @param countryCode ISO 3166 alpha-2 code, e.g. `"US"`.
99
+ */
100
+ getCountry(countryCode: string): Promise<Country>;
101
+ /** Returns all supported language codes. */
102
+ getLanguages(): Promise<Language[]>;
103
+ }
104
+
105
+ declare class HolidaysApiError extends Error {
106
+ readonly status: number;
107
+ readonly body: unknown;
108
+ constructor(message: string, status: number, body: unknown);
109
+ }
110
+
111
+ export { type ClientOptions, type Country, type Holiday, type HolidayDay, type HolidayName, HolidaysApiError, HolidaysClient, type HolidaysParams, type Language, type Subdivision };
package/dist/index.js ADDED
@@ -0,0 +1,105 @@
1
+ // src/errors.ts
2
+ var HolidaysApiError = class extends Error {
3
+ status;
4
+ body;
5
+ constructor(message, status, body) {
6
+ super(message);
7
+ this.name = "HolidaysApiError";
8
+ this.status = status;
9
+ this.body = body;
10
+ }
11
+ };
12
+
13
+ // src/client.ts
14
+ var DEFAULT_BASE_URL = "https://api.holidays.rest/v1";
15
+ function toCommaSeparated(value) {
16
+ return Array.isArray(value) ? value.join(",") : String(value);
17
+ }
18
+ function buildQuery(params) {
19
+ const query = new URLSearchParams();
20
+ for (const [key, value] of Object.entries(params)) {
21
+ if (value === void 0 || value === null) continue;
22
+ query.set(key, toCommaSeparated(value));
23
+ }
24
+ const qs = query.toString();
25
+ return qs ? `?${qs}` : "";
26
+ }
27
+ var HolidaysClient = class {
28
+ apiKey;
29
+ baseUrl;
30
+ constructor({ apiKey, baseUrl = DEFAULT_BASE_URL }) {
31
+ if (!apiKey) throw new Error("HolidaysClient: apiKey is required");
32
+ this.apiKey = apiKey;
33
+ this.baseUrl = baseUrl.replace(/\/$/, "");
34
+ }
35
+ // ── internal ──────────────────────────────────────────────────────────────
36
+ async request(path, query = "") {
37
+ const url = `${this.baseUrl}${path}${query}`;
38
+ const response = await fetch(url, {
39
+ method: "GET",
40
+ headers: {
41
+ Authorization: `Bearer ${this.apiKey}`,
42
+ Accept: "application/json"
43
+ }
44
+ });
45
+ let body;
46
+ const contentType = response.headers.get("content-type") ?? "";
47
+ if (contentType.includes("application/json")) {
48
+ body = await response.json();
49
+ } else {
50
+ body = await response.text();
51
+ }
52
+ if (!response.ok) {
53
+ const message = typeof body === "object" && body !== null && "message" in body && typeof body.message === "string" ? body.message : response.statusText;
54
+ throw new HolidaysApiError(message, response.status, body);
55
+ }
56
+ return body;
57
+ }
58
+ // ── public API ────────────────────────────────────────────────────────────
59
+ /**
60
+ * Fetches public holidays matching the given parameters.
61
+ *
62
+ * @example
63
+ * await client.getHolidays({ country: "US", year: 2024 });
64
+ * await client.getHolidays({ country: "TR", year: 2024, type: ["national", "religious"] });
65
+ */
66
+ async getHolidays(params) {
67
+ if (!params.country) throw new Error("getHolidays: country is required");
68
+ if (!params.year) throw new Error("getHolidays: year is required");
69
+ const query = buildQuery({
70
+ country: params.country,
71
+ year: params.year,
72
+ month: params.month,
73
+ day: params.day,
74
+ type: params.type,
75
+ religion: params.religion,
76
+ region: params.region,
77
+ lang: params.lang,
78
+ response: params.response
79
+ });
80
+ return this.request("/holidays", query);
81
+ }
82
+ /** Returns all supported countries. */
83
+ async getCountries() {
84
+ return this.request("/countries");
85
+ }
86
+ /**
87
+ * Returns details for one country, including subdivision codes
88
+ * usable as `region` filters in {@link getHolidays}.
89
+ *
90
+ * @param countryCode ISO 3166 alpha-2 code, e.g. `"US"`.
91
+ */
92
+ async getCountry(countryCode) {
93
+ if (!countryCode) throw new Error("getCountry: countryCode is required");
94
+ return this.request(`/country/${encodeURIComponent(countryCode)}`);
95
+ }
96
+ /** Returns all supported language codes. */
97
+ async getLanguages() {
98
+ return this.request("/languages");
99
+ }
100
+ };
101
+ export {
102
+ HolidaysApiError,
103
+ HolidaysClient
104
+ };
105
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/client.ts"],"sourcesContent":["export class HolidaysApiError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body: unknown) {\n super(message);\n this.name = \"HolidaysApiError\";\n this.status = status;\n this.body = body;\n }\n}\n","import { HolidaysApiError } from \"./errors.js\";\nimport type {\n ClientOptions,\n Country,\n Holiday,\n HolidaysParams,\n Language,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.holidays.rest/v1\";\n\nfunction toCommaSeparated(value: string | string[] | number | number[]): string {\n return Array.isArray(value) ? value.join(\",\") : String(value);\n}\n\nfunction buildQuery(params: Record<string, string | number | string[] | number[] | undefined>): string {\n const query = new URLSearchParams();\n\n for (const [key, value] of Object.entries(params)) {\n if (value === undefined || value === null) continue;\n query.set(key, toCommaSeparated(value as string | string[] | number | number[]));\n }\n\n const qs = query.toString();\n return qs ? `?${qs}` : \"\";\n}\n\nexport class HolidaysClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n\n constructor({ apiKey, baseUrl = DEFAULT_BASE_URL }: ClientOptions) {\n if (!apiKey) throw new Error(\"HolidaysClient: apiKey is required\");\n this.apiKey = apiKey;\n this.baseUrl = baseUrl.replace(/\\/$/, \"\");\n }\n\n // ── internal ──────────────────────────────────────────────────────────────\n\n private async request<T>(path: string, query = \"\"): Promise<T> {\n const url = `${this.baseUrl}${path}${query}`;\n\n const response = await fetch(url, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${this.apiKey}`,\n Accept: \"application/json\",\n },\n });\n\n let body: unknown;\n const contentType = response.headers.get(\"content-type\") ?? \"\";\n\n if (contentType.includes(\"application/json\")) {\n body = await response.json();\n } else {\n body = await response.text();\n }\n\n if (!response.ok) {\n const message =\n typeof body === \"object\" &&\n body !== null &&\n \"message\" in body &&\n typeof (body as Record<string, unknown>).message === \"string\"\n ? (body as { message: string }).message\n : response.statusText;\n\n throw new HolidaysApiError(message, response.status, body);\n }\n\n return body as T;\n }\n\n // ── public API ────────────────────────────────────────────────────────────\n\n /**\n * Fetches public holidays matching the given parameters.\n *\n * @example\n * await client.getHolidays({ country: \"US\", year: 2024 });\n * await client.getHolidays({ country: \"TR\", year: 2024, type: [\"national\", \"religious\"] });\n */\n async getHolidays(params: HolidaysParams): Promise<Holiday[]> {\n if (!params.country) throw new Error(\"getHolidays: country is required\");\n if (!params.year) throw new Error(\"getHolidays: year is required\");\n\n const query = buildQuery({\n country: params.country,\n year: params.year,\n month: params.month,\n day: params.day,\n type: params.type,\n religion: params.religion,\n region: params.region,\n lang: params.lang,\n response: params.response,\n });\n\n return this.request<Holiday[]>(\"/holidays\", query);\n }\n\n /** Returns all supported countries. */\n async getCountries(): Promise<Country[]> {\n return this.request<Country[]>(\"/countries\");\n }\n\n /**\n * Returns details for one country, including subdivision codes\n * usable as `region` filters in {@link getHolidays}.\n *\n * @param countryCode ISO 3166 alpha-2 code, e.g. `\"US\"`.\n */\n async getCountry(countryCode: string): Promise<Country> {\n if (!countryCode) throw new Error(\"getCountry: countryCode is required\");\n return this.request<Country>(`/country/${encodeURIComponent(countryCode)}`);\n }\n\n /** Returns all supported language codes. */\n async getLanguages(): Promise<Language[]> {\n return this.request<Language[]>(\"/languages\");\n }\n}\n"],"mappings":";AAAO,IAAM,mBAAN,cAA+B,MAAM;AAAA,EACjC;AAAA,EACA;AAAA,EAET,YAAY,SAAiB,QAAgB,MAAe;AAC1D,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,EACd;AACF;;;ACDA,IAAM,mBAAmB;AAEzB,SAAS,iBAAiB,OAAsD;AAC9E,SAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,GAAG,IAAI,OAAO,KAAK;AAC9D;AAEA,SAAS,WAAW,QAAmF;AACrG,QAAM,QAAQ,IAAI,gBAAgB;AAElC,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,QAAI,UAAU,UAAa,UAAU,KAAM;AAC3C,UAAM,IAAI,KAAK,iBAAiB,KAA8C,CAAC;AAAA,EACjF;AAEA,QAAM,KAAK,MAAM,SAAS;AAC1B,SAAO,KAAK,IAAI,EAAE,KAAK;AACzB;AAEO,IAAM,iBAAN,MAAqB;AAAA,EACT;AAAA,EACA;AAAA,EAEjB,YAAY,EAAE,QAAQ,UAAU,iBAAiB,GAAkB;AACjE,QAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,oCAAoC;AACjE,SAAK,SAAS;AACd,SAAK,UAAU,QAAQ,QAAQ,OAAO,EAAE;AAAA,EAC1C;AAAA;AAAA,EAIA,MAAc,QAAW,MAAc,QAAQ,IAAgB;AAC7D,UAAM,MAAM,GAAG,KAAK,OAAO,GAAG,IAAI,GAAG,KAAK;AAE1C,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,eAAe,UAAU,KAAK,MAAM;AAAA,QACpC,QAAQ;AAAA,MACV;AAAA,IACF,CAAC;AAED,QAAI;AACJ,UAAM,cAAc,SAAS,QAAQ,IAAI,cAAc,KAAK;AAE5D,QAAI,YAAY,SAAS,kBAAkB,GAAG;AAC5C,aAAO,MAAM,SAAS,KAAK;AAAA,IAC7B,OAAO;AACL,aAAO,MAAM,SAAS,KAAK;AAAA,IAC7B;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,UACJ,OAAO,SAAS,YAChB,SAAS,QACT,aAAa,QACb,OAAQ,KAAiC,YAAY,WAChD,KAA6B,UAC9B,SAAS;AAEf,YAAM,IAAI,iBAAiB,SAAS,SAAS,QAAQ,IAAI;AAAA,IAC3D;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,MAAM,YAAY,QAA4C;AAC5D,QAAI,CAAC,OAAO,QAAS,OAAM,IAAI,MAAM,kCAAkC;AACvE,QAAI,CAAC,OAAO,KAAM,OAAM,IAAI,MAAM,+BAA+B;AAEjE,UAAM,QAAQ,WAAW;AAAA,MACvB,SAAU,OAAO;AAAA,MACjB,MAAU,OAAO;AAAA,MACjB,OAAU,OAAO;AAAA,MACjB,KAAU,OAAO;AAAA,MACjB,MAAU,OAAO;AAAA,MACjB,UAAU,OAAO;AAAA,MACjB,QAAU,OAAO;AAAA,MACjB,MAAU,OAAO;AAAA,MACjB,UAAU,OAAO;AAAA,IACnB,CAAC;AAED,WAAO,KAAK,QAAmB,aAAa,KAAK;AAAA,EACnD;AAAA;AAAA,EAGA,MAAM,eAAmC;AACvC,WAAO,KAAK,QAAmB,YAAY;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,WAAW,aAAuC;AACtD,QAAI,CAAC,YAAa,OAAM,IAAI,MAAM,qCAAqC;AACvE,WAAO,KAAK,QAAiB,YAAY,mBAAmB,WAAW,CAAC,EAAE;AAAA,EAC5E;AAAA;AAAA,EAGA,MAAM,eAAoC;AACxC,WAAO,KAAK,QAAoB,YAAY;AAAA,EAC9C;AACF;","names":[]}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@holidays-rest/sdk-ts",
3
+ "version": "1.0.0",
4
+ "description": "Official TypeScript SDK for the holidays.rest API",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "import": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "require": {
13
+ "types": "./dist/index.d.cts",
14
+ "default": "./dist/index.cjs"
15
+ }
16
+ }
17
+ },
18
+ "main": "./dist/index.cjs",
19
+ "module": "./dist/index.js",
20
+ "types": "./dist/index.d.ts",
21
+ "files": [
22
+ "dist"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsup",
26
+ "dev": "tsup --watch",
27
+ "typecheck": "tsc --noEmit",
28
+ "test": "vitest run",
29
+ "test:watch": "vitest",
30
+ "test:coverage": "vitest run --coverage"
31
+ },
32
+ "keywords": [
33
+ "holidays",
34
+ "public-holidays",
35
+ "calendar",
36
+ "api",
37
+ "sdk",
38
+ "typescript"
39
+ ],
40
+ "author": "msdundar",
41
+ "license": "MIT",
42
+ "engines": {
43
+ "node": ">=18.0.0"
44
+ },
45
+ "devDependencies": {
46
+ "@vitest/coverage-v8": "^4.1.4",
47
+ "tsup": "^8.0.0",
48
+ "typescript": "^6.0.2",
49
+ "vitest": "^4.1.4"
50
+ }
51
+ }