@relyio/sdk 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +400 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/{chunk-DY2XT7OF.cjs → chunk-72UK5U7T.cjs} +70 -60
- package/dist/{chunk-I3ESNANB.js → chunk-H6IBQSMM.js} +2 -2
- package/dist/{chunk-7ABNXVWL.js → chunk-QFHVF3YF.js} +1617 -404
- package/dist/{chunk-HT6NFRM2.cjs → chunk-TZL374LS.cjs} +2 -2
- package/dist/{chunk-GCMQUM6N.cjs → chunk-XIE7WJV3.cjs} +1621 -407
- package/dist/{chunk-XJVY2CLY.js → chunk-YEJUMW4X.js} +70 -60
- package/dist/client/index.cjs +4 -3
- package/dist/client/index.d.cts +5 -4
- package/dist/client/index.d.ts +5 -4
- package/dist/client/index.js +2 -1
- package/dist/{index-d1Kz1e19.d.cts → define-content-type-cEGjIwT5.d.cts} +1859 -733
- package/dist/{index-yPbhZc6q.d.ts → define-content-type-cEGjIwT5.d.ts} +1859 -733
- package/dist/{define-schema-1kr2HIzN.d.cts → define-schema-BPj2srgh.d.ts} +189 -85
- package/dist/{define-schema-ChvAzY8H.d.ts → define-schema-DgbJ5Kwn.d.cts} +189 -85
- package/dist/index-B_rIWO_l.d.cts +860 -0
- package/dist/index-Cv4tmcrw.d.ts +860 -0
- package/dist/{index-Df1VsfJR.d.cts → index-DBoPPlT1.d.cts} +684 -277
- package/dist/{index-XqoGUaAY.d.ts → index-yRJ2IqM_.d.ts} +684 -277
- package/dist/index.cjs +148 -124
- package/dist/index.d.cts +174 -64
- package/dist/index.d.ts +174 -64
- package/dist/index.js +43 -18
- package/dist/schema/index.cjs +35 -35
- package/dist/schema/index.d.cts +13 -11
- package/dist/schema/index.d.ts +13 -11
- package/dist/schema/index.js +1 -1
- package/dist/types/index.d.cts +2 -2
- package/dist/types/index.d.ts +2 -2
- package/dist/utils/index.cjs +19 -19
- package/dist/utils/index.d.cts +10 -1
- package/dist/utils/index.d.ts +10 -1
- package/dist/utils/index.js +1 -1
- package/package.json +2 -1
- package/dist/define-content-type-CDB3kyeA.d.cts +0 -1173
- package/dist/define-content-type-CDB3kyeA.d.ts +0 -1173
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parseDevEnv, DEV_CONTEXT_DIR, DEV_CONTEXT_FILENAME, parseDevContextFile } from './chunk-BBUB7C2Z.js';
|
|
2
|
+
import { isBrowser } from './chunk-H6IBQSMM.js';
|
|
2
3
|
import { __name, __publicField } from './chunk-XQ4QHWZE.js';
|
|
3
4
|
|
|
4
5
|
/* RelyIO SDK - Built with ❤️ by the RelyIO Team */
|
|
@@ -230,18 +231,6 @@ __name(_UnknownContractError, "UnknownContractError");
|
|
|
230
231
|
var UnknownContractError = _UnknownContractError;
|
|
231
232
|
|
|
232
233
|
// src/errors/auth-errors.ts
|
|
233
|
-
var _AuthenticationError = class _AuthenticationError extends SDKError {
|
|
234
|
-
constructor(message = "Authentication failed", statusCode = 401, cause, context) {
|
|
235
|
-
super(message, statusCode, cause, context);
|
|
236
|
-
__publicField(this, "code", "AUTHENTICATION_ERROR");
|
|
237
|
-
__publicField(this, "retryable", false);
|
|
238
|
-
}
|
|
239
|
-
getUserMessage() {
|
|
240
|
-
return "Authentication failed. Please check your API key and try again.";
|
|
241
|
-
}
|
|
242
|
-
};
|
|
243
|
-
__name(_AuthenticationError, "AuthenticationError");
|
|
244
|
-
var AuthenticationError = _AuthenticationError;
|
|
245
234
|
var _AuthorizationError = class _AuthorizationError extends SDKError {
|
|
246
235
|
constructor(message = "Authorization failed", statusCode = 403, cause, context) {
|
|
247
236
|
super(message, statusCode, cause, context);
|
|
@@ -420,58 +409,45 @@ var _InvalidPayloadError = class _InvalidPayloadError extends BaseValidationErro
|
|
|
420
409
|
};
|
|
421
410
|
__name(_InvalidPayloadError, "InvalidPayloadError");
|
|
422
411
|
var InvalidPayloadError = _InvalidPayloadError;
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
412
|
+
|
|
413
|
+
// ../../platform/api-contract/dist/chunk-PYZCLRXR.js
|
|
414
|
+
function defineRoute(route) {
|
|
415
|
+
if (route.kind === "raw" && route.standardShape === void 0) {
|
|
416
|
+
throw new Error("kind 'raw' requires a named StandardShape");
|
|
427
417
|
}
|
|
428
|
-
|
|
429
|
-
|
|
418
|
+
if (route.kind === "raw" && route.success === 302 && !route.headers.some((header) => header.name === "Location")) {
|
|
419
|
+
throw new Error("a raw route declaring success 302 must declare its 'Location' header");
|
|
430
420
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
var SchemaValidationError = _SchemaValidationError;
|
|
434
|
-
var _RequiredFieldError = class _RequiredFieldError extends BaseValidationError {
|
|
435
|
-
constructor(fieldName, message, cause) {
|
|
436
|
-
const violation = {
|
|
437
|
-
field: fieldName,
|
|
438
|
-
message: message ?? `Field "${fieldName}" is required`,
|
|
439
|
-
code: "REQUIRED"
|
|
440
|
-
};
|
|
441
|
-
super(`Required field "${fieldName}" is missing`, [violation], 400, cause);
|
|
442
|
-
__publicField(this, "code", "REQUIRED_FIELD_ERROR");
|
|
421
|
+
if (route.kind === "stream" && route.stream === void 0) {
|
|
422
|
+
throw new Error("kind 'stream' requires a StreamDeclaration");
|
|
443
423
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
const fieldName = violation?.field;
|
|
447
|
-
return `The field "${fieldName}" is required and cannot be empty.`;
|
|
424
|
+
if (route.kind === "collection" && route.page === void 0) {
|
|
425
|
+
throw new Error("kind 'collection' requires a page dialect");
|
|
448
426
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
`
|
|
462
|
-
[violation],
|
|
463
|
-
400,
|
|
464
|
-
cause
|
|
427
|
+
if ((route.kind === "object" || route.kind === "collection") && route.negotiated !== void 0) {
|
|
428
|
+
refuseMismatchedAlternate(route, route.negotiated["text/event-stream"]);
|
|
429
|
+
}
|
|
430
|
+
return route;
|
|
431
|
+
}
|
|
432
|
+
__name(defineRoute, "defineRoute");
|
|
433
|
+
function refuseMismatchedAlternate(route, stream) {
|
|
434
|
+
if (stream.kind !== "stream") {
|
|
435
|
+
throw new Error("a negotiated 'text/event-stream' alternate must be kind 'stream'");
|
|
436
|
+
}
|
|
437
|
+
if (stream.stream.mediaType !== "text/event-stream") {
|
|
438
|
+
throw new Error(
|
|
439
|
+
`a negotiated 'text/event-stream' alternate declares media type "${stream.stream.mediaType}"`
|
|
465
440
|
);
|
|
466
|
-
__publicField(this, "code", "INVALID_FIELD_TYPE");
|
|
467
441
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
442
|
+
const own = [...new Set(route.codes)].sort();
|
|
443
|
+
const alternate = [...new Set(stream.codes)].sort();
|
|
444
|
+
if (own.length !== alternate.length || own.some((code, index) => code !== alternate[index])) {
|
|
445
|
+
throw new Error(
|
|
446
|
+
`a negotiated alternate refuses with [${alternate.join(", ")}] while its route refuses with [${own.join(", ")}]; one route refuses with one set of codes on every transport`
|
|
447
|
+
);
|
|
471
448
|
}
|
|
472
|
-
}
|
|
473
|
-
__name(
|
|
474
|
-
var InvalidFieldTypeError = _InvalidFieldTypeError;
|
|
449
|
+
}
|
|
450
|
+
__name(refuseMismatchedAlternate, "refuseMismatchedAlternate");
|
|
475
451
|
|
|
476
452
|
// ../../platform/api-contract/dist/index.js
|
|
477
453
|
var HEADER_RETRY_AFTER = { name: "Retry-After" };
|
|
@@ -480,6 +456,169 @@ var HEADER_IF_MATCH = {
|
|
|
480
456
|
name: "If-Match"};
|
|
481
457
|
var HEADER_IDEMPOTENCY_KEY = {
|
|
482
458
|
name: "Idempotency-Key"};
|
|
459
|
+
var FRESH_PARAMETER = "_fresh";
|
|
460
|
+
var VISITOR_LIVE_PATH = "/visitor/changes";
|
|
461
|
+
var VISITOR_QUERY = {
|
|
462
|
+
/** The publishable key. */
|
|
463
|
+
key: "key",
|
|
464
|
+
/** A dataset id or name the key reaches; absent, the key's default dataset. */
|
|
465
|
+
dataset: "dataset",
|
|
466
|
+
/** The cursor to continue from; absent, the socket starts now. */
|
|
467
|
+
since: "since",
|
|
468
|
+
/** Comma-separated content types to keep; absent keeps every type. */
|
|
469
|
+
types: "types",
|
|
470
|
+
/** Comma-separated entry ids to keep; absent keeps every entry. */
|
|
471
|
+
ids: "ids",
|
|
472
|
+
/** The poll's client id. Required on a poll, and not read on a socket. */
|
|
473
|
+
client: "client"
|
|
474
|
+
};
|
|
475
|
+
var VISITOR_CURSOR = /^[\w.~-]{1,256}$/;
|
|
476
|
+
var VISITOR_POLL_INTERVAL_MS = 15e3;
|
|
477
|
+
var VISITOR_KEEPALIVE = { request: "ping", response: "pong" };
|
|
478
|
+
var VISITOR_FRAMES = {
|
|
479
|
+
opened: "feed.opened",
|
|
480
|
+
changes: "feed.changes",
|
|
481
|
+
resync: "feed.resync"
|
|
482
|
+
};
|
|
483
|
+
defineRoute({
|
|
484
|
+
kind: "object",
|
|
485
|
+
headers: [HEADER_RETRY_AFTER],
|
|
486
|
+
codes: [
|
|
487
|
+
"REQUEST_INVALID",
|
|
488
|
+
"MISSING_API_KEY",
|
|
489
|
+
"INVALID_API_KEY",
|
|
490
|
+
"ACCESS_DENIED",
|
|
491
|
+
"METHOD_NOT_ALLOWED",
|
|
492
|
+
"SERVICE_INDETERMINATE",
|
|
493
|
+
"USAGE_QUOTA_EXCEEDED",
|
|
494
|
+
"RATE_LIMIT_EXCEEDED"
|
|
495
|
+
]
|
|
496
|
+
});
|
|
497
|
+
var VISITOR_CLOSE_CODES = {
|
|
498
|
+
/** A query the door cannot read: a malformed `since`, filter, `dataset` or `client`. */
|
|
499
|
+
requestInvalid: 4400,
|
|
500
|
+
/** No publishable key, or one that is unknown, revoked, expired, a secret key or a preview key. */
|
|
501
|
+
keyRefused: 4401,
|
|
502
|
+
/** The page's `Origin` is absent, or not on the key's allowed origins. */
|
|
503
|
+
originRefused: 4403,
|
|
504
|
+
/** `dataset` names nothing the key reaches. */
|
|
505
|
+
datasetRefused: 4404,
|
|
506
|
+
/** The key's scope, the project's counter or the feed could not be reached just now. */
|
|
507
|
+
unavailable: 1013,
|
|
508
|
+
/**
|
|
509
|
+
* The project holds as many visitor connections as its plan allows — Starter's 100 — or its
|
|
510
|
+
* Organization is suspended. As HTTP's 402: more needs a bigger plan, or visitors leaving.
|
|
511
|
+
*/
|
|
512
|
+
allowanceReached: 4402,
|
|
513
|
+
/** This client's address holds as many connections to the project as one address may. */
|
|
514
|
+
addressLimitReached: 4429,
|
|
515
|
+
/** The project is at its per-project safety ceiling — Pro's 50,000 — whatever it would pay for. */
|
|
516
|
+
ceilingReached: 4503
|
|
517
|
+
};
|
|
518
|
+
var VISITOR_CLOSE_RETRY = {
|
|
519
|
+
4400: "stop",
|
|
520
|
+
4401: "stop",
|
|
521
|
+
4403: "stop",
|
|
522
|
+
4404: "stop",
|
|
523
|
+
1013: "retry",
|
|
524
|
+
4402: "back-off",
|
|
525
|
+
4429: "back-off",
|
|
526
|
+
4503: "back-off"
|
|
527
|
+
};
|
|
528
|
+
var VISITOR_POLL_RETRY = {
|
|
529
|
+
REQUEST_INVALID: "stop",
|
|
530
|
+
MISSING_API_KEY: "stop",
|
|
531
|
+
INVALID_API_KEY: "stop",
|
|
532
|
+
ACCESS_DENIED: "stop",
|
|
533
|
+
METHOD_NOT_ALLOWED: "stop",
|
|
534
|
+
SERVICE_INDETERMINATE: "retry",
|
|
535
|
+
USAGE_QUOTA_EXCEEDED: "back-off",
|
|
536
|
+
RATE_LIMIT_EXCEEDED: "back-off"
|
|
537
|
+
};
|
|
538
|
+
var VISITOR_ADMISSION_RETRY_AFTER_SECONDS = 60;
|
|
539
|
+
function parseVisitorFrame(text) {
|
|
540
|
+
let value;
|
|
541
|
+
try {
|
|
542
|
+
value = JSON.parse(text);
|
|
543
|
+
} catch {
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
if (!isRecord2(value) || !isCursor(value.cursor)) return null;
|
|
547
|
+
switch (value.type) {
|
|
548
|
+
case VISITOR_FRAMES.opened: {
|
|
549
|
+
return hasOnly(value, ["type", "cursor"]) ? { type: VISITOR_FRAMES.opened, cursor: value.cursor } : null;
|
|
550
|
+
}
|
|
551
|
+
case VISITOR_FRAMES.changes: {
|
|
552
|
+
const changes = hasOnly(value, ["type", "changes", "cursor"]) ? readVisitorChanges(value.changes) : null;
|
|
553
|
+
return changes === null ? null : { type: VISITOR_FRAMES.changes, changes, cursor: value.cursor };
|
|
554
|
+
}
|
|
555
|
+
case VISITOR_FRAMES.resync: {
|
|
556
|
+
return hasOnly(value, ["type", "reason", "cursor"]) && isResyncReason(value.reason) ? { type: VISITOR_FRAMES.resync, reason: value.reason, cursor: value.cursor } : null;
|
|
557
|
+
}
|
|
558
|
+
default: {
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
__name(parseVisitorFrame, "parseVisitorFrame");
|
|
564
|
+
function readVisitorPollAnswer(value) {
|
|
565
|
+
if (!isRecord2(value) || !isCursor(value.cursor)) return null;
|
|
566
|
+
if (value.kind === "resync") {
|
|
567
|
+
return hasOnly(value, ["kind", "reason", "cursor"]) && isResyncReason(value.reason) ? { kind: "resync", reason: value.reason, cursor: value.cursor } : null;
|
|
568
|
+
}
|
|
569
|
+
if (value.kind !== "changes" || !hasOnly(value, ["kind", "changes", "cursor"])) return null;
|
|
570
|
+
const changes = readVisitorChanges(value.changes);
|
|
571
|
+
return changes === null ? null : { kind: "changes", changes, cursor: value.cursor };
|
|
572
|
+
}
|
|
573
|
+
__name(readVisitorPollAnswer, "readVisitorPollAnswer");
|
|
574
|
+
var CHANGE_KINDS = /* @__PURE__ */ new Set([
|
|
575
|
+
"published",
|
|
576
|
+
"updated",
|
|
577
|
+
"unpublished",
|
|
578
|
+
"deleted"
|
|
579
|
+
]);
|
|
580
|
+
var RESYNC_REASONS = /* @__PURE__ */ new Set([
|
|
581
|
+
"retention",
|
|
582
|
+
"dataset-replaced",
|
|
583
|
+
"ahead-of-head",
|
|
584
|
+
"foreign-cursor",
|
|
585
|
+
"too-far-behind"
|
|
586
|
+
]);
|
|
587
|
+
function isResyncReason(value) {
|
|
588
|
+
return typeof value === "string" && RESYNC_REASONS.has(value);
|
|
589
|
+
}
|
|
590
|
+
__name(isResyncReason, "isResyncReason");
|
|
591
|
+
function readVisitorChanges(value) {
|
|
592
|
+
if (!Array.isArray(value)) return null;
|
|
593
|
+
const changes = [];
|
|
594
|
+
for (const item of value) {
|
|
595
|
+
if (!isRecord2(item) || !hasOnly(item, ["entryId", "contentType", "locale", "kind"])) return null;
|
|
596
|
+
const { entryId, contentType, locale, kind } = item;
|
|
597
|
+
if (!isNonEmptyString2(entryId) || !isNonEmptyString2(contentType)) return null;
|
|
598
|
+
if (locale !== null && typeof locale !== "string") return null;
|
|
599
|
+
if (typeof kind !== "string" || !CHANGE_KINDS.has(kind)) return null;
|
|
600
|
+
changes.push({ entryId, contentType, locale, kind });
|
|
601
|
+
}
|
|
602
|
+
return changes;
|
|
603
|
+
}
|
|
604
|
+
__name(readVisitorChanges, "readVisitorChanges");
|
|
605
|
+
function isCursor(value) {
|
|
606
|
+
return typeof value === "string" && VISITOR_CURSOR.test(value);
|
|
607
|
+
}
|
|
608
|
+
__name(isCursor, "isCursor");
|
|
609
|
+
function isRecord2(value) {
|
|
610
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
611
|
+
}
|
|
612
|
+
__name(isRecord2, "isRecord2");
|
|
613
|
+
function isNonEmptyString2(value) {
|
|
614
|
+
return typeof value === "string" && value.length > 0;
|
|
615
|
+
}
|
|
616
|
+
__name(isNonEmptyString2, "isNonEmptyString2");
|
|
617
|
+
function hasOnly(value, keys) {
|
|
618
|
+
const present = Object.keys(value);
|
|
619
|
+
return present.length === keys.length && present.every((key) => keys.includes(key));
|
|
620
|
+
}
|
|
621
|
+
__name(hasOnly, "hasOnly");
|
|
483
622
|
|
|
484
623
|
// ../../platform/api-contract/dist/client/index.js
|
|
485
624
|
var _a;
|
|
@@ -748,6 +887,8 @@ var DECLARED_PATHS = {
|
|
|
748
887
|
"/health/cache": true,
|
|
749
888
|
"/health/live": true,
|
|
750
889
|
"/health/ready": true,
|
|
890
|
+
"/internal/live/visitor-feed/{projectId}/{datasetId}/changes": true,
|
|
891
|
+
"/internal/live/visitor-key": true,
|
|
751
892
|
"/internal/media/claim": true,
|
|
752
893
|
"/internal/media/file-complete": true,
|
|
753
894
|
"/internal/media/progress": true,
|
|
@@ -871,6 +1012,20 @@ function isAborted(signal) {
|
|
|
871
1012
|
}
|
|
872
1013
|
__name(isAborted, "isAborted");
|
|
873
1014
|
|
|
1015
|
+
// src/http/request-timeout.ts
|
|
1016
|
+
var MAX_TIMER_DELAY_MS = 2147483647;
|
|
1017
|
+
function isTimerDelay(value) {
|
|
1018
|
+
return typeof value === "number" && value >= 1 && value <= MAX_TIMER_DELAY_MS;
|
|
1019
|
+
}
|
|
1020
|
+
__name(isTimerDelay, "isTimerDelay");
|
|
1021
|
+
function assertRequestTimeout(timeout) {
|
|
1022
|
+
if (isTimerDelay(timeout)) return;
|
|
1023
|
+
throw new Error(
|
|
1024
|
+
`timeout must be a number of milliseconds from 1 to ${MAX_TIMER_DELAY_MS} \u2014 a timer given 0, NaN, Infinity or more fires at once and ends every request \u2014 and got ${String(timeout)}`
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
__name(assertRequestTimeout, "assertRequestTimeout");
|
|
1028
|
+
|
|
874
1029
|
// src/errors/instructed-delay.ts
|
|
875
1030
|
function serviceInstructedDelayMs(error, nowMs) {
|
|
876
1031
|
if (error instanceof ServiceIndeterminateError) {
|
|
@@ -924,43 +1079,31 @@ var DefaultRetryPolicy = _DefaultRetryPolicy;
|
|
|
924
1079
|
// src/http/http-client.ts
|
|
925
1080
|
var _HttpClient = class _HttpClient {
|
|
926
1081
|
constructor(config) {
|
|
927
|
-
|
|
928
|
-
__publicField(this, "
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
}
|
|
948
|
-
if (typeof window === "undefined") {
|
|
949
|
-
const globalWithVersion = globalThis;
|
|
950
|
-
this.defaultHeaders["User-Agent"] = `RelyIO-SDK/${globalWithVersion.__VERSION__ ?? "0.0.0"}`;
|
|
951
|
-
}
|
|
952
|
-
this.retryPolicy = new DefaultRetryPolicy({
|
|
953
|
-
maxAttempts: config.retryAttempts,
|
|
954
|
-
baseDelay: 1e3,
|
|
955
|
-
maxDelay: 1e4,
|
|
956
|
-
backoffFactor: 2,
|
|
957
|
-
jitter: true
|
|
958
|
-
});
|
|
1082
|
+
/** What every request reads off the configuration, derived by {@link configure} and nowhere else. */
|
|
1083
|
+
__publicField(this, "connection");
|
|
1084
|
+
this.connection = connectionFor(config);
|
|
1085
|
+
this.announce();
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* Run on `config` from now on: every later request — whoever holds this client — is sent to its
|
|
1089
|
+
* `baseUrl`, with its headers, key and dataset, its timeout and retries, through its `fetch`, and
|
|
1090
|
+
* logged by its `debug` options.
|
|
1091
|
+
*
|
|
1092
|
+
* The client's own `updateConfig()` calls this rather than building a second client, so a
|
|
1093
|
+
* `content()` or other sub-client taken before the update sends through the update too. Anything
|
|
1094
|
+
* set on this client since it was last configured — `setDefaultHeaders()`, `setBaseUrl()` — is
|
|
1095
|
+
* replaced by what `config` says.
|
|
1096
|
+
*/
|
|
1097
|
+
configure(config) {
|
|
1098
|
+
this.connection = connectionFor(config);
|
|
1099
|
+
this.announce();
|
|
1100
|
+
}
|
|
1101
|
+
announce() {
|
|
959
1102
|
this.log("debug", "HttpClient initialized", {
|
|
960
|
-
baseUrl: this.baseUrl,
|
|
961
|
-
timeout: this.timeout,
|
|
962
|
-
retryAttempts: config.retryAttempts,
|
|
963
|
-
environment:
|
|
1103
|
+
baseUrl: this.connection.baseUrl,
|
|
1104
|
+
timeout: this.connection.timeout,
|
|
1105
|
+
retryAttempts: this.connection.config.retryAttempts,
|
|
1106
|
+
environment: isBrowser() ? "browser" : "node"
|
|
964
1107
|
});
|
|
965
1108
|
}
|
|
966
1109
|
/**
|
|
@@ -997,19 +1140,24 @@ var _HttpClient = class _HttpClient {
|
|
|
997
1140
|
*
|
|
998
1141
|
* The loop always makes one attempt. The incumbent's `while (attempt <
|
|
999
1142
|
* maxAttempts)` made none at `retryAttempts: 0` — a value the config validator
|
|
1000
|
-
*
|
|
1143
|
+
* then accepted, and now refuses — and then dereferenced an unassigned
|
|
1144
|
+
* `lastError`. `retryAttempts` is the most attempts, the first included; an
|
|
1145
|
+
* `HttpClient` constructed directly is not validated, so the first attempt
|
|
1146
|
+
* stays unconditional.
|
|
1001
1147
|
*
|
|
1002
1148
|
* A request that would carry no `X-API-Key` makes none: it rejects with
|
|
1003
1149
|
* `MissingApiKeyError` before `fetch` is called, is not retried, and names
|
|
1004
1150
|
* `apiKey`. This is where a client built without a key learns it — the
|
|
1005
1151
|
* constructor accepts one so that a module-scope client can be evaluated by a
|
|
1006
|
-
* build that holds no key (REL-1120)
|
|
1152
|
+
* build that holds no key (REL-1120) — and so does one whose `setApiKey` was
|
|
1153
|
+
* given none.
|
|
1007
1154
|
*/
|
|
1008
1155
|
async request(config) {
|
|
1156
|
+
if (config.timeout !== void 0) assertRequestTimeout(config.timeout);
|
|
1009
1157
|
const mergedConfig = this.mergeRequestConfig(config);
|
|
1010
1158
|
if (presentKey(headerValue(mergedConfig.headers, API_KEY_HEADER)) === void 0) {
|
|
1011
1159
|
throw new MissingApiKeyError(
|
|
1012
|
-
"`apiKey` is missing: this client
|
|
1160
|
+
"`apiKey` is missing: this client holds no API key, so the request was not sent. Pass `apiKey` to createClient \u2014 the documented setup reads it from RELYIO_API_KEY.",
|
|
1013
1161
|
{ url: mergedConfig.url, method: mergedConfig.method, sent: false }
|
|
1014
1162
|
);
|
|
1015
1163
|
}
|
|
@@ -1019,7 +1167,7 @@ var _HttpClient = class _HttpClient {
|
|
|
1019
1167
|
// Show the final URL with query params
|
|
1020
1168
|
});
|
|
1021
1169
|
if (config.method === "DELETE" && mergedConfig.headers) {
|
|
1022
|
-
|
|
1170
|
+
mergedConfig.headers = layerHeaders(mergedConfig.headers, { "Content-Type": null });
|
|
1023
1171
|
}
|
|
1024
1172
|
for (let attempt = 1; ; attempt++) {
|
|
1025
1173
|
try {
|
|
@@ -1038,8 +1186,8 @@ var _HttpClient = class _HttpClient {
|
|
|
1038
1186
|
attempt,
|
|
1039
1187
|
url: mergedConfig.url
|
|
1040
1188
|
});
|
|
1041
|
-
if (this.retryPolicy.shouldRetry(attempt, failure, mergedConfig)) {
|
|
1042
|
-
const delay = this.retryPolicy.delayFor(attempt, failure);
|
|
1189
|
+
if (this.connection.retryPolicy.shouldRetry(attempt, failure, mergedConfig)) {
|
|
1190
|
+
const delay = this.connection.retryPolicy.delayFor(attempt, failure);
|
|
1043
1191
|
this.log("debug", "Retrying request", { attempt, delay });
|
|
1044
1192
|
await sleep(delay, config.signal);
|
|
1045
1193
|
continue;
|
|
@@ -1111,34 +1259,60 @@ var _HttpClient = class _HttpClient {
|
|
|
1111
1259
|
});
|
|
1112
1260
|
}
|
|
1113
1261
|
/**
|
|
1114
|
-
*
|
|
1262
|
+
* Add headers to every later request, each replacing any header of the same name — the name
|
|
1263
|
+
* matched without regard to case — until the client is next configured.
|
|
1115
1264
|
*/
|
|
1116
1265
|
setDefaultHeaders(headers) {
|
|
1117
|
-
this.
|
|
1266
|
+
this.connection = {
|
|
1267
|
+
...this.connection,
|
|
1268
|
+
defaultHeaders: layerHeaders(this.connection.defaultHeaders, headers)
|
|
1269
|
+
};
|
|
1118
1270
|
}
|
|
1119
1271
|
/**
|
|
1120
|
-
*
|
|
1272
|
+
* Replace the key every later request sends as `X-API-Key`, and the `apiKey` the configuration
|
|
1273
|
+
* reports.
|
|
1274
|
+
*
|
|
1275
|
+
* The key is read the way construction reads it (`presentKey`): an absent, empty or blank key is
|
|
1276
|
+
* no key, so the header is dropped and the next request is refused with `MissingApiKeyError`
|
|
1277
|
+
* before anything is sent. A key header of any other spelling — one given in `headers` — goes
|
|
1278
|
+
* too, from the headers sent and from the headers the configuration reports, so the old key is
|
|
1279
|
+
* never sent beside the new one.
|
|
1280
|
+
*
|
|
1281
|
+
* The configuration is the one the owning `TypedRelyIOClient` holds (it hands its own to the
|
|
1282
|
+
* constructor), so its `getConfig().apiKey` — what a surface reads to decide a key's kind — is the
|
|
1283
|
+
* key sent from here on.
|
|
1284
|
+
*
|
|
1285
|
+
* This replaced `setAuthToken`, which added `Authorization: Bearer` beside an `X-API-Key` it
|
|
1286
|
+
* never touched: the API reads `X-API-Key` first, so the old key was the one used (REL-1385).
|
|
1121
1287
|
*/
|
|
1122
|
-
|
|
1123
|
-
|
|
1288
|
+
setApiKey(apiKey) {
|
|
1289
|
+
const { config } = this.connection;
|
|
1290
|
+
config.apiKey = apiKey;
|
|
1291
|
+
config.headers = layerHeaders(config.headers, { [API_KEY_HEADER]: null });
|
|
1292
|
+
this.connection = {
|
|
1293
|
+
...this.connection,
|
|
1294
|
+
defaultHeaders: layerHeaders(this.connection.defaultHeaders, {
|
|
1295
|
+
[API_KEY_HEADER]: presentKey(apiKey) ?? null
|
|
1296
|
+
})
|
|
1297
|
+
};
|
|
1124
1298
|
}
|
|
1125
1299
|
/**
|
|
1126
1300
|
* Get current default headers
|
|
1127
1301
|
*/
|
|
1128
1302
|
getDefaultHeaders() {
|
|
1129
|
-
return { ...this.defaultHeaders };
|
|
1303
|
+
return { ...this.connection.defaultHeaders };
|
|
1130
1304
|
}
|
|
1131
1305
|
/**
|
|
1132
1306
|
* Update base URL
|
|
1133
1307
|
*/
|
|
1134
1308
|
setBaseUrl(baseUrl) {
|
|
1135
|
-
this.
|
|
1309
|
+
this.connection = { ...this.connection, baseUrl };
|
|
1136
1310
|
}
|
|
1137
1311
|
/**
|
|
1138
1312
|
* Get current base URL
|
|
1139
1313
|
*/
|
|
1140
1314
|
getBaseUrl() {
|
|
1141
|
-
return this.baseUrl;
|
|
1315
|
+
return this.connection.baseUrl;
|
|
1142
1316
|
}
|
|
1143
1317
|
/**
|
|
1144
1318
|
* One attempt.
|
|
@@ -1160,7 +1334,7 @@ var _HttpClient = class _HttpClient {
|
|
|
1160
1334
|
const timeoutId = setTimeout(() => {
|
|
1161
1335
|
timedOut = true;
|
|
1162
1336
|
controller.abort();
|
|
1163
|
-
}, config.timeout ?? this.timeout);
|
|
1337
|
+
}, config.timeout ?? this.connection.timeout);
|
|
1164
1338
|
const onCallerAbort = /* @__PURE__ */ __name(() => controller.abort(), "onCallerAbort");
|
|
1165
1339
|
caller?.addEventListener("abort", onCallerAbort, { once: true });
|
|
1166
1340
|
try {
|
|
@@ -1170,14 +1344,15 @@ var _HttpClient = class _HttpClient {
|
|
|
1170
1344
|
headers: filteredHeaders,
|
|
1171
1345
|
body: this.prepareRequestBody(config),
|
|
1172
1346
|
signal: controller.signal,
|
|
1173
|
-
// Only
|
|
1174
|
-
// govern, so the field is left off and the
|
|
1175
|
-
//
|
|
1176
|
-
|
|
1177
|
-
|
|
1347
|
+
// Only in a browser — a `window` with a `document` (`isBrowser()`). A server or edge
|
|
1348
|
+
// runtime has no cookie jar for the mode to govern, so the field is left off and the
|
|
1349
|
+
// runtime's default stands; that includes Deno 1.x, whose global is a `window` with no
|
|
1350
|
+
// `document`. A Web Worker gets that default too: it has cookies but no `window`.
|
|
1351
|
+
...isBrowser() && {
|
|
1352
|
+
credentials: credentialsModeFor(this.connection.config.browser.includeCredentials)
|
|
1178
1353
|
}
|
|
1179
1354
|
};
|
|
1180
|
-
const send = this.config.fetch ?? globalThis.fetch;
|
|
1355
|
+
const send = this.connection.config.fetch ?? globalThis.fetch;
|
|
1181
1356
|
const response = await send(url, requestInit);
|
|
1182
1357
|
clearTimeout(timeoutId);
|
|
1183
1358
|
const responseBody = await this.parseResponse(response);
|
|
@@ -1210,10 +1385,8 @@ var _HttpClient = class _HttpClient {
|
|
|
1210
1385
|
}
|
|
1211
1386
|
if (error instanceof DOMException && error.name === "AbortError") {
|
|
1212
1387
|
if (!timedOut && isAborted(caller)) throw error;
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
config.timeout ?? this.timeout
|
|
1216
|
-
);
|
|
1388
|
+
const timeout = config.timeout ?? this.connection.timeout;
|
|
1389
|
+
throw new TimeoutError(`Request timed out after ${timeout}ms`, timeout);
|
|
1217
1390
|
}
|
|
1218
1391
|
if (error instanceof TypeError && error.message.includes("CORS")) {
|
|
1219
1392
|
const corsError = new NetworkError(
|
|
@@ -1227,9 +1400,10 @@ var _HttpClient = class _HttpClient {
|
|
|
1227
1400
|
suggestion: "Check if the API server allows requests from your domain and has proper CORS headers configured."
|
|
1228
1401
|
}
|
|
1229
1402
|
);
|
|
1230
|
-
|
|
1403
|
+
const { onCorsError } = this.connection.config.browser;
|
|
1404
|
+
if (isBrowser() && onCorsError) {
|
|
1231
1405
|
try {
|
|
1232
|
-
|
|
1406
|
+
onCorsError(corsError, {
|
|
1233
1407
|
url: config.url,
|
|
1234
1408
|
method: config.method,
|
|
1235
1409
|
originalError: error
|
|
@@ -1246,20 +1420,20 @@ var _HttpClient = class _HttpClient {
|
|
|
1246
1420
|
}
|
|
1247
1421
|
}
|
|
1248
1422
|
/**
|
|
1249
|
-
*
|
|
1423
|
+
* The headers without those a browser controls itself, when this is a browser; unchanged when it
|
|
1424
|
+
* is not.
|
|
1425
|
+
*
|
|
1426
|
+
* A name is matched without regard to case (RFC 9110 §5.1), because a browser refuses `Origin`
|
|
1427
|
+
* whatever case a page spells it in. This matched only the all-lowercase and all-uppercase
|
|
1428
|
+
* spellings, so `Origin` and `User-Agent` — how people write them — reached `fetch` (REL-1409).
|
|
1250
1429
|
*/
|
|
1251
1430
|
filterHeadersForBrowser(headers) {
|
|
1252
|
-
if (
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
delete filteredHeaders[header.toUpperCase()];
|
|
1259
|
-
});
|
|
1260
|
-
return filteredHeaders;
|
|
1261
|
-
}
|
|
1262
|
-
return headers;
|
|
1431
|
+
if (!isBrowser()) return headers;
|
|
1432
|
+
return Object.fromEntries(
|
|
1433
|
+
Object.entries(headers).filter(
|
|
1434
|
+
([name]) => !BROWSER_CONTROLLED_HEADERS.has(name.toLowerCase())
|
|
1435
|
+
)
|
|
1436
|
+
);
|
|
1263
1437
|
}
|
|
1264
1438
|
/**
|
|
1265
1439
|
* Prepare request body based on content type and environment
|
|
@@ -1295,7 +1469,7 @@ var _HttpClient = class _HttpClient {
|
|
|
1295
1469
|
if (url.startsWith("http://") || url.startsWith("https://")) {
|
|
1296
1470
|
return url;
|
|
1297
1471
|
}
|
|
1298
|
-
return `${this.baseUrl}${url.startsWith("/") ? "" : "/"}${url}`;
|
|
1472
|
+
return `${this.connection.baseUrl}${url.startsWith("/") ? "" : "/"}${url}`;
|
|
1299
1473
|
}
|
|
1300
1474
|
mergeRequestConfig(config) {
|
|
1301
1475
|
const url = this.resolveUrl(config.url);
|
|
@@ -1304,11 +1478,11 @@ var _HttpClient = class _HttpClient {
|
|
|
1304
1478
|
return {
|
|
1305
1479
|
...config,
|
|
1306
1480
|
url: finalUrl,
|
|
1307
|
-
headers:
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
timeout: config.timeout ?? this.timeout
|
|
1481
|
+
// The request's own headers replace the client's of the same name, whatever the case: a
|
|
1482
|
+
// per-request `x-relyio-dataset` sent beside the client's `X-RelyIO-Dataset` is one header
|
|
1483
|
+
// `Headers` joins into `main, staging`.
|
|
1484
|
+
headers: layerHeaders(this.connection.defaultHeaders, config.headers),
|
|
1485
|
+
timeout: config.timeout ?? this.connection.timeout
|
|
1312
1486
|
};
|
|
1313
1487
|
}
|
|
1314
1488
|
buildQueryString(params) {
|
|
@@ -1332,9 +1506,10 @@ var _HttpClient = class _HttpClient {
|
|
|
1332
1506
|
return obj;
|
|
1333
1507
|
}
|
|
1334
1508
|
log(level, message, data) {
|
|
1335
|
-
|
|
1336
|
-
if (
|
|
1337
|
-
|
|
1509
|
+
const { debug } = this.connection.config;
|
|
1510
|
+
if (!debug.enabled) return;
|
|
1511
|
+
if (debug.logger != null) {
|
|
1512
|
+
debug.logger(level, message, data);
|
|
1338
1513
|
} else {
|
|
1339
1514
|
console.log(`[RelyIO SDK ${level.toUpperCase()}] ${message}`, data ?? "");
|
|
1340
1515
|
}
|
|
@@ -1343,29 +1518,104 @@ var _HttpClient = class _HttpClient {
|
|
|
1343
1518
|
__name(_HttpClient, "HttpClient");
|
|
1344
1519
|
var HttpClient = _HttpClient;
|
|
1345
1520
|
var API_KEY_HEADER = "X-API-Key";
|
|
1521
|
+
var DATASET_HEADER = "X-RelyIO-Dataset";
|
|
1522
|
+
var BROWSER_CONTROLLED_HEADERS = /* @__PURE__ */ new Set([
|
|
1523
|
+
"user-agent",
|
|
1524
|
+
"referer",
|
|
1525
|
+
"origin",
|
|
1526
|
+
"host",
|
|
1527
|
+
"connection",
|
|
1528
|
+
"upgrade"
|
|
1529
|
+
]);
|
|
1530
|
+
function connectionFor(config) {
|
|
1531
|
+
const globalWithVersion = globalThis;
|
|
1532
|
+
const defaultHeaders = layerHeaders(
|
|
1533
|
+
layerHeaders({ "Content-Type": "application/json" }, config.headers),
|
|
1534
|
+
{
|
|
1535
|
+
[API_KEY_HEADER]: presentKey(config.apiKey),
|
|
1536
|
+
[DATASET_HEADER]: config.dataset,
|
|
1537
|
+
// Only outside a browser, which controls this header. "A browser" is the core's one
|
|
1538
|
+
// definition, `isBrowser()` — every environment question in this file asks it.
|
|
1539
|
+
"User-Agent": isBrowser() ? void 0 : `RelyIO-SDK/${globalWithVersion.__VERSION__ ?? "0.0.0"}`
|
|
1540
|
+
}
|
|
1541
|
+
);
|
|
1542
|
+
return {
|
|
1543
|
+
config,
|
|
1544
|
+
baseUrl: config.baseUrl,
|
|
1545
|
+
defaultHeaders,
|
|
1546
|
+
timeout: config.timeout,
|
|
1547
|
+
// The policy, not a predicate over `(error, attempt)`: whether a request may be sent again is a
|
|
1548
|
+
// property of the **route** as much as of the refusal, and a callback that never sees the
|
|
1549
|
+
// request cannot express that.
|
|
1550
|
+
retryPolicy: new DefaultRetryPolicy({
|
|
1551
|
+
maxAttempts: config.retryAttempts,
|
|
1552
|
+
baseDelay: 1e3,
|
|
1553
|
+
maxDelay: 1e4,
|
|
1554
|
+
backoffFactor: 2,
|
|
1555
|
+
jitter: true
|
|
1556
|
+
})
|
|
1557
|
+
};
|
|
1558
|
+
}
|
|
1559
|
+
__name(connectionFor, "connectionFor");
|
|
1560
|
+
function layerHeaders(base, over) {
|
|
1561
|
+
const layered = { ...base };
|
|
1562
|
+
for (const [name, value] of Object.entries(over ?? {})) {
|
|
1563
|
+
if (value === void 0) continue;
|
|
1564
|
+
const lower = name.toLowerCase();
|
|
1565
|
+
for (const existing of Object.keys(layered)) {
|
|
1566
|
+
if (existing.toLowerCase() === lower) delete layered[existing];
|
|
1567
|
+
}
|
|
1568
|
+
if (value !== null) layered[name] = value;
|
|
1569
|
+
}
|
|
1570
|
+
return layered;
|
|
1571
|
+
}
|
|
1572
|
+
__name(layerHeaders, "layerHeaders");
|
|
1346
1573
|
function presentKey(value) {
|
|
1347
1574
|
return typeof value === "string" && value.trim() !== "" ? value : void 0;
|
|
1348
1575
|
}
|
|
1349
1576
|
__name(presentKey, "presentKey");
|
|
1350
1577
|
function credentialsModeFor(includeCredentials) {
|
|
1351
|
-
|
|
1352
|
-
case false: {
|
|
1353
|
-
return "same-origin";
|
|
1354
|
-
}
|
|
1355
|
-
case true:
|
|
1356
|
-
case void 0: {
|
|
1357
|
-
return "include";
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1578
|
+
return includeCredentials ? "include" : "same-origin";
|
|
1360
1579
|
}
|
|
1361
1580
|
__name(credentialsModeFor, "credentialsModeFor");
|
|
1362
1581
|
|
|
1582
|
+
// src/content/client-locale.ts
|
|
1583
|
+
function callLocale(named, clientLocale) {
|
|
1584
|
+
if (named !== void 0) return named;
|
|
1585
|
+
return clientLocale === void 0 || clientLocale === "" ? void 0 : clientLocale;
|
|
1586
|
+
}
|
|
1587
|
+
__name(callLocale, "callLocale");
|
|
1588
|
+
function readClientLocale(locale) {
|
|
1589
|
+
return typeof locale === "function" ? locale : () => locale;
|
|
1590
|
+
}
|
|
1591
|
+
__name(readClientLocale, "readClientLocale");
|
|
1592
|
+
|
|
1363
1593
|
// src/content/query-builder.ts
|
|
1594
|
+
function namesMetadata(key) {
|
|
1595
|
+
return key === "_metadata" || key.startsWith("_metadata.");
|
|
1596
|
+
}
|
|
1597
|
+
__name(namesMetadata, "namesMetadata");
|
|
1598
|
+
function refuseMetadataConditions(conditions) {
|
|
1599
|
+
const named = Object.keys(conditions).filter(namesMetadata);
|
|
1600
|
+
if (named.length === 0) return;
|
|
1601
|
+
throw new ValidationError(
|
|
1602
|
+
`A condition cannot name ${named.join(", ")}: the API filters on the fields the content type declares, and reports _metadata beside them. Filter on a field, and read _metadata from the result.`,
|
|
1603
|
+
named.map((field) => ({
|
|
1604
|
+
field,
|
|
1605
|
+
code: "METADATA_CONDITION",
|
|
1606
|
+
message: `${field} is not a field the API can filter on`
|
|
1607
|
+
}))
|
|
1608
|
+
);
|
|
1609
|
+
}
|
|
1610
|
+
__name(refuseMetadataConditions, "refuseMetadataConditions");
|
|
1364
1611
|
var _ContentQueryBuilder = class _ContentQueryBuilder {
|
|
1365
|
-
|
|
1612
|
+
/**
|
|
1613
|
+
* @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), sent when the
|
|
1614
|
+
* query names none: the function `TypedRelyIOClient` hands over, or a locale fixed for it
|
|
1615
|
+
*/
|
|
1616
|
+
constructor(httpClient, contentType, locale) {
|
|
1366
1617
|
this.httpClient = httpClient;
|
|
1367
1618
|
this.contentType = contentType;
|
|
1368
|
-
this.defaultLocale = defaultLocale;
|
|
1369
1619
|
__publicField(this, "queryOptions", {});
|
|
1370
1620
|
__publicField(this, "localeValue");
|
|
1371
1621
|
__publicField(this, "localesArray");
|
|
@@ -1375,6 +1625,21 @@ var _ContentQueryBuilder = class _ContentQueryBuilder {
|
|
|
1375
1625
|
__publicField(this, "pageNumber");
|
|
1376
1626
|
__publicField(this, "pageSizeValue");
|
|
1377
1627
|
__publicField(this, "requestHeaders");
|
|
1628
|
+
/** The client's locale, asked when the query is sent rather than copied when it was built. */
|
|
1629
|
+
__publicField(this, "clientLocale");
|
|
1630
|
+
__publicField(this, "freshValue");
|
|
1631
|
+
this.clientLocale = readClientLocale(locale);
|
|
1632
|
+
}
|
|
1633
|
+
/**
|
|
1634
|
+
* The feed position this read follows — a change's `fresh` (`FeedChange`), or a resync's. Sent as
|
|
1635
|
+
* `?_fresh`, the one term the API declares for it: no cache — the Content API's edge or the
|
|
1636
|
+
* browser's own — holds an answer for that position from before it, so the read is answered after
|
|
1637
|
+
* the change, and every read naming the same position shares that answer. The answer is the
|
|
1638
|
+
* read's own; omit it for a read no change prompted.
|
|
1639
|
+
*/
|
|
1640
|
+
fresh(position) {
|
|
1641
|
+
this.freshValue = position;
|
|
1642
|
+
return this;
|
|
1378
1643
|
}
|
|
1379
1644
|
/**
|
|
1380
1645
|
* Headers this query adds to the client's own, e.g. `X-RelyIO-Dataset` to list another dataset
|
|
@@ -1386,9 +1651,11 @@ var _ContentQueryBuilder = class _ContentQueryBuilder {
|
|
|
1386
1651
|
return this;
|
|
1387
1652
|
}
|
|
1388
1653
|
/**
|
|
1389
|
-
* Add where conditions to the query
|
|
1654
|
+
* Add where conditions to the query: conditions on the fields the content type declares. A
|
|
1655
|
+
* condition on `_metadata` is refused here, before anything is sent (`refuseMetadataConditions`).
|
|
1390
1656
|
*/
|
|
1391
1657
|
where(conditions) {
|
|
1658
|
+
refuseMetadataConditions(conditions);
|
|
1392
1659
|
this.queryOptions.where = {
|
|
1393
1660
|
...this.queryOptions.where,
|
|
1394
1661
|
...conditions
|
|
@@ -1645,7 +1912,11 @@ var _ContentQueryBuilder = class _ContentQueryBuilder {
|
|
|
1645
1912
|
* Clone the query builder
|
|
1646
1913
|
*/
|
|
1647
1914
|
clone() {
|
|
1648
|
-
const cloned = new _ContentQueryBuilder(
|
|
1915
|
+
const cloned = new _ContentQueryBuilder(
|
|
1916
|
+
this.httpClient,
|
|
1917
|
+
this.contentType,
|
|
1918
|
+
this.clientLocale
|
|
1919
|
+
);
|
|
1649
1920
|
cloned.queryOptions = JSON.parse(JSON.stringify(this.queryOptions));
|
|
1650
1921
|
cloned.localeValue = this.localeValue;
|
|
1651
1922
|
cloned.localesArray = this.localesArray ? [...this.localesArray] : void 0;
|
|
@@ -1654,6 +1925,7 @@ var _ContentQueryBuilder = class _ContentQueryBuilder {
|
|
|
1654
1925
|
cloned.pageNumber = this.pageNumber;
|
|
1655
1926
|
cloned.pageSizeValue = this.pageSizeValue;
|
|
1656
1927
|
cloned.requestHeaders = this.requestHeaders ? { ...this.requestHeaders } : void 0;
|
|
1928
|
+
cloned.freshValue = this.freshValue;
|
|
1657
1929
|
return cloned;
|
|
1658
1930
|
}
|
|
1659
1931
|
/**
|
|
@@ -1720,14 +1992,15 @@ var _ContentQueryBuilder = class _ContentQueryBuilder {
|
|
|
1720
1992
|
if (this.queryOptions.cursor !== void 0) {
|
|
1721
1993
|
params.cursor = this.queryOptions.cursor;
|
|
1722
1994
|
}
|
|
1995
|
+
const clientLocale = this.clientLocale();
|
|
1723
1996
|
if (this.localeValue != null && this.localeValue !== "") {
|
|
1724
1997
|
params.locale = this.localeValue;
|
|
1725
1998
|
} else if (this.fetchAllLocales) {
|
|
1726
1999
|
params.locale = "*";
|
|
1727
2000
|
} else if (this.localesArray && this.localesArray.length > 0) {
|
|
1728
2001
|
params.locales = this.localesArray.join(",");
|
|
1729
|
-
} else if (
|
|
1730
|
-
params.locale =
|
|
2002
|
+
} else if (clientLocale != null && clientLocale !== "") {
|
|
2003
|
+
params.locale = clientLocale;
|
|
1731
2004
|
}
|
|
1732
2005
|
if (this.pageNumber !== void 0) {
|
|
1733
2006
|
params.page = this.pageNumber;
|
|
@@ -1745,23 +2018,44 @@ var _ContentQueryBuilder = class _ContentQueryBuilder {
|
|
|
1745
2018
|
params.childrenLimit = this.childrenOptions.limit;
|
|
1746
2019
|
}
|
|
1747
2020
|
}
|
|
2021
|
+
if (this.freshValue !== void 0) {
|
|
2022
|
+
params[FRESH_PARAMETER] = this.freshValue;
|
|
2023
|
+
}
|
|
1748
2024
|
return params;
|
|
1749
2025
|
}
|
|
1750
2026
|
};
|
|
1751
2027
|
__name(_ContentQueryBuilder, "ContentQueryBuilder");
|
|
1752
2028
|
var ContentQueryBuilder = _ContentQueryBuilder;
|
|
1753
2029
|
var _ContentByIdBuilder = class _ContentByIdBuilder {
|
|
1754
|
-
|
|
2030
|
+
/**
|
|
2031
|
+
* @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), sent when the
|
|
2032
|
+
* read names none: the function `TypedRelyIOClient` hands over, or a locale fixed for it
|
|
2033
|
+
*/
|
|
2034
|
+
constructor(httpClient, contentType, id, locale) {
|
|
1755
2035
|
this.httpClient = httpClient;
|
|
1756
2036
|
this.contentType = contentType;
|
|
1757
2037
|
this.id = id;
|
|
1758
|
-
this.defaultLocale = defaultLocale;
|
|
1759
2038
|
__publicField(this, "includeRelations");
|
|
1760
2039
|
__publicField(this, "localeValue");
|
|
1761
2040
|
__publicField(this, "localesArray");
|
|
1762
2041
|
__publicField(this, "fetchAllLocales", false);
|
|
1763
2042
|
__publicField(this, "childrenOptions");
|
|
1764
2043
|
__publicField(this, "requestHeaders");
|
|
2044
|
+
/** The client's locale, asked when the read is sent rather than copied when it was built. */
|
|
2045
|
+
__publicField(this, "clientLocale");
|
|
2046
|
+
__publicField(this, "freshValue");
|
|
2047
|
+
this.clientLocale = readClientLocale(locale);
|
|
2048
|
+
}
|
|
2049
|
+
/**
|
|
2050
|
+
* The feed position this read follows — a change's `fresh` (`FeedChange`), or a resync's. Sent as
|
|
2051
|
+
* `?_fresh`, the one term the API declares for it: no cache — the Content API's edge or the
|
|
2052
|
+
* browser's own — holds an answer for that position from before it, so the read is answered after
|
|
2053
|
+
* the change, and every read naming the same position shares that answer. The answer is the
|
|
2054
|
+
* read's own; omit it for a read no change prompted.
|
|
2055
|
+
*/
|
|
2056
|
+
fresh(position) {
|
|
2057
|
+
this.freshValue = position;
|
|
2058
|
+
return this;
|
|
1765
2059
|
}
|
|
1766
2060
|
/**
|
|
1767
2061
|
* Include related content in the response
|
|
@@ -1831,14 +2125,15 @@ var _ContentByIdBuilder = class _ContentByIdBuilder {
|
|
|
1831
2125
|
if (this.includeRelations != null) {
|
|
1832
2126
|
params.include = this.includeRelations.join(",");
|
|
1833
2127
|
}
|
|
2128
|
+
const clientLocale = this.clientLocale();
|
|
1834
2129
|
if (this.localeValue != null && this.localeValue !== "") {
|
|
1835
2130
|
params.locale = this.localeValue;
|
|
1836
2131
|
} else if (this.fetchAllLocales) {
|
|
1837
2132
|
params.locale = "*";
|
|
1838
2133
|
} else if (this.localesArray && this.localesArray.length > 0) {
|
|
1839
2134
|
params.locales = this.localesArray.join(",");
|
|
1840
|
-
} else if (
|
|
1841
|
-
params.locale =
|
|
2135
|
+
} else if (clientLocale != null && clientLocale !== "") {
|
|
2136
|
+
params.locale = clientLocale;
|
|
1842
2137
|
}
|
|
1843
2138
|
if (this.childrenOptions != null) {
|
|
1844
2139
|
if (this.childrenOptions.contentTypes) {
|
|
@@ -1850,6 +2145,9 @@ var _ContentByIdBuilder = class _ContentByIdBuilder {
|
|
|
1850
2145
|
params.childrenLimit = this.childrenOptions.limit;
|
|
1851
2146
|
}
|
|
1852
2147
|
}
|
|
2148
|
+
if (this.freshValue !== void 0) {
|
|
2149
|
+
params[FRESH_PARAMETER] = this.freshValue;
|
|
2150
|
+
}
|
|
1853
2151
|
try {
|
|
1854
2152
|
return await this.httpClient.fetchPayload(
|
|
1855
2153
|
"/v1/{contentType}/{id}",
|
|
@@ -1903,22 +2201,28 @@ __name(entityTagOf, "entityTagOf");
|
|
|
1903
2201
|
|
|
1904
2202
|
// src/content/content-client.ts
|
|
1905
2203
|
var _ContentClient = class _ContentClient {
|
|
1906
|
-
|
|
2204
|
+
/**
|
|
2205
|
+
* @param locale - The client's locale: the function `TypedRelyIOClient` hands over, asked on
|
|
2206
|
+
* every call, or a locale fixed for this client
|
|
2207
|
+
*/
|
|
2208
|
+
constructor(httpClient, contentType, locale) {
|
|
1907
2209
|
this.httpClient = httpClient;
|
|
1908
2210
|
this.contentType = contentType;
|
|
1909
|
-
this.
|
|
2211
|
+
/** The client's locale, asked on every call rather than copied when this client was taken. */
|
|
2212
|
+
__publicField(this, "clientLocale");
|
|
2213
|
+
this.clientLocale = readClientLocale(locale);
|
|
1910
2214
|
}
|
|
1911
2215
|
/**
|
|
1912
2216
|
* Create a new query builder for this content type
|
|
1913
2217
|
*/
|
|
1914
2218
|
query() {
|
|
1915
|
-
return new ContentQueryBuilder(this.httpClient, this.contentType, this.
|
|
2219
|
+
return new ContentQueryBuilder(this.httpClient, this.contentType, this.clientLocale);
|
|
1916
2220
|
}
|
|
1917
2221
|
/**
|
|
1918
2222
|
* Find content by ID with fluent options
|
|
1919
2223
|
*/
|
|
1920
2224
|
findById(id) {
|
|
1921
|
-
return new ContentByIdBuilder(this.httpClient, this.contentType, id, this.
|
|
2225
|
+
return new ContentByIdBuilder(this.httpClient, this.contentType, id, this.clientLocale);
|
|
1922
2226
|
}
|
|
1923
2227
|
/**
|
|
1924
2228
|
* Fetch all content items
|
|
@@ -2002,7 +2306,7 @@ var _ContentClient = class _ContentClient {
|
|
|
2002
2306
|
async fetchOneWithTag(id, options) {
|
|
2003
2307
|
const query = {};
|
|
2004
2308
|
if (options?.include != null) query.include = options.include.join(",");
|
|
2005
|
-
const locale = options?.locale ?? this.
|
|
2309
|
+
const locale = options?.locale ?? this.clientLocale();
|
|
2006
2310
|
if (locale != null && locale !== "") query.locale = locale;
|
|
2007
2311
|
const response = await this.httpClient.send(
|
|
2008
2312
|
"/v1/{contentType}/{id}",
|
|
@@ -2032,16 +2336,17 @@ var _ContentClient = class _ContentClient {
|
|
|
2032
2336
|
* @param options - Create options (slug, locale, parentId, sortOrder), and `headers` this one
|
|
2033
2337
|
* request adds — e.g. `{ 'X-RelyIO-Dataset': 'staging' }` to write a dataset other than the
|
|
2034
2338
|
* client's. They are sent, never put in the body; `idempotencyKey` still wins over an
|
|
2035
|
-
* `Idempotency-Key` among them.
|
|
2339
|
+
* `Idempotency-Key` among them. `locale` defaults to the client's own (`withLocale()`).
|
|
2036
2340
|
*/
|
|
2037
2341
|
async create(data, options) {
|
|
2038
|
-
const { idempotencyKey, headers, ...body } = options ?? {};
|
|
2342
|
+
const { idempotencyKey, headers, locale, ...body } = options ?? {};
|
|
2343
|
+
const sent = callLocale(locale, this.clientLocale());
|
|
2039
2344
|
return this.httpClient.fetchPayload(
|
|
2040
2345
|
"/v1/{contentType}",
|
|
2041
2346
|
"post",
|
|
2042
2347
|
{
|
|
2043
2348
|
path: { contentType: this.contentType },
|
|
2044
|
-
body: { content: data, ...body },
|
|
2349
|
+
body: { content: data, ...body, ...sent === void 0 ? {} : { locale: sent } },
|
|
2045
2350
|
headers: requestHeaders(headers, writeHeaders({ idempotencyKey }))
|
|
2046
2351
|
}
|
|
2047
2352
|
);
|
|
@@ -2052,16 +2357,18 @@ var _ContentClient = class _ContentClient {
|
|
|
2052
2357
|
* @param data - Fields to update
|
|
2053
2358
|
* @param options - Update options (slug, locale, parentId, sortOrder), and `headers` this one
|
|
2054
2359
|
* request adds, as for {@link create}. `ifMatch` and `idempotencyKey` win over the same
|
|
2055
|
-
* header among them.
|
|
2360
|
+
* header among them. `locale` defaults to the client's own, the locale whose entity tag
|
|
2361
|
+
* {@link fetchOneWithTag} reads, so the tag it hands back is checked against the same locale.
|
|
2056
2362
|
*/
|
|
2057
2363
|
async update(id, data, options) {
|
|
2058
|
-
const { idempotencyKey, ifMatch, headers, ...body } = options ?? {};
|
|
2364
|
+
const { idempotencyKey, ifMatch, headers, locale, ...body } = options ?? {};
|
|
2365
|
+
const sent = callLocale(locale, this.clientLocale());
|
|
2059
2366
|
return this.httpClient.fetchPayload(
|
|
2060
2367
|
"/v1/{contentType}/{id}",
|
|
2061
2368
|
"patch",
|
|
2062
2369
|
{
|
|
2063
2370
|
path: { contentType: this.contentType, id },
|
|
2064
|
-
body: { content: data, ...body },
|
|
2371
|
+
body: { content: data, ...body, ...sent === void 0 ? {} : { locale: sent } },
|
|
2065
2372
|
headers: requestHeaders(headers, writeHeaders({ idempotencyKey, ifMatch }))
|
|
2066
2373
|
}
|
|
2067
2374
|
);
|
|
@@ -2093,32 +2400,35 @@ var _ContentClient = class _ContentClient {
|
|
|
2093
2400
|
* Get version history for one locale of an entry.
|
|
2094
2401
|
*
|
|
2095
2402
|
* Versions are numbered per locale, so this is one locale's timeline: `locale` defaults to the
|
|
2096
|
-
* schema default
|
|
2097
|
-
* rather than another locale's rows. Every row names the
|
|
2403
|
+
* client's own (`withLocale()`), else the schema default; no fallback is applied, and a locale
|
|
2404
|
+
* with no history returns an empty array rather than another locale's rows. Every row names the
|
|
2405
|
+
* locale it belongs to.
|
|
2098
2406
|
*/
|
|
2099
2407
|
async versions(id, options) {
|
|
2408
|
+
const locale = callLocale(options?.locale, this.clientLocale());
|
|
2100
2409
|
return this.httpClient.fetchPayload("/v1/{contentType}/{id}/versions", "get", {
|
|
2101
2410
|
path: { contentType: this.contentType, id },
|
|
2102
2411
|
query: {
|
|
2103
2412
|
limit: options?.limit ?? 10,
|
|
2104
|
-
...
|
|
2413
|
+
...locale === void 0 ? {} : { locale }
|
|
2105
2414
|
}
|
|
2106
2415
|
});
|
|
2107
2416
|
}
|
|
2108
2417
|
/**
|
|
2109
2418
|
* Get a specific version of an entry.
|
|
2110
2419
|
*
|
|
2111
|
-
* `locale` defaults to the
|
|
2112
|
-
* different content in each one.
|
|
2420
|
+
* `locale` defaults to the client's own (`withLocale()`), else the schema default. Version
|
|
2421
|
+
* numbers are per locale, so version 2 is different content in each one.
|
|
2113
2422
|
*/
|
|
2114
2423
|
async version(id, versionNumber, options) {
|
|
2424
|
+
const locale = callLocale(options?.locale, this.clientLocale());
|
|
2115
2425
|
return this.httpClient.fetchPayload("/v1/{contentType}/{id}/versions/{versionNumber}", "get", {
|
|
2116
2426
|
path: {
|
|
2117
2427
|
contentType: this.contentType,
|
|
2118
2428
|
id,
|
|
2119
2429
|
versionNumber: String(versionNumber)
|
|
2120
2430
|
},
|
|
2121
|
-
query:
|
|
2431
|
+
query: locale === void 0 ? void 0 : { locale }
|
|
2122
2432
|
});
|
|
2123
2433
|
}
|
|
2124
2434
|
/**
|
|
@@ -2157,7 +2467,7 @@ var _ContentClient = class _ContentClient {
|
|
|
2157
2467
|
if (options?.include != null) {
|
|
2158
2468
|
params.include = options.include.join(",");
|
|
2159
2469
|
}
|
|
2160
|
-
const locale = options?.locale ?? this.
|
|
2470
|
+
const locale = options?.locale ?? this.clientLocale();
|
|
2161
2471
|
if (locale != null && locale !== "") {
|
|
2162
2472
|
params.locale = locale;
|
|
2163
2473
|
}
|
|
@@ -2177,19 +2487,20 @@ var _ContentClient = class _ContentClient {
|
|
|
2177
2487
|
* Create a new ContentClient for a different content type
|
|
2178
2488
|
*/
|
|
2179
2489
|
ofType(contentType) {
|
|
2180
|
-
return new _ContentClient(this.httpClient, contentType, this.
|
|
2490
|
+
return new _ContentClient(this.httpClient, contentType, this.clientLocale);
|
|
2181
2491
|
}
|
|
2182
2492
|
/**
|
|
2183
|
-
* Create a new ContentClient with a different default locale
|
|
2493
|
+
* Create a new ContentClient with a different default locale, fixed: an update of the client's
|
|
2494
|
+
* locale (`updateConfig({ locale })`) does not reach it
|
|
2184
2495
|
*/
|
|
2185
2496
|
withLocale(locale) {
|
|
2186
2497
|
return new _ContentClient(this.httpClient, this.contentType, locale);
|
|
2187
2498
|
}
|
|
2188
2499
|
/**
|
|
2189
|
-
* Get the
|
|
2500
|
+
* Get the default locale as it is now: the client's, or the one `withLocale()` fixed
|
|
2190
2501
|
*/
|
|
2191
2502
|
getDefaultLocale() {
|
|
2192
|
-
return this.
|
|
2503
|
+
return this.clientLocale();
|
|
2193
2504
|
}
|
|
2194
2505
|
};
|
|
2195
2506
|
__name(_ContentClient, "ContentClient");
|
|
@@ -2198,7 +2509,7 @@ var ContentClient = _ContentClient;
|
|
|
2198
2509
|
// src/errors/file-errors.ts
|
|
2199
2510
|
var _FileUploadError = class _FileUploadError extends SDKError {
|
|
2200
2511
|
constructor(code, message, options = {}) {
|
|
2201
|
-
super(message, options.statusCode, options.cause, { fileErrorCode: code });
|
|
2512
|
+
super(message, options.statusCode, options.cause, { ...options.context, fileErrorCode: code });
|
|
2202
2513
|
__publicField(this, "code");
|
|
2203
2514
|
__publicField(this, "retryable");
|
|
2204
2515
|
this.code = code;
|
|
@@ -2245,11 +2556,52 @@ function validateFile(file, constraints = {}) {
|
|
|
2245
2556
|
}
|
|
2246
2557
|
__name(validateFile, "validateFile");
|
|
2247
2558
|
|
|
2559
|
+
// src/errors/file-error-codes.ts
|
|
2560
|
+
var FILE_ERROR_CODES = [
|
|
2561
|
+
"UPLOAD_TOO_LARGE",
|
|
2562
|
+
"UNSUPPORTED_MIME",
|
|
2563
|
+
"DIMENSION_OUT_OF_RANGE",
|
|
2564
|
+
"ASPECT_RATIO",
|
|
2565
|
+
"CORRUPT_FILE",
|
|
2566
|
+
"UNSUPPORTED_CODEC",
|
|
2567
|
+
"IMAGES_LIMIT_EXCEEDED",
|
|
2568
|
+
"STREAM_ENCODE_FAILED",
|
|
2569
|
+
"SCAN_INFECTED",
|
|
2570
|
+
"INTERNAL"
|
|
2571
|
+
];
|
|
2572
|
+
|
|
2248
2573
|
// src/content/asset-client.ts
|
|
2574
|
+
function assertWaitOptions(options) {
|
|
2575
|
+
const { timeoutMs, pollIntervalMs } = options;
|
|
2576
|
+
if (timeoutMs !== void 0 && !(typeof timeoutMs === "number" && timeoutMs >= 0 && timeoutMs <= MAX_TIMER_DELAY_MS)) {
|
|
2577
|
+
throw new Error(
|
|
2578
|
+
`timeoutMs must be a number of milliseconds from 0 to ${MAX_TIMER_DELAY_MS}, and got ${String(timeoutMs)}`
|
|
2579
|
+
);
|
|
2580
|
+
}
|
|
2581
|
+
if (pollIntervalMs !== void 0 && !isTimerDelay(pollIntervalMs)) {
|
|
2582
|
+
throw new Error(
|
|
2583
|
+
`pollIntervalMs must be a number of milliseconds from 1 to ${MAX_TIMER_DELAY_MS} \u2014 a timer given 0, NaN, Infinity or more fires at once \u2014 and got ${String(pollIntervalMs)}`
|
|
2584
|
+
);
|
|
2585
|
+
}
|
|
2586
|
+
}
|
|
2587
|
+
__name(assertWaitOptions, "assertWaitOptions");
|
|
2249
2588
|
function hasText(value) {
|
|
2250
2589
|
return value != null && value !== "";
|
|
2251
2590
|
}
|
|
2252
2591
|
__name(hasText, "hasText");
|
|
2592
|
+
function processingFailure(subject, stored) {
|
|
2593
|
+
const storedCode = stored?.code;
|
|
2594
|
+
const code = FILE_ERROR_CODES.find((known) => known === storedCode) ?? "PROCESSING_FAILED";
|
|
2595
|
+
return new FileUploadError(
|
|
2596
|
+
code,
|
|
2597
|
+
`Asset ${subject}: ${stored?.message ?? "File processing failed"}`,
|
|
2598
|
+
{
|
|
2599
|
+
retriable: stored?.retriable === true,
|
|
2600
|
+
context: { processingError: stored }
|
|
2601
|
+
}
|
|
2602
|
+
);
|
|
2603
|
+
}
|
|
2604
|
+
__name(processingFailure, "processingFailure");
|
|
2253
2605
|
async function computeSha256Hex(blob) {
|
|
2254
2606
|
try {
|
|
2255
2607
|
const subtle = globalThis.crypto?.subtle;
|
|
@@ -2265,11 +2617,17 @@ async function computeSha256Hex(blob) {
|
|
|
2265
2617
|
}
|
|
2266
2618
|
__name(computeSha256Hex, "computeSha256Hex");
|
|
2267
2619
|
var _AssetFileBuilder = class _AssetFileBuilder {
|
|
2268
|
-
|
|
2620
|
+
/**
|
|
2621
|
+
* @param locale - The client's locale, sent when a call names none: the function
|
|
2622
|
+
* `TypedRelyIOClient` hands over, or a locale fixed for this builder
|
|
2623
|
+
*/
|
|
2624
|
+
constructor(httpClient, assetType, entryUid, locale) {
|
|
2269
2625
|
this.httpClient = httpClient;
|
|
2270
2626
|
this.assetType = assetType;
|
|
2271
2627
|
this.entryUid = entryUid;
|
|
2272
|
-
this.
|
|
2628
|
+
/** The client's locale, asked on every call rather than copied when this builder was taken. */
|
|
2629
|
+
__publicField(this, "clientLocale");
|
|
2630
|
+
this.clientLocale = readClientLocale(locale);
|
|
2273
2631
|
}
|
|
2274
2632
|
/**
|
|
2275
2633
|
* The path parameters every `/file/*` route on this builder takes.
|
|
@@ -2282,7 +2640,16 @@ var _AssetFileBuilder = class _AssetFileBuilder {
|
|
|
2282
2640
|
return { contentType: this.assetType, id: this.entryUid };
|
|
2283
2641
|
}
|
|
2284
2642
|
withLocale(locale) {
|
|
2285
|
-
return locale ?? this.
|
|
2643
|
+
return locale ?? this.clientLocale();
|
|
2644
|
+
}
|
|
2645
|
+
/**
|
|
2646
|
+
* The `locale` body field of a multipart call: the session's, else the client's. The API finds
|
|
2647
|
+
* the binding by it, so a session a `withLocale()` client opened must name the same locale on
|
|
2648
|
+
* every later call or its `objectKey` is refused as another binding's.
|
|
2649
|
+
*/
|
|
2650
|
+
localeField(locale) {
|
|
2651
|
+
const sent = this.withLocale(locale);
|
|
2652
|
+
return hasText(sent) ? { locale: sent } : {};
|
|
2286
2653
|
}
|
|
2287
2654
|
/**
|
|
2288
2655
|
* Issue a single upload target — an R2 presigned PUT (image/file) or a
|
|
@@ -2341,7 +2708,7 @@ var _AssetFileBuilder = class _AssetFileBuilder {
|
|
|
2341
2708
|
uploadId: target.uploadId,
|
|
2342
2709
|
objectKey: target.objectKey,
|
|
2343
2710
|
partNumber: target.partNumber,
|
|
2344
|
-
...
|
|
2711
|
+
...this.localeField(target.locale)
|
|
2345
2712
|
}
|
|
2346
2713
|
});
|
|
2347
2714
|
}
|
|
@@ -2352,7 +2719,7 @@ var _AssetFileBuilder = class _AssetFileBuilder {
|
|
|
2352
2719
|
body: {
|
|
2353
2720
|
uploadId: target.uploadId,
|
|
2354
2721
|
objectKey: target.objectKey,
|
|
2355
|
-
...
|
|
2722
|
+
...this.localeField(target.locale)
|
|
2356
2723
|
}
|
|
2357
2724
|
});
|
|
2358
2725
|
return payload.parts;
|
|
@@ -2365,7 +2732,7 @@ var _AssetFileBuilder = class _AssetFileBuilder {
|
|
|
2365
2732
|
uploadId: target.uploadId,
|
|
2366
2733
|
objectKey: target.objectKey,
|
|
2367
2734
|
parts: target.parts.map((p) => ({ partNumber: p.partNumber, etag: p.etag })),
|
|
2368
|
-
...
|
|
2735
|
+
...this.localeField(target.locale)
|
|
2369
2736
|
}
|
|
2370
2737
|
});
|
|
2371
2738
|
}
|
|
@@ -2376,7 +2743,7 @@ var _AssetFileBuilder = class _AssetFileBuilder {
|
|
|
2376
2743
|
body: {
|
|
2377
2744
|
uploadId: target.uploadId,
|
|
2378
2745
|
objectKey: target.objectKey,
|
|
2379
|
-
...
|
|
2746
|
+
...this.localeField(target.locale)
|
|
2380
2747
|
}
|
|
2381
2748
|
});
|
|
2382
2749
|
}
|
|
@@ -2396,6 +2763,25 @@ var _AssetFileBuilder = class _AssetFileBuilder {
|
|
|
2396
2763
|
{ path: this.target, query: params }
|
|
2397
2764
|
);
|
|
2398
2765
|
}
|
|
2766
|
+
/**
|
|
2767
|
+
* Read where this file's upload got to — `status`, and the processing stage, progress and error
|
|
2768
|
+
* the pipeline recorded — whatever the asset entry is published in.
|
|
2769
|
+
*
|
|
2770
|
+
* An asset is a draft until its file is ready, and stays one when its file fails, so a key that
|
|
2771
|
+
* reads published content finds no entry for an upload in flight or one that failed. This read
|
|
2772
|
+
* answers for the binding itself. It takes the capability the upload calls take, so a read-only
|
|
2773
|
+
* key is refused with `ReadOnlyKeyError`, and it never returns the entry's fields.
|
|
2774
|
+
*/
|
|
2775
|
+
async status(options = {}) {
|
|
2776
|
+
const params = {};
|
|
2777
|
+
const locale = this.withLocale(options.locale);
|
|
2778
|
+
if (hasText(locale)) params.locale = locale;
|
|
2779
|
+
return this.httpClient.fetchPayload(
|
|
2780
|
+
"/v1/{contentType}/{id}/file",
|
|
2781
|
+
"get",
|
|
2782
|
+
{ path: this.target, query: params }
|
|
2783
|
+
);
|
|
2784
|
+
}
|
|
2399
2785
|
/**
|
|
2400
2786
|
* Update the binding's delivery visibility (token-based; no byte relocation —
|
|
2401
2787
|
* a downgrade purges the gateway cache). Returns the refreshed asset entry.
|
|
@@ -2434,14 +2820,31 @@ var _AssetFileBuilder = class _AssetFileBuilder {
|
|
|
2434
2820
|
});
|
|
2435
2821
|
}
|
|
2436
2822
|
/**
|
|
2437
|
-
* Poll the asset entry until its file binding reaches `ready
|
|
2438
|
-
*
|
|
2823
|
+
* Poll the asset entry until its file binding reaches `ready`, and resolve with the ready entry.
|
|
2824
|
+
*
|
|
2825
|
+
* Each poll reads the entry through the key's lens. When that finds nothing — a key that reads
|
|
2826
|
+
* published content, and an asset that is a draft until its file is ready and stays one when it
|
|
2827
|
+
* fails — the poll reads the binding itself ({@link status}), so a failed upload rejects as one
|
|
2828
|
+
* rather than running out the limit (REL-1481).
|
|
2829
|
+
*
|
|
2830
|
+
* @throws {FileUploadError} When processing failed. `code` is the processing code the service
|
|
2831
|
+
* stored — `UNSUPPORTED_CODEC`, `CORRUPT_FILE` and so on — or `PROCESSING_FAILED` where it
|
|
2832
|
+
* stored none this SDK knows; `context.processingError` holds what it stored, and `retryable`
|
|
2833
|
+
* is its `retriable` hint.
|
|
2834
|
+
* @throws {TimeoutError} When `timeoutMs` passes first. The file may still become ready; the
|
|
2835
|
+
* error's `context.timeout` is the limit and `context.fileStatus` the status last seen. A file
|
|
2836
|
+
* that is `ready` in an asset the key's lens never shows — created with `publish: false`, or
|
|
2837
|
+
* awaiting a workflow — ends here too, and the message says so.
|
|
2838
|
+
* @throws {ReadOnlyKeyError} When the key's lens does not show the asset and the key may not read
|
|
2839
|
+
* its binding either: nothing it can read would ever report the file.
|
|
2439
2840
|
*/
|
|
2440
2841
|
async waitUntilReady(options = {}) {
|
|
2842
|
+
assertWaitOptions(options);
|
|
2441
2843
|
const timeoutMs = options.timeoutMs ?? 6e4;
|
|
2442
2844
|
const pollIntervalMs = options.pollIntervalMs ?? 1500;
|
|
2443
2845
|
const locale = this.withLocale(options.locale);
|
|
2444
2846
|
const deadline = Date.now() + timeoutMs;
|
|
2847
|
+
const subject = `${this.assetType}/${this.entryUid}`;
|
|
2445
2848
|
for (; ; ) {
|
|
2446
2849
|
const builder = new ContentByIdBuilder(
|
|
2447
2850
|
this.httpClient,
|
|
@@ -2451,15 +2854,24 @@ var _AssetFileBuilder = class _AssetFileBuilder {
|
|
|
2451
2854
|
);
|
|
2452
2855
|
if (hasText(locale)) builder.locale(locale);
|
|
2453
2856
|
const entry = await builder.execute();
|
|
2454
|
-
|
|
2455
|
-
if (entry
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
throw
|
|
2857
|
+
let status;
|
|
2858
|
+
if (entry === null) {
|
|
2859
|
+
const binding = await this.status({ ...hasText(locale) ? { locale } : {} });
|
|
2860
|
+
status = binding.status;
|
|
2861
|
+
if (status === "failed") throw processingFailure(subject, binding.processing?.error);
|
|
2862
|
+
} else {
|
|
2863
|
+
status = entry.file?.status;
|
|
2864
|
+
if (status === "ready") return entry;
|
|
2865
|
+
if (status === "failed") throw processingFailure(subject, entry.file?.processing?.error);
|
|
2459
2866
|
}
|
|
2460
2867
|
if (Date.now() >= deadline) {
|
|
2461
|
-
|
|
2462
|
-
|
|
2868
|
+
const unseen = entry === null && status === "ready" ? "; the file is ready and this key's lens does not show the asset \u2014 it is not published" : "";
|
|
2869
|
+
throw new TimeoutError(
|
|
2870
|
+
`Timed out after ${timeoutMs}ms waiting for ${subject} (status: ${status ?? "unknown"})${unseen}`,
|
|
2871
|
+
timeoutMs,
|
|
2872
|
+
void 0,
|
|
2873
|
+
void 0,
|
|
2874
|
+
{ fileStatus: status }
|
|
2463
2875
|
);
|
|
2464
2876
|
}
|
|
2465
2877
|
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
@@ -2469,11 +2881,14 @@ var _AssetFileBuilder = class _AssetFileBuilder {
|
|
|
2469
2881
|
__name(_AssetFileBuilder, "AssetFileBuilder");
|
|
2470
2882
|
var AssetFileBuilder = _AssetFileBuilder;
|
|
2471
2883
|
var _AssetUploadBuilder = class _AssetUploadBuilder {
|
|
2472
|
-
|
|
2884
|
+
/**
|
|
2885
|
+
* @param locale - The client's locale, the upload's when `.locale()` names none: the function
|
|
2886
|
+
* `TypedRelyIOClient` hands over, or a locale fixed for this upload
|
|
2887
|
+
*/
|
|
2888
|
+
constructor(httpClient, assetType, file, locale) {
|
|
2473
2889
|
this.httpClient = httpClient;
|
|
2474
2890
|
this.assetType = assetType;
|
|
2475
2891
|
this.file = file;
|
|
2476
|
-
this.defaultLocale = defaultLocale;
|
|
2477
2892
|
__publicField(this, "_fileName");
|
|
2478
2893
|
__publicField(this, "_visibility");
|
|
2479
2894
|
__publicField(this, "_locale");
|
|
@@ -2485,6 +2900,9 @@ var _AssetUploadBuilder = class _AssetUploadBuilder {
|
|
|
2485
2900
|
__publicField(this, "_onProgress");
|
|
2486
2901
|
__publicField(this, "_validate");
|
|
2487
2902
|
__publicField(this, "_maxPartRetries", 3);
|
|
2903
|
+
/** The client's locale, read once when the upload starts. */
|
|
2904
|
+
__publicField(this, "clientLocale");
|
|
2905
|
+
this.clientLocale = readClientLocale(locale);
|
|
2488
2906
|
}
|
|
2489
2907
|
/** Subscribe to client → storage upload progress (single PUT + multipart). */
|
|
2490
2908
|
onProgress(callback) {
|
|
@@ -2534,8 +2952,14 @@ var _AssetUploadBuilder = class _AssetUploadBuilder {
|
|
|
2534
2952
|
this._slug = slug;
|
|
2535
2953
|
return this;
|
|
2536
2954
|
}
|
|
2537
|
-
/**
|
|
2955
|
+
/**
|
|
2956
|
+
* Wait until the file binding becomes `ready` before resolving.
|
|
2957
|
+
*
|
|
2958
|
+
* @throws {Error} When `timeoutMs` or `pollIntervalMs` is one the wait cannot honour — refused
|
|
2959
|
+
* here, before anything is uploaded, rather than after the bytes are in.
|
|
2960
|
+
*/
|
|
2538
2961
|
waitForReady(options = true) {
|
|
2962
|
+
if (typeof options === "object") assertWaitOptions(options);
|
|
2539
2963
|
if (options === false) {
|
|
2540
2964
|
this._waitForReady = false;
|
|
2541
2965
|
} else {
|
|
@@ -2550,7 +2974,8 @@ var _AssetUploadBuilder = class _AssetUploadBuilder {
|
|
|
2550
2974
|
if (!fileName) {
|
|
2551
2975
|
throw new Error('A file name is required: pass a File, or call .filename("name.ext")');
|
|
2552
2976
|
}
|
|
2553
|
-
const
|
|
2977
|
+
const clientLocale = this.clientLocale();
|
|
2978
|
+
const locale = this._locale ?? clientLocale;
|
|
2554
2979
|
if (this._validate) {
|
|
2555
2980
|
const violations = validateFile(this.file, this._validate);
|
|
2556
2981
|
if (violations.length > 0) {
|
|
@@ -2561,7 +2986,7 @@ var _AssetUploadBuilder = class _AssetUploadBuilder {
|
|
|
2561
2986
|
}
|
|
2562
2987
|
}
|
|
2563
2988
|
const hash = await computeSha256Hex(this.file);
|
|
2564
|
-
const client = new AssetClient(this.httpClient, this.assetType,
|
|
2989
|
+
const client = new AssetClient(this.httpClient, this.assetType, clientLocale);
|
|
2565
2990
|
const created = await client.create(this._content, {
|
|
2566
2991
|
...hasText(this._slug) ? { slug: this._slug } : {},
|
|
2567
2992
|
...hasText(locale) ? { locale } : {},
|
|
@@ -2712,17 +3137,26 @@ var _AssetUploadBuilder = class _AssetUploadBuilder {
|
|
|
2712
3137
|
__name(_AssetUploadBuilder, "AssetUploadBuilder");
|
|
2713
3138
|
var AssetUploadBuilder = _AssetUploadBuilder;
|
|
2714
3139
|
var _AssetClient = class _AssetClient {
|
|
2715
|
-
|
|
3140
|
+
/**
|
|
3141
|
+
* @param locale - The client's locale: the function `TypedRelyIOClient` hands over, asked on
|
|
3142
|
+
* every call, or a locale fixed for this client
|
|
3143
|
+
*/
|
|
3144
|
+
constructor(httpClient, assetType, locale) {
|
|
2716
3145
|
this.httpClient = httpClient;
|
|
2717
3146
|
this.assetType = assetType;
|
|
2718
|
-
this.
|
|
3147
|
+
/** The client's locale, asked on every call rather than copied when this client was taken. */
|
|
3148
|
+
__publicField(this, "clientLocale");
|
|
3149
|
+
this.clientLocale = readClientLocale(locale);
|
|
2719
3150
|
}
|
|
2720
|
-
/**
|
|
3151
|
+
/**
|
|
3152
|
+
* Create a query builder for this asset type. It resolves asset reads; its conditions name the
|
|
3153
|
+
* asset type's fields, which the `file` and `urls` blocks are not.
|
|
3154
|
+
*/
|
|
2721
3155
|
query() {
|
|
2722
3156
|
return new ContentQueryBuilder(
|
|
2723
3157
|
this.httpClient,
|
|
2724
3158
|
this.assetType,
|
|
2725
|
-
this.
|
|
3159
|
+
this.clientLocale
|
|
2726
3160
|
);
|
|
2727
3161
|
}
|
|
2728
3162
|
/** Find an asset entry by id (or slug) with fluent options. */
|
|
@@ -2731,7 +3165,7 @@ var _AssetClient = class _AssetClient {
|
|
|
2731
3165
|
this.httpClient,
|
|
2732
3166
|
this.assetType,
|
|
2733
3167
|
id,
|
|
2734
|
-
this.
|
|
3168
|
+
this.clientLocale
|
|
2735
3169
|
);
|
|
2736
3170
|
}
|
|
2737
3171
|
/** Fetch a single asset entry by id (or slug), or `null` if not found. */
|
|
@@ -2752,28 +3186,34 @@ var _AssetClient = class _AssetClient {
|
|
|
2752
3186
|
* Create a new asset entry as a draft. The binary is uploaded separately via
|
|
2753
3187
|
* `file(uid)` (or use the high-level `upload()` helper), and the entry
|
|
2754
3188
|
* auto-publishes when its file becomes ready (unless `publish: false`).
|
|
3189
|
+
* `locale` defaults to the client's own (`withLocale()`).
|
|
2755
3190
|
*/
|
|
2756
3191
|
async create(data, options) {
|
|
2757
|
-
const { idempotencyKey, ...rest } = options ?? {};
|
|
3192
|
+
const { idempotencyKey, locale, ...rest } = options ?? {};
|
|
3193
|
+
const sent = callLocale(locale, this.clientLocale());
|
|
2758
3194
|
return this.httpClient.fetchPayload(
|
|
2759
3195
|
"/v1/{contentType}",
|
|
2760
3196
|
"post",
|
|
2761
3197
|
{
|
|
2762
3198
|
path: { contentType: this.assetType },
|
|
2763
|
-
body: { content: data, ...rest },
|
|
3199
|
+
body: { content: data, ...rest, ...sent === void 0 ? {} : { locale: sent } },
|
|
2764
3200
|
headers: writeHeaders({ idempotencyKey })
|
|
2765
3201
|
}
|
|
2766
3202
|
);
|
|
2767
3203
|
}
|
|
2768
|
-
/**
|
|
3204
|
+
/**
|
|
3205
|
+
* Update an asset entry's metadata fields (and/or slug). `locale` defaults to the client's own
|
|
3206
|
+
* (`withLocale()`).
|
|
3207
|
+
*/
|
|
2769
3208
|
async update(id, data, options) {
|
|
2770
|
-
const { idempotencyKey, ifMatch, ...rest } = options ?? {};
|
|
3209
|
+
const { idempotencyKey, ifMatch, locale, ...rest } = options ?? {};
|
|
3210
|
+
const sent = callLocale(locale, this.clientLocale());
|
|
2771
3211
|
return this.httpClient.fetchPayload(
|
|
2772
3212
|
"/v1/{contentType}/{id}",
|
|
2773
3213
|
"patch",
|
|
2774
3214
|
{
|
|
2775
3215
|
path: { contentType: this.assetType, id },
|
|
2776
|
-
body: { content: data, ...rest },
|
|
3216
|
+
body: { content: data, ...rest, ...sent === void 0 ? {} : { locale: sent } },
|
|
2777
3217
|
headers: writeHeaders({ idempotencyKey, ifMatch })
|
|
2778
3218
|
}
|
|
2779
3219
|
);
|
|
@@ -2787,21 +3227,24 @@ var _AssetClient = class _AssetClient {
|
|
|
2787
3227
|
}
|
|
2788
3228
|
/** Get the file sub-resource builder for a specific asset entry. */
|
|
2789
3229
|
file(entryUid) {
|
|
2790
|
-
return new AssetFileBuilder(this.httpClient, this.assetType, entryUid, this.
|
|
3230
|
+
return new AssetFileBuilder(this.httpClient, this.assetType, entryUid, this.clientLocale);
|
|
2791
3231
|
}
|
|
2792
3232
|
/** Start a high-level upload (create → presign → PUT → confirm). */
|
|
2793
3233
|
upload(file) {
|
|
2794
|
-
return new AssetUploadBuilder(this.httpClient, this.assetType, file, this.
|
|
3234
|
+
return new AssetUploadBuilder(this.httpClient, this.assetType, file, this.clientLocale);
|
|
2795
3235
|
}
|
|
2796
3236
|
/** Get the asset type id. */
|
|
2797
3237
|
getAssetType() {
|
|
2798
3238
|
return this.assetType;
|
|
2799
3239
|
}
|
|
2800
|
-
/** Get the
|
|
3240
|
+
/** Get the default locale as it is now: the client's, or the one `withLocale()` fixed. */
|
|
2801
3241
|
getDefaultLocale() {
|
|
2802
|
-
return this.
|
|
3242
|
+
return this.clientLocale();
|
|
2803
3243
|
}
|
|
2804
|
-
/**
|
|
3244
|
+
/**
|
|
3245
|
+
* Create a new AssetClient for this type with a different default locale, fixed: an update of the
|
|
3246
|
+
* client's locale (`updateConfig({ locale })`) does not reach it.
|
|
3247
|
+
*/
|
|
2805
3248
|
withLocale(locale) {
|
|
2806
3249
|
return new _AssetClient(this.httpClient, this.assetType, locale);
|
|
2807
3250
|
}
|
|
@@ -2817,12 +3260,18 @@ __name(isAssetEntry, "isAssetEntry");
|
|
|
2817
3260
|
|
|
2818
3261
|
// src/content/singleton-client.ts
|
|
2819
3262
|
var _SingletonClient = class _SingletonClient {
|
|
2820
|
-
|
|
3263
|
+
/**
|
|
3264
|
+
* @param locale - The client's locale: the function `TypedRelyIOClient` hands over, asked on
|
|
3265
|
+
* every call, or a locale fixed for this client
|
|
3266
|
+
*/
|
|
3267
|
+
constructor(httpClient, singletonKey, locale) {
|
|
2821
3268
|
this.httpClient = httpClient;
|
|
2822
3269
|
this.singletonKey = singletonKey;
|
|
2823
|
-
this.defaultLocale = defaultLocale;
|
|
2824
3270
|
__publicField(this, "includeFields");
|
|
2825
3271
|
__publicField(this, "localeValue");
|
|
3272
|
+
/** The client's locale, asked on every call rather than copied when this client was taken. */
|
|
3273
|
+
__publicField(this, "clientLocale");
|
|
3274
|
+
this.clientLocale = readClientLocale(locale);
|
|
2826
3275
|
}
|
|
2827
3276
|
/**
|
|
2828
3277
|
* Include reference fields in the response
|
|
@@ -2862,9 +3311,10 @@ var _SingletonClient = class _SingletonClient {
|
|
|
2862
3311
|
async fetchWithTag(options) {
|
|
2863
3312
|
const query = {};
|
|
2864
3313
|
const includeValue = options?.include ?? this.includeFields;
|
|
2865
|
-
const localeValueFinal = options?.locale ?? this.localeValue ?? this.
|
|
3314
|
+
const localeValueFinal = options?.locale ?? this.localeValue ?? this.clientLocale();
|
|
2866
3315
|
if (includeValue) query.include = includeValue.join(",");
|
|
2867
3316
|
if (localeValueFinal != null && localeValueFinal !== "") query.locale = localeValueFinal;
|
|
3317
|
+
if (options?.fresh !== void 0) query[FRESH_PARAMETER] = options.fresh;
|
|
2868
3318
|
const response = await this.httpClient.send(
|
|
2869
3319
|
"/v1/singletons/{key}",
|
|
2870
3320
|
"get",
|
|
@@ -2876,11 +3326,12 @@ var _SingletonClient = class _SingletonClient {
|
|
|
2876
3326
|
* Update singleton content. The API auto-publishes on PATCH — there is no
|
|
2877
3327
|
* separate publish route; calling `update()` writes and publishes.
|
|
2878
3328
|
* @param data - The content fields to update
|
|
2879
|
-
* @param locale - Optional locale for localized content (overrides fluent locale
|
|
3329
|
+
* @param locale - Optional locale for localized content (overrides the fluent locale, which
|
|
3330
|
+
* overrides the client's own)
|
|
2880
3331
|
* @param options - `If-Match` / `Idempotency-Key`, both declared on this route
|
|
2881
3332
|
*/
|
|
2882
3333
|
async update(data, locale, options) {
|
|
2883
|
-
const finalLocale =
|
|
3334
|
+
const finalLocale = this.localeOf(locale);
|
|
2884
3335
|
return this.httpClient.fetchPayload(
|
|
2885
3336
|
"/v1/singletons/{key}",
|
|
2886
3337
|
"patch",
|
|
@@ -2909,15 +3360,17 @@ var _SingletonClient = class _SingletonClient {
|
|
|
2909
3360
|
* Get version history for one locale of the singleton.
|
|
2910
3361
|
*
|
|
2911
3362
|
* Versions are numbered per locale, so this is one locale's timeline: `locale` defaults to the
|
|
2912
|
-
*
|
|
2913
|
-
*
|
|
3363
|
+
* fluent `.locale()`, then the client's own (`withLocale()`), then the schema default; no
|
|
3364
|
+
* fallback is applied, and a locale with no history returns an empty array rather than another
|
|
3365
|
+
* locale's rows. Every row names the locale it belongs to.
|
|
2914
3366
|
*/
|
|
2915
3367
|
async versions(options) {
|
|
3368
|
+
const locale = this.localeOf(options?.locale);
|
|
2916
3369
|
const payload = await this.httpClient.fetchPayload("/v1/singletons/{key}/versions", "get", {
|
|
2917
3370
|
path: { key: this.singletonKey },
|
|
2918
3371
|
query: {
|
|
2919
3372
|
limit: options?.limit ?? 10,
|
|
2920
|
-
...
|
|
3373
|
+
...locale === void 0 ? {} : { locale }
|
|
2921
3374
|
}
|
|
2922
3375
|
});
|
|
2923
3376
|
return payload.versions;
|
|
@@ -2925,20 +3378,25 @@ var _SingletonClient = class _SingletonClient {
|
|
|
2925
3378
|
/**
|
|
2926
3379
|
* Get a specific version of the singleton.
|
|
2927
3380
|
*
|
|
2928
|
-
* `locale` defaults to the
|
|
2929
|
-
* different content in each one.
|
|
3381
|
+
* `locale` defaults to the fluent `.locale()`, then the client's own (`withLocale()`), then the
|
|
3382
|
+
* schema default. Version numbers are per locale, so version 2 is different content in each one.
|
|
2930
3383
|
*/
|
|
2931
3384
|
async version(versionNumber, options) {
|
|
3385
|
+
const locale = this.localeOf(options?.locale);
|
|
2932
3386
|
try {
|
|
2933
3387
|
return await this.httpClient.fetchPayload("/v1/singletons/{key}/versions/{versionNumber}", "get", {
|
|
2934
3388
|
path: { key: this.singletonKey, versionNumber: String(versionNumber) },
|
|
2935
|
-
query:
|
|
3389
|
+
query: locale === void 0 ? void 0 : { locale }
|
|
2936
3390
|
});
|
|
2937
3391
|
} catch (error) {
|
|
2938
3392
|
if (isResourceNotFound(error)) return null;
|
|
2939
3393
|
throw error;
|
|
2940
3394
|
}
|
|
2941
3395
|
}
|
|
3396
|
+
/** The locale a write or history read sends: the call's, the fluent one, else the client's. */
|
|
3397
|
+
localeOf(named) {
|
|
3398
|
+
return callLocale(named ?? this.localeValue, this.clientLocale());
|
|
3399
|
+
}
|
|
2942
3400
|
/**
|
|
2943
3401
|
* Get the singleton key
|
|
2944
3402
|
*/
|
|
@@ -2959,12 +3417,18 @@ var SingletonClient = _SingletonClient;
|
|
|
2959
3417
|
|
|
2960
3418
|
// src/content/settings-client.ts
|
|
2961
3419
|
var _SettingsClient = class _SettingsClient {
|
|
2962
|
-
|
|
3420
|
+
/**
|
|
3421
|
+
* @param locale - The client's locale: the function `TypedRelyIOClient` hands over, asked on
|
|
3422
|
+
* every call, or a locale fixed for this client
|
|
3423
|
+
*/
|
|
3424
|
+
constructor(httpClient, groupId, locale) {
|
|
2963
3425
|
this.httpClient = httpClient;
|
|
2964
3426
|
this.groupId = groupId;
|
|
2965
|
-
this.defaultLocale = defaultLocale;
|
|
2966
3427
|
__publicField(this, "includeFields");
|
|
2967
3428
|
__publicField(this, "localeValue");
|
|
3429
|
+
/** The client's locale, asked on every call rather than copied when this client was taken. */
|
|
3430
|
+
__publicField(this, "clientLocale");
|
|
3431
|
+
this.clientLocale = readClientLocale(locale);
|
|
2968
3432
|
}
|
|
2969
3433
|
/**
|
|
2970
3434
|
* Include reference fields in the response
|
|
@@ -2996,12 +3460,20 @@ var _SettingsClient = class _SettingsClient {
|
|
|
2996
3460
|
}
|
|
2997
3461
|
/**
|
|
2998
3462
|
* Update settings group
|
|
3463
|
+
*
|
|
3464
|
+
* The fields travel as `content` beside an optional `locale`, the body the route validates
|
|
3465
|
+
* (`UpdateSettingsDto`); any other top-level key is refused `REQUEST_INVALID`.
|
|
3466
|
+
*
|
|
2999
3467
|
* @param settings - The settings fields to update
|
|
3000
|
-
* @param locale - Optional locale for localized settings (overrides fluent locale
|
|
3468
|
+
* @param locale - Optional locale for localized settings (overrides the fluent locale, which
|
|
3469
|
+
* overrides the client's own)
|
|
3001
3470
|
*/
|
|
3002
3471
|
async update(settings, locale, options) {
|
|
3003
|
-
const finalLocale =
|
|
3004
|
-
const body =
|
|
3472
|
+
const finalLocale = this.localeOf(locale);
|
|
3473
|
+
const body = {
|
|
3474
|
+
content: settings,
|
|
3475
|
+
...finalLocale != null && finalLocale !== "" ? { locale: finalLocale } : {}
|
|
3476
|
+
};
|
|
3005
3477
|
return this.httpClient.fetchPayload(
|
|
3006
3478
|
"/v1/settings/{groupId}",
|
|
3007
3479
|
"patch",
|
|
@@ -3016,7 +3488,7 @@ var _SettingsClient = class _SettingsClient {
|
|
|
3016
3488
|
async fetchWithTag(options) {
|
|
3017
3489
|
const query = {};
|
|
3018
3490
|
const includeValue = options?.include ?? this.includeFields;
|
|
3019
|
-
const localeValueFinal = options?.locale ?? this.localeValue ?? this.
|
|
3491
|
+
const localeValueFinal = options?.locale ?? this.localeValue ?? this.clientLocale();
|
|
3020
3492
|
if (includeValue) query.include = includeValue.join(",");
|
|
3021
3493
|
if (localeValueFinal != null && localeValueFinal !== "") query.locale = localeValueFinal;
|
|
3022
3494
|
const response = await this.httpClient.send(
|
|
@@ -3028,16 +3500,20 @@ var _SettingsClient = class _SettingsClient {
|
|
|
3028
3500
|
}
|
|
3029
3501
|
/**
|
|
3030
3502
|
* Reset settings to default values
|
|
3031
|
-
*
|
|
3503
|
+
*
|
|
3504
|
+
* The route reads its locale from `?locale=` and takes no body, so that is where it is sent.
|
|
3505
|
+
*
|
|
3506
|
+
* @param locale - Optional locale for localized settings (overrides the fluent locale, which
|
|
3507
|
+
* overrides the client's own)
|
|
3032
3508
|
*/
|
|
3033
3509
|
async reset(locale, options) {
|
|
3034
|
-
const finalLocale =
|
|
3510
|
+
const finalLocale = this.localeOf(locale);
|
|
3035
3511
|
return this.httpClient.fetchPayload(
|
|
3036
3512
|
"/v1/settings/{groupId}/reset",
|
|
3037
3513
|
"post",
|
|
3038
3514
|
{
|
|
3039
3515
|
path: { groupId: this.groupId },
|
|
3040
|
-
|
|
3516
|
+
query: finalLocale != null && finalLocale !== "" ? { locale: finalLocale } : void 0,
|
|
3041
3517
|
headers: writeHeaders(options)
|
|
3042
3518
|
}
|
|
3043
3519
|
);
|
|
@@ -3068,15 +3544,17 @@ var _SettingsClient = class _SettingsClient {
|
|
|
3068
3544
|
* Get version history for one locale of the settings group.
|
|
3069
3545
|
*
|
|
3070
3546
|
* Versions are numbered per locale, so this is one locale's timeline: `locale` defaults to the
|
|
3071
|
-
*
|
|
3072
|
-
*
|
|
3547
|
+
* fluent `.locale()`, then the client's own (`withLocale()`), then the schema default; no
|
|
3548
|
+
* fallback is applied, and a locale with no history returns an empty array rather than another
|
|
3549
|
+
* locale's rows. Every row names the locale it belongs to.
|
|
3073
3550
|
*/
|
|
3074
3551
|
async versions(options) {
|
|
3552
|
+
const locale = this.localeOf(options?.locale);
|
|
3075
3553
|
const payload = await this.httpClient.fetchPayload("/v1/settings/{groupId}/versions", "get", {
|
|
3076
3554
|
path: { groupId: this.groupId },
|
|
3077
3555
|
query: {
|
|
3078
3556
|
limit: options?.limit ?? 10,
|
|
3079
|
-
...
|
|
3557
|
+
...locale === void 0 ? {} : { locale }
|
|
3080
3558
|
}
|
|
3081
3559
|
});
|
|
3082
3560
|
return payload.versions;
|
|
@@ -3084,20 +3562,25 @@ var _SettingsClient = class _SettingsClient {
|
|
|
3084
3562
|
/**
|
|
3085
3563
|
* Get a specific version of the settings.
|
|
3086
3564
|
*
|
|
3087
|
-
* `locale` defaults to the
|
|
3088
|
-
* different content in each one.
|
|
3565
|
+
* `locale` defaults to the fluent `.locale()`, then the client's own (`withLocale()`), then the
|
|
3566
|
+
* schema default. Version numbers are per locale, so version 2 is different content in each one.
|
|
3089
3567
|
*/
|
|
3090
3568
|
async version(versionNumber, options) {
|
|
3569
|
+
const locale = this.localeOf(options?.locale);
|
|
3091
3570
|
try {
|
|
3092
3571
|
return await this.httpClient.fetchPayload("/v1/settings/{groupId}/versions/{versionNumber}", "get", {
|
|
3093
3572
|
path: { groupId: this.groupId, versionNumber: String(versionNumber) },
|
|
3094
|
-
query:
|
|
3573
|
+
query: locale === void 0 ? void 0 : { locale }
|
|
3095
3574
|
});
|
|
3096
3575
|
} catch (error) {
|
|
3097
3576
|
if (isResourceNotFound(error)) return null;
|
|
3098
3577
|
throw error;
|
|
3099
3578
|
}
|
|
3100
3579
|
}
|
|
3580
|
+
/** The locale a write or history read sends: the call's, the fluent one, else the client's. */
|
|
3581
|
+
localeOf(named) {
|
|
3582
|
+
return callLocale(named ?? this.localeValue, this.clientLocale());
|
|
3583
|
+
}
|
|
3101
3584
|
/**
|
|
3102
3585
|
* Get the settings group ID
|
|
3103
3586
|
*/
|
|
@@ -3119,15 +3602,19 @@ var SettingsClient = _SettingsClient;
|
|
|
3119
3602
|
// src/search/search-client.ts
|
|
3120
3603
|
var _SearchBuilder = class _SearchBuilder {
|
|
3121
3604
|
/**
|
|
3122
|
-
* @param
|
|
3605
|
+
* @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), sent when
|
|
3606
|
+
* `.locale()` names none: the function `TypedRelyIOClient` hands over, or a locale fixed for it
|
|
3123
3607
|
*/
|
|
3124
|
-
constructor(httpClient, query,
|
|
3608
|
+
constructor(httpClient, query, locale) {
|
|
3125
3609
|
this.httpClient = httpClient;
|
|
3126
|
-
this.defaultLocale = defaultLocale;
|
|
3127
3610
|
__publicField(this, "query");
|
|
3128
3611
|
__publicField(this, "searchOptions", {});
|
|
3129
3612
|
__publicField(this, "requestHeaders");
|
|
3613
|
+
__publicField(this, "freshValue");
|
|
3614
|
+
/** The client's locale, asked when the query is sent rather than copied when it was built. */
|
|
3615
|
+
__publicField(this, "clientLocale");
|
|
3130
3616
|
this.query = query;
|
|
3617
|
+
this.clientLocale = readClientLocale(locale);
|
|
3131
3618
|
}
|
|
3132
3619
|
/**
|
|
3133
3620
|
* Headers this search adds to the client's own, e.g. `X-RelyIO-Dataset` to search another
|
|
@@ -3137,6 +3624,15 @@ var _SearchBuilder = class _SearchBuilder {
|
|
|
3137
3624
|
this.requestHeaders = headers;
|
|
3138
3625
|
return this;
|
|
3139
3626
|
}
|
|
3627
|
+
/**
|
|
3628
|
+
* The feed position this search follows — a change's `fresh` (`FeedChange`), or a resync's. Sent
|
|
3629
|
+
* as `?_fresh` on the query string beside the body: the edge keys a search on both, so no answer
|
|
3630
|
+
* it holds from before that position can answer it. The answer is the search's own.
|
|
3631
|
+
*/
|
|
3632
|
+
fresh(position) {
|
|
3633
|
+
this.freshValue = position;
|
|
3634
|
+
return this;
|
|
3635
|
+
}
|
|
3140
3636
|
/**
|
|
3141
3637
|
* Search within a single content type. Where a schema types the client, the name is one of its
|
|
3142
3638
|
* content types and the results are that type's entries.
|
|
@@ -3158,10 +3654,12 @@ var _SearchBuilder = class _SearchBuilder {
|
|
|
3158
3654
|
return this;
|
|
3159
3655
|
}
|
|
3160
3656
|
/**
|
|
3161
|
-
* Add filter conditions
|
|
3657
|
+
* Add filter conditions, on the fields the content types declare. A condition on `_metadata` is
|
|
3658
|
+
* refused here, before anything is sent.
|
|
3162
3659
|
* @param conditions - Filter conditions
|
|
3163
3660
|
*/
|
|
3164
3661
|
where(conditions) {
|
|
3662
|
+
refuseMetadataConditions(conditions);
|
|
3165
3663
|
this.searchOptions.where = {
|
|
3166
3664
|
...this.searchOptions.where,
|
|
3167
3665
|
...conditions
|
|
@@ -3269,14 +3767,18 @@ var _SearchBuilder = class _SearchBuilder {
|
|
|
3269
3767
|
if (this.searchOptions.explain === true) {
|
|
3270
3768
|
body.explain = true;
|
|
3271
3769
|
}
|
|
3272
|
-
const locale = this.searchOptions.locale ?? this.
|
|
3770
|
+
const locale = this.searchOptions.locale ?? this.clientLocale();
|
|
3273
3771
|
if (locale != null && locale !== "") {
|
|
3274
3772
|
body.locale = locale;
|
|
3275
3773
|
}
|
|
3276
3774
|
const response = await this.httpClient.send(
|
|
3277
3775
|
"/v1/search",
|
|
3278
3776
|
"post",
|
|
3279
|
-
{
|
|
3777
|
+
{
|
|
3778
|
+
body,
|
|
3779
|
+
headers: this.requestHeaders,
|
|
3780
|
+
...this.freshValue === void 0 ? {} : { query: { [FRESH_PARAMETER]: this.freshValue } }
|
|
3781
|
+
}
|
|
3280
3782
|
);
|
|
3281
3783
|
return response.body;
|
|
3282
3784
|
}
|
|
@@ -3297,14 +3799,17 @@ __name(_SearchBuilder, "SearchBuilder");
|
|
|
3297
3799
|
var SearchBuilder = _SearchBuilder;
|
|
3298
3800
|
var _AutocompleteBuilder = class _AutocompleteBuilder {
|
|
3299
3801
|
/**
|
|
3300
|
-
* @param
|
|
3802
|
+
* @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), sent when
|
|
3803
|
+
* `.locale()` names none: the function `TypedRelyIOClient` hands over, or a locale fixed for it
|
|
3301
3804
|
*/
|
|
3302
|
-
constructor(httpClient, query,
|
|
3805
|
+
constructor(httpClient, query, locale) {
|
|
3303
3806
|
this.httpClient = httpClient;
|
|
3304
|
-
this.defaultLocale = defaultLocale;
|
|
3305
3807
|
__publicField(this, "query");
|
|
3306
3808
|
__publicField(this, "autocompleteOptions", {});
|
|
3809
|
+
/** The client's locale, asked when the query is sent rather than copied when it was built. */
|
|
3810
|
+
__publicField(this, "clientLocale");
|
|
3307
3811
|
this.query = query;
|
|
3812
|
+
this.clientLocale = readClientLocale(locale);
|
|
3308
3813
|
}
|
|
3309
3814
|
/**
|
|
3310
3815
|
* Autocomplete within a content type
|
|
@@ -3330,10 +3835,12 @@ var _AutocompleteBuilder = class _AutocompleteBuilder {
|
|
|
3330
3835
|
return this;
|
|
3331
3836
|
}
|
|
3332
3837
|
/**
|
|
3333
|
-
* Add filter conditions
|
|
3838
|
+
* Add filter conditions, on the fields the content types declare. A condition on `_metadata` is
|
|
3839
|
+
* refused here, before anything is sent.
|
|
3334
3840
|
* @param conditions - Filter conditions
|
|
3335
3841
|
*/
|
|
3336
3842
|
where(conditions) {
|
|
3843
|
+
refuseMetadataConditions(conditions);
|
|
3337
3844
|
this.autocompleteOptions.where = {
|
|
3338
3845
|
...this.autocompleteOptions.where,
|
|
3339
3846
|
...conditions
|
|
@@ -3380,7 +3887,7 @@ var _AutocompleteBuilder = class _AutocompleteBuilder {
|
|
|
3380
3887
|
if (this.autocompleteOptions.limit !== void 0) {
|
|
3381
3888
|
body.limit = this.autocompleteOptions.limit;
|
|
3382
3889
|
}
|
|
3383
|
-
const locale = this.autocompleteOptions.locale ?? this.
|
|
3890
|
+
const locale = this.autocompleteOptions.locale ?? this.clientLocale();
|
|
3384
3891
|
if (locale != null && locale !== "") {
|
|
3385
3892
|
body.locale = locale;
|
|
3386
3893
|
}
|
|
@@ -3398,15 +3905,18 @@ __name(_AutocompleteBuilder, "AutocompleteBuilder");
|
|
|
3398
3905
|
var AutocompleteBuilder = _AutocompleteBuilder;
|
|
3399
3906
|
var _SimilarBuilder = class _SimilarBuilder {
|
|
3400
3907
|
/**
|
|
3401
|
-
* @param
|
|
3908
|
+
* @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), sent when
|
|
3909
|
+
* `.locale()` names none: the function `TypedRelyIOClient` hands over, or a locale fixed for it
|
|
3402
3910
|
*/
|
|
3403
|
-
constructor(httpClient, sourceId,
|
|
3911
|
+
constructor(httpClient, sourceId, locale) {
|
|
3404
3912
|
this.httpClient = httpClient;
|
|
3405
|
-
this.defaultLocale = defaultLocale;
|
|
3406
3913
|
__publicField(this, "sourceId");
|
|
3407
3914
|
__publicField(this, "similarOptions", {});
|
|
3408
3915
|
__publicField(this, "requestHeaders");
|
|
3916
|
+
/** The client's locale, asked when the read is sent rather than copied when it was built. */
|
|
3917
|
+
__publicField(this, "clientLocale");
|
|
3409
3918
|
this.sourceId = sourceId;
|
|
3919
|
+
this.clientLocale = readClientLocale(locale);
|
|
3410
3920
|
}
|
|
3411
3921
|
/**
|
|
3412
3922
|
* Headers this read adds to the client's own, e.g. `X-RelyIO-Dataset` to read another dataset
|
|
@@ -3434,17 +3944,12 @@ var _SimilarBuilder = class _SimilarBuilder {
|
|
|
3434
3944
|
this.similarOptions.contentType = void 0;
|
|
3435
3945
|
return this;
|
|
3436
3946
|
}
|
|
3437
|
-
|
|
3438
|
-
*
|
|
3439
|
-
*
|
|
3947
|
+
/*
|
|
3948
|
+
* No `where()` (REL-1452). Similar ranks from the source entry's stored vector, and the vector
|
|
3949
|
+
* index can pre-filter by content type and locale only — never by an entry field. The API used to
|
|
3950
|
+
* accept `where` here and apply nothing; it now refuses the property, so the builder does not
|
|
3951
|
+
* offer a filter that cannot be honoured. Narrow with `.in()`, `.across()` and `.locale()`.
|
|
3440
3952
|
*/
|
|
3441
|
-
where(conditions) {
|
|
3442
|
-
this.similarOptions.where = {
|
|
3443
|
-
...this.similarOptions.where,
|
|
3444
|
-
...conditions
|
|
3445
|
-
};
|
|
3446
|
-
return this;
|
|
3447
|
-
}
|
|
3448
3953
|
/**
|
|
3449
3954
|
* Limit number of results
|
|
3450
3955
|
* @param count - Maximum results
|
|
@@ -3478,13 +3983,10 @@ var _SimilarBuilder = class _SimilarBuilder {
|
|
|
3478
3983
|
if (contentTypes != null) {
|
|
3479
3984
|
body.contentTypes = contentTypes;
|
|
3480
3985
|
}
|
|
3481
|
-
if (this.similarOptions.where && Object.keys(this.similarOptions.where).length > 0) {
|
|
3482
|
-
body.where = this.similarOptions.where;
|
|
3483
|
-
}
|
|
3484
3986
|
if (this.similarOptions.limit !== void 0) {
|
|
3485
3987
|
body.limit = this.similarOptions.limit;
|
|
3486
3988
|
}
|
|
3487
|
-
const locale = this.similarOptions.locale ?? this.
|
|
3989
|
+
const locale = this.similarOptions.locale ?? this.clientLocale();
|
|
3488
3990
|
if (locale != null && locale !== "") {
|
|
3489
3991
|
body.locale = locale;
|
|
3490
3992
|
}
|
|
@@ -3512,41 +4014,36 @@ __name(_SimilarBuilder, "SimilarBuilder");
|
|
|
3512
4014
|
var SimilarBuilder = _SimilarBuilder;
|
|
3513
4015
|
var _SearchClient = class _SearchClient {
|
|
3514
4016
|
/**
|
|
3515
|
-
* @param
|
|
3516
|
-
* the query names none, as `content()`'s reads do
|
|
4017
|
+
* @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`): every builder
|
|
4018
|
+
* here sends it when the query names none, as `content()`'s reads do — the function
|
|
4019
|
+
* `TypedRelyIOClient` hands over, or a locale fixed for this client
|
|
3517
4020
|
*/
|
|
3518
|
-
constructor(httpClient,
|
|
4021
|
+
constructor(httpClient, locale) {
|
|
3519
4022
|
this.httpClient = httpClient;
|
|
3520
|
-
|
|
4023
|
+
/** The client's locale, handed to every builder here to ask when it sends. */
|
|
4024
|
+
__publicField(this, "clientLocale");
|
|
4025
|
+
this.clientLocale = readClientLocale(locale);
|
|
3521
4026
|
}
|
|
3522
4027
|
/**
|
|
3523
4028
|
* Start a search query
|
|
3524
4029
|
* @param query - Search query string
|
|
3525
4030
|
*/
|
|
3526
4031
|
search(query) {
|
|
3527
|
-
return new SearchBuilder(this.httpClient, query, this.
|
|
4032
|
+
return new SearchBuilder(this.httpClient, query, this.clientLocale);
|
|
3528
4033
|
}
|
|
3529
4034
|
/**
|
|
3530
4035
|
* Start an autocomplete query
|
|
3531
4036
|
* @param query - Partial query to autocomplete
|
|
3532
4037
|
*/
|
|
3533
4038
|
autocomplete(query) {
|
|
3534
|
-
return new AutocompleteBuilder(this.httpClient, query, this.
|
|
4039
|
+
return new AutocompleteBuilder(this.httpClient, query, this.clientLocale);
|
|
3535
4040
|
}
|
|
3536
4041
|
/**
|
|
3537
4042
|
* Find content similar to a given entry
|
|
3538
4043
|
* @param sourceId - Entry ID to find similar content for
|
|
3539
4044
|
*/
|
|
3540
4045
|
similar(sourceId) {
|
|
3541
|
-
return new SimilarBuilder(this.httpClient, sourceId, this.
|
|
3542
|
-
}
|
|
3543
|
-
/**
|
|
3544
|
-
* Quick search helper - searches all content types
|
|
3545
|
-
* @param query - Search query
|
|
3546
|
-
* @param limit - Max results (default 20)
|
|
3547
|
-
*/
|
|
3548
|
-
async global(query, limit = 20) {
|
|
3549
|
-
return this.search(query).across("*").limit(limit).execute();
|
|
4046
|
+
return new SimilarBuilder(this.httpClient, sourceId, this.clientLocale);
|
|
3550
4047
|
}
|
|
3551
4048
|
};
|
|
3552
4049
|
__name(_SearchClient, "SearchClient");
|
|
@@ -3554,7 +4051,12 @@ var SearchClient = _SearchClient;
|
|
|
3554
4051
|
|
|
3555
4052
|
// src/batch/batch-client.ts
|
|
3556
4053
|
var _BatchBuilder = class _BatchBuilder {
|
|
3557
|
-
|
|
4054
|
+
/**
|
|
4055
|
+
* @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), each
|
|
4056
|
+
* operation's when it names none: the function `TypedRelyIOClient` hands over, or a locale fixed
|
|
4057
|
+
* for this batch
|
|
4058
|
+
*/
|
|
4059
|
+
constructor(httpClient, locale) {
|
|
3558
4060
|
this.httpClient = httpClient;
|
|
3559
4061
|
__publicField(this, "operations", []);
|
|
3560
4062
|
__publicField(this, "defaultOptions", {
|
|
@@ -3564,6 +4066,17 @@ var _BatchBuilder = class _BatchBuilder {
|
|
|
3564
4066
|
continueOnError: true
|
|
3565
4067
|
});
|
|
3566
4068
|
__publicField(this, "requestHeaders");
|
|
4069
|
+
/** The client's locale, asked as each operation is added rather than copied when this was built. */
|
|
4070
|
+
__publicField(this, "clientLocale");
|
|
4071
|
+
this.clientLocale = readClientLocale(locale);
|
|
4072
|
+
}
|
|
4073
|
+
/**
|
|
4074
|
+
* An operation's body with its locale: the one it names, else the client's as it is when the
|
|
4075
|
+
* operation is added.
|
|
4076
|
+
*/
|
|
4077
|
+
localized(data) {
|
|
4078
|
+
const locale = callLocale(data.locale, this.clientLocale());
|
|
4079
|
+
return locale === void 0 ? data : { ...data, locale };
|
|
3567
4080
|
}
|
|
3568
4081
|
// ==========================================================================
|
|
3569
4082
|
// ENTRY OPERATIONS
|
|
@@ -3579,7 +4092,7 @@ var _BatchBuilder = class _BatchBuilder {
|
|
|
3579
4092
|
id,
|
|
3580
4093
|
method: "POST",
|
|
3581
4094
|
path: `/${contentType}`,
|
|
3582
|
-
body: data
|
|
4095
|
+
body: this.localized(data)
|
|
3583
4096
|
});
|
|
3584
4097
|
return this;
|
|
3585
4098
|
}
|
|
@@ -3599,7 +4112,7 @@ var _BatchBuilder = class _BatchBuilder {
|
|
|
3599
4112
|
id,
|
|
3600
4113
|
method: "PATCH",
|
|
3601
4114
|
path: `/${contentType}/${entryId}`,
|
|
3602
|
-
body: data
|
|
4115
|
+
body: this.localized(data)
|
|
3603
4116
|
});
|
|
3604
4117
|
return this;
|
|
3605
4118
|
}
|
|
@@ -3619,7 +4132,7 @@ var _BatchBuilder = class _BatchBuilder {
|
|
|
3619
4132
|
id,
|
|
3620
4133
|
method: "PUT",
|
|
3621
4134
|
path: `/${contentType}/${entryId}`,
|
|
3622
|
-
body: data
|
|
4135
|
+
body: this.localized(data)
|
|
3623
4136
|
});
|
|
3624
4137
|
return this;
|
|
3625
4138
|
}
|
|
@@ -3651,7 +4164,7 @@ var _BatchBuilder = class _BatchBuilder {
|
|
|
3651
4164
|
id,
|
|
3652
4165
|
method: "PATCH",
|
|
3653
4166
|
path: `/_singletons/${singletonKey}`,
|
|
3654
|
-
body: data
|
|
4167
|
+
body: this.localized(data)
|
|
3655
4168
|
});
|
|
3656
4169
|
return this;
|
|
3657
4170
|
}
|
|
@@ -3666,7 +4179,7 @@ var _BatchBuilder = class _BatchBuilder {
|
|
|
3666
4179
|
id,
|
|
3667
4180
|
method: "POST",
|
|
3668
4181
|
path: `/_singletons/${singletonKey}`,
|
|
3669
|
-
body: data
|
|
4182
|
+
body: this.localized(data)
|
|
3670
4183
|
});
|
|
3671
4184
|
return this;
|
|
3672
4185
|
}
|
|
@@ -3674,7 +4187,8 @@ var _BatchBuilder = class _BatchBuilder {
|
|
|
3674
4187
|
// RAW OPERATIONS
|
|
3675
4188
|
// ==========================================================================
|
|
3676
4189
|
/**
|
|
3677
|
-
* Add a raw operation (for advanced use cases)
|
|
4190
|
+
* Add a raw operation (for advanced use cases). It is sent exactly as written: the client's
|
|
4191
|
+
* locale is not added to it.
|
|
3678
4192
|
* @param operation - The operation to add
|
|
3679
4193
|
*/
|
|
3680
4194
|
raw(operation) {
|
|
@@ -3800,7 +4314,7 @@ var _BatchBuilder = class _BatchBuilder {
|
|
|
3800
4314
|
* Clone the builder
|
|
3801
4315
|
*/
|
|
3802
4316
|
clone() {
|
|
3803
|
-
const cloned = new _BatchBuilder(this.httpClient);
|
|
4317
|
+
const cloned = new _BatchBuilder(this.httpClient, this.clientLocale);
|
|
3804
4318
|
cloned.operations = [...this.operations];
|
|
3805
4319
|
cloned.defaultOptions = { ...this.defaultOptions };
|
|
3806
4320
|
cloned.requestHeaders = this.requestHeaders === void 0 ? void 0 : { ...this.requestHeaders };
|
|
@@ -3853,14 +4367,21 @@ var _BatchBuilder = class _BatchBuilder {
|
|
|
3853
4367
|
__name(_BatchBuilder, "BatchBuilder");
|
|
3854
4368
|
var BatchBuilder = _BatchBuilder;
|
|
3855
4369
|
var _BatchClient = class _BatchClient {
|
|
3856
|
-
|
|
4370
|
+
/**
|
|
4371
|
+
* @param locale - The client's locale: the function `TypedRelyIOClient` hands over, or a locale
|
|
4372
|
+
* fixed for this client
|
|
4373
|
+
*/
|
|
4374
|
+
constructor(httpClient, locale) {
|
|
3857
4375
|
this.httpClient = httpClient;
|
|
4376
|
+
/** The client's locale, handed to every batch this builds to ask as operations are added. */
|
|
4377
|
+
__publicField(this, "clientLocale");
|
|
4378
|
+
this.clientLocale = readClientLocale(locale);
|
|
3858
4379
|
}
|
|
3859
4380
|
/**
|
|
3860
|
-
* Create a new batch builder
|
|
4381
|
+
* Create a new batch builder, whose operations default to this client's locale
|
|
3861
4382
|
*/
|
|
3862
4383
|
create() {
|
|
3863
|
-
return new BatchBuilder(this.httpClient);
|
|
4384
|
+
return new BatchBuilder(this.httpClient, this.clientLocale);
|
|
3864
4385
|
}
|
|
3865
4386
|
/**
|
|
3866
4387
|
* Quick bulk create entries
|
|
@@ -3911,15 +4432,18 @@ function withClientLocale(parameters, clientLocale) {
|
|
|
3911
4432
|
__name(withClientLocale, "withClientLocale");
|
|
3912
4433
|
var _AdhocQueryBuilder = class _AdhocQueryBuilder {
|
|
3913
4434
|
/**
|
|
3914
|
-
* @param
|
|
3915
|
-
* when `.param('locale', …)` binds none
|
|
4435
|
+
* @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), sent as the
|
|
4436
|
+
* `locale` parameter when `.param('locale', …)` binds none: the function `TypedRelyIOClient`
|
|
4437
|
+
* hands over, or a locale fixed for this builder
|
|
3916
4438
|
*/
|
|
3917
|
-
constructor(httpClient,
|
|
4439
|
+
constructor(httpClient, locale) {
|
|
3918
4440
|
this.httpClient = httpClient;
|
|
3919
|
-
this.defaultLocale = defaultLocale;
|
|
3920
4441
|
__publicField(this, "queryDef", {});
|
|
3921
4442
|
__publicField(this, "parameters", {});
|
|
3922
4443
|
__publicField(this, "options", {});
|
|
4444
|
+
/** The client's locale, asked when the query is sent rather than copied when it was built. */
|
|
4445
|
+
__publicField(this, "clientLocale");
|
|
4446
|
+
this.clientLocale = readClientLocale(locale);
|
|
3923
4447
|
}
|
|
3924
4448
|
// ==========================================================================
|
|
3925
4449
|
// QUERY BLOCKS
|
|
@@ -3964,14 +4488,6 @@ var _AdhocQueryBuilder = class _AdhocQueryBuilder {
|
|
|
3964
4488
|
this.queryDef.search = config;
|
|
3965
4489
|
return this;
|
|
3966
4490
|
}
|
|
3967
|
-
/**
|
|
3968
|
-
* Add similar block
|
|
3969
|
-
* @param config - Similar configuration
|
|
3970
|
-
*/
|
|
3971
|
-
similar(config) {
|
|
3972
|
-
this.queryDef.similar = config;
|
|
3973
|
-
return this;
|
|
3974
|
-
}
|
|
3975
4491
|
// ==========================================================================
|
|
3976
4492
|
// FETCH MODIFIERS
|
|
3977
4493
|
// ==========================================================================
|
|
@@ -4148,15 +4664,15 @@ var _AdhocQueryBuilder = class _AdhocQueryBuilder {
|
|
|
4148
4664
|
* Execute the query
|
|
4149
4665
|
*/
|
|
4150
4666
|
async execute() {
|
|
4151
|
-
if (!this.queryDef.fetch && !this.queryDef.aggregate && !this.queryDef.search
|
|
4667
|
+
if (!this.queryDef.fetch && !this.queryDef.aggregate && !this.queryDef.search) {
|
|
4152
4668
|
throw new Error(
|
|
4153
|
-
"Query must have at least one of: fetch, aggregate
|
|
4669
|
+
"Query must have at least one of: fetch, aggregate or search. Use .from(), .aggregate() or .search()"
|
|
4154
4670
|
);
|
|
4155
4671
|
}
|
|
4156
4672
|
const body = {
|
|
4157
4673
|
query: this.queryDef
|
|
4158
4674
|
};
|
|
4159
|
-
const parameters = withClientLocale(this.parameters, this.
|
|
4675
|
+
const parameters = withClientLocale(this.parameters, this.clientLocale());
|
|
4160
4676
|
if (parameters !== void 0 && Object.keys(parameters).length > 0) {
|
|
4161
4677
|
body.parameters = parameters;
|
|
4162
4678
|
}
|
|
@@ -4189,7 +4705,7 @@ var _AdhocQueryBuilder = class _AdhocQueryBuilder {
|
|
|
4189
4705
|
* Clone the builder
|
|
4190
4706
|
*/
|
|
4191
4707
|
clone() {
|
|
4192
|
-
const cloned = new _AdhocQueryBuilder(this.httpClient, this.
|
|
4708
|
+
const cloned = new _AdhocQueryBuilder(this.httpClient, this.clientLocale);
|
|
4193
4709
|
cloned.queryDef = JSON.parse(JSON.stringify(this.queryDef));
|
|
4194
4710
|
cloned.parameters = { ...this.parameters };
|
|
4195
4711
|
cloned.options = { ...this.options };
|
|
@@ -4200,18 +4716,21 @@ __name(_AdhocQueryBuilder, "AdhocQueryBuilder");
|
|
|
4200
4716
|
var AdhocQueryBuilder = _AdhocQueryBuilder;
|
|
4201
4717
|
var _AdhocQueryClient = class _AdhocQueryClient {
|
|
4202
4718
|
/**
|
|
4203
|
-
* @param
|
|
4204
|
-
* every query that binds none
|
|
4719
|
+
* @param locale - The client's locale (`withLocale()`, `updateConfig({ locale })`), sent as the
|
|
4720
|
+
* `locale` parameter of every query that binds none: the function `TypedRelyIOClient` hands
|
|
4721
|
+
* over, or a locale fixed for this client
|
|
4205
4722
|
*/
|
|
4206
|
-
constructor(httpClient,
|
|
4723
|
+
constructor(httpClient, locale) {
|
|
4207
4724
|
this.httpClient = httpClient;
|
|
4208
|
-
this.
|
|
4725
|
+
/** The client's locale, asked on every query rather than copied when this client was taken. */
|
|
4726
|
+
__publicField(this, "clientLocale");
|
|
4727
|
+
this.clientLocale = readClientLocale(locale);
|
|
4209
4728
|
}
|
|
4210
4729
|
/**
|
|
4211
4730
|
* Create a new query builder
|
|
4212
4731
|
*/
|
|
4213
4732
|
build() {
|
|
4214
|
-
return new AdhocQueryBuilder(this.httpClient, this.
|
|
4733
|
+
return new AdhocQueryBuilder(this.httpClient, this.clientLocale);
|
|
4215
4734
|
}
|
|
4216
4735
|
/**
|
|
4217
4736
|
* Execute a query definition directly
|
|
@@ -4226,7 +4745,7 @@ var _AdhocQueryClient = class _AdhocQueryClient {
|
|
|
4226
4745
|
const body = {
|
|
4227
4746
|
query: definition
|
|
4228
4747
|
};
|
|
4229
|
-
const sent = withClientLocale(parameters, this.
|
|
4748
|
+
const sent = withClientLocale(parameters, this.clientLocale());
|
|
4230
4749
|
if (sent !== void 0 && Object.keys(sent).length > 0) {
|
|
4231
4750
|
body.parameters = sent;
|
|
4232
4751
|
}
|
|
@@ -4275,23 +4794,67 @@ var AdhocQueryClient = _AdhocQueryClient;
|
|
|
4275
4794
|
|
|
4276
4795
|
// src/changes/changes.ts
|
|
4277
4796
|
var CHANGES_POLL_TIMEOUT_MS = 25e3 + 5e3;
|
|
4797
|
+
var EXPIRED_REASONS = {
|
|
4798
|
+
retention: "retention",
|
|
4799
|
+
"dataset-replaced": "dataset-replaced",
|
|
4800
|
+
"ahead-of-head": "ahead-of-head",
|
|
4801
|
+
"foreign-cursor": "foreign-cursor",
|
|
4802
|
+
"too-far-behind": "too-far-behind"
|
|
4803
|
+
};
|
|
4278
4804
|
var _ChangesExpiredError = class _ChangesExpiredError extends ResourceExpiredError {
|
|
4279
|
-
/** The base class's signature, so `withContext` can rebuild one. */
|
|
4280
|
-
constructor(message, statusCode = 410, cause, context) {
|
|
4805
|
+
/** The base class's signature, so `withContext` can rebuild one; then the position resynced to. */
|
|
4806
|
+
constructor(message, statusCode = 410, cause, context, fresh) {
|
|
4281
4807
|
super(message, statusCode, cause, context);
|
|
4282
4808
|
__publicField(this, "reason");
|
|
4809
|
+
/**
|
|
4810
|
+
* The position the feed resynced to, which a refetch of everything names as `?_fresh`, as a
|
|
4811
|
+
* change's {@link FeedChange.fresh} is named: the head a `/v1/changes` resync took, or the cursor
|
|
4812
|
+
* the edge's `feed.resync` sent. `undefined` when there was no resync to take one — the error was
|
|
4813
|
+
* thrown (`onExpired: 'throw'`) — and on an error a caller built itself.
|
|
4814
|
+
*/
|
|
4815
|
+
__publicField(this, "fresh");
|
|
4283
4816
|
this.name = "ChangesExpiredError";
|
|
4284
4817
|
this.reason = expiredReasonOf(message);
|
|
4818
|
+
this.fresh = fresh;
|
|
4819
|
+
}
|
|
4820
|
+
/** The base class rebuilds from its four arguments, which would drop the position resynced to. */
|
|
4821
|
+
withContext(context) {
|
|
4822
|
+
const Constructor = this.constructor;
|
|
4823
|
+
return new Constructor(
|
|
4824
|
+
this.message,
|
|
4825
|
+
this.statusCode,
|
|
4826
|
+
this.cause,
|
|
4827
|
+
{ ...this.context, ...context },
|
|
4828
|
+
this.fresh
|
|
4829
|
+
);
|
|
4830
|
+
}
|
|
4831
|
+
/** `/v1/changes`' refusal, with the head the feed resynced to when it did. */
|
|
4832
|
+
static of(expired, fresh) {
|
|
4833
|
+
return new _ChangesExpiredError(
|
|
4834
|
+
expired.message,
|
|
4835
|
+
expired.statusCode,
|
|
4836
|
+
expired,
|
|
4837
|
+
expired.context,
|
|
4838
|
+
fresh
|
|
4839
|
+
);
|
|
4285
4840
|
}
|
|
4286
|
-
|
|
4287
|
-
|
|
4841
|
+
/** The live edge's `feed.resync`: refetch everything, then continue from the cursor it sent. */
|
|
4842
|
+
static resync(reason, cursor) {
|
|
4843
|
+
return new _ChangesExpiredError(
|
|
4844
|
+
`The live edge cannot list the changes since this cursor (${reason}). Refetch everything derived from the feed; it continues from the position it sent.`,
|
|
4845
|
+
410,
|
|
4846
|
+
void 0,
|
|
4847
|
+
{ transport: "live-edge" },
|
|
4848
|
+
cursor
|
|
4849
|
+
);
|
|
4288
4850
|
}
|
|
4289
4851
|
};
|
|
4290
4852
|
__name(_ChangesExpiredError, "ChangesExpiredError");
|
|
4291
4853
|
var ChangesExpiredError = _ChangesExpiredError;
|
|
4292
4854
|
function expiredReasonOf(message) {
|
|
4293
|
-
|
|
4294
|
-
|
|
4855
|
+
for (const reason of Object.values(EXPIRED_REASONS)) {
|
|
4856
|
+
if (message.includes(`(${reason})`)) return reason;
|
|
4857
|
+
}
|
|
4295
4858
|
return "unknown";
|
|
4296
4859
|
}
|
|
4297
4860
|
__name(expiredReasonOf, "expiredReasonOf");
|
|
@@ -4311,7 +4874,7 @@ async function* pollChanges(http, options = {}) {
|
|
|
4311
4874
|
const onExpired = options.onExpired ?? "throw";
|
|
4312
4875
|
if (onExpired === "throw") throw expired;
|
|
4313
4876
|
since = void 0;
|
|
4314
|
-
resynced = typeof onExpired === "function" ?
|
|
4877
|
+
resynced = typeof onExpired === "function" ? error : void 0;
|
|
4315
4878
|
continue;
|
|
4316
4879
|
}
|
|
4317
4880
|
const { data, page } = response.body;
|
|
@@ -4320,13 +4883,13 @@ async function* pollChanges(http, options = {}) {
|
|
|
4320
4883
|
}
|
|
4321
4884
|
since = page.nextCursor;
|
|
4322
4885
|
if (resynced !== void 0 && typeof options.onExpired === "function") {
|
|
4323
|
-
const expired = resynced;
|
|
4886
|
+
const expired = ChangesExpiredError.of(resynced, page.nextCursor);
|
|
4324
4887
|
resynced = void 0;
|
|
4325
4888
|
await options.onExpired(expired);
|
|
4326
4889
|
if (isAborted(signal)) return;
|
|
4327
4890
|
}
|
|
4328
4891
|
for (const change of data) {
|
|
4329
|
-
yield change;
|
|
4892
|
+
yield { ...change, fresh: change.cursor };
|
|
4330
4893
|
if (isAborted(signal)) return;
|
|
4331
4894
|
}
|
|
4332
4895
|
if (data.length === 0) {
|
|
@@ -4365,6 +4928,477 @@ async function pollOnce(http, since, options) {
|
|
|
4365
4928
|
}
|
|
4366
4929
|
__name(pollOnce, "pollOnce");
|
|
4367
4930
|
|
|
4931
|
+
// src/config/key-kind.ts
|
|
4932
|
+
var PUBLISHABLE_KEY_PREFIX = "rly_pub_";
|
|
4933
|
+
var isPublishableKey = /* @__PURE__ */ __name((apiKey) => apiKey?.startsWith(PUBLISHABLE_KEY_PREFIX) === true, "isPublishableKey");
|
|
4934
|
+
|
|
4935
|
+
// src/errors/live-errors.ts
|
|
4936
|
+
var _LiveEdgeError = class _LiveEdgeError extends SDKError {
|
|
4937
|
+
constructor(message, end, retry, options = {}) {
|
|
4938
|
+
super(message, options.statusCode, options.cause, options.context);
|
|
4939
|
+
__publicField(this, "code", "LIVE_EDGE_ENDED");
|
|
4940
|
+
__publicField(this, "retryable");
|
|
4941
|
+
__publicField(this, "end");
|
|
4942
|
+
__publicField(this, "retry");
|
|
4943
|
+
/** A refused poll's `Retry-After`, in milliseconds, where it carried a readable one. */
|
|
4944
|
+
__publicField(this, "retryAfterMs");
|
|
4945
|
+
this.end = end;
|
|
4946
|
+
this.retry = retry;
|
|
4947
|
+
this.retryable = retry !== "stop";
|
|
4948
|
+
this.retryAfterMs = options.retryAfterMs;
|
|
4949
|
+
}
|
|
4950
|
+
/**
|
|
4951
|
+
* The base class rebuilds an error through its own four-argument constructor, which this class
|
|
4952
|
+
* does not have; rebuilt that way, `end` would be a string and `retry` a number.
|
|
4953
|
+
*/
|
|
4954
|
+
withContext(context) {
|
|
4955
|
+
const rebuilt = new _LiveEdgeError(this.message, this.end, this.retry, {
|
|
4956
|
+
...this.statusCode === void 0 ? {} : { statusCode: this.statusCode },
|
|
4957
|
+
...this.cause === void 0 ? {} : { cause: this.cause },
|
|
4958
|
+
...this.retryAfterMs === void 0 ? {} : { retryAfterMs: this.retryAfterMs },
|
|
4959
|
+
context: { ...this.context, ...context }
|
|
4960
|
+
});
|
|
4961
|
+
return rebuilt;
|
|
4962
|
+
}
|
|
4963
|
+
getUserMessage() {
|
|
4964
|
+
return this.message;
|
|
4965
|
+
}
|
|
4966
|
+
};
|
|
4967
|
+
__name(_LiveEdgeError, "LiveEdgeError");
|
|
4968
|
+
var LiveEdgeError = _LiveEdgeError;
|
|
4969
|
+
|
|
4970
|
+
// src/config/live-url.ts
|
|
4971
|
+
var LIVE_EDGE_OF_API = {
|
|
4972
|
+
"https://api.relyio.com": "https://live.relyio.com"
|
|
4973
|
+
};
|
|
4974
|
+
var HTTP_SCHEME_OF = {
|
|
4975
|
+
"http:": "http:",
|
|
4976
|
+
"https:": "https:",
|
|
4977
|
+
"ws:": "http:",
|
|
4978
|
+
"wss:": "https:"
|
|
4979
|
+
};
|
|
4980
|
+
function liveEdgeAddress(config) {
|
|
4981
|
+
if (config.liveUrl !== void 0) return readLiveUrl(config.liveUrl);
|
|
4982
|
+
const derived = originOf(config.baseUrl);
|
|
4983
|
+
const edge = derived === void 0 ? void 0 : LIVE_EDGE_OF_API[derived];
|
|
4984
|
+
if (edge !== void 0) return { ok: true, url: new URL(edge) };
|
|
4985
|
+
return {
|
|
4986
|
+
ok: false,
|
|
4987
|
+
message: `Live updates need the address of RelyIO\u2019s live edge, and this client cannot derive it: \`baseUrl\` is not RelyIO\u2019s API (${Object.keys(LIVE_EDGE_OF_API).join(", ")}). Set \`liveUrl\` in createClient() to the live edge this API belongs with. Nothing was sent.`
|
|
4988
|
+
};
|
|
4989
|
+
}
|
|
4990
|
+
__name(liveEdgeAddress, "liveEdgeAddress");
|
|
4991
|
+
function readLiveUrl(liveUrl) {
|
|
4992
|
+
let url;
|
|
4993
|
+
try {
|
|
4994
|
+
url = new URL(liveUrl);
|
|
4995
|
+
} catch {
|
|
4996
|
+
return { ok: false, message: `\`liveUrl\` is not a URL: "${liveUrl}". Nothing was sent.` };
|
|
4997
|
+
}
|
|
4998
|
+
const scheme = HTTP_SCHEME_OF[url.protocol];
|
|
4999
|
+
if (scheme === void 0) {
|
|
5000
|
+
return {
|
|
5001
|
+
ok: false,
|
|
5002
|
+
message: `\`liveUrl\` must be an https:, wss:, http: or ws: URL, not ${url.protocol}. Nothing was sent.`
|
|
5003
|
+
};
|
|
5004
|
+
}
|
|
5005
|
+
url.protocol = scheme;
|
|
5006
|
+
url.search = "";
|
|
5007
|
+
url.hash = "";
|
|
5008
|
+
return { ok: true, url };
|
|
5009
|
+
}
|
|
5010
|
+
__name(readLiveUrl, "readLiveUrl");
|
|
5011
|
+
function originOf(baseUrl) {
|
|
5012
|
+
try {
|
|
5013
|
+
return new URL(baseUrl).origin;
|
|
5014
|
+
} catch {
|
|
5015
|
+
return void 0;
|
|
5016
|
+
}
|
|
5017
|
+
}
|
|
5018
|
+
__name(originOf, "originOf");
|
|
5019
|
+
|
|
5020
|
+
// src/changes/live-edge.ts
|
|
5021
|
+
var LIVE_KEEPALIVE_INTERVAL_MS = 3e4;
|
|
5022
|
+
var LIVE_POLLS_BEFORE_SOCKET = 4;
|
|
5023
|
+
async function* subscribeLiveEdge(client, options = {}) {
|
|
5024
|
+
const { signal } = options;
|
|
5025
|
+
if (isAborted(signal)) return;
|
|
5026
|
+
const key = client.apiKey;
|
|
5027
|
+
if (key === void 0 || !isPublishableKey(key)) {
|
|
5028
|
+
throw new LiveEdgeError(NEEDS_PUBLISHABLE_KEY, { kind: "needs-publishable-key" }, "stop");
|
|
5029
|
+
}
|
|
5030
|
+
const address = liveEdgeAddress(client);
|
|
5031
|
+
if (!address.ok) throw new LiveEdgeError(address.message, { kind: "needs-live-url" }, "stop");
|
|
5032
|
+
const request = {
|
|
5033
|
+
base: address.url,
|
|
5034
|
+
key,
|
|
5035
|
+
dataset: client.dataset,
|
|
5036
|
+
types: options.types,
|
|
5037
|
+
ids: options.ids
|
|
5038
|
+
};
|
|
5039
|
+
const reach = { position: options.since };
|
|
5040
|
+
for (; ; ) {
|
|
5041
|
+
const socket = openSocket(client, edgeUrl(request, reach.position, "socket"));
|
|
5042
|
+
if (socket !== void 0) {
|
|
5043
|
+
const ended = yield* followSocket(socket, reach, options);
|
|
5044
|
+
if (ended === "aborted") return;
|
|
5045
|
+
}
|
|
5046
|
+
const polled = yield* followPolls(client, request, reach, options);
|
|
5047
|
+
if (polled === "aborted") return;
|
|
5048
|
+
}
|
|
5049
|
+
}
|
|
5050
|
+
__name(subscribeLiveEdge, "subscribeLiveEdge");
|
|
5051
|
+
var SOCKET_CONNECTING = 0;
|
|
5052
|
+
var SOCKET_OPEN = 1;
|
|
5053
|
+
function runtimeWebSocket() {
|
|
5054
|
+
const candidate = globalThis.WebSocket;
|
|
5055
|
+
return typeof candidate === "function" ? candidate : void 0;
|
|
5056
|
+
}
|
|
5057
|
+
__name(runtimeWebSocket, "runtimeWebSocket");
|
|
5058
|
+
function openSocket(client, url) {
|
|
5059
|
+
const Socket = client.webSocket ?? runtimeWebSocket();
|
|
5060
|
+
if (Socket === void 0) return void 0;
|
|
5061
|
+
try {
|
|
5062
|
+
return new Socket(url);
|
|
5063
|
+
} catch {
|
|
5064
|
+
return void 0;
|
|
5065
|
+
}
|
|
5066
|
+
}
|
|
5067
|
+
__name(openSocket, "openSocket");
|
|
5068
|
+
async function* followSocket(socket, reach, options) {
|
|
5069
|
+
const { signal } = options;
|
|
5070
|
+
const inbox = socketInbox(socket, signal);
|
|
5071
|
+
let open = false;
|
|
5072
|
+
try {
|
|
5073
|
+
for (; ; ) {
|
|
5074
|
+
const event = await inbox.next();
|
|
5075
|
+
if (event === void 0) return "aborted";
|
|
5076
|
+
switch (event.kind) {
|
|
5077
|
+
case "open": {
|
|
5078
|
+
open = true;
|
|
5079
|
+
inbox.keepAlive();
|
|
5080
|
+
continue;
|
|
5081
|
+
}
|
|
5082
|
+
case "error": {
|
|
5083
|
+
if (!open) return "unopened";
|
|
5084
|
+
continue;
|
|
5085
|
+
}
|
|
5086
|
+
case "close": {
|
|
5087
|
+
if (!open) return "unopened";
|
|
5088
|
+
throw closedBy(event.code, event.reason);
|
|
5089
|
+
}
|
|
5090
|
+
case "silent": {
|
|
5091
|
+
throw new LiveEdgeError(
|
|
5092
|
+
"The live edge\u2019s socket stopped answering its keepalive, so the connection is presumed dead; reconnecting from the last cursor.",
|
|
5093
|
+
{ kind: "lost" },
|
|
5094
|
+
"retry"
|
|
5095
|
+
);
|
|
5096
|
+
}
|
|
5097
|
+
case "message": {
|
|
5098
|
+
if (event.data === VISITOR_KEEPALIVE.response) continue;
|
|
5099
|
+
const frame = typeof event.data === "string" ? parseVisitorFrame(event.data) : null;
|
|
5100
|
+
if (frame === null)
|
|
5101
|
+
throw unreadable(`a frame the wire does not declare: ${sample(event.data)}`);
|
|
5102
|
+
switch (frame.type) {
|
|
5103
|
+
case VISITOR_FRAMES.opened: {
|
|
5104
|
+
reach.position = frame.cursor;
|
|
5105
|
+
options.opened?.();
|
|
5106
|
+
options.reached?.(frame.cursor);
|
|
5107
|
+
continue;
|
|
5108
|
+
}
|
|
5109
|
+
case VISITOR_FRAMES.changes: {
|
|
5110
|
+
const prior = reach.position;
|
|
5111
|
+
if (prior === void 0) throw unreadable("changes before the socket had a position");
|
|
5112
|
+
for (const change of changesOf(frame.changes, frame.cursor, prior)) {
|
|
5113
|
+
yield change;
|
|
5114
|
+
if (isAborted(signal)) return "aborted";
|
|
5115
|
+
}
|
|
5116
|
+
reach.position = frame.cursor;
|
|
5117
|
+
if (frame.changes.length === 0) options.reached?.(frame.cursor);
|
|
5118
|
+
continue;
|
|
5119
|
+
}
|
|
5120
|
+
case VISITOR_FRAMES.resync: {
|
|
5121
|
+
await expire(frame.reason, frame.cursor, options);
|
|
5122
|
+
if (isAborted(signal)) return "aborted";
|
|
5123
|
+
reach.position = frame.cursor;
|
|
5124
|
+
options.reached?.(frame.cursor);
|
|
5125
|
+
continue;
|
|
5126
|
+
}
|
|
5127
|
+
}
|
|
5128
|
+
}
|
|
5129
|
+
}
|
|
5130
|
+
}
|
|
5131
|
+
} finally {
|
|
5132
|
+
inbox.dispose();
|
|
5133
|
+
}
|
|
5134
|
+
}
|
|
5135
|
+
__name(followSocket, "followSocket");
|
|
5136
|
+
function socketInbox(socket, signal) {
|
|
5137
|
+
const events = [];
|
|
5138
|
+
let wake;
|
|
5139
|
+
let disposed = false;
|
|
5140
|
+
let heard = true;
|
|
5141
|
+
let keepalive;
|
|
5142
|
+
const rouse = /* @__PURE__ */ __name(() => {
|
|
5143
|
+
const waking = wake;
|
|
5144
|
+
wake = void 0;
|
|
5145
|
+
waking?.();
|
|
5146
|
+
}, "rouse");
|
|
5147
|
+
const push = /* @__PURE__ */ __name((event) => {
|
|
5148
|
+
if (disposed) return;
|
|
5149
|
+
events.push(event);
|
|
5150
|
+
rouse();
|
|
5151
|
+
}, "push");
|
|
5152
|
+
socket.addEventListener("open", () => push({ kind: "open" }));
|
|
5153
|
+
socket.addEventListener("message", (event) => {
|
|
5154
|
+
heard = true;
|
|
5155
|
+
push({ kind: "message", data: isRecord(event) ? event["data"] : void 0 });
|
|
5156
|
+
});
|
|
5157
|
+
socket.addEventListener("close", (event) => {
|
|
5158
|
+
const code = isRecord(event) ? event["code"] : void 0;
|
|
5159
|
+
const reason = isRecord(event) ? event["reason"] : void 0;
|
|
5160
|
+
push({
|
|
5161
|
+
kind: "close",
|
|
5162
|
+
// A close event always carries both; a socket that somehow raised one without them closed
|
|
5163
|
+
// abnormally, which is what 1006 says.
|
|
5164
|
+
code: typeof code === "number" ? code : 1006,
|
|
5165
|
+
reason: typeof reason === "string" ? reason : ""
|
|
5166
|
+
});
|
|
5167
|
+
});
|
|
5168
|
+
socket.addEventListener("error", () => push({ kind: "error" }));
|
|
5169
|
+
signal?.addEventListener("abort", rouse, { once: true });
|
|
5170
|
+
return {
|
|
5171
|
+
async next() {
|
|
5172
|
+
for (; ; ) {
|
|
5173
|
+
if (isAborted(signal)) return void 0;
|
|
5174
|
+
const event = events.shift();
|
|
5175
|
+
if (event !== void 0) return event;
|
|
5176
|
+
await new Promise((resolve) => {
|
|
5177
|
+
wake = resolve;
|
|
5178
|
+
});
|
|
5179
|
+
}
|
|
5180
|
+
},
|
|
5181
|
+
keepAlive() {
|
|
5182
|
+
keepalive = setInterval(() => {
|
|
5183
|
+
if (!heard) {
|
|
5184
|
+
push({ kind: "silent" });
|
|
5185
|
+
return;
|
|
5186
|
+
}
|
|
5187
|
+
heard = false;
|
|
5188
|
+
if (socket.readyState === SOCKET_OPEN) socket.send(VISITOR_KEEPALIVE.request);
|
|
5189
|
+
}, LIVE_KEEPALIVE_INTERVAL_MS);
|
|
5190
|
+
},
|
|
5191
|
+
dispose() {
|
|
5192
|
+
disposed = true;
|
|
5193
|
+
if (keepalive !== void 0) clearInterval(keepalive);
|
|
5194
|
+
signal?.removeEventListener("abort", rouse);
|
|
5195
|
+
if (socket.readyState === SOCKET_CONNECTING || socket.readyState === SOCKET_OPEN) {
|
|
5196
|
+
socket.close(1e3);
|
|
5197
|
+
}
|
|
5198
|
+
}
|
|
5199
|
+
};
|
|
5200
|
+
}
|
|
5201
|
+
__name(socketInbox, "socketInbox");
|
|
5202
|
+
function closedBy(code, reason) {
|
|
5203
|
+
const retry = closeRetryOf(code);
|
|
5204
|
+
const meaning = hasOwn(CLOSE_MEANING, code) ? CLOSE_MEANING[code] : code < 4e3 ? "the connection dropped" : "a refusal this version of the SDK does not know";
|
|
5205
|
+
const said = `close ${code}${reason === "" ? "" : ` ${reason}`}`;
|
|
5206
|
+
return new LiveEdgeError(
|
|
5207
|
+
`The live edge ended this subscription (${said}): ${meaning}. ${AFTER_A_CLOSE[retry]}`,
|
|
5208
|
+
{ kind: "closed", code, reason },
|
|
5209
|
+
retry
|
|
5210
|
+
);
|
|
5211
|
+
}
|
|
5212
|
+
__name(closedBy, "closedBy");
|
|
5213
|
+
var AFTER_A_CLOSE = {
|
|
5214
|
+
stop: "The same request would be refused again, so it was not retried.",
|
|
5215
|
+
retry: "Reconnecting from the last cursor after a short back-off.",
|
|
5216
|
+
"back-off": "Retrying after a back-off of at least a minute, without polling, which the same admission refuses."
|
|
5217
|
+
};
|
|
5218
|
+
function closeRetryOf(code) {
|
|
5219
|
+
if (hasOwn(VISITOR_CLOSE_RETRY, code)) return VISITOR_CLOSE_RETRY[code];
|
|
5220
|
+
return code < 4e3 ? "retry" : "stop";
|
|
5221
|
+
}
|
|
5222
|
+
__name(closeRetryOf, "closeRetryOf");
|
|
5223
|
+
var CLOSE_MEANING = {
|
|
5224
|
+
[VISITOR_CLOSE_CODES.requestInvalid]: "the edge could not read the request \u2014 a malformed cursor or filter",
|
|
5225
|
+
[VISITOR_CLOSE_CODES.keyRefused]: "the key is not a publishable key the edge accepts \u2014 unknown, revoked or expired",
|
|
5226
|
+
[VISITOR_CLOSE_CODES.originRefused]: "this page\u2019s origin is not on the publishable key\u2019s allowed origins",
|
|
5227
|
+
[VISITOR_CLOSE_CODES.datasetRefused]: "`dataset` names no dataset the key reaches",
|
|
5228
|
+
[VISITOR_CLOSE_CODES.unavailable]: "the edge could not reach what it needed to decide just now",
|
|
5229
|
+
[VISITOR_CLOSE_CODES.allowanceReached]: "the project holds as many live connections as its plan allows",
|
|
5230
|
+
[VISITOR_CLOSE_CODES.addressLimitReached]: "this address holds as many live connections to the project as one address may",
|
|
5231
|
+
[VISITOR_CLOSE_CODES.ceilingReached]: "the project is at its safety ceiling of live connections"
|
|
5232
|
+
};
|
|
5233
|
+
async function* followPolls(client, request, reach, options) {
|
|
5234
|
+
const { signal } = options;
|
|
5235
|
+
for (let poll = 0; poll < LIVE_POLLS_BEFORE_SOCKET; poll += 1) {
|
|
5236
|
+
if (isAborted(signal)) return "aborted";
|
|
5237
|
+
const answer = await pollOnce2(client, request, reach.position, signal);
|
|
5238
|
+
if (answer === void 0) return "aborted";
|
|
5239
|
+
options.opened?.();
|
|
5240
|
+
if (answer.kind === "resync") {
|
|
5241
|
+
await expire(answer.reason, answer.cursor, options);
|
|
5242
|
+
if (isAborted(signal)) return "aborted";
|
|
5243
|
+
reach.position = answer.cursor;
|
|
5244
|
+
options.reached?.(answer.cursor);
|
|
5245
|
+
} else {
|
|
5246
|
+
const prior = reach.position;
|
|
5247
|
+
if (answer.changes.length > 0 && prior === void 0) {
|
|
5248
|
+
throw unreadable("a poll without a cursor answered changes");
|
|
5249
|
+
}
|
|
5250
|
+
for (const change of changesOf(answer.changes, answer.cursor, prior ?? answer.cursor)) {
|
|
5251
|
+
yield change;
|
|
5252
|
+
if (isAborted(signal)) return "aborted";
|
|
5253
|
+
}
|
|
5254
|
+
reach.position = answer.cursor;
|
|
5255
|
+
if (answer.changes.length === 0) options.reached?.(answer.cursor);
|
|
5256
|
+
}
|
|
5257
|
+
await sleep(VISITOR_POLL_INTERVAL_MS, signal);
|
|
5258
|
+
}
|
|
5259
|
+
return isAborted(signal) ? "aborted" : "socket";
|
|
5260
|
+
}
|
|
5261
|
+
__name(followPolls, "followPolls");
|
|
5262
|
+
async function pollOnce2(client, request, since, signal) {
|
|
5263
|
+
if (isAborted(signal)) return void 0;
|
|
5264
|
+
const url = edgeUrl(request, since, "poll", client.clientId);
|
|
5265
|
+
const context = { url: edgeUrl({ ...request, key: void 0 }, since, "poll", client.clientId) };
|
|
5266
|
+
const controller = new AbortController();
|
|
5267
|
+
let timedOut = false;
|
|
5268
|
+
const timer = setTimeout(() => {
|
|
5269
|
+
timedOut = true;
|
|
5270
|
+
controller.abort();
|
|
5271
|
+
}, client.timeout);
|
|
5272
|
+
const onAbort = /* @__PURE__ */ __name(() => controller.abort(), "onAbort");
|
|
5273
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
5274
|
+
try {
|
|
5275
|
+
let response;
|
|
5276
|
+
let text;
|
|
5277
|
+
try {
|
|
5278
|
+
const send = client.fetch ?? globalThis.fetch;
|
|
5279
|
+
response = await send(url, { method: "GET", signal: controller.signal });
|
|
5280
|
+
text = await response.text();
|
|
5281
|
+
} catch (error) {
|
|
5282
|
+
if (isAborted(signal)) return void 0;
|
|
5283
|
+
if (timedOut) {
|
|
5284
|
+
throw new TimeoutError(
|
|
5285
|
+
`The live edge did not answer a poll within ${client.timeout}ms`,
|
|
5286
|
+
client.timeout,
|
|
5287
|
+
void 0,
|
|
5288
|
+
void 0,
|
|
5289
|
+
context
|
|
5290
|
+
);
|
|
5291
|
+
}
|
|
5292
|
+
throw ErrorMapper.mapGenericError(
|
|
5293
|
+
error instanceof Error ? error : new Error(String(error)),
|
|
5294
|
+
context
|
|
5295
|
+
);
|
|
5296
|
+
}
|
|
5297
|
+
const body = parseJson(text);
|
|
5298
|
+
if (!response.ok) throw refusedPoll(response, body, context);
|
|
5299
|
+
const answer = readVisitorPollAnswer(isRecord(body) ? body["data"] : void 0);
|
|
5300
|
+
if (answer === null)
|
|
5301
|
+
throw unreadable(`a poll answer the wire does not declare: ${sample(text)}`);
|
|
5302
|
+
return answer;
|
|
5303
|
+
} finally {
|
|
5304
|
+
clearTimeout(timer);
|
|
5305
|
+
signal?.removeEventListener("abort", onAbort);
|
|
5306
|
+
}
|
|
5307
|
+
}
|
|
5308
|
+
__name(pollOnce2, "pollOnce");
|
|
5309
|
+
function refusedPoll(response, body, context) {
|
|
5310
|
+
const headers = {};
|
|
5311
|
+
response.headers.forEach((value, name) => {
|
|
5312
|
+
headers[name] = value;
|
|
5313
|
+
});
|
|
5314
|
+
const mapped = ErrorMapper.mapHttpError(
|
|
5315
|
+
{ status: response.status, statusText: response.statusText, body, headers },
|
|
5316
|
+
{ ...context, method: "GET" }
|
|
5317
|
+
);
|
|
5318
|
+
const refusal = readError(body);
|
|
5319
|
+
if (refusal === void 0 || !hasOwn(VISITOR_POLL_RETRY, refusal.code)) return mapped;
|
|
5320
|
+
const code = refusal.code;
|
|
5321
|
+
const retryAfterMs = parseRetryAfterMs(headerValue(headers, "Retry-After"), Date.now());
|
|
5322
|
+
return new LiveEdgeError(
|
|
5323
|
+
`The live edge refused a poll (${code}): ${refusal.messages[0] ?? mapped.message}`,
|
|
5324
|
+
{ kind: "refused", code },
|
|
5325
|
+
VISITOR_POLL_RETRY[code],
|
|
5326
|
+
{
|
|
5327
|
+
statusCode: response.status,
|
|
5328
|
+
cause: mapped,
|
|
5329
|
+
...retryAfterMs === void 0 ? {} : { retryAfterMs },
|
|
5330
|
+
context
|
|
5331
|
+
}
|
|
5332
|
+
);
|
|
5333
|
+
}
|
|
5334
|
+
__name(refusedPoll, "refusedPoll");
|
|
5335
|
+
function edgeUrl(request, since, transport, clientId) {
|
|
5336
|
+
const url = new URL(request.base.href);
|
|
5337
|
+
url.pathname = `${url.pathname.replace(/\/+$/, "")}${VISITOR_LIVE_PATH}`;
|
|
5338
|
+
if (transport === "socket") url.protocol = url.protocol === "http:" ? "ws:" : "wss:";
|
|
5339
|
+
const query = new URLSearchParams();
|
|
5340
|
+
if (request.key !== void 0) query.set(VISITOR_QUERY.key, request.key);
|
|
5341
|
+
if (request.dataset !== void 0 && request.dataset !== "") {
|
|
5342
|
+
query.set(VISITOR_QUERY.dataset, request.dataset);
|
|
5343
|
+
}
|
|
5344
|
+
if (since !== void 0) query.set(VISITOR_QUERY.since, since);
|
|
5345
|
+
if (request.types !== void 0) query.set(VISITOR_QUERY.types, request.types.join(","));
|
|
5346
|
+
if (request.ids !== void 0) query.set(VISITOR_QUERY.ids, request.ids.join(","));
|
|
5347
|
+
if (transport === "poll" && clientId !== void 0) query.set(VISITOR_QUERY.client, clientId);
|
|
5348
|
+
url.search = query.toString();
|
|
5349
|
+
return url.href;
|
|
5350
|
+
}
|
|
5351
|
+
__name(edgeUrl, "edgeUrl");
|
|
5352
|
+
function changesOf(changes, cursor, prior) {
|
|
5353
|
+
const last = changes.length - 1;
|
|
5354
|
+
return changes.map((change, index) => ({
|
|
5355
|
+
entryId: change.entryId,
|
|
5356
|
+
contentType: change.contentType,
|
|
5357
|
+
locale: change.locale,
|
|
5358
|
+
kind: change.kind,
|
|
5359
|
+
cursor: index === last ? cursor : prior,
|
|
5360
|
+
fresh: cursor
|
|
5361
|
+
}));
|
|
5362
|
+
}
|
|
5363
|
+
__name(changesOf, "changesOf");
|
|
5364
|
+
async function expire(reason, cursor, options) {
|
|
5365
|
+
const expired = ChangesExpiredError.resync(reason, cursor);
|
|
5366
|
+
const onExpired = options.onExpired ?? "throw";
|
|
5367
|
+
if (onExpired === "throw") throw expired;
|
|
5368
|
+
if (typeof onExpired === "function") await onExpired(expired);
|
|
5369
|
+
}
|
|
5370
|
+
__name(expire, "expire");
|
|
5371
|
+
function unreadable(what) {
|
|
5372
|
+
return new LiveEdgeError(
|
|
5373
|
+
`The live edge sent ${what}. This version of the SDK cannot follow it, so it stopped.`,
|
|
5374
|
+
{ kind: "unreadable", what },
|
|
5375
|
+
"stop"
|
|
5376
|
+
);
|
|
5377
|
+
}
|
|
5378
|
+
__name(unreadable, "unreadable");
|
|
5379
|
+
var NEEDS_PUBLISHABLE_KEY = `Live updates at RelyIO\u2019s edge need a publishable key (\`${PUBLISHABLE_KEY_PREFIX}\u2026\`), and this client holds none. A secret or preview key is never sent to the edge; on a server, follow \`changes()\` with it instead. Nothing was sent.`;
|
|
5380
|
+
function sample(value) {
|
|
5381
|
+
const text = typeof value === "string" ? value : `(${typeof value})`;
|
|
5382
|
+
return text.length > 200 ? `${text.slice(0, 200)}\u2026` : text;
|
|
5383
|
+
}
|
|
5384
|
+
__name(sample, "sample");
|
|
5385
|
+
function parseJson(text) {
|
|
5386
|
+
try {
|
|
5387
|
+
return JSON.parse(text);
|
|
5388
|
+
} catch {
|
|
5389
|
+
return void 0;
|
|
5390
|
+
}
|
|
5391
|
+
}
|
|
5392
|
+
__name(parseJson, "parseJson");
|
|
5393
|
+
function isRecord(value) {
|
|
5394
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5395
|
+
}
|
|
5396
|
+
__name(isRecord, "isRecord");
|
|
5397
|
+
function hasOwn(object, key) {
|
|
5398
|
+
return Object.prototype.hasOwnProperty.call(object, key);
|
|
5399
|
+
}
|
|
5400
|
+
__name(hasOwn, "hasOwn");
|
|
5401
|
+
|
|
4368
5402
|
// src/config/dev-detection.ts
|
|
4369
5403
|
function sandboxStateOf(resolution) {
|
|
4370
5404
|
return resolution.state === "active" ? resolution.sandbox : resolution;
|
|
@@ -4475,7 +5509,7 @@ var DEFAULT_CONFIG = {
|
|
|
4475
5509
|
environment: "production",
|
|
4476
5510
|
headers: {},
|
|
4477
5511
|
browser: {
|
|
4478
|
-
includeCredentials:
|
|
5512
|
+
includeCredentials: false,
|
|
4479
5513
|
useRelativeUrls: false
|
|
4480
5514
|
},
|
|
4481
5515
|
cache: {
|
|
@@ -4507,45 +5541,48 @@ function validateConfig(config) {
|
|
|
4507
5541
|
if (config.baseUrl != null && typeof config.baseUrl !== "string") {
|
|
4508
5542
|
throw new Error("Base URL must be a string");
|
|
4509
5543
|
}
|
|
4510
|
-
if (config.
|
|
4511
|
-
throw new Error("
|
|
5544
|
+
if (config.liveUrl != null && typeof config.liveUrl !== "string") {
|
|
5545
|
+
throw new Error("liveUrl must be a string");
|
|
5546
|
+
}
|
|
5547
|
+
if (config.webSocket != null && typeof config.webSocket !== "function") {
|
|
5548
|
+
throw new Error("webSocket must be a WebSocket class");
|
|
4512
5549
|
}
|
|
4513
|
-
if (config.
|
|
4514
|
-
|
|
5550
|
+
if (config.timeout != null) assertRequestTimeout(config.timeout);
|
|
5551
|
+
if (config.retryAttempts != null && !(Number.isInteger(config.retryAttempts) && config.retryAttempts >= 1)) {
|
|
5552
|
+
throw new Error(
|
|
5553
|
+
`retryAttempts must be a whole number of at least 1 \u2014 the most attempts one request makes, the first included, so 1 turns retrying off \u2014 and got ${String(config.retryAttempts)}`
|
|
5554
|
+
);
|
|
4515
5555
|
}
|
|
4516
5556
|
}
|
|
4517
5557
|
__name(validateConfig, "validateConfig");
|
|
4518
5558
|
var devOverrideNotified = false;
|
|
4519
5559
|
function resolveConfig(userConfig) {
|
|
4520
5560
|
validateConfig(userConfig);
|
|
4521
|
-
|
|
4522
|
-
|
|
4523
|
-
|
|
4524
|
-
|
|
4525
|
-
|
|
4526
|
-
|
|
4527
|
-
|
|
4528
|
-
|
|
4529
|
-
|
|
4530
|
-
|
|
4531
|
-
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
|
|
4543
|
-
|
|
4544
|
-
|
|
4545
|
-
const dev = resolveDevOverride(userConfig.dev);
|
|
5561
|
+
return withHandshake(withDefaults(DEFAULT_CONFIG, userConfig, DEFAULT_CONFIG));
|
|
5562
|
+
}
|
|
5563
|
+
__name(resolveConfig, "resolveConfig");
|
|
5564
|
+
function resolveConfigUpdate(configured, update) {
|
|
5565
|
+
validateConfig(update);
|
|
5566
|
+
const base = update.apiKey === void 0 ? configured : withoutKeyHeader(configured);
|
|
5567
|
+
return withHandshake(withDefaults(base, update, DEFAULT_CONFIG));
|
|
5568
|
+
}
|
|
5569
|
+
__name(resolveConfigUpdate, "resolveConfigUpdate");
|
|
5570
|
+
function withConfiguredKey(configured, apiKey) {
|
|
5571
|
+
return { ...withoutKeyHeader(configured), apiKey };
|
|
5572
|
+
}
|
|
5573
|
+
__name(withConfiguredKey, "withConfiguredKey");
|
|
5574
|
+
function withoutKeyHeader(configured) {
|
|
5575
|
+
return { ...configured, headers: layerHeaders(configured.headers, { [API_KEY_HEADER]: null }) };
|
|
5576
|
+
}
|
|
5577
|
+
__name(withoutKeyHeader, "withoutKeyHeader");
|
|
5578
|
+
function withHandshake(configured) {
|
|
5579
|
+
const dev = resolveDevOverride(configured.dev);
|
|
5580
|
+
const merged = withDefaults(
|
|
5581
|
+
configured,
|
|
5582
|
+
dev.state === "active" ? { apiKey: dev.apiKey, dataset: dev.sandbox.datasetId } : {},
|
|
5583
|
+
DEFAULT_CONFIG
|
|
5584
|
+
);
|
|
4546
5585
|
if (dev.state === "active") {
|
|
4547
|
-
merged.apiKey = dev.apiKey;
|
|
4548
|
-
merged.dataset = dev.sandbox.datasetId;
|
|
4549
5586
|
if (!devOverrideNotified) {
|
|
4550
5587
|
devOverrideNotified = true;
|
|
4551
5588
|
console.info(
|
|
@@ -4563,30 +5600,69 @@ function resolveConfig(userConfig) {
|
|
|
4563
5600
|
`[RelyIO] Your .relyio/dev.json could not be read (${dev.reason}) \u2014 ignoring it and using the configured dataset "${merged.dataset}". Run \`relyio dev\` to rewrite it.`
|
|
4564
5601
|
);
|
|
4565
5602
|
}
|
|
4566
|
-
return { config: merged, sandbox: sandboxStateOf(dev) };
|
|
5603
|
+
return { config: merged, sandbox: sandboxStateOf(dev), configured };
|
|
4567
5604
|
}
|
|
4568
|
-
__name(
|
|
5605
|
+
__name(withHandshake, "withHandshake");
|
|
5606
|
+
function withDefaults(base, given, defaults) {
|
|
5607
|
+
const merged = new Map(Object.entries(base));
|
|
5608
|
+
const givenValues = new Map(Object.entries(given ?? {}));
|
|
5609
|
+
const defaultValues = new Map(Object.entries(defaults));
|
|
5610
|
+
for (const key of /* @__PURE__ */ new Set([...merged.keys(), ...givenValues.keys()])) {
|
|
5611
|
+
const fallback = merged.get(key);
|
|
5612
|
+
const value = givenValues.get(key);
|
|
5613
|
+
const givenBag = isOptionBag(value) ? value : void 0;
|
|
5614
|
+
if (isOptionBag(fallback) && key === "headers") {
|
|
5615
|
+
merged.set(key, layerHeaders(fallback, givenBag));
|
|
5616
|
+
} else if (isOptionBag(fallback)) {
|
|
5617
|
+
const defaultBag = defaultValues.get(key);
|
|
5618
|
+
merged.set(key, withDefaults(fallback, givenBag, isOptionBag(defaultBag) ? defaultBag : {}));
|
|
5619
|
+
} else if (value === null) {
|
|
5620
|
+
if (defaultValues.has(key)) merged.set(key, defaultValues.get(key));
|
|
5621
|
+
else merged.delete(key);
|
|
5622
|
+
} else if (value !== void 0) {
|
|
5623
|
+
merged.set(key, value);
|
|
5624
|
+
}
|
|
5625
|
+
}
|
|
5626
|
+
return Object.fromEntries(merged);
|
|
5627
|
+
}
|
|
5628
|
+
__name(withDefaults, "withDefaults");
|
|
5629
|
+
function isOptionBag(value) {
|
|
5630
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5631
|
+
}
|
|
5632
|
+
__name(isOptionBag, "isOptionBag");
|
|
4569
5633
|
|
|
4570
5634
|
// src/client/create-client.ts
|
|
4571
5635
|
var API_VERSION_SEGMENT = "v1";
|
|
4572
5636
|
var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
4573
5637
|
constructor(config) {
|
|
5638
|
+
/**
|
|
5639
|
+
* The one HTTP client every sub-client sends through. `updateConfig()` and `setApiKey()` change it
|
|
5640
|
+
* in place rather than replacing it, so a sub-client taken before either sends through the change.
|
|
5641
|
+
*/
|
|
4574
5642
|
__publicField(this, "httpClient");
|
|
4575
5643
|
__publicField(this, "config");
|
|
5644
|
+
/**
|
|
5645
|
+
* The configuration as `createClient()`, `setApiKey()` and `updateConfig()` gave it, before the
|
|
5646
|
+
* `relyio dev` handshake: what the next update is laid over, and what `withLocale()` starts from.
|
|
5647
|
+
*/
|
|
5648
|
+
__publicField(this, "configured");
|
|
4576
5649
|
__publicField(this, "_sandbox");
|
|
4577
|
-
__publicField(this, "_search");
|
|
4578
|
-
__publicField(this, "_batch");
|
|
4579
|
-
__publicField(this, "_adhocQuery");
|
|
4580
5650
|
__publicField(this, "_schema");
|
|
5651
|
+
/**
|
|
5652
|
+
* The client's locale as it is when a call is made. Every sub-client and builder is handed this
|
|
5653
|
+
* rather than a copy of the locale, so one taken before `updateConfig({ locale })` sends the new
|
|
5654
|
+
* locale, or none after `locale: null` (REL-1453).
|
|
5655
|
+
*/
|
|
5656
|
+
__publicField(this, "currentLocale", /* @__PURE__ */ __name(() => this.config.locale, "currentLocale"));
|
|
5657
|
+
/** The live edge's poll client id: chosen once, when first needed, and kept for this client's life. */
|
|
5658
|
+
__publicField(this, "_liveClientId");
|
|
4581
5659
|
const { schema, ...clientConfig } = config;
|
|
4582
5660
|
this._schema = schema;
|
|
4583
5661
|
const resolved = resolveConfig(clientConfig);
|
|
4584
5662
|
this.config = resolved.config;
|
|
5663
|
+
this.configured = resolved.configured;
|
|
4585
5664
|
this._sandbox = resolved.sandbox;
|
|
4586
5665
|
this.httpClient = new HttpClient(this.config);
|
|
4587
|
-
this._search = new SearchClient(this.httpClient, this.config.locale);
|
|
4588
|
-
this._batch = new BatchClient(this.httpClient);
|
|
4589
|
-
this._adhocQuery = new AdhocQueryClient(this.httpClient, this.config.locale);
|
|
4590
5666
|
}
|
|
4591
5667
|
// ============================================================================
|
|
4592
5668
|
// CORE CLIENTS
|
|
@@ -4615,10 +5691,10 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4615
5691
|
return this._sandbox;
|
|
4616
5692
|
}
|
|
4617
5693
|
/**
|
|
4618
|
-
* Get the ad-hoc query client
|
|
5694
|
+
* Get the ad-hoc query client. Every query it sends is in the client's locale as it is then.
|
|
4619
5695
|
*/
|
|
4620
5696
|
get query() {
|
|
4621
|
-
return this.
|
|
5697
|
+
return new AdhocQueryClient(this.httpClient, this.currentLocale);
|
|
4622
5698
|
}
|
|
4623
5699
|
// ============================================================================
|
|
4624
5700
|
// CONTENT OPERATIONS
|
|
@@ -4638,7 +5714,7 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4638
5714
|
* ```
|
|
4639
5715
|
*/
|
|
4640
5716
|
content(contentType) {
|
|
4641
|
-
return new ContentClient(this.httpClient, contentType, this.
|
|
5717
|
+
return new ContentClient(this.httpClient, contentType, this.currentLocale);
|
|
4642
5718
|
}
|
|
4643
5719
|
/**
|
|
4644
5720
|
* Get an asset client for a specific asset type.
|
|
@@ -4659,7 +5735,7 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4659
5735
|
* ```
|
|
4660
5736
|
*/
|
|
4661
5737
|
asset(assetType) {
|
|
4662
|
-
return new AssetClient(this.httpClient, assetType, this.
|
|
5738
|
+
return new AssetClient(this.httpClient, assetType, this.currentLocale);
|
|
4663
5739
|
}
|
|
4664
5740
|
/**
|
|
4665
5741
|
* Get a singleton client for a specific singleton key
|
|
@@ -4674,7 +5750,7 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4674
5750
|
* ```
|
|
4675
5751
|
*/
|
|
4676
5752
|
singleton(key) {
|
|
4677
|
-
return new SingletonClient(this.httpClient, key, this.
|
|
5753
|
+
return new SingletonClient(this.httpClient, key, this.currentLocale);
|
|
4678
5754
|
}
|
|
4679
5755
|
/**
|
|
4680
5756
|
* Get a settings client for a specific settings group
|
|
@@ -4688,7 +5764,7 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4688
5764
|
* ```
|
|
4689
5765
|
*/
|
|
4690
5766
|
settings(groupId) {
|
|
4691
|
-
return new SettingsClient(this.httpClient, groupId, this.
|
|
5767
|
+
return new SettingsClient(this.httpClient, groupId, this.currentLocale);
|
|
4692
5768
|
}
|
|
4693
5769
|
// ============================================================================
|
|
4694
5770
|
// SEARCH OPERATIONS
|
|
@@ -4710,7 +5786,7 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4710
5786
|
* ```
|
|
4711
5787
|
*/
|
|
4712
5788
|
search(query) {
|
|
4713
|
-
return this.
|
|
5789
|
+
return this.searchClient().search(query);
|
|
4714
5790
|
}
|
|
4715
5791
|
/**
|
|
4716
5792
|
* Start an autocomplete query for search suggestions
|
|
@@ -4726,7 +5802,7 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4726
5802
|
* ```
|
|
4727
5803
|
*/
|
|
4728
5804
|
autocomplete(query) {
|
|
4729
|
-
return this.
|
|
5805
|
+
return this.searchClient().autocomplete(query);
|
|
4730
5806
|
}
|
|
4731
5807
|
/**
|
|
4732
5808
|
* Find content similar to a given entry using vector search
|
|
@@ -4741,13 +5817,17 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4741
5817
|
* ```
|
|
4742
5818
|
*/
|
|
4743
5819
|
similar(sourceId) {
|
|
4744
|
-
return this.
|
|
5820
|
+
return this.searchClient().similar(sourceId);
|
|
4745
5821
|
}
|
|
4746
5822
|
// ============================================================================
|
|
4747
5823
|
// BATCH OPERATIONS
|
|
4748
5824
|
// ============================================================================
|
|
4749
5825
|
/**
|
|
4750
|
-
* Start a batch operation for bulk create/update/delete
|
|
5826
|
+
* Start a batch operation for bulk create/update/delete.
|
|
5827
|
+
*
|
|
5828
|
+
* By default each operation lands or fails on its own. `atomic: true` makes the batch one
|
|
5829
|
+
* transaction, which the API accepts only when every operation is the same kind of write and none
|
|
5830
|
+
* writes a singleton that does not exist yet.
|
|
4751
5831
|
*
|
|
4752
5832
|
* @example
|
|
4753
5833
|
* ```typescript
|
|
@@ -4756,13 +5836,23 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4756
5836
|
* .update('product', 'entry_abc', { content: { price: 899 } })
|
|
4757
5837
|
* .delete('product', 'entry_xyz')
|
|
4758
5838
|
* .upsertSingleton('homepage', { content: { hero: 'Welcome!' } })
|
|
4759
|
-
* .execute(
|
|
5839
|
+
* .execute()
|
|
4760
5840
|
*
|
|
4761
5841
|
* console.log(result.summary.succeeded)
|
|
5842
|
+
*
|
|
5843
|
+
* // All or nothing: one kind of write
|
|
5844
|
+
* await relyio.batch()
|
|
5845
|
+
* .update('product', 'entry_abc', { content: { price: 899 } })
|
|
5846
|
+
* .update('product', 'entry_def', { content: { price: 799 } })
|
|
5847
|
+
* .execute({ atomic: true })
|
|
4762
5848
|
* ```
|
|
4763
5849
|
*/
|
|
4764
5850
|
batch() {
|
|
4765
|
-
return this.
|
|
5851
|
+
return new BatchClient(this.httpClient, this.currentLocale).create();
|
|
5852
|
+
}
|
|
5853
|
+
/** The search client, whose builders send the client's locale as it is when each one is sent. */
|
|
5854
|
+
searchClient() {
|
|
5855
|
+
return new SearchClient(this.httpClient, this.currentLocale);
|
|
4766
5856
|
}
|
|
4767
5857
|
// ============================================================================
|
|
4768
5858
|
// CHANGES FEED
|
|
@@ -4774,13 +5864,15 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4774
5864
|
* Lazy — nothing is requested until the first `next()`. It runs until
|
|
4775
5865
|
* `signal` aborts, which ends the `for await` cleanly rather than throwing.
|
|
4776
5866
|
* A change names what changed; refetch it through the ordinary read path
|
|
4777
|
-
* (`INV-SS-CHANGE-IS-IDS`)
|
|
5867
|
+
* (`INV-SS-CHANGE-IS-IDS`), naming its `fresh` so no cache answers with the
|
|
5868
|
+
* body from before it (`FeedChange`).
|
|
4778
5869
|
*
|
|
4779
5870
|
* @example
|
|
4780
5871
|
* ```typescript
|
|
4781
5872
|
* const controller = new AbortController()
|
|
4782
5873
|
* for await (const change of relyio.changes({ since: cursor, types: ['post'], signal: controller.signal })) {
|
|
4783
|
-
* // { entryId, contentType, locale, kind, cursor }
|
|
5874
|
+
* // { entryId, contentType, locale, kind, cursor, fresh }
|
|
5875
|
+
* await relyio.content(change.contentType).findById(change.entryId).fresh(change.fresh).execute()
|
|
4784
5876
|
* }
|
|
4785
5877
|
* ```
|
|
4786
5878
|
*
|
|
@@ -4788,13 +5880,60 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4788
5880
|
* ```typescript
|
|
4789
5881
|
* for await (const change of relyio.changes({
|
|
4790
5882
|
* since: cursor,
|
|
4791
|
-
* onExpired: async
|
|
5883
|
+
* onExpired: async expired => refetchEverything(expired.fresh),
|
|
4792
5884
|
* })) { … }
|
|
4793
5885
|
* ```
|
|
4794
5886
|
*/
|
|
4795
5887
|
changes(options = {}) {
|
|
4796
5888
|
return pollChanges(this.httpClient, options);
|
|
4797
5889
|
}
|
|
5890
|
+
/**
|
|
5891
|
+
* Follow the dataset's **published** changes at RelyIO's live edge, with a publishable key
|
|
5892
|
+
* (`rly_pub_…`): each change as ids, in order — the transport a browser follows changes with.
|
|
5893
|
+
*
|
|
5894
|
+
* A WebSocket to the edge, from `since` or the dataset's current position; where a socket cannot
|
|
5895
|
+
* open — the runtime has none, or the network refuses it — it polls the edge instead, about every
|
|
5896
|
+
* fifteen seconds, from the same cursor. The edge is `liveUrl`, or derived from the default
|
|
5897
|
+
* `baseUrl` (see `RelyIOConfig.liveUrl`).
|
|
5898
|
+
*
|
|
5899
|
+
* One subscription ends only when `signal` aborts, which ends the `for await` cleanly; every other
|
|
5900
|
+
* end — a socket a deploy dropped, a refusal, a refused poll — is thrown as a `LiveEdgeError`
|
|
5901
|
+
* that says whether to stop, retry or back off. Hand it to `followChanges()`, the one loop that
|
|
5902
|
+
* reconnects from the last cursor with jittered back-off and honours those answers:
|
|
5903
|
+
*
|
|
5904
|
+
* @example
|
|
5905
|
+
* ```typescript
|
|
5906
|
+
* const relyio = createClient({ apiKey: 'rly_pub_…' })
|
|
5907
|
+
* const controller = new AbortController()
|
|
5908
|
+
* await followChanges(from => relyio.liveChanges({ ...from, types: ['post'] }), {
|
|
5909
|
+
* signal: controller.signal,
|
|
5910
|
+
* // `fresh` names a position no cache holds a body for from before the change.
|
|
5911
|
+
* onChange: change =>
|
|
5912
|
+
* relyio.content(change.contentType).findById(change.entryId).fresh(change.fresh).execute(),
|
|
5913
|
+
* onResync: resync => refetchEverything(resync.cause === 'expired' ? resync.expired.fresh : undefined),
|
|
5914
|
+
* })
|
|
5915
|
+
* ```
|
|
5916
|
+
*
|
|
5917
|
+
* A client without a publishable key sends nothing: a secret or preview key is never put in the
|
|
5918
|
+
* edge's URL. On a server, follow `changes()` with it instead.
|
|
5919
|
+
*/
|
|
5920
|
+
liveChanges(options = {}) {
|
|
5921
|
+
const { apiKey, dataset, baseUrl, liveUrl, fetch: fetch2, webSocket, timeout } = this.config;
|
|
5922
|
+
this._liveClientId ?? (this._liveClientId = randomClientId());
|
|
5923
|
+
return subscribeLiveEdge(
|
|
5924
|
+
{
|
|
5925
|
+
apiKey,
|
|
5926
|
+
dataset,
|
|
5927
|
+
baseUrl,
|
|
5928
|
+
liveUrl,
|
|
5929
|
+
fetch: fetch2,
|
|
5930
|
+
webSocket,
|
|
5931
|
+
timeout,
|
|
5932
|
+
clientId: this._liveClientId
|
|
5933
|
+
},
|
|
5934
|
+
options
|
|
5935
|
+
);
|
|
5936
|
+
}
|
|
4798
5937
|
/**
|
|
4799
5938
|
* The dataset's current position in the changes feed, as a cursor to start
|
|
4800
5939
|
* `changes({ since })` or `followChanges(…, { since })` from. One
|
|
@@ -4836,7 +5975,7 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4836
5975
|
* ```
|
|
4837
5976
|
*/
|
|
4838
5977
|
adhoc() {
|
|
4839
|
-
return this.
|
|
5978
|
+
return this.query.build();
|
|
4840
5979
|
}
|
|
4841
5980
|
/**
|
|
4842
5981
|
* Execute a predefined query by name (from schema)
|
|
@@ -4921,16 +6060,53 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4921
6060
|
}
|
|
4922
6061
|
}
|
|
4923
6062
|
/**
|
|
4924
|
-
*
|
|
6063
|
+
* Change some of the client's options and keep the rest.
|
|
6064
|
+
*
|
|
6065
|
+
* What you pass is laid over the configuration the client holds, field by field:
|
|
6066
|
+
*
|
|
6067
|
+
* - an option you name replaces the value the client holds;
|
|
6068
|
+
* - an option left out, or given as `undefined`, keeps the value the client holds;
|
|
6069
|
+
* - an option with no default given as `null` is cleared — `locale`, `dataset`, `apiKey`, `dev`,
|
|
6070
|
+
* `fetch`, `browser.onCorsError`, `realtime.wsUrl`, `debug.logger` — so the client runs as though
|
|
6071
|
+
* it had never been given one. An option with a default takes no `null`;
|
|
6072
|
+
* - a bag of options you name — `browser`, `cache`, `realtime`, `debug` — keeps every field of it
|
|
6073
|
+
* you do not name, so `updateConfig({ browser: { includeCredentials: true } })` keeps a
|
|
6074
|
+
* configured `onCorsError`;
|
|
6075
|
+
* - `headers` is laid over the headers the client sends, a name matched without regard to case: a
|
|
6076
|
+
* header you name is added or replaces every spelling of it, every other one is still sent, and a
|
|
6077
|
+
* header given as `null` is no longer sent;
|
|
6078
|
+
* - naming `apiKey` — a key, or `null` — replaces every key the client holds, one given in
|
|
6079
|
+
* `headers` included.
|
|
6080
|
+
*
|
|
6081
|
+
* The configuration it is laid over is what `createClient()`, `setApiKey()` and earlier updates
|
|
6082
|
+
* gave the client. The `relyio dev` handshake is read again, as `createClient()` reads it: while a
|
|
6083
|
+
* sandbox is active its key and dataset are sent over the configured ones, and once it has gone
|
|
6084
|
+
* the configured ones are.
|
|
6085
|
+
*
|
|
6086
|
+
* The client's one HTTP client is changed in place, so every request from now on is sent as the
|
|
6087
|
+
* update says — including one from a `content()`, `asset()`, `singleton()` or `settings()`
|
|
6088
|
+
* sub-client, a `query` client, or a builder such as `search()` or `batch()`, taken before the
|
|
6089
|
+
* call. The locale reaches them too: each reads the client's locale when it sends, so a new
|
|
6090
|
+
* `locale` is the default of its next request and `locale: null` leaves that request with none. A
|
|
6091
|
+
* locale the call names, and a sub-client's own `withLocale()`, still win over it. A client made
|
|
6092
|
+
* by `withLocale()` is a client of its own: an update of this one does not reach it.
|
|
6093
|
+
*
|
|
6094
|
+
* @example
|
|
6095
|
+
* ```typescript
|
|
6096
|
+
* const relyio = createClient({ apiKey, headers: { 'X-Tenant': 'acme' }, browser: { onCorsError } })
|
|
6097
|
+
*
|
|
6098
|
+
* relyio.updateConfig({ headers: { 'X-Trace': traceId } }) // X-Tenant is still sent
|
|
6099
|
+
* relyio.updateConfig({ browser: { includeCredentials: true } }) // onCorsError is kept
|
|
6100
|
+
* relyio.updateConfig({ headers: { 'X-Tenant': null } }) // X-Tenant is no longer sent
|
|
6101
|
+
* relyio.updateConfig({ locale: null }) // reads name no locale of the client's
|
|
6102
|
+
* ```
|
|
4925
6103
|
*/
|
|
4926
|
-
updateConfig(
|
|
4927
|
-
const resolved =
|
|
6104
|
+
updateConfig(update) {
|
|
6105
|
+
const resolved = resolveConfigUpdate(this.configured, update);
|
|
4928
6106
|
this.config = resolved.config;
|
|
6107
|
+
this.configured = resolved.configured;
|
|
4929
6108
|
this._sandbox = resolved.sandbox;
|
|
4930
|
-
this.httpClient
|
|
4931
|
-
this._search = new SearchClient(this.httpClient, this.config.locale);
|
|
4932
|
-
this._batch = new BatchClient(this.httpClient);
|
|
4933
|
-
this._adhocQuery = new AdhocQueryClient(this.httpClient, this.config.locale);
|
|
6109
|
+
this.httpClient.configure(this.config);
|
|
4934
6110
|
}
|
|
4935
6111
|
/**
|
|
4936
6112
|
* Get the current configuration
|
|
@@ -4939,11 +6115,31 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4939
6115
|
return { ...this.config };
|
|
4940
6116
|
}
|
|
4941
6117
|
/**
|
|
4942
|
-
*
|
|
6118
|
+
* Replace the API key every later request sends as `X-API-Key`.
|
|
6119
|
+
*
|
|
6120
|
+
* It reaches every request this client makes from now on, including those of a `content()`,
|
|
6121
|
+
* `asset()` or other sub-client taken before the call, and `getConfig().apiKey` reports it. A key
|
|
6122
|
+
* given in `headers`, under any spelling, is replaced with it and does not come back on a later
|
|
6123
|
+
* `updateConfig()`. A client already derived with `withLocale()` is a client of its own and keeps
|
|
6124
|
+
* its key.
|
|
6125
|
+
*
|
|
6126
|
+
* An empty or blank key is no key, as it is when passed to `createClient`: the next request
|
|
6127
|
+
* rejects with `MissingApiKeyError` and nothing is sent.
|
|
6128
|
+
*
|
|
6129
|
+
* Under an active `relyio dev` sandbox the sandbox's scoped key stands, as it does over the key
|
|
6130
|
+
* passed to `createClient` — replacing the configured key does not retarget the client at the
|
|
6131
|
+
* sandbox's dataset with a key the sandbox did not issue.
|
|
6132
|
+
*
|
|
6133
|
+
* @example
|
|
6134
|
+
* ```typescript
|
|
6135
|
+
* const relyio = createClient({ apiKey: process.env.RELYIO_API_KEY! })
|
|
6136
|
+
* relyio.setApiKey(rotatedKey) // the next read sends `X-API-Key: <rotatedKey>`
|
|
6137
|
+
* ```
|
|
4943
6138
|
*/
|
|
4944
6139
|
setApiKey(apiKey) {
|
|
4945
|
-
this.
|
|
4946
|
-
this.
|
|
6140
|
+
this.configured = withConfiguredKey(this.configured, apiKey);
|
|
6141
|
+
if (this._sandbox.state === "active") return;
|
|
6142
|
+
this.httpClient.setApiKey(apiKey);
|
|
4947
6143
|
}
|
|
4948
6144
|
/**
|
|
4949
6145
|
* Get the HTTP client instance (for advanced usage)
|
|
@@ -4957,9 +6153,12 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4957
6153
|
/**
|
|
4958
6154
|
* Create a locale-scoped client
|
|
4959
6155
|
*
|
|
4960
|
-
* Every
|
|
4961
|
-
* `
|
|
4962
|
-
*
|
|
6156
|
+
* Every call the returned client builds is in this locale unless the call names one of its own,
|
|
6157
|
+
* which wins: every read — `content()`, `asset()`, `singleton()`, `settings()`, `search()`,
|
|
6158
|
+
* `similar()`, `autocomplete()`, `executeQuery()` and the ad-hoc queries — and every write and
|
|
6159
|
+
* version-history read — `create()` and `update()`, the singleton and settings writes, the asset
|
|
6160
|
+
* file writes, `versions()` and `version()`, and each operation `batch()` builds. `restore()`
|
|
6161
|
+
* always names its locale; a delete, and a `batch().raw()` operation, carry none of the client's.
|
|
4963
6162
|
*
|
|
4964
6163
|
* Type-safe when schema is provided: autocompletes valid locale codes!
|
|
4965
6164
|
*
|
|
@@ -4967,10 +6166,11 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4967
6166
|
* ```typescript
|
|
4968
6167
|
* const spanish = relyio.withLocale('es-ES') // ← Autocompletes with schema!
|
|
4969
6168
|
*
|
|
4970
|
-
* // Reads use Spanish
|
|
6169
|
+
* // Reads and writes use Spanish
|
|
4971
6170
|
* const products = await spanish.content('product').query().execute()
|
|
4972
6171
|
* const homepage = await spanish.singleton('homepage').fetch()
|
|
4973
6172
|
* const results = await spanish.search('zapatos').execute()
|
|
6173
|
+
* await spanish.content('product').update(id, { title: 'Zapatos' })
|
|
4974
6174
|
*
|
|
4975
6175
|
* // Original client unchanged
|
|
4976
6176
|
* const englishProducts = await relyio.content('product').query().execute()
|
|
@@ -4978,7 +6178,7 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
|
4978
6178
|
*/
|
|
4979
6179
|
withLocale(locale) {
|
|
4980
6180
|
const newConfig = {
|
|
4981
|
-
...this.
|
|
6181
|
+
...this.configured,
|
|
4982
6182
|
locale,
|
|
4983
6183
|
schema: this._schema
|
|
4984
6184
|
};
|
|
@@ -5026,7 +6226,20 @@ function createClient(config) {
|
|
|
5026
6226
|
return new TypedRelyIOClient(config);
|
|
5027
6227
|
}
|
|
5028
6228
|
__name(createClient, "createClient");
|
|
6229
|
+
function randomClientId() {
|
|
6230
|
+
const bytes = new Uint8Array(16);
|
|
6231
|
+
const random = globalThis.crypto;
|
|
6232
|
+
if (typeof random?.getRandomValues === "function") {
|
|
6233
|
+
random.getRandomValues.call(random, bytes);
|
|
6234
|
+
} else {
|
|
6235
|
+
for (let index = 0; index < bytes.length; index += 1) {
|
|
6236
|
+
bytes[index] = Math.floor(Math.random() * 256);
|
|
6237
|
+
}
|
|
6238
|
+
}
|
|
6239
|
+
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
6240
|
+
}
|
|
6241
|
+
__name(randomClientId, "randomClientId");
|
|
5029
6242
|
|
|
5030
|
-
export { AdhocQueryBuilder, AdhocQueryClient, AssetClient, AssetFileBuilder, AssetUploadBuilder,
|
|
5031
|
-
//# sourceMappingURL=chunk-
|
|
5032
|
-
//# sourceMappingURL=chunk-
|
|
6243
|
+
export { AdhocQueryBuilder, AdhocQueryClient, AssetClient, AssetFileBuilder, AssetUploadBuilder, AuthorizationError, AutocompleteBuilder, BaseValidationError, BatchBuilder, BatchClient, ChangesExpiredError, ConnectionError, ContentByIdBuilder, ContentClient, ContentQueryBuilder, ErrorMapper, FileUploadError, FileValidationError, HttpClient, InvalidApiKeyError, InvalidPayloadError, LIVE_KEEPALIVE_INTERVAL_MS, LiveEdgeError, MethodNotAllowedError, MissingApiKeyError, NetworkError, PUBLISHABLE_KEY_PREFIX, PayloadTooLargeError, PlanFeatureUnavailableError, RateLimitError, ReadOnlyKeyError, ResourceConflictError, ResourceExpiredError, ResourceNotFoundError, SDKError, SearchBuilder, SearchClient, ServerError, ServiceIndeterminateError, SettingsClient, SimilarBuilder, SingletonClient, TimeoutError, TransactionRolledBackError, TypedRelyIOClient, UnknownContractError, UsageQuotaExceededError, VISITOR_ADMISSION_RETRY_AFTER_SECONDS, ValidationError, createClient, entityTagOf, isAborted, isAssetEntry, isPublishableKey, isResourceNotFound, matchRoute, serviceInstructedDelayMs, sleep, validateFile };
|
|
6244
|
+
//# sourceMappingURL=chunk-QFHVF3YF.js.map
|
|
6245
|
+
//# sourceMappingURL=chunk-QFHVF3YF.js.map
|