polystore 0.15.12 → 0.16.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/index.d.ts +82 -0
- package/index.js +809 -0
- package/package.json +20 -23
- package/readme.md +327 -52
- package/src/clients/Client.js +0 -7
- package/src/clients/api.js +0 -30
- package/src/clients/cloudflare.js +0 -56
- package/src/clients/cookie.js +0 -51
- package/src/clients/etcd.js +0 -29
- package/src/clients/file.js +0 -70
- package/src/clients/folder.js +0 -49
- package/src/clients/forage.js +0 -29
- package/src/clients/index.js +0 -25
- package/src/clients/level.js +0 -40
- package/src/clients/memory.js +0 -19
- package/src/clients/redis.js +0 -51
- package/src/clients/storage.js +0 -25
- package/src/index.d.ts +0 -209
- package/src/index.js +0 -278
- package/src/server.js +0 -81
- package/src/utils.js +0 -47
package/src/index.d.ts
DELETED
|
@@ -1,209 +0,0 @@
|
|
|
1
|
-
export type Options = { expires?: number | string | null };
|
|
2
|
-
export type Value = string | { [key: string]: Value } | Value[];
|
|
3
|
-
|
|
4
|
-
export interface Store {
|
|
5
|
-
/**
|
|
6
|
-
* Save the data on an autogenerated key, can add expiration as well:
|
|
7
|
-
*
|
|
8
|
-
* ```js
|
|
9
|
-
* const key1 = await store.add("value1");
|
|
10
|
-
* const key2 = await store.add({ hello: "world" });
|
|
11
|
-
* const key3 = await store.add("value3", { expires: "1h" });
|
|
12
|
-
* ```
|
|
13
|
-
*
|
|
14
|
-
* **[→ Full .add() Docs](https://polystore.dev/documentation#add)**
|
|
15
|
-
*/
|
|
16
|
-
add: <T = Value>(value: T, options?: Options) => Promise<string>;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Save the data on the given key, can add expiration as well:
|
|
20
|
-
*
|
|
21
|
-
* ```js
|
|
22
|
-
* const key = await store.set("key1", "value1");
|
|
23
|
-
* await store.set("key2", { hello: "world" });
|
|
24
|
-
* await store.set("key3", "value3", { expires: "1h" });
|
|
25
|
-
* ```
|
|
26
|
-
*
|
|
27
|
-
* **[→ Full .set() Docs](https://polystore.dev/documentation#set)**
|
|
28
|
-
*/
|
|
29
|
-
set: <T = Value>(key: string, value: T, options?: Options) => Promise<string>;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Read a single value from the KV store:
|
|
33
|
-
*
|
|
34
|
-
* ```js
|
|
35
|
-
* const value1 = await store.get("key1");
|
|
36
|
-
* // null (doesn't exist or has expired)
|
|
37
|
-
* const value2 = await store.get("key2");
|
|
38
|
-
* // "value2"
|
|
39
|
-
* const value3 = await store.get("key3");
|
|
40
|
-
* // { hello: "world" }
|
|
41
|
-
* ```
|
|
42
|
-
*
|
|
43
|
-
* **[→ Full .get() Docs](https://polystore.dev/documentation#get)**
|
|
44
|
-
*/
|
|
45
|
-
get: <T = Value>(key: string) => Promise<T | null>;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Check whether a key exists or not:
|
|
49
|
-
*
|
|
50
|
-
* ```js
|
|
51
|
-
* if (await store.has("key1")) { ... }
|
|
52
|
-
* ```
|
|
53
|
-
*
|
|
54
|
-
* If you are going to use the value, it's better to just read it:
|
|
55
|
-
*
|
|
56
|
-
* ```js
|
|
57
|
-
* const val = await store.get("key1");
|
|
58
|
-
* if (val) { ... }
|
|
59
|
-
* ```
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* **[→ Full .has() Docs](https://polystore.dev/documentation#has)**
|
|
63
|
-
*/
|
|
64
|
-
has: (key: string) => Promise<boolean>;
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Remove a single key and its value from the store:
|
|
68
|
-
*
|
|
69
|
-
* ```js
|
|
70
|
-
* const key = await store.del("key1");
|
|
71
|
-
* ```
|
|
72
|
-
*
|
|
73
|
-
* **[→ Full .del() Docs](https://polystore.dev/documentation#del)**
|
|
74
|
-
*/
|
|
75
|
-
del: (key: string) => Promise<string>;
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Return an array of the entries, in the [key, value] format:
|
|
79
|
-
*
|
|
80
|
-
* ```js
|
|
81
|
-
* const entries = await store.entries();
|
|
82
|
-
* // [["key1", "value1"], ["key2", { hello: "world" }], ...]
|
|
83
|
-
*
|
|
84
|
-
* // To limit it to a given prefix, use `.prefix()`:
|
|
85
|
-
* const sessions = await store.prefix("session:").entries();
|
|
86
|
-
* ```
|
|
87
|
-
*
|
|
88
|
-
* **[→ Full .entries() Docs](https://polystore.dev/documentation#entries)**
|
|
89
|
-
*/
|
|
90
|
-
entries: <T = Value>() => Promise<[key: string, value: T][]>;
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Return an array of the keys in the store:
|
|
94
|
-
*
|
|
95
|
-
* ```js
|
|
96
|
-
* const keys = await store.keys();
|
|
97
|
-
* // ["key1", "key2", ...]
|
|
98
|
-
*
|
|
99
|
-
* // To limit it to a given prefix, use `.prefix()`:
|
|
100
|
-
* const sessions = await store.prefix("session:").keys();
|
|
101
|
-
* ```
|
|
102
|
-
*
|
|
103
|
-
* **[→ Full .keys() Docs](https://polystore.dev/documentation#keys)**
|
|
104
|
-
*/
|
|
105
|
-
keys: () => Promise<string[]>;
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Return an array of the values in the store:
|
|
109
|
-
*
|
|
110
|
-
* ```js
|
|
111
|
-
* const values = await store.values();
|
|
112
|
-
* // ["value1", { hello: "world" }, ...]
|
|
113
|
-
*
|
|
114
|
-
* // To limit it to a given prefix, use `.prefix()`:
|
|
115
|
-
* const sessions = await store.prefix("session:").values();
|
|
116
|
-
* ```
|
|
117
|
-
*
|
|
118
|
-
* **[→ Full .values() Docs](https://polystore.dev/documentation#values)**
|
|
119
|
-
*/
|
|
120
|
-
values: <T = Value>() => Promise<T[]>;
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Return an object with the keys:values in the store:
|
|
124
|
-
*
|
|
125
|
-
* ```js
|
|
126
|
-
* const obj = await store.all();
|
|
127
|
-
* // { key1: "value1", key2: { hello: "world" }, ... }
|
|
128
|
-
*
|
|
129
|
-
* // To limit it to a given prefix, use `.prefix()`:
|
|
130
|
-
* const sessions = await store.prefix("session:").all();
|
|
131
|
-
* ```
|
|
132
|
-
*
|
|
133
|
-
* **[→ Full .all() Docs](https://polystore.dev/documentation#all)**
|
|
134
|
-
*/
|
|
135
|
-
all: <T = Value>() => Promise<{ [key: string]: T }>;
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Delete all of the records of the store:
|
|
139
|
-
*
|
|
140
|
-
* ```js
|
|
141
|
-
* await store.clear();
|
|
142
|
-
* ```
|
|
143
|
-
*
|
|
144
|
-
* It's useful for cache invalidation, clearing the data, and testing.
|
|
145
|
-
*
|
|
146
|
-
* **[→ Full .clear() Docs](https://polystore.dev/documentation#clear)**
|
|
147
|
-
*/
|
|
148
|
-
clear: () => Promise<null>;
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Create a substore where all the keys are stored with
|
|
152
|
-
* the given prefix:
|
|
153
|
-
*
|
|
154
|
-
* ```js
|
|
155
|
-
* const session = store.prefix("session:");
|
|
156
|
-
* await session.set("key1", "value1");
|
|
157
|
-
* console.log(await session.entries()); // session.
|
|
158
|
-
* // [["key1", "value1"]]
|
|
159
|
-
* console.log(await store.entries()); // store.
|
|
160
|
-
* // [["session:key1", "value1"]]
|
|
161
|
-
* ```
|
|
162
|
-
*
|
|
163
|
-
* **[→ Full .prefix() Docs](https://polystore.dev/documentation#prefix)**
|
|
164
|
-
*/
|
|
165
|
-
prefix: (prefix: string) => Store;
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Stop the connection to the store, if any:
|
|
169
|
-
*
|
|
170
|
-
* ```js
|
|
171
|
-
* await session.set("key1", "value1");
|
|
172
|
-
* await store.close();
|
|
173
|
-
* await session.set("key2", "value2"); // error
|
|
174
|
-
* ```
|
|
175
|
-
*
|
|
176
|
-
* **[→ Full .close() Docs](https://polystore.dev/documentation#close)**
|
|
177
|
-
*/
|
|
178
|
-
close?: () => Promise<null>;
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* An iterator that goes through all of the key:value pairs in the client
|
|
182
|
-
*
|
|
183
|
-
* ```js
|
|
184
|
-
* for await (const [key, value] of store) {
|
|
185
|
-
* console.log(key, value);
|
|
186
|
-
* }
|
|
187
|
-
* ```
|
|
188
|
-
*
|
|
189
|
-
* **[→ Full Iterator Docs](https://polystore.dev/documentation#iterator)**
|
|
190
|
-
*/
|
|
191
|
-
[Symbol.asyncIterator]: () => {
|
|
192
|
-
next: () => Promise<{ value: [string, Value] }>;
|
|
193
|
-
};
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Create a Store instance with the given client:
|
|
198
|
-
*
|
|
199
|
-
* ```js
|
|
200
|
-
* import kv from "polystore";
|
|
201
|
-
* const store1 = kv(new Map());
|
|
202
|
-
* const store2 = kv(localStorage);
|
|
203
|
-
* const store3 = kv(redisClient);
|
|
204
|
-
* const store4 = kv(yourOwnStore);
|
|
205
|
-
* ```
|
|
206
|
-
*
|
|
207
|
-
* **[→ Full kv() Docs](https://polystore.dev/documentation)**
|
|
208
|
-
*/
|
|
209
|
-
export default function (client?: any): Store;
|
package/src/index.js
DELETED
|
@@ -1,278 +0,0 @@
|
|
|
1
|
-
import clients from "./clients/index.js";
|
|
2
|
-
import { createId, parse, unix } from "./utils.js";
|
|
3
|
-
|
|
4
|
-
class Store {
|
|
5
|
-
PREFIX = "";
|
|
6
|
-
|
|
7
|
-
constructor(clientPromise) {
|
|
8
|
-
this.promise = Promise.resolve(clientPromise).then(async (client) => {
|
|
9
|
-
this.client = this.#find(client);
|
|
10
|
-
this.#validate(this.client);
|
|
11
|
-
this.promise = null;
|
|
12
|
-
await this.client.promise;
|
|
13
|
-
return client;
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
#find(store) {
|
|
18
|
-
// Already a fully compliant KV store
|
|
19
|
-
if (store instanceof Store) return store.client;
|
|
20
|
-
|
|
21
|
-
// One of the supported ones, so we receive an instance and
|
|
22
|
-
// wrap it with the client wrapper
|
|
23
|
-
for (let client of Object.values(clients)) {
|
|
24
|
-
if (client.test && client.test(store)) {
|
|
25
|
-
return new client(store);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// We get passed a class
|
|
30
|
-
if (
|
|
31
|
-
typeof store === "function" &&
|
|
32
|
-
/^class\s/.test(Function.prototype.toString.call(store))
|
|
33
|
-
) {
|
|
34
|
-
return new store();
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// A raw one, we just receive the single instance to use directly
|
|
38
|
-
return store;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
#validate(client) {
|
|
42
|
-
if (!client) throw new Error("No client received");
|
|
43
|
-
if (!client.set || !client.get || !client.iterate) {
|
|
44
|
-
throw new Error("Client should have .get(), .set() and .iterate()");
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (!client.EXPIRES) {
|
|
48
|
-
for (let method of ["has", "keys", "values"]) {
|
|
49
|
-
if (client[method]) {
|
|
50
|
-
throw new Error(
|
|
51
|
-
`You can only define client.${method}() when the client manages the expiration.`,
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Check if the given data is fresh or not; if
|
|
59
|
-
#isFresh(data, key) {
|
|
60
|
-
// Should never happen, but COULD happen; schedule it for
|
|
61
|
-
// removal and mark it as stale
|
|
62
|
-
if (!data || !data.value || typeof data !== "object") {
|
|
63
|
-
if (key) this.del(key);
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// It never expires, so keep it
|
|
68
|
-
if (data.expires === null) return true;
|
|
69
|
-
|
|
70
|
-
// It's fresh, keep it
|
|
71
|
-
if (data.expires > Date.now()) return true;
|
|
72
|
-
|
|
73
|
-
// It's expired, remove it
|
|
74
|
-
if (key) this.del(key);
|
|
75
|
-
return false;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async add(value, options = {}) {
|
|
79
|
-
await this.promise;
|
|
80
|
-
let expires = parse(options.expire ?? options.expires);
|
|
81
|
-
|
|
82
|
-
// Use the underlying one from the client if found
|
|
83
|
-
if (this.client.add) {
|
|
84
|
-
if (this.client.EXPIRES) {
|
|
85
|
-
return await this.client.add(this.PREFIX, value, { expires });
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// In the data we need the timestamp since we need it "absolute":
|
|
89
|
-
expires = unix(expires);
|
|
90
|
-
const key = await this.client.add(this.PREFIX, { expires, value });
|
|
91
|
-
return key;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const key = createId();
|
|
95
|
-
return this.set(key, value, { expires });
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
async set(key, value, options = {}) {
|
|
99
|
-
await this.promise;
|
|
100
|
-
const id = this.PREFIX + key;
|
|
101
|
-
let expires = parse(options.expire ?? options.expires);
|
|
102
|
-
|
|
103
|
-
// Quick delete
|
|
104
|
-
if (value === null || (typeof expires === "number" && expires <= 0)) {
|
|
105
|
-
return this.del(id);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// The client manages the expiration, so let it manage it
|
|
109
|
-
if (this.client.EXPIRES) {
|
|
110
|
-
await this.client.set(id, value, { expires });
|
|
111
|
-
return key;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// In the data we need the timestamp since we need it "absolute":
|
|
115
|
-
expires = unix(expires);
|
|
116
|
-
await this.client.set(id, { expires, value });
|
|
117
|
-
return key;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
async get(key) {
|
|
121
|
-
await this.promise;
|
|
122
|
-
const id = this.PREFIX + key;
|
|
123
|
-
|
|
124
|
-
const data = (await this.client.get(id)) ?? null;
|
|
125
|
-
|
|
126
|
-
// No value; nothing to do/check
|
|
127
|
-
if (data === null) return null;
|
|
128
|
-
|
|
129
|
-
// The client already managed expiration and there's STILL some data,
|
|
130
|
-
// so we can assume it's the raw user data
|
|
131
|
-
if (this.client.EXPIRES) return data;
|
|
132
|
-
|
|
133
|
-
if (!this.#isFresh(data, key)) return null;
|
|
134
|
-
return data.value;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
async has(key) {
|
|
138
|
-
await this.promise;
|
|
139
|
-
const id = this.PREFIX + key;
|
|
140
|
-
|
|
141
|
-
if (this.client.has) {
|
|
142
|
-
return this.client.has(id);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
return (await this.get(key)) !== null;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async del(key) {
|
|
149
|
-
await this.promise;
|
|
150
|
-
const id = this.PREFIX + key;
|
|
151
|
-
|
|
152
|
-
if (this.client.del) {
|
|
153
|
-
await this.client.del(id);
|
|
154
|
-
return key;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
await this.client.set(id, null, { expires: 0 });
|
|
158
|
-
return key;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
async *[Symbol.asyncIterator]() {
|
|
162
|
-
await this.promise;
|
|
163
|
-
|
|
164
|
-
for await (const [name, data] of this.client.iterate(this.PREFIX)) {
|
|
165
|
-
const key = name.slice(this.PREFIX.length);
|
|
166
|
-
if (this.client.EXPIRES) {
|
|
167
|
-
yield [key, data];
|
|
168
|
-
} else if (this.#isFresh(data, key)) {
|
|
169
|
-
yield [key, data.value];
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
async entries() {
|
|
175
|
-
await this.promise;
|
|
176
|
-
|
|
177
|
-
// Cut the key to size
|
|
178
|
-
const trim = (key) => key.slice(this.PREFIX.length);
|
|
179
|
-
|
|
180
|
-
let list = [];
|
|
181
|
-
if (this.client.entries) {
|
|
182
|
-
const entries = await this.client.entries(this.PREFIX);
|
|
183
|
-
list = entries.map(([key, value]) => [trim(key), value]);
|
|
184
|
-
} else {
|
|
185
|
-
for await (const [key, value] of this.client.iterate(this.PREFIX)) {
|
|
186
|
-
list.push([trim(key), value]);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// The client already manages the expiration, so we can assume
|
|
191
|
-
// that at this point, all entries are not-expired
|
|
192
|
-
if (this.client.EXPIRES) return list;
|
|
193
|
-
|
|
194
|
-
// We need to do manual expiration checking
|
|
195
|
-
return list
|
|
196
|
-
.filter(([key, data]) => this.#isFresh(data, key))
|
|
197
|
-
.map(([key, data]) => [key, data.value]);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
async keys() {
|
|
201
|
-
await this.promise;
|
|
202
|
-
|
|
203
|
-
if (this.client.keys) {
|
|
204
|
-
const list = await this.client.keys(this.PREFIX);
|
|
205
|
-
if (!this.PREFIX) return list;
|
|
206
|
-
return list.map((k) => k.slice(this.PREFIX.length));
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const entries = await this.entries();
|
|
210
|
-
return entries.map((e) => e[0]);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
async values() {
|
|
214
|
-
await this.promise;
|
|
215
|
-
|
|
216
|
-
if (this.client.values) {
|
|
217
|
-
const list = this.client.values(this.PREFIX);
|
|
218
|
-
if (this.client.EXPIRES) return list;
|
|
219
|
-
return list
|
|
220
|
-
.filter((data) => this.#isFresh(data))
|
|
221
|
-
.map((data) => data.value);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
const entries = await this.entries();
|
|
225
|
-
return entries.map((e) => e[1]);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
async all() {
|
|
229
|
-
await this.promise;
|
|
230
|
-
|
|
231
|
-
if (this.client.all) {
|
|
232
|
-
const obj = await this.client.all(this.PREFIX);
|
|
233
|
-
if (!this.PREFIX) return obj;
|
|
234
|
-
const all = {};
|
|
235
|
-
for (let key in obj) {
|
|
236
|
-
all[key.slice(this.PREFIX.length)] = obj[key];
|
|
237
|
-
}
|
|
238
|
-
return all;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const entries = await this.entries();
|
|
242
|
-
return Object.fromEntries(entries);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
async clear() {
|
|
246
|
-
await this.promise;
|
|
247
|
-
|
|
248
|
-
if (!this.PREFIX && this.client.clearAll) {
|
|
249
|
-
return this.client.clearAll();
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (this.client.clear) {
|
|
253
|
-
return this.client.clear(this.PREFIX);
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
const keys = await this.keys();
|
|
257
|
-
// Note: this gives trouble of concurrent deletes in the FS
|
|
258
|
-
await Promise.all(keys.map((key) => this.del(key)));
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
prefix(prefix = "") {
|
|
262
|
-
const store = new Store(
|
|
263
|
-
Promise.resolve(this.promise).then(() => this.client),
|
|
264
|
-
);
|
|
265
|
-
store.PREFIX = this.PREFIX + prefix;
|
|
266
|
-
return store;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
async close() {
|
|
270
|
-
await this.promise;
|
|
271
|
-
|
|
272
|
-
if (this.client.close) {
|
|
273
|
-
return this.client.close();
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
export default (client) => new Store(client);
|
package/src/server.js
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
// This is an example server implementation of the HTTP library!
|
|
2
|
-
import http from "node:http";
|
|
3
|
-
import kv from "./index.js";
|
|
4
|
-
|
|
5
|
-
// Add/remove the key whether you want the API to be behind a key
|
|
6
|
-
const key = null;
|
|
7
|
-
// const key = 'MY-SECRET-KEY';
|
|
8
|
-
|
|
9
|
-
// Modify this to use any sub-store as desired. It's nice
|
|
10
|
-
// to use polystore itself for the polystore server library!'
|
|
11
|
-
const store = kv(new Map());
|
|
12
|
-
|
|
13
|
-
// Some reply helpers
|
|
14
|
-
const notFound = () => new Response(null, { status: 404 });
|
|
15
|
-
const sendJson = (data, status = 200) => {
|
|
16
|
-
const body = JSON.stringify(data);
|
|
17
|
-
const headers = { "content-type": "application/json" };
|
|
18
|
-
return new Response(body, { status, headers });
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
async function fetch({ method, url, body }) {
|
|
22
|
-
method = method.toLowerCase();
|
|
23
|
-
url = new URL(url);
|
|
24
|
-
let [, id] = url.pathname.split("/");
|
|
25
|
-
id = decodeURIComponent(id);
|
|
26
|
-
const expires = Number(url.searchParams.get("expires")) || null;
|
|
27
|
-
const prefix = url.searchParams.get("prefix") || null;
|
|
28
|
-
|
|
29
|
-
let local = store;
|
|
30
|
-
if (prefix) local = store.prefix(prefix);
|
|
31
|
-
|
|
32
|
-
if (method === "get") {
|
|
33
|
-
if (id === "ping") return new Response(null, { status: 200 });
|
|
34
|
-
if (!id) return sendJson(await local.all());
|
|
35
|
-
const data = await local.get(id);
|
|
36
|
-
if (data === null) return notFound();
|
|
37
|
-
return sendJson(data);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (method === "put") {
|
|
41
|
-
if (!id) return notFound();
|
|
42
|
-
const data = await new Response(body).json();
|
|
43
|
-
if (!data) return notFound();
|
|
44
|
-
await local.set(id, data, { expires });
|
|
45
|
-
return sendJson(id);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (method === "delete" && id) {
|
|
49
|
-
await local.del(id);
|
|
50
|
-
return sendJson(id);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return notFound();
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// http or express server-like handler:
|
|
57
|
-
async function server(req, res) {
|
|
58
|
-
// Secure it behind a key (optional)
|
|
59
|
-
if (key && req.headers.get("x-api-key") !== key) return res.send(401);
|
|
60
|
-
|
|
61
|
-
const url = new URL(req.url, "http://localhost:3000/").href;
|
|
62
|
-
const reply = await fetch({ ...req, url });
|
|
63
|
-
res.writeHead(reply.status, null, reply.headers || {});
|
|
64
|
-
if (reply.body) res.write(reply.body);
|
|
65
|
-
res.end();
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function start(port = 3000) {
|
|
69
|
-
return new Promise((resolve, reject) => {
|
|
70
|
-
const server = http.createServer(server);
|
|
71
|
-
server.on("clientError", (error, socket) => {
|
|
72
|
-
reject(error);
|
|
73
|
-
socket.end("HTTP/1.1 400 Bad Request\r\n\r\n");
|
|
74
|
-
});
|
|
75
|
-
server.listen(port, resolve);
|
|
76
|
-
return () => server.close();
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export { fetch, server, start };
|
|
81
|
-
export default { fetch, server, start };
|
package/src/utils.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
const times = /(-?(?:\d+\.?\d*|\d*\.?\d+)(?:e[-+]?\d+)?)\s*([\p{L}]*)/iu;
|
|
2
|
-
|
|
3
|
-
parse.millisecond = parse.ms = 0.001;
|
|
4
|
-
parse.second = parse.sec = parse.s = parse[""] = 1;
|
|
5
|
-
parse.minute = parse.min = parse.m = parse.s * 60;
|
|
6
|
-
parse.hour = parse.hr = parse.h = parse.m * 60;
|
|
7
|
-
parse.day = parse.d = parse.h * 24;
|
|
8
|
-
parse.week = parse.wk = parse.w = parse.d * 7;
|
|
9
|
-
parse.year = parse.yr = parse.y = parse.d * 365.25;
|
|
10
|
-
parse.month = parse.b = parse.y / 12;
|
|
11
|
-
|
|
12
|
-
// Returns the time in seconds
|
|
13
|
-
export function parse(str) {
|
|
14
|
-
if (str === null || str === undefined) return null;
|
|
15
|
-
if (typeof str === "number") return str;
|
|
16
|
-
// ignore commas/placeholders
|
|
17
|
-
str = str.toLowerCase().replace(/[,_]/g, "");
|
|
18
|
-
let [_, value, units] = times.exec(str) || [];
|
|
19
|
-
if (!units) return null;
|
|
20
|
-
const unitValue = parse[units] || parse[units.replace(/s$/, "")];
|
|
21
|
-
if (!unitValue) return null;
|
|
22
|
-
const result = unitValue * parseFloat(value, 10);
|
|
23
|
-
return Math.abs(Math.round(result * 1000) / 1000);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// "nanoid" imported manually
|
|
27
|
-
// Something about improved GZIP performance with this string
|
|
28
|
-
const urlAlphabet =
|
|
29
|
-
"useandom26T198340PX75pxJACKVERYMINDBUSHWOLFGQZbfghjklqvwyzrict";
|
|
30
|
-
|
|
31
|
-
export function createId() {
|
|
32
|
-
let size = 24;
|
|
33
|
-
let id = "";
|
|
34
|
-
let bytes = crypto.getRandomValues(new Uint8Array(size));
|
|
35
|
-
while (size--) {
|
|
36
|
-
// Using the bitwise AND operator to "cap" the value of
|
|
37
|
-
// the random byte from 255 to 63, in that way we can make sure
|
|
38
|
-
// that the value will be a valid index for the "chars" string.
|
|
39
|
-
id += urlAlphabet[bytes[size] & 61];
|
|
40
|
-
}
|
|
41
|
-
return id;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function unix(expires) {
|
|
45
|
-
const now = new Date().getTime();
|
|
46
|
-
return expires === null ? null : now + expires * 1000;
|
|
47
|
-
}
|