applesauce-core 0.0.0-next-20241103143210

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.
Files changed (89) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +39 -0
  3. package/dist/event-store/common.d.ts +1 -0
  4. package/dist/event-store/common.js +2 -0
  5. package/dist/event-store/database.d.ts +60 -0
  6. package/dist/event-store/database.js +294 -0
  7. package/dist/event-store/event-store.d.ts +26 -0
  8. package/dist/event-store/event-store.js +236 -0
  9. package/dist/event-store/index.d.ts +2 -0
  10. package/dist/event-store/index.js +2 -0
  11. package/dist/helpers/bolt11.d.ts +8 -0
  12. package/dist/helpers/bolt11.js +14 -0
  13. package/dist/helpers/cache.d.ts +5 -0
  14. package/dist/helpers/cache.js +17 -0
  15. package/dist/helpers/emoji.d.ts +2 -0
  16. package/dist/helpers/emoji.js +4 -0
  17. package/dist/helpers/event.d.ts +34 -0
  18. package/dist/helpers/event.js +64 -0
  19. package/dist/helpers/filter.d.ts +12 -0
  20. package/dist/helpers/filter.js +50 -0
  21. package/dist/helpers/hashtag.d.ts +2 -0
  22. package/dist/helpers/hashtag.js +7 -0
  23. package/dist/helpers/index.d.ts +16 -0
  24. package/dist/helpers/index.js +16 -0
  25. package/dist/helpers/json.d.ts +1 -0
  26. package/dist/helpers/json.js +8 -0
  27. package/dist/helpers/lru.d.ts +32 -0
  28. package/dist/helpers/lru.js +148 -0
  29. package/dist/helpers/mailboxes.d.ts +17 -0
  30. package/dist/helpers/mailboxes.js +37 -0
  31. package/dist/helpers/mailboxes.test.d.ts +1 -0
  32. package/dist/helpers/mailboxes.test.js +80 -0
  33. package/dist/helpers/pointers.d.ts +22 -0
  34. package/dist/helpers/pointers.js +127 -0
  35. package/dist/helpers/profile.d.ts +25 -0
  36. package/dist/helpers/profile.js +28 -0
  37. package/dist/helpers/relays.d.ts +12 -0
  38. package/dist/helpers/relays.js +31 -0
  39. package/dist/helpers/string.d.ts +4 -0
  40. package/dist/helpers/string.js +13 -0
  41. package/dist/helpers/tags.d.ts +6 -0
  42. package/dist/helpers/tags.js +18 -0
  43. package/dist/helpers/threading.d.ts +55 -0
  44. package/dist/helpers/threading.js +73 -0
  45. package/dist/helpers/time.d.ts +2 -0
  46. package/dist/helpers/time.js +4 -0
  47. package/dist/helpers/url.d.ts +11 -0
  48. package/dist/helpers/url.js +29 -0
  49. package/dist/helpers/zap.d.ts +12 -0
  50. package/dist/helpers/zap.js +51 -0
  51. package/dist/index.d.ts +5 -0
  52. package/dist/index.js +5 -0
  53. package/dist/logger.d.ts +2 -0
  54. package/dist/logger.js +2 -0
  55. package/dist/observable/getValue.d.ts +2 -0
  56. package/dist/observable/getValue.js +13 -0
  57. package/dist/observable/index.d.ts +2 -0
  58. package/dist/observable/index.js +2 -0
  59. package/dist/observable/share-behavior.d.ts +2 -0
  60. package/dist/observable/share-behavior.js +7 -0
  61. package/dist/observable/share-latest-value.d.ts +8 -0
  62. package/dist/observable/share-latest-value.js +21 -0
  63. package/dist/observable/stateful.d.ts +10 -0
  64. package/dist/observable/stateful.js +60 -0
  65. package/dist/observable/throttle.d.ts +3 -0
  66. package/dist/observable/throttle.js +23 -0
  67. package/dist/promise/deferred.d.ts +5 -0
  68. package/dist/promise/deferred.js +14 -0
  69. package/dist/promise/index.d.ts +1 -0
  70. package/dist/promise/index.js +1 -0
  71. package/dist/queries/index.d.ts +6 -0
  72. package/dist/queries/index.js +6 -0
  73. package/dist/queries/mailboxes.d.ts +5 -0
  74. package/dist/queries/mailboxes.js +12 -0
  75. package/dist/queries/profile.d.ts +3 -0
  76. package/dist/queries/profile.js +11 -0
  77. package/dist/queries/reactions.d.ts +4 -0
  78. package/dist/queries/reactions.js +19 -0
  79. package/dist/queries/simple.d.ts +16 -0
  80. package/dist/queries/simple.js +38 -0
  81. package/dist/queries/thread.d.ts +23 -0
  82. package/dist/queries/thread.js +66 -0
  83. package/dist/queries/zaps.d.ts +4 -0
  84. package/dist/queries/zaps.js +16 -0
  85. package/dist/query-store/index.d.ts +47 -0
  86. package/dist/query-store/index.js +60 -0
  87. package/dist/utils/lru.d.ts +32 -0
  88. package/dist/utils/lru.js +148 -0
  89. package/package.json +83 -0
