@helia/ipns 0.0.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.
Files changed (44) hide show
  1. package/LICENSE +4 -0
  2. package/README.md +59 -0
  3. package/dist/index.min.js +25 -0
  4. package/dist/src/index.d.ts +124 -0
  5. package/dist/src/index.d.ts.map +1 -0
  6. package/dist/src/index.js +192 -0
  7. package/dist/src/index.js.map +1 -0
  8. package/dist/src/routing/dht.d.ts +18 -0
  9. package/dist/src/routing/dht.d.ts.map +1 -0
  10. package/dist/src/routing/dht.js +65 -0
  11. package/dist/src/routing/dht.js.map +1 -0
  12. package/dist/src/routing/index.d.ts +17 -0
  13. package/dist/src/routing/index.d.ts.map +1 -0
  14. package/dist/src/routing/index.js +3 -0
  15. package/dist/src/routing/index.js.map +1 -0
  16. package/dist/src/routing/local-store.d.ts +15 -0
  17. package/dist/src/routing/local-store.d.ts.map +1 -0
  18. package/dist/src/routing/local-store.js +48 -0
  19. package/dist/src/routing/local-store.js.map +1 -0
  20. package/dist/src/routing/pubsub.d.ts +20 -0
  21. package/dist/src/routing/pubsub.d.ts.map +1 -0
  22. package/dist/src/routing/pubsub.js +150 -0
  23. package/dist/src/routing/pubsub.js.map +1 -0
  24. package/dist/src/utils/resolve-dns-link.browser.d.ts +6 -0
  25. package/dist/src/utils/resolve-dns-link.browser.d.ts.map +1 -0
  26. package/dist/src/utils/resolve-dns-link.browser.js +46 -0
  27. package/dist/src/utils/resolve-dns-link.browser.js.map +1 -0
  28. package/dist/src/utils/resolve-dns-link.d.ts +3 -0
  29. package/dist/src/utils/resolve-dns-link.d.ts.map +1 -0
  30. package/dist/src/utils/resolve-dns-link.js +54 -0
  31. package/dist/src/utils/resolve-dns-link.js.map +1 -0
  32. package/dist/src/utils/tlru.d.ts +15 -0
  33. package/dist/src/utils/tlru.d.ts.map +1 -0
  34. package/dist/src/utils/tlru.js +39 -0
  35. package/dist/src/utils/tlru.js.map +1 -0
  36. package/package.json +191 -0
  37. package/src/index.ts +296 -0
  38. package/src/routing/dht.ts +85 -0
  39. package/src/routing/index.ts +26 -0
  40. package/src/routing/local-store.ts +63 -0
  41. package/src/routing/pubsub.ts +195 -0
  42. package/src/utils/resolve-dns-link.browser.ts +61 -0
  43. package/src/utils/resolve-dns-link.ts +65 -0
  44. package/src/utils/tlru.ts +52 -0
