@_mustachio/openauth 0.6.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/dist/esm/client.js +186 -0
- package/dist/esm/css.d.js +0 -0
- package/dist/esm/error.js +73 -0
- package/dist/esm/index.js +14 -0
- package/dist/esm/issuer.js +558 -0
- package/dist/esm/jwt.js +16 -0
- package/dist/esm/keys.js +113 -0
- package/dist/esm/pkce.js +35 -0
- package/dist/esm/provider/apple.js +28 -0
- package/dist/esm/provider/arctic.js +43 -0
- package/dist/esm/provider/code.js +58 -0
- package/dist/esm/provider/cognito.js +16 -0
- package/dist/esm/provider/discord.js +15 -0
- package/dist/esm/provider/facebook.js +24 -0
- package/dist/esm/provider/github.js +15 -0
- package/dist/esm/provider/google.js +25 -0
- package/dist/esm/provider/index.js +3 -0
- package/dist/esm/provider/jumpcloud.js +15 -0
- package/dist/esm/provider/keycloak.js +15 -0
- package/dist/esm/provider/linkedin.js +15 -0
- package/dist/esm/provider/m2m.js +17 -0
- package/dist/esm/provider/microsoft.js +24 -0
- package/dist/esm/provider/oauth2.js +119 -0
- package/dist/esm/provider/oidc.js +69 -0
- package/dist/esm/provider/passkey.js +315 -0
- package/dist/esm/provider/password.js +306 -0
- package/dist/esm/provider/provider.js +10 -0
- package/dist/esm/provider/slack.js +15 -0
- package/dist/esm/provider/spotify.js +15 -0
- package/dist/esm/provider/twitch.js +15 -0
- package/dist/esm/provider/x.js +16 -0
- package/dist/esm/provider/yahoo.js +15 -0
- package/dist/esm/random.js +27 -0
- package/dist/esm/storage/aws.js +39 -0
- package/dist/esm/storage/cloudflare.js +42 -0
- package/dist/esm/storage/dynamo.js +116 -0
- package/dist/esm/storage/memory.js +88 -0
- package/dist/esm/storage/storage.js +36 -0
- package/dist/esm/subject.js +7 -0
- package/dist/esm/ui/base.js +407 -0
- package/dist/esm/ui/code.js +151 -0
- package/dist/esm/ui/form.js +43 -0
- package/dist/esm/ui/icon.js +92 -0
- package/dist/esm/ui/passkey.js +329 -0
- package/dist/esm/ui/password.js +338 -0
- package/dist/esm/ui/select.js +187 -0
- package/dist/esm/ui/theme.js +115 -0
- package/dist/esm/util.js +54 -0
- package/dist/types/client.d.ts +466 -0
- package/dist/types/client.d.ts.map +1 -0
- package/dist/types/error.d.ts +77 -0
- package/dist/types/error.d.ts.map +1 -0
- package/dist/types/index.d.ts +20 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/issuer.d.ts +465 -0
- package/dist/types/issuer.d.ts.map +1 -0
- package/dist/types/jwt.d.ts +6 -0
- package/dist/types/jwt.d.ts.map +1 -0
- package/dist/types/keys.d.ts +18 -0
- package/dist/types/keys.d.ts.map +1 -0
- package/dist/types/pkce.d.ts +7 -0
- package/dist/types/pkce.d.ts.map +1 -0
- package/dist/types/provider/apple.d.ts +108 -0
- package/dist/types/provider/apple.d.ts.map +1 -0
- package/dist/types/provider/arctic.d.ts +16 -0
- package/dist/types/provider/arctic.d.ts.map +1 -0
- package/dist/types/provider/code.d.ts +74 -0
- package/dist/types/provider/code.d.ts.map +1 -0
- package/dist/types/provider/cognito.d.ts +64 -0
- package/dist/types/provider/cognito.d.ts.map +1 -0
- package/dist/types/provider/discord.d.ts +38 -0
- package/dist/types/provider/discord.d.ts.map +1 -0
- package/dist/types/provider/facebook.d.ts +74 -0
- package/dist/types/provider/facebook.d.ts.map +1 -0
- package/dist/types/provider/github.d.ts +38 -0
- package/dist/types/provider/github.d.ts.map +1 -0
- package/dist/types/provider/google.d.ts +74 -0
- package/dist/types/provider/google.d.ts.map +1 -0
- package/dist/types/provider/index.d.ts +4 -0
- package/dist/types/provider/index.d.ts.map +1 -0
- package/dist/types/provider/jumpcloud.d.ts +38 -0
- package/dist/types/provider/jumpcloud.d.ts.map +1 -0
- package/dist/types/provider/keycloak.d.ts +67 -0
- package/dist/types/provider/keycloak.d.ts.map +1 -0
- package/dist/types/provider/linkedin.d.ts +6 -0
- package/dist/types/provider/linkedin.d.ts.map +1 -0
- package/dist/types/provider/m2m.d.ts +34 -0
- package/dist/types/provider/m2m.d.ts.map +1 -0
- package/dist/types/provider/microsoft.d.ts +89 -0
- package/dist/types/provider/microsoft.d.ts.map +1 -0
- package/dist/types/provider/oauth2.d.ts +133 -0
- package/dist/types/provider/oauth2.d.ts.map +1 -0
- package/dist/types/provider/oidc.d.ts +91 -0
- package/dist/types/provider/oidc.d.ts.map +1 -0
- package/dist/types/provider/passkey.d.ts +143 -0
- package/dist/types/provider/passkey.d.ts.map +1 -0
- package/dist/types/provider/password.d.ts +210 -0
- package/dist/types/provider/password.d.ts.map +1 -0
- package/dist/types/provider/provider.d.ts +29 -0
- package/dist/types/provider/provider.d.ts.map +1 -0
- package/dist/types/provider/slack.d.ts +59 -0
- package/dist/types/provider/slack.d.ts.map +1 -0
- package/dist/types/provider/spotify.d.ts +38 -0
- package/dist/types/provider/spotify.d.ts.map +1 -0
- package/dist/types/provider/twitch.d.ts +38 -0
- package/dist/types/provider/twitch.d.ts.map +1 -0
- package/dist/types/provider/x.d.ts +38 -0
- package/dist/types/provider/x.d.ts.map +1 -0
- package/dist/types/provider/yahoo.d.ts +38 -0
- package/dist/types/provider/yahoo.d.ts.map +1 -0
- package/dist/types/random.d.ts +3 -0
- package/dist/types/random.d.ts.map +1 -0
- package/dist/types/storage/aws.d.ts +4 -0
- package/dist/types/storage/aws.d.ts.map +1 -0
- package/dist/types/storage/cloudflare.d.ts +34 -0
- package/dist/types/storage/cloudflare.d.ts.map +1 -0
- package/dist/types/storage/dynamo.d.ts +65 -0
- package/dist/types/storage/dynamo.d.ts.map +1 -0
- package/dist/types/storage/memory.d.ts +49 -0
- package/dist/types/storage/memory.d.ts.map +1 -0
- package/dist/types/storage/storage.d.ts +15 -0
- package/dist/types/storage/storage.d.ts.map +1 -0
- package/dist/types/subject.d.ts +122 -0
- package/dist/types/subject.d.ts.map +1 -0
- package/dist/types/ui/base.d.ts +5 -0
- package/dist/types/ui/base.d.ts.map +1 -0
- package/dist/types/ui/code.d.ts +104 -0
- package/dist/types/ui/code.d.ts.map +1 -0
- package/dist/types/ui/form.d.ts +6 -0
- package/dist/types/ui/form.d.ts.map +1 -0
- package/dist/types/ui/icon.d.ts +6 -0
- package/dist/types/ui/icon.d.ts.map +1 -0
- package/dist/types/ui/passkey.d.ts +5 -0
- package/dist/types/ui/passkey.d.ts.map +1 -0
- package/dist/types/ui/password.d.ts +139 -0
- package/dist/types/ui/password.d.ts.map +1 -0
- package/dist/types/ui/select.d.ts +55 -0
- package/dist/types/ui/select.d.ts.map +1 -0
- package/dist/types/ui/theme.d.ts +207 -0
- package/dist/types/ui/theme.d.ts.map +1 -0
- package/dist/types/util.d.ts +8 -0
- package/dist/types/util.d.ts.map +1 -0
- package/package.json +51 -0
- package/src/client.ts +749 -0
- package/src/css.d.ts +4 -0
- package/src/error.ts +120 -0
- package/src/index.ts +26 -0
- package/src/issuer.ts +1302 -0
- package/src/jwt.ts +17 -0
- package/src/keys.ts +139 -0
- package/src/pkce.ts +40 -0
- package/src/provider/apple.ts +127 -0
- package/src/provider/arctic.ts +66 -0
- package/src/provider/code.ts +227 -0
- package/src/provider/cognito.ts +74 -0
- package/src/provider/discord.ts +45 -0
- package/src/provider/facebook.ts +84 -0
- package/src/provider/github.ts +45 -0
- package/src/provider/google.ts +85 -0
- package/src/provider/index.ts +3 -0
- package/src/provider/jumpcloud.ts +45 -0
- package/src/provider/keycloak.ts +75 -0
- package/src/provider/linkedin.ts +12 -0
- package/src/provider/m2m.ts +56 -0
- package/src/provider/microsoft.ts +100 -0
- package/src/provider/oauth2.ts +297 -0
- package/src/provider/oidc.ts +179 -0
- package/src/provider/passkey.ts +655 -0
- package/src/provider/password.ts +672 -0
- package/src/provider/provider.ts +33 -0
- package/src/provider/slack.ts +67 -0
- package/src/provider/spotify.ts +45 -0
- package/src/provider/twitch.ts +45 -0
- package/src/provider/x.ts +46 -0
- package/src/provider/yahoo.ts +45 -0
- package/src/random.ts +24 -0
- package/src/storage/aws.ts +59 -0
- package/src/storage/cloudflare.ts +77 -0
- package/src/storage/dynamo.ts +193 -0
- package/src/storage/memory.ts +135 -0
- package/src/storage/storage.ts +46 -0
- package/src/subject.ts +130 -0
- package/src/ui/base.tsx +118 -0
- package/src/ui/code.tsx +215 -0
- package/src/ui/form.tsx +40 -0
- package/src/ui/icon.tsx +95 -0
- package/src/ui/passkey.tsx +321 -0
- package/src/ui/password.tsx +405 -0
- package/src/ui/select.tsx +221 -0
- package/src/ui/theme.ts +319 -0
- package/src/ui/ui.css +252 -0
- package/src/util.ts +58 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// src/provider/yahoo.ts
|
|
2
|
+
import { Oauth2Provider } from "./oauth2.js";
|
|
3
|
+
function YahooProvider(config) {
|
|
4
|
+
return Oauth2Provider({
|
|
5
|
+
...config,
|
|
6
|
+
type: "yahoo",
|
|
7
|
+
endpoint: {
|
|
8
|
+
authorization: "https://api.login.yahoo.com/oauth2/request_auth",
|
|
9
|
+
token: "https://api.login.yahoo.com/oauth2/get_token"
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
export {
|
|
14
|
+
YahooProvider
|
|
15
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// src/random.ts
|
|
2
|
+
import { timingSafeEqual } from "node:crypto";
|
|
3
|
+
function generateUnbiasedDigits(length) {
|
|
4
|
+
const result = [];
|
|
5
|
+
while (result.length < length) {
|
|
6
|
+
const buffer = crypto.getRandomValues(new Uint8Array(length * 2));
|
|
7
|
+
for (const byte of buffer) {
|
|
8
|
+
if (byte < 250 && result.length < length) {
|
|
9
|
+
result.push(byte % 10);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return result.join("");
|
|
14
|
+
}
|
|
15
|
+
function timingSafeCompare(a, b) {
|
|
16
|
+
if (typeof a !== "string" || typeof b !== "string") {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
if (a.length !== b.length) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
return timingSafeEqual(Buffer.from(a), Buffer.from(b));
|
|
23
|
+
}
|
|
24
|
+
export {
|
|
25
|
+
timingSafeCompare,
|
|
26
|
+
generateUnbiasedDigits
|
|
27
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// src/storage/aws.ts
|
|
2
|
+
import { AwsClient } from "aws4fetch";
|
|
3
|
+
var cachedCredentials = null;
|
|
4
|
+
async function getCredentials(url) {
|
|
5
|
+
if (cachedCredentials) {
|
|
6
|
+
const currentTime = new Date;
|
|
7
|
+
const fiveMinutesFromNow = new Date(currentTime.getTime() + 5 * 60000);
|
|
8
|
+
const expirationTime = new Date(cachedCredentials.Expiration);
|
|
9
|
+
if (expirationTime > fiveMinutesFromNow) {
|
|
10
|
+
return cachedCredentials;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const credentials = await fetch(url).then((res) => res.json());
|
|
14
|
+
cachedCredentials = credentials;
|
|
15
|
+
return credentials;
|
|
16
|
+
}
|
|
17
|
+
async function client() {
|
|
18
|
+
if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) {
|
|
19
|
+
return new AwsClient({
|
|
20
|
+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
|
21
|
+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
|
22
|
+
sessionToken: process.env.AWS_SESSION_TOKEN,
|
|
23
|
+
region: process.env.AWS_REGION
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) {
|
|
27
|
+
const credentials = await getCredentials("http://169.254.170.2" + process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI);
|
|
28
|
+
return new AwsClient({
|
|
29
|
+
accessKeyId: credentials.AccessKeyId,
|
|
30
|
+
secretAccessKey: credentials.SecretAccessKey,
|
|
31
|
+
sessionToken: credentials.Token,
|
|
32
|
+
region: process.env.AWS_REGION
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
throw new Error("No AWS credentials found");
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
client
|
|
39
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// src/storage/cloudflare.ts
|
|
2
|
+
import { joinKey, splitKey } from "./storage.js";
|
|
3
|
+
function CloudflareStorage(options) {
|
|
4
|
+
return {
|
|
5
|
+
async get(key) {
|
|
6
|
+
const value = await options.namespace.get(joinKey(key), "json");
|
|
7
|
+
if (!value)
|
|
8
|
+
return;
|
|
9
|
+
return value;
|
|
10
|
+
},
|
|
11
|
+
async set(key, value, expiry) {
|
|
12
|
+
await options.namespace.put(joinKey(key), JSON.stringify(value), {
|
|
13
|
+
expirationTtl: expiry ? Math.max(Math.floor((expiry.getTime() - Date.now()) / 1000), 60) : undefined
|
|
14
|
+
});
|
|
15
|
+
},
|
|
16
|
+
async remove(key) {
|
|
17
|
+
await options.namespace.delete(joinKey(key));
|
|
18
|
+
},
|
|
19
|
+
async* scan(prefix) {
|
|
20
|
+
let cursor;
|
|
21
|
+
while (true) {
|
|
22
|
+
const result = await options.namespace.list({
|
|
23
|
+
prefix: joinKey([...prefix, ""]),
|
|
24
|
+
cursor
|
|
25
|
+
});
|
|
26
|
+
for (const key of result.keys) {
|
|
27
|
+
const value = await options.namespace.get(key.name, "json");
|
|
28
|
+
if (value !== null) {
|
|
29
|
+
yield [splitKey(key.name), value];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (result.list_complete) {
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
cursor = result.cursor;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export {
|
|
41
|
+
CloudflareStorage
|
|
42
|
+
};
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// src/storage/dynamo.ts
|
|
2
|
+
import { client } from "./aws.js";
|
|
3
|
+
import { joinKey } from "./storage.js";
|
|
4
|
+
function DynamoStorage(options) {
|
|
5
|
+
const pk = options.pk || "pk";
|
|
6
|
+
const sk = options.sk || "sk";
|
|
7
|
+
const ttl = options.ttl || "expiry";
|
|
8
|
+
const tableName = options.table;
|
|
9
|
+
function parseKey(key) {
|
|
10
|
+
if (key.length === 2) {
|
|
11
|
+
return {
|
|
12
|
+
pk: key[0],
|
|
13
|
+
sk: key[1]
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
pk: joinKey(key.slice(0, 2)),
|
|
18
|
+
sk: joinKey(key.slice(2))
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
async function dynamo(action, payload) {
|
|
22
|
+
const c = await client();
|
|
23
|
+
const endpoint = options.endpoint || `https://dynamodb.${c.region}.amazonaws.com`;
|
|
24
|
+
const response = await c.fetch(endpoint, {
|
|
25
|
+
method: "POST",
|
|
26
|
+
headers: {
|
|
27
|
+
"Content-Type": "application/x-amz-json-1.0",
|
|
28
|
+
"X-Amz-Target": `DynamoDB_20120810.${action}`
|
|
29
|
+
},
|
|
30
|
+
body: JSON.stringify(payload)
|
|
31
|
+
});
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
throw new Error(`DynamoDB request failed: ${response.statusText}`);
|
|
34
|
+
}
|
|
35
|
+
return response.json();
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
async get(key) {
|
|
39
|
+
const { pk: keyPk, sk: keySk } = parseKey(key);
|
|
40
|
+
const params = {
|
|
41
|
+
TableName: tableName,
|
|
42
|
+
Key: {
|
|
43
|
+
[pk]: { S: keyPk },
|
|
44
|
+
[sk]: { S: keySk }
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const result = await dynamo("GetItem", params);
|
|
48
|
+
if (!result.Item)
|
|
49
|
+
return;
|
|
50
|
+
if (result.Item[ttl] && result.Item[ttl].N < Date.now() / 1000) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
return JSON.parse(result.Item.value.S);
|
|
54
|
+
},
|
|
55
|
+
async set(key, value, expiry) {
|
|
56
|
+
const parsed = parseKey(key);
|
|
57
|
+
const params = {
|
|
58
|
+
TableName: tableName,
|
|
59
|
+
Item: {
|
|
60
|
+
[pk]: { S: parsed.pk },
|
|
61
|
+
[sk]: { S: parsed.sk },
|
|
62
|
+
...expiry ? {
|
|
63
|
+
[ttl]: { N: Math.floor(expiry.getTime() / 1000).toString() }
|
|
64
|
+
} : {},
|
|
65
|
+
value: { S: JSON.stringify(value) }
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
await dynamo("PutItem", params);
|
|
69
|
+
},
|
|
70
|
+
async remove(key) {
|
|
71
|
+
const { pk: keyPk, sk: keySk } = parseKey(key);
|
|
72
|
+
const params = {
|
|
73
|
+
TableName: tableName,
|
|
74
|
+
Key: {
|
|
75
|
+
[pk]: { S: keyPk },
|
|
76
|
+
[sk]: { S: keySk }
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
await dynamo("DeleteItem", params);
|
|
80
|
+
},
|
|
81
|
+
async* scan(prefix) {
|
|
82
|
+
const prefixPk = prefix.length >= 2 ? joinKey(prefix.slice(0, 2)) : prefix[0];
|
|
83
|
+
const prefixSk = prefix.length > 2 ? joinKey(prefix.slice(2)) : "";
|
|
84
|
+
let lastEvaluatedKey = undefined;
|
|
85
|
+
const now = Date.now() / 1000;
|
|
86
|
+
while (true) {
|
|
87
|
+
const params = {
|
|
88
|
+
TableName: tableName,
|
|
89
|
+
ExclusiveStartKey: lastEvaluatedKey,
|
|
90
|
+
KeyConditionExpression: prefixSk ? `#pk = :pk AND begins_with(#sk, :sk)` : `#pk = :pk`,
|
|
91
|
+
ExpressionAttributeNames: {
|
|
92
|
+
"#pk": pk,
|
|
93
|
+
...prefixSk && { "#sk": sk }
|
|
94
|
+
},
|
|
95
|
+
ExpressionAttributeValues: {
|
|
96
|
+
":pk": { S: prefixPk },
|
|
97
|
+
...prefixSk && { ":sk": { S: prefixSk } }
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const result = await dynamo("Query", params);
|
|
101
|
+
for (const item of result.Items || []) {
|
|
102
|
+
if (item[ttl] && item[ttl].N < now) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
yield [[item[pk].S, item[sk].S], JSON.parse(item.value.S)];
|
|
106
|
+
}
|
|
107
|
+
if (!result.LastEvaluatedKey)
|
|
108
|
+
break;
|
|
109
|
+
lastEvaluatedKey = result.LastEvaluatedKey;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export {
|
|
115
|
+
DynamoStorage
|
|
116
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// src/storage/memory.ts
|
|
2
|
+
import { joinKey, splitKey } from "./storage.js";
|
|
3
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
+
import { writeFile } from "node:fs/promises";
|
|
5
|
+
function MemoryStorage(input) {
|
|
6
|
+
const store = [];
|
|
7
|
+
if (input?.persist) {
|
|
8
|
+
if (existsSync(input.persist)) {
|
|
9
|
+
const file = readFileSync(input?.persist);
|
|
10
|
+
store.push(...JSON.parse(file.toString()));
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
async function save() {
|
|
14
|
+
if (!input?.persist)
|
|
15
|
+
return;
|
|
16
|
+
const file = JSON.stringify(store);
|
|
17
|
+
await writeFile(input.persist, file);
|
|
18
|
+
}
|
|
19
|
+
function search(key) {
|
|
20
|
+
let left = 0;
|
|
21
|
+
let right = store.length - 1;
|
|
22
|
+
while (left <= right) {
|
|
23
|
+
const mid = Math.floor((left + right) / 2);
|
|
24
|
+
const comparison = key.localeCompare(store[mid][0]);
|
|
25
|
+
if (comparison === 0) {
|
|
26
|
+
return { found: true, index: mid };
|
|
27
|
+
} else if (comparison < 0) {
|
|
28
|
+
right = mid - 1;
|
|
29
|
+
} else {
|
|
30
|
+
left = mid + 1;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { found: false, index: left };
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
async get(key) {
|
|
37
|
+
const match = search(joinKey(key));
|
|
38
|
+
if (!match.found)
|
|
39
|
+
return;
|
|
40
|
+
const entry = store[match.index][1];
|
|
41
|
+
if (entry.expiry && Date.now() >= entry.expiry) {
|
|
42
|
+
store.splice(match.index, 1);
|
|
43
|
+
await save();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
return entry.value;
|
|
47
|
+
},
|
|
48
|
+
async set(key, value, expiry) {
|
|
49
|
+
const joined = joinKey(key);
|
|
50
|
+
const match = search(joined);
|
|
51
|
+
const entry = [
|
|
52
|
+
joined,
|
|
53
|
+
{
|
|
54
|
+
value,
|
|
55
|
+
expiry: expiry ? expiry.getTime() : expiry
|
|
56
|
+
}
|
|
57
|
+
];
|
|
58
|
+
if (!match.found) {
|
|
59
|
+
store.splice(match.index, 0, entry);
|
|
60
|
+
} else {
|
|
61
|
+
store[match.index] = entry;
|
|
62
|
+
}
|
|
63
|
+
await save();
|
|
64
|
+
},
|
|
65
|
+
async remove(key) {
|
|
66
|
+
const joined = joinKey(key);
|
|
67
|
+
const match = search(joined);
|
|
68
|
+
if (match.found) {
|
|
69
|
+
store.splice(match.index, 1);
|
|
70
|
+
await save();
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
async* scan(prefix) {
|
|
74
|
+
const now = Date.now();
|
|
75
|
+
const prefixStr = joinKey(prefix);
|
|
76
|
+
for (const [key, entry] of store) {
|
|
77
|
+
if (!key.startsWith(prefixStr))
|
|
78
|
+
continue;
|
|
79
|
+
if (entry.expiry && now >= entry.expiry)
|
|
80
|
+
continue;
|
|
81
|
+
yield [splitKey(key), entry.value];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
export {
|
|
87
|
+
MemoryStorage
|
|
88
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// src/storage/storage.ts
|
|
2
|
+
var SEPERATOR = String.fromCharCode(31);
|
|
3
|
+
function joinKey(key) {
|
|
4
|
+
return key.join(SEPERATOR);
|
|
5
|
+
}
|
|
6
|
+
function splitKey(key) {
|
|
7
|
+
return key.split(SEPERATOR);
|
|
8
|
+
}
|
|
9
|
+
var Storage;
|
|
10
|
+
((Storage) => {
|
|
11
|
+
function encode(key) {
|
|
12
|
+
return key.map((k) => k.replaceAll(SEPERATOR, ""));
|
|
13
|
+
}
|
|
14
|
+
function get(adapter, key) {
|
|
15
|
+
return adapter.get(encode(key));
|
|
16
|
+
}
|
|
17
|
+
Storage.get = get;
|
|
18
|
+
function set(adapter, key, value, ttl) {
|
|
19
|
+
const expiry = ttl ? new Date(Date.now() + ttl * 1000) : undefined;
|
|
20
|
+
return adapter.set(encode(key), value, expiry);
|
|
21
|
+
}
|
|
22
|
+
Storage.set = set;
|
|
23
|
+
function remove(adapter, key) {
|
|
24
|
+
return adapter.remove(encode(key));
|
|
25
|
+
}
|
|
26
|
+
Storage.remove = remove;
|
|
27
|
+
function scan(adapter, key) {
|
|
28
|
+
return adapter.scan(encode(key));
|
|
29
|
+
}
|
|
30
|
+
Storage.scan = scan;
|
|
31
|
+
})(Storage ||= {});
|
|
32
|
+
export {
|
|
33
|
+
splitKey,
|
|
34
|
+
joinKey,
|
|
35
|
+
Storage
|
|
36
|
+
};
|