Skip to content

Commit 8e73be6

Browse files
guguclaude
andcommitted
regenerate SDK with semantic operation IDs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 0331eba commit 8e73be6

18 files changed

Lines changed: 2304 additions & 195 deletions

openapi/config.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,73 @@ export default defineConfig({
1414
format: "prettier",
1515
lint: "eslint",
1616
},
17+
parser: {
18+
patch: {
19+
operations: {
20+
// Links - CRUD
21+
'POST /links': (op) => { op.operationId = 'createLink'; },
22+
'GET /api/links': (op) => { op.operationId = 'listLinks'; },
23+
'GET /links/{linkId}': (op) => { op.operationId = 'getLink'; },
24+
'POST /links/{linkId}': (op) => { op.operationId = 'updateLink'; },
25+
'DELETE /links/{link_id}': (op) => { op.operationId = 'deleteLink'; },
26+
'DELETE /links/delete_bulk': (op) => { op.operationId = 'deleteLinksBulk'; },
27+
'GET /links/expand': (op) => { op.operationId = 'expandLink'; },
28+
'GET /links/by-original-url': (op) => { op.operationId = 'getLinkByOriginalUrl'; },
29+
'GET /links/multiple-by-url': (op) => { op.operationId = 'getLinksByUrl'; },
30+
'GET /links/tweetbot': (op) => { op.operationId = 'createLinkSimple'; },
31+
'POST /links/public': (op) => { op.operationId = 'createLinkPublic'; },
32+
'POST /links/bulk': (op) => { op.operationId = 'createLinksBulk'; },
33+
'POST /links/examples': (op) => { op.operationId = 'createExampleLinks'; },
34+
'POST /links/duplicate/{linkId}': (op) => { op.operationId = 'duplicateLink'; },
35+
36+
// Links - Archive
37+
'POST /links/archive': (op) => { op.operationId = 'archiveLink'; },
38+
'POST /links/archive_bulk': (op) => { op.operationId = 'archiveLinksBulk'; },
39+
'POST /links/unarchive': (op) => { op.operationId = 'unarchiveLink'; },
40+
'POST /links/unarchive_bulk': (op) => { op.operationId = 'unarchiveLinksBulk'; },
41+
42+
// Links - QR
43+
'POST /links/qr/{linkIdString}': (op) => { op.operationId = 'generateQrCode'; },
44+
'POST /links/qr/bulk': (op) => { op.operationId = 'generateQrCodesBulk'; },
45+
46+
// Links - OpenGraph
47+
'GET /links/opengraph/{domainId}/{linkId}': (op) => { op.operationId = 'getLinkOpengraph'; },
48+
'PUT /links/opengraph/{domainId}/{linkId}': (op) => { op.operationId = 'updateLinkOpengraph'; },
49+
50+
// Links - Permissions
51+
'GET /links/permissions/{domainId}/{linkId}': (op) => { op.operationId = 'getLinkPermissions'; },
52+
'DELETE /links/permissions/{domainId}/{linkId}/{userId}': (op) => { op.operationId = 'deleteLinkPermission'; },
53+
'POST /links/permissions/{domainId}/{linkId}/{userId}': (op) => { op.operationId = 'addLinkPermission'; },
54+
55+
// Country targeting
56+
'GET /link_country/{linkId}': (op) => { op.operationId = 'getLinkCountries'; },
57+
'POST /link_country/{linkId}': (op) => { op.operationId = 'createLinkCountry'; },
58+
'POST /link_country/bulk/{linkId}': (op) => { op.operationId = 'createLinkCountriesBulk'; },
59+
'DELETE /link_country/{linkId}/{country}': (op) => { op.operationId = 'deleteLinkCountry'; },
60+
61+
// Region targeting
62+
'GET /link_region/{linkId}': (op) => { op.operationId = 'getLinkRegions'; },
63+
'POST /link_region/{linkId}': (op) => { op.operationId = 'createLinkRegion'; },
64+
'GET /link_region/list/{country}': (op) => { op.operationId = 'getRegionsByCountry'; },
65+
'POST /link_region/bulk/{linkId}': (op) => { op.operationId = 'createLinkRegionsBulk'; },
66+
'DELETE /link_region/{linkId}/{country}/{region}': (op) => { op.operationId = 'deleteLinkRegion'; },
67+
68+
// Folders
69+
'GET /links/folders/{domainId}': (op) => { op.operationId = 'listFolders'; },
70+
'GET /links/folders/{domainId}/{folderId}': (op) => { op.operationId = 'getFolder'; },
71+
'POST /links/folders': (op) => { op.operationId = 'createFolder'; },
72+
73+
// Domains
74+
'GET /api/domains': (op) => { op.operationId = 'listDomains'; },
75+
'GET /domains/{domainId}': (op) => { op.operationId = 'getDomain'; },
76+
'POST /domains/settings/{domainId}': (op) => { op.operationId = 'updateDomainSettings'; },
77+
'POST /domains': (op) => { op.operationId = 'createDomain'; },
78+
79+
// Tags
80+
'POST /tags/bulk': (op) => { op.operationId = 'addTagsBulk'; },
81+
},
82+
},
83+
},
1784
plugins: [
1885
"@hey-api/schemas",
1986
"@hey-api/sdk",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@short.io/client-node",
3-
"version": "2.3.0",
3+
"version": "3.0.0",
44
"description": "Official Short.io Node.js SDK for URL shortening, link management, QR codes, analytics, and geographic targeting",
55
"main": "dist/index.js",
66
"type": "module",

src/generated/client/client.gen.ts

Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
// This file is auto-generated by @hey-api/openapi-ts
2+
3+
import { createSseClient } from '../core/serverSentEvents.gen';
4+
import type { HttpMethod } from '../core/types.gen';
5+
import { getValidRequestBody } from '../core/utils.gen';
6+
import type {
7+
Client,
8+
Config,
9+
RequestOptions,
10+
ResolvedRequestOptions,
11+
} from './types.gen';
12+
import {
13+
buildUrl,
14+
createConfig,
15+
createInterceptors,
16+
getParseAs,
17+
mergeConfigs,
18+
mergeHeaders,
19+
setAuthParams,
20+
} from './utils.gen';
21+
22+
type ReqInit = Omit<RequestInit, 'body' | 'headers'> & {
23+
body?: any;
24+
headers: ReturnType<typeof mergeHeaders>;
25+
};
26+
27+
export const createClient = (config: Config = {}): Client => {
28+
let _config = mergeConfigs(createConfig(), config);
29+
30+
const getConfig = (): Config => ({ ..._config });
31+
32+
const setConfig = (config: Config): Config => {
33+
_config = mergeConfigs(_config, config);
34+
return getConfig();
35+
};
36+
37+
const interceptors = createInterceptors<
38+
Request,
39+
Response,
40+
unknown,
41+
ResolvedRequestOptions
42+
>();
43+
44+
const beforeRequest = async (options: RequestOptions) => {
45+
const opts = {
46+
..._config,
47+
...options,
48+
fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
49+
headers: mergeHeaders(_config.headers, options.headers),
50+
serializedBody: undefined,
51+
};
52+
53+
if (opts.security) {
54+
await setAuthParams({
55+
...opts,
56+
security: opts.security,
57+
});
58+
}
59+
60+
if (opts.requestValidator) {
61+
await opts.requestValidator(opts);
62+
}
63+
64+
if (opts.body !== undefined && opts.bodySerializer) {
65+
opts.serializedBody = opts.bodySerializer(opts.body);
66+
}
67+
68+
// remove Content-Type header if body is empty to avoid sending invalid requests
69+
if (opts.body === undefined || opts.serializedBody === '') {
70+
opts.headers.delete('Content-Type');
71+
}
72+
73+
const url = buildUrl(opts);
74+
75+
return { opts, url };
76+
};
77+
78+
const request: Client['request'] = async (options) => {
79+
// @ts-expect-error
80+
const { opts, url } = await beforeRequest(options);
81+
const requestInit: ReqInit = {
82+
redirect: 'follow',
83+
...opts,
84+
body: getValidRequestBody(opts),
85+
};
86+
87+
let request = new Request(url, requestInit);
88+
89+
for (const fn of interceptors.request.fns) {
90+
if (fn) {
91+
request = await fn(request, opts);
92+
}
93+
}
94+
95+
// fetch must be assigned here, otherwise it would throw the error:
96+
// TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
97+
const _fetch = opts.fetch!;
98+
let response: Response;
99+
100+
try {
101+
response = await _fetch(request);
102+
} catch (error) {
103+
// Handle fetch exceptions (AbortError, network errors, etc.)
104+
let finalError = error;
105+
106+
for (const fn of interceptors.error.fns) {
107+
if (fn) {
108+
finalError = (await fn(
109+
error,
110+
undefined as any,
111+
request,
112+
opts,
113+
)) as unknown;
114+
}
115+
}
116+
117+
finalError = finalError || ({} as unknown);
118+
119+
if (opts.throwOnError) {
120+
throw finalError;
121+
}
122+
123+
// Return error response
124+
return opts.responseStyle === 'data'
125+
? undefined
126+
: {
127+
error: finalError,
128+
request,
129+
response: undefined as any,
130+
};
131+
}
132+
133+
for (const fn of interceptors.response.fns) {
134+
if (fn) {
135+
response = await fn(response, request, opts);
136+
}
137+
}
138+
139+
const result = {
140+
request,
141+
response,
142+
};
143+
144+
if (response.ok) {
145+
const parseAs =
146+
(opts.parseAs === 'auto'
147+
? getParseAs(response.headers.get('Content-Type'))
148+
: opts.parseAs) ?? 'json';
149+
150+
if (
151+
response.status === 204 ||
152+
response.headers.get('Content-Length') === '0'
153+
) {
154+
let emptyData: any;
155+
switch (parseAs) {
156+
case 'arrayBuffer':
157+
case 'blob':
158+
case 'text':
159+
emptyData = await response[parseAs]();
160+
break;
161+
case 'formData':
162+
emptyData = new FormData();
163+
break;
164+
case 'stream':
165+
emptyData = response.body;
166+
break;
167+
case 'json':
168+
default:
169+
emptyData = {};
170+
break;
171+
}
172+
return opts.responseStyle === 'data'
173+
? emptyData
174+
: {
175+
data: emptyData,
176+
...result,
177+
};
178+
}
179+
180+
let data: any;
181+
switch (parseAs) {
182+
case 'arrayBuffer':
183+
case 'blob':
184+
case 'formData':
185+
case 'text':
186+
data = await response[parseAs]();
187+
break;
188+
case 'json': {
189+
// Some servers return 200 with no Content-Length and empty body.
190+
// response.json() would throw; read as text and parse if non-empty.
191+
const text = await response.text();
192+
data = text ? JSON.parse(text) : {};
193+
break;
194+
}
195+
case 'stream':
196+
return opts.responseStyle === 'data'
197+
? response.body
198+
: {
199+
data: response.body,
200+
...result,
201+
};
202+
}
203+
204+
if (parseAs === 'json') {
205+
if (opts.responseValidator) {
206+
await opts.responseValidator(data);
207+
}
208+
209+
if (opts.responseTransformer) {
210+
data = await opts.responseTransformer(data);
211+
}
212+
}
213+
214+
return opts.responseStyle === 'data'
215+
? data
216+
: {
217+
data,
218+
...result,
219+
};
220+
}
221+
222+
const textError = await response.text();
223+
let jsonError: unknown;
224+
225+
try {
226+
jsonError = JSON.parse(textError);
227+
} catch {
228+
// noop
229+
}
230+
231+
const error = jsonError ?? textError;
232+
let finalError = error;
233+
234+
for (const fn of interceptors.error.fns) {
235+
if (fn) {
236+
finalError = (await fn(error, response, request, opts)) as string;
237+
}
238+
}
239+
240+
finalError = finalError || ({} as string);
241+
242+
if (opts.throwOnError) {
243+
throw finalError;
244+
}
245+
246+
// TODO: we probably want to return error and improve types
247+
return opts.responseStyle === 'data'
248+
? undefined
249+
: {
250+
error: finalError,
251+
...result,
252+
};
253+
};
254+
255+
const makeMethodFn =
256+
(method: Uppercase<HttpMethod>) => (options: RequestOptions) =>
257+
request({ ...options, method });
258+
259+
const makeSseFn =
260+
(method: Uppercase<HttpMethod>) => async (options: RequestOptions) => {
261+
const { opts, url } = await beforeRequest(options);
262+
return createSseClient({
263+
...opts,
264+
body: opts.body as BodyInit | null | undefined,
265+
headers: opts.headers as unknown as Record<string, string>,
266+
method,
267+
onRequest: async (url, init) => {
268+
let request = new Request(url, init);
269+
for (const fn of interceptors.request.fns) {
270+
if (fn) {
271+
request = await fn(request, opts);
272+
}
273+
}
274+
return request;
275+
},
276+
serializedBody: getValidRequestBody(opts) as
277+
| BodyInit
278+
| null
279+
| undefined,
280+
url,
281+
});
282+
};
283+
284+
return {
285+
buildUrl,
286+
connect: makeMethodFn('CONNECT'),
287+
delete: makeMethodFn('DELETE'),
288+
get: makeMethodFn('GET'),
289+
getConfig,
290+
head: makeMethodFn('HEAD'),
291+
interceptors,
292+
options: makeMethodFn('OPTIONS'),
293+
patch: makeMethodFn('PATCH'),
294+
post: makeMethodFn('POST'),
295+
put: makeMethodFn('PUT'),
296+
request,
297+
setConfig,
298+
sse: {
299+
connect: makeSseFn('CONNECT'),
300+
delete: makeSseFn('DELETE'),
301+
get: makeSseFn('GET'),
302+
head: makeSseFn('HEAD'),
303+
options: makeSseFn('OPTIONS'),
304+
patch: makeSseFn('PATCH'),
305+
post: makeSseFn('POST'),
306+
put: makeSseFn('PUT'),
307+
trace: makeSseFn('TRACE'),
308+
},
309+
trace: makeMethodFn('TRACE'),
310+
} as Client;
311+
};

0 commit comments

Comments
 (0)