@stackframe/stack-shared 1.0.0 → 1.2.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/helpers/fetch-token.d.ts +1 -0
- package/dist/helpers/password.d.ts +1 -0
- package/dist/hooks/use-async-external-store.d.ts +1 -0
- package/dist/hooks/use-strict-memo.d.ts +1 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/interface/adminInterface.d.ts +44 -53
- package/dist/interface/adminInterface.d.ts.map +1 -1
- package/dist/interface/adminInterface.js +10 -52
- package/dist/interface/clientInterface.d.ts +30 -21
- package/dist/interface/clientInterface.d.ts.map +1 -1
- package/dist/interface/clientInterface.js +37 -44
- package/dist/interface/serverInterface.d.ts +4 -6
- package/dist/interface/serverInterface.d.ts.map +1 -1
- package/dist/interface/serverInterface.js +0 -23
- package/dist/utils/arrays.d.ts +1 -0
- package/dist/utils/bytes.d.ts +3 -0
- package/dist/utils/bytes.d.ts.map +1 -0
- package/dist/utils/bytes.js +48 -0
- package/dist/utils/caches.d.ts +34 -28
- package/dist/utils/caches.d.ts.map +1 -1
- package/dist/utils/caches.js +81 -46
- package/dist/utils/crypto.d.ts +1 -0
- package/dist/utils/crypto.d.ts.map +1 -1
- package/dist/utils/crypto.js +6 -2
- package/dist/utils/dates.d.ts +1 -0
- package/dist/utils/dom.d.ts +1 -0
- package/dist/utils/env.d.ts +1 -0
- package/dist/utils/errors.d.ts +1 -0
- package/dist/utils/html.d.ts +1 -0
- package/dist/utils/json.d.ts +1 -0
- package/dist/utils/jwt.d.ts +1 -0
- package/dist/utils/maps.d.ts +24 -0
- package/dist/utils/maps.d.ts.map +1 -0
- package/dist/utils/maps.js +110 -0
- package/dist/utils/math.d.ts +1 -0
- package/dist/utils/numbers.d.ts +1 -0
- package/dist/utils/objects.d.ts +1 -0
- package/dist/utils/password.d.ts +1 -0
- package/dist/utils/promises.d.ts +2 -0
- package/dist/utils/promises.d.ts.map +1 -1
- package/dist/utils/promises.js +4 -1
- package/dist/utils/react.d.ts +1 -0
- package/dist/utils/results.d.ts +1 -0
- package/dist/utils/stores.d.ts +1 -0
- package/dist/utils/stores.d.ts.map +1 -1
- package/dist/utils/stores.js +2 -4
- package/dist/utils/strings.d.ts +1 -0
- package/dist/utils/types.d.ts +11 -13
- package/dist/utils/types.d.ts.map +1 -1
- package/dist/utils/uuids.d.ts +1 -0
- package/package.json +5 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serverInterface.d.ts","sourceRoot":"","sources":["../../src/interface/serverInterface.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,QAAQ,EACR,UAAU,EACV,oBAAoB,
|
|
1
|
+
{"version":3,"file":"serverInterface.d.ts","sourceRoot":"","sources":["../../src/interface/serverInterface.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,oBAAoB,EACpB,QAAQ,EACR,UAAU,EACV,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAG1C,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7C,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG;IACtC,QAAQ,CAAC,cAAc,EAAE,YAAY,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG,oBAAoB,GAAG;IAC9D,QAAQ,CAAC,cAAc,EAAE,YAAY,CAAC;IACtC,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,oBAAoB,EAAE,OAAO,CAAC;CACxC,CAAA;AAGD,MAAM,MAAM,4BAA4B,GAAG,CACvC,sBAAsB,GACtB,CACE;IACA,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC,GACC;IACA,QAAQ,CAAC,kBAAkB,EAAE,kBAAkB,CAAC;CACjD,CACF,CACF,CAAC;AAGF,qBAAa,oBAAqB,SAAQ,oBAAoB;IAChC,OAAO,EAAE,4BAA4B;gBAArC,OAAO,EAAE,4BAA4B;cAIjD,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,GAAG,IAAI;;;;;;IAc7F,oBAAoB,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;IAW7E,SAAS,IAAI,OAAO,CAAC,cAAc,EAAE,CAAC;IAKtC,6BAA6B,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,0BAA0B,CAAC;IAczF,gBAAgB,CAAC,MAAM,EAAE,MAAM;CAatC"}
|
|
@@ -1,33 +1,10 @@
|
|
|
1
1
|
import { StackClientInterface, } from "./clientInterface";
|
|
2
2
|
import { Result } from "../utils/results";
|
|
3
|
-
import { AsyncCache } from "../utils/caches";
|
|
4
|
-
import { runAsynchronously } from "../utils/promises";
|
|
5
3
|
export class StackServerInterface extends StackClientInterface {
|
|
6
4
|
options;
|
|
7
|
-
// note that we intentionally use TokenStore (a reference type) as a key, as different token stores with the same tokens should be treated differently
|
|
8
|
-
// (if we wouldn't do that, we would cache users across requests, which may cause caching issues)
|
|
9
|
-
currentServerUserCache;
|
|
10
5
|
constructor(options) {
|
|
11
6
|
super(options);
|
|
12
7
|
this.options = options;
|
|
13
|
-
this.currentServerUserCache = new AsyncCache(async (key, isFirst) => {
|
|
14
|
-
if (isFirst) {
|
|
15
|
-
key.onChange((newValue, oldValue) => {
|
|
16
|
-
if (JSON.stringify(newValue) === JSON.stringify(oldValue))
|
|
17
|
-
return;
|
|
18
|
-
runAsynchronously(this.currentServerUserCache.refresh(key));
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
const user = await this.getServerUserByToken(key);
|
|
22
|
-
return Result.or(user, null);
|
|
23
|
-
});
|
|
24
|
-
// TODO override the client user cache to use the server user cache, so we save some requests
|
|
25
|
-
}
|
|
26
|
-
async refreshUser(tokenStore) {
|
|
27
|
-
await Promise.all([
|
|
28
|
-
super.refreshUser(tokenStore),
|
|
29
|
-
this.currentServerUserCache.refresh(tokenStore),
|
|
30
|
-
]);
|
|
31
8
|
}
|
|
32
9
|
async sendServerRequest(path, options, tokenStore) {
|
|
33
10
|
return await this.sendClientRequest(path, {
|
package/dist/utils/arrays.d.ts
CHANGED
|
@@ -8,3 +8,4 @@ export declare function range(startInclusive: number, endExclusive: number, step
|
|
|
8
8
|
export declare function rotateLeft(arr: readonly any[], n: number): any[];
|
|
9
9
|
export declare function rotateRight(arr: readonly any[], n: number): any[];
|
|
10
10
|
export declare function shuffle<T>(arr: readonly T[]): T[];
|
|
11
|
+
//# sourceMappingURL=arrays.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../../src/utils/bytes.tsx"],"names":[],"mappings":"AAOA,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAgBtD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CAuBtD"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
const crockfordAlphabet = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
2
|
+
const crockfordReplacements = new Map([
|
|
3
|
+
["o", "0"],
|
|
4
|
+
["i", "1"],
|
|
5
|
+
["l", "1"],
|
|
6
|
+
]);
|
|
7
|
+
export function encodeBase32(input) {
|
|
8
|
+
let bits = 0;
|
|
9
|
+
let value = 0;
|
|
10
|
+
let output = "";
|
|
11
|
+
for (let i = 0; i < input.length; i++) {
|
|
12
|
+
value = (value << 8) | input[i];
|
|
13
|
+
bits += 8;
|
|
14
|
+
while (bits >= 5) {
|
|
15
|
+
output += crockfordAlphabet[(value >>> (bits - 5)) & 31];
|
|
16
|
+
bits -= 5;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
if (bits > 0) {
|
|
20
|
+
output += crockfordAlphabet[(value << (5 - bits)) & 31];
|
|
21
|
+
}
|
|
22
|
+
return output;
|
|
23
|
+
}
|
|
24
|
+
export function decodeBase32(input) {
|
|
25
|
+
const output = new Uint8Array((input.length * 5 / 8) | 0);
|
|
26
|
+
let bits = 0;
|
|
27
|
+
let value = 0;
|
|
28
|
+
let outputIndex = 0;
|
|
29
|
+
for (let i = 0; i < input.length; i++) {
|
|
30
|
+
let char = input[i].toLowerCase();
|
|
31
|
+
if (char === " ")
|
|
32
|
+
continue;
|
|
33
|
+
if (crockfordReplacements.has(char)) {
|
|
34
|
+
char = crockfordReplacements.get(char);
|
|
35
|
+
}
|
|
36
|
+
const index = crockfordAlphabet.indexOf(char);
|
|
37
|
+
if (index === -1) {
|
|
38
|
+
throw new Error(`Invalid character: ${char}`);
|
|
39
|
+
}
|
|
40
|
+
value = (value << 5) | index;
|
|
41
|
+
bits += 5;
|
|
42
|
+
if (bits >= 8) {
|
|
43
|
+
output[outputIndex++] = (value >>> (bits - 8)) & 255;
|
|
44
|
+
bits -= 8;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return output;
|
|
48
|
+
}
|
package/dist/utils/caches.d.ts
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { RateLimitOptions, ReactPromise } from "./promises";
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Can be used to cache the result of a function call, for example for the `use` hook in React.
|
|
4
|
+
*/
|
|
5
|
+
export declare function cacheFunction<F extends Function>(f: F): F;
|
|
6
|
+
type CacheStrategy = "write-only" | "read-write" | "never";
|
|
7
|
+
export declare class AsyncCache<D extends any[], T> {
|
|
4
8
|
private readonly _fetcher;
|
|
5
|
-
private readonly
|
|
6
|
-
private _map;
|
|
7
|
-
constructor(_fetcher: (
|
|
9
|
+
private readonly _options;
|
|
10
|
+
private readonly _map;
|
|
11
|
+
constructor(_fetcher: (dependencies: D) => Promise<T>, _options?: {
|
|
12
|
+
onSubscribe?: (key: D, refresh: () => void) => (() => void);
|
|
13
|
+
rateLimiter?: Omit<RateLimitOptions, "batchCalls">;
|
|
14
|
+
});
|
|
8
15
|
private _createKeyed;
|
|
9
|
-
getValueCache(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
get: (key: K) => ({
|
|
16
|
+
getValueCache(dependencies: D): AsyncValueCache<T>;
|
|
17
|
+
readonly isCacheAvailable: (key: D) => boolean;
|
|
18
|
+
readonly getIfCached: (key: D) => ({
|
|
13
19
|
status: "pending";
|
|
14
20
|
} & {
|
|
15
21
|
progress: void;
|
|
@@ -26,25 +32,26 @@ export declare class AsyncCache<K extends object, T> {
|
|
|
26
32
|
} & {
|
|
27
33
|
status: "ok";
|
|
28
34
|
});
|
|
29
|
-
getOrWait: (key:
|
|
30
|
-
refresh: (key:
|
|
31
|
-
invalidate: (key:
|
|
32
|
-
onChange: (key:
|
|
33
|
-
unsubscribe: () => void;
|
|
34
|
-
};
|
|
35
|
-
onceChange: (key: K, callback: (value: T, oldValue: T | undefined) => void) => {
|
|
35
|
+
readonly getOrWait: (key: D, cacheStrategy: CacheStrategy) => ReactPromise<T>;
|
|
36
|
+
readonly refresh: (key: D) => Promise<T>;
|
|
37
|
+
readonly invalidate: (key: D) => Promise<T>;
|
|
38
|
+
readonly onChange: (key: D, callback: (value: T, oldValue: T | undefined) => void) => {
|
|
36
39
|
unsubscribe: () => void;
|
|
37
40
|
};
|
|
38
41
|
}
|
|
39
|
-
|
|
40
|
-
private readonly
|
|
42
|
+
declare class AsyncValueCache<T> {
|
|
43
|
+
private readonly _options;
|
|
41
44
|
private _store;
|
|
42
|
-
private
|
|
43
|
-
private
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
private _fetcher;
|
|
46
|
+
private readonly _rateLimitOptions;
|
|
47
|
+
private _subscriptionsCount;
|
|
48
|
+
private _unsubscribers;
|
|
49
|
+
constructor(fetcher: () => Promise<T>, _options?: {
|
|
50
|
+
onSubscribe?: (refresh: () => void) => (() => void);
|
|
51
|
+
rateLimiter?: Omit<RateLimitOptions, "batchCalls">;
|
|
52
|
+
});
|
|
53
|
+
isCacheAvailable(): boolean;
|
|
54
|
+
getIfCached(): ({
|
|
48
55
|
status: "pending";
|
|
49
56
|
} & {
|
|
50
57
|
progress: void;
|
|
@@ -61,7 +68,7 @@ export declare class AsyncValueCache<T> implements ReadonlyAsyncStore<T> {
|
|
|
61
68
|
} & {
|
|
62
69
|
status: "ok";
|
|
63
70
|
});
|
|
64
|
-
getOrWait(): ReactPromise<T>;
|
|
71
|
+
getOrWait(cacheStrategy: CacheStrategy): ReactPromise<T>;
|
|
65
72
|
private _set;
|
|
66
73
|
private _setAsync;
|
|
67
74
|
private _refetch;
|
|
@@ -70,7 +77,6 @@ export declare class AsyncValueCache<T> implements ReadonlyAsyncStore<T> {
|
|
|
70
77
|
onChange(callback: (value: T, oldValue: T | undefined) => void): {
|
|
71
78
|
unsubscribe: () => void;
|
|
72
79
|
};
|
|
73
|
-
onceChange(callback: (value: T, oldValue: T | undefined) => void): {
|
|
74
|
-
unsubscribe: () => void;
|
|
75
|
-
};
|
|
76
80
|
}
|
|
81
|
+
export {};
|
|
82
|
+
//# sourceMappingURL=caches.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"caches.d.ts","sourceRoot":"","sources":["../../src/utils/caches.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"caches.d.ts","sourceRoot":"","sources":["../../src/utils/caches.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAqD,MAAM,YAAY,CAAC;AAG/G;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,QAAQ,EAAE,CAAC,EAAE,CAAC,GAAG,CAAC,CAYzD;AAGD,KAAK,aAAa,GAAG,YAAY,GAAG,YAAY,GAAG,OAAO,CAAC;AAE3D,qBAAa,UAAU,CAAC,CAAC,SAAS,GAAG,EAAE,EAAE,CAAC;IAItC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ3B,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAgD;gBAGlD,QAAQ,EAAE,CAAC,YAAY,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EACzC,QAAQ,GAAE;QACzB,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;QAC5D,WAAW,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC;KAC/C;IAKR,OAAO,CAAC,YAAY;IASpB,aAAa,CAAC,YAAY,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC;IAelD,QAAQ,CAAC,gBAAgB,QAtBhB,CAAC,aAsBwD;IAClE,QAAQ,CAAC,WAAW,QAvBX,CAAC;;;;;;;;;;;;;;;;OAuB8C;IACxD,QAAQ,CAAC,SAAS,QAxBT,CAAC,mDAwB0C;IACpD,QAAQ,CAAC,OAAO,QAzBP,CAAC,gBAyBsC;IAChD,QAAQ,CAAC,UAAU,QA1BV,CAAC,gBA0B4C;IACtD,QAAQ,CAAC,QAAQ,QA3BR,CAAC;;MA2BwC;CACnD;AAED,cAAM,eAAe,CAAC,CAAC;IASnB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAR3B,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAuC;IACzE,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,cAAc,CAAsB;gBAG1C,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACR,QAAQ,GAAE;QACzB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;QACpD,WAAW,CAAC,EAAE,IAAI,CAAC,gBAAgB,EAAE,YAAY,CAAC,CAAC;KAC/C;IAgBR,gBAAgB,IAAI,OAAO;IAI3B,WAAW;;;;;;;;;;;;;;;;;IAIX,SAAS,CAAC,aAAa,EAAE,aAAa,GAAG,YAAY,CAAC,CAAC,CAAC;IASxD,OAAO,CAAC,IAAI;YAIE,SAAS;YAIT,QAAQ;IAahB,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC;IAIrB,UAAU,IAAI,OAAO,CAAC,CAAC,CAAC;IAK9B,QAAQ,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,SAAS,KAAK,IAAI,GAAG;QAAE,WAAW,EAAE,MAAM,IAAI,CAAA;KAAE;CA0B7F"}
|
package/dist/utils/caches.js
CHANGED
|
@@ -1,12 +1,28 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { DependenciesMap } from "./maps";
|
|
2
|
+
import { filterUndefined } from "./objects";
|
|
3
|
+
import { pending, rateLimited, resolved, runAsynchronously } from "./promises";
|
|
2
4
|
import { AsyncStore } from "./stores";
|
|
5
|
+
/**
|
|
6
|
+
* Can be used to cache the result of a function call, for example for the `use` hook in React.
|
|
7
|
+
*/
|
|
8
|
+
export function cacheFunction(f) {
|
|
9
|
+
const dependenciesMap = new DependenciesMap();
|
|
10
|
+
return ((...args) => {
|
|
11
|
+
if (dependenciesMap.has(args)) {
|
|
12
|
+
return dependenciesMap.get(args);
|
|
13
|
+
}
|
|
14
|
+
const value = f(...args);
|
|
15
|
+
dependenciesMap.set(args, value);
|
|
16
|
+
return value;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
3
19
|
export class AsyncCache {
|
|
4
20
|
_fetcher;
|
|
5
|
-
|
|
6
|
-
_map = new
|
|
7
|
-
constructor(_fetcher,
|
|
21
|
+
_options;
|
|
22
|
+
_map = new DependenciesMap();
|
|
23
|
+
constructor(_fetcher, _options = {}) {
|
|
8
24
|
this._fetcher = _fetcher;
|
|
9
|
-
this.
|
|
25
|
+
this._options = _options;
|
|
10
26
|
// nothing here yet
|
|
11
27
|
}
|
|
12
28
|
_createKeyed(functionName) {
|
|
@@ -15,57 +31,56 @@ export class AsyncCache {
|
|
|
15
31
|
return valueCache[functionName].apply(valueCache, args);
|
|
16
32
|
};
|
|
17
33
|
}
|
|
18
|
-
getValueCache(
|
|
19
|
-
let cache = this._map.get(
|
|
34
|
+
getValueCache(dependencies) {
|
|
35
|
+
let cache = this._map.get(dependencies);
|
|
20
36
|
if (!cache) {
|
|
21
|
-
cache = new AsyncValueCache(async (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
37
|
+
cache = new AsyncValueCache(async () => await this._fetcher(dependencies), {
|
|
38
|
+
...this._options,
|
|
39
|
+
onSubscribe: this._options.onSubscribe ? (cb) => this._options.onSubscribe(dependencies, cb) : undefined,
|
|
40
|
+
});
|
|
41
|
+
this._map.set(dependencies, cache);
|
|
25
42
|
}
|
|
26
43
|
return cache;
|
|
27
44
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
get = this._createKeyed("get");
|
|
45
|
+
isCacheAvailable = this._createKeyed("isCacheAvailable");
|
|
46
|
+
getIfCached = this._createKeyed("getIfCached");
|
|
31
47
|
getOrWait = this._createKeyed("getOrWait");
|
|
32
48
|
refresh = this._createKeyed("refresh");
|
|
33
49
|
invalidate = this._createKeyed("invalidate");
|
|
34
50
|
onChange = this._createKeyed("onChange");
|
|
35
|
-
onceChange = this._createKeyed("onceChange");
|
|
36
51
|
}
|
|
37
|
-
|
|
38
|
-
|
|
52
|
+
class AsyncValueCache {
|
|
53
|
+
_options;
|
|
39
54
|
_store;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
55
|
+
_fetcher;
|
|
56
|
+
_rateLimitOptions;
|
|
57
|
+
_subscriptionsCount = 0;
|
|
58
|
+
_unsubscribers = [];
|
|
59
|
+
constructor(fetcher, _options = {}) {
|
|
60
|
+
this._options = _options;
|
|
44
61
|
this._store = new AsyncStore();
|
|
45
|
-
this.
|
|
46
|
-
|
|
62
|
+
this._rateLimitOptions = {
|
|
63
|
+
concurrency: 1,
|
|
64
|
+
debounceMs: 300,
|
|
65
|
+
...filterUndefined(_options.rateLimiter ?? {}),
|
|
47
66
|
};
|
|
48
|
-
this.
|
|
49
|
-
return await fetcher(false);
|
|
50
|
-
}, {
|
|
67
|
+
this._fetcher = rateLimited(fetcher, {
|
|
51
68
|
...this._rateLimitOptions,
|
|
52
69
|
batchCalls: true,
|
|
53
70
|
});
|
|
54
|
-
this._refetch(true).catch(() => {
|
|
55
|
-
this._store.setRejected(new Error("unavailable"));
|
|
56
|
-
});
|
|
57
71
|
}
|
|
58
|
-
|
|
72
|
+
isCacheAvailable() {
|
|
59
73
|
return this._store.isAvailable();
|
|
60
74
|
}
|
|
61
|
-
|
|
62
|
-
this._store.setUnavailable();
|
|
63
|
-
}
|
|
64
|
-
get() {
|
|
75
|
+
getIfCached() {
|
|
65
76
|
return this._store.get();
|
|
66
77
|
}
|
|
67
|
-
getOrWait() {
|
|
68
|
-
|
|
78
|
+
getOrWait(cacheStrategy) {
|
|
79
|
+
const cached = this.getIfCached();
|
|
80
|
+
if (cacheStrategy === "read-write" && cached.status === "ok") {
|
|
81
|
+
return resolved(cached.data);
|
|
82
|
+
}
|
|
83
|
+
return pending(this._refetch(cacheStrategy === "read-write" ? "write-only" : cacheStrategy));
|
|
69
84
|
}
|
|
70
85
|
_set(value) {
|
|
71
86
|
this._store.set(value);
|
|
@@ -73,10 +88,12 @@ export class AsyncValueCache {
|
|
|
73
88
|
async _setAsync(value) {
|
|
74
89
|
return await this._store.setAsync(value);
|
|
75
90
|
}
|
|
76
|
-
async _refetch(
|
|
91
|
+
async _refetch(cacheStrategy) {
|
|
77
92
|
try {
|
|
78
|
-
const res =
|
|
79
|
-
|
|
93
|
+
const res = this._fetcher();
|
|
94
|
+
if (cacheStrategy === "write-only") {
|
|
95
|
+
await this._setAsync(res);
|
|
96
|
+
}
|
|
80
97
|
return await res;
|
|
81
98
|
}
|
|
82
99
|
catch (e) {
|
|
@@ -85,16 +102,34 @@ export class AsyncValueCache {
|
|
|
85
102
|
}
|
|
86
103
|
}
|
|
87
104
|
async refresh() {
|
|
88
|
-
return await this.
|
|
105
|
+
return await this.getOrWait("write-only");
|
|
89
106
|
}
|
|
90
107
|
async invalidate() {
|
|
91
|
-
this.setUnavailable();
|
|
92
|
-
return await this.
|
|
108
|
+
this._store.setUnavailable();
|
|
109
|
+
return await this.refresh();
|
|
93
110
|
}
|
|
94
111
|
onChange(callback) {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
112
|
+
const storeObj = this._store.onChange(callback);
|
|
113
|
+
if (this._subscriptionsCount++ === 0 && this._options.onSubscribe) {
|
|
114
|
+
const unsubscribe = this._options.onSubscribe(() => {
|
|
115
|
+
runAsynchronously(this.refresh());
|
|
116
|
+
});
|
|
117
|
+
this._unsubscribers.push(unsubscribe);
|
|
118
|
+
}
|
|
119
|
+
runAsynchronously(this.refresh());
|
|
120
|
+
let hasUnsubscribed = false;
|
|
121
|
+
return {
|
|
122
|
+
unsubscribe: () => {
|
|
123
|
+
if (hasUnsubscribed)
|
|
124
|
+
return;
|
|
125
|
+
hasUnsubscribed = true;
|
|
126
|
+
storeObj.unsubscribe();
|
|
127
|
+
if (--this._subscriptionsCount === 0) {
|
|
128
|
+
for (const unsubscribe of this._unsubscribers) {
|
|
129
|
+
unsubscribe();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
};
|
|
99
134
|
}
|
|
100
135
|
}
|
package/dist/utils/crypto.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/utils/crypto.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/utils/crypto.tsx"],"names":[],"mappings":"AAMA,wBAAgB,0BAA0B,CAAC,gBAAgB,GAAE,MAAY,UAGxE"}
|
package/dist/utils/crypto.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
|
-
|
|
2
|
+
import { encodeBase32 } from "./bytes";
|
|
3
|
+
const characters = "23456789abcdefghjkmnopqrstuvwxyz";
|
|
4
|
+
if (characters.length !== 32)
|
|
5
|
+
throw new Error("Invalid characters length");
|
|
6
|
+
export function generateSecureRandomString(minBitsOfEntropy = 224) {
|
|
3
7
|
const randomBytes = crypto.randomBytes(Math.ceil(minBitsOfEntropy / 8));
|
|
4
|
-
return randomBytes.
|
|
8
|
+
return encodeBase32(randomBytes).toLowerCase();
|
|
5
9
|
}
|
package/dist/utils/dates.d.ts
CHANGED
|
@@ -10,3 +10,4 @@ export declare function fromNowDetailed(date: Date): {
|
|
|
10
10
|
* Returns a string representation of the given date in the format expected by the `datetime-local` input type.
|
|
11
11
|
*/
|
|
12
12
|
export declare function getInputDatetimeLocalString(date: Date): string;
|
|
13
|
+
//# sourceMappingURL=dates.d.ts.map
|
package/dist/utils/dom.d.ts
CHANGED
package/dist/utils/env.d.ts
CHANGED
package/dist/utils/errors.d.ts
CHANGED
package/dist/utils/html.d.ts
CHANGED
package/dist/utils/json.d.ts
CHANGED
|
@@ -8,3 +8,4 @@ export type ReadonlyJson = null | boolean | number | string | readonly ReadonlyJ
|
|
|
8
8
|
export declare function isJson(value: unknown): value is Json;
|
|
9
9
|
export declare function parseJson(json: string): Result<Json>;
|
|
10
10
|
export declare function stringifyJson(json: Json): Result<string>;
|
|
11
|
+
//# sourceMappingURL=json.d.ts.map
|
package/dist/utils/jwt.d.ts
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare class MaybeWeakMap<K, V> {
|
|
2
|
+
private readonly _primitiveMap;
|
|
3
|
+
private readonly _weakMap;
|
|
4
|
+
constructor(entries?: readonly (readonly [K, V])[] | null);
|
|
5
|
+
private _isAllowedInWeakMap;
|
|
6
|
+
get(key: K): V | undefined;
|
|
7
|
+
set(key: K, value: V): this;
|
|
8
|
+
delete(key: K): boolean;
|
|
9
|
+
has(key: K): boolean;
|
|
10
|
+
[Symbol.toStringTag]: string;
|
|
11
|
+
}
|
|
12
|
+
export declare class DependenciesMap<K extends any[], V> {
|
|
13
|
+
private _inner;
|
|
14
|
+
private _valueToResult;
|
|
15
|
+
private _unwrapFromInner;
|
|
16
|
+
private _setInInner;
|
|
17
|
+
get(dependencies: K): V | undefined;
|
|
18
|
+
set(dependencies: K, value: V): this;
|
|
19
|
+
delete(dependencies: K): boolean;
|
|
20
|
+
has(dependencies: K): boolean;
|
|
21
|
+
clear(): void;
|
|
22
|
+
[Symbol.toStringTag]: string;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=maps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"maps.d.ts","sourceRoot":"","sources":["../../src/utils/maps.tsx"],"names":[],"mappings":"AAEA,qBAAa,YAAY,CAAC,CAAC,EAAE,CAAC;IAC5B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAY;IAC1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA0B;gBAEvC,OAAO,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI;IAMzD,OAAO,CAAC,mBAAmB;IAI3B,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAQ1B,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAS3B,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAQvB,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAQpB,CAAC,MAAM,CAAC,WAAW,CAAC,SAAkB;CACvC;AAWD,qBAAa,eAAe,CAAC,CAAC,SAAS,GAAG,EAAE,EAAE,CAAC;IAC7C,OAAO,CAAC,MAAM,CAA2F;IAEzG,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,WAAW;IAqBnB,GAAG,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAInC,GAAG,CAAC,YAAY,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAKpC,MAAM,CAAC,YAAY,EAAE,CAAC,GAAG,OAAO;IAIhC,GAAG,CAAC,YAAY,EAAE,CAAC,GAAG,OAAO;IAI7B,KAAK,IAAI,IAAI;IAIb,CAAC,MAAM,CAAC,WAAW,CAAC,SAAqB;CAC1C"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Result } from "./results";
|
|
2
|
+
export class MaybeWeakMap {
|
|
3
|
+
_primitiveMap;
|
|
4
|
+
_weakMap;
|
|
5
|
+
constructor(entries) {
|
|
6
|
+
const entriesArray = [...entries ?? []];
|
|
7
|
+
this._primitiveMap = new Map(entriesArray.filter((e) => !this._isAllowedInWeakMap(e[0])));
|
|
8
|
+
this._weakMap = new WeakMap(entriesArray.filter((e) => this._isAllowedInWeakMap(e[0])));
|
|
9
|
+
}
|
|
10
|
+
_isAllowedInWeakMap(key) {
|
|
11
|
+
return (typeof key === "object" && key !== null) || (typeof key === "symbol" && Symbol.keyFor(key) === undefined);
|
|
12
|
+
}
|
|
13
|
+
get(key) {
|
|
14
|
+
if (this._isAllowedInWeakMap(key)) {
|
|
15
|
+
return this._weakMap.get(key);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
return this._primitiveMap.get(key);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
set(key, value) {
|
|
22
|
+
if (this._isAllowedInWeakMap(key)) {
|
|
23
|
+
this._weakMap.set(key, value);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
this._primitiveMap.set(key, value);
|
|
27
|
+
}
|
|
28
|
+
return this;
|
|
29
|
+
}
|
|
30
|
+
delete(key) {
|
|
31
|
+
if (this._isAllowedInWeakMap(key)) {
|
|
32
|
+
return this._weakMap.delete(key);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
return this._primitiveMap.delete(key);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
has(key) {
|
|
39
|
+
if (this._isAllowedInWeakMap(key)) {
|
|
40
|
+
return this._weakMap.has(key);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return this._primitiveMap.has(key);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
[Symbol.toStringTag] = "MaybeWeakMap";
|
|
47
|
+
}
|
|
48
|
+
export class DependenciesMap {
|
|
49
|
+
_inner = { map: new MaybeWeakMap(), hasValue: false, value: undefined };
|
|
50
|
+
_valueToResult(inner) {
|
|
51
|
+
if (inner.hasValue) {
|
|
52
|
+
return Result.ok(inner.value);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
return Result.error(undefined);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
_unwrapFromInner(dependencies, inner) {
|
|
59
|
+
if ((dependencies.length === 0)) {
|
|
60
|
+
return this._valueToResult(inner);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const [key, ...rest] = dependencies;
|
|
64
|
+
const newInner = inner.map.get(key);
|
|
65
|
+
if (!newInner) {
|
|
66
|
+
return Result.error(undefined);
|
|
67
|
+
}
|
|
68
|
+
return this._unwrapFromInner(rest, newInner);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
_setInInner(dependencies, value, inner) {
|
|
72
|
+
if (dependencies.length === 0) {
|
|
73
|
+
const res = this._valueToResult(inner);
|
|
74
|
+
if (value.status === "ok") {
|
|
75
|
+
inner.hasValue = true;
|
|
76
|
+
inner.value = value.data;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
inner.hasValue = false;
|
|
80
|
+
inner.value = undefined;
|
|
81
|
+
}
|
|
82
|
+
return res;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
const [key, ...rest] = dependencies;
|
|
86
|
+
let newInner = inner.map.get(key);
|
|
87
|
+
if (!newInner) {
|
|
88
|
+
inner.map.set(key, newInner = { map: new MaybeWeakMap(), hasValue: false, value: undefined });
|
|
89
|
+
}
|
|
90
|
+
return this._setInInner(rest, value, newInner);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
get(dependencies) {
|
|
94
|
+
return Result.or(this._unwrapFromInner(dependencies, this._inner), undefined);
|
|
95
|
+
}
|
|
96
|
+
set(dependencies, value) {
|
|
97
|
+
this._setInInner(dependencies, Result.ok(value), this._inner);
|
|
98
|
+
return this;
|
|
99
|
+
}
|
|
100
|
+
delete(dependencies) {
|
|
101
|
+
return this._setInInner(dependencies, Result.error(undefined), this._inner).status === "ok";
|
|
102
|
+
}
|
|
103
|
+
has(dependencies) {
|
|
104
|
+
return this._unwrapFromInner(dependencies, this._inner).status === "ok";
|
|
105
|
+
}
|
|
106
|
+
clear() {
|
|
107
|
+
this._inner = { map: new MaybeWeakMap(), hasValue: false, value: undefined };
|
|
108
|
+
}
|
|
109
|
+
[Symbol.toStringTag] = "DependenciesMap";
|
|
110
|
+
}
|
package/dist/utils/math.d.ts
CHANGED
package/dist/utils/numbers.d.ts
CHANGED
package/dist/utils/objects.d.ts
CHANGED
package/dist/utils/password.d.ts
CHANGED
package/dist/utils/promises.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export declare function resolved<T>(value: T): ReactPromise<T>;
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function rejected<T>(reason: unknown): ReactPromise<T>;
|
|
15
15
|
export declare function neverResolve(): ReactPromise<never>;
|
|
16
|
+
export declare function pending<T>(promise: Promise<T>): ReactPromise<T>;
|
|
16
17
|
export declare function wait(ms: number): Promise<void>;
|
|
17
18
|
export declare function waitUntil(date: Date): Promise<void>;
|
|
18
19
|
export declare function runAsynchronously(promiseOrFunc: Promise<unknown> | (() => Promise<unknown>) | undefined): void;
|
|
@@ -47,3 +48,4 @@ export type RateLimitOptions = {
|
|
|
47
48
|
export declare function rateLimited<T>(func: () => Promise<T>, options: RateLimitOptions): () => Promise<T>;
|
|
48
49
|
export declare function throttled<T, A extends any[]>(func: (...args: A) => Promise<T>, delayMs: number): (...args: A) => Promise<T>;
|
|
49
50
|
export {};
|
|
51
|
+
//# sourceMappingURL=promises.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"promises.d.ts","sourceRoot":"","sources":["../../src/utils/promises.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnC,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAElF,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CACvC,gBAAgB,CAAC,CAAC,CAAC,GACnB,iBAAiB,CAAC,CAAC,CAAC,GACpB,eAAe,CAAC,CAAC,CAAC,CACrB,CAAC;AAEF,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AACrC,KAAK,MAAM,GAAG,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;AACxC,wBAAgB,aAAa,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,CA0BzG;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAKrD;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC,CAK5D;AAED,wBAAgB,YAAY,IAAI,YAAY,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"promises.d.ts","sourceRoot":"","sources":["../../src/utils/promises.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAEnC,OAAO,KAAK,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAElF,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,CACvC,gBAAgB,CAAC,CAAC,CAAC,GACnB,iBAAiB,CAAC,CAAC,CAAC,GACpB,eAAe,CAAC,CAAC,CAAC,CACrB,CAAC;AAEF,KAAK,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;AACrC,KAAK,MAAM,GAAG,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;AACxC,wBAAgB,aAAa,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,YAAY,CAAC,CAAC,CAAC,CA0BzG;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAKrD;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,GAAG,YAAY,CAAC,CAAC,CAAC,CAK5D;AAED,wBAAgB,YAAY,IAAI,YAAY,CAAC,KAAK,CAAC,CAElD;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAI/D;AAED,wBAAsB,IAAI,CAAC,EAAE,EAAE,MAAM,iBAEpC;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,IAAI,iBAEzC;AAED,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,GAAG,IAAI,CAa9G;AAGD,cAAM,YAAa,SAAQ,KAAK;aACF,EAAE,EAAE,MAAM;gBAAV,EAAE,EAAE,MAAM;CAIvC;AAED,wBAAsB,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAKlG;AAED,wBAAsB,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAEjF;AAGD,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;OAEG;IACH,WAAW,EAAE,CAAC,CAAC;IAEf;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,wBAAgB,WAAW,CAAC,CAAC,EAC3B,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACtB,OAAO,EAAE,gBAAgB,GACxB,MAAM,OAAO,CAAC,CAAC,CAAC,CAoDlB;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,CAAC,SAAS,GAAG,EAAE,EAAE,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAe3H"}
|