@relyio/sdk 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/CHANGELOG.md +170 -0
  2. package/dist/.tsbuildinfo +1 -1
  3. package/dist/{chunk-5AZUUV5B.js → chunk-7ABNXVWL.js} +398 -184
  4. package/dist/{chunk-HAQELDZN.cjs → chunk-DY2XT7OF.cjs} +2 -2
  5. package/dist/{chunk-CIRT6TVT.cjs → chunk-GCMQUM6N.cjs} +400 -183
  6. package/dist/{chunk-XY5KFEFY.js → chunk-XJVY2CLY.js} +2 -2
  7. package/dist/client/index.cjs +3 -3
  8. package/dist/client/index.d.cts +4 -4
  9. package/dist/client/index.d.ts +4 -4
  10. package/dist/client/index.js +1 -1
  11. package/dist/define-content-type-CDB3kyeA.d.cts +1173 -0
  12. package/dist/define-content-type-CDB3kyeA.d.ts +1173 -0
  13. package/dist/{define-schema-DzYXk4HL.d.ts → define-schema-1kr2HIzN.d.cts} +25 -347
  14. package/dist/{define-schema-2xbHgtK-.d.cts → define-schema-ChvAzY8H.d.ts} +25 -347
  15. package/dist/{index-BozPz4n1.d.cts → index-Df1VsfJR.d.cts} +233 -74
  16. package/dist/{index-Dzo-upNS.d.ts → index-XqoGUaAY.d.ts} +233 -74
  17. package/dist/{index-C5mRBb6t.d.cts → index-d1Kz1e19.d.cts} +62 -29
  18. package/dist/{index-mXUx2H99.d.ts → index-yPbhZc6q.d.ts} +62 -29
  19. package/dist/index.cjs +161 -68
  20. package/dist/index.d.cts +283 -8
  21. package/dist/index.d.ts +283 -8
  22. package/dist/index.js +90 -3
  23. package/dist/schema/index.cjs +35 -35
  24. package/dist/schema/index.d.cts +11 -7
  25. package/dist/schema/index.d.ts +11 -7
  26. package/dist/schema/index.js +1 -1
  27. package/dist/types/index.d.cts +2 -2
  28. package/dist/types/index.d.ts +2 -2
  29. package/package.json +1 -1
  30. package/dist/define-content-type-CBfnTWSS.d.cts +0 -716
  31. package/dist/define-content-type-CBfnTWSS.d.ts +0 -716
