core-3nweb-client-lib 0.52.3 → 0.53.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.
@@ -54,6 +54,13 @@ declare namespace web3n {
54
54
  connectType: 'dns';
55
55
  }
56
56
 
57
+ interface NetworkConnectException extends ConnectException {
58
+ connectType: 'network';
59
+ networkType: 'regular' | 'onion' | 'i2p';
60
+ location: string;
61
+ noImplementationSet?: true;
62
+ }
63
+
57
64
  interface HTTPException extends HTTPErrorDetails {
58
65
  type: 'http-request';
59
66
  status: number;
@@ -1,4 +1,4 @@
1
- import { ServiceLocator } from '../../lib-client/service-locator';
1
+ import { ServiceLocator } from '../../lib-client/networks';
2
2
  import { ParamOnServer } from '../../lib-client/asmail/service-config';
3
3
  import { GetSigner } from '../id-manager';
4
4
  import { NetClient } from '../../lib-client/request-utils';
@@ -5,7 +5,7 @@ import { Encryptor } from '../../../lib-common/async-cryptor-wrap';
5
5
  import { ASMailKeyPair } from '../../keyring/common';
6
6
  import { NetClient } from '../../../lib-client/request-utils';
7
7
  import { LogWarning, LogError } from '../../../lib-client/logging/log-to-file';
8
- import { ServiceLocator } from '../../../lib-client/service-locator';
8
+ import { ServiceLocator } from '../../../lib-client/networks';
9
9
  import { AsyncRNG } from '../../../lib-common/rng-def';
10
10
  type OutgoingMessage = web3n.asmail.OutgoingMessage;
11
11
  type DeliveryProgress = web3n.asmail.DeliveryProgress;
@@ -1,5 +1,5 @@
1
1
  import { StorageGetter } from '../../../lib-client/xsp-fs/common';
2
- import { ServiceLocator } from '../../../lib-client/service-locator';
2
+ import { ServiceLocator } from '../../../lib-client/networks';
3
3
  import { OpenedMsg } from '../msg/opener';
4
4
  import { MsgKeyInfo } from '../../keyring';
5
5
  import * as delivApi from '../../../lib-common/service-api/asmail/delivery';
@@ -5,7 +5,7 @@ import { StorageGetter } from '../../lib-client/xsp-fs/common';
5
5
  import { GetSigner } from '../id-manager';
6
6
  import { AsyncSBoxCryptor } from 'xsp-files';
7
7
  import { Logger } from '../../lib-client/logging/log-to-file';
8
- import { ServiceLocatorMaker } from '../../lib-client/service-locator';
8
+ import { ServiceLocatorMaker } from '../../lib-client/networks';
9
9
  import { MakeNet } from '..';
10
10
  import { AsyncRNG } from '../../lib-common/rng-def';
11
11
  type WritableFS = web3n.files.WritableFS;
@@ -1,5 +1,5 @@
1
- import { ServiceLocator } from '../../lib-client/service-locator';
2
1
  import { NetClient } from '../../lib-client/request-utils';
2
+ import { ServiceLocator } from '../../lib-client/networks';
3
3
  type JsonKey = web3n.keys.JsonKey;
4
4
  type SignedLoad = web3n.keys.SignedLoad;
5
5
  type PKeyCertChain = web3n.keys.PKeyCertChain;
@@ -1,7 +1,7 @@
1
1
  import { GenerateKey } from '../startup/sign-in';
2
2
  import { LogError, LogWarning } from '../../lib-client/logging/log-to-file';
3
3
  import { NetClient } from '../../lib-client/request-utils';
4
- import { ServiceLocator } from '../../lib-client/service-locator';
4
+ import { ServiceLocator } from '../../lib-client/networks';
5
5
  import { MailerIdSigner } from '../../lib-common/mailerid-sigs/user';
6
6
  import { AsyncRNG } from '../../lib-common/rng-def';
7
7
  type JsonKey = web3n.keys.JsonKey;
@@ -1,6 +1,6 @@
1
1
  import { FactoryOfFSs } from './storage';
2
2
  import { NetClient } from '../lib-client/request-utils';
3
- import { ServiceLocatorMaker } from '../lib-client/service-locator';
3
+ import { ServiceLocatorMaker } from '../lib-client/networks';
4
4
  import type { makeCryptor } from 'ecma-nacl-cryptors';
5
5
  import { AsyncRNG } from '../lib-common/rng-def';
6
6
  export type { makeCryptor } from 'ecma-nacl-cryptors';
@@ -1,7 +1,7 @@
1
1
  import { GetSigner } from '../id-manager';
2
2
  import { GenerateKey } from '../startup/sign-in';
3
3
  import { StorageGetter } from '../../lib-client/xsp-fs/common';
4
- import { ServiceLocator } from '../../lib-client/service-locator';
4
+ import { ServiceLocator } from '../../lib-client/networks';
5
5
  import { ScryptGenParams } from '../../lib-client/key-derivation';
6
6
  import { AsyncSBoxCryptor } from 'xsp-files';
7
7
  import { StoragePathForUser } from '../app-files';
@@ -3,8 +3,8 @@
3
3
  */
4
4
  import { NetClient } from '../request-utils';
5
5
  import * as api from '../../lib-common/service-api/asmail/delivery';
6
- import { ServiceLocator } from '../service-locator';
7
6
  import { MailerIdSigner } from '../../lib-common/mailerid-sigs/user';
7
+ import { ServiceLocator } from '../networks';
8
8
  export type FirstSaveReqOpts = api.PutObjFirstQueryOpts;
9
9
  export type FollowingSaveReqOpts = api.PutObjSecondQueryOpts;
10
10
  export type ServiceUrlGetter = (address: string) => Promise<string>;
@@ -1,3 +1,3 @@
1
1
  import { RequestFn } from "./request-utils";
2
- import { DnsResolver } from "./service-locator";
2
+ import { DnsResolver } from "./networks";
3
3
  export declare function dohAt(request: RequestFn<unknown>, dohServerUrl: string): DnsResolver;
@@ -17,7 +17,7 @@
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.dohAt = dohAt;
20
- const service_locator_1 = require("./service-locator");
20
+ const networks_1 = require("./networks");
21
21
  function dohAt(request, dohServerUrl) {
22
22
  async function resolveTxt(domain) {
23
23
  const answer = await sendDohQuestion(request, dohServerUrl, domain, 'TXT');
@@ -41,27 +41,27 @@ async function sendDohQuestion(request, serverUrl, domain, type) {
41
41
  const reply = await request(opts)
42
42
  .catch((exc) => {
43
43
  if (exc.type === 'connect') {
44
- throw { code: service_locator_1.CONNREFUSED, hostname: domain, cause: exc };
44
+ throw { code: networks_1.CONNREFUSED, hostname: domain, cause: exc };
45
45
  }
46
46
  else {
47
- throw { code: service_locator_1.SERVFAIL, hostname: domain, cause: exc };
47
+ throw { code: networks_1.SERVFAIL, hostname: domain, cause: exc };
48
48
  }
49
49
  });
50
50
  const { status, data } = reply;
51
51
  if (status !== 200) {
52
52
  throw {
53
- code: service_locator_1.SERVFAIL, hostname: domain,
53
+ code: networks_1.SERVFAIL, hostname: domain,
54
54
  message: `status ${reply.status} from DoH server`
55
55
  };
56
56
  }
57
57
  if (data.Status !== 0) {
58
- throw { code: service_locator_1.NOTFOUND, hostname: domain };
58
+ throw { code: networks_1.NOTFOUND, hostname: domain };
59
59
  }
60
60
  if (data.Answer) {
61
61
  return data.Answer;
62
62
  }
63
63
  else {
64
- throw { code: service_locator_1.NODATA, hostname: domain };
64
+ throw { code: networks_1.NODATA, hostname: domain };
65
65
  }
66
66
  }
67
67
  Object.freeze(exports);
@@ -132,7 +132,7 @@ ${(0, error_1.stringifyErr)(err)}`;
132
132
  return [];
133
133
  }))
134
134
  .filter(fName => fName.endsWith(`.${LOG_FILE_EXT}`));
135
- const tsCutOff = Date.now() - 4 * 24 * 12 * 60 * 60 * 1000;
135
+ const tsCutOff = Date.now() - 4 * 24 * 60 * 60 * 1000;
136
136
  for (const fName of logs) {
137
137
  try {
138
138
  const fPath = (0, path_1.join)(logsDir, fName);
@@ -0,0 +1,29 @@
1
+ import { NetClient, RequestFn, OpenServiceEventsSource } from './request-utils';
2
+ import { LogError } from './logging/log-to-file';
3
+ export type ServiceTypeDNSLabel = 'mailerid' | 'asmail' | '3nstorage';
4
+ export type ServiceLocatorMaker = (serviceLabel: ServiceTypeDNSLabel | 'report', logError: LogError) => ServiceLocator;
5
+ export type ServiceLocator = (address: string) => Promise<string>;
6
+ export interface DnsResolver {
7
+ resolveTxt: (hostname: string) => Promise<string[][]>;
8
+ }
9
+ export declare const NODATA = "ENODATA";
10
+ export declare const SERVFAIL = "ESERVFAIL";
11
+ export declare const NOTFOUND = "ENOTFOUND";
12
+ export declare const CONNREFUSED = "ECONNREFUSED";
13
+ export declare const TIMEOUT = "ETIMEOUT";
14
+ export interface NetworkFns {
15
+ requests: RequestFn<unknown>;
16
+ openServiceEventsSource: OpenServiceEventsSource;
17
+ }
18
+ export interface RegularNetworkFns extends NetworkFns {
19
+ naming: DnsResolver[];
20
+ }
21
+ export declare function wrapNetworkFns(impls: {
22
+ regular: RegularNetworkFns;
23
+ onion?: NetworkFns;
24
+ i2p?: NetworkFns;
25
+ resetConnectivity?: () => void;
26
+ }): {
27
+ makeLocator: ServiceLocatorMaker;
28
+ makeNet: () => NetClient;
29
+ };
@@ -0,0 +1,320 @@
1
+ "use strict";
2
+ /*
3
+ Copyright (C) 2015 - 2017, 2020 - 2021, 2024 - 2026 3NSoft Inc.
4
+
5
+ This program is free software: you can redistribute it and/or modify it under
6
+ the terms of the GNU General Public License as published by the Free Software
7
+ Foundation, either version 3 of the License, or (at your option) any later
8
+ version.
9
+
10
+ This program is distributed in the hope that it will be useful, but
11
+ WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
+ See the GNU General Public License for more details.
14
+
15
+ You should have received a copy of the GNU General Public License along with
16
+ this program. If not, see <http://www.gnu.org/licenses/>.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.TIMEOUT = exports.CONNREFUSED = exports.NOTFOUND = exports.SERVFAIL = exports.NODATA = void 0;
20
+ exports.wrapNetworkFns = wrapNetworkFns;
21
+ const request_utils_1 = require("./request-utils");
22
+ const runtime_1 = require("../lib-common/exceptions/runtime");
23
+ /**
24
+ * @param address
25
+ * @return domain string, extracted from a given address
26
+ */
27
+ function domainOfAddress(address) {
28
+ address = address.trim();
29
+ const indOfAt = address.lastIndexOf('@');
30
+ if (indOfAt < 0) {
31
+ return address;
32
+ }
33
+ else {
34
+ return address.substring(indOfAt + 1);
35
+ }
36
+ }
37
+ function checkAndPrepareURL(value) {
38
+ // XXX insert some value sanity check
39
+ return 'https://' + value;
40
+ }
41
+ function domainNotFoundExc(address, cause) {
42
+ return (0, runtime_1.makeRuntimeException)('service-locating', { address, cause }, { domainNotFound: true });
43
+ }
44
+ function noServiceRecordExc(address) {
45
+ return (0, runtime_1.makeRuntimeException)('service-locating', { address }, { noServiceRecord: true });
46
+ }
47
+ function noConnectionExc(cause) {
48
+ return (0, runtime_1.makeRuntimeException)('connect', {
49
+ connectType: 'dns',
50
+ message: `The most likely cause of this error is device not connected. Next likely cause is DNS not setup, or not connecting properly. Like the saying goes: "It's not DNS. There is no way it's DNS. It was DNS."`,
51
+ cause
52
+ }, {});
53
+ }
54
+ function noConnectionToWellKnownExc(message, cause) {
55
+ return (0, runtime_1.makeRuntimeException)('connect', {
56
+ connectType: 'dns',
57
+ message,
58
+ cause
59
+ }, {});
60
+ }
61
+ /**
62
+ * This implementation extracts exactly one string value for a given service.
63
+ * All other values are ignored, without raising error about misconfiguration.
64
+ * In time we may have several records for the same service type, yet, for now
65
+ * only one TXT per service per domain is considered valid.
66
+ * @param txtRecords are TXT records from dns.
67
+ * @param serviceLabel is a label of service, for which we want to get string
68
+ * value from TXT record.
69
+ * @return string value for a given service among given dns TXT records, or
70
+ * undefined, when service record is not found.
71
+ */
72
+ function extractPair(txtRecords, serviceLabel) {
73
+ for (const txtRecord of txtRecords) {
74
+ let joinedTXTstanzas = txtRecord.join('');
75
+ let record = getRecordAtStartOf(joinedTXTstanzas);
76
+ while (record) {
77
+ if (record.service === serviceLabel) {
78
+ const value = record.value.trim();
79
+ if (value.length > 0) {
80
+ return value;
81
+ }
82
+ }
83
+ if (record.txtTail) {
84
+ record = getRecordAtStartOf(record.txtTail);
85
+ }
86
+ else {
87
+ break;
88
+ }
89
+ }
90
+ }
91
+ return;
92
+ }
93
+ const recordsStarts = {
94
+ "3nstorage": '3nstorage=',
95
+ asmail: 'asmail=',
96
+ mailerid: 'mailerid=',
97
+ report: 'report='
98
+ };
99
+ function getRecordAtStartOf(txt) {
100
+ let service = undefined;
101
+ for (const [label, startSeq] of Object.entries(recordsStarts)) {
102
+ if (txt.startsWith(startSeq)) {
103
+ service = label;
104
+ txt = txt.substring(startSeq.length);
105
+ break;
106
+ }
107
+ }
108
+ if (!service) {
109
+ return;
110
+ }
111
+ for (const delimiter of Object.values(recordsStarts)) {
112
+ const endPos = txt.indexOf(delimiter);
113
+ if (endPos >= 0) {
114
+ return {
115
+ service,
116
+ value: txt.substring(0, endPos),
117
+ txtTail: txt.substring(endPos)
118
+ };
119
+ }
120
+ }
121
+ return {
122
+ service,
123
+ value: txt
124
+ };
125
+ }
126
+ exports.NODATA = "ENODATA";
127
+ // export const FORMERR = "EFORMERR";
128
+ exports.SERVFAIL = "ESERVFAIL";
129
+ exports.NOTFOUND = "ENOTFOUND";
130
+ // export const NOTIMP = "ENOTIMP";
131
+ // export const REFUSED = "EREFUSED";
132
+ // export const BADQUERY = "EBADQUERY";
133
+ // export const BADNAME = "EBADNAME";
134
+ // export const BADFAMILY = "EBADFAMILY";
135
+ // export const BADRESP = "EBADRESP";
136
+ exports.CONNREFUSED = "ECONNREFUSED";
137
+ exports.TIMEOUT = "ETIMEOUT";
138
+ // export const EOF = "EOF";
139
+ // export const FILE = "EFILE";
140
+ // export const NOMEM = "ENOMEM";
141
+ // export const DESTRUCTION = "EDESTRUCTION";
142
+ // export const BADSTR = "EBADSTR";
143
+ // export const BADFLAGS = "EBADFLAGS";
144
+ // export const NONAME = "ENONAME";
145
+ // export const BADHINTS = "EBADHINTS";
146
+ // export const NOTINITIALIZED = "ENOTINITIALIZED";
147
+ // export const LOADIPHLPAPI = "ELOADIPHLPAPI";
148
+ // export const ADDRGETNETWORKPARAMS = "EADDRGETNETWORKPARAMS";
149
+ // export const CANCELLED = "ECANCELLED";
150
+ // XXX
151
+ // - service exist in network.
152
+ // - we go from name to address to service location in a network, and a request function ?
153
+ // Or, should we assume that request function will engage with proper network, based on location,
154
+ // implying that location string should always map to one network: regular | tor | i2p.
155
+ // Implicit rule allows us not to force making request fn here, as well as naming system in one net,
156
+ // pointing to another. bank.xyz can be served by server <key>.onion, and vice versa.
157
+ //
158
+ // - this function, ... doesn't have to change, as DnsResolver is given. May be we should make a more
159
+ // general wrap, that gonna use this for DNS in regular net?
160
+ // - and, we may probably want to make function here as a reusable wrapper around different network request fns.
161
+ // Regular net request, may internally switch into proxy/hidding modes. Other networks provide anonymity,
162
+ // hence, these additional options should stay "inside" regular network request fn.
163
+ //
164
+ // - should we have a reusable wrapper creator here, creating coherent pair of naming and request functions.
165
+ // Cause, if embedding doesn't have explicit implementation for Tor, both initial naming and net-maker functions
166
+ // should be coherent.
167
+ const onionTLD = '.onion';
168
+ const i2pTLD = '.i2p';
169
+ function makeMissingImplementationException(networkType, location) {
170
+ return (0, runtime_1.makeRuntimeException)('connect', { networkType, location }, { noImplementationSet: true });
171
+ }
172
+ function wrapNetworkFns(impls) {
173
+ const { regular, i2p, onion, resetConnectivity } = impls;
174
+ const locateInDNS = makeServiceLocatorInDNS(...regular.naming);
175
+ const locationRequesterInOnion = (onion ?
176
+ (0, request_utils_1.makeNetClient)(onion.requests, undefined).doBodylessRequest : undefined);
177
+ const locationRequesterInI2P = (i2p ?
178
+ (0, request_utils_1.makeNetClient)(i2p.requests, undefined).doBodylessRequest : undefined);
179
+ const makeLocator = function (serviceLabel, logError) {
180
+ return async (address) => {
181
+ if (address.endsWith(onionTLD)) {
182
+ if (locationRequesterInOnion) {
183
+ return locateInWellKnown(locationRequesterInOnion, address, serviceLabel);
184
+ }
185
+ else {
186
+ throw makeMissingImplementationException('onion', address);
187
+ }
188
+ }
189
+ else if (address.endsWith(i2pTLD)) {
190
+ if (locationRequesterInI2P) {
191
+ return locateInWellKnown(locationRequesterInI2P, address, serviceLabel);
192
+ }
193
+ else {
194
+ throw makeMissingImplementationException('i2p', address);
195
+ }
196
+ }
197
+ else {
198
+ return locateInDNS(address, serviceLabel, logError);
199
+ }
200
+ };
201
+ };
202
+ function fnsForUrl(url) {
203
+ const u = new URL(url);
204
+ if (u.hostname.endsWith(onionTLD)) {
205
+ if (onion) {
206
+ return onion;
207
+ }
208
+ else {
209
+ throw makeMissingImplementationException('onion', url);
210
+ }
211
+ }
212
+ else if (u.hostname.endsWith(i2pTLD)) {
213
+ if (i2p) {
214
+ return i2p;
215
+ }
216
+ else {
217
+ throw makeMissingImplementationException('i2p', url);
218
+ }
219
+ }
220
+ else {
221
+ return regular;
222
+ }
223
+ }
224
+ const requestFn = async function (opts, reqContentType, reqBody) {
225
+ const { requests } = fnsForUrl(opts.url);
226
+ return requests(opts, reqContentType, reqBody);
227
+ };
228
+ const openEventSourceFn = async function (req) {
229
+ const { openServiceEventsSource } = fnsForUrl(req.url);
230
+ return openServiceEventsSource(req);
231
+ };
232
+ function makeNet() {
233
+ return (0, request_utils_1.makeNetClient)(requestFn, openEventSourceFn, resetConnectivity);
234
+ }
235
+ return { makeLocator, makeNet };
236
+ }
237
+ function makeServiceLocatorInDNS(...resolvers) {
238
+ if (resolvers.length === 0) {
239
+ throw Error(`no DNS resolvers given`);
240
+ }
241
+ async function locateInDNS(address, serviceLabel, logError) {
242
+ const domain = domainOfAddress(address);
243
+ let exc = undefined;
244
+ let connectionExc = undefined;
245
+ for (let i = 0; i < resolvers.length; i += 1) {
246
+ const resolver = resolvers[i];
247
+ let txtRecords;
248
+ try {
249
+ txtRecords = await resolver.resolveTxt(domain);
250
+ }
251
+ catch (err) {
252
+ await logError(err, `Resolver ${i + 1} fails to get TXT records of ${domain}`);
253
+ const { code, hostname, message } = err;
254
+ if (code === exports.NODATA) {
255
+ exc = noServiceRecordExc(address);
256
+ }
257
+ else if ((code === exports.SERVFAIL)
258
+ || (code === exports.CONNREFUSED)
259
+ || (code === exports.TIMEOUT)) {
260
+ connectionExc !== null && connectionExc !== void 0 ? connectionExc : (connectionExc = noConnectionExc({ code, hostname, message }));
261
+ }
262
+ else if ((code === exports.NOTFOUND) || hostname) {
263
+ exc !== null && exc !== void 0 ? exc : (exc = domainNotFoundExc(address, { code, hostname, message }));
264
+ }
265
+ else {
266
+ exc !== null && exc !== void 0 ? exc : (exc = err);
267
+ }
268
+ continue;
269
+ }
270
+ const recValue = extractPair(txtRecords, serviceLabel);
271
+ if (recValue) {
272
+ if (serviceLabel === 'report') {
273
+ return recValue;
274
+ }
275
+ else {
276
+ return checkAndPrepareURL(recValue);
277
+ }
278
+ }
279
+ else {
280
+ exc !== null && exc !== void 0 ? exc : (exc = noServiceRecordExc(address));
281
+ }
282
+ }
283
+ throw exc !== null && exc !== void 0 ? exc : connectionExc;
284
+ }
285
+ return locateInDNS;
286
+ }
287
+ const wellKnownPath = '/.well-known/3nweb.json';
288
+ async function locateInWellKnown(request, address, serviceLabel) {
289
+ const host = domainOfAddress(address);
290
+ try {
291
+ const reply = await request({
292
+ method: 'GET',
293
+ url: `https://${host}${wellKnownPath}`
294
+ });
295
+ if (reply.status === 200) {
296
+ const recValue = reply.data[serviceLabel];
297
+ if (recValue) {
298
+ if (serviceLabel === 'report') {
299
+ return recValue;
300
+ }
301
+ else {
302
+ return checkAndPrepareURL(recValue);
303
+ }
304
+ }
305
+ else {
306
+ throw noServiceRecordExc(address);
307
+ }
308
+ }
309
+ else if (reply.status === 404) {
310
+ throw noServiceRecordExc(address);
311
+ }
312
+ else {
313
+ throw noConnectionToWellKnownExc(`Fail to read ${wellKnownPath} for ${address}`, { httpStatus: reply.status });
314
+ }
315
+ }
316
+ catch (exc) {
317
+ throw noConnectionToWellKnownExc(`Fail to read ${wellKnownPath} for ${address}`, exc);
318
+ }
319
+ }
320
+ Object.freeze(exports);
@@ -1,5 +1,5 @@
1
+ import { ServiceLocatorMaker } from "./networks";
1
2
  import { NetClient } from "./request-utils";
