@chatlychat/sdk-node 0.0.0-stage → 0.2.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 +68 -0
- package/LICENSE +21 -0
- package/README.md +107 -2
- package/dist/errors.d.ts +113 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +177 -0
- package/dist/errors.js.map +1 -0
- package/dist/identity.d.ts +21 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +30 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +111 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +116 -0
- package/dist/index.js.map +1 -0
- package/dist/pagination.d.ts +32 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +43 -0
- package/dist/pagination.js.map +1 -0
- package/dist/resources.d.ts +237 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +183 -0
- package/dist/resources.js.map +1 -0
- package/dist/transport.d.ts +63 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +178 -0
- package/dist/transport.js.map +1 -0
- package/dist/webhooks.d.ts +35 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +47 -0
- package/dist/webhooks.js.map +1 -0
- package/package.json +61 -3
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { listable } from './pagination.js';
|
|
2
|
+
export function contactsResource(t) {
|
|
3
|
+
return {
|
|
4
|
+
...listable(t, '/v1/contacts'),
|
|
5
|
+
get: (id) => t.get(`/v1/contacts/${encodeURIComponent(id)}`),
|
|
6
|
+
upsert: (body) => t.post('/v1/contacts', body),
|
|
7
|
+
/** Up to the API's documented batch ceiling per call. One idempotency key covers the batch. */
|
|
8
|
+
bulkUpsert: (contacts) => t.post('/v1/contacts/bulk', { contacts }),
|
|
9
|
+
merge: (id, body) => t.post(`/v1/contacts/${encodeURIComponent(id)}/merge`, body),
|
|
10
|
+
/**
|
|
11
|
+
* Add ONE tag. The route's body is `{ tag }`, singular.
|
|
12
|
+
*
|
|
13
|
+
* Kept plural in the cross-language manifest (`contacts.add_tags`) because
|
|
14
|
+
* eight SDKs have locked that name, but the wire shape is one tag per
|
|
15
|
+
* call: loop for several. This sent `{ tags: [...] }`, which zod dropped,
|
|
16
|
+
* so it returned 200 and added nothing.
|
|
17
|
+
*/
|
|
18
|
+
addTags: (id, tag) => t.post(`/v1/contacts/${encodeURIComponent(id)}/tags`, { tag }),
|
|
19
|
+
removeTag: (id, tag) => t.del(`/v1/contacts/${encodeURIComponent(id)}/tags/${encodeURIComponent(tag)}`),
|
|
20
|
+
/**
|
|
21
|
+
* Opt the contact out of outbound email, or back in.
|
|
22
|
+
*
|
|
23
|
+
* `unsubscribed` is required — there is no default, and omitting it is a
|
|
24
|
+
* 400. Honoured across campaigns and workflows.
|
|
25
|
+
*/
|
|
26
|
+
unsubscribe: (id, body) => t.post(`/v1/contacts/${encodeURIComponent(id)}/unsubscribe`, body),
|
|
27
|
+
/** The contact's timeline: conversations, events, tickets, in one ordered read. */
|
|
28
|
+
journey: (id, query = {}) => t.get(`/v1/contacts/${encodeURIComponent(id)}/journey`, query),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function conversationsResource(t) {
|
|
32
|
+
return {
|
|
33
|
+
...listable(t, '/v1/conversations'),
|
|
34
|
+
get: (id) => t.get(`/v1/conversations/${encodeURIComponent(id)}`),
|
|
35
|
+
/**
|
|
36
|
+
* Read a thread before replying to it.
|
|
37
|
+
*
|
|
38
|
+
* The MCP server has exposed this as `get_messages` since it shipped and
|
|
39
|
+
* the SDK did not, so an integrator writing "read the conversation, then
|
|
40
|
+
* decide" against the SDK had to hand-roll the request while the same
|
|
41
|
+
* capability sat behind a tool call.
|
|
42
|
+
*/
|
|
43
|
+
messages: (conversationId, query = {}) => t.get(`/v1/conversations/${encodeURIComponent(conversationId)}/messages`, query),
|
|
44
|
+
send: (body) => t.post(`/v1/conversations/${encodeURIComponent(body.conversationId)}/messages`, body),
|
|
45
|
+
/** Open conversations with no agent reply yet — the queue a bot or a rota works from. */
|
|
46
|
+
unattended: (query = {}) => t.get('/v1/conversations/unattended', query),
|
|
47
|
+
/** Is a human or a bot driving this thread right now. */
|
|
48
|
+
agentHandling: (id) => t.get(`/v1/conversations/${encodeURIComponent(id)}/agent-handling`),
|
|
49
|
+
/** Hand the thread to a bot, or take it back. `{ state, reason? }`. */
|
|
50
|
+
setAgentHandling: (id, body) => t.put(`/v1/conversations/${encodeURIComponent(id)}/agent-handling`, body),
|
|
51
|
+
/** Drafts the AI produced for this thread, newest first. */
|
|
52
|
+
generatedReplies: (id, query = {}) => t.get(`/v1/conversations/${encodeURIComponent(id)}/generated-replies`, query),
|
|
53
|
+
/** Ask for a draft now. Returns the draft; it is not sent until you send it. */
|
|
54
|
+
generateReply: (id, body = {}) => t.post(`/v1/conversations/${encodeURIComponent(id)}/generated-replies`, body),
|
|
55
|
+
scheduledMessages: (id, query = {}) => t.get(`/v1/conversations/${encodeURIComponent(id)}/scheduled-messages`, query),
|
|
56
|
+
scheduleMessage: (id, body) => t.post(`/v1/conversations/${encodeURIComponent(id)}/scheduled-messages`, body),
|
|
57
|
+
cancelScheduled: (scheduledId) => t.del(`/v1/conversations/scheduled-messages/${encodeURIComponent(scheduledId)}`),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export function ticketsResource(t) {
|
|
61
|
+
return {
|
|
62
|
+
...listable(t, '/v1/tickets'),
|
|
63
|
+
comments: (id, query = {}) => t.get(`/v1/tickets/${encodeURIComponent(id)}/comments`, query),
|
|
64
|
+
/** The ticket auto-created from a conversation, if there is one. */
|
|
65
|
+
forConversation: (conversationId) => t.get(`/v1/tickets/for-conversation/${encodeURIComponent(conversationId)}`),
|
|
66
|
+
queues: () => t.get('/v1/tickets/queues'),
|
|
67
|
+
/** `id` is the WINNER; the body names the loser. */
|
|
68
|
+
merge: (id, body) => t.post(`/v1/tickets/${encodeURIComponent(id)}/merge`, body),
|
|
69
|
+
split: (id, body) => t.post(`/v1/tickets/${encodeURIComponent(id)}/split`, body),
|
|
70
|
+
/** One status, assignee or queue change applied to many tickets under one key. */
|
|
71
|
+
bulk: (body) => t.post('/v1/tickets/bulk', body),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function macrosResource(t) {
|
|
75
|
+
return {
|
|
76
|
+
...listable(t, '/v1/macros'),
|
|
77
|
+
create: (body) => t.post('/v1/macros', body),
|
|
78
|
+
update: (id, body) => t.patch(`/v1/macros/${encodeURIComponent(id)}`, body),
|
|
79
|
+
delete: (id) => t.del(`/v1/macros/${encodeURIComponent(id)}`),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export function kbResource(t) {
|
|
83
|
+
return {
|
|
84
|
+
...listable(t, '/v1/kb/articles'),
|
|
85
|
+
/** One article by slug — the same slug the help centre serves it under. */
|
|
86
|
+
article: (slug) => t.get(`/v1/kb/articles/${encodeURIComponent(slug)}`),
|
|
87
|
+
collections: () => t.get('/v1/kb/collections'),
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
export function searchResource(t) {
|
|
91
|
+
return {
|
|
92
|
+
/** Full-text across contacts, conversations, messages, tickets and the KB. */
|
|
93
|
+
query: (q, opts = {}) => t.get('/v1/search', { q, ...opts }),
|
|
94
|
+
/**
|
|
95
|
+
* Help-centre articles only.
|
|
96
|
+
*
|
|
97
|
+
* Sends `kinds=kb` as a repeated PLAIN key. It used to send `kinds[]=kb`,
|
|
98
|
+
* and `searchQuerySchema` is an object schema, so zod stripped the
|
|
99
|
+
* bracketed key and the search ran UNNARROWED — this method returned
|
|
100
|
+
* contacts, conversations, messages and tickets with a 200 and nothing to
|
|
101
|
+
* indicate the filter had been ignored. The MCP server's `search_kb` tool
|
|
102
|
+
* and the dashboard sent the same spelling. The schema now folds
|
|
103
|
+
* `kinds[]` and a scalar `kinds` as well, but the plain repeated key is
|
|
104
|
+
* the form to send.
|
|
105
|
+
*/
|
|
106
|
+
kb: (q, opts = {}) => t.get('/v1/search', { q, kinds: ['kb'], ...opts }),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export function eventsResource(t) {
|
|
110
|
+
const page = listable(t, '/v1/cdp/events');
|
|
111
|
+
return {
|
|
112
|
+
track: (input) => t.post('/v1/cdp/events', input),
|
|
113
|
+
/**
|
|
114
|
+
* Read back what was tracked — the same `/v1/cdp/events` collection
|
|
115
|
+
* `track` writes to, cursor-paginated newest-first.
|
|
116
|
+
*
|
|
117
|
+
* The SDK could write events and never read them, so "did my tracking
|
|
118
|
+
* call actually land?" had no answer outside the dashboard. Pass
|
|
119
|
+
* `contactId` to scope the stream to one person.
|
|
120
|
+
*/
|
|
121
|
+
list: (query = {}) => page.list(query),
|
|
122
|
+
listAll: page.listAll,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
export function webhooksResource(t) {
|
|
126
|
+
return {
|
|
127
|
+
...listable(t, '/v1/webhooks/deliveries'),
|
|
128
|
+
/** One delivery, with its request, response and attempt history. */
|
|
129
|
+
delivery: (id) => t.get(`/v1/webhooks/deliveries/${encodeURIComponent(id)}`),
|
|
130
|
+
/** Send it again. The receiver sees the same payload with a fresh signature. */
|
|
131
|
+
redeliver: (id) => t.post(`/v1/webhooks/deliveries/${encodeURIComponent(id)}/redeliver`),
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
export function reportsResource(t) {
|
|
135
|
+
return {
|
|
136
|
+
overview: (query = {}) => t.get('/v1/reports/overview', query),
|
|
137
|
+
agents: (query = {}) => t.get('/v1/reports/agents', query),
|
|
138
|
+
conversationsByDay: (query = {}) => t.get('/v1/reports/conversations-by-day', query),
|
|
139
|
+
sla: (query = {}) => t.get('/v1/reports/sla', query),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
export function channelsResource(t) {
|
|
143
|
+
return {
|
|
144
|
+
list: (query = {}) => t.get('/v1/channels', query),
|
|
145
|
+
get: (id) => t.get(`/v1/channels/${encodeURIComponent(id)}`),
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
export function billingResource(t) {
|
|
149
|
+
return {
|
|
150
|
+
/** The plan catalogue this deployment can actually sell. */
|
|
151
|
+
plans: () => t.get('/v1/billing/plans'),
|
|
152
|
+
/** Current period usage, including AI spend against the workspace ceiling. */
|
|
153
|
+
usage: (query = {}) => t.get('/v1/billing/usage', query),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
export function workspaceResource(t) {
|
|
157
|
+
return {
|
|
158
|
+
settings: () => t.get('/v1/workspace/settings'),
|
|
159
|
+
labels: () => t.get('/v1/labels'),
|
|
160
|
+
inboxes: () => t.get('/v1/inboxes'),
|
|
161
|
+
/**
|
|
162
|
+
* One team's roster. The only team read an API key can make.
|
|
163
|
+
*
|
|
164
|
+
* The workspace-wide `GET /v1/teams` list carries no `@AllowApiKey()` —
|
|
165
|
+
* it reads a calling user id, and a key is not a person — so it answers
|
|
166
|
+
* every key with 403. This manifest listed it anyway for a day, because
|
|
167
|
+
* the script that derived the key-accessible surface took the nearest
|
|
168
|
+
* `@AllowApiKey()` in either direction and credited the list route with
|
|
169
|
+
* the decorator belonging to this one. Get a team id from a workflow, an
|
|
170
|
+
* inbox's `settings.routingTeamId`, or a conversation's `teamId`.
|
|
171
|
+
*/
|
|
172
|
+
teamMembers: (teamId) => t.get(`/v1/teams/${encodeURIComponent(teamId)}/members`),
|
|
173
|
+
/**
|
|
174
|
+
* The workspace's skill taxonomy — the distinct labels routing matches on.
|
|
175
|
+
*
|
|
176
|
+
* `GET /v1/agents` has no handler at all; the agents controller serves
|
|
177
|
+
* only `/agents/skills` and `/agents/{userId}/skills`. There is no
|
|
178
|
+
* key-accessible way to list the people with a seat here.
|
|
179
|
+
*/
|
|
180
|
+
agentSkills: () => t.get('/v1/agents/skills'),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=resources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** Production. Never a dev or example host — see the note in {@link Transport}. */
|
|
2
|
+
export declare const DEFAULT_API_URL = "https://api.chatlychat.com";
|
|
3
|
+
/** The package version, stamped into `User-Agent` so our logs can see SDK age. */
|
|
4
|
+
export declare const SDK_VERSION = "0.2.0";
|
|
5
|
+
export interface RetryPolicy {
|
|
6
|
+
/** Total attempts, including the first. `1` disables retrying. */
|
|
7
|
+
readonly attempts: number;
|
|
8
|
+
/** First backoff, doubled per attempt. Jitter is applied on top. */
|
|
9
|
+
readonly baseDelayMs: number;
|
|
10
|
+
/** Ceiling for a single backoff, before jitter. */
|
|
11
|
+
readonly maxDelayMs: number;
|
|
12
|
+
}
|
|
13
|
+
export declare const DEFAULT_RETRY: RetryPolicy;
|
|
14
|
+
export interface TransportOptions {
|
|
15
|
+
readonly apiKey: string;
|
|
16
|
+
readonly apiUrl?: string;
|
|
17
|
+
readonly timeoutMs?: number;
|
|
18
|
+
readonly fetchFn?: typeof fetch;
|
|
19
|
+
readonly retry?: Partial<RetryPolicy>;
|
|
20
|
+
/** Test seam. Defaults to a real timer; a test passes a no-op. */
|
|
21
|
+
readonly sleepFn?: (ms: number) => Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
export type Query = Record<string, string | number | boolean | undefined | ReadonlyArray<string>>;
|
|
24
|
+
/**
|
|
25
|
+
* One HTTP concern, in one place: auth, idempotency, retry, error mapping.
|
|
26
|
+
*
|
|
27
|
+
* Resource namespaces hold no HTTP logic at all — they build a path and a
|
|
28
|
+
* body and hand both here. That is what makes the retry policy and the error
|
|
29
|
+
* model uniform across 55 routes instead of per-method.
|
|
30
|
+
*/
|
|
31
|
+
export declare class Transport {
|
|
32
|
+
private readonly base;
|
|
33
|
+
private readonly headers;
|
|
34
|
+
private readonly timeoutMs;
|
|
35
|
+
private readonly fetchFn;
|
|
36
|
+
private readonly retry;
|
|
37
|
+
private readonly sleepFn;
|
|
38
|
+
/**
|
|
39
|
+
* When set, every write uses this exact `Idempotency-Key` instead of a fresh
|
|
40
|
+
* UUID. Set by `ChatlyClient.withIdempotencyKey`, which returns a client
|
|
41
|
+
* sharing this transport's configuration with a pinned key.
|
|
42
|
+
*/
|
|
43
|
+
readonly pinnedIdempotencyKey: string | null;
|
|
44
|
+
constructor(opts: TransportOptions, pinnedIdempotencyKey?: string | null);
|
|
45
|
+
/** A copy of this transport with a pinned idempotency key. */
|
|
46
|
+
withPinnedKey(key: string): Transport;
|
|
47
|
+
get<T>(path: string, query?: Query): Promise<T>;
|
|
48
|
+
post<T>(path: string, body?: unknown, query?: Query): Promise<T>;
|
|
49
|
+
patch<T>(path: string, body?: unknown, query?: Query): Promise<T>;
|
|
50
|
+
put<T>(path: string, body?: unknown, query?: Query): Promise<T>;
|
|
51
|
+
del<T>(path: string, query?: Query): Promise<T>;
|
|
52
|
+
/**
|
|
53
|
+
* The escape hatch, and the one method every other method goes through.
|
|
54
|
+
*
|
|
55
|
+
* 85 of the 140 key-accessible routes have no typed method here. Without a
|
|
56
|
+
* public way in, reaching one means abandoning the SDK and hand-rolling
|
|
57
|
+
* HTTP with a bearer token — losing the retry policy, the error model and
|
|
58
|
+
* the idempotency contract in the process.
|
|
59
|
+
*/
|
|
60
|
+
send<T>(method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE', path: string, query?: Query, body?: unknown): Promise<T>;
|
|
61
|
+
private attempt;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAYA,mFAAmF;AACnF,eAAO,MAAM,eAAe,+BAA+B,CAAC;AAE5D,kFAAkF;AAClF,eAAO,MAAM,WAAW,UAAU,CAAC;AAEnC,MAAM,WAAW,WAAW;IAC1B,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,mDAAmD;IACnD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED,eAAO,MAAM,aAAa,EAAE,WAAkE,CAAC;AAE/F,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IACtC,kEAAkE;IAClE,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD;AAED,MAAM,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;AAElG;;;;;;GAMG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IACvC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAc;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD;;;;OAIG;IACH,SAAgB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;gBAEjC,IAAI,EAAE,gBAAgB,EAAE,oBAAoB,GAAE,MAAM,GAAG,IAAW;IA6BrF,8DAA8D;IACvD,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS;IAM/B,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,KAAU,GAAG,OAAO,CAAC,CAAC,CAAC;IAInD,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,GAAE,KAAU,GAAG,OAAO,CAAC,CAAC,CAAC;IAIpE,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,GAAE,KAAU,GAAG,OAAO,CAAC,CAAC,CAAC;IAIrE,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,GAAE,KAAU,GAAG,OAAO,CAAC,CAAC,CAAC;IAInE,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,GAAE,KAAU,GAAG,OAAO,CAAC,CAAC,CAAC;IAIhE;;;;;;;OAOG;IACU,IAAI,CAAC,CAAC,EACjB,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,EACnD,IAAI,EAAE,MAAM,EACZ,KAAK,GAAE,KAAU,EACjB,IAAI,CAAC,EAAE,OAAO,GACb,OAAO,CAAC,CAAC,CAAC;YAqDC,OAAO;CA8CtB"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { ChatlyApiError, ChatlyRateLimitError, ChatlyServerError, ChatlyTransportError, errorForResponse, parseRetryAfter, retryAfterHeader, } from './errors.js';
|
|
3
|
+
/** Production. Never a dev or example host — see the note in {@link Transport}. */
|
|
4
|
+
export const DEFAULT_API_URL = 'https://api.chatlychat.com';
|
|
5
|
+
/** The package version, stamped into `User-Agent` so our logs can see SDK age. */
|
|
6
|
+
export const SDK_VERSION = '0.2.0';
|
|
7
|
+
export const DEFAULT_RETRY = { attempts: 3, baseDelayMs: 250, maxDelayMs: 4_000 };
|
|
8
|
+
/**
|
|
9
|
+
* One HTTP concern, in one place: auth, idempotency, retry, error mapping.
|
|
10
|
+
*
|
|
11
|
+
* Resource namespaces hold no HTTP logic at all — they build a path and a
|
|
12
|
+
* body and hand both here. That is what makes the retry policy and the error
|
|
13
|
+
* model uniform across 55 routes instead of per-method.
|
|
14
|
+
*/
|
|
15
|
+
export class Transport {
|
|
16
|
+
base;
|
|
17
|
+
headers;
|
|
18
|
+
timeoutMs;
|
|
19
|
+
fetchFn;
|
|
20
|
+
retry;
|
|
21
|
+
sleepFn;
|
|
22
|
+
/**
|
|
23
|
+
* When set, every write uses this exact `Idempotency-Key` instead of a fresh
|
|
24
|
+
* UUID. Set by `ChatlyClient.withIdempotencyKey`, which returns a client
|
|
25
|
+
* sharing this transport's configuration with a pinned key.
|
|
26
|
+
*/
|
|
27
|
+
pinnedIdempotencyKey;
|
|
28
|
+
constructor(opts, pinnedIdempotencyKey = null) {
|
|
29
|
+
if (typeof opts.apiKey !== 'string' || opts.apiKey.length === 0) {
|
|
30
|
+
throw new TypeError('ChatlyClient: apiKey is required and must be a non-empty string');
|
|
31
|
+
}
|
|
32
|
+
/*
|
|
33
|
+
* Production, not a dev hostname.
|
|
34
|
+
*
|
|
35
|
+
* This was `https://api.livechat.local`, which resolves nowhere — so a
|
|
36
|
+
* developer who followed the published docs and omitted `apiUrl` got an
|
|
37
|
+
* SDK whose every call failed, and the docs page carried a warning
|
|
38
|
+
* telling each reader to override it. Reported by a customer who
|
|
39
|
+
* integrated from the docs alone, not caught here, because every test in
|
|
40
|
+
* this package passes an explicit `apiUrl`. The same defect shipped in
|
|
41
|
+
* the Kotlin and Swift SDKs as `api.chatly.example`.
|
|
42
|
+
*/
|
|
43
|
+
this.base = (opts.apiUrl ?? DEFAULT_API_URL).replace(/\/+$/, '');
|
|
44
|
+
this.headers = new Headers({
|
|
45
|
+
authorization: `Bearer ${opts.apiKey}`,
|
|
46
|
+
'content-type': 'application/json',
|
|
47
|
+
accept: 'application/json',
|
|
48
|
+
'user-agent': `chatly-sdk-node/${SDK_VERSION}`,
|
|
49
|
+
});
|
|
50
|
+
this.timeoutMs = opts.timeoutMs ?? 15_000;
|
|
51
|
+
this.fetchFn = opts.fetchFn ?? globalThis.fetch;
|
|
52
|
+
this.retry = { ...DEFAULT_RETRY, ...opts.retry };
|
|
53
|
+
this.sleepFn = opts.sleepFn ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
54
|
+
this.pinnedIdempotencyKey = pinnedIdempotencyKey;
|
|
55
|
+
}
|
|
56
|
+
/** A copy of this transport with a pinned idempotency key. */
|
|
57
|
+
withPinnedKey(key) {
|
|
58
|
+
const clone = Object.create(Transport.prototype);
|
|
59
|
+
Object.assign(clone, this, { pinnedIdempotencyKey: key });
|
|
60
|
+
return clone;
|
|
61
|
+
}
|
|
62
|
+
async get(path, query = {}) {
|
|
63
|
+
return this.send('GET', path, query, undefined);
|
|
64
|
+
}
|
|
65
|
+
async post(path, body, query = {}) {
|
|
66
|
+
return this.send('POST', path, query, body);
|
|
67
|
+
}
|
|
68
|
+
async patch(path, body, query = {}) {
|
|
69
|
+
return this.send('PATCH', path, query, body);
|
|
70
|
+
}
|
|
71
|
+
async put(path, body, query = {}) {
|
|
72
|
+
return this.send('PUT', path, query, body);
|
|
73
|
+
}
|
|
74
|
+
async del(path, query = {}) {
|
|
75
|
+
return this.send('DELETE', path, query, undefined);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The escape hatch, and the one method every other method goes through.
|
|
79
|
+
*
|
|
80
|
+
* 85 of the 140 key-accessible routes have no typed method here. Without a
|
|
81
|
+
* public way in, reaching one means abandoning the SDK and hand-rolling
|
|
82
|
+
* HTTP with a bearer token — losing the retry policy, the error model and
|
|
83
|
+
* the idempotency contract in the process.
|
|
84
|
+
*/
|
|
85
|
+
async send(method, path, query = {}, body) {
|
|
86
|
+
const url = new URL(`${this.base}${path}`);
|
|
87
|
+
for (const [key, value] of Object.entries(query)) {
|
|
88
|
+
if (value === undefined) {
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (Array.isArray(value)) {
|
|
92
|
+
// Repeated keys stay repeated: `kinds=kb&kinds=ticket`. Joining them
|
|
93
|
+
// with a comma produces one parameter whose value is not a member of
|
|
94
|
+
// the enum — which is what the MCP server's `search` tool did.
|
|
95
|
+
for (const item of value) {
|
|
96
|
+
url.searchParams.append(key, item);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
url.searchParams.set(key, String(value));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
const isWrite = method !== 'GET';
|
|
104
|
+
/*
|
|
105
|
+
* One key for the whole call, retries included.
|
|
106
|
+
*
|
|
107
|
+
* Minting a fresh key per ATTEMPT would defeat the header's only purpose:
|
|
108
|
+
* attempt 2 of a POST that actually succeeded upstream would be accepted
|
|
109
|
+
* as a new operation and double-write. Minting it per CALL means a retry
|
|
110
|
+
* is recognised as the same logical operation and replayed.
|
|
111
|
+
*/
|
|
112
|
+
const idempotencyKey = isWrite ? (this.pinnedIdempotencyKey ?? randomUUID()) : null;
|
|
113
|
+
let lastError;
|
|
114
|
+
for (let attempt = 1; attempt <= this.retry.attempts; attempt++) {
|
|
115
|
+
try {
|
|
116
|
+
return await this.attempt(url, method, body, idempotencyKey);
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
lastError = err;
|
|
120
|
+
const retryable = err instanceof ChatlyTransportError ||
|
|
121
|
+
err instanceof ChatlyRateLimitError ||
|
|
122
|
+
err instanceof ChatlyServerError;
|
|
123
|
+
if (!retryable || attempt === this.retry.attempts) {
|
|
124
|
+
throw err;
|
|
125
|
+
}
|
|
126
|
+
const advised = err instanceof ChatlyApiError && err.retryAfter !== null ? err.retryAfter * 1000 : null;
|
|
127
|
+
const backoff = Math.min(this.retry.baseDelayMs * 2 ** (attempt - 1), this.retry.maxDelayMs);
|
|
128
|
+
// Jitter: a fleet of workers retrying in lockstep is a second outage.
|
|
129
|
+
const jittered = backoff * (0.5 + Math.random() / 2);
|
|
130
|
+
await this.sleepFn(advised ?? jittered);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
throw lastError;
|
|
134
|
+
}
|
|
135
|
+
async attempt(url, method, body, idempotencyKey) {
|
|
136
|
+
const ctrl = new AbortController();
|
|
137
|
+
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
138
|
+
try {
|
|
139
|
+
const headers = new Headers(this.headers);
|
|
140
|
+
if (idempotencyKey !== null) {
|
|
141
|
+
headers.set('idempotency-key', idempotencyKey);
|
|
142
|
+
}
|
|
143
|
+
let res;
|
|
144
|
+
try {
|
|
145
|
+
res = await this.fetchFn(url.toString(), {
|
|
146
|
+
method,
|
|
147
|
+
headers,
|
|
148
|
+
signal: ctrl.signal,
|
|
149
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (cause) {
|
|
153
|
+
const why = ctrl.signal.aborted ? `timed out after ${String(this.timeoutMs)}ms` : 'failed';
|
|
154
|
+
throw new ChatlyTransportError(`Chatly request ${why}: ${method} ${url.pathname}`, cause);
|
|
155
|
+
}
|
|
156
|
+
if (!res.ok) {
|
|
157
|
+
throw errorForResponse({
|
|
158
|
+
status: res.status,
|
|
159
|
+
body: await res.text(),
|
|
160
|
+
requestId: res.headers.get('x-request-id'),
|
|
161
|
+
retryAfter: parseRetryAfter(retryAfterHeader(res.headers), Date.now()),
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (res.status === 204) {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
const text = await res.text();
|
|
168
|
+
if (text.length === 0) {
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
return JSON.parse(text);
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
clearTimeout(timer);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Freshness window for `t`, in seconds. Matches what the docs recommend. */
|
|
2
|
+
export declare const WEBHOOK_SIGNATURE_TOLERANCE_SECONDS = 300;
|
|
3
|
+
export interface VerifyWebhookSignatureInput {
|
|
4
|
+
/**
|
|
5
|
+
* The EXACT bytes we POSTed. Pass the raw body — re-serialising parsed JSON
|
|
6
|
+
* reorders keys and changes whitespace, and the digest will not match.
|
|
7
|
+
*/
|
|
8
|
+
body: string | Uint8Array;
|
|
9
|
+
/** The `x-livechat-signature` header value: `t=<unix seconds>,v1=<hex>`. */
|
|
10
|
+
signatureHeader: string;
|
|
11
|
+
/** The endpoint's `whsec_…` signing secret. */
|
|
12
|
+
secret: string;
|
|
13
|
+
/**
|
|
14
|
+
* How far `t` may be from now before the delivery is treated as a replay.
|
|
15
|
+
* Defaults to {@link WEBHOOK_SIGNATURE_TOLERANCE_SECONDS}; pass `Infinity`
|
|
16
|
+
* only when re-verifying an archived delivery.
|
|
17
|
+
*/
|
|
18
|
+
toleranceSeconds?: number;
|
|
19
|
+
/** Escape hatch for tests. Unix seconds. */
|
|
20
|
+
nowSeconds?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Verify the signature header on a webhook delivery from the platform.
|
|
24
|
+
*
|
|
25
|
+
* `v1` is HMAC-SHA256 over `` `${t}.${body}` `` — the timestamp is bound INTO
|
|
26
|
+
* the digest, which is what makes the replay window enforceable at all.
|
|
27
|
+
*
|
|
28
|
+
* This used to HMAC the body ALONE, so it rejected every genuine delivery the
|
|
29
|
+
* platform sent: the producer has always signed `${t}.${body}` and our own
|
|
30
|
+
* published documentation has always described that construction. Anyone
|
|
31
|
+
* following either the SDK or the docs got a hard "invalid signature" on 100%
|
|
32
|
+
* of real traffic. The wire format is unchanged — only this verifier was wrong.
|
|
33
|
+
*/
|
|
34
|
+
export declare function verifyWebhookSignature(input: VerifyWebhookSignatureInput): boolean;
|
|
35
|
+
//# sourceMappingURL=webhooks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAEA,6EAA6E;AAC7E,eAAO,MAAM,mCAAmC,MAAM,CAAC;AAEvD,MAAM,WAAW,2BAA2B;IAC1C;;;OAGG;IACH,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAC1B,4EAA4E;IAC5E,eAAe,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4CAA4C;IAC5C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,2BAA2B,GAAG,OAAO,CAiClF"}
|
package/dist/webhooks.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from 'node:crypto';
|
|
2
|
+
/** Freshness window for `t`, in seconds. Matches what the docs recommend. */
|
|
3
|
+
export const WEBHOOK_SIGNATURE_TOLERANCE_SECONDS = 300;
|
|
4
|
+
/**
|
|
5
|
+
* Verify the signature header on a webhook delivery from the platform.
|
|
6
|
+
*
|
|
7
|
+
* `v1` is HMAC-SHA256 over `` `${t}.${body}` `` — the timestamp is bound INTO
|
|
8
|
+
* the digest, which is what makes the replay window enforceable at all.
|
|
9
|
+
*
|
|
10
|
+
* This used to HMAC the body ALONE, so it rejected every genuine delivery the
|
|
11
|
+
* platform sent: the producer has always signed `${t}.${body}` and our own
|
|
12
|
+
* published documentation has always described that construction. Anyone
|
|
13
|
+
* following either the SDK or the docs got a hard "invalid signature" on 100%
|
|
14
|
+
* of real traffic. The wire format is unchanged — only this verifier was wrong.
|
|
15
|
+
*/
|
|
16
|
+
export function verifyWebhookSignature(input) {
|
|
17
|
+
const parts = new Map();
|
|
18
|
+
for (const part of input.signatureHeader.split(',')) {
|
|
19
|
+
const eq = part.indexOf('=');
|
|
20
|
+
if (eq > 0) {
|
|
21
|
+
parts.set(part.slice(0, eq).trim(), part.slice(eq + 1).trim());
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const rawTs = parts.get('t');
|
|
25
|
+
const tag = parts.get('v1');
|
|
26
|
+
if (rawTs === undefined || tag === undefined) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const issuedAt = Number.parseInt(rawTs, 10);
|
|
30
|
+
if (!Number.isFinite(issuedAt)) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
const tolerance = input.toleranceSeconds ?? WEBHOOK_SIGNATURE_TOLERANCE_SECONDS;
|
|
34
|
+
const now = input.nowSeconds ?? Math.floor(Date.now() / 1000);
|
|
35
|
+
if (Math.abs(now - issuedAt) > tolerance) {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
const hmac = createHmac('sha256', input.secret);
|
|
39
|
+
hmac.update(`${rawTs}.`);
|
|
40
|
+
hmac.update(typeof input.body === 'string' ? Buffer.from(input.body, 'utf8') : input.body);
|
|
41
|
+
const a = Buffer.from(tag, 'hex');
|
|
42
|
+
const b = Buffer.from(hmac.digest('hex'), 'hex');
|
|
43
|
+
// Length check first: timingSafeEqual throws on a length mismatch, and a
|
|
44
|
+
// thrown verifier is an accepted webhook in a handler that forgot try/catch.
|
|
45
|
+
return a.length === b.length && timingSafeEqual(a, b);
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=webhooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webhooks.js","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE1D,6EAA6E;AAC7E,MAAM,CAAC,MAAM,mCAAmC,GAAG,GAAG,CAAC;AAsBvD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAkC;IACvE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAkB,CAAC;IACxC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QACpD,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC;YACX,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IAED,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,KAAK,KAAK,SAAS,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QAC7C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC5C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,SAAS,GAAG,KAAK,CAAC,gBAAgB,IAAI,mCAAmC,CAAC;IAChF,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC;IAC9D,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,GAAG,QAAQ,CAAC,GAAG,SAAS,EAAE,CAAC;QACzC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAChD,IAAI,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;IACzB,IAAI,CAAC,MAAM,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3F,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAClC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC;IACjD,yEAAyE;IACzE,6EAA6E;IAC7E,OAAO,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,IAAI,eAAe,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACxD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,64 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatlychat/sdk-node",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Official Node.js SDK for the Chatly API — typed REST client, webhook signature verification, and the widget identity hash.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"homepage": "https://github.com/chatlychat/chatly-sdk-node#readme",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/chatlychat/chatly-sdk-node.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/chatlychat/chatly-sdk-node/issues"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"chatly",
|
|
16
|
+
"chat",
|
|
17
|
+
"messaging",
|
|
18
|
+
"support",
|
|
19
|
+
"webhooks",
|
|
20
|
+
"sdk",
|
|
21
|
+
"server"
|
|
22
|
+
],
|
|
23
|
+
"author": "Chatly",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "./dist/index.js",
|
|
26
|
+
"module": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.js",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"README.md",
|
|
38
|
+
"LICENSE",
|
|
39
|
+
"CHANGELOG.md"
|
|
40
|
+
],
|
|
41
|
+
"sideEffects": false,
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public",
|
|
44
|
+
"registry": "https://registry.npmjs.org/",
|
|
45
|
+
"provenance": true
|
|
46
|
+
},
|
|
47
|
+
"engines": {
|
|
48
|
+
"node": ">=18"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "20.14.10",
|
|
52
|
+
"typescript": "5.6.2",
|
|
53
|
+
"vitest": "2.1.2",
|
|
54
|
+
"@livechat/config": "0.1.0",
|
|
55
|
+
"@livechat/shared": "0.1.0"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "tsc -p tsconfig.build.json",
|
|
60
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
61
|
+
"lint": "eslint src --max-warnings=0",
|
|
62
|
+
"test": "vitest run"
|
|
63
|
+
}
|
|
6
64
|
}
|