@@ -71,44 +71,6 @@ var _SDKError = class _SDKError extends Error {
71
71
  chunkMWMSFIWN_cjs.__name(_SDKError, "SDKError");
72
72
  var SDKError = _SDKError;
73
73
 
74
- // src/errors/auth-errors.ts
75
- var _AuthenticationError = class _AuthenticationError extends SDKError {
76
- constructor(message = "Authentication failed", statusCode = 401, cause, context) {
77
- super(message, statusCode, cause, context);
78
- chunkMWMSFIWN_cjs.__publicField(this, "code", "AUTHENTICATION_ERROR");
79
- chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
80
- }
81
- getUserMessage() {
82
- return "Authentication failed. Please check your API key and try again.";
83
- }
84
- };
85
- chunkMWMSFIWN_cjs.__name(_AuthenticationError, "AuthenticationError");
86
- var AuthenticationError = _AuthenticationError;
87
- var _AuthorizationError = class _AuthorizationError extends SDKError {
88
- constructor(message = "Authorization failed", statusCode = 403, cause, context) {
89
- super(message, statusCode, cause, context);
90
- chunkMWMSFIWN_cjs.__publicField(this, "code", "AUTHORIZATION_ERROR");
91
- chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
92
- }
93
- getUserMessage() {
94
- return "You do not have permission to access this resource.";
95
- }
96
- };
97
- chunkMWMSFIWN_cjs.__name(_AuthorizationError, "AuthorizationError");
98
- var AuthorizationError = _AuthorizationError;
99
- var _InvalidApiKeyError = class _InvalidApiKeyError extends SDKError {
100
- constructor(message = "Invalid API key", cause, context) {
101
- super(message, 401, cause, context);
102
- chunkMWMSFIWN_cjs.__publicField(this, "code", "INVALID_API_KEY");
103
- chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
104
- }
105
- getUserMessage() {
106
- return "Invalid API key. Please check your API key and try again.";
107
- }
108
- };
109
- chunkMWMSFIWN_cjs.__name(_InvalidApiKeyError, "InvalidApiKeyError");
110
- var InvalidApiKeyError = _InvalidApiKeyError;
111
-
112
74
  // src/errors/contract-errors.ts
113
75
  var _MissingApiKeyError = class _MissingApiKeyError extends SDKError {
114
76
  constructor(message = "No API key was sent", context) {
@@ -269,6 +231,44 @@ var _UnknownContractError = class _UnknownContractError extends SDKError {
269
231
  chunkMWMSFIWN_cjs.__name(_UnknownContractError, "UnknownContractError");
270
232
  var UnknownContractError = _UnknownContractError;
271
233
 
234
+ // src/errors/auth-errors.ts
235
+ var _AuthenticationError = class _AuthenticationError extends SDKError {
236
+ constructor(message = "Authentication failed", statusCode = 401, cause, context) {
237
+ super(message, statusCode, cause, context);
238
+ chunkMWMSFIWN_cjs.__publicField(this, "code", "AUTHENTICATION_ERROR");
239
+ chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
240
+ }
241
+ getUserMessage() {
242
+ return "Authentication failed. Please check your API key and try again.";
243
+ }
244
+ };
245
+ chunkMWMSFIWN_cjs.__name(_AuthenticationError, "AuthenticationError");
246
+ var AuthenticationError = _AuthenticationError;
247
+ var _AuthorizationError = class _AuthorizationError extends SDKError {
248
+ constructor(message = "Authorization failed", statusCode = 403, cause, context) {
249
+ super(message, statusCode, cause, context);
250
+ chunkMWMSFIWN_cjs.__publicField(this, "code", "AUTHORIZATION_ERROR");
251
+ chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
252
+ }
253
+ getUserMessage() {
254
+ return "You do not have permission to access this resource.";
255
+ }
256
+ };
257
+ chunkMWMSFIWN_cjs.__name(_AuthorizationError, "AuthorizationError");
258
+ var AuthorizationError = _AuthorizationError;
259
+ var _InvalidApiKeyError = class _InvalidApiKeyError extends SDKError {
260
+ constructor(message = "Invalid API key", cause, context) {
261
+ super(message, 401, cause, context);
262
+ chunkMWMSFIWN_cjs.__publicField(this, "code", "INVALID_API_KEY");
263
+ chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
264
+ }
265
+ getUserMessage() {
266
+ return "Invalid API key. Please check your API key and try again.";
267
+ }
268
+ };
269
+ chunkMWMSFIWN_cjs.__name(_InvalidApiKeyError, "InvalidApiKeyError");
270
+ var InvalidApiKeyError = _InvalidApiKeyError;
271
+
272
272
  // src/errors/content-errors.ts
273
273
  var _ResourceNotFoundError = class _ResourceNotFoundError extends SDKError {
274
274
  constructor(message = "Resource not found", statusCode = 404, cause, context) {
@@ -330,8 +330,12 @@ var _NetworkError = class _NetworkError extends SDKError {
330
330
  chunkMWMSFIWN_cjs.__name(_NetworkError, "NetworkError");
331
331
  var NetworkError = _NetworkError;
332
332
  var _TimeoutError = class _TimeoutError extends SDKError {
333
- constructor(message = "Request timed out", timeout, statusCode, cause) {
334
- super(message, statusCode, cause, { timeout });
333
+ /**
334
+ * @param context - Carried beside `timeout`. The mapper passes a `REQUEST_TIMEOUT` refusal's body
335
+ * as `context.response`, as it does for every other contract code.
336
+ */
337
+ constructor(message = "Request timed out", timeout, statusCode, cause, context) {
338
+ super(message, statusCode, cause, { ...context, timeout });
335
339
  chunkMWMSFIWN_cjs.__publicField(this, "code", "TIMEOUT_ERROR");
336
340
  chunkMWMSFIWN_cjs.__publicField(this, "retryable", true);
337
341
  }
@@ -357,8 +361,13 @@ var ConnectionError = _ConnectionError;
357
361
 
358
362
  // src/errors/validation-errors.ts
359
363
  var _BaseValidationError = class _BaseValidationError extends SDKError {
360
- constructor(message = "Validation failed", violations = [], statusCode = 400, cause) {
361
- super(message, statusCode, cause, { violations });
364
+ /**
365
+ * @param context - Carried beside `violations`. The mapper passes the refusal body as
366
+ * `context.response`, as it does for every other contract code, so a consumer that hands the
367
+ * service's own words on can read them here too.
368
+ */
369
+ constructor(message = "Validation failed", violations = [], statusCode = 400, cause, context) {
370
+ super(message, statusCode, cause, { ...context, violations });
362
371
  chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
363
372
  chunkMWMSFIWN_cjs.__publicField(this, "violations");
364
373
  this.violations = violations;
@@ -403,8 +412,8 @@ var _ValidationError = class _ValidationError extends BaseValidationError {
403
412
  chunkMWMSFIWN_cjs.__name(_ValidationError, "ValidationError");
404
413
  var ValidationError = _ValidationError;
405
414
  var _InvalidPayloadError = class _InvalidPayloadError extends BaseValidationError {
406
- constructor(message = "Invalid payload", violations = [], cause) {
407
- super(message, violations, 400, cause);
415
+ constructor(message = "Invalid payload", violations = [], cause, context) {
416
+ super(message, violations, 400, cause, context);
408
417
  chunkMWMSFIWN_cjs.__publicField(this, "code", "INVALID_PAYLOAD");
409
418
  }
410
419
  getUserMessage() {
@@ -593,10 +602,10 @@ var _ErrorMapper = class _ErrorMapper {
593
602
  );
594
603
  }
595
604
  case "REQUEST_INVALID": {
596
- return new InvalidPayloadError(message, violationsOf(refusal));
605
+ return new InvalidPayloadError(message, violationsOf(refusal), void 0, context);
597
606
  }
598
607
  case "CONTENT_INVALID": {
599
- return new ValidationError(message, violationsOf(refusal), 400);
608
+ return new ValidationError(message, violationsOf(refusal), 400, void 0, context);
600
609
  }
601
610
  case "METHOD_NOT_ALLOWED": {
602
611
  return new MethodNotAllowedError(message, context);
@@ -619,7 +628,7 @@ var _ErrorMapper = class _ErrorMapper {
619
628
  return new ResourceExpiredError(message, 410, void 0, context);
620
629
  }
621
630
  case "REQUEST_TIMEOUT": {
622
- return new TimeoutError(message, void 0, void 0, void 0);
631
+ return new TimeoutError(message, void 0, status, void 0, context);
623
632
  }
624
633
  case "PAYLOAD_TOO_LARGE": {
625
634
  return new PayloadTooLargeError(message, detailNumber(refusal, "limitBytes"), context);
@@ -842,20 +851,29 @@ function readQuery(searchParams) {
842
851
  }
843
852
  chunkMWMSFIWN_cjs.__name(readQuery, "readQuery");
844
853
 
845
- // src/http/retry-policy.ts
846
- var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set([
847
- "GET",
848
- "HEAD",
849
- "OPTIONS",
850
- "PUT",
851
- "DELETE",
852
- "TRACE"
853
- ]);
854
- function isReplaySafe(request) {
855
- if (IDEMPOTENT_METHODS.has(request.method.toUpperCase())) return true;
856
- return headerValue(request.headers, HEADER_IDEMPOTENCY_KEY.name) !== void 0;
854
+ // src/http/abortable-sleep.ts
855
+ function sleep(ms, signal) {
856
+ return new Promise((resolve) => {
857
+ if (isAborted(signal)) {
858
+ resolve();
859
+ return;
860
+ }
861
+ const done = /* @__PURE__ */ chunkMWMSFIWN_cjs.__name(() => {
862
+ clearTimeout(timer);
863
+ signal?.removeEventListener("abort", done);
864
+ resolve();
865
+ }, "done");
866
+ const timer = setTimeout(done, ms);
867
+ signal?.addEventListener("abort", done, { once: true });
868
+ });
857
869
  }
858
- chunkMWMSFIWN_cjs.__name(isReplaySafe, "isReplaySafe");
870
+ chunkMWMSFIWN_cjs.__name(sleep, "sleep");
871
+ function isAborted(signal) {
872
+ return signal?.aborted === true;
873
+ }
874
+ chunkMWMSFIWN_cjs.__name(isAborted, "isAborted");
875
+
876
+ // src/errors/instructed-delay.ts
859
877
  function serviceInstructedDelayMs(error, nowMs) {
860
878
  if (error instanceof ServiceIndeterminateError) {
861
879
  return error.retryAfterMs;
@@ -869,6 +887,21 @@ function serviceInstructedDelayMs(error, nowMs) {
869
887
  return wait > 0 ? wait : void 0;
870
888
  }
871
889
  chunkMWMSFIWN_cjs.__name(serviceInstructedDelayMs, "serviceInstructedDelayMs");
890
+
891
+ // src/http/retry-policy.ts
892
+ var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set([
893
+ "GET",
894
+ "HEAD",
895
+ "OPTIONS",
896
+ "PUT",
897
+ "DELETE",
898
+ "TRACE"
899
+ ]);
900
+ function isReplaySafe(request) {
901
+ if (IDEMPOTENT_METHODS.has(request.method.toUpperCase())) return true;
902
+ return headerValue(request.headers, HEADER_IDEMPOTENCY_KEY.name) !== void 0;
903
+ }
904
+ chunkMWMSFIWN_cjs.__name(isReplaySafe, "isReplaySafe");
872
905
  var _DefaultRetryPolicy = class _DefaultRetryPolicy {
873
906
  constructor(config) {
874
907
  chunkMWMSFIWN_cjs.__publicField(this, "config");
@@ -905,9 +938,10 @@ var _HttpClient = class _HttpClient {
905
938
  this.timeout = config.timeout;
906
939
  this.debug = config.debug.enabled ?? false;
907
940
  this.logger = config.debug.logger;
941
+ const apiKey = presentKey(config.apiKey);
908
942
  this.defaultHeaders = {
909
943
  "Content-Type": "application/json",
910
- "X-API-Key": config.apiKey,
944
+ ...apiKey === void 0 ? {} : { [API_KEY_HEADER]: apiKey },
911
945
  ...config.headers
912
946
  };
913
947
  if (config.dataset != null) {
@@ -966,9 +1000,21 @@ var _HttpClient = class _HttpClient {
966
1000
  * The loop always makes one attempt. The incumbent's `while (attempt <
967
1001
  * maxAttempts)` made none at `retryAttempts: 0` — a value the config validator
968
1002
  * accepts — and then dereferenced an unassigned `lastError`.
1003
+ *
1004
+ * A request that would carry no `X-API-Key` makes none: it rejects with
1005
+ * `MissingApiKeyError` before `fetch` is called, is not retried, and names
1006
+ * `apiKey`. This is where a client built without a key learns it — the
1007
+ * constructor accepts one so that a module-scope client can be evaluated by a
1008
+ * build that holds no key (REL-1120).
969
1009
  */
970
1010
  async request(config) {
971
1011
  const mergedConfig = this.mergeRequestConfig(config);
1012
+ if (presentKey(headerValue(mergedConfig.headers, API_KEY_HEADER)) === void 0) {
1013
+ throw new MissingApiKeyError(
1014
+ "`apiKey` is missing: this client was created without an API key, so the request was not sent. Pass `apiKey` to createClient \u2014 the documented setup reads it from RELYIO_API_KEY.",
1015
+ { url: mergedConfig.url, method: mergedConfig.method, sent: false }
1016
+ );
1017
+ }
972
1018
  this.log("debug", "Making request", {
973
1019
  method: mergedConfig.method,
974
1020
  url: mergedConfig.url
@@ -997,7 +1043,7 @@ var _HttpClient = class _HttpClient {
997
1043
  if (this.retryPolicy.shouldRetry(attempt, failure, mergedConfig)) {
998
1044
  const delay = this.retryPolicy.delayFor(attempt, failure);
999
1045
  this.log("debug", "Retrying request", { attempt, delay });
1000
- await this.sleep(delay, config.signal);
1046
+ await sleep(delay, config.signal);
1001
1047
  continue;
1002
1048
  }
1003
1049
  const sdkError = ErrorMapper.mapGenericError(failure, {
@@ -1126,8 +1172,12 @@ var _HttpClient = class _HttpClient {
1126
1172
  headers: filteredHeaders,
1127
1173
  body: this.prepareRequestBody(config),
1128
1174
  signal: controller.signal,
1129
- // Include credentials for CORS requests if in browser and configured
1130
- ...typeof window !== "undefined" && this.config.browser.includeCredentials != null && { credentials: "include" }
1175
+ // Only where `window` exists. A server or edge runtime has no cookie jar for the mode to
1176
+ // govern, so the field is left off and the runtime's default stands. A Web Worker gets
1177
+ // that default too: it has cookies but no `window`.
1178
+ ...typeof window !== "undefined" && {
1179
+ credentials: credentialsModeFor(this.config.browser.includeCredentials)
1180
+ }
1131
1181
  };
1132
1182
  const send = this.config.fetch ?? globalThis.fetch;
1133
1183
  const response = await send(url, requestInit);
@@ -1283,22 +1333,6 @@ var _HttpClient = class _HttpClient {
1283
1333
  });
1284
1334
  return obj;
1285
1335
  }
1286
- /** A backoff the caller's abort cuts short; the next attempt then refuses to send. */
1287
- sleep(ms, signal) {
1288
- return new Promise((resolve) => {
1289
- if (isAborted(signal)) {
1290
- resolve();
1291
- return;
1292
- }
1293
- const done = /* @__PURE__ */ chunkMWMSFIWN_cjs.__name(() => {
1294
- clearTimeout(timer);
1295
- signal?.removeEventListener("abort", done);
1296
- resolve();
1297
- }, "done");
1298
- const timer = setTimeout(done, ms);
1299
- signal?.addEventListener("abort", done, { once: true });
1300
- });
1301
- }
1302
1336
  log(level, message, data) {
1303
1337
  if (!this.debug) return;
1304
1338
  if (this.logger != null) {
@@ -1310,10 +1344,23 @@ var _HttpClient = class _HttpClient {
1310
1344
  };
1311
1345
  chunkMWMSFIWN_cjs.__name(_HttpClient, "HttpClient");
1312
1346
  var HttpClient = _HttpClient;
1313
- function isAborted(signal) {
1314
- return signal?.aborted === true;
1347
+ var API_KEY_HEADER = "X-API-Key";
1348
+ function presentKey(value) {
1349
+ return typeof value === "string" && value.trim() !== "" ? value : void 0;
1315
1350
  }
1316
- chunkMWMSFIWN_cjs.__name(isAborted, "isAborted");
1351
+ chunkMWMSFIWN_cjs.__name(presentKey, "presentKey");
1352
+ function credentialsModeFor(includeCredentials) {
1353
+ switch (includeCredentials) {
1354
+ case false: {
1355
+ return "same-origin";
1356
+ }
1357
+ case true:
1358
+ case void 0: {
1359
+ return "include";
1360
+ }
1361
+ }
1362
+ }
1363
+ chunkMWMSFIWN_cjs.__name(credentialsModeFor, "credentialsModeFor");
1317
1364
 
1318
1365
  // src/content/query-builder.ts
1319
1366
  var _ContentQueryBuilder = class _ContentQueryBuilder {
@@ -1329,6 +1376,16 @@ var _ContentQueryBuilder = class _ContentQueryBuilder {
1329
1376
  chunkMWMSFIWN_cjs.__publicField(this, "childrenOptions");
1330
1377
  chunkMWMSFIWN_cjs.__publicField(this, "pageNumber");
1331
1378
  chunkMWMSFIWN_cjs.__publicField(this, "pageSizeValue");
1379
+ chunkMWMSFIWN_cjs.__publicField(this, "requestHeaders");
1380
+ }
1381
+ /**
1382
+ * Headers this query adds to the client's own, e.g. `X-RelyIO-Dataset` to list another dataset
1383
+ * the key can see. A name the client already sends is replaced for this query only; the client
1384
+ * is not changed.
1385
+ */
1386
+ headers(headers) {
1387
+ this.requestHeaders = headers;
1388
+ return this;
1332
1389
  }
1333
1390
  /**
1334
1391
  * Add where conditions to the query
@@ -1526,7 +1583,11 @@ var _ContentQueryBuilder = class _ContentQueryBuilder {
1526
1583
  const response = await this.httpClient.send(
1527
1584
  "/v1/{contentType}",
1528
1585
  "get",
1529
- { path: { contentType: this.contentType }, query: this.buildQueryParams() }
1586
+ {
1587
+ path: { contentType: this.contentType },
1588
+ query: this.buildQueryParams(),
1589
+ headers: this.requestHeaders
1590
+ }
1530
1591
  );
1531
1592
  return response.body;
1532
1593
  }
@@ -1594,6 +1655,7 @@ var _ContentQueryBuilder = class _ContentQueryBuilder {
1594
1655
  cloned.fieldsProjection = this.fieldsProjection ? [...this.fieldsProjection] : void 0;
1595
1656
  cloned.pageNumber = this.pageNumber;
1596
1657
  cloned.pageSizeValue = this.pageSizeValue;
1658
+ cloned.requestHeaders = this.requestHeaders ? { ...this.requestHeaders } : void 0;
1597
1659
  return cloned;
1598
1660
  }
1599
1661
  /**
@@ -1701,6 +1763,7 @@ var _ContentByIdBuilder = class _ContentByIdBuilder {
1701
1763
  chunkMWMSFIWN_cjs.__publicField(this, "localesArray");
1702
1764
  chunkMWMSFIWN_cjs.__publicField(this, "fetchAllLocales", false);
1703
1765
  chunkMWMSFIWN_cjs.__publicField(this, "childrenOptions");
1766
+ chunkMWMSFIWN_cjs.__publicField(this, "requestHeaders");
1704
1767
  }
1705
1768
  /**
1706
1769
  * Include related content in the response
@@ -1731,6 +1794,15 @@ var _ContentByIdBuilder = class _ContentByIdBuilder {
1731
1794
  this.fetchAllLocales = false;
1732
1795
  return this;
1733
1796
  }
1797
+ /**
1798
+ * Headers this one read adds to the client's own, e.g. `X-RelyIO-Dataset` to read another dataset
1799
+ * the key can see. A name the client already sends is replaced for this request only; the client
1800
+ * is not changed.
1801
+ */
1802
+ headers(headers) {
1803
+ this.requestHeaders = headers;
1804
+ return this;
1805
+ }
1734
1806
  /**
1735
1807
  * Fetch content in all available locales
1736
1808
  * Returns default locale at top level, others in _metadata.localizations
@@ -1784,7 +1856,11 @@ var _ContentByIdBuilder = class _ContentByIdBuilder {
1784
1856
  return await this.httpClient.fetchPayload(
1785
1857
  "/v1/{contentType}/{id}",
1786
1858
  "get",
1787
- { path: { contentType: this.contentType, id: this.id }, query: params }
1859
+ {
1860
+ path: { contentType: this.contentType, id: this.id },
1861
+ query: params,
1862
+ headers: this.requestHeaders
1863
+ }
1788
1864
  );
1789
1865
  } catch (error) {
1790
1866
  if (isResourceNotFound(error)) return null;
@@ -1814,6 +1890,14 @@ function writeHeaders(options) {
1814
1890
  return Object.keys(headers).length > 0 ? headers : void 0;
1815
1891
  }
1816
1892
  chunkMWMSFIWN_cjs.__name(writeHeaders, "writeHeaders");
1893
+ function requestHeaders(headers, typed) {
1894
+ if (headers === void 0) return typed;
1895
+ if (typed === void 0) return headers;
1896
+ const typedNames = new Set(Object.keys(typed).map((name) => name.toLowerCase()));
1897
+ const own = Object.entries(headers).filter(([name]) => !typedNames.has(name.toLowerCase()));
1898
+ return { ...Object.fromEntries(own), ...typed };
1899
+ }
1900
+ chunkMWMSFIWN_cjs.__name(requestHeaders, "requestHeaders");
1817
1901
  function entityTagOf(response) {
1818
1902
  return headerValue(response.headers, HEADER_ENTITY_TAG.name);
1819
1903
  }
@@ -1883,7 +1967,8 @@ var _ContentClient = class _ContentClient {
1883
1967
  /**
1884
1968
  * Fetch a single content item by ID
1885
1969
  * @param id - Entry ID or slug
1886
- * @param options - Fetch options (include, locale)
1970
+ * @param options - Fetch options (include, locale, and `headers` this one request adds — e.g.
1971
+ * `{ 'X-RelyIO-Dataset': 'staging' }` to read a dataset other than the client's)
1887
1972
  */
1888
1973
  async fetchOne(id, options) {
1889
1974
  const builder = this.findById(id);
@@ -1893,6 +1978,9 @@ var _ContentClient = class _ContentClient {
1893
1978
  if (options?.locale != null && options.locale !== "") {
1894
1979
  builder.locale(options.locale);
1895
1980
  }
1981
+ if (options?.headers != null) {
1982
+ builder.headers(options.headers);
1983
+ }
1896
1984
  return builder.execute();
1897
1985
  }
1898
1986
  /**
@@ -1907,6 +1995,11 @@ var _ContentClient = class _ContentClient {
1907
1995
  * `entityTag` is `undefined` when the response came from the cache: a cached
1908
1996
  * body cannot know the current version, and an absent tag beats a stale one
1909
1997
  * that would refuse the next write.
1998
+ *
1999
+ * Unlike {@link fetchOne}, a missing entry is not `null`: the service's
2000
+ * `RESOURCE_NOT_FOUND` is thrown as it arrived, with the `kind` that says
2001
+ * whether the entry, the content type or the schema was missing. `headers`
2002
+ * are the ones this read adds, as for `fetchOne`.
1910
2003
  */
1911
2004
  async fetchOneWithTag(id, options) {
1912
2005
  const query = {};
@@ -1916,7 +2009,7 @@ var _ContentClient = class _ContentClient {
1916
2009
  const response = await this.httpClient.send(
1917
2010
  "/v1/{contentType}/{id}",
1918
2011
  "get",
1919
- { path: { contentType: this.contentType, id }, query }
2012
+ { path: { contentType: this.contentType, id }, query, headers: options?.headers }
1920
2013
  );
1921
2014
  return { entry: readPayload(response.body), entityTag: entityTagOf(response) };
1922
2015
  }
@@ -1938,17 +2031,20 @@ var _ContentClient = class _ContentClient {
1938
2031
  /**
1939
2032
  * Create a new content item
1940
2033
  * @param data - Content data
1941
- * @param options - Create options (slug, locale, parentId, sortOrder)
2034
+ * @param options - Create options (slug, locale, parentId, sortOrder), and `headers` this one
2035
+ * request adds — e.g. `{ 'X-RelyIO-Dataset': 'staging' }` to write a dataset other than the
2036
+ * client's. They are sent, never put in the body; `idempotencyKey` still wins over an
2037
+ * `Idempotency-Key` among them.
1942
2038
  */
1943
2039
  async create(data, options) {
1944
- const { idempotencyKey, ...body } = options ?? {};
2040
+ const { idempotencyKey, headers, ...body } = options ?? {};
1945
2041
  return this.httpClient.fetchPayload(
1946
2042
  "/v1/{contentType}",
1947
2043
  "post",
1948
2044
  {
1949
2045
  path: { contentType: this.contentType },
1950
2046
  body: { content: data, ...body },
1951
- headers: writeHeaders({ idempotencyKey })
2047
+ headers: requestHeaders(headers, writeHeaders({ idempotencyKey }))
1952
2048
  }
1953
2049
  );
1954
2050
  }
@@ -1956,17 +2052,19 @@ var _ContentClient = class _ContentClient {
1956
2052
  * Update a content item by ID (partial update)
1957
2053
  * @param id - Entry ID
1958
2054
  * @param data - Fields to update
1959
- * @param options - Update options (slug, locale, parentId, sortOrder)
2055
+ * @param options - Update options (slug, locale, parentId, sortOrder), and `headers` this one
2056
+ * request adds, as for {@link create}. `ifMatch` and `idempotencyKey` win over the same
2057
+ * header among them.
1960
2058
  */
1961
2059
  async update(id, data, options) {
1962
- const { idempotencyKey, ifMatch, ...body } = options ?? {};
2060
+ const { idempotencyKey, ifMatch, headers, ...body } = options ?? {};
1963
2061
  return this.httpClient.fetchPayload(
1964
2062
  "/v1/{contentType}/{id}",
1965
2063
  "patch",
1966
2064
  {
1967
2065
  path: { contentType: this.contentType, id },
1968
2066
  body: { content: data, ...body },
1969
- headers: writeHeaders({ idempotencyKey, ifMatch })
2067
+ headers: requestHeaders(headers, writeHeaders({ idempotencyKey, ifMatch }))
1970
2068
  }
1971
2069
  );
1972
2070
  }
@@ -2061,8 +2159,9 @@ var _ContentClient = class _ContentClient {
2061
2159
  if (options?.include != null) {
2062
2160
  params.include = options.include.join(",");
2063
2161
  }
2064
- if (options?.locale != null && options.locale !== "") {
2065
- params.locale = options.locale;
2162
+ const locale = options?.locale ?? this.defaultLocale;
2163
+ if (locale != null && locale !== "") {
2164
+ params.locale = locale;
2066
2165
  }
2067
2166
  return this.httpClient.fetchPayload(
2068
2167
  "/v1/{contentType}/{id}/children",
@@ -2720,9 +2819,10 @@ chunkMWMSFIWN_cjs.__name(isAssetEntry, "isAssetEntry");
2720
2819
 
2721
2820
  // src/content/singleton-client.ts
2722
2821
  var _SingletonClient = class _SingletonClient {
2723
- constructor(httpClient, singletonKey) {
2822
+ constructor(httpClient, singletonKey, defaultLocale) {
2724
2823
  this.httpClient = httpClient;
2725
2824
  this.singletonKey = singletonKey;
2825
+ this.defaultLocale = defaultLocale;
2726
2826
  chunkMWMSFIWN_cjs.__publicField(this, "includeFields");
2727
2827
  chunkMWMSFIWN_cjs.__publicField(this, "localeValue");
2728
2828
  }
@@ -2747,11 +2847,6 @@ var _SingletonClient = class _SingletonClient {
2747
2847
  * @param options - Optional fetch options (alternative to fluent methods)
2748
2848
  */
2749
2849
  async fetch(options) {
2750
- const includeValue = options?.include ?? this.includeFields;
2751
- options?.locale ?? this.localeValue;
2752
- if (includeValue) {
2753
- includeValue.join(",");
2754
- }
2755
2850
  try {
2756
2851
  return (await this.fetchWithTag(options)).content;
2757
2852
  } catch (error) {
@@ -2769,7 +2864,7 @@ var _SingletonClient = class _SingletonClient {
2769
2864
  async fetchWithTag(options) {
2770
2865
  const query = {};
2771
2866
  const includeValue = options?.include ?? this.includeFields;
2772
- const localeValueFinal = options?.locale ?? this.localeValue;
2867
+ const localeValueFinal = options?.locale ?? this.localeValue ?? this.defaultLocale;
2773
2868
  if (includeValue) query.include = includeValue.join(",");
2774
2869
  if (localeValueFinal != null && localeValueFinal !== "") query.locale = localeValueFinal;
2775
2870
  const response = await this.httpClient.send(
@@ -2866,9 +2961,10 @@ var SingletonClient = _SingletonClient;
2866
2961
 
2867
2962
  // src/content/settings-client.ts
2868
2963
  var _SettingsClient = class _SettingsClient {
2869
- constructor(httpClient, groupId) {
2964
+ constructor(httpClient, groupId, defaultLocale) {
2870
2965
  this.httpClient = httpClient;
2871
2966
  this.groupId = groupId;
2967
+ this.defaultLocale = defaultLocale;
2872
2968
  chunkMWMSFIWN_cjs.__publicField(this, "includeFields");
2873
2969
  chunkMWMSFIWN_cjs.__publicField(this, "localeValue");
2874
2970
  }
@@ -2893,11 +2989,6 @@ var _SettingsClient = class _SettingsClient {
2893
2989
  * @param options - Optional fetch options (alternative to fluent methods)
2894
2990
  */
2895
2991
  async fetch(options) {
2896
- const includeValue = options?.include ?? this.includeFields;
2897
- options?.locale ?? this.localeValue;
2898
- if (includeValue) {
2899
- includeValue.join(",");
2900
- }
2901
2992
  try {
2902
2993
  return (await this.fetchWithTag(options)).settings;
2903
2994
  } catch (error) {
@@ -2927,7 +3018,7 @@ var _SettingsClient = class _SettingsClient {
2927
3018
  async fetchWithTag(options) {
2928
3019
  const query = {};
2929
3020
  const includeValue = options?.include ?? this.includeFields;
2930
- const localeValueFinal = options?.locale ?? this.localeValue;
3021
+ const localeValueFinal = options?.locale ?? this.localeValue ?? this.defaultLocale;
2931
3022
  if (includeValue) query.include = includeValue.join(",");
2932
3023
  if (localeValueFinal != null && localeValueFinal !== "") query.locale = localeValueFinal;
2933
3024
  const response = await this.httpClient.send(
@@ -3029,14 +3120,28 @@ var SettingsClient = _SettingsClient;
3029
3120
 
3030
3121
  // src/search/search-client.ts
3031
3122
  var _SearchBuilder = class _SearchBuilder {
3032
- constructor(httpClient, query) {
3123
+ /**
3124
+ * @param defaultLocale - The client's locale (`withLocale()`), sent when `.locale()` names none
3125
+ */
3126
+ constructor(httpClient, query, defaultLocale) {
3033
3127
  this.httpClient = httpClient;
3128
+ this.defaultLocale = defaultLocale;
3034
3129
  chunkMWMSFIWN_cjs.__publicField(this, "query");
3035
3130
  chunkMWMSFIWN_cjs.__publicField(this, "searchOptions", {});
3131
+ chunkMWMSFIWN_cjs.__publicField(this, "requestHeaders");
3036
3132
  this.query = query;
3037
3133
  }
3038
3134
  /**
3039
- * Search within a single content type
3135
+ * Headers this search adds to the client's own, e.g. `X-RelyIO-Dataset` to search another
3136
+ * dataset the key can see. A name the client already sends is replaced for this search only.
3137
+ */
3138
+ headers(headers) {
3139
+ this.requestHeaders = headers;
3140
+ return this;
3141
+ }
3142
+ /**
3143
+ * Search within a single content type. Where a schema types the client, the name is one of its
3144
+ * content types and the results are that type's entries.
3040
3145
  * @param contentType - Content type to search in
3041
3146
  */
3042
3147
  in(contentType) {
@@ -3045,7 +3150,8 @@ var _SearchBuilder = class _SearchBuilder {
3045
3150
  return this;
3046
3151
  }
3047
3152
  /**
3048
- * Search across multiple content types
3153
+ * Search across multiple content types. Where a schema types the client, the names are its content
3154
+ * types and the results are a union of their entries — every type's, for `'*'`.
3049
3155
  * @param contentTypes - Array of content types or '*' for all
3050
3156
  */
3051
3157
  across(contentTypes) {
@@ -3080,6 +3186,16 @@ var _SearchBuilder = class _SearchBuilder {
3080
3186
  this.searchOptions.fields = projection;
3081
3187
  return this;
3082
3188
  }
3189
+ /**
3190
+ * Order the results by a field instead of by relevance. Called again, it adds a tie-breaker.
3191
+ * `_score` names relevance itself.
3192
+ * @param field - Field path to sort by (e.g. `updatedAt`, `pricing.basePrice`)
3193
+ * @param direction - `asc` or `desc` (the route's default is `desc`)
3194
+ */
3195
+ sort(field, direction = "desc") {
3196
+ this.searchOptions.sort = [...this.searchOptions.sort ?? [], { field, direction }];
3197
+ return this;
3198
+ }
3083
3199
  /**
3084
3200
  * Limit number of results
3085
3201
  * @param count - Maximum results
@@ -3140,6 +3256,9 @@ var _SearchBuilder = class _SearchBuilder {
3140
3256
  if (this.searchOptions.fields) {
3141
3257
  body.fields = this.searchOptions.fields;
3142
3258
  }
3259
+ if (this.searchOptions.sort) {
3260
+ body.sort = this.searchOptions.sort;
3261
+ }
3143
3262
  if (this.searchOptions.limit !== void 0) {
3144
3263
  body.limit = this.searchOptions.limit;
3145
3264
  }
@@ -3152,13 +3271,14 @@ var _SearchBuilder = class _SearchBuilder {
3152
3271
  if (this.searchOptions.explain === true) {
3153
3272
  body.explain = true;
3154
3273
  }
3155
- if (this.searchOptions.locale != null && this.searchOptions.locale !== "") {
3156
- body.locale = this.searchOptions.locale;
3274
+ const locale = this.searchOptions.locale ?? this.defaultLocale;
3275
+ if (locale != null && locale !== "") {
3276
+ body.locale = locale;
3157
3277
  }
3158
3278
  const response = await this.httpClient.send(
3159
3279
  "/v1/search",
3160
3280
  "post",
3161
- { body }
3281
+ { body, headers: this.requestHeaders }
3162
3282
  );
3163
3283
  return response.body;
3164
3284
  }
@@ -3178,8 +3298,12 @@ var _SearchBuilder = class _SearchBuilder {
3178
3298
  chunkMWMSFIWN_cjs.__name(_SearchBuilder, "SearchBuilder");
3179
3299
  var SearchBuilder = _SearchBuilder;
3180
3300
  var _AutocompleteBuilder = class _AutocompleteBuilder {
3181
- constructor(httpClient, query) {
3301
+ /**
3302
+ * @param defaultLocale - The client's locale (`withLocale()`), sent when `.locale()` names none
3303
+ */
3304
+ constructor(httpClient, query, defaultLocale) {
3182
3305
  this.httpClient = httpClient;
3306
+ this.defaultLocale = defaultLocale;
3183
3307
  chunkMWMSFIWN_cjs.__publicField(this, "query");
3184
3308
  chunkMWMSFIWN_cjs.__publicField(this, "autocompleteOptions", {});
3185
3309
  this.query = query;
@@ -3258,8 +3382,9 @@ var _AutocompleteBuilder = class _AutocompleteBuilder {
3258
3382
  if (this.autocompleteOptions.limit !== void 0) {
3259
3383
  body.limit = this.autocompleteOptions.limit;
3260
3384
  }
3261
- if (this.autocompleteOptions.locale != null && this.autocompleteOptions.locale !== "") {
3262
- body.locale = this.autocompleteOptions.locale;
3385
+ const locale = this.autocompleteOptions.locale ?? this.defaultLocale;
3386
+ if (locale != null && locale !== "") {
3387
+ body.locale = locale;
3263
3388
  }
3264
3389
  const response = await this.httpClient.send("/v1/search/autocomplete", "post", { body });
3265
3390
  return response.body;
@@ -3274,18 +3399,41 @@ var _AutocompleteBuilder = class _AutocompleteBuilder {
3274
3399
  chunkMWMSFIWN_cjs.__name(_AutocompleteBuilder, "AutocompleteBuilder");
3275
3400
  var AutocompleteBuilder = _AutocompleteBuilder;
3276
3401
  var _SimilarBuilder = class _SimilarBuilder {
3277
- constructor(httpClient, sourceId) {
3402
+ /**
3403
+ * @param defaultLocale - The client's locale (`withLocale()`), sent when `.locale()` names none
3404
+ */
3405
+ constructor(httpClient, sourceId, defaultLocale) {
3278
3406
  this.httpClient = httpClient;
3407
+ this.defaultLocale = defaultLocale;
3279
3408
  chunkMWMSFIWN_cjs.__publicField(this, "sourceId");
3280
3409
  chunkMWMSFIWN_cjs.__publicField(this, "similarOptions", {});
3410
+ chunkMWMSFIWN_cjs.__publicField(this, "requestHeaders");
3281
3411
  this.sourceId = sourceId;
3282
3412
  }
3413
+ /**
3414
+ * Headers this read adds to the client's own, e.g. `X-RelyIO-Dataset` to read another dataset
3415
+ * the key can see. A name the client already sends is replaced for this read only.
3416
+ */
3417
+ headers(headers) {
3418
+ this.requestHeaders = headers;
3419
+ return this;
3420
+ }
3283
3421
  /**
3284
3422
  * Find similar content within a content type
3285
3423
  * @param contentType - Content type to search
3286
3424
  */
3287
3425
  in(contentType) {
3288
3426
  this.similarOptions.contentType = contentType;
3427
+ this.similarOptions.contentTypes = void 0;
3428
+ return this;
3429
+ }
3430
+ /**
3431
+ * Find similar content across several content types, or `'*'` for every type
3432
+ * @param contentTypes - Array of content types or '*' for all
3433
+ */
3434
+ across(contentTypes) {
3435
+ this.similarOptions.contentTypes = contentTypes;
3436
+ this.similarOptions.contentType = void 0;
3289
3437
  return this;
3290
3438
  }
3291
3439
  /**
@@ -3319,26 +3467,33 @@ var _SimilarBuilder = class _SimilarBuilder {
3319
3467
  * Execute the similar query
3320
3468
  */
3321
3469
  async execute() {
3322
- if (this.similarOptions.contentType == null || this.similarOptions.contentType === "") {
3323
- throw new Error('Content type is required for similar search. Use .in("contentType")');
3470
+ const { contentType, contentTypes } = this.similarOptions;
3471
+ if ((contentType == null || contentType === "") && contentTypes == null) {
3472
+ throw new Error(
3473
+ 'Content type is required for similar search. Use .in("contentType") or .across("*")'
3474
+ );
3475
+ }
3476
+ const body = { source: this.sourceId };
3477
+ if (contentType != null && contentType !== "") {
3478
+ body.contentType = contentType;
3479
+ }
3480
+ if (contentTypes != null) {
3481
+ body.contentTypes = contentTypes;
3324
3482
  }
3325
- const body = {
3326
- source: this.sourceId,
3327
- contentType: this.similarOptions.contentType
3328
- };
3329
3483
  if (this.similarOptions.where && Object.keys(this.similarOptions.where).length > 0) {
3330
3484
  body.where = this.similarOptions.where;
3331
3485
  }
3332
3486
  if (this.similarOptions.limit !== void 0) {
3333
3487
  body.limit = this.similarOptions.limit;
3334
3488
  }
3335
- if (this.similarOptions.locale != null && this.similarOptions.locale !== "") {
3336
- body.locale = this.similarOptions.locale;
3489
+ const locale = this.similarOptions.locale ?? this.defaultLocale;
3490
+ if (locale != null && locale !== "") {
3491
+ body.locale = locale;
3337
3492
  }
3338
3493
  const response = await this.httpClient.send(
3339
3494
  "/v1/search/similar",
3340
3495
  "post",
3341
- { body }
3496
+ { body, headers: this.requestHeaders }
3342
3497
  );
3343
3498
  return response.body;
3344
3499
  }
@@ -3358,29 +3513,34 @@ var _SimilarBuilder = class _SimilarBuilder {
3358
3513
  chunkMWMSFIWN_cjs.__name(_SimilarBuilder, "SimilarBuilder");
3359
3514
  var SimilarBuilder = _SimilarBuilder;
3360
3515
  var _SearchClient = class _SearchClient {
3361
- constructor(httpClient) {
3516
+ /**
3517
+ * @param defaultLocale - The client's locale (`withLocale()`): every builder here sends it when
3518
+ * the query names none, as `content()`'s reads do
3519
+ */
3520
+ constructor(httpClient, defaultLocale) {
3362
3521
  this.httpClient = httpClient;
3522
+ this.defaultLocale = defaultLocale;
3363
3523
  }
3364
3524
  /**
3365
3525
  * Start a search query
3366
3526
  * @param query - Search query string
3367
3527
  */
3368
3528
  search(query) {
3369
- return new SearchBuilder(this.httpClient, query);
3529
+ return new SearchBuilder(this.httpClient, query, this.defaultLocale);
3370
3530
  }
3371
3531
  /**
3372
3532
  * Start an autocomplete query
3373
3533
  * @param query - Partial query to autocomplete
3374
3534
  */
3375
3535
  autocomplete(query) {
3376
- return new AutocompleteBuilder(this.httpClient, query);
3536
+ return new AutocompleteBuilder(this.httpClient, query, this.defaultLocale);
3377
3537
  }
3378
3538
  /**
3379
3539
  * Find content similar to a given entry
3380
3540
  * @param sourceId - Entry ID to find similar content for
3381
3541
  */
3382
3542
  similar(sourceId) {
3383
- return new SimilarBuilder(this.httpClient, sourceId);
3543
+ return new SimilarBuilder(this.httpClient, sourceId, this.defaultLocale);
3384
3544
  }
3385
3545
  /**
3386
3546
  * Quick search helper - searches all content types
@@ -3405,6 +3565,7 @@ var _BatchBuilder = class _BatchBuilder {
3405
3565
  returnData: true,
3406
3566
  continueOnError: true
3407
3567
  });
3568
+ chunkMWMSFIWN_cjs.__publicField(this, "requestHeaders");
3408
3569
  }
3409
3570
  // ==========================================================================
3410
3571
  // ENTRY OPERATIONS
@@ -3606,6 +3767,15 @@ var _BatchBuilder = class _BatchBuilder {
3606
3767
  this.defaultOptions.continueOnError = false;
3607
3768
  return this;
3608
3769
  }
3770
+ /**
3771
+ * Headers the batch request adds — e.g. `{ 'X-RelyIO-Dataset': 'staging' }` to write a dataset
3772
+ * other than the client's. They replace the client's own header of the same name for this
3773
+ * request alone, and apply to every operation in it: a batch is one request.
3774
+ */
3775
+ headers(headers) {
3776
+ this.requestHeaders = { ...this.requestHeaders, ...headers };
3777
+ return this;
3778
+ }
3609
3779
  // ==========================================================================
3610
3780
  // EXECUTION
3611
3781
  // ==========================================================================
@@ -3635,6 +3805,7 @@ var _BatchBuilder = class _BatchBuilder {
3635
3805
  const cloned = new _BatchBuilder(this.httpClient);
3636
3806
  cloned.operations = [...this.operations];
3637
3807
  cloned.defaultOptions = { ...this.defaultOptions };
3808
+ cloned.requestHeaders = this.requestHeaders === void 0 ? void 0 : { ...this.requestHeaders };
3638
3809
  return cloned;
3639
3810
  }
3640
3811
  /**
@@ -3652,7 +3823,8 @@ var _BatchBuilder = class _BatchBuilder {
3652
3823
  }
3653
3824
  const finalOptions = { ...this.defaultOptions, ...options };
3654
3825
  return this.httpClient.fetchPayload("/v1/batch", "post", {
3655
- body: { operations: this.operations, options: finalOptions }
3826
+ body: { operations: this.operations, options: finalOptions },
3827
+ headers: this.requestHeaders
3656
3828
  });
3657
3829
  }
3658
3830
  /**
@@ -3732,9 +3904,21 @@ chunkMWMSFIWN_cjs.__name(_BatchClient, "BatchClient");
3732
3904
  var BatchClient = _BatchClient;
3733
3905
 
3734
3906
  // src/queries/adhoc-query-client.ts
3907
+ function withClientLocale(parameters, clientLocale) {
3908
+ if (clientLocale === void 0 || clientLocale === "" || parameters?.locale != null) {
3909
+ return parameters;
3910
+ }
3911
+ return { ...parameters, locale: clientLocale };
3912
+ }
3913
+ chunkMWMSFIWN_cjs.__name(withClientLocale, "withClientLocale");
3735
3914
  var _AdhocQueryBuilder = class _AdhocQueryBuilder {
3736
- constructor(httpClient) {
3915
+ /**
3916
+ * @param defaultLocale - The client's locale (`withLocale()`), sent as the `locale` parameter
3917
+ * when `.param('locale', …)` binds none
3918
+ */
3919
+ constructor(httpClient, defaultLocale) {
3737
3920
  this.httpClient = httpClient;
3921
+ this.defaultLocale = defaultLocale;
3738
3922
  chunkMWMSFIWN_cjs.__publicField(this, "queryDef", {});
3739
3923
  chunkMWMSFIWN_cjs.__publicField(this, "parameters", {});
3740
3924
  chunkMWMSFIWN_cjs.__publicField(this, "options", {});
@@ -3974,8 +4158,9 @@ var _AdhocQueryBuilder = class _AdhocQueryBuilder {
3974
4158
  const body = {
3975
4159
  query: this.queryDef
3976
4160
  };
3977
- if (Object.keys(this.parameters).length > 0) {
3978
- body.parameters = this.parameters;
4161
+ const parameters = withClientLocale(this.parameters, this.defaultLocale);
4162
+ if (parameters !== void 0 && Object.keys(parameters).length > 0) {
4163
+ body.parameters = parameters;
3979
4164
  }
3980
4165
  if (Object.keys(this.options).length > 0) {
3981
4166
  body.options = this.options;
@@ -4006,7 +4191,7 @@ var _AdhocQueryBuilder = class _AdhocQueryBuilder {
4006
4191
  * Clone the builder
4007
4192
  */
4008
4193
  clone() {
4009
- const cloned = new _AdhocQueryBuilder(this.httpClient);
4194
+ const cloned = new _AdhocQueryBuilder(this.httpClient, this.defaultLocale);
4010
4195
  cloned.queryDef = JSON.parse(JSON.stringify(this.queryDef));
4011
4196
  cloned.parameters = { ...this.parameters };
4012
4197
  cloned.options = { ...this.options };
@@ -4016,27 +4201,36 @@ var _AdhocQueryBuilder = class _AdhocQueryBuilder {
4016
4201
  chunkMWMSFIWN_cjs.__name(_AdhocQueryBuilder, "AdhocQueryBuilder");
4017
4202
  var AdhocQueryBuilder = _AdhocQueryBuilder;
4018
4203
  var _AdhocQueryClient = class _AdhocQueryClient {
4019
- constructor(httpClient) {
4204
+ /**
4205
+ * @param defaultLocale - The client's locale (`withLocale()`), sent as the `locale` parameter of
4206
+ * every query that binds none
4207
+ */
4208
+ constructor(httpClient, defaultLocale) {
4020
4209
  this.httpClient = httpClient;
4210
+ this.defaultLocale = defaultLocale;
4021
4211
  }
4022
4212
  /**
4023
4213
  * Create a new query builder
4024
4214
  */
4025
4215
  build() {
4026
- return new AdhocQueryBuilder(this.httpClient);
4216
+ return new AdhocQueryBuilder(this.httpClient, this.defaultLocale);
4027
4217
  }
4028
4218
  /**
4029
4219
  * Execute a query definition directly
4030
4220
  * @param definition - Query definition
4031
4221
  * @param parameters - Parameter values
4032
4222
  * @param options - Execution options
4223
+ * @param request - `headers` this one request adds to the client's own, e.g. `X-RelyIO-Dataset`
4224
+ * to query another dataset the key can see. A name the client already sends is replaced for
4225
+ * this request only.
4033
4226
  */
4034
- async execute(definition, parameters, options) {
4227
+ async execute(definition, parameters, options, request) {
4035
4228
  const body = {
4036
4229
  query: definition
4037
4230
  };
4038
- if (parameters && Object.keys(parameters).length > 0) {
4039
- body.parameters = parameters;
4231
+ const sent = withClientLocale(parameters, this.defaultLocale);
4232
+ if (sent !== void 0 && Object.keys(sent).length > 0) {
4233
+ body.parameters = sent;
4040
4234
  }
4041
4235
  if (options && Object.keys(options).length > 0) {
4042
4236
  body.options = options;
@@ -4044,7 +4238,7 @@ var _AdhocQueryClient = class _AdhocQueryClient {
4044
4238
  return this.httpClient.fetchPayload(
4045
4239
  "/v1/query",
4046
4240
  "post",
4047
- { body }
4241
+ { body, headers: request?.headers }
4048
4242
  );
4049
4243
  }
4050
4244
  /**
@@ -4108,12 +4302,12 @@ async function* pollChanges(http, options = {}) {
4108
4302
  let since = options.since;
4109
4303
  let resynced;
4110
4304
  for (; ; ) {
4111
- if (aborted(signal)) return;
4305
+ if (isAborted(signal)) return;
4112
4306
  let response;
4113
4307
  try {
4114
4308
  response = await pollOnce(http, since, options);
4115
4309
  } catch (error) {
4116
- if (aborted(signal)) return;
4310
+ if (isAborted(signal)) return;
4117
4311
  if (!(error instanceof ResourceExpiredError) || since === void 0) throw error;
4118
4312
  const expired = ChangesExpiredError.of(error);
4119
4313
  const onExpired = options.onExpired ?? "throw";
@@ -4131,11 +4325,11 @@ async function* pollChanges(http, options = {}) {
4131
4325
  const expired = resynced;
4132
4326
  resynced = void 0;
4133
4327
  await options.onExpired(expired);
4134
- if (aborted(signal)) return;
4328
+ if (isAborted(signal)) return;
4135
4329
  }
4136
4330
  for (const change of data) {
4137
4331
  yield change;
4138
- if (aborted(signal)) return;
4332
+ if (isAborted(signal)) return;
4139
4333
  }
4140
4334
  if (data.length === 0) {
4141
4335
  const wait = parseRetryAfterMs(headerValue(response.headers, "Retry-After"), Date.now());
@@ -4144,6 +4338,21 @@ async function* pollChanges(http, options = {}) {
4144
4338
  }
4145
4339
  }
4146
4340
  chunkMWMSFIWN_cjs.__name(pollChanges, "pollChanges");
4341
+ async function readChangesHead(http, options = {}) {
4342
+ const response = await http.send("/v1/changes", "get", {
4343
+ query: {},
4344
+ ...options.signal === void 0 ? {} : { signal: options.signal }
4345
+ });
4346
+ const { data, page } = response.body;
4347
+ if (page.nextCursor === null) {
4348
+ throw new Error("GET /v1/changes answered without page.nextCursor");
4349
+ }
4350
+ if (data.length > 0) {
4351
+ throw new Error("GET /v1/changes without since answered changes; a head carries none");
4352
+ }
4353
+ return page.nextCursor;
4354
+ }
4355
+ chunkMWMSFIWN_cjs.__name(readChangesHead, "readChangesHead");
4147
4356
  async function pollOnce(http, since, options) {
4148
4357
  const query = {};
4149
4358
  if (since !== void 0) query.since = since;
@@ -4157,26 +4366,6 @@ async function pollOnce(http, since, options) {
4157
4366
  });
4158
4367
  }
4159
4368
  chunkMWMSFIWN_cjs.__name(pollOnce, "pollOnce");
4160
- function sleep(ms, signal) {
4161
- return new Promise((resolve) => {
4162
- if (aborted(signal)) {
4163
- resolve();
4164
- return;
4165
- }
4166
- const done = /* @__PURE__ */ chunkMWMSFIWN_cjs.__name(() => {
4167
- clearTimeout(timer);
4168
- signal?.removeEventListener("abort", done);
4169
- resolve();
4170
- }, "done");
4171
- const timer = setTimeout(done, ms);
4172
- signal?.addEventListener("abort", done, { once: true });
4173
- });
4174
- }
4175
- chunkMWMSFIWN_cjs.__name(sleep, "sleep");
4176
- function aborted(signal) {
4177
- return signal?.aborted === true;
4178
- }
4179
- chunkMWMSFIWN_cjs.__name(aborted, "aborted");
4180
4369
 
4181
4370
  // src/config/dev-detection.ts
4182
4371
  function sandboxStateOf(resolution) {
@@ -4314,10 +4503,7 @@ var DEFAULT_CONFIG = {
4314
4503
  }
4315
4504
  };
4316
4505
  function validateConfig(config) {
4317
- if (!config.apiKey || config.apiKey.length === 0) {
4318
- throw new Error("API key is required");
4319
- }
4320
- if (typeof config.apiKey !== "string") {
4506
+ if (config.apiKey != null && typeof config.apiKey !== "string") {
4321
4507
  throw new Error("API key must be a string");
4322
4508
  }
4323
4509
  if (config.baseUrl != null && typeof config.baseUrl !== "string") {
@@ -4400,9 +4586,9 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4400
4586
  this.config = resolved.config;
4401
4587
  this._sandbox = resolved.sandbox;
4402
4588
  this.httpClient = new HttpClient(this.config);
4403
- this._search = new SearchClient(this.httpClient);
4589
+ this._search = new SearchClient(this.httpClient, this.config.locale);
4404
4590
  this._batch = new BatchClient(this.httpClient);
4405
- this._adhocQuery = new AdhocQueryClient(this.httpClient);
4591
+ this._adhocQuery = new AdhocQueryClient(this.httpClient, this.config.locale);
4406
4592
  }
4407
4593
  // ============================================================================
4408
4594
  // CORE CLIENTS
@@ -4490,7 +4676,7 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4490
4676
  * ```
4491
4677
  */
4492
4678
  singleton(key) {
4493
- return new SingletonClient(this.httpClient, key);
4679
+ return new SingletonClient(this.httpClient, key, this.config.locale);
4494
4680
  }
4495
4681
  /**
4496
4682
  * Get a settings client for a specific settings group
@@ -4504,13 +4690,16 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4504
4690
  * ```
4505
4691
  */
4506
4692
  settings(groupId) {
4507
- return new SettingsClient(this.httpClient, groupId);
4693
+ return new SettingsClient(this.httpClient, groupId, this.config.locale);
4508
4694
  }
4509
4695
  // ============================================================================
4510
4696
  // SEARCH OPERATIONS
4511
4697
  // ============================================================================
4512
4698
  /**
4513
- * Start a search query with full-text, semantic, or hybrid search
4699
+ * Start a search query with full-text, semantic, or hybrid search.
4700
+ *
4701
+ * Where a schema types the client, the results are its entries — every content type's until
4702
+ * `in()` or `across()` narrows them (`INV-SS-TYPES-INFERRED`).
4514
4703
  *
4515
4704
  * @example
4516
4705
  * ```typescript
@@ -4608,6 +4797,25 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4608
4797
  changes(options = {}) {
4609
4798
  return pollChanges(this.httpClient, options);
4610
4799
  }
4800
+ /**
4801
+ * The dataset's current position in the changes feed, as a cursor to start
4802
+ * `changes({ since })` or `followChanges(…, { since })` from. One
4803
+ * `GET /v1/changes` without a cursor: answered at once, holding nothing.
4804
+ *
4805
+ * Take it **before** you read what you will follow. A write your read did not
4806
+ * see is then after the position and replayed; opening the feed afterwards
4807
+ * without a `since` starts after it and misses it.
4808
+ *
4809
+ * @example
4810
+ * ```typescript
4811
+ * const since = await relyio.changesHead()
4812
+ * const post = await relyio.content('post').findById(id).execute()
4813
+ * for await (const change of relyio.changes({ since, ids: [id] })) { … }
4814
+ * ```
4815
+ */
4816
+ changesHead(options = {}) {
4817
+ return readChangesHead(this.httpClient, options);
4818
+ }
4611
4819
  // ============================================================================
4612
4820
  // QUERY OPERATIONS
4613
4821
  // ============================================================================
@@ -4645,10 +4853,12 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4645
4853
  * ```
4646
4854
  */
4647
4855
  async executeQuery(queryName, params) {
4856
+ const clientLocale = this.config.locale;
4857
+ const namesLocale = params?.locale != null || params?.locales != null;
4648
4858
  const response = await this.httpClient.request({
4649
4859
  method: "GET",
4650
4860
  url: `/${API_VERSION_SEGMENT}/${String(queryName)}`,
4651
- params: params ?? {}
4861
+ params: namesLocale || clientLocale === void 0 || clientLocale === "" ? params ?? {} : { ...params, locale: clientLocale }
4652
4862
  });
4653
4863
  const payload = readPayload(response.body);
4654
4864
  return Array.isArray(payload) ? payload : [payload];
@@ -4720,9 +4930,9 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4720
4930
  this.config = resolved.config;
4721
4931
  this._sandbox = resolved.sandbox;
4722
4932
  this.httpClient = new HttpClient(this.config);
4723
- this._search = new SearchClient(this.httpClient);
4933
+ this._search = new SearchClient(this.httpClient, this.config.locale);
4724
4934
  this._batch = new BatchClient(this.httpClient);
4725
- this._adhocQuery = new AdhocQueryClient(this.httpClient);
4935
+ this._adhocQuery = new AdhocQueryClient(this.httpClient, this.config.locale);
4726
4936
  }
4727
4937
  /**
4728
4938
  * Get the current configuration
@@ -4748,7 +4958,10 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4748
4958
  // ============================================================================
4749
4959
  /**
4750
4960
  * Create a locale-scoped client
4751
- * All content operations on the returned client will use the specified locale
4961
+ *
4962
+ * Every read the returned client builds — `content()`, `asset()`, `singleton()`, `settings()`,
4963
+ * `search()`, `similar()`, `autocomplete()`, `executeQuery()` and the ad-hoc queries — is in this
4964
+ * locale unless the call names one of its own, which wins.
4752
4965
  *
4753
4966
  * Type-safe when schema is provided: autocompletes valid locale codes!
4754
4967
  *
@@ -4756,9 +4969,10 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4756
4969
  * ```typescript
4757
4970
  * const spanish = relyio.withLocale('es-ES') // ← Autocompletes with schema!
4758
4971
  *
4759
- * // All operations use Spanish
4972
+ * // Reads use Spanish
4760
4973
  * const products = await spanish.content('product').query().execute()
4761
4974
  * const homepage = await spanish.singleton('homepage').fetch()
4975
+ * const results = await spanish.search('zapatos').execute()
4762
4976
  *
4763
4977
  * // Original client unchanged
4764
4978
  * const englishProducts = await relyio.content('product').query().execute()
@@ -4866,9 +5080,12 @@ exports.UsageQuotaExceededError = UsageQuotaExceededError;
4866
5080
  exports.ValidationError = ValidationError;
4867
5081
  exports.createClient = createClient;
4868
5082
  exports.entityTagOf = entityTagOf;
5083
+ exports.isAborted = isAborted;
4869
5084
  exports.isAssetEntry = isAssetEntry;
4870
5085
  exports.isResourceNotFound = isResourceNotFound;
4871
5086
  exports.matchRoute = matchRoute;
5087
+ exports.serviceInstructedDelayMs = serviceInstructedDelayMs;
5088
+ exports.sleep = sleep;
4872
5089
  exports.validateFile = validateFile;
4873
- //# sourceMappingURL=chunk-CIRT6TVT.cjs.map
4874
- //# sourceMappingURL=chunk-CIRT6TVT.cjs.map
5090
+ //# sourceMappingURL=chunk-GCMQUM6N.cjs.map
5091
+ //# sourceMappingURL=chunk-GCMQUM6N.cjs.map