@typedb/driver-http 3.14.0-rc0 → 3.15.0-rc3

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/README.md CHANGED
@@ -45,3 +45,16 @@ if (answer.answerType === "conceptRows") {
45
45
  })
46
46
  }
47
47
  ```
48
+
49
+ To connect over TLS to a server whose certificate is signed by a private CA, pass the CA certificate file in Node.js:
50
+
51
+ ```ts
52
+ const driver = new TypeDBHttpDriver({
53
+ username: "admin",
54
+ password: "password",
55
+ addresses: [ "https://typedb.example.com:8000" ],
56
+ tls: { rootCAPath: "ca.pem" },
57
+ });
58
+ ```
59
+
60
+ With a root CA, every address must use `https`. In browsers, add the CA to the operating system's or the browser's trust store instead.
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// ../../../../../../../../../execroot/_main/bazel-out/k8-opt/bin/http-ts/src/params.ts
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// ../../../../../../../../../execroot/_main/bazel-out/k8-opt/bin/http-ts/src/params.ts
2
2
  function isBasicParams(params) {
3
3
  return `addresses` in params;
4
4
  }
@@ -47,6 +47,126 @@ function allOrigins(params) {
47
47
  else return params.translatedAddresses.map((ta) => ta.external);
48
48
  }
49
49
 
50
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-opt/bin/http-ts/src/transport.ts
51
+ var CONNECT_TIMEOUT_MILLIS = 1e4;
52
+ var REQUEST_INACTIVITY_TIMEOUT_MILLIS = 3e5;
53
+ var KEEP_ALIVE_SOCKET_TIMEOUT_MILLIS = 5e3;
54
+ var PEM_CERTIFICATE_REGEX = /-----BEGIN (TRUSTED )?CERTIFICATE-----.*?-----END \1CERTIFICATE-----/gs;
55
+ var TlsConfigError = class extends Error {
56
+ };
57
+ function createFetch(tls, origins) {
58
+ const rootCAPath = _optionalChain([tls, 'optionalAccess', _ => _.rootCAPath]);
59
+ if (rootCAPath === void 0) return (url, init) => fetch(url, init);
60
+ const plainOrigin = origins.find((origin) => !isHttps(origin));
61
+ const plainOriginError = plainOrigin === void 0 ? void 0 : plainHttpError(plainOrigin);
62
+ let httpsClient;
63
+ return async (url, init) => {
64
+ if (plainOriginError !== void 0) throw plainOriginError;
65
+ if (!isHttps(url)) {
66
+ const { protocol, host } = new URL(url);
67
+ throw plainHttpError(`${protocol}//${host}`);
68
+ }
69
+ _nullishCoalesce(httpsClient, () => ( (httpsClient = createHttpsClient(rootCAPath).catch((error) => {
70
+ httpsClient = void 0;
71
+ throw error;
72
+ }))));
73
+ const { https, agent } = await httpsClient;
74
+ return requestWithAgent(https, agent, url, init);
75
+ };
76
+ }
77
+ async function createHttpsClient(rootCAPath) {
78
+ if (!isNodeJs()) {
79
+ throw new TlsConfigError("A TLS root CA can only be set in Node.js: in browsers, add the CA to the trust store.");
80
+ }
81
+ let https;
82
+ let fs;
83
+ let crypto;
84
+ try {
85
+ [https, fs, crypto] = await Promise.all([
86
+ importNodeModule("node:https"),
87
+ importNodeModule("node:fs/promises"),
88
+ importNodeModule("node:crypto")
89
+ ]);
90
+ } catch (error) {
91
+ throw new TlsConfigError(`Cannot load the Node.js modules for TLS: ${error.message}`);
92
+ }
93
+ let rootCA;
94
+ try {
95
+ rootCA = await fs.readFile(rootCAPath, "utf8");
96
+ } catch (error) {
97
+ throw new TlsConfigError(`Cannot read the TLS root CA file '${rootCAPath}': ${error.message}`);
98
+ }
99
+ const certificates = _nullishCoalesce(rootCA.match(PEM_CERTIFICATE_REGEX), () => ( []));
100
+ if (certificates.length === 0) {
101
+ throw new TlsConfigError(`The TLS root CA file '${rootCAPath}' does not contain a PEM certificate.`);
102
+ }
103
+ try {
104
+ certificates.forEach((certificate) => new crypto.X509Certificate(certificate));
105
+ } catch (error) {
106
+ throw new TlsConfigError(
107
+ `The TLS root CA file '${rootCAPath}' contains an invalid certificate: ${error.message}`
108
+ );
109
+ }
110
+ const agent = new https.Agent({ ca: rootCA, keepAlive: true, timeout: KEEP_ALIVE_SOCKET_TIMEOUT_MILLIS });
111
+ return { https, agent };
112
+ }
113
+ function plainHttpError(origin) {
114
+ return new TlsConfigError(`A TLS root CA is set, but ${origin} does not use https.`);
115
+ }
116
+ function isHttps(url) {
117
+ try {
118
+ return new URL(url).protocol === "https:";
119
+ } catch (e3) {
120
+ return false;
121
+ }
122
+ }
123
+ function isNodeJs() {
124
+ return typeof process !== "undefined" && typeof _optionalChain([process, 'access', _2 => _2.versions, 'optionalAccess', _3 => _3.node]) === "string";
125
+ }
126
+ function importNodeModule(specifier) {
127
+ return Promise.resolve().then(() => _interopRequireWildcard(require(
128
+ /* @vite-ignore */
129
+ /* webpackIgnore: true */
130
+ specifier
131
+ )));
132
+ }
133
+ function requestWithAgent(https, agent, url, init) {
134
+ return new Promise((resolve, reject) => {
135
+ const options = { method: init.method, headers: init.headers, agent };
136
+ const request = https.request(url, options, (response) => {
137
+ const chunks = [];
138
+ response.on("data", (chunk) => chunks.push(chunk));
139
+ response.on("error", reject);
140
+ response.on("end", () => {
141
+ try {
142
+ const status = _nullishCoalesce(response.statusCode, () => ( 0));
143
+ const headers = new Headers();
144
+ for (const [name, value] of Object.entries(response.headers)) {
145
+ if (value !== void 0) headers.set(name, Array.isArray(value) ? value.join(", ") : value);
146
+ }
147
+ const body = status === 204 || status === 304 ? null : Buffer.concat(chunks);
148
+ resolve(new Response(body, { status, statusText: response.statusMessage, headers }));
149
+ } catch (error) {
150
+ reject(error);
151
+ }
152
+ });
153
+ });
154
+ request.on("error", reject);
155
+ request.on("socket", (socket) => {
156
+ if (!socket.connecting) return;
157
+ socket.setTimeout(0);
158
+ const connectTimeout = setTimeout(
159
+ () => request.destroy(new Error("Connection timed out")),
160
+ CONNECT_TIMEOUT_MILLIS
161
+ );
162
+ socket.once("secureConnect", () => clearTimeout(connectTimeout));
163
+ socket.once("close", () => clearTimeout(connectTimeout));
164
+ });
165
+ request.setTimeout(REQUEST_INACTIVITY_TIMEOUT_MILLIS, () => request.destroy(new Error("Request timed out")));
166
+ request.end(init.body);
167
+ });
168
+ }
169
+
50
170
  // ../../../../../../../../../execroot/_main/bazel-out/k8-opt/bin/http-ts/src/response.ts
