@hasna/shortlinks 0.1.19 → 0.1.21
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/dist/cli/index.js +27 -10
- package/dist/cloudflare.js +3 -1
- package/dist/index.js +27 -10
- package/dist/server.d.ts +5 -0
- package/dist/server.js +27 -10
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -3335,7 +3335,9 @@ function normalizeHostname(input) {
|
|
|
3335
3335
|
throw new Error(`Invalid domain: ${input}`);
|
|
3336
3336
|
}
|
|
3337
3337
|
hostname = hostname.replace(/\.$/, "");
|
|
3338
|
-
|
|
3338
|
+
const labels = hostname.split(".");
|
|
3339
|
+
const labelsAreValid = labels.every((label) => label.length >= 1 && label.length <= 63 && /^[a-z0-9-]+$/.test(label) && !label.startsWith("-") && !label.endsWith("-"));
|
|
3340
|
+
if (hostname.length > 253 || !labelsAreValid) {
|
|
3339
3341
|
throw new Error(`Invalid domain: ${input}`);
|
|
3340
3342
|
}
|
|
3341
3343
|
return hostname;
|
|
@@ -4138,6 +4140,9 @@ function getClientIp(request) {
|
|
|
4138
4140
|
function isExpired(link) {
|
|
4139
4141
|
return Boolean(link.expires_at && new Date(link.expires_at).getTime() <= Date.now());
|
|
4140
4142
|
}
|
|
4143
|
+
function logRecordClickError(link) {
|
|
4144
|
+
console.error(`[shortlinks] Click analytics recording failed for ${link.hostname}/${link.slug}.`);
|
|
4145
|
+
}
|
|
4141
4146
|
function createShortlinksHandler(options = {}) {
|
|
4142
4147
|
const store = options.store || new ShortlinksStore(options.dbPath);
|
|
4143
4148
|
const redirectStatus = options.redirectStatus || 302;
|
|
@@ -4176,16 +4181,28 @@ function createShortlinksHandler(options = {}) {
|
|
|
4176
4181
|
if (isExpired(link))
|
|
4177
4182
|
return json({ error: "Shortlink is expired.", slug, host }, 410);
|
|
4178
4183
|
if (request.method === "GET") {
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4185
|
-
|
|
4186
|
-
|
|
4184
|
+
try {
|
|
4185
|
+
await store.recordClick(link, {
|
|
4186
|
+
ip: getClientIp(request),
|
|
4187
|
+
userAgent: request.headers.get("user-agent"),
|
|
4188
|
+
referer: request.headers.get("referer"),
|
|
4189
|
+
country: request.headers.get("cf-ipcountry"),
|
|
4190
|
+
metadata: {
|
|
4191
|
+
path: url.pathname,
|
|
4192
|
+
query: url.search
|
|
4193
|
+
}
|
|
4194
|
+
});
|
|
4195
|
+
} catch (error) {
|
|
4196
|
+
if (options.onRecordClickError) {
|
|
4197
|
+
try {
|
|
4198
|
+
await options.onRecordClickError(error, { link, request });
|
|
4199
|
+
} catch {
|
|
4200
|
+
logRecordClickError(link);
|
|
4201
|
+
}
|
|
4202
|
+
} else {
|
|
4203
|
+
logRecordClickError(link);
|
|
4187
4204
|
}
|
|
4188
|
-
}
|
|
4205
|
+
}
|
|
4189
4206
|
}
|
|
4190
4207
|
return Response.redirect(link.destination_url, redirectStatus);
|
|
4191
4208
|
};
|
package/dist/cloudflare.js
CHANGED
|
@@ -20,7 +20,9 @@ function normalizeHostname(input) {
|
|
|
20
20
|
throw new Error(`Invalid domain: ${input}`);
|
|
21
21
|
}
|
|
22
22
|
hostname = hostname.replace(/\.$/, "");
|
|
23
|
-
|
|
23
|
+
const labels = hostname.split(".");
|
|
24
|
+
const labelsAreValid = labels.every((label) => label.length >= 1 && label.length <= 63 && /^[a-z0-9-]+$/.test(label) && !label.startsWith("-") && !label.endsWith("-"));
|
|
25
|
+
if (hostname.length > 253 || !labelsAreValid) {
|
|
24
26
|
throw new Error(`Invalid domain: ${input}`);
|
|
25
27
|
}
|
|
26
28
|
return hostname;
|
package/dist/index.js
CHANGED
|
@@ -71,7 +71,9 @@ function normalizeHostname(input) {
|
|
|
71
71
|
throw new Error(`Invalid domain: ${input}`);
|
|
72
72
|
}
|
|
73
73
|
hostname = hostname.replace(/\.$/, "");
|
|
74
|
-
|
|
74
|
+
const labels = hostname.split(".");
|
|
75
|
+
const labelsAreValid = labels.every((label) => label.length >= 1 && label.length <= 63 && /^[a-z0-9-]+$/.test(label) && !label.startsWith("-") && !label.endsWith("-"));
|
|
76
|
+
if (hostname.length > 253 || !labelsAreValid) {
|
|
75
77
|
throw new Error(`Invalid domain: ${input}`);
|
|
76
78
|
}
|
|
77
79
|
return hostname;
|
|
@@ -874,6 +876,9 @@ function getClientIp(request) {
|
|
|
874
876
|
function isExpired(link) {
|
|
875
877
|
return Boolean(link.expires_at && new Date(link.expires_at).getTime() <= Date.now());
|
|
876
878
|
}
|
|
879
|
+
function logRecordClickError(link) {
|
|
880
|
+
console.error(`[shortlinks] Click analytics recording failed for ${link.hostname}/${link.slug}.`);
|
|
881
|
+
}
|
|
877
882
|
function createShortlinksHandler(options = {}) {
|
|
878
883
|
const store = options.store || new ShortlinksStore(options.dbPath);
|
|
879
884
|
const redirectStatus = options.redirectStatus || 302;
|
|
@@ -912,16 +917,28 @@ function createShortlinksHandler(options = {}) {
|
|
|
912
917
|
if (isExpired(link))
|
|
913
918
|
return json({ error: "Shortlink is expired.", slug, host }, 410);
|
|
914
919
|
if (request.method === "GET") {
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
920
|
+
try {
|
|
921
|
+
await store.recordClick(link, {
|
|
922
|
+
ip: getClientIp(request),
|
|
923
|
+
userAgent: request.headers.get("user-agent"),
|
|
924
|
+
referer: request.headers.get("referer"),
|
|
925
|
+
country: request.headers.get("cf-ipcountry"),
|
|
926
|
+
metadata: {
|
|
927
|
+
path: url.pathname,
|
|
928
|
+
query: url.search
|
|
929
|
+
}
|
|
930
|
+
});
|
|
931
|
+
} catch (error) {
|
|
932
|
+
if (options.onRecordClickError) {
|
|
933
|
+
try {
|
|
934
|
+
await options.onRecordClickError(error, { link, request });
|
|
935
|
+
} catch {
|
|
936
|
+
logRecordClickError(link);
|
|
937
|
+
}
|
|
938
|
+
} else {
|
|
939
|
+
logRecordClickError(link);
|
|
923
940
|
}
|
|
924
|
-
}
|
|
941
|
+
}
|
|
925
942
|
}
|
|
926
943
|
return Response.redirect(link.destination_url, redirectStatus);
|
|
927
944
|
};
|
package/dist/server.d.ts
CHANGED
|
@@ -12,11 +12,16 @@ export interface ShortlinksRuntimeStore {
|
|
|
12
12
|
resolve(hostname: string, slug: string): Link | null | Promise<Link | null>;
|
|
13
13
|
recordClick(link: Link, input?: ClickInput): unknown | Promise<unknown>;
|
|
14
14
|
}
|
|
15
|
+
export interface RecordClickErrorContext {
|
|
16
|
+
link: Link;
|
|
17
|
+
request: Request;
|
|
18
|
+
}
|
|
15
19
|
export interface ShortlinksHandlerOptions {
|
|
16
20
|
store?: ShortlinksRuntimeStore;
|
|
17
21
|
dbPath?: string;
|
|
18
22
|
defaultHost?: string;
|
|
19
23
|
redirectStatus?: 301 | 302 | 307 | 308;
|
|
24
|
+
onRecordClickError?: (error: unknown, context: RecordClickErrorContext) => void | Promise<void>;
|
|
20
25
|
}
|
|
21
26
|
export declare function createShortlinksHandler(options?: ShortlinksHandlerOptions): (request: Request) => Response | Promise<Response>;
|
|
22
27
|
export declare function serveShortlinks(options?: ShortlinksHandlerOptions & {
|
package/dist/server.js
CHANGED
|
@@ -72,7 +72,9 @@ function normalizeHostname(input) {
|
|
|
72
72
|
throw new Error(`Invalid domain: ${input}`);
|
|
73
73
|
}
|
|
74
74
|
hostname = hostname.replace(/\.$/, "");
|
|
75
|
-
|
|
75
|
+
const labels = hostname.split(".");
|
|
76
|
+
const labelsAreValid = labels.every((label) => label.length >= 1 && label.length <= 63 && /^[a-z0-9-]+$/.test(label) && !label.startsWith("-") && !label.endsWith("-"));
|
|
77
|
+
if (hostname.length > 253 || !labelsAreValid) {
|
|
76
78
|
throw new Error(`Invalid domain: ${input}`);
|
|
77
79
|
}
|
|
78
80
|
return hostname;
|
|
@@ -555,6 +557,9 @@ function getClientIp(request) {
|
|
|
555
557
|
function isExpired(link) {
|
|
556
558
|
return Boolean(link.expires_at && new Date(link.expires_at).getTime() <= Date.now());
|
|
557
559
|
}
|
|
560
|
+
function logRecordClickError(link) {
|
|
561
|
+
console.error(`[shortlinks] Click analytics recording failed for ${link.hostname}/${link.slug}.`);
|
|
562
|
+
}
|
|
558
563
|
function createShortlinksHandler(options = {}) {
|
|
559
564
|
const store = options.store || new ShortlinksStore(options.dbPath);
|
|
560
565
|
const redirectStatus = options.redirectStatus || 302;
|
|
@@ -593,16 +598,28 @@ function createShortlinksHandler(options = {}) {
|
|
|
593
598
|
if (isExpired(link))
|
|
594
599
|
return json({ error: "Shortlink is expired.", slug, host }, 410);
|
|
595
600
|
if (request.method === "GET") {
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
601
|
+
try {
|
|
602
|
+
await store.recordClick(link, {
|
|
603
|
+
ip: getClientIp(request),
|
|
604
|
+
userAgent: request.headers.get("user-agent"),
|
|
605
|
+
referer: request.headers.get("referer"),
|
|
606
|
+
country: request.headers.get("cf-ipcountry"),
|
|
607
|
+
metadata: {
|
|
608
|
+
path: url.pathname,
|
|
609
|
+
query: url.search
|
|
610
|
+
}
|
|
611
|
+
});
|
|
612
|
+
} catch (error) {
|
|
613
|
+
if (options.onRecordClickError) {
|
|
614
|
+
try {
|
|
615
|
+
await options.onRecordClickError(error, { link, request });
|
|
616
|
+
} catch {
|
|
617
|
+
logRecordClickError(link);
|
|
618
|
+
}
|
|
619
|
+
} else {
|
|
620
|
+
logRecordClickError(link);
|
|
604
621
|
}
|
|
605
|
-
}
|
|
622
|
+
}
|
|
606
623
|
}
|
|
607
624
|
return Response.redirect(link.destination_url, redirectStatus);
|
|
608
625
|
};
|
package/package.json
CHANGED