2
- import { ServiceLocatorMaker } from "./service-locator";
3
3
  export interface Check {
4
4
  service: 'signup' | 'asmail' | '3nstorage' | 'mailerid';
5
5
  }
@@ -1,7 +1,7 @@
1
1
  import { NetClient } from './request-utils';
2
2
  import { StorageRootRoute } from '../lib-common/service-api/3nstorage/root-route';
3
3
  import { ASMailRootRoute } from '../lib-common/service-api/asmail/root-route';
4
- import { LogError } from './logging/log-to-file';
4
+ import { ServiceLocator } from './networks';
5
5
  type SignedLoad = web3n.keys.SignedLoad;
6
6
  /**
7
7
  * This returns a promise, resolvable to ASMailRootRoute object.
@@ -26,18 +26,6 @@ export declare function mailerIdInfoAt(client: NetClient, url: string): Promise<
26
26
  * @param url
27
27
  */
28
28
  export declare function storageInfoAt(client: NetClient, url: string): Promise<StorageRootRoute>;
29
- export type ServiceTypeDNSLabel = 'mailerid' | 'asmail' | '3nstorage';
30
- export type ServiceLocatorMaker = (serviceLabel: ServiceTypeDNSLabel | 'report', logError: LogError) => ServiceLocator;
31
- export type ServiceLocator = (address: string) => Promise<string>;
32
- export interface DnsResolver {
33
- resolveTxt: (hostname: string) => Promise<string[][]>;
34
- }
35
- export declare const NODATA = "ENODATA";
36
- export declare const SERVFAIL = "ESERVFAIL";
37
- export declare const NOTFOUND = "ENOTFOUND";
38
- export declare const CONNREFUSED = "ECONNREFUSED";
39
- export declare const TIMEOUT = "ETIMEOUT";
40
- export declare function makeServiceLocator(...resolvers: DnsResolver[]): ServiceLocatorMaker;
41
29
  /**
42
30
  * @param resolver
43
31
  * @param address
@@ -16,14 +16,11 @@
16
16
  this program. If not, see <http://www.gnu.org/licenses/>.
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
- exports.TIMEOUT = exports.CONNREFUSED = exports.NOTFOUND = exports.SERVFAIL = exports.NODATA = void 0;
20
19
  exports.asmailInfoAt = asmailInfoAt;
21
20
  exports.mailerIdInfoAt = mailerIdInfoAt;
22
21
  exports.storageInfoAt = storageInfoAt;
23
- exports.makeServiceLocator = makeServiceLocator;
24
22
  exports.getMailerIdInfoFor = getMailerIdInfoFor;
25
23
  const jwkeys_1 = require("../lib-common/jwkeys");
26
- const runtime_1 = require("../lib-common/exceptions/runtime");
27
24
  const http_1 = require("../lib-common/exceptions/http");
28
25
  async function readJSONLocatedAt(client, url) {
29
26
  if ((new URL(url)).protocol !== 'https:') {
@@ -121,175 +118,6 @@ async function storageInfoAt(client, url) {
121
118
  }
122
119
  return transform;
123
120
  }
124
- /**
125
- * @param address
126
- * @return domain string, extracted from a given address
127
- */
128
- function domainOfAddress(address) {
129
- address = address.trim();
130
- const indOfAt = address.lastIndexOf('@');
131
- if (indOfAt < 0) {
132
- return address;
133
- }
134
- else {
135
- return address.substring(indOfAt + 1);
136
- }
137
- }
138
- function checkAndPrepareURL(value) {
139
- // XXX insert some value sanity check
140
- return 'https://' + value;
141
- }
142
- function domainNotFoundExc(address, cause) {
143
- return (0, runtime_1.makeRuntimeException)('service-locating', { address, cause }, { domainNotFound: true });
144
- }
145
- function noServiceRecordExc(address) {
146
- return (0, runtime_1.makeRuntimeException)('service-locating', { address }, { noServiceRecord: true });
147
- }
148
- function noConnectionExc(cause) {
149
- return (0, runtime_1.makeRuntimeException)('connect', {
150
- connectType: 'dns',
151
- message: `The most likely cause of this error is device not connected. Next likely cause is DNS not setup, or not connecting properly. Like the saying goes: "It's not DNS. There is no way it's DNS. It was DNS."`,
152
- cause
153
- }, {});
154
- }
155
- /**
156
- * This implementation extracts exactly one string value for a given service.
157
- * All other values are ignored, without raising error about misconfiguration.
158
- * In time we may have several records for the same service type, yet, for now
159
- * only one TXT per service per domain is considered valid.
160
- * @param txtRecords are TXT records from dns.
161
- * @param serviceLabel is a label of service, for which we want to get string
162
- * value from TXT record.
163
- * @return string value for a given service among given dns TXT records, or
164
- * undefined, when service record is not found.
165
- */
166
- function extractPair(txtRecords, serviceLabel) {
167
- for (const txtRecord of txtRecords) {
168
- let joinedTXTstanzas = txtRecord.join('');
169
- let record = getRecordAtStartOf(joinedTXTstanzas);
170
- while (record) {
171
- if (record.service === serviceLabel) {
172
- const value = record.value.trim();
173
- if (value.length > 0) {
174
- return value;
175
- }
176
- }
177
- if (record.txtTail) {
178
- record = getRecordAtStartOf(record.txtTail);
179
- }
180
- else {
181
- break;
182
- }
183
- }
184
- }
185
- return;
186
- }
187
- const recordsStarts = {
188
- "3nstorage": '3nstorage=',
189
- asmail: 'asmail=',
190
- mailerid: 'mailerid=',
191
- report: 'report='
192
- };
193
- function getRecordAtStartOf(txt) {
194
- let service = undefined;
195
- for (const [label, startSeq] of Object.entries(recordsStarts)) {
196
- if (txt.startsWith(startSeq)) {
197
- service = label;
198
- txt = txt.substring(startSeq.length);
199
- break;
200
- }
201
- }
202
- if (!service) {
203
- return;
204
- }
205
- for (const delimiter of Object.values(recordsStarts)) {
206
- const endPos = txt.indexOf(delimiter);
207
- if (endPos >= 0) {
208
- return {
209
- service,
210
- value: txt.substring(0, endPos),
211
- txtTail: txt.substring(endPos)
212
- };
213
- }
214
- }
215
- return {
216
- service,
217
- value: txt
218
- };
219
- }
220
- exports.NODATA = "ENODATA";
221
- // export const FORMERR = "EFORMERR";
222
- exports.SERVFAIL = "ESERVFAIL";
223
- exports.NOTFOUND = "ENOTFOUND";
224
- // export const NOTIMP = "ENOTIMP";
225
- // export const REFUSED = "EREFUSED";
226
- // export const BADQUERY = "EBADQUERY";
227
- // export const BADNAME = "EBADNAME";
228
- // export const BADFAMILY = "EBADFAMILY";
229
- // export const BADRESP = "EBADRESP";
230
- exports.CONNREFUSED = "ECONNREFUSED";
231
- exports.TIMEOUT = "ETIMEOUT";
232
- // export const EOF = "EOF";
233
- // export const FILE = "EFILE";
234
- // export const NOMEM = "ENOMEM";
235
- // export const DESTRUCTION = "EDESTRUCTION";
236
- // export const BADSTR = "EBADSTR";
237
- // export const BADFLAGS = "EBADFLAGS";
238
- // export const NONAME = "ENONAME";
239
- // export const BADHINTS = "EBADHINTS";
240
- // export const NOTINITIALIZED = "ENOTINITIALIZED";
241
- // export const LOADIPHLPAPI = "ELOADIPHLPAPI";
242
- // export const ADDRGETNETWORKPARAMS = "EADDRGETNETWORKPARAMS";
243
- // export const CANCELLED = "ECANCELLED";
244
- function makeServiceLocator(...resolvers) {
245
- if (resolvers.length === 0) {
246
- throw Error(`no DNS resolvers given`);
247
- }
248
- return (serviceLabel, logError) => async (address) => {
249
- const domain = domainOfAddress(address);
250
- let exc = undefined;
251
- let connectionExc = undefined;
252
- for (let i = 0; i < resolvers.length; i += 1) {
253
- const resolver = resolvers[i];
254
- let txtRecords;
255
- try {
256
- txtRecords = await resolver.resolveTxt(domain);
257
- }
258
- catch (err) {
259
- await logError(err, `Resolver ${i + 1} fails to get TXT records of ${domain}`);
260
- const { code, hostname, message } = err;
261
- if (code === exports.NODATA) {
262
- exc = noServiceRecordExc(address);
263
- }
264
- else if ((code === exports.SERVFAIL)
265
- || (code === exports.CONNREFUSED)
266
- || (code === exports.TIMEOUT)) {
267
- connectionExc !== null && connectionExc !== void 0 ? connectionExc : (connectionExc = noConnectionExc({ code, hostname, message }));
268
- }
269
- else if ((code === exports.NOTFOUND) || hostname) {
270
- exc !== null && exc !== void 0 ? exc : (exc = domainNotFoundExc(address, { code, hostname, message }));
271
- }
272
- else {
273
- exc !== null && exc !== void 0 ? exc : (exc = err);
274
- }
275
- continue;
276
- }
277
- const recValue = extractPair(txtRecords, serviceLabel);
278
- if (recValue) {
279
- if (serviceLabel === 'report') {
280
- return recValue;
281
- }
282
- else {
283
- return checkAndPrepareURL(recValue);
284
- }
285
- }
286
- else {
287
- exc !== null && exc !== void 0 ? exc : (exc = noServiceRecordExc(address));
288
- }
289
- }
290
- throw exc !== null && exc !== void 0 ? exc : connectionExc;
291
- };
292
- }
293
121
  /**
294
122
  * @param resolver
295
123
  * @param address
@@ -1,2 +1,3 @@
1
+ import * as https from 'https';
1
2
  import { RequestFn } from '../lib-client/request-utils';
2
- export declare function makeRequestFromNode(): RequestFn<unknown>;
3
+ export declare function makeRequestFromNode(getAgent?: () => https.Agent | undefined): RequestFn<unknown>;
@@ -19,10 +19,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.makeRequestFromNode = makeRequestFromNode;
20
20
  const https = require("https");
21
21
  const request_utils_1 = require("../lib-client/request-utils");
22
- function makeRequestFromNode() {
22
+ function makeRequestFromNode(getAgent) {
23
23
  const nodeRequest = (opts) => https.request(opts);
24
24
  return (opts, reqContentType, reqBody) => {
25
25
  const httpsOpts = (0, request_utils_1.formHttpsReqOpts)(opts, reqContentType, reqBody);
26
+ const agent = getAgent === null || getAgent === void 0 ? void 0 : getAgent();
27
+ if (agent) {
28
+ httpsOpts.agent = agent;
29
+ }
26
30
  return (0, request_utils_1.processRequest)(nodeRequest, httpsOpts, opts, reqBody);
27
31
  };
28
32
  }
@@ -5,7 +5,7 @@ export { IdManager } from './core/id-manager';
5
5
  export { Storages, FactoryOfFSs, reverseDomain } from './core/storage';
6
6
  export { SignIn, GenerateKey, CompleteInitWithoutCache } from './core/startup/sign-in';
7
7
  export { ASMail } from './core/asmail';
8
- export { makeServiceLocator, ServiceLocatorMaker } from "./lib-client/service-locator";
8
+ export { wrapNetworkFns, ServiceLocatorMaker } from "./lib-client/networks";
9
9
  export { makeNetClient, NetClient } from "./lib-client/request-utils";
10
10
  export { appDirs } from './core/app-files';
11
11
  export { sysFolders } from "./core/storage/system-folders";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /*
3
- Copyright (C) 2020 - 2022, 2025 3NSoft Inc.
3
+ Copyright (C) 2020 - 2022, 2025 - 2026 3NSoft Inc.
4
4
 
5
5
  This program is free software: you can redistribute it and/or modify it under
6
6
  the terms of the GNU General Public License as published by the Free Software
@@ -30,7 +30,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
30
30
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
31
31
  };
32
32
  Object.defineProperty(exports, "__esModule", { value: true });
33
- exports.dohAt = exports.checkServicesStartingFromSignup = exports.SYSTEM_DOMAIN = exports.makeLogger = exports.DeviceFS = exports.sysFolders = exports.appDirs = exports.makeNetClient = exports.makeServiceLocator = exports.ASMail = exports.SignIn = exports.reverseDomain = exports.Storages = exports.IdManager = exports.SignUp = void 0;
33
+ exports.dohAt = exports.checkServicesStartingFromSignup = exports.SYSTEM_DOMAIN = exports.makeLogger = exports.DeviceFS = exports.sysFolders = exports.appDirs = exports.makeNetClient = exports.wrapNetworkFns = exports.ASMail = exports.SignIn = exports.reverseDomain = exports.Storages = exports.IdManager = exports.SignUp = void 0;
34
34
  __exportStar(require("./core"), exports);
35
35
  var sign_up_1 = require("./core/startup/sign-up");
36
36
  Object.defineProperty(exports, "SignUp", { enumerable: true, get: function () { return sign_up_1.SignUp; } });
@@ -43,8 +43,8 @@ var sign_in_1 = require("./core/startup/sign-in");
43
43
  Object.defineProperty(exports, "SignIn", { enumerable: true, get: function () { return sign_in_1.SignIn; } });
44
44
  var asmail_1 = require("./core/asmail");
45
45
  Object.defineProperty(exports, "ASMail", { enumerable: true, get: function () { return asmail_1.ASMail; } });
46
- var service_locator_1 = require("./lib-client/service-locator");
47
- Object.defineProperty(exports, "makeServiceLocator", { enumerable: true, get: function () { return service_locator_1.makeServiceLocator; } });
46
+ var networks_1 = require("./lib-client/networks");
47
+ Object.defineProperty(exports, "wrapNetworkFns", { enumerable: true, get: function () { return networks_1.wrapNetworkFns; } });
48
48
  var request_utils_1 = require("./lib-client/request-utils");
49
49
  Object.defineProperty(exports, "makeNetClient", { enumerable: true, get: function () { return request_utils_1.makeNetClient; } });
50
50
  var app_files_1 = require("./core/app-files");
@@ -26,7 +26,7 @@ const log_to_file_1 = require("../../lib-client/logging/log-to-file");
26
26
  const error_1 = require("../../lib-common/exceptions/error");
27
27
  const assert_1 = require("../../lib-common/assert");
28
28
  const caps_ipc_wrap_1 = require("./caps-ipc-wrap");
29
- const service_locator_1 = require("../../lib-client/service-locator");
29
+ const networks_1 = require("../../lib-client/networks");
30
30
  const dns_1 = require("dns");
31
31
  const napi_nacl_1 = require("napi-nacl");
32
32
  const request_from_node_1 = require("../../lib-common-on-node/request-from-node");
@@ -62,6 +62,21 @@ exports.testApp = {
62
62
  };
63
63
  let numOfRunningCores = 0;
64
64
  const DATA_FOLDER = (0, path_1.join)(__dirname, '..', '..', '..', 'test-data');
65
+ function resolveTxt(domain) {
66
+ return new Promise((resolve, reject) => (0, dns_1.resolveTxt)(domain, (err, records) => {
67
+ if (err) {
68
+ reject(err);
69
+ }
70
+ else {
71
+ resolve(records);
72
+ }
73
+ }));
74
+ }
75
+ const { makeLocator, makeNet } = (0, networks_1.wrapNetworkFns)({ regular: {
76
+ naming: [{ resolveTxt }],
77
+ openServiceEventsSource: websocket_from_node_1.openServiceEventsSrcFromNode,
78
+ requests: (0, request_from_node_1.makeRequestFromNode)()
79
+ } });
65
80
  class CoreRunner {
66
81
  constructor(signUpUrl) {
67
82
  this.signUpUrl = signUpUrl;
@@ -83,16 +98,7 @@ class CoreRunner {
83
98
  this.appCaps.close();
84
99
  this.appCaps = undefined;
85
100
  }
86
- this.runningCore = lib_index_1.Core.make({ dataDir: this.dataFolder, signUpUrl: this.signUpUrl }, () => (0, lib_index_1.makeNetClient)((0, request_from_node_1.makeRequestFromNode)(), websocket_from_node_1.openServiceEventsSrcFromNode), (0, service_locator_1.makeServiceLocator)({
87
- resolveTxt: domain => new Promise((resolve, reject) => (0, dns_1.resolveTxt)(domain, (err, texts) => {
88
- if (err) {
89
- reject(err);
90
- }
91
- else {
92
- resolve(texts);
93
- }
94
- }))
95
- }), device_fs_places_1.sysFilesOnDevice, napi_nacl_1.makeNativeCryptor, random_node_1.bytes);
101
+ this.runningCore = lib_index_1.Core.make({ dataDir: this.dataFolder, signUpUrl: this.signUpUrl }, makeNet, makeLocator, device_fs_places_1.sysFilesOnDevice, napi_nacl_1.makeNativeCryptor, random_node_1.bytes);
96
102
  }
97
103
  async close() {
98
104
  if (this.appCaps) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "core-3nweb-client-lib",
3
- "version": "0.52.3",
3
+ "version": "0.53.0",
4
4
  "description": "3NWeb client core library, embeddable into different environments",
5
5
  "main": "build/lib-index.js",
6
6
  "types": "build/lib-index.d.ts",