51
171
  function isApiError(err) {
52
172
  return err != null && typeof err.code === "string" && typeof err.message === "string";
@@ -293,25 +413,38 @@ function isDigit(c) {
293
413
 
294
414
  // ../../../../../../../../../execroot/_main/bazel-out/k8-opt/bin/http-ts/src/analyzed-conjunction.ts
295
415
  function getVariableName(structure, variable) {
296
- return _optionalChain([structure, 'access', _ => _.variables, 'access', _2 => _2[variable.id], 'optionalAccess', _3 => _3.name]);
416
+ return _optionalChain([structure, 'access', _4 => _4.variables, 'access', _5 => _5[variable.id], 'optionalAccess', _6 => _6.name]);
297
417
  }
298
418
 
299
419
  // ../../../../../../../../../execroot/_main/bazel-out/k8-opt/bin/http-ts/src/index.ts
300
420
  var HTTP_UNAUTHORIZED = 401;
301
421
  var HTTP_MISDIRECTED = 421;
302
- var TRANSIENT_SIGNIN_ERROR_CODES = /* @__PURE__ */ new Set(["HDR2", "SRV14", "CSV8", "CSV9"]);
422
+ var TRANSIENT_SIGNIN_ERROR_CODES = /* @__PURE__ */ new Set(["SRV14", "CSV8", "CSV9"]);
303
423
  var PRIMARY_FAILOVER_RETRIES = 3;
304
424
  var PRIMARY_SELECTION_WAIT_MS = 2e3;
305
425
  function isApiErrResp(value) {
306
- return value !== null && "err" in value;
426
+ return "err" in value;
427
+ }
428
+ function isOriginFailure(value) {
429
+ return "failure" in value;
430
+ }
431
+ function tokenErrToResult(tokenResp, origin) {
432
+ const { code, message } = tokenResp.err;
433
+ if (TRANSIENT_SIGNIN_ERROR_CODES.has(code))
434
+ return { failure: `Server at ${origin} failed signin with [${code}] ${message}` };
435
+ else
436
+ return tokenResp;
307
437
  }
308
- function tokenErrToResult(tokenResp) {
309
- return TRANSIENT_SIGNIN_ERROR_CODES.has(tokenResp.err.code) ? null : tokenResp;
438
+ function errorMessage(error) {
439
+ const reason = _nullishCoalesce(_optionalChain([error, 'optionalAccess', _7 => _7.cause]), () => ( error));
440
+ const { message, code } = _nullishCoalesce(reason, () => ( {}));
441
+ return (message || code || String(reason)).trim();
310
442
  }
311
443
  var TypeDBHttpDriver = class {
312
444
  constructor(params) {
313
445
  this.params = params;
314
446
  this.currentOrigin = remoteOrigin(params);
447
+ this.fetch = createFetch(params.tls, allOrigins(params));
315
448
  }
316
449
  getDatabases() {
317
450
  return this.apiGet(`/v1/databases`);
@@ -428,59 +561,58 @@ var TypeDBHttpDriver = class {
428
561
  const initial = await this.tryApiReq(method, path, body, options);
429
562
  if (isApiErrResp(initial)) return initial;
430
563
  let result = initial;
431
- if (result !== null && result.status === HTTP_MISDIRECTED) {
564
+ if (!isOriginFailure(result) && result.status === HTTP_MISDIRECTED) {
432
565
  const redirected = await this.followMisdirected(result, method, path, body, options);
433
566
  if (isApiErrResp(redirected)) return redirected;
434
567
  result = redirected;
435
568
  }
436
- if (result === null || !result.ok) {
437
- const startOrigin = this.currentOrigin;
438
- triedOrigins.add(startOrigin);
439
- const fallback = await this.tryFallbackOrigins(method, path, body, options, triedOrigins);
569
+ if (isOriginFailure(result) || !result.ok) {
570
+ const fallback = await this.tryFallbackOrigins(method, path, body, options, triedOrigins, result);
440
571
  if (isApiErrResp(fallback)) return fallback;
441
- if (fallback !== null && (result === null || fallback.ok)) {
442
- result = fallback;
443
- } else if (result !== null) {
444
- this.switchOrigin(startOrigin);
445
- }
572
+ result = fallback;
573
+ }
574
+ if (isOriginFailure(result)) {
575
+ return driverError("HDR1", `Cannot connect to any server (last error: ${result.failure})`);
446
576
  }
447
- return _nullishCoalesce(result, () => ( driverError("HDR1", "Cannot connect to any server")));
577
+ return result;
448
578
  }
449
579
  async tryApiReq(method, path, body, options) {
450
- const url = `${this.currentOrigin}${path}`;
580
+ const origin = this.currentOrigin;
451
581
  let tokenResp = await this.getToken();
452
- if ("err" in tokenResp) return tokenErrToResult(tokenResp);
582
+ if (isOriginFailure(tokenResp)) return tokenResp;
583
+ if (isApiErrResp(tokenResp)) return tokenErrToResult(tokenResp, origin);
453
584
  const bodyString = body !== void 0 ? stringifyJson(body) : void 0;
454
585
  let headers = this.authHeaders(tokenResp.ok.token, options);
455
- let resp;
456
- try {
457
- resp = await fetch(url, { method, body: bodyString, headers });
458
- } catch (e3) {
459
- return null;
460
- }
461
- if (resp.status === HTTP_UNAUTHORIZED) {
586
+ let resp = await this.fetchFrom(origin, path, { method, body: bodyString, headers });
587
+ if (!isApiErrResp(resp) && !isOriginFailure(resp) && resp.status === HTTP_UNAUTHORIZED) {
462
588
  tokenResp = await this.refreshToken();
463
- if ("err" in tokenResp) return tokenErrToResult(tokenResp);
589
+ if (isOriginFailure(tokenResp)) return tokenResp;
590
+ if (isApiErrResp(tokenResp)) return tokenErrToResult(tokenResp, origin);
464
591
  headers = this.authHeaders(tokenResp.ok.token, options);
465
- try {
466
- resp = await fetch(url, { method, body: bodyString, headers });
467
- } catch (e4) {
468
- return null;
469
- }
592
+ resp = await this.fetchFrom(origin, path, { method, body: bodyString, headers });
470
593
  }
471
594
  return resp;
472
595
  }
596
+ async fetchFrom(origin, path, init) {
597
+ try {
598
+ return await this.fetch(`${origin}${path}`, init);
599
+ } catch (error) {
600
+ if (error instanceof TlsConfigError) return driverError("HDR3", error.message);
601
+ return { failure: `Cannot connect to server at ${origin}: ${errorMessage(error)}` };
602
+ }
603
+ }
473
604
  async followMisdirected(resp, method, path, body, options) {
605
+ const origin = this.currentOrigin;
474
606
  if (await this.switchToRedirectTarget(resp)) {
475
607
  return await this.tryApiReq(method, path, body, options);
476
608
  }
477
- return null;
609
+ return { failure: `Server at ${origin} returned 421 Misdirected Request without another primary address` };
478
610
  }
479
611
  async switchToRedirectTarget(resp) {
480
612
  let json;
481
613
  try {
482
614
  json = parseJson(await resp.text());
483
- } catch (e5) {
615
+ } catch (e4) {
484
616
  return false;
485
617
  }
486
618
  if (!isMisdirectedError(json)) return false;
@@ -489,33 +621,46 @@ var TypeDBHttpDriver = class {
489
621
  this.switchOrigin(newOrigin);
490
622
  return true;
491
623
  }
492
- async tryFallbackOrigins(method, path, body, options, triedOrigins) {
624
+ async tryFallbackOrigins(method, path, body, options, triedOrigins, startResult) {
625
+ const startOrigin = this.currentOrigin;
626
+ triedOrigins.add(startOrigin);
493
627
  const origins = allOrigins(this.params);
494
628
  let lastError = null;
629
+ let lastFailure = null;
495
630
  for (const origin of origins) {
496
631
  if (triedOrigins.has(origin)) continue;
497
632
  this.switchOrigin(origin);
498
633
  const initial = await this.tryApiReq(method, path, body, options);
499
- if (initial === null) continue;
634
+ if (isOriginFailure(initial)) {
635
+ lastFailure = initial;
636
+ continue;
637
+ }
500
638
  if (isApiErrResp(initial)) return initial;
501
639
  let result = initial;
502
640
  if (result.status === HTTP_MISDIRECTED) {
503
641
  const redirected = await this.followMisdirected(result, method, path, body, options);
504
- if (redirected === null) continue;
642
+ if (isOriginFailure(redirected)) {
643
+ lastFailure = redirected;
644
+ continue;
645
+ }
505
646
  if (isApiErrResp(redirected)) return redirected;
506
647
  result = redirected;
507
648
  }
508
649
  if (result.ok) return result;
509
650
  lastError = result;
510
651
  }
511
- return lastError;
652
+ if (!isOriginFailure(startResult)) {
653
+ this.switchOrigin(startOrigin);
654
+ return startResult;
655
+ }
656
+ return _nullishCoalesce(_nullishCoalesce(lastError, () => ( lastFailure)), () => ( startResult));
512
657
  }
513
658
  switchOrigin(origin) {
514
659
  this.currentOrigin = origin;
515
660
  this.token = void 0;
516
661
  }
517
662
  authHeaders(token, options) {
518
- return { "Authorization": `Bearer ${token}`, "Content-Type": "application/json", ..._optionalChain([options, 'optionalAccess', _4 => _4.headers]) };
663
+ return { "Authorization": `Bearer ${token}`, "Content-Type": "application/json", ..._optionalChain([options, 'optionalAccess', _8 => _8.headers]) };
519
664
  }
520
665
  getToken() {
521
666
  if (this.token) {
@@ -527,26 +672,21 @@ var TypeDBHttpDriver = class {
527
672
  return this.signinOnce();
528
673
  }
529
674
  async signinOnce() {
530
- const url = `${this.currentOrigin}/v1/signin`;
531
675
  const body = { username: this.params.username, password: this.params.password };
532
- let resp;
533
- try {
534
- resp = await fetch(url, { method: "POST", body: stringifyJson(body), headers: { "Content-Type": "application/json" } });
535
- } catch (e6) {
536
- return driverError("HDR2", `Cannot connect to server at ${this.currentOrigin}`);
537
- }
676
+ const resp = await this.fetchFrom(this.currentOrigin, "/v1/signin", { method: "POST", body: stringifyJson(body), headers: { "Content-Type": "application/json" } });
677
+ if (isApiErrResp(resp) || isOriginFailure(resp)) return resp;
538
678
  const json = await this.jsonOrNull(resp);
539
- if (resp.ok && typeof _optionalChain([json, 'optionalAccess', _5 => _5.token]) === "string") {
679
+ if (resp.ok && typeof _optionalChain([json, 'optionalAccess', _9 => _9.token]) === "string") {
540
680
  this.token = json.token;
541
681
  return { ok: json };
542
682
  }
543
683
  if (isApiError(json)) return { err: json, status: resp.status };
544
- return driverError("HDR2", `Unexpected signin response from ${this.currentOrigin} (HTTP ${resp.status})`);
684
+ return { failure: `Unexpected signin response from ${this.currentOrigin} (HTTP ${resp.status})` };
545
685
  }
546
686
  async jsonOrNull(resp) {
547
687
  try {
548
688
  return parseJson(await resp.text());
549
- } catch (e7) {
689
+ } catch (e5) {
550
690
  return null;
551
691
  }
552
692
  }
package/dist/index.d.ts CHANGED
@@ -2,11 +2,26 @@ interface DriverParamsBasic {
2
2
  username: string;
3
3
  password: string;
4
4
  addresses: string[];
5
+ tls?: DriverTlsConfig;
5
6
  }
6
7
  interface DriverParamsTranslated {
7
8
  username: string;
8
9
  password: string;
9
10
  translatedAddresses: TranslatedAddress[];
11
+ tls?: DriverTlsConfig;
12
+ }
13
+ /**
14
+ * TLS settings for `https` addresses. Without them, the driver trusts the certificate authorities that the
15
+ * runtime trusts by default.
16
+ */
17
+ interface DriverTlsConfig {
18
+ /**
19
+ * The path to the PEM file with the certificate of the CA that signed the servers' certificates. When set, the
20
+ * driver trusts only this CA, and every address must use `https`. The file is read on the first request, and
21
+ * requests fail with `HDR3` if it cannot be used. Supported in Node.js only: browsers trust the certificates of
22
+ * their own trust store.
23
+ */
24
+ rootCAPath?: string;
10
25
  }
11
26
  interface TranslatedAddress {
12
27
  external: string;
@@ -123,7 +138,7 @@ type ConjunctionIndex$1 = number;
123
138
  type VariableInfo = {
124
139
  name: string | null;
125
140
  };
126
- type ConstraintAny = ConstraintIsa | ConstraintIsaExact | ConstraintHas | ConstraintLinks | ConstraintSub | ConstraintSubExact | ConstraintOwns | ConstraintRelates | ConstraintPlays | ConstraintExpression | ConstraintFunction | ConstraintComparison | ConstraintIs | ConstraintIid | ConstraintDeleteConcepts | ConstraintKind | ConstraintValue | ConstraintLabel | ConstraintOr | ConstraintNot | ConstraintTry;
141
+ type ConstraintAny = ConstraintIsa | ConstraintIsaExact | ConstraintHas | ConstraintLinks | ConstraintSub | ConstraintSubExact | ConstraintOwns | ConstraintRelates | ConstraintPlays | ConstraintExpression | ConstraintFunction | ConstraintComparison | ConstraintIs | ConstraintIid | ConstraintDeleteConcepts | ConstraintKind | ConstraintValue | ConstraintIsSet | ConstraintLabel | ConstraintOr | ConstraintNot | ConstraintTry;
127
142
  type ConstraintSpan = {
128
143
  begin: number;
129
144
  end: number;
@@ -233,6 +248,11 @@ interface ConstraintValue {
233
248
  attributeType: ConstraintVertexVariable;
234
249
  valueType: string;
235
250
  }
251
+ interface ConstraintIsSet {
252
+ tag: "isSet";
253
+ textSpan: ConstraintSpan;
254
+ variables: ConstraintVertexVariable[];
255
+ }
236
256
  interface ConstraintKind {
237
257
  tag: "kind";
238
258
  textSpan: ConstraintSpan;
@@ -485,6 +505,7 @@ declare class TypeDBHttpDriver {
485
505
  private params;
486
506
  private token?;
487
507
  private currentOrigin;
508
+ private readonly fetch;
488
509
  constructor(params: DriverParams);
489
510
  getDatabases(): Promise<ApiResponse<DatabasesListResponse>>;
490
511
  getDatabase(name: string): Promise<ApiResponse<Database>>;
@@ -519,6 +540,7 @@ declare class TypeDBHttpDriver {
519
540
  private apiReq;
520
541
  private apiReqOnce;
521
542
  private tryApiReq;
543
+ private fetchFrom;
522
544
  private followMisdirected;
523
545
  private switchToRedirectTarget;
524
546
  private tryFallbackOrigins;
@@ -554,4 +576,4 @@ type GivenRows = {
554
576
  [varName: string]: GivenRowEntry;
555
577
  }[];
556
578
 
557
- export { type AnalyzeOptions, type AnalyzeResponse, type AnalyzedConjunction, type AnalyzedFetch, type AnalyzedFunction, type AnalyzedPipeline, type Answer, type AnswerType, type ApiError, type ApiErrorResponse, type ApiOkResponse, type ApiResponse, type Attribute, type AttributeType, type Concept, type ConceptDocument, type ConceptDocumentsQueryResponse, type ConceptRow, type ConceptRowAnswer, type ConceptRowsQueryResponse, type ConceptRowsQueryResponseLegacy, type ConjunctionAnnotations, type ConstraintAny, type ConstraintComparison, type ConstraintDeleteConcepts, type ConstraintExpression, type ConstraintExpressionLegacy, type ConstraintFunction, type ConstraintHas, type ConstraintIid, type ConstraintIs, type ConstraintIsa, type ConstraintIsaExact, type ConstraintKind, type ConstraintLabel, type ConstraintLinks, type ConstraintLinksLegacy, type ConstraintNot, type ConstraintOr, type ConstraintOwns, type ConstraintPlays, type ConstraintRelates, type ConstraintSpan, type ConstraintSub, type ConstraintSubExact, type ConstraintTry, type ConstraintValue, type ConstraintVertexAny, type ConstraintVertexLabel, type ConstraintVertexNamedRole, type ConstraintVertexValue, type ConstraintVertexVariable, type Database, type DatabasesListResponse, type Distribution, type DriverParams, type DriverParamsBasic, type DriverParamsTranslated, type EdgeKind, type Entity, type EntityType, type FetchAnnotationFieldEntry, type FunctionReturnStructure, type FunctionSingleReturnSelector, type Given, type GivenRowEntry, type GivenRows, type InstantiableType, type MisdirectedError, type OkQueryResponse, type PipelineStage, type QueryConjunctionLegacy, type QueryOptions, type QueryResponse, type QueryResponseBase, type QueryStructureLegacy, type QueryType, type Reducer, type Relation, type RelationType, type ReplicationRole, type ReplicationStatus, type RoleType, type Server, type ServersListResponse, type SignInResponse, type ThingKind, type TransactionOpenResponse, type TransactionOptions, type TransactionType, type TranslatedAddress, type Type, TypeDBHttpDriver, type TypeKind, type User, type UsersListResponse, type Value, type ValueKind, type ValueType, type VariableAnnotations, type VariableId, type VariableInfo, type VersionResponse, allOrigins, driverError, getVariableName, hostPortFromOrigin, isApiError, isApiErrorResponse, isBasicParams, isMisdirectedError, isOkResponse, isQueryResponseLegacy, isTranslatedParams, parseJson, remoteOrigin, resolveOrigin, stringifyJson };
579
+ export { type AnalyzeOptions, type AnalyzeResponse, type AnalyzedConjunction, type AnalyzedFetch, type AnalyzedFunction, type AnalyzedPipeline, type Answer, type AnswerType, type ApiError, type ApiErrorResponse, type ApiOkResponse, type ApiResponse, type Attribute, type AttributeType, type Concept, type ConceptDocument, type ConceptDocumentsQueryResponse, type ConceptRow, type ConceptRowAnswer, type ConceptRowsQueryResponse, type ConceptRowsQueryResponseLegacy, type ConjunctionAnnotations, type ConstraintAny, type ConstraintComparison, type ConstraintDeleteConcepts, type ConstraintExpression, type ConstraintExpressionLegacy, type ConstraintFunction, type ConstraintHas, type ConstraintIid, type ConstraintIs, type ConstraintIsSet, type ConstraintIsa, type ConstraintIsaExact, type ConstraintKind, type ConstraintLabel, type ConstraintLinks, type ConstraintLinksLegacy, type ConstraintNot, type ConstraintOr, type ConstraintOwns, type ConstraintPlays, type ConstraintRelates, type ConstraintSpan, type ConstraintSub, type ConstraintSubExact, type ConstraintTry, type ConstraintValue, type ConstraintVertexAny, type ConstraintVertexLabel, type ConstraintVertexNamedRole, type ConstraintVertexValue, type ConstraintVertexVariable, type Database, type DatabasesListResponse, type Distribution, type DriverParams, type DriverParamsBasic, type DriverParamsTranslated, type DriverTlsConfig, type EdgeKind, type Entity, type EntityType, type FetchAnnotationFieldEntry, type FunctionReturnStructure, type FunctionSingleReturnSelector, type Given, type GivenRowEntry, type GivenRows, type InstantiableType, type MisdirectedError, type OkQueryResponse, type PipelineStage, type QueryConjunctionLegacy, type QueryOptions, type QueryResponse, type QueryResponseBase, type QueryStructureLegacy, type QueryType, type Reducer, type Relation, type RelationType, type ReplicationRole, type ReplicationStatus, type RoleType, type Server, type ServersListResponse, type SignInResponse, type ThingKind, type TransactionOpenResponse, type TransactionOptions, type TransactionType, type TranslatedAddress, type Type, TypeDBHttpDriver, type TypeKind, type User, type UsersListResponse, type Value, type ValueKind, type ValueType, type VariableAnnotations, type VariableId, type VariableInfo, type VersionResponse, allOrigins, driverError, getVariableName, hostPortFromOrigin, isApiError, isApiErrorResponse, isBasicParams, isMisdirectedError, isOkResponse, isQueryResponseLegacy, isTranslatedParams, parseJson, remoteOrigin, resolveOrigin, stringifyJson };
package/dist/index.mjs CHANGED
@@ -47,6 +47,126 @@ function allOrigins(params) {
47
47
  else return params.translatedAddresses.map((ta) => ta.external);
48
48
  }
49
49
 
50
+ // ../../../../../../../../../execroot/_main/bazel-out/k8-opt/bin/http-ts/src/transport.ts
51
+ var CONNECT_TIMEOUT_MILLIS = 1e4;
52
+ var REQUEST_INACTIVITY_TIMEOUT_MILLIS = 3e5;
53
+ var KEEP_ALIVE_SOCKET_TIMEOUT_MILLIS = 5e3;
54
+ var PEM_CERTIFICATE_REGEX = /-----BEGIN (TRUSTED )?CERTIFICATE-----.*?-----END \1CERTIFICATE-----/gs;
55
+ var TlsConfigError = class extends Error {
56
+ };
57
+ function createFetch(tls, origins) {
58
+ const rootCAPath = tls?.rootCAPath;
59
+ if (rootCAPath === void 0) return (url, init) => fetch(url, init);
60
+ const plainOrigin = origins.find((origin) => !isHttps(origin));
61
+ const plainOriginError = plainOrigin === void 0 ? void 0 : plainHttpError(plainOrigin);
62
+ let httpsClient;
63
+ return async (url, init) => {
64
+ if (plainOriginError !== void 0) throw plainOriginError;
65
+ if (!isHttps(url)) {
66
+ const { protocol, host } = new URL(url);
67
+ throw plainHttpError(`${protocol}//${host}`);
68
+ }
69
+ httpsClient ?? (httpsClient = createHttpsClient(rootCAPath).catch((error) => {
70
+ httpsClient = void 0;
71
+ throw error;
72
+ }));
73
+ const { https, agent } = await httpsClient;
74
+ return requestWithAgent(https, agent, url, init);
75
+ };
76
+ }
77
+ async function createHttpsClient(rootCAPath) {
78
+ if (!isNodeJs()) {
79
+ throw new TlsConfigError("A TLS root CA can only be set in Node.js: in browsers, add the CA to the trust store.");
80
+ }
81
+ let https;
82
+ let fs;
83
+ let crypto;
84
+ try {
85
+ [https, fs, crypto] = await Promise.all([
86
+ importNodeModule("node:https"),
87
+ importNodeModule("node:fs/promises"),
88
+ importNodeModule("node:crypto")
89
+ ]);
90
+ } catch (error) {
91
+ throw new TlsConfigError(`Cannot load the Node.js modules for TLS: ${error.message}`);
92
+ }
93
+ let rootCA;
94
+ try {
95
+ rootCA = await fs.readFile(rootCAPath, "utf8");
96
+ } catch (error) {
97
+ throw new TlsConfigError(`Cannot read the TLS root CA file '${rootCAPath}': ${error.message}`);
98
+ }
99
+ const certificates = rootCA.match(PEM_CERTIFICATE_REGEX) ?? [];
100
+ if (certificates.length === 0) {
101
+ throw new TlsConfigError(`The TLS root CA file '${rootCAPath}' does not contain a PEM certificate.`);
102
+ }
103
+ try {
104
+ certificates.forEach((certificate) => new crypto.X509Certificate(certificate));
105
+ } catch (error) {
106
+ throw new TlsConfigError(
107
+ `The TLS root CA file '${rootCAPath}' contains an invalid certificate: ${error.message}`
108
+ );
109
+ }
110
+ const agent = new https.Agent({ ca: rootCA, keepAlive: true, timeout: KEEP_ALIVE_SOCKET_TIMEOUT_MILLIS });
111
+ return { https, agent };
112
+ }
113
+ function plainHttpError(origin) {
114
+ return new TlsConfigError(`A TLS root CA is set, but ${origin} does not use https.`);
115
+ }
116
+ function isHttps(url) {
117
+ try {
118
+ return new URL(url).protocol === "https:";
119
+ } catch {
120
+ return false;
121
+ }
122
+ }
123
+ function isNodeJs() {
124
+ return typeof process !== "undefined" && typeof process.versions?.node === "string";
125
+ }
126
+ function importNodeModule(specifier) {
127
+ return import(
128
+ /* @vite-ignore */
129
+ /* webpackIgnore: true */
130
+ specifier
131
+ );
132
+ }
133
+ function requestWithAgent(https, agent, url, init) {
134
+ return new Promise((resolve, reject) => {
135
+ const options = { method: init.method, headers: init.headers, agent };
136
+ const request = https.request(url, options, (response) => {
137
+ const chunks = [];
138
+ response.on("data", (chunk) => chunks.push(chunk));
139
+ response.on("error", reject);
140
+ response.on("end", () => {
141
+ try {
142
+ const status = response.statusCode ?? 0;
143
+ const headers = new Headers();
144
+ for (const [name, value] of Object.entries(response.headers)) {
145
+ if (value !== void 0) headers.set(name, Array.isArray(value) ? value.join(", ") : value);
146
+ }
147
+ const body = status === 204 || status === 304 ? null : Buffer.concat(chunks);
148
+ resolve(new Response(body, { status, statusText: response.statusMessage, headers }));
149
+ } catch (error) {
150
+ reject(error);
151
+ }
152
+ });
153
+ });
154
+ request.on("error", reject);
155
+ request.on("socket", (socket) => {
156
+ if (!socket.connecting) return;
157
+ socket.setTimeout(0);
158
+ const connectTimeout = setTimeout(
159
+ () => request.destroy(new Error("Connection timed out")),
160
+ CONNECT_TIMEOUT_MILLIS
161
+ );
162
+ socket.once("secureConnect", () => clearTimeout(connectTimeout));
163
+ socket.once("close", () => clearTimeout(connectTimeout));
164
+ });
165
+ request.setTimeout(REQUEST_INACTIVITY_TIMEOUT_MILLIS, () => request.destroy(new Error("Request timed out")));
166
+ request.end(init.body);
167
+ });
168
+ }
169
+
50
170
  // ../../../../../../../../../execroot/_main/bazel-out/k8-opt/bin/http-ts/src/response.ts
51
171
  function isApiError(err) {
52
172
  return err != null && typeof err.code === "string" && typeof err.message === "string";
@@ -299,19 +419,32 @@ function getVariableName(structure, variable) {
299
419
  // ../../../../../../../../../execroot/_main/bazel-out/k8-opt/bin/http-ts/src/index.ts
300
420
  var HTTP_UNAUTHORIZED = 401;
301
421
  var HTTP_MISDIRECTED = 421;
302
- var TRANSIENT_SIGNIN_ERROR_CODES = /* @__PURE__ */ new Set(["HDR2", "SRV14", "CSV8", "CSV9"]);
422
+ var TRANSIENT_SIGNIN_ERROR_CODES = /* @__PURE__ */ new Set(["SRV14", "CSV8", "CSV9"]);
303
423
  var PRIMARY_FAILOVER_RETRIES = 3;
304
424
  var PRIMARY_SELECTION_WAIT_MS = 2e3;
305
425
  function isApiErrResp(value) {
306
- return value !== null && "err" in value;
426
+ return "err" in value;
427
+ }
428
+ function isOriginFailure(value) {
429
+ return "failure" in value;
430
+ }
431
+ function tokenErrToResult(tokenResp, origin) {
432
+ const { code, message } = tokenResp.err;
433
+ if (TRANSIENT_SIGNIN_ERROR_CODES.has(code))
434
+ return { failure: `Server at ${origin} failed signin with [${code}] ${message}` };
435
+ else
436
+ return tokenResp;
307
437
  }
308
- function tokenErrToResult(tokenResp) {
309
- return TRANSIENT_SIGNIN_ERROR_CODES.has(tokenResp.err.code) ? null : tokenResp;
438
+ function errorMessage(error) {
439
+ const reason = error?.cause ?? error;
440
+ const { message, code } = reason ?? {};
441
+ return (message || code || String(reason)).trim();
310
442
  }
311
443
  var TypeDBHttpDriver = class {
312
444
  constructor(params) {
313
445
  this.params = params;
314
446
  this.currentOrigin = remoteOrigin(params);
447
+ this.fetch = createFetch(params.tls, allOrigins(params));
315
448
  }
316
449
  getDatabases() {
317
450
  return this.apiGet(`/v1/databases`);
@@ -428,53 +561,52 @@ var TypeDBHttpDriver = class {
428
561
  const initial = await this.tryApiReq(method, path, body, options);
429
562
  if (isApiErrResp(initial)) return initial;
430
563
  let result = initial;
431
- if (result !== null && result.status === HTTP_MISDIRECTED) {
564
+ if (!isOriginFailure(result) && result.status === HTTP_MISDIRECTED) {
432
565
  const redirected = await this.followMisdirected(result, method, path, body, options);
433
566
  if (isApiErrResp(redirected)) return redirected;
434
567
  result = redirected;
435
568
  }
436
- if (result === null || !result.ok) {
437
- const startOrigin = this.currentOrigin;
438
- triedOrigins.add(startOrigin);
439
- const fallback = await this.tryFallbackOrigins(method, path, body, options, triedOrigins);
569
+ if (isOriginFailure(result) || !result.ok) {
570
+ const fallback = await this.tryFallbackOrigins(method, path, body, options, triedOrigins, result);
440
571
  if (isApiErrResp(fallback)) return fallback;
441
- if (fallback !== null && (result === null || fallback.ok)) {
442
- result = fallback;
443
- } else if (result !== null) {
444
- this.switchOrigin(startOrigin);
445
- }
572
+ result = fallback;
573
+ }
574
+ if (isOriginFailure(result)) {
575
+ return driverError("HDR1", `Cannot connect to any server (last error: ${result.failure})`);
446
576
  }
447
- return result ?? driverError("HDR1", "Cannot connect to any server");
577
+ return result;
448
578
  }
449
579
  async tryApiReq(method, path, body, options) {
450
- const url = `${this.currentOrigin}${path}`;
580
+ const origin = this.currentOrigin;
451
581
  let tokenResp = await this.getToken();
452
- if ("err" in tokenResp) return tokenErrToResult(tokenResp);
582
+ if (isOriginFailure(tokenResp)) return tokenResp;
583
+ if (isApiErrResp(tokenResp)) return tokenErrToResult(tokenResp, origin);
453
584
  const bodyString = body !== void 0 ? stringifyJson(body) : void 0;
454
585
  let headers = this.authHeaders(tokenResp.ok.token, options);
455
- let resp;
456
- try {
457
- resp = await fetch(url, { method, body: bodyString, headers });
458
- } catch {
459
- return null;
460
- }
461
- if (resp.status === HTTP_UNAUTHORIZED) {
586
+ let resp = await this.fetchFrom(origin, path, { method, body: bodyString, headers });
587
+ if (!isApiErrResp(resp) && !isOriginFailure(resp) && resp.status === HTTP_UNAUTHORIZED) {
462
588
  tokenResp = await this.refreshToken();
463
- if ("err" in tokenResp) return tokenErrToResult(tokenResp);
589
+ if (isOriginFailure(tokenResp)) return tokenResp;
590
+ if (isApiErrResp(tokenResp)) return tokenErrToResult(tokenResp, origin);
464
591
  headers = this.authHeaders(tokenResp.ok.token, options);
465
- try {
466
- resp = await fetch(url, { method, body: bodyString, headers });
467
- } catch {
468
- return null;
469
- }
592
+ resp = await this.fetchFrom(origin, path, { method, body: bodyString, headers });
470
593
  }
471
594
  return resp;
472
595
  }
596
+ async fetchFrom(origin, path, init) {
597
+ try {
598
+ return await this.fetch(`${origin}${path}`, init);
599
+ } catch (error) {
600
+ if (error instanceof TlsConfigError) return driverError("HDR3", error.message);
601
+ return { failure: `Cannot connect to server at ${origin}: ${errorMessage(error)}` };
602
+ }
603
+ }
473
604
  async followMisdirected(resp, method, path, body, options) {
605
+ const origin = this.currentOrigin;
474
606
  if (await this.switchToRedirectTarget(resp)) {
475
607
  return await this.tryApiReq(method, path, body, options);
476
608
  }
477
- return null;
609
+ return { failure: `Server at ${origin} returned 421 Misdirected Request without another primary address` };
478
610
  }
479
611
  async switchToRedirectTarget(resp) {
480
612
  let json;
@@ -489,26 +621,39 @@ var TypeDBHttpDriver = class {
489
621
  this.switchOrigin(newOrigin);
490
622
  return true;
491
623
  }
492
- async tryFallbackOrigins(method, path, body, options, triedOrigins) {
624
+ async tryFallbackOrigins(method, path, body, options, triedOrigins, startResult) {
625
+ const startOrigin = this.currentOrigin;
626
+ triedOrigins.add(startOrigin);
493
627
  const origins = allOrigins(this.params);
494
628
  let lastError = null;
629
+ let lastFailure = null;
495
630
  for (const origin of origins) {
496
631
  if (triedOrigins.has(origin)) continue;
497
632
  this.switchOrigin(origin);
498
633
  const initial = await this.tryApiReq(method, path, body, options);
499
- if (initial === null) continue;
634
+ if (isOriginFailure(initial)) {
635
+ lastFailure = initial;
636
+ continue;
637
+ }
500
638
  if (isApiErrResp(initial)) return initial;
501
639
  let result = initial;
502
640
  if (result.status === HTTP_MISDIRECTED) {
503
641
  const redirected = await this.followMisdirected(result, method, path, body, options);
504
- if (redirected === null) continue;
642
+ if (isOriginFailure(redirected)) {
643
+ lastFailure = redirected;
644
+ continue;
645
+ }
505
646
  if (isApiErrResp(redirected)) return redirected;
506
647
  result = redirected;
507
648
  }
508
649
  if (result.ok) return result;
509
650
  lastError = result;
510
651
  }
511
- return lastError;
652
+ if (!isOriginFailure(startResult)) {
653
+ this.switchOrigin(startOrigin);
654
+ return startResult;
655
+ }
656
+ return lastError ?? lastFailure ?? startResult;
512
657
  }
513
658
  switchOrigin(origin) {
514
659
  this.currentOrigin = origin;
@@ -527,21 +672,16 @@ var TypeDBHttpDriver = class {
527
672
  return this.signinOnce();
528
673
  }
529
674
  async signinOnce() {
530
- const url = `${this.currentOrigin}/v1/signin`;
531
675
  const body = { username: this.params.username, password: this.params.password };
532
- let resp;
533
- try {
534
- resp = await fetch(url, { method: "POST", body: stringifyJson(body), headers: { "Content-Type": "application/json" } });
535
- } catch {
536
- return driverError("HDR2", `Cannot connect to server at ${this.currentOrigin}`);
537
- }
676
+ const resp = await this.fetchFrom(this.currentOrigin, "/v1/signin", { method: "POST", body: stringifyJson(body), headers: { "Content-Type": "application/json" } });
677
+ if (isApiErrResp(resp) || isOriginFailure(resp)) return resp;
538
678
  const json = await this.jsonOrNull(resp);
539
679
  if (resp.ok && typeof json?.token === "string") {
540
680
  this.token = json.token;
541
681
  return { ok: json };
542
682
  }
543
683
  if (isApiError(json)) return { err: json, status: resp.status };
544
- return driverError("HDR2", `Unexpected signin response from ${this.currentOrigin} (HTTP ${resp.status})`);
684
+ return { failure: `Unexpected signin response from ${this.currentOrigin} (HTTP ${resp.status})` };
545
685
  }
546
686
  async jsonOrNull(resp) {
547
687
  try {
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name": "@typedb/driver-http", "version": "3.14.0-rc0", "description": "TypeDB HTTP Driver", "author": "TypeDB Community <community@typedb.com>", "license": "Apache-2.0", "homepage": "https://typedb.com", "repository": {"type": "git", "url": "https://github.com/typedb/typedb-driver"}, "bugs": {"url": "https://github.com/typedb/typedb-driver/issues"}, "keywords": ["database", "typedb"], "main": "dist/index.cjs", "module": "dist/index.mjs", "types": "dist/index.d.ts", "type": "module", "exports": {".": {"types": "./dist/index.d.ts", "import": "./dist/index.mjs", "require": "./dist/index.cjs"}}, "scripts": {"build:dev": "tsc", "build": "tsup", "test": "cucumber-js", "docs": "typedoc"}, "packageManager": "pnpm@8.15.9", "packageConfig": {"access": "public"}, "devDependencies": {"@cucumber/cucumber": "9.0.0", "@types/node": "24.0.12", "tsup": "8.5.0", "typedoc": "^0.28.7", "typescript": "5.8.3"}}
1
+ {"name": "@typedb/driver-http", "version": "3.15.0-rc3", "description": "TypeDB HTTP Driver", "author": "TypeDB Community <community@typedb.com>", "license": "Apache-2.0", "homepage": "https://typedb.com", "repository": {"type": "git", "url": "https://github.com/typedb/typedb-driver"}, "bugs": {"url": "https://github.com/typedb/typedb-driver/issues"}, "keywords": ["database", "typedb"], "main": "dist/index.cjs", "module": "dist/index.mjs", "types": "dist/index.d.ts", "type": "module", "exports": {".": {"types": "./dist/index.d.ts", "import": "./dist/index.mjs", "require": "./dist/index.cjs"}}, "scripts": {"build:dev": "tsc", "build": "tsup", "test": "cucumber-js", "docs": "typedoc"}, "packageManager": "pnpm@8.15.9", "packageConfig": {"access": "public"}, "devDependencies": {"@cucumber/cucumber": "9.0.0", "@types/node": "24.0.12", "tsup": "8.5.0", "typedoc": "^0.28.7", "typescript": "5.8.3"}}