@meridesk/node-sdk 0.1.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/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@meridesk/node-sdk` will be documented in this file.
4
+
5
+ ## [0.1.0] - Unreleased
6
+
7
+ ### Added
8
+
9
+ - Initial release of the Meridesk Node.js SDK.
10
+ - `Meridesk` client authenticated via `apiKey`.
11
+ - `meridesk.identity.generateUserHash()` for secure widget identity verification (local HMAC-SHA256).
12
+ - `meridesk.customers` — `upsert`, `get`, `update`.
13
+ - `meridesk.tickets` — `create`, `list`, `get`, `reply`.
14
+ - `meridesk.articles` — `listCategories`, `search`.
15
+ - Dual CommonJS/ESM build with TypeScript type declarations.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Meridesk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # Meridesk Node.js SDK
2
+
3
+ Official Node.js SDK for [Meridesk](https://meridesk.live) — integrate customer support into your backend: create and manage tickets and customers on behalf of your users, search your knowledge base, and securely verify user identity for the Meridesk widget. Authenticated with a single API key.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/%40meridesk%2Fnode-sdk.svg)](https://www.npmjs.com/package/@meridesk/node-sdk)
6
+
7
+ > This SDK is designed to run on your **backend server only**. It talks to Meridesk's server-to-server API using a secret API key and must never be bundled into a browser or mobile app. For client-side widgets, see the [iOS](../MerideskSDK), [Flutter](../meridesk_flutter_sdk), and [WordPress](../Meridesk-WP-Plugin) SDKs.
8
+
9
+ ## Features
10
+
11
+ - 🔑 **API key authentication** — a single secret key identifies your website/account, no OAuth dance required.
12
+ - 🔐 **Secure identity verification** — generate HMAC user hashes for the Meridesk widget's secure mode, computed locally (no network call).
13
+ - 👤 **Customers** — create, fetch, and update customer records.
14
+ - 🎫 **Tickets** — create tickets on behalf of a customer, list/filter them, and post replies.
15
+ - 📚 **Knowledge base** — list categories and full-text search articles.
16
+ - 🧩 **TypeScript-first** — ships with full type declarations and works from both CommonJS (`require`) and ESM (`import`).
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ npm install @meridesk/node-sdk
22
+ ```
23
+
24
+ ## Quick start
25
+
26
+ ```ts
27
+ import { Meridesk } from '@meridesk/node-sdk';
28
+
29
+ const meridesk = new Meridesk({
30
+ apiKey: process.env.MERIDESK_API_KEY!,
31
+ // Optional — only needed for meridesk.identity.generateUserHash()
32
+ identitySecret: process.env.MERIDESK_IDENTITY_SECRET,
33
+ });
34
+
35
+ // Confirm your credentials are wired up correctly
36
+ const { website } = await meridesk.me();
37
+ console.log(`Connected to ${website.name}`);
38
+ ```
39
+
40
+ Using CommonJS:
41
+
42
+ ```js
43
+ const { Meridesk } = require('@meridesk/node-sdk');
44
+ ```
45
+
46
+ Get your API key and identity secret from your Meridesk dashboard under **Settings → API Keys**. The identity secret is only shown once, when a key is created or regenerated.
47
+
48
+ ## Usage
49
+
50
+ ### Secure widget identity verification
51
+
52
+ If you enable secure mode on the Meridesk widget, sign each logged-in user's identifier on your backend and pass the resulting hash to the widget alongside their id/email:
53
+
54
+ ```ts
55
+ const userHash = meridesk.identity.generateUserHash(user.email);
56
+
57
+ // Pass `user.email` and `userHash` to the Meridesk widget on the client.
58
+ ```
59
+
60
+ This never makes a network request — it's a local HMAC-SHA256 computation using your `identitySecret`.
61
+
62
+ ### Customers
63
+
64
+ ```ts
65
+ const customer = await meridesk.customers.upsert({
66
+ email: 'jane@example.com',
67
+ name: 'Jane Doe',
68
+ company: 'Acme Inc.',
69
+ });
70
+
71
+ await meridesk.customers.get('jane@example.com');
72
+
73
+ await meridesk.customers.update('jane@example.com', { status: 'customer' });
74
+ ```
75
+
76
+ ### Tickets
77
+
78
+ ```ts
79
+ const ticket = await meridesk.tickets.create({
80
+ email: 'jane@example.com',
81
+ name: 'Jane Doe',
82
+ title: 'Refund request',
83
+ message: 'I would like a refund for order #1234.',
84
+ priority: 'high',
85
+ });
86
+
87
+ const openTickets = await meridesk.tickets.list({ email: 'jane@example.com', status: 'open' });
88
+
89
+ await meridesk.tickets.reply(ticket.id, { message: 'We\'ve processed your refund.' });
90
+ ```
91
+
92
+ ### Knowledge base articles
93
+
94
+ ```ts
95
+ const categories = await meridesk.articles.listCategories();
96
+ const results = await meridesk.articles.search('refund policy');
97
+ ```
98
+
99
+ ## Error handling
100
+
101
+ All API errors are thrown as `MerideskAPIError` (with `.status` and `.body`); configuration mistakes throw `MerideskConfigError`; network/timeout failures throw `MerideskConnectionError`. All three extend `MerideskError`.
102
+
103
+ ```ts
104
+ import { MerideskAPIError } from '@meridesk/node-sdk';
105
+
106
+ try {
107
+ await meridesk.tickets.get('unknown-id');
108
+ } catch (error) {
109
+ if (error instanceof MerideskAPIError && error.status === 404) {
110
+ // handle not found
111
+ }
112
+ throw error;
113
+ }
114
+ ```
115
+
116
+ ## Configuration reference
117
+
118
+ | Option | Required | Description |
119
+ | ----------------- | -------- | ----------------------------------------------------------------------------- |
120
+ | `apiKey` | Yes | Your Meridesk API key (`mdk_live_...`). |
121
+ | `identitySecret` | No | Required only for `identity.generateUserHash()`. |
122
+ | `baseUrl` | No | Override the API base URL (defaults to the production Meridesk API). |
123
+ | `timeoutMs` | No | Request timeout in milliseconds (default `15000`). |
124
+
125
+ ## Documentation
126
+
127
+ Full guides and API reference: <https://docs.meridesk.live/docs/nodejs/getting-started/introduction>
128
+
129
+ ## License
130
+
131
+ MIT © Meridesk
package/dist/index.cjs ADDED
@@ -0,0 +1,265 @@
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
+ DEFAULT_BASE_URL: () => DEFAULT_BASE_URL,
24
+ Meridesk: () => Meridesk,
25
+ MerideskAPIError: () => MerideskAPIError,
26
+ MerideskConfigError: () => MerideskConfigError,
27
+ MerideskConnectionError: () => MerideskConnectionError,
28
+ MerideskError: () => MerideskError,
29
+ default: () => Meridesk
30
+ });
31
+ module.exports = __toCommonJS(index_exports);
32
+
33
+ // src/errors.ts
34
+ var MerideskError = class _MerideskError extends Error {
35
+ constructor(message) {
36
+ super(message);
37
+ this.name = "MerideskError";
38
+ Object.setPrototypeOf(this, _MerideskError.prototype);
39
+ }
40
+ };
41
+ var MerideskConfigError = class _MerideskConfigError extends MerideskError {
42
+ constructor(message) {
43
+ super(message);
44
+ this.name = "MerideskConfigError";
45
+ Object.setPrototypeOf(this, _MerideskConfigError.prototype);
46
+ }
47
+ };
48
+ var MerideskAPIError = class _MerideskAPIError extends MerideskError {
49
+ constructor(message, status, body) {
50
+ super(message);
51
+ this.name = "MerideskAPIError";
52
+ this.status = status;
53
+ this.body = body;
54
+ Object.setPrototypeOf(this, _MerideskAPIError.prototype);
55
+ }
56
+ };
57
+ var MerideskConnectionError = class _MerideskConnectionError extends MerideskError {
58
+ constructor(message, cause) {
59
+ super(message);
60
+ this.name = "MerideskConnectionError";
61
+ this.cause = cause;
62
+ Object.setPrototypeOf(this, _MerideskConnectionError.prototype);
63
+ }
64
+ };
65
+
66
+ // src/http.ts
67
+ var DEFAULT_TIMEOUT_MS = 15e3;
68
+ var HttpClient = class {
69
+ constructor(options) {
70
+ this.apiKey = options.apiKey;
71
+ this.baseUrl = options.baseUrl.endsWith("/") ? options.baseUrl : `${options.baseUrl}/`;
72
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
73
+ }
74
+ buildUrl(path, query) {
75
+ const url = new URL(path.replace(/^\//, ""), this.baseUrl);
76
+ if (query) {
77
+ for (const [key, value] of Object.entries(query)) {
78
+ if (value !== void 0 && value !== null) {
79
+ url.searchParams.set(key, String(value));
80
+ }
81
+ }
82
+ }
83
+ return url.toString();
84
+ }
85
+ async request(method, path, options = {}) {
86
+ const url = this.buildUrl(path, options.query);
87
+ const controller = new AbortController();
88
+ const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
89
+ let response;
90
+ try {
91
+ response = await fetch(url, {
92
+ method,
93
+ headers: {
94
+ "Content-Type": "application/json",
95
+ Accept: "application/json",
96
+ "X-API-Key": this.apiKey,
97
+ "User-Agent": "meridesk-node-sdk"
98
+ },
99
+ body: options.body !== void 0 ? JSON.stringify(options.body) : void 0,
100
+ signal: controller.signal
101
+ });
102
+ } catch (err) {
103
+ if (err instanceof Error && err.name === "AbortError") {
104
+ throw new MerideskConnectionError(`Meridesk API request to ${path} timed out after ${this.timeoutMs}ms`, err);
105
+ }
106
+ throw new MerideskConnectionError(`Failed to reach the Meridesk API: ${err?.message ?? err}`, err);
107
+ } finally {
108
+ clearTimeout(timeout);
109
+ }
110
+ let json;
111
+ try {
112
+ json = await response.json();
113
+ } catch {
114
+ json = void 0;
115
+ }
116
+ if (!response.ok) {
117
+ const message = json?.message || `Meridesk API request failed with status ${response.status}`;
118
+ throw new MerideskAPIError(message, response.status, json);
119
+ }
120
+ return json && Object.prototype.hasOwnProperty.call(json, "data") ? json.data : json;
121
+ }
122
+ get(path, query) {
123
+ return this.request("GET", path, { query });
124
+ }
125
+ post(path, body) {
126
+ return this.request("POST", path, { body });
127
+ }
128
+ patch(path, body) {
129
+ return this.request("PATCH", path, { body });
130
+ }
131
+ };
132
+
133
+ // src/resources/articles.ts
134
+ var ArticlesResource = class {
135
+ constructor(http) {
136
+ this.http = http;
137
+ }
138
+ /** Lists all knowledge base categories (with their nested articles/topics) for this website. */
139
+ async listCategories() {
140
+ const { categories } = await this.http.get("/articles/categories");
141
+ return categories;
142
+ }
143
+ /** Full-text searches article titles/topics for this website. */
144
+ async search(query) {
145
+ const { results } = await this.http.get("/articles/search", { q: query });
146
+ return results;
147
+ }
148
+ };
149
+
150
+ // src/resources/customers.ts
151
+ var CustomersResource = class {
152
+ constructor(http) {
153
+ this.http = http;
154
+ }
155
+ /**
156
+ * Creates a customer if one doesn't exist for this email yet, or updates
157
+ * the provided fields on the existing one.
158
+ */
159
+ async upsert(input) {
160
+ const { customer } = await this.http.post("/customers", input);
161
+ return customer;
162
+ }
163
+ /** Fetches a customer by email. */
164
+ async get(email) {
165
+ const { customer } = await this.http.get(`/customers/${encodeURIComponent(email)}`);
166
+ return customer;
167
+ }
168
+ /** Updates one or more fields on an existing customer. */
169
+ async update(email, input) {
170
+ const { customer } = await this.http.patch(`/customers/${encodeURIComponent(email)}`, input);
171
+ return customer;
172
+ }
173
+ };
174
+
175
+ // src/resources/identity.ts
176
+ var import_node_crypto = require("crypto");
177
+ var IdentityResource = class {
178
+ constructor(identitySecret) {
179
+ this.identitySecret = identitySecret;
180
+ }
181
+ /**
182
+ * Computes the identity hash for a given user identifier (typically the
183
+ * customer's email address).
184
+ *
185
+ * @throws {MerideskConfigError} if no `identitySecret` was provided when
186
+ * constructing the `Meridesk` client, or if `userId` is empty.
187
+ */
188
+ generateUserHash(userId) {
189
+ if (!this.identitySecret) {
190
+ throw new MerideskConfigError(
191
+ "Set `identitySecret` when constructing the Meridesk client to use identity.generateUserHash(). Find it in your Meridesk dashboard under Settings > API Keys (shown once when a key is created or regenerated)."
192
+ );
193
+ }
194
+ if (!userId) {
195
+ throw new MerideskConfigError("userId is required to generate an identity hash");
196
+ }
197
+ return (0, import_node_crypto.createHmac)("sha256", this.identitySecret).update(String(userId)).digest("hex");
198
+ }
199
+ };
200
+
201
+ // src/resources/tickets.ts
202
+ var TicketsResource = class {
203
+ constructor(http) {
204
+ this.http = http;
205
+ }
206
+ /**
207
+ * Creates a support ticket on behalf of a customer, identified by email.
208
+ * The customer is created automatically if they don't already exist.
209
+ */
210
+ async create(input) {
211
+ const { ticket } = await this.http.post("/tickets", input);
212
+ return ticket;
213
+ }
214
+ /** Lists tickets for this website, optionally filtered by customer email and/or status. */
215
+ async list(params = {}) {
216
+ const { tickets } = await this.http.get("/tickets", {
217
+ email: params.email,
218
+ status: params.status
219
+ });
220
+ return tickets;
221
+ }
222
+ /** Fetches a single ticket by id. */
223
+ async get(ticketId) {
224
+ const { ticket } = await this.http.get(`/tickets/${encodeURIComponent(ticketId)}`);
225
+ return ticket;
226
+ }
227
+ /** Adds a reply to a ticket (defaults to a 'support' reply from your backend). */
228
+ async reply(ticketId, input) {
229
+ const { ticket } = await this.http.post(`/tickets/${encodeURIComponent(ticketId)}/replies`, input);
230
+ return ticket;
231
+ }
232
+ };
233
+
234
+ // src/client.ts
235
+ var DEFAULT_BASE_URL = "https://api.north-america.meridesk.live/sdk/v1";
236
+ var Meridesk = class {
237
+ constructor(config) {
238
+ if (!config || !config.apiKey) {
239
+ throw new MerideskConfigError("An `apiKey` is required to initialize the Meridesk client. Find yours in your Meridesk dashboard under Settings > API Keys.");
240
+ }
241
+ this.http = new HttpClient({
242
+ apiKey: config.apiKey,
243
+ baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,
244
+ timeoutMs: config.timeoutMs
245
+ });
246
+ this.identity = new IdentityResource(config.identitySecret);
247
+ this.customers = new CustomersResource(this.http);
248
+ this.tickets = new TicketsResource(this.http);
249
+ this.articles = new ArticlesResource(this.http);
250
+ }
251
+ /** Verifies the configured API key is valid and returns basic website/account info. */
252
+ me() {
253
+ return this.http.get("/me");
254
+ }
255
+ };
256
+ // Annotate the CommonJS export names for ESM import in node:
257
+ 0 && (module.exports = {
258
+ DEFAULT_BASE_URL,
259
+ Meridesk,
260
+ MerideskAPIError,
261
+ MerideskConfigError,
262
+ MerideskConnectionError,
263
+ MerideskError
264
+ });
265
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/http.ts","../src/resources/articles.ts","../src/resources/customers.ts","../src/resources/identity.ts","../src/resources/tickets.ts","../src/client.ts"],"sourcesContent":["export { Meridesk, DEFAULT_BASE_URL } from './client';\nexport { MerideskAPIError, MerideskConfigError, MerideskConnectionError, MerideskError } from './errors';\n\nexport type {\n Article,\n ArticleCategory,\n ArticleSearchResult,\n ArticleTopic,\n CreateTicketInput,\n Customer,\n ListTicketsParams,\n MeResponse,\n MerideskApiKeyInfo,\n MerideskConfig,\n MerideskWebsite,\n ReplyToTicketInput,\n Ticket,\n TicketMessage,\n TicketPriority,\n TicketStatus,\n UpdateCustomerInput,\n UpsertCustomerInput\n} from './types';\n\nexport { Meridesk as default } from './client';\n","/**\n * Base class for all errors thrown by the Meridesk SDK.\n */\nexport class MerideskError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'MerideskError';\n Object.setPrototypeOf(this, MerideskError.prototype);\n }\n}\n\n/**\n * Thrown when the Meridesk client is misconfigured (missing/invalid\n * credentials, missing required arguments, etc.) — always a local,\n * pre-request error.\n */\nexport class MerideskConfigError extends MerideskError {\n constructor(message: string) {\n super(message);\n this.name = 'MerideskConfigError';\n Object.setPrototypeOf(this, MerideskConfigError.prototype);\n }\n}\n\n/**\n * Thrown when the Meridesk API responds with a non-2xx status code.\n */\nexport class MerideskAPIError extends MerideskError {\n /** HTTP status code returned by the API. */\n readonly status: number;\n /** Parsed JSON response body, if any. */\n readonly body: unknown;\n\n constructor(message: string, status: number, body?: unknown) {\n super(message);\n this.name = 'MerideskAPIError';\n this.status = status;\n this.body = body;\n Object.setPrototypeOf(this, MerideskAPIError.prototype);\n }\n}\n\n/**\n * Thrown when a request to the Meridesk API fails for network reasons\n * (timeout, DNS failure, connection reset, etc.) rather than an API error\n * response.\n */\nexport class MerideskConnectionError extends MerideskError {\n readonly cause?: unknown;\n\n constructor(message: string, cause?: unknown) {\n super(message);\n this.name = 'MerideskConnectionError';\n this.cause = cause;\n Object.setPrototypeOf(this, MerideskConnectionError.prototype);\n }\n}\n","import { MerideskAPIError, MerideskConnectionError } from './errors';\n\nexport interface HttpClientOptions {\n apiKey: string;\n baseUrl: string;\n timeoutMs?: number;\n}\n\ninterface RequestOptions {\n query?: Record<string, string | number | boolean | undefined | null>;\n body?: unknown;\n}\n\ninterface ApiEnvelope<T> {\n success: boolean;\n message?: string;\n data?: T;\n errors?: unknown;\n}\n\nconst DEFAULT_TIMEOUT_MS = 15_000;\n\n/**\n * Thin wrapper around the global `fetch` for talking to the Meridesk SDK API.\n * Handles auth headers, JSON encoding/decoding, timeouts, and translating\n * non-2xx responses into `MerideskAPIError`.\n */\nexport class HttpClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeoutMs: number;\n\n constructor(options: HttpClientOptions) {\n this.apiKey = options.apiKey;\n this.baseUrl = options.baseUrl.endsWith('/') ? options.baseUrl : `${options.baseUrl}/`;\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n }\n\n private buildUrl(path: string, query?: RequestOptions['query']): string {\n const url = new URL(path.replace(/^\\//, ''), this.baseUrl);\n if (query) {\n for (const [key, value] of Object.entries(query)) {\n if (value !== undefined && value !== null) {\n url.searchParams.set(key, String(value));\n }\n }\n }\n return url.toString();\n }\n\n private async request<T>(method: string, path: string, options: RequestOptions = {}): Promise<T> {\n const url = this.buildUrl(path, options.query);\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method,\n headers: {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n 'X-API-Key': this.apiKey,\n 'User-Agent': 'meridesk-node-sdk'\n },\n body: options.body !== undefined ? JSON.stringify(options.body) : undefined,\n signal: controller.signal\n });\n } catch (err: unknown) {\n if (err instanceof Error && err.name === 'AbortError') {\n throw new MerideskConnectionError(`Meridesk API request to ${path} timed out after ${this.timeoutMs}ms`, err);\n }\n throw new MerideskConnectionError(`Failed to reach the Meridesk API: ${(err as Error)?.message ?? err}`, err);\n } finally {\n clearTimeout(timeout);\n }\n\n let json: ApiEnvelope<T> | undefined;\n try {\n json = (await response.json()) as ApiEnvelope<T>;\n } catch {\n json = undefined;\n }\n\n if (!response.ok) {\n const message = json?.message || `Meridesk API request failed with status ${response.status}`;\n throw new MerideskAPIError(message, response.status, json);\n }\n\n return (json && Object.prototype.hasOwnProperty.call(json, 'data') ? (json.data as T) : (json as unknown as T));\n }\n\n get<T>(path: string, query?: RequestOptions['query']): Promise<T> {\n return this.request<T>('GET', path, { query });\n }\n\n post<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('POST', path, { body });\n }\n\n patch<T>(path: string, body?: unknown): Promise<T> {\n return this.request<T>('PATCH', path, { body });\n }\n}\n","import type { HttpClient } from '../http';\nimport type { ArticleCategory, ArticleSearchResult } from '../types';\n\ninterface CategoryListEnvelope {\n categories: ArticleCategory[];\n}\n\ninterface SearchEnvelope {\n results: ArticleSearchResult[];\n}\n\nexport class ArticlesResource {\n constructor(private readonly http: HttpClient) {}\n\n /** Lists all knowledge base categories (with their nested articles/topics) for this website. */\n async listCategories(): Promise<ArticleCategory[]> {\n const { categories } = await this.http.get<CategoryListEnvelope>('/articles/categories');\n return categories;\n }\n\n /** Full-text searches article titles/topics for this website. */\n async search(query: string): Promise<ArticleSearchResult[]> {\n const { results } = await this.http.get<SearchEnvelope>('/articles/search', { q: query });\n return results;\n }\n}\n","import type { HttpClient } from '../http';\nimport type { Customer, UpdateCustomerInput, UpsertCustomerInput } from '../types';\n\ninterface CustomerEnvelope {\n customer: Customer;\n}\n\nexport class CustomersResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Creates a customer if one doesn't exist for this email yet, or updates\n * the provided fields on the existing one.\n */\n async upsert(input: UpsertCustomerInput): Promise<Customer> {\n const { customer } = await this.http.post<CustomerEnvelope>('/customers', input);\n return customer;\n }\n\n /** Fetches a customer by email. */\n async get(email: string): Promise<Customer> {\n const { customer } = await this.http.get<CustomerEnvelope>(`/customers/${encodeURIComponent(email)}`);\n return customer;\n }\n\n /** Updates one or more fields on an existing customer. */\n async update(email: string, input: UpdateCustomerInput): Promise<Customer> {\n const { customer } = await this.http.patch<CustomerEnvelope>(`/customers/${encodeURIComponent(email)}`, input);\n return customer;\n }\n}\n","import { createHmac } from 'node:crypto';\nimport { MerideskConfigError } from '../errors';\n\n/**\n * Generates HMAC-SHA256 identity verification hashes for the Meridesk\n * widget's \"secure mode\", entirely locally — no network call is made.\n *\n * This mirrors the identity verification pattern used by Intercom/Zendesk:\n * your backend signs a stable identifier for the logged-in user (their\n * email or internal user id) with the `identitySecret` from your Meridesk\n * API key, and passes the resulting hash to the widget alongside that\n * identifier. Meridesk recomputes the hash server-side to confirm the\n * request really came from your backend.\n */\nexport class IdentityResource {\n constructor(private readonly identitySecret?: string) {}\n\n /**\n * Computes the identity hash for a given user identifier (typically the\n * customer's email address).\n *\n * @throws {MerideskConfigError} if no `identitySecret` was provided when\n * constructing the `Meridesk` client, or if `userId` is empty.\n */\n generateUserHash(userId: string): string {\n if (!this.identitySecret) {\n throw new MerideskConfigError(\n 'Set `identitySecret` when constructing the Meridesk client to use identity.generateUserHash(). ' +\n 'Find it in your Meridesk dashboard under Settings > API Keys (shown once when a key is created or regenerated).'\n );\n }\n\n if (!userId) {\n throw new MerideskConfigError('userId is required to generate an identity hash');\n }\n\n return createHmac('sha256', this.identitySecret).update(String(userId)).digest('hex');\n }\n}\n","import type { HttpClient } from '../http';\nimport type { CreateTicketInput, ListTicketsParams, ReplyToTicketInput, Ticket } from '../types';\n\ninterface TicketEnvelope {\n ticket: Ticket;\n}\n\ninterface TicketListEnvelope {\n tickets: Ticket[];\n}\n\nexport class TicketsResource {\n constructor(private readonly http: HttpClient) {}\n\n /**\n * Creates a support ticket on behalf of a customer, identified by email.\n * The customer is created automatically if they don't already exist.\n */\n async create(input: CreateTicketInput): Promise<Ticket> {\n const { ticket } = await this.http.post<TicketEnvelope>('/tickets', input);\n return ticket;\n }\n\n /** Lists tickets for this website, optionally filtered by customer email and/or status. */\n async list(params: ListTicketsParams = {}): Promise<Ticket[]> {\n const { tickets } = await this.http.get<TicketListEnvelope>('/tickets', {\n email: params.email,\n status: params.status\n });\n return tickets;\n }\n\n /** Fetches a single ticket by id. */\n async get(ticketId: string): Promise<Ticket> {\n const { ticket } = await this.http.get<TicketEnvelope>(`/tickets/${encodeURIComponent(ticketId)}`);\n return ticket;\n }\n\n /** Adds a reply to a ticket (defaults to a 'support' reply from your backend). */\n async reply(ticketId: string, input: ReplyToTicketInput): Promise<Ticket> {\n const { ticket } = await this.http.post<TicketEnvelope>(`/tickets/${encodeURIComponent(ticketId)}/replies`, input);\n return ticket;\n }\n}\n","import { MerideskConfigError } from './errors';\nimport { HttpClient } from './http';\nimport { ArticlesResource } from './resources/articles';\nimport { CustomersResource } from './resources/customers';\nimport { IdentityResource } from './resources/identity';\nimport { TicketsResource } from './resources/tickets';\nimport type { MeResponse, MerideskConfig } from './types';\n\nexport const DEFAULT_BASE_URL = 'https://api.north-america.meridesk.live/sdk/v1';\n\n/**\n * Official Meridesk Node.js SDK client for backend integrations.\n *\n * @example\n * ```ts\n * import { Meridesk } from '@meridesk/node-sdk';\n *\n * const meridesk = new Meridesk({\n * apiKey: process.env.MERIDESK_API_KEY!,\n * identitySecret: process.env.MERIDESK_IDENTITY_SECRET, // optional, for secure widget mode\n * });\n *\n * const ticket = await meridesk.tickets.create({\n * email: 'jane@example.com',\n * name: 'Jane Doe',\n * title: 'Refund request',\n * message: 'I would like a refund for order #1234.',\n * });\n * ```\n */\nexport class Meridesk {\n /** Generate secure widget identity verification hashes, computed locally. */\n readonly identity: IdentityResource;\n /** Create, fetch, and update customers. */\n readonly customers: CustomersResource;\n /** Create, list, fetch, and reply to support tickets. */\n readonly tickets: TicketsResource;\n /** List and search knowledge base articles. */\n readonly articles: ArticlesResource;\n\n private readonly http: HttpClient;\n\n constructor(config: MerideskConfig) {\n if (!config || !config.apiKey) {\n throw new MerideskConfigError('An `apiKey` is required to initialize the Meridesk client. Find yours in your Meridesk dashboard under Settings > API Keys.');\n }\n\n this.http = new HttpClient({\n apiKey: config.apiKey,\n baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,\n timeoutMs: config.timeoutMs\n });\n\n this.identity = new IdentityResource(config.identitySecret);\n this.customers = new CustomersResource(this.http);\n this.tickets = new TicketsResource(this.http);\n this.articles = new ArticlesResource(this.http);\n }\n\n /** Verifies the configured API key is valid and returns basic website/account info. */\n me(): Promise<MeResponse> {\n return this.http.get<MeResponse>('/me');\n }\n}\n"],"mappings":"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","names":[]}