@@ -0,0 +1,60 @@
1
+ import { Observable } from "rxjs";
2
+ /** Wraps an {@link Observable} and makes it stateful */
3
+ export function stateful(observable, cleanup = false) {
4
+ let subscription = undefined;
5
+ let observers = [];
6
+ const self = new Observable((observer) => {
7
+ // add observer to list
8
+ observers.push(observer);
9
+ // pass any cached values
10
+ if (self.value)
11
+ observer.next(self.value);
12
+ if (self.error)
13
+ observer.error(self.error);
14
+ if (self.complete)
15
+ observer.complete();
16
+ // subscribe if not already
17
+ if (!subscription) {
18
+ subscription = observable.subscribe({
19
+ next: (v) => {
20
+ self.value = v;
21
+ for (const observer of observers)
22
+ observer.next(v);
23
+ },
24
+ error: (err) => {
25
+ self.error = err;
26
+ for (const observer of observers)
27
+ observer.error(err);
28
+ },
29
+ complete: () => {
30
+ self.complete = true;
31
+ for (const observer of observers)
32
+ observer.complete();
33
+ },
34
+ });
35
+ }
36
+ return () => {
37
+ let i = observers.indexOf(observer);
38
+ if (i !== -1) {
39
+ // remove observer from list
40
+ observers.splice(i, 1);
41
+ if (subscription && observers.length === 0) {
42
+ subscription.unsubscribe();
43
+ subscription = undefined;
44
+ // reset cached values
45
+ if (cleanup) {
46
+ delete self.value;
47
+ delete self.error;
48
+ delete self.complete;
49
+ }
50
+ }
51
+ }
52
+ };
53
+ });
54
+ self._stateful = true;
55
+ return self;
56
+ }
57
+ export function isStateful(observable) {
58
+ // @ts-expect-error
59
+ return observable._stateful;
60
+ }
@@ -0,0 +1,3 @@
1
+ import Observable from "zen-observable";
2
+ /** Throttles an {@link Observable} */
3
+ export declare function throttle<T>(source: Observable<T>, interval: number): Observable<T>;
@@ -0,0 +1,23 @@
1
+ import Observable from "zen-observable";
2
+ /** Throttles an {@link Observable} */
3
+ export function throttle(source, interval) {
4
+ return new Observable((observer) => {
5
+ let lastEmissionTime = 0;
6
+ let subscription = source.subscribe({
7
+ next(value) {
8
+ const currentTime = Date.now();
9
+ if (currentTime - lastEmissionTime >= interval) {
10
+ lastEmissionTime = currentTime;
11
+ observer.next(value);
12
+ }
13
+ },
14
+ error(err) {
15
+ observer.error(err);
16
+ },
17
+ complete() {
18
+ observer.complete();
19
+ },
20
+ });
21
+ return () => subscription.unsubscribe();
22
+ });
23
+ }
@@ -0,0 +1,5 @@
1
+ export type Deferred<T> = Promise<T> & {
2
+ resolve: (value?: T | PromiseLike<T>) => void;
3
+ reject: (reason?: any) => void;
4
+ };
5
+ export declare function createDefer<T>(): Deferred<T>;
@@ -0,0 +1,14 @@
1
+ export function createDefer() {
2
+ let _resolve;
3
+ let _reject;
4
+ const promise = new Promise((resolve, reject) => {
5
+ // @ts-ignore
6
+ _resolve = resolve;
7
+ _reject = reject;
8
+ });
9
+ // @ts-ignore
10
+ promise.resolve = _resolve;
11
+ // @ts-ignore
12
+ promise.reject = _reject;
13
+ return promise;
14
+ }
@@ -0,0 +1 @@
1
+ export * from "./deferred.js";
@@ -0,0 +1 @@
1
+ export * from "./deferred.js";
@@ -0,0 +1,6 @@
1
+ export * from "./simple.js";
2
+ export * from "./profile.js";
3
+ export * from "./mailboxes.js";
4
+ export * from "./reactions.js";
5
+ export * from "./thread.js";
6
+ export * from "./zaps.js";
@@ -0,0 +1,6 @@
1
+ export * from "./simple.js";
2
+ export * from "./profile.js";
3
+ export * from "./mailboxes.js";
4
+ export * from "./reactions.js";
5
+ export * from "./thread.js";
6
+ export * from "./zaps.js";
@@ -0,0 +1,5 @@
1
+ import { Query } from "../query-store/index.js";
2
+ export declare function MailboxesQuery(pubkey: string): Query<{
3
+ inboxes: string[];
4
+ outboxes: string[];
5
+ } | undefined>;
@@ -0,0 +1,12 @@
1
+ import { kinds } from "nostr-tools";
2
+ import { map } from "rxjs/operators";
3
+ import { getInboxes, getOutboxes } from "../helpers/mailboxes.js";
4
+ export function MailboxesQuery(pubkey) {
5
+ return {
6
+ key: pubkey,
7
+ run: (events) => events.replaceable(kinds.RelayList, pubkey).pipe(map((event) => event && {
8
+ inboxes: getInboxes(event),
9
+ outboxes: getOutboxes(event),
10
+ })),
11
+ };
12
+ }
@@ -0,0 +1,3 @@
1
+ import { ProfileContent } from "../helpers/profile.js";
2
+ import { Query } from "../query-store/index.js";
3
+ export declare function ProfileQuery(pubkey: string): Query<ProfileContent | undefined>;
@@ -0,0 +1,11 @@
1
+ import { kinds } from "nostr-tools";
2
+ import { map } from "rxjs/operators";
3
+ import { getProfileContent } from "../helpers/profile.js";
4
+ export function ProfileQuery(pubkey) {
5
+ return {
6
+ key: pubkey,
7
+ run: (events) => {
8
+ return events.replaceable(kinds.Metadata, pubkey).pipe(map((event) => event && getProfileContent(event)));
9
+ },
10
+ };
11
+ }
@@ -0,0 +1,4 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ import { Query } from "../query-store/index.js";
3
+ /** Creates a query that returns all reactions to an event (supports replaceable events) */
4
+ export declare function ReactionsQuery(event: NostrEvent): Query<NostrEvent[]>;
@@ -0,0 +1,19 @@
1
+ import { kinds } from "nostr-tools";
2
+ import { getEventUID, isReplaceable } from "../helpers/event.js";
3
+ /** Creates a query that returns all reactions to an event (supports replaceable events) */
4
+ export function ReactionsQuery(event) {
5
+ return {
6
+ key: getEventUID(event),
7
+ run: (events) => events.timeline(isReplaceable(event.kind)
8
+ ? [
9
+ { kinds: [kinds.Reaction], "#e": [event.id] },
10
+ { kinds: [kinds.Reaction], "#a": [getEventUID(event)] },
11
+ ]
12
+ : [
13
+ {
14
+ kinds: [kinds.Reaction],
15
+ "#e": [event.id],
16
+ },
17
+ ]),
18
+ };
19
+ }
@@ -0,0 +1,16 @@
1
+ import { Filter, NostrEvent } from "nostr-tools";
2
+ import { Query } from "../query-store/index.js";
3
+ /** Creates a Query that returns a single event or undefined */
4
+ export declare function SingleEventQuery(uid: string): Query<NostrEvent | undefined>;
5
+ /** Creates a Query that returns a multiple events in a map */
6
+ export declare function MultipleEventsQuery(uids: string[]): Query<Map<string, NostrEvent>>;
7
+ /** Creates a Query returning the latest version of a replaceable event */
8
+ export declare function ReplaceableQuery(kind: number, pubkey: string, d?: string): Query<NostrEvent | undefined>;
9
+ /** Creates a Query that returns an array of sorted events matching the filters */
10
+ export declare function TimelineQuery(filters: Filter | Filter[]): Query<NostrEvent[]>;
11
+ /** Creates a Query that returns a directory of events by their UID */
12
+ export declare function ReplaceableSetQuery(pointers: {
13
+ kind: number;
14
+ pubkey: string;
15
+ identifier?: string;
16
+ }[]): Query<Map<string, NostrEvent>>;
@@ -0,0 +1,38 @@
1
+ import stringify from "json-stringify-deterministic";
2
+ import { getReplaceableUID } from "../helpers/event.js";
3
+ /** Creates a Query that returns a single event or undefined */
4
+ export function SingleEventQuery(uid) {
5
+ return {
6
+ key: uid,
7
+ run: (events) => events.event(uid),
8
+ };
9
+ }
10
+ /** Creates a Query that returns a multiple events in a map */
11
+ export function MultipleEventsQuery(uids) {
12
+ return {
13
+ key: uids.join(","),
14
+ run: (events) => events.events(uids),
15
+ };
16
+ }
17
+ /** Creates a Query returning the latest version of a replaceable event */
18
+ export function ReplaceableQuery(kind, pubkey, d) {
19
+ return {
20
+ key: getReplaceableUID(kind, pubkey, d),
21
+ run: (events) => events.replaceable(kind, pubkey, d),
22
+ };
23
+ }
24
+ /** Creates a Query that returns an array of sorted events matching the filters */
25
+ export function TimelineQuery(filters) {
26
+ return {
27
+ key: stringify(filters),
28
+ run: (events) => events.timeline(Array.isArray(filters) ? filters : [filters]),
29
+ };
30
+ }
31
+ /** Creates a Query that returns a directory of events by their UID */
32
+ export function ReplaceableSetQuery(pointers) {
33
+ const cords = pointers.map((pointer) => getReplaceableUID(pointer.kind, pointer.pubkey, pointer.identifier));
34
+ return {
35
+ key: stringify(pointers),
36
+ run: (events) => events.events(cords),
37
+ };
38
+ }
@@ -0,0 +1,23 @@
1
+ import { NostrEvent } from "nostr-tools";
2
+ import { AddressPointer, EventPointer } from "nostr-tools/nip19";
3
+ import { Query } from "../query-store/index.js";
4
+ import { ThreadReferences } from "../helpers/threading.js";
5
+ export type Thread = {
6
+ root?: ThreadItem;
7
+ all: Map<string, ThreadItem>;
8
+ };
9
+ export type ThreadItem = {
10
+ /** underlying nostr event */
11
+ event: NostrEvent;
12
+ refs: ThreadReferences;
13
+ /** the thread root, according to this event */
14
+ root?: ThreadItem;
15
+ /** the parent event this is replying to */
16
+ parent?: ThreadItem;
17
+ /** direct child replies */
18
+ replies: Set<ThreadItem>;
19
+ };
20
+ export type ThreadQueryOptions = {
21
+ kinds?: number[];
22
+ };
23
+ export declare function ThreadQuery(root: string | AddressPointer | EventPointer, opts?: ThreadQueryOptions): Query<Thread>;
@@ -0,0 +1,66 @@
1
+ import { kinds } from "nostr-tools";
2
+ import { map } from "rxjs/operators";
3
+ import { getNip10References } from "../helpers/threading.js";
4
+ import { getCoordinateFromAddressPointer, isAddressPointer } from "../helpers/pointers.js";
5
+ import { getEventUID } from "../helpers/event.js";
6
+ const defaultOptions = {
7
+ kinds: [kinds.ShortTextNote],
8
+ };
9
+ export function ThreadQuery(root, opts) {
10
+ const parentReferences = new Map();
11
+ const items = new Map();
12
+ const { kinds } = { ...defaultOptions, ...opts };
13
+ let rootUID = "";
14
+ const rootFilter = {};
15
+ const replyFilter = { kinds };
16
+ if (isAddressPointer(root)) {
17
+ rootUID = getCoordinateFromAddressPointer(root);
18
+ rootFilter.kinds = [root.kind];
19
+ rootFilter.authors = [root.pubkey];
20
+ rootFilter["#d"] = [root.identifier];
21
+ replyFilter["#a"] = [rootUID];
22
+ }
23
+ else if (typeof root === "string") {
24
+ rootUID = root;
25
+ rootFilter.ids = [root];
26
+ replyFilter["#e"] = [root];
27
+ }
28
+ else {
29
+ rootUID = root.id;
30
+ rootFilter.ids = [root.id];
31
+ replyFilter["#e"] = [root.id];
32
+ }
33
+ return {
34
+ key: `${rootUID}-${kinds.join(",")}`,
35
+ run: (events) => events.stream([rootFilter, replyFilter]).pipe(map((event) => {
36
+ if (!items.has(getEventUID(event))) {
37
+ const refs = getNip10References(event);
38
+ const replies = parentReferences.get(getEventUID(event)) || new Set();
39
+ const item = { event, refs, replies };
40
+ for (const child of replies) {
41
+ child.parent = item;
42
+ }
43
+ // add item to parent
44
+ if (refs.reply?.e || refs.reply?.a) {
45
+ let uid = refs.reply.e ? refs.reply.e.id : getCoordinateFromAddressPointer(refs.reply.a);
46
+ item.parent = items.get(uid);
47
+ if (item.parent) {
48
+ item.parent.replies.add(item);
49
+ }
50
+ else {
51
+ // parent isn't created yet, store ref for later
52
+ let set = parentReferences.get(uid);
53
+ if (!set) {
54
+ set = new Set();
55
+ parentReferences.set(uid, set);
56
+ }
57
+ set.add(item);
58
+ }
59
+ }
60
+ // add item to map
61
+ items.set(getEventUID(event), item);
62
+ }
63
+ return { root: items.get(rootUID), all: items };
64
+ })),
65
+ };
66
+ }
@@ -0,0 +1,4 @@
1
+ import { AddressPointer, EventPointer } from "nostr-tools/nip19";
2
+ import { NostrEvent } from "nostr-tools";
3
+ import { Query } from "../query-store/index.js";
4
+ export declare function EventZapsQuery(id: string | EventPointer | AddressPointer): Query<NostrEvent[]>;
@@ -0,0 +1,16 @@
1
+ import { kinds } from "nostr-tools";
2
+ import { getCoordinateFromAddressPointer, isAddressPointer } from "../helpers/pointers.js";
3
+ export function EventZapsQuery(id) {
4
+ return {
5
+ key: JSON.stringify(id),
6
+ run: (events) => {
7
+ if (isAddressPointer(id)) {
8
+ return events.timeline([{ kinds: [kinds.Zap], "#a": [getCoordinateFromAddressPointer(id)] }]);
9
+ }
10
+ else {
11
+ id = typeof id === "string" ? id : id.id;
12
+ return events.timeline([{ kinds: [kinds.Zap], "#e": [id] }]);
13
+ }
14
+ },
15
+ };
16
+ }
@@ -0,0 +1,47 @@
1
+ import { BehaviorSubject, Observable } from "rxjs";
2
+ import { Filter, NostrEvent } from "nostr-tools";
3
+ import { EventStore } from "../event-store/event-store.js";
4
+ import { LRU } from "../helpers/lru.js";
5
+ import * as Queries from "../queries/index.js";
6
+ import { AddressPointer, EventPointer } from "nostr-tools/nip19";
7
+ export type Query<T extends unknown> = {
8
+ key: string;
9
+ run: (events: EventStore, store: QueryStore) => Observable<T>;
10
+ };
11
+ export type QueryConstructor<T extends unknown, Args extends Array<any>> = (...args: Args) => Query<T>;
12
+ export declare class QueryStore {
13
+ static Queries: typeof Queries;
14
+ store: EventStore;
15
+ constructor(store: EventStore);
16
+ queries: LRU<Observable<any> | BehaviorSubject<any>>;
17
+ /** Creates a cached query */
18
+ runQuery<T extends unknown, Args extends Array<any>>(queryConstructor: (...args: Args) => {
19
+ key: string;
20
+ run: (events: EventStore, store: QueryStore) => Observable<T>;
21
+ }): (...args: Args) => Observable<T>;
22
+ /** Returns a single event */
23
+ event(id: string): Observable<import("nostr-tools").Event | undefined>;
24
+ /** Returns a single event */
25
+ events(ids: string[]): Observable<Map<string, import("nostr-tools").Event>>;
26
+ /** Returns the latest version of a replaceable event */
27
+ replaceable(kind: number, pubkey: string, d?: string): Observable<import("nostr-tools").Event | undefined>;
28
+ /** Returns a directory of events by their UID */
29
+ replaceableSet(pointers: {
30
+ kind: number;
31
+ pubkey: string;
32
+ identifier?: string;
33
+ }[]): Observable<Map<string, import("nostr-tools").Event>>;
34
+ /** Returns an array of events that match the filter */
35
+ timeline(filters: Filter | Filter[]): Observable<import("nostr-tools").Event[]>;
36
+ /** Returns the parsed profile (0) for a pubkey */
37
+ profile(pubkey: string): Observable<import("../helpers/profile.js").ProfileContent | undefined>;
38
+ /** Returns all reactions for an event (supports replaceable events) */
39
+ reactions(event: NostrEvent): Observable<import("nostr-tools").Event[]>;
40
+ /** Returns the parsed relay list (10002) for the pubkey */
41
+ mailboxes(pubkey: string): Observable<{
42
+ inboxes: string[];
43
+ outboxes: string[];
44
+ } | undefined>;
45
+ thread(root: string | EventPointer | AddressPointer): Observable<Queries.Thread>;
46
+ }
47
+ export { Queries };
@@ -0,0 +1,60 @@
1
+ import { LRU } from "../helpers/lru.js";
2
+ import * as Queries from "../queries/index.js";
3
+ import { shareLatestValue } from "../observable/share-latest-value.js";
4
+ export class QueryStore {
5
+ static Queries = Queries;
6
+ store;
7
+ constructor(store) {
8
+ this.store = store;
9
+ }
10
+ queries = new LRU();
11
+ /** Creates a cached query */
12
+ runQuery(queryConstructor) {
13
+ return (...args) => {
14
+ const query = queryConstructor(...args);
15
+ const key = `${queryConstructor.name}|${query.key}`;
16
+ if (!this.queries.has(key)) {
17
+ const observable = query.run(this.store, this).pipe(shareLatestValue());
18
+ this.queries.set(key, observable);
19
+ return observable;
20
+ }
21
+ return this.queries.get(key);
22
+ };
23
+ }
24
+ /** Returns a single event */
25
+ event(id) {
26
+ return this.runQuery(Queries.SingleEventQuery)(id);
27
+ }
28
+ /** Returns a single event */
29
+ events(ids) {
30
+ return this.runQuery(Queries.MultipleEventsQuery)(ids);
31
+ }
32
+ /** Returns the latest version of a replaceable event */
33
+ replaceable(kind, pubkey, d) {
34
+ return this.runQuery(Queries.ReplaceableQuery)(kind, pubkey, d);
35
+ }
36
+ /** Returns a directory of events by their UID */
37
+ replaceableSet(pointers) {
38
+ return this.runQuery(Queries.ReplaceableSetQuery)(pointers);
39
+ }
40
+ /** Returns an array of events that match the filter */
41
+ timeline(filters) {
42
+ return this.runQuery(Queries.TimelineQuery)(filters);
43
+ }
44
+ /** Returns the parsed profile (0) for a pubkey */
45
+ profile(pubkey) {
46
+ return this.runQuery(Queries.ProfileQuery)(pubkey);
47
+ }
48
+ /** Returns all reactions for an event (supports replaceable events) */
49
+ reactions(event) {
50
+ return this.runQuery(Queries.ReactionsQuery)(event);
51
+ }
52
+ /** Returns the parsed relay list (10002) for the pubkey */
53
+ mailboxes(pubkey) {
54
+ return this.runQuery(Queries.MailboxesQuery)(pubkey);
55
+ }
56
+ thread(root) {
57
+ return this.runQuery(Queries.ThreadQuery)(root);
58
+ }
59
+ }
60
+ export { Queries };
@@ -0,0 +1,32 @@
1
+ type Item<T> = {
2
+ key: string;
3
+ prev: Item<T> | null;
4
+ value: T;
5
+ next: Item<T> | null;
6
+ expiry: number;
7
+ };
8
+ /**
9
+ * Copied from tiny-lru and modified to support typescript
10
+ * @see https://github.com/avoidwork/tiny-lru/blob/master/src/lru.js
11
+ */
12
+ export declare class LRU<T extends unknown> {
13
+ first: Item<T> | null;
14
+ items: Record<string, Item<T>>;
15
+ last: Item<T> | null;
16
+ max: number;
17
+ resetTtl: boolean;
18
+ size: number;
19
+ ttl: number;
20
+ constructor(max?: number, ttl?: number, resetTtl?: boolean);
21
+ clear(): this;
22
+ delete(key: string): this;
23
+ entries(keys?: string[]): (string | T | undefined)[][];
24
+ evict(bypass?: boolean): this;
25
+ expiresAt(key: string): number | undefined;
26
+ get(key: string): T | undefined;
27
+ has(key: string): boolean;
28
+ keys(): string[];
29
+ set(key: string, value: T, bypass?: boolean, resetTtl?: boolean): this;
30
+ values(keys?: string[]): NonNullable<T>[];
31
+ }
32
+ export {};
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Copied from tiny-lru and modified to support typescript
3
+ * @see https://github.com/avoidwork/tiny-lru/blob/master/src/lru.js
4
+ */
5
+ export class LRU {
6
+ first = null;
7
+ items = Object.create(null);
8
+ last = null;
9
+ max;
10
+ resetTtl;
11
+ size;
12
+ ttl;
13
+ constructor(max = 0, ttl = 0, resetTtl = false) {
14
+ this.first = null;
15
+ this.items = Object.create(null);
16
+ this.last = null;
17
+ this.max = max;
18
+ this.resetTtl = resetTtl;
19
+ this.size = 0;
20
+ this.ttl = ttl;
21
+ }
22
+ clear() {
23
+ this.first = null;
24
+ this.items = Object.create(null);
25
+ this.last = null;
26
+ this.size = 0;
27
+ return this;
28
+ }
29
+ delete(key) {
30
+ if (this.has(key)) {
31
+ const item = this.items[key];
32
+ delete this.items[key];
33
+ this.size--;
34
+ if (item.prev !== null) {
35
+ item.prev.next = item.next;
36
+ }
37
+ if (item.next !== null) {
38
+ item.next.prev = item.prev;
39
+ }
40
+ if (this.first === item) {
41
+ this.first = item.next;
42
+ }
43
+ if (this.last === item) {
44
+ this.last = item.prev;
45
+ }
46
+ }
47
+ return this;
48
+ }
49
+ entries(keys = this.keys()) {
50
+ return keys.map((key) => [key, this.get(key)]);
51
+ }
52
+ evict(bypass = false) {
53
+ if (bypass || this.size > 0) {
54
+ const item = this.first;
55
+ delete this.items[item.key];
56
+ if (--this.size === 0) {
57
+ this.first = null;
58
+ this.last = null;
59
+ }
60
+ else {
61
+ this.first = item.next;
62
+ this.first.prev = null;
63
+ }
64
+ }
65
+ return this;
66
+ }
67
+ expiresAt(key) {
68
+ let result;
69
+ if (this.has(key)) {
70
+ result = this.items[key].expiry;
71
+ }
72
+ return result;
73
+ }
74
+ get(key) {
75
+ let result;
76
+ if (this.has(key)) {
77
+ const item = this.items[key];
78
+ if (this.ttl > 0 && item.expiry <= Date.now()) {
79
+ this.delete(key);
80
+ }
81
+ else {
82
+ result = item.value;
83
+ this.set(key, result, true);
84
+ }
85
+ }
86
+ return result;
87
+ }
88
+ has(key) {
89
+ return key in this.items;
90
+ }
91
+ keys() {
92
+ const result = [];
93
+ let x = this.first;
94
+ while (x !== null) {
95
+ result.push(x.key);
96
+ x = x.next;
97
+ }
98
+ return result;
99
+ }
100
+ set(key, value, bypass = false, resetTtl = this.resetTtl) {
101
+ let item;
102
+ if (bypass || this.has(key)) {
103
+ item = this.items[key];
104
+ item.value = value;
105
+ if (bypass === false && resetTtl) {
106
+ item.expiry = this.ttl > 0 ? Date.now() + this.ttl : this.ttl;
107
+ }
108
+ if (this.last !== item) {
109
+ const last = this.last, next = item.next, prev = item.prev;
110
+ if (this.first === item) {
111
+ this.first = item.next;
112
+ }
113
+ item.next = null;
114
+ item.prev = this.last;
115
+ last.next = item;
116
+ if (prev !== null) {
117
+ prev.next = next;
118
+ }
119
+ if (next !== null) {
120
+ next.prev = prev;
121
+ }
122
+ }
123
+ }
124
+ else {
125
+ if (this.max > 0 && this.size === this.max) {
126
+ this.evict(true);
127
+ }
128
+ item = this.items[key] = {
129
+ expiry: this.ttl > 0 ? Date.now() + this.ttl : this.ttl,
130
+ key: key,
131
+ prev: this.last,
132
+ next: null,
133
+ value,
134
+ };
135
+ if (++this.size === 1) {
136
+ this.first = item;
137
+ }
138
+ else {
139
+ this.last.next = item;
140
+ }
141
+ }
142
+ this.last = item;
143
+ return this;
144
+ }
145
+ values(keys = this.keys()) {
146
+ return keys.map((key) => this.get(key));
147
+ }
148
+ }