@@ -0,0 +1,150 @@
1
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
2
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
3
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
4
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
5
+ };
6
+ var _PubSubRouting_instances, _PubSubRouting_processPubSubMessage;
7
+ import { peerIdToRoutingKey } from 'ipns';
8
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
9
+ import { toString as uint8ArrayToString } from 'uint8arrays/to-string';
10
+ import { logger } from '@libp2p/logger';
11
+ import { CodeError } from '@libp2p/interfaces/errors';
12
+ import { localStore } from './local-store.js';
13
+ import { ipnsValidator } from 'ipns/validator';
14
+ import { ipnsSelector } from 'ipns/selector';
15
+ import { equals as uint8ArrayEquals } from 'uint8arrays/equals';
16
+ import { CustomProgressEvent } from 'progress-events';
17
+ const log = logger('helia:ipns:routing:pubsub');
18
+ /**
19
+ * This IPNS routing receives IPNS record updates via dedicated
20
+ * pubsub topic.
21
+ *
22
+ * Note we must first be subscribed to the topic in order to receive
23
+ * updated records, so the first call to `.get` should be expected
24
+ * to fail!
25
+ */
26
+ class PubSubRouting {
27
+ constructor(components) {
28
+ _PubSubRouting_instances.add(this);
29
+ this.subscriptions = [];
30
+ this.localStore = localStore(components.datastore);
31
+ this.peerId = components.libp2p.peerId;
32
+ this.pubsub = components.libp2p.pubsub;
33
+ this.pubsub.addEventListener('message', (evt) => {
34
+ const message = evt.detail;
35
+ if (!this.subscriptions.includes(message.topic)) {
36
+ return;
37
+ }
38
+ __classPrivateFieldGet(this, _PubSubRouting_instances, "m", _PubSubRouting_processPubSubMessage).call(this, message).catch(err => {
39
+ log.error('Error processing message', err);
40
+ });
41
+ });
42
+ }
43
+ /**
44
+ * Put a value to the pubsub datastore indexed by the received key properly encoded
45
+ */
46
+ async put(routingKey, marshaledRecord, options = {}) {
47
+ try {
48
+ const topic = keyToTopic(routingKey);
49
+ log('publish value for topic %s', topic);
50
+ const result = await this.pubsub.publish(topic, marshaledRecord);
51
+ log('published record on topic %s to %d recipients', topic, result.recipients);
52
+ options.onProgress?.(new CustomProgressEvent('ipns:pubsub:publish', { topic, result }));
53
+ }
54
+ catch (err) {
55
+ options.onProgress?.(new CustomProgressEvent('ipns:pubsub:error', err));
56
+ throw err;
57
+ }
58
+ }
59
+ /**
60
+ * Get a value from the pubsub datastore indexed by the received key properly encoded.
61
+ * Also, the identifier topic is subscribed to and the pubsub datastore records will be
62
+ * updated once new publishes occur
63
+ */
64
+ async get(routingKey, options = {}) {
65
+ try {
66
+ const topic = keyToTopic(routingKey);
67
+ // ensure we are subscribed to topic
68
+ if (!this.pubsub.getTopics().includes(topic)) {
69
+ log('add subscription for topic', topic);
70
+ this.pubsub.subscribe(topic);
71
+ this.subscriptions.push(topic);
72
+ options.onProgress?.(new CustomProgressEvent('ipns:pubsub:subscribe', { topic }));
73
+ }
74
+ // chain through to local store
75
+ return await this.localStore.get(routingKey, options);
76
+ }
77
+ catch (err) {
78
+ options.onProgress?.(new CustomProgressEvent('ipns:pubsub:error', err));
79
+ throw err;
80
+ }
81
+ }
82
+ /**
83
+ * Get pubsub subscriptions related to ipns
84
+ */
85
+ getSubscriptions() {
86
+ return this.subscriptions;
87
+ }
88
+ /**
89
+ * Cancel pubsub subscriptions related to ipns
90
+ */
91
+ cancel(key) {
92
+ const routingKey = peerIdToRoutingKey(key);
93
+ const topic = keyToTopic(routingKey);
94
+ // Not found topic
95
+ if (!this.subscriptions.includes(topic)) {
96
+ return;
97
+ }
98
+ this.pubsub.unsubscribe(topic);
99
+ this.subscriptions = this.subscriptions.filter(t => t !== topic);
100
+ }
101
+ }
102
+ _PubSubRouting_instances = new WeakSet(), _PubSubRouting_processPubSubMessage = async function _PubSubRouting_processPubSubMessage(message) {
103
+ log('message received for topic', message.topic);
104
+ if (message.type !== 'signed') {
105
+ log.error('unsigned message received, this module can only work with signed messages');
106
+ return;
107
+ }
108
+ if (message.from.equals(this.peerId)) {
109
+ log('not storing record from self');
110
+ return;
111
+ }
112
+ const routingKey = topicToKey(message.topic);
113
+ await ipnsValidator(routingKey, message.data);
114
+ if (await this.localStore.has(routingKey)) {
115
+ const currentRecord = await this.localStore.get(routingKey);
116
+ if (uint8ArrayEquals(currentRecord, message.data)) {
117
+ log('not storing record as we already have it');
118
+ return;
119
+ }
120
+ const records = [currentRecord, message.data];
121
+ const index = ipnsSelector(routingKey, records);
122
+ if (index === 0) {
123
+ log('not storing record as the one we have is better');
124
+ return;
125
+ }
126
+ }
127
+ await this.localStore.put(routingKey, message.data);
128
+ };
129
+ const PUBSUB_NAMESPACE = '/record/';
130
+ /**
131
+ * converts a binary record key to a pubsub topic key
132
+ */
133
+ function keyToTopic(key) {
134
+ const b64url = uint8ArrayToString(key, 'base64url');
135
+ return `${PUBSUB_NAMESPACE}${b64url}`;
136
+ }
137
+ /**
138
+ * converts a pubsub topic key to a binary record key
139
+ */
140
+ function topicToKey(topic) {
141
+ if (topic.substring(0, PUBSUB_NAMESPACE.length) !== PUBSUB_NAMESPACE) {
142
+ throw new CodeError('topic received is not from a record', 'ERR_TOPIC_IS_NOT_FROM_RECORD_NAMESPACE');
143
+ }
144
+ const key = topic.substring(PUBSUB_NAMESPACE.length);
145
+ return uint8ArrayFromString(key, 'base64url');
146
+ }
147
+ export function pubsub(components) {
148
+ return new PubSubRouting(components);
149
+ }
150
+ //# sourceMappingURL=pubsub.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pubsub.js","sourceRoot":"","sources":["../../../src/routing/pubsub.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { AbortOptions } from '@libp2p/interfaces';
2
+ export interface ResolveDnsLinkOptions extends AbortOptions {
3
+ nocache?: boolean;
4
+ }
5
+ export declare function resolveDnslink(fqdn: string, opts?: ResolveDnsLinkOptions): Promise<string>;
6
+ //# sourceMappingURL=resolve-dns-link.browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-dns-link.browser.d.ts","sourceRoot":"","sources":["../../../src/utils/resolve-dns-link.browser.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAoBtD,MAAM,WAAW,qBAAsB,SAAQ,YAAY;IACzD,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,wBAAsB,cAAc,CAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,qBAA0B,GAAG,OAAO,CAAC,MAAM,CAAC,CAgCrG"}
@@ -0,0 +1,46 @@
1
+ /* eslint-env browser */
2
+ import { TLRU } from './tlru.js';
3
+ import PQueue from 'p-queue';
4
+ // Avoid sending multiple queries for the same hostname by caching results
5
+ const cache = new TLRU(1000);
6
+ // TODO: /api/v0/dns does not return TTL yet: https://github.com/ipfs/go-ipfs/issues/5884
7
+ // However we know browsers themselves cache DNS records for at least 1 minute,
8
+ // which acts a provisional default ttl: https://stackoverflow.com/a/36917902/11518426
9
+ const ttl = 60 * 1000;
10
+ // browsers limit concurrent connections per host,
11
+ // we don't want preload calls to exhaust the limit (~6)
12
+ const httpQueue = new PQueue({ concurrency: 4 });
13
+ const ipfsPath = (response) => {
14
+ if (response.Path != null) {
15
+ return response.Path;
16
+ }
17
+ throw new Error(response.Message);
18
+ };
19
+ export async function resolveDnslink(fqdn, opts = {}) {
20
+ const resolve = async (fqdn, opts = {}) => {
21
+ // @ts-expect-error - URLSearchParams does not take boolean options, only strings
22
+ const searchParams = new URLSearchParams(opts);
23
+ searchParams.set('arg', fqdn);
24
+ // try cache first
25
+ const query = searchParams.toString();
26
+ if (opts.nocache !== true && cache.has(query)) {
27
+ const response = cache.get(query);
28
+ if (response != null) {
29
+ return ipfsPath(response);
30
+ }
31
+ }
32
+ // fallback to delegated DNS resolver
33
+ const response = await httpQueue.add(async () => {
34
+ // Delegated HTTP resolver sending DNSLink queries to ipfs.io
35
+ // TODO: replace hardcoded host with configurable DNS over HTTPS: https://github.com/ipfs/js-ipfs/issues/2212
36
+ const res = await fetch(`https://ipfs.io/api/v0/dns?${searchParams}`);
37
+ const query = new URL(res.url).search.slice(1);
38
+ const json = await res.json();
39
+ cache.set(query, json, ttl);
40
+ return json;
41
+ });
42
+ return ipfsPath(response);
43
+ };
44
+ return await resolve(fqdn, opts);
45
+ }
46
+ //# sourceMappingURL=resolve-dns-link.browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-dns-link.browser.js","sourceRoot":"","sources":["../../../src/utils/resolve-dns-link.browser.ts"],"names":[],"mappings":"AAAA,wBAAwB;AAExB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAChC,OAAO,MAAM,MAAM,SAAS,CAAA;AAG5B,0EAA0E;AAC1E,MAAM,KAAK,GAAG,IAAI,IAAI,CAAoC,IAAI,CAAC,CAAA;AAC/D,yFAAyF;AACzF,+EAA+E;AAC/E,sFAAsF;AACtF,MAAM,GAAG,GAAG,EAAE,GAAG,IAAI,CAAA;AAErB,kDAAkD;AAClD,wDAAwD;AACxD,MAAM,SAAS,GAAG,IAAI,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC,CAAA;AAEhD,MAAM,QAAQ,GAAG,CAAC,QAA2C,EAAU,EAAE;IACvE,IAAI,QAAQ,CAAC,IAAI,IAAI,IAAI,EAAE;QACzB,OAAO,QAAQ,CAAC,IAAI,CAAA;KACrB;IACD,MAAM,IAAI,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;AACnC,CAAC,CAAA;AAMD,MAAM,CAAC,KAAK,UAAU,cAAc,CAAE,IAAY,EAAE,OAA8B,EAAE;IAClF,MAAM,OAAO,GAAG,KAAK,EAAE,IAAY,EAAE,OAA8B,EAAE,EAAmB,EAAE;QACxF,iFAAiF;QACjF,MAAM,YAAY,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,CAAA;QAC9C,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;QAE7B,kBAAkB;QAClB,MAAM,KAAK,GAAG,YAAY,CAAC,QAAQ,EAAE,CAAA;QACrC,IAAI,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;YAC7C,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;YAEjC,IAAI,QAAQ,IAAI,IAAI,EAAE;gBACpB,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAA;aAC1B;SACF;QAED,qCAAqC;QACrC,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE;YAC9C,6DAA6D;YAC7D,6GAA6G;YAC7G,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,8BAA8B,YAAY,EAAE,CAAC,CAAA;YACrE,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;YAC9C,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAA;YAC7B,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,CAAA;YAE3B,OAAO,IAAI,CAAA;QACb,CAAC,CAAC,CAAA;QAEF,OAAO,QAAQ,CAAC,QAAQ,CAAC,CAAA;IAC3B,CAAC,CAAA;IAED,OAAO,MAAM,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;AAClC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { AbortOptions } from '@libp2p/interfaces';
2
+ export declare function resolveDnslink(domain: string, options?: AbortOptions): Promise<string>;
3
+ //# sourceMappingURL=resolve-dns-link.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-dns-link.d.ts","sourceRoot":"","sources":["../../../src/utils/resolve-dns-link.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAKtD,wBAAsB,cAAc,CAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAEjG"}
@@ -0,0 +1,54 @@
1
+ import dns from 'dns';
2
+ import { promisify } from 'util';
3
+ import * as isIPFS from 'is-ipfs';
4
+ const MAX_RECURSIVE_DEPTH = 32;
5
+ export async function resolveDnslink(domain, options = {}) {
6
+ return await recursiveResolveDnslink(domain, MAX_RECURSIVE_DEPTH, options);
7
+ }
8
+ async function recursiveResolveDnslink(domain, depth, options = {}) {
9
+ if (depth === 0) {
10
+ throw new Error('recursion limit exceeded');
11
+ }
12
+ let dnslinkRecord;
13
+ try {
14
+ dnslinkRecord = await resolve(domain);
15
+ }
16
+ catch (err) {
17
+ // If the code is not ENOTFOUND or ERR_DNSLINK_NOT_FOUND or ENODATA then throw the error
18
+ if (err.code !== 'ENOTFOUND' && err.code !== 'ERR_DNSLINK_NOT_FOUND' && err.code !== 'ENODATA') {
19
+ throw err;
20
+ }
21
+ if (domain.startsWith('_dnslink.')) {
22
+ // The supplied domain contains a _dnslink component
23
+ // Check the non-_dnslink domain
24
+ dnslinkRecord = await resolve(domain.replace('_dnslink.', ''));
25
+ }
26
+ else {
27
+ // Check the _dnslink subdomain
28
+ const _dnslinkDomain = `_dnslink.${domain}`;
29
+ // If this throws then we propagate the error
30
+ dnslinkRecord = await resolve(_dnslinkDomain);
31
+ }
32
+ }
33
+ const result = dnslinkRecord.replace('dnslink=', '');
34
+ const domainOrCID = result.split('/')[2];
35
+ const isIPFSCID = isIPFS.cid(domainOrCID);
36
+ if (isIPFSCID || depth === 0) {
37
+ return result;
38
+ }
39
+ return await recursiveResolveDnslink(domainOrCID, depth - 1, options);
40
+ }
41
+ async function resolve(domain, options = {}) {
42
+ const DNSLINK_REGEX = /^dnslink=.+$/;
43
+ const records = await promisify(dns.resolveTxt)(domain);
44
+ const dnslinkRecords = records.reduce((rs, r) => rs.concat(r), [])
45
+ .filter(record => DNSLINK_REGEX.test(record));
46
+ const dnslinkRecord = dnslinkRecords[0];
47
+ // we now have dns text entries as an array of strings
48
+ // only records passing the DNSLINK_REGEX text are included
49
+ if (dnslinkRecord == null) {
50
+ throw new Error(`No dnslink records found for domain: ${domain}`);
51
+ }
52
+ return dnslinkRecord;
53
+ }
54
+ //# sourceMappingURL=resolve-dns-link.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolve-dns-link.js","sourceRoot":"","sources":["../../../src/utils/resolve-dns-link.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,KAAK,CAAA;AACrB,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAA;AAEhC,OAAO,KAAK,MAAM,MAAM,SAAS,CAAA;AAEjC,MAAM,mBAAmB,GAAG,EAAE,CAAA;AAE9B,MAAM,CAAC,KAAK,UAAU,cAAc,CAAE,MAAc,EAAE,UAAwB,EAAE;IAC9E,OAAO,MAAM,uBAAuB,CAAC,MAAM,EAAE,mBAAmB,EAAE,OAAO,CAAC,CAAA;AAC5E,CAAC;AAED,KAAK,UAAU,uBAAuB,CAAE,MAAc,EAAE,KAAa,EAAE,UAAwB,EAAE;IAC/F,IAAI,KAAK,KAAK,CAAC,EAAE;QACf,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;KAC5C;IAED,IAAI,aAAa,CAAA;IAEjB,IAAI;QACF,aAAa,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;KACtC;IAAC,OAAO,GAAQ,EAAE;QACjB,wFAAwF;QACxF,IAAI,GAAG,CAAC,IAAI,KAAK,WAAW,IAAI,GAAG,CAAC,IAAI,KAAK,uBAAuB,IAAI,GAAG,CAAC,IAAI,KAAK,SAAS,EAAE;YAC9F,MAAM,GAAG,CAAA;SACV;QAED,IAAI,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE;YAClC,oDAAoD;YACpD,gCAAgC;YAChC,aAAa,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAA;SAC/D;aAAM;YACL,+BAA+B;YAC/B,MAAM,cAAc,GAAG,YAAY,MAAM,EAAE,CAAA;YAC3C,6CAA6C;YAC7C,aAAa,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,CAAA;SAC9C;KACF;IAED,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;IACpD,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;IACxC,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IAEzC,IAAI,SAAS,IAAI,KAAK,KAAK,CAAC,EAAE;QAC5B,OAAO,MAAM,CAAA;KACd;IAED,OAAO,MAAM,uBAAuB,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC,EAAE,OAAO,CAAC,CAAA;AACvE,CAAC;AAED,KAAK,UAAU,OAAO,CAAE,MAAc,EAAE,UAAwB,EAAE;IAChE,MAAM,aAAa,GAAG,cAAc,CAAA;IACpC,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAA;IACvD,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;SAC/D,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;IAE/C,MAAM,aAAa,GAAG,cAAc,CAAC,CAAC,CAAC,CAAA;IAEvC,sDAAsD;IACtD,2DAA2D;IAC3D,IAAI,aAAa,IAAI,IAAI,EAAE;QACzB,MAAM,IAAI,KAAK,CAAC,wCAAwC,MAAM,EAAE,CAAC,CAAA;KAClE;IAED,OAAO,aAAa,CAAA;AACtB,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Time Aware Least Recent Used Cache
3
+ *
4
+ * @see https://arxiv.org/pdf/1801.00390
5
+ */
6
+ export declare class TLRU<T> {
7
+ private readonly lru;
8
+ constructor(maxSize: number);
9
+ get(key: string): T | undefined;
10
+ set(key: string, value: T, ttl: number): void;
11
+ has(key: string): boolean;
12
+ remove(key: string): void;
13
+ clear(): void;
14
+ }
15
+ //# sourceMappingURL=tlru.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tlru.d.ts","sourceRoot":"","sources":["../../../src/utils/tlru.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,qBAAa,IAAI,CAAC,CAAC;IACjB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA4B;gBAEnC,OAAO,EAAE,MAAM;IAI5B,GAAG,CAAE,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAgBhC,GAAG,CAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAI9C,GAAG,CAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAU1B,MAAM,CAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAI1B,KAAK,IAAK,IAAI;CAGf"}
@@ -0,0 +1,39 @@
1
+ import hashlru from 'hashlru';
2
+ /**
3
+ * Time Aware Least Recent Used Cache
4
+ *
5
+ * @see https://arxiv.org/pdf/1801.00390
6
+ */
7
+ export class TLRU {
8
+ constructor(maxSize) {
9
+ this.lru = hashlru(maxSize);
10
+ }
11
+ get(key) {
12
+ const value = this.lru.get(key);
13
+ if (value != null) {
14
+ if (value.expire != null && value.expire < Date.now()) {
15
+ this.lru.remove(key);
16
+ return undefined;
17
+ }
18
+ return value.value;
19
+ }
20
+ return undefined;
21
+ }
22
+ set(key, value, ttl) {
23
+ this.lru.set(key, { value, expire: Date.now() + ttl });
24
+ }
25
+ has(key) {
26
+ const value = this.get(key);
27
+ if (value != null) {
28
+ return true;
29
+ }
30
+ return false;
31
+ }
32
+ remove(key) {
33
+ this.lru.remove(key);
34
+ }
35
+ clear() {
36
+ this.lru.clear();
37
+ }
38
+ }
39
+ //# sourceMappingURL=tlru.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tlru.js","sourceRoot":"","sources":["../../../src/utils/tlru.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAA;AAE7B;;;;GAIG;AACH,MAAM,OAAO,IAAI;IAGf,YAAa,OAAe;QAC1B,IAAI,CAAC,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IAC7B,CAAC;IAED,GAAG,CAAE,GAAW;QACd,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAE/B,IAAI,KAAK,IAAI,IAAI,EAAE;YACjB,IAAI,KAAK,CAAC,MAAM,IAAI,IAAI,IAAI,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,EAAE;gBACrD,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;gBAEpB,OAAO,SAAS,CAAA;aACjB;YAED,OAAO,KAAK,CAAC,KAAK,CAAA;SACnB;QAED,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,GAAG,CAAE,GAAW,EAAE,KAAQ,EAAE,GAAW;QACrC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,EAAE,CAAC,CAAA;IACxD,CAAC;IAED,GAAG,CAAE,GAAW;QACd,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAE3B,IAAI,KAAK,IAAI,IAAI,EAAE;YACjB,OAAO,IAAI,CAAA;SACZ;QAED,OAAO,KAAK,CAAA;IACd,CAAC;IAED,MAAM,CAAE,GAAW;QACjB,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACtB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAA;IAClB,CAAC;CACF"}
package/package.json ADDED
@@ -0,0 +1,191 @@
1
+ {
2
+ "name": "@helia/ipns",
3
+ "version": "0.0.0",
4
+ "description": "An implementation of IPNS for Helia",
5
+ "license": "Apache-2.0 OR MIT",
6
+ "homepage": "https://github.com/ipfs/helia-ipns/tree/master/packages/ipns#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/ipfs/helia-ipns.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/ipfs/helia-ipns/issues"
13
+ },
14
+ "keywords": [
15
+ "IPFS"
16
+ ],
17
+ "engines": {
18
+ "node": ">=16.0.0",
19
+ "npm": ">=7.0.0"
20
+ },
21
+ "type": "module",
22
+ "types": "./dist/src/index.d.ts",
23
+ "typesVersions": {
24
+ "*": {
25
+ "*": [
26
+ "*",
27
+ "dist/*",
28
+ "dist/src/*",
29
+ "dist/src/*/index"
30
+ ],
31
+ "src/*": [
32
+ "*",
33
+ "dist/*",
34
+ "dist/src/*",
35
+ "dist/src/*/index"
36
+ ]
37
+ }
38
+ },
39
+ "files": [
40
+ "src",
41
+ "dist",
42
+ "!dist/test",
43
+ "!**/*.tsbuildinfo"
44
+ ],
45
+ "exports": {
46
+ ".": {
47
+ "types": "./dist/src/index.d.ts",
48
+ "import": "./dist/src/index.js"
49
+ },
50
+ "./routing": {
51
+ "types": "./dist/src/routing/index.d.ts",
52
+ "import": "./dist/src/routing/index.js"
53
+ }
54
+ },
55
+ "eslintConfig": {
56
+ "extends": "ipfs",
57
+ "parserOptions": {
58
+ "sourceType": "module"
59
+ }
60
+ },
61
+ "release": {
62
+ "branches": [
63
+ "main"
64
+ ],
65
+ "plugins": [
66
+ [
67
+ "@semantic-release/commit-analyzer",
68
+ {
69
+ "preset": "conventionalcommits",
70
+ "releaseRules": [
71
+ {
72
+ "breaking": true,
73
+ "release": "major"
74
+ },
75
+ {
76
+ "revert": true,
77
+ "release": "patch"
78
+ },
79
+ {
80
+ "type": "feat",
81
+ "release": "minor"
82
+ },
83
+ {
84
+ "type": "fix",
85
+ "release": "patch"
86
+ },
87
+ {
88
+ "type": "docs",
89
+ "release": "patch"
90
+ },
91
+ {
92
+ "type": "test",
93
+ "release": "patch"
94
+ },
95
+ {
96
+ "type": "deps",
97
+ "release": "patch"
98
+ },
99
+ {
100
+ "scope": "no-release",
101
+ "release": false
102
+ }
103
+ ]
104
+ }
105
+ ],
106
+ [
107
+ "@semantic-release/release-notes-generator",
108
+ {
109
+ "preset": "conventionalcommits",
110
+ "presetConfig": {
111
+ "types": [
112
+ {
113
+ "type": "feat",
114
+ "section": "Features"
115
+ },
116
+ {
117
+ "type": "fix",
118
+ "section": "Bug Fixes"
119
+ },
120
+ {
121
+ "type": "chore",
122
+ "section": "Trivial Changes"
123
+ },
124
+ {
125
+ "type": "docs",
126
+ "section": "Documentation"
127
+ },
128
+ {
129
+ "type": "deps",
130
+ "section": "Dependencies"
131
+ },
132
+ {
133
+ "type": "test",
134
+ "section": "Tests"
135
+ }
136
+ ]
137
+ }
138
+ }
139
+ ],
140
+ "@semantic-release/changelog",
141
+ "@semantic-release/npm",
142
+ "@semantic-release/github",
143
+ "@semantic-release/git"
144
+ ]
145
+ },
146
+ "scripts": {
147
+ "clean": "aegir clean",
148
+ "lint": "aegir lint",
149
+ "dep-check": "aegir dep-check",
150
+ "build": "aegir build",
151
+ "docs": "aegir docs",
152
+ "test": "aegir test",
153
+ "test:chrome": "aegir test -t browser --cov",
154
+ "test:chrome-webworker": "aegir test -t webworker",
155
+ "test:firefox": "aegir test -t browser -- --browser firefox",
156
+ "test:firefox-webworker": "aegir test -t webworker -- --browser firefox",
157
+ "test:node": "aegir test -t node --cov",
158
+ "test:electron-main": "aegir test -t electron-main",
159
+ "release": "aegir release"
160
+ },
161
+ "dependencies": {
162
+ "@helia/interface": "next",
163
+ "@libp2p/interface-dht": "^2.0.1",
164
+ "@libp2p/interface-peer-id": "^2.0.1",
165
+ "@libp2p/interface-pubsub": "^3.0.6",
166
+ "@libp2p/interfaces": "^3.3.1",
167
+ "@libp2p/logger": "^2.0.5",
168
+ "@libp2p/peer-id": "^2.0.1",
169
+ "@libp2p/record": "^3.0.0",
170
+ "hashlru": "^2.3.0",
171
+ "interface-datastore": "^7.0.4",
172
+ "ipns": "^5.0.1",
173
+ "is-ipfs": "^8.0.1",
174
+ "multiformats": "^11.0.1",
175
+ "p-queue": "^7.3.0",
176
+ "progress-events": "^1.0.0",
177
+ "uint8arrays": "^4.0.3"
178
+ },
179
+ "devDependencies": {
180
+ "@libp2p/peer-id-factory": "^2.0.1",
181
+ "aegir": "^38.1.0",
182
+ "datastore-core": "^8.0.4",
183
+ "sinon": "^15.0.1"
184
+ },
185
+ "browser": {
186
+ "./dist/src/utils/resolve-dns-link.js": "./dist/src/utils/resolve-dns-link.browser.js"
187
+ },
188
+ "typedoc": {
189
+ "entryPoint": "./src/index.ts"
190
+ }
191
+ }