@potonz/shortlinks-manager-cf-d1 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/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/index.d.ts +140 -0
- package/dist/index.js +47 -0
- package/package.json +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Thomas Nguyen
|
|
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,15 @@
|
|
|
1
|
+
# @potonz/shortlinks-manage-cf-d1
|
|
2
|
+
|
|
3
|
+
Short links manager backend using Cloudflare D1 database.
|
|
4
|
+
|
|
5
|
+
## Setting up
|
|
6
|
+
|
|
7
|
+
You have to manually set up the DB for short links.
|
|
8
|
+
A function is provided for this, this should only be called once.
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { env } from "cloudflare:workers";
|
|
12
|
+
|
|
13
|
+
const backend = createD1Backend(env.DB);
|
|
14
|
+
await backend.setupTables();
|
|
15
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
// Generated by dts-bundle-generator v9.5.1
|
|
2
|
+
|
|
3
|
+
export interface D1Meta {
|
|
4
|
+
duration: number;
|
|
5
|
+
size_after: number;
|
|
6
|
+
rows_read: number;
|
|
7
|
+
rows_written: number;
|
|
8
|
+
last_row_id: number;
|
|
9
|
+
changed_db: boolean;
|
|
10
|
+
changes: number;
|
|
11
|
+
/**
|
|
12
|
+
* The region of the database instance that executed the query.
|
|
13
|
+
*/
|
|
14
|
+
served_by_region?: string;
|
|
15
|
+
/**
|
|
16
|
+
* True if-and-only-if the database instance that executed the query was the primary.
|
|
17
|
+
*/
|
|
18
|
+
served_by_primary?: boolean;
|
|
19
|
+
timings?: {
|
|
20
|
+
/**
|
|
21
|
+
* The duration of the SQL query execution by the database instance. It doesn't include any network time.
|
|
22
|
+
*/
|
|
23
|
+
sql_duration_ms: number;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Number of total attempts to execute the query, due to automatic retries.
|
|
27
|
+
* Note: All other fields in the response like `timings` only apply to the last attempt.
|
|
28
|
+
*/
|
|
29
|
+
total_attempts?: number;
|
|
30
|
+
}
|
|
31
|
+
export interface D1Response {
|
|
32
|
+
success: true;
|
|
33
|
+
meta: D1Meta & Record<string, unknown>;
|
|
34
|
+
error?: never;
|
|
35
|
+
}
|
|
36
|
+
export type D1Result<T = unknown> = D1Response & {
|
|
37
|
+
results: T[];
|
|
38
|
+
};
|
|
39
|
+
export interface D1ExecResult {
|
|
40
|
+
count: number;
|
|
41
|
+
duration: number;
|
|
42
|
+
}
|
|
43
|
+
export type D1SessionConstraint =
|
|
44
|
+
// Indicates that the first query should go to the primary, and the rest queries
|
|
45
|
+
// using the same D1DatabaseSession will go to any replica that is consistent with
|
|
46
|
+
// the bookmark maintained by the session (returned by the first query).
|
|
47
|
+
"first-primary"
|
|
48
|
+
// Indicates that the first query can go anywhere (primary or replica), and the rest queries
|
|
49
|
+
// using the same D1DatabaseSession will go to any replica that is consistent with
|
|
50
|
+
// the bookmark maintained by the session (returned by the first query).
|
|
51
|
+
| "first-unconstrained";
|
|
52
|
+
export type D1SessionBookmark = string;
|
|
53
|
+
declare abstract class D1Database {
|
|
54
|
+
prepare(query: string): D1PreparedStatement;
|
|
55
|
+
batch<T = unknown>(statements: D1PreparedStatement[]): Promise<D1Result<T>[]>;
|
|
56
|
+
exec(query: string): Promise<D1ExecResult>;
|
|
57
|
+
/**
|
|
58
|
+
* Creates a new D1 Session anchored at the given constraint or the bookmark.
|
|
59
|
+
* All queries executed using the created session will have sequential consistency,
|
|
60
|
+
* meaning that all writes done through the session will be visible in subsequent reads.
|
|
61
|
+
*
|
|
62
|
+
* @param constraintOrBookmark Either the session constraint or the explicit bookmark to anchor the created session.
|
|
63
|
+
*/
|
|
64
|
+
withSession(constraintOrBookmark?: D1SessionBookmark | D1SessionConstraint): D1DatabaseSession;
|
|
65
|
+
/**
|
|
66
|
+
* @deprecated dump() will be removed soon, only applies to deprecated alpha v1 databases.
|
|
67
|
+
*/
|
|
68
|
+
dump(): Promise<ArrayBuffer>;
|
|
69
|
+
}
|
|
70
|
+
declare abstract class D1DatabaseSession {
|
|
71
|
+
prepare(query: string): D1PreparedStatement;
|
|
72
|
+
batch<T = unknown>(statements: D1PreparedStatement[]): Promise<D1Result<T>[]>;
|
|
73
|
+
/**
|
|
74
|
+
* @returns The latest session bookmark across all executed queries on the session.
|
|
75
|
+
* If no query has been executed yet, `null` is returned.
|
|
76
|
+
*/
|
|
77
|
+
getBookmark(): D1SessionBookmark | null;
|
|
78
|
+
}
|
|
79
|
+
declare abstract class D1PreparedStatement {
|
|
80
|
+
bind(...values: unknown[]): D1PreparedStatement;
|
|
81
|
+
first<T = unknown>(colName: string): Promise<T | null>;
|
|
82
|
+
first<T = Record<string, unknown>>(): Promise<T | null>;
|
|
83
|
+
run<T = Record<string, unknown>>(): Promise<D1Result<T>>;
|
|
84
|
+
all<T = Record<string, unknown>>(): Promise<D1Result<T>>;
|
|
85
|
+
raw<T = unknown[]>(options: {
|
|
86
|
+
columnNames: true;
|
|
87
|
+
}): Promise<[
|
|
88
|
+
string[],
|
|
89
|
+
...T[]
|
|
90
|
+
]>;
|
|
91
|
+
raw<T = unknown[]>(options?: {
|
|
92
|
+
columnNames?: false;
|
|
93
|
+
}): Promise<T[]>;
|
|
94
|
+
}
|
|
95
|
+
// Generated by dts-bundle-generator v9.5.1
|
|
96
|
+
export interface IShortLinksManagerBackend {
|
|
97
|
+
/**
|
|
98
|
+
* Initialise any logic before the manager can do its thing. E.g. setting up tables.
|
|
99
|
+
* Run once when {@link createManager} is called
|
|
100
|
+
*/
|
|
101
|
+
init?: () => unknown;
|
|
102
|
+
/**
|
|
103
|
+
* Get target URL for the given short ID
|
|
104
|
+
* @param {string} shortId
|
|
105
|
+
* @returns the short ID or null if not found
|
|
106
|
+
*/
|
|
107
|
+
getTargetUrl: (shortId: string) => string | null | Promise<string | null>;
|
|
108
|
+
/**
|
|
109
|
+
* Create a short link map with the given short ID and target URL
|
|
110
|
+
* @param {string} shortId
|
|
111
|
+
* @param {string} targetUrl
|
|
112
|
+
*/
|
|
113
|
+
createShortLink: (shortId: string, targetUrl: string) => void | Promise<void>;
|
|
114
|
+
/**
|
|
115
|
+
* Check the provided list of short IDs and return the ones that already exist.
|
|
116
|
+
* @param {string[]} shortIds
|
|
117
|
+
*/
|
|
118
|
+
checkShortIdsExist: (shortIds: string[]) => string[] | Promise<string[]>;
|
|
119
|
+
/**
|
|
120
|
+
* Update last accessed time to current timestamp
|
|
121
|
+
* @param shortId
|
|
122
|
+
*/
|
|
123
|
+
updateShortLinkLastAccessTime(shortId: string): void | Promise<void>;
|
|
124
|
+
/**
|
|
125
|
+
* Remove unused links that are older than the given maxAge
|
|
126
|
+
* @param maxAge number of days the record should be kept
|
|
127
|
+
*/
|
|
128
|
+
cleanUnusedLinks(maxAge: number): void | Promise<void>;
|
|
129
|
+
}
|
|
130
|
+
export interface IShortLinksManagerD1Backend extends IShortLinksManagerBackend {
|
|
131
|
+
setupTables: () => Promise<void>;
|
|
132
|
+
}
|
|
133
|
+
export declare function createD1Backend(db: D1Database): IShortLinksManagerD1Backend;
|
|
134
|
+
declare const _default: {};
|
|
135
|
+
|
|
136
|
+
export {
|
|
137
|
+
_default as default,
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/* MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Thomas Nguyen
|
|
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.
|
|
22
|
+
*/
|
|
23
|
+
/*! *****************************************************************************
|
|
24
|
+
Copyright (c) Cloudflare. All rights reserved.
|
|
25
|
+
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
26
|
+
|
|
27
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
28
|
+
this file except in compliance with the License. You may obtain a copy of the
|
|
29
|
+
License at http://www.apache.org/licenses/LICENSE-2.0
|
|
30
|
+
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
31
|
+
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
|
32
|
+
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
|
33
|
+
MERCHANTABLITY OR NON-INFRINGEMENT.
|
|
34
|
+
See the Apache Version 2.0 License for specific language governing permissions
|
|
35
|
+
and limitations under the License.
|
|
36
|
+
***************************************************************************** */function F(j){let w=null,y=null,z=null,B=null,C=null;return{async setupTables(){await j.prepare(`
|
|
37
|
+
CREATE TABLE IF NOT EXISTS sl_links_map (
|
|
38
|
+
short_id VARCHAR(255) NOT NULL PRIMARY KEY,
|
|
39
|
+
target_url VARCHAR(65535) NOT NULL,
|
|
40
|
+
last_accessed_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
41
|
+
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
CREATE INDEX IF NOT EXISTS idx_sl_links_map_last_accessed_at ON sl_links_map(last_accessed_at);
|
|
45
|
+
|
|
46
|
+
PRAGMA optimize;
|
|
47
|
+
`).run()},async getTargetUrl(f){if(!w)w=j.prepare("SELECT target_url FROM sl_links_map WHERE short_id = ? LIMIT 1");return(await w.bind(f).first())?.target_url??null},async createShortLink(f,q){if(!z)z=j.prepare("INSERT INTO sl_links_map (short_id, target_url) VALUES (?, ?)");await z.bind(f,q).run()},async checkShortIdsExist(f){if(!y){let D=Array.from("?".repeat(f.length)).join(",");y=j.prepare(`SELECT short_id FROM sl_links_map WHERE short_id IN (${D})`)}let q=await y.bind(...f).all();if(!q.success)return[];return q.results.map((D)=>D.short_id)},async updateShortLinkLastAccessTime(f){if(!B)B=j.prepare("UPDATE sl_links_map SET last_accessed_at = CURRENT_TIMESTAMP WHERE short_id = ?");await B.bind(f).run()},async cleanUnusedLinks(f){if(!C)C=j.prepare("DELETE FROM sl_links_map WHERE last_accessed_at < datetime(CURRENT_TIMESTAMP, ?)");await C.bind(`-${f} days`).run()}}}var J={};export{J as default,F as createD1Backend};
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@potonz/shortlinks-manager-cf-d1",
|
|
3
|
+
"description": "Short links manager extension for Cloudflare D1",
|
|
4
|
+
"author": {
|
|
5
|
+
"name": "Thomas Nguyen",
|
|
6
|
+
"email": "tom@tomng.dev"
|
|
7
|
+
},
|
|
8
|
+
"homepage": "https://github.com/potonz/shortlinks-manager",
|
|
9
|
+
"repository": {
|
|
10
|
+
"url": "git+https://github.com/potonz/shortlinks-manager.git",
|
|
11
|
+
"type": "git"
|
|
12
|
+
},
|
|
13
|
+
"version": "0.1.0",
|
|
14
|
+
"type": "module",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@potonz/shortlinks-manager": "0.1.0"
|
|
18
|
+
},
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"module": "./dist/index.js",
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"import": "./dist/index.js",
|
|
25
|
+
"require": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"access": "public"
|
|
34
|
+
}
|
|
35
|
+
}
|