@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
@@ -69,44 +69,6 @@ var _SDKError = class _SDKError extends Error {
69
69
  __name(_SDKError, "SDKError");
70
70
  var SDKError = _SDKError;
71
71
 
72
- // src/errors/auth-errors.ts
73
- var _AuthenticationError = class _AuthenticationError extends SDKError {
74
- constructor(message = "Authentication failed", statusCode = 401, cause, context) {
75
- super(message, statusCode, cause, context);
76
- __publicField(this, "code", "AUTHENTICATION_ERROR");
77
- __publicField(this, "retryable", false);
78
- }
79
- getUserMessage() {
80
- return "Authentication failed. Please check your API key and try again.";
81
- }
82
- };
83
- __name(_AuthenticationError, "AuthenticationError");
84
- var AuthenticationError = _AuthenticationError;
85
- var _AuthorizationError = class _AuthorizationError extends SDKError {
86
- constructor(message = "Authorization failed", statusCode = 403, cause, context) {
87
- super(message, statusCode, cause, context);
88
- __publicField(this, "code", "AUTHORIZATION_ERROR");
89
- __publicField(this, "retryable", false);
90
- }
91
- getUserMessage() {
92
- return "You do not have permission to access this resource.";
93
- }
94
- };
95
- __name(_AuthorizationError, "AuthorizationError");
96
- var AuthorizationError = _AuthorizationError;
97
- var _InvalidApiKeyError = class _InvalidApiKeyError extends SDKError {
98
- constructor(message = "Invalid API key", cause, context) {
99
- super(message, 401, cause, context);
100
- __publicField(this, "code", "INVALID_API_KEY");
101
- __publicField(this, "retryable", false);
102
- }
103
- getUserMessage() {
104
- return "Invalid API key. Please check your API key and try again.";
105
- }
106
- };
107
- __name(_InvalidApiKeyError, "InvalidApiKeyError");
108
- var InvalidApiKeyError = _InvalidApiKeyError;
109
-
110
72
  // src/errors/contract-errors.ts
111
73
  var _MissingApiKeyError = class _MissingApiKeyError extends SDKError {
112
74
  constructor(message = "No API key was sent", context) {
@@ -267,6 +229,44 @@ var _UnknownContractError = class _UnknownContractError extends SDKError {
267
229
  __name(_UnknownContractError, "UnknownContractError");
268
230
  var UnknownContractError = _UnknownContractError;
269
231
 
232
+ // 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
+ var _AuthorizationError = class _AuthorizationError extends SDKError {
246
+ constructor(message = "Authorization failed", statusCode = 403, cause, context) {
247
+ super(message, statusCode, cause, context);
248
+ __publicField(this, "code", "AUTHORIZATION_ERROR");
249
+ __publicField(this, "retryable", false);
250
+ }
251
+ getUserMessage() {
252
+ return "You do not have permission to access this resource.";
253
+ }
254
+ };
255
+ __name(_AuthorizationError, "AuthorizationError");
256
+ var AuthorizationError = _AuthorizationError;
257
+ var _InvalidApiKeyError = class _InvalidApiKeyError extends SDKError {
258
+ constructor(message = "Invalid API key", cause, context) {
259
+ super(message, 401, cause, context);
260
+ __publicField(this, "code", "INVALID_API_KEY");
261
+ __publicField(this, "retryable", false);
262
+ }
263
+ getUserMessage() {
264
+ return "Invalid API key. Please check your API key and try again.";
265
+ }
266
+ };
267
+ __name(_InvalidApiKeyError, "InvalidApiKeyError");
268
+ var InvalidApiKeyError = _InvalidApiKeyError;
269
+
270
270
  // src/errors/content-errors.ts
271
271
  var _ResourceNotFoundError = class _ResourceNotFoundError extends SDKError {
272
272
  constructor(message = "Resource not found", statusCode = 404, cause, context) {
@@ -328,8 +328,12 @@ var _NetworkError = class _NetworkError extends SDKError {
328
328
  __name(_NetworkError, "NetworkError");
329
329
  var NetworkError = _NetworkError;
330
330
  var _TimeoutError = class _TimeoutError extends SDKError {
331
- constructor(message = "Request timed out", timeout, statusCode, cause) {
332
- super(message, statusCode, cause, { timeout });
331
+ /**
332
+ * @param context - Carried beside `timeout`. The mapper passes a `REQUEST_TIMEOUT` refusal's body
333
+ * as `context.response`, as it does for every other contract code.
334
+ */
335
+ constructor(message = "Request timed out", timeout, statusCode, cause, context) {
336
+ super(message, statusCode, cause, { ...context, timeout });
333
337
  __publicField(this, "code", "TIMEOUT_ERROR");
334
338
  __publicField(this, "retryable", true);
335
339
  }
@@ -355,8 +359,13 @@ var ConnectionError = _ConnectionError;
355
359
 
356
360
  // src/errors/validation-errors.ts
357
361
  var _BaseValidationError = class _BaseValidationError extends SDKError {
358
- constructor(message = "Validation failed", violations = [], statusCode = 400, cause) {
359
- super(message, statusCode, cause, { violations });
362
+ /**
363
+ * @param context - Carried beside `violations`. The mapper passes the refusal body as
364
+ * `context.response`, as it does for every other contract code, so a consumer that hands the
365
+ * service's own words on can read them here too.
366
+ */
367
+ constructor(message = "Validation failed", violations = [], statusCode = 400, cause, context) {
368
+ super(message, statusCode, cause, { ...context, violations });
360
369
  __publicField(this, "retryable", false);
361
370
  __publicField(this, "violations");
362
371
  this.violations = violations;
@@ -401,8 +410,8 @@ var _ValidationError = class _ValidationError extends BaseValidationError {
401
410
  __name(_ValidationError, "ValidationError");
402
411
  var ValidationError = _ValidationError;
403
412
  var _InvalidPayloadError = class _InvalidPayloadError extends BaseValidationError {
404
- constructor(message = "Invalid payload", violations = [], cause) {
405
- super(message, violations, 400, cause);
413
+ constructor(message = "Invalid payload", violations = [], cause, context) {
414
+ super(message, violations, 400, cause, context);
406
415
  __publicField(this, "code", "INVALID_PAYLOAD");
407
416
  }
408
417
  getUserMessage() {
@@ -591,10 +600,10 @@ var _ErrorMapper = class _ErrorMapper {
591
600
  );
592
601
  }
593
602
  case "REQUEST_INVALID": {
594
- return new InvalidPayloadError(message, violationsOf(refusal));
603
+ return new InvalidPayloadError(message, violationsOf(refusal), void 0, context);
595
604
  }
596
605
  case "CONTENT_INVALID": {
597
- return new ValidationError(message, violationsOf(refusal), 400);
606
+ return new ValidationError(message, violationsOf(refusal), 400, void 0, context);
598
607
  }
599
608
  case "METHOD_NOT_ALLOWED": {
600
609
  return new MethodNotAllowedError(message, context);
@@ -617,7 +626,7 @@ var _ErrorMapper = class _ErrorMapper {
617
626
  return new ResourceExpiredError(message, 410, void 0, context);
618
627
  }
619
628
  case "REQUEST_TIMEOUT": {
620
- return new TimeoutError(message, void 0, void 0, void 0);
629
+ return new TimeoutError(message, void 0, status, void 0, context);
621
630
  }
622
631
  case "PAYLOAD_TOO_LARGE": {
623
632
  return new PayloadTooLargeError(message, detailNumber(refusal, "limitBytes"), context);
@@ -840,20 +849,29 @@ function readQuery(searchParams) {
840
849
  }
841
850
  __name(readQuery, "readQuery");
842
851
 
843
- // src/http/retry-policy.ts
844
- var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set([
845
- "GET",
846
- "HEAD",
847
- "OPTIONS",
848
- "PUT",
849
- "DELETE",
850
- "TRACE"
851
- ]);
852
- function isReplaySafe(request) {
853
- if (IDEMPOTENT_METHODS.has(request.method.toUpperCase())) return true;
854
- return headerValue(request.headers, HEADER_IDEMPOTENCY_KEY.name) !== void 0;
852
+ // src/http/abortable-sleep.ts
853
+ function sleep(ms, signal) {
854
+ return new Promise((resolve) => {
855
+ if (isAborted(signal)) {
856
+ resolve();
857
+ return;
858
+ }
859
+ const done = /* @__PURE__ */ __name(() => {
860
+ clearTimeout(timer);
861
+ signal?.removeEventListener("abort", done);
862
+ resolve();
863
+ }, "done");
864
+ const timer = setTimeout(done, ms);
865
+ signal?.addEventListener("abort", done, { once: true });
866
+ });
855
867
  }
856
- __name(isReplaySafe, "isReplaySafe");
868
+ __name(sleep, "sleep");
869
+ function isAborted(signal) {
870
+ return signal?.aborted === true;
871
+ }
872
+ __name(isAborted, "isAborted");
873
+
874
+ // src/errors/instructed-delay.ts
857
875
  function serviceInstructedDelayMs(error, nowMs) {
858
876
  if (error instanceof ServiceIndeterminateError) {
859
877
  return error.retryAfterMs;
@@ -867,6 +885,21 @@ function serviceInstructedDelayMs(error, nowMs) {
867
885
  return wait > 0 ? wait : void 0;
868
886
  }
869
887
  __name(serviceInstructedDelayMs, "serviceInstructedDelayMs");
888
+
889
+ // src/http/retry-policy.ts
890
+ var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set([
891
+ "GET",
892
+ "HEAD",
893
+ "OPTIONS",
894
+ "PUT",
895
+ "DELETE",
896
+ "TRACE"
897
+ ]);
898
+ function isReplaySafe(request) {
899
+ if (IDEMPOTENT_METHODS.has(request.method.toUpperCase())) return true;
900
+ return headerValue(request.headers, HEADER_IDEMPOTENCY_KEY.name) !== void 0;
901
+ }
902
+ __name(isReplaySafe, "isReplaySafe");
870
903
  var _DefaultRetryPolicy = class _DefaultRetryPolicy {
871
904
  constructor(config) {
872
905
  __publicField(this, "config");
@@ -903,9 +936,10 @@ var _HttpClient = class _HttpClient {
903
936
  this.timeout = config.timeout;
904
937
  this.debug = config.debug.enabled ?? false;
905
938
  this.logger = config.debug.logger;
939
+ const apiKey = presentKey(config.apiKey);
906
940
  this.defaultHeaders = {
907
941
  "Content-Type": "application/json",
908
- "X-API-Key": config.apiKey,
942
+ ...apiKey === void 0 ? {} : { [API_KEY_HEADER]: apiKey },
909
943
  ...config.headers
910
944
  };
911
945
  if (config.dataset != null) {
@@ -964,9 +998,21 @@ var _HttpClient = class _HttpClient {
964
998
  * The loop always makes one attempt. The incumbent's `while (attempt <
965
999
  * maxAttempts)` made none at `retryAttempts: 0` — a value the config validator
966
1000
  * accepts — and then dereferenced an unassigned `lastError`.
1001
+ *
1002
+ * A request that would carry no `X-API-Key` makes none: it rejects with
1003
+ * `MissingApiKeyError` before `fetch` is called, is not retried, and names
1004
+ * `apiKey`. This is where a client built without a key learns it — the
1005
+ * constructor accepts one so that a module-scope client can be evaluated by a
1006
+ * build that holds no key (REL-1120).
967
1007
  */
968
1008
  async request(config) {
969
1009
  const mergedConfig = this.mergeRequestConfig(config);
1010
+ if (presentKey(headerValue(mergedConfig.headers, API_KEY_HEADER)) === void 0) {
1011
+ throw new MissingApiKeyError(
1012
+ "`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.",
1013
+ { url: mergedConfig.url, method: mergedConfig.method, sent: false }
1014
+ );
1015
+ }
970
1016
  this.log("debug", "Making request", {
971
1017
  method: mergedConfig.method,
972
1018
  url: mergedConfig.url
@@ -995,7 +1041,7 @@ var _HttpClient = class _HttpClient {
995
1041
  if (this.retryPolicy.shouldRetry(attempt, failure, mergedConfig)) {
996
1042
  const delay = this.retryPolicy.delayFor(attempt, failure);
997
1043
  this.log("debug", "Retrying request", { attempt, delay });
998
- await this.sleep(delay, config.signal);
1044
+ await sleep(delay, config.signal);
999
1045
  continue;
1000
1046
  }
1001
1047
  const sdkError = ErrorMapper.mapGenericError(failure, {
@@ -1124,8 +1170,12 @@ var _HttpClient = class _HttpClient {
1124
1170
  headers: filteredHeaders,
1125
1171
  body: this.prepareRequestBody(config),
1126
1172
  signal: controller.signal,
1127
- // Include credentials for CORS requests if in browser and configured
1128
- ...typeof window !== "undefined" && this.config.browser.includeCredentials != null && { credentials: "include" }
1173
+ // Only where `window` exists. A server or edge runtime has no cookie jar for the mode to
1174
+ // govern, so the field is left off and the runtime's default stands. A Web Worker gets
1175
+ // that default too: it has cookies but no `window`.
1176
+ ...typeof window !== "undefined" && {
1177
+ credentials: credentialsModeFor(this.config.browser.includeCredentials)
1178
+ }
1129
1179
  };
1130
1180
  const send = this.config.fetch ?? globalThis.fetch;
1131
1181
  const response = await send(url, requestInit);
@@ -1281,22 +1331,6 @@ var _HttpClient = class _HttpClient {
1281
1331
  });
1282
1332
  return obj;
1283
1333
  }
1284
- /** A backoff the caller's abort cuts short; the next attempt then refuses to send. */
1285
- sleep(ms, signal) {
1286
- return new Promise((resolve) => {
1287
- if (isAborted(signal)) {
1288
- resolve();
1289
- return;
1290
- }
1291
- const done = /* @__PURE__ */ __name(() => {
1292
- clearTimeout(timer);
1293
- signal?.removeEventListener("abort", done);
1294
- resolve();
1295
- }, "done");
1296
- const timer = setTimeout(done, ms);
1297
- signal?.addEventListener("abort", done, { once: true });
1298
- });
1299
- }
1300
1334
  log(level, message, data) {
1301
1335
  if (!this.debug) return;
1302
1336
  if (this.logger != null) {
@@ -1308,10 +1342,23 @@ var _HttpClient = class _HttpClient {
1308
1342
  };
1309
1343
  __name(_HttpClient, "HttpClient");
1310
1344
  var HttpClient = _HttpClient;
1311
- function isAborted(signal) {
1312
- return signal?.aborted === true;
1345
+ var API_KEY_HEADER = "X-API-Key";
1346
+ function presentKey(value) {
1347
+ return typeof value === "string" && value.trim() !== "" ? value : void 0;
1313
1348
  }
1314
- __name(isAborted, "isAborted");
1349
+ __name(presentKey, "presentKey");
1350
+ function credentialsModeFor(includeCredentials) {
1351
+ switch (includeCredentials) {
1352
+ case false: {
1353
+ return "same-origin";
1354
+ }
1355
+ case true:
1356
+ case void 0: {
1357
+ return "include";
1358
+ }
1359
+ }
1360
+ }
1361
+ __name(credentialsModeFor, "credentialsModeFor");
1315
1362
 
1316
1363
  // src/content/query-builder.ts
1317
1364
  var _ContentQueryBuilder = class _ContentQueryBuilder {
@@ -1327,6 +1374,16 @@ var _ContentQueryBuilder = class _ContentQueryBuilder {
1327
1374
  __publicField(this, "childrenOptions");
1328
1375
  __publicField(this, "pageNumber");
1329
1376
  __publicField(this, "pageSizeValue");
1377
+ __publicField(this, "requestHeaders");
1378
+ }
1379
+ /**
1380
+ * Headers this query adds to the client's own, e.g. `X-RelyIO-Dataset` to list another dataset
1381
+ * the key can see. A name the client already sends is replaced for this query only; the client
1382
+ * is not changed.
1383
+ */
1384
+ headers(headers) {
1385
+ this.requestHeaders = headers;
1386
+ return this;
1330
1387
  }
1331
1388
  /**
1332
1389
  * Add where conditions to the query
@@ -1524,7 +1581,11 @@ var _ContentQueryBuilder = class _ContentQueryBuilder {
1524
1581
  const response = await this.httpClient.send(
1525
1582
  "/v1/{contentType}",
1526
1583
  "get",
1527
- { path: { contentType: this.contentType }, query: this.buildQueryParams() }
1584
+ {
1585
+ path: { contentType: this.contentType },
1586
+ query: this.buildQueryParams(),
1587
+ headers: this.requestHeaders
1588
+ }
1528
1589
  );
1529
1590
  return response.body;
1530
1591
  }
@@ -1592,6 +1653,7 @@ var _ContentQueryBuilder = class _ContentQueryBuilder {
1592
1653
  cloned.fieldsProjection = this.fieldsProjection ? [...this.fieldsProjection] : void 0;
1593
1654
  cloned.pageNumber = this.pageNumber;
1594
1655
  cloned.pageSizeValue = this.pageSizeValue;
1656
+ cloned.requestHeaders = this.requestHeaders ? { ...this.requestHeaders } : void 0;
1595
1657
  return cloned;
1596
1658
  }
1597
1659
  /**
@@ -1699,6 +1761,7 @@ var _ContentByIdBuilder = class _ContentByIdBuilder {
1699
1761
  __publicField(this, "localesArray");
1700
1762
  __publicField(this, "fetchAllLocales", false);
1701
1763
  __publicField(this, "childrenOptions");
1764
+ __publicField(this, "requestHeaders");
1702
1765
  }
1703
1766
  /**
1704
1767
  * Include related content in the response
@@ -1729,6 +1792,15 @@ var _ContentByIdBuilder = class _ContentByIdBuilder {
1729
1792
  this.fetchAllLocales = false;
1730
1793
  return this;
1731
1794
  }
1795
+ /**
1796
+ * Headers this one read adds to the client's own, e.g. `X-RelyIO-Dataset` to read another dataset
1797
+ * the key can see. A name the client already sends is replaced for this request only; the client
1798
+ * is not changed.
1799
+ */
1800
+ headers(headers) {
1801
+ this.requestHeaders = headers;
1802
+ return this;
1803
+ }
1732
1804
  /**
1733
1805
  * Fetch content in all available locales
1734
1806
  * Returns default locale at top level, others in _metadata.localizations
@@ -1782,7 +1854,11 @@ var _ContentByIdBuilder = class _ContentByIdBuilder {
1782
1854
  return await this.httpClient.fetchPayload(
1783
1855
  "/v1/{contentType}/{id}",
1784
1856
  "get",
1785
- { path: { contentType: this.contentType, id: this.id }, query: params }
1857
+ {
1858
+ path: { contentType: this.contentType, id: this.id },
1859
+ query: params,
1860
+ headers: this.requestHeaders
1861
+ }
1786
1862
  );
1787
1863
  } catch (error) {
1788
1864
  if (isResourceNotFound(error)) return null;
@@ -1812,6 +1888,14 @@ function writeHeaders(options) {
1812
1888
  return Object.keys(headers).length > 0 ? headers : void 0;
1813
1889
  }
1814
1890
  __name(writeHeaders, "writeHeaders");
1891
+ function requestHeaders(headers, typed) {
1892
+ if (headers === void 0) return typed;
1893
+ if (typed === void 0) return headers;
1894
+ const typedNames = new Set(Object.keys(typed).map((name) => name.toLowerCase()));
1895
+ const own = Object.entries(headers).filter(([name]) => !typedNames.has(name.toLowerCase()));
1896
+ return { ...Object.fromEntries(own), ...typed };
1897
+ }
1898
+ __name(requestHeaders, "requestHeaders");
1815
1899
  function entityTagOf(response) {
1816
1900
  return headerValue(response.headers, HEADER_ENTITY_TAG.name);
1817
1901
  }
@@ -1881,7 +1965,8 @@ var _ContentClient = class _ContentClient {
1881
1965
  /**
1882
1966
  * Fetch a single content item by ID
1883
1967
  * @param id - Entry ID or slug
1884
- * @param options - Fetch options (include, locale)
1968
+ * @param options - Fetch options (include, locale, and `headers` this one request adds — e.g.
1969
+ * `{ 'X-RelyIO-Dataset': 'staging' }` to read a dataset other than the client's)
1885
1970
  */
1886
1971
  async fetchOne(id, options) {
1887
1972
  const builder = this.findById(id);
@@ -1891,6 +1976,9 @@ var _ContentClient = class _ContentClient {
1891
1976
  if (options?.locale != null && options.locale !== "") {
1892
1977
  builder.locale(options.locale);
1893
1978
  }
1979
+ if (options?.headers != null) {
1980
+ builder.headers(options.headers);
1981
+ }
1894
1982
  return builder.execute();
1895
1983
  }
1896
1984
  /**
@@ -1905,6 +1993,11 @@ var _ContentClient = class _ContentClient {
1905
1993
  * `entityTag` is `undefined` when the response came from the cache: a cached
1906
1994
  * body cannot know the current version, and an absent tag beats a stale one
1907
1995
  * that would refuse the next write.
1996
+ *
1997
+ * Unlike {@link fetchOne}, a missing entry is not `null`: the service's
1998
+ * `RESOURCE_NOT_FOUND` is thrown as it arrived, with the `kind` that says
1999
+ * whether the entry, the content type or the schema was missing. `headers`
2000
+ * are the ones this read adds, as for `fetchOne`.
1908
2001
  */
1909
2002
  async fetchOneWithTag(id, options) {
1910
2003
  const query = {};
@@ -1914,7 +2007,7 @@ var _ContentClient = class _ContentClient {
1914
2007
  const response = await this.httpClient.send(
1915
2008
  "/v1/{contentType}/{id}",
1916
2009
  "get",
1917
- { path: { contentType: this.contentType, id }, query }
2010
+ { path: { contentType: this.contentType, id }, query, headers: options?.headers }
1918
2011
  );
1919
2012
  return { entry: readPayload(response.body), entityTag: entityTagOf(response) };
1920
2013
  }
@@ -1936,17 +2029,20 @@ var _ContentClient = class _ContentClient {
1936
2029
  /**
1937
2030
  * Create a new content item
1938
2031
  * @param data - Content data
1939
- * @param options - Create options (slug, locale, parentId, sortOrder)
2032
+ * @param options - Create options (slug, locale, parentId, sortOrder), and `headers` this one
2033
+ * request adds — e.g. `{ 'X-RelyIO-Dataset': 'staging' }` to write a dataset other than the
2034
+ * client's. They are sent, never put in the body; `idempotencyKey` still wins over an
2035
+ * `Idempotency-Key` among them.
1940
2036
  */
1941
2037
  async create(data, options) {
1942
- const { idempotencyKey, ...body } = options ?? {};
2038
+ const { idempotencyKey, headers, ...body } = options ?? {};
1943
2039
  return this.httpClient.fetchPayload(
1944
2040
  "/v1/{contentType}",
1945
2041
  "post",
1946
2042
  {
1947
2043
  path: { contentType: this.contentType },
1948
2044
  body: { content: data, ...body },
1949
- headers: writeHeaders({ idempotencyKey })
2045
+ headers: requestHeaders(headers, writeHeaders({ idempotencyKey }))
1950
2046
  }
1951
2047
  );
1952
2048
  }
@@ -1954,17 +2050,19 @@ var _ContentClient = class _ContentClient {
1954
2050
  * Update a content item by ID (partial update)
1955
2051
  * @param id - Entry ID
1956
2052
  * @param data - Fields to update
1957
- * @param options - Update options (slug, locale, parentId, sortOrder)
2053
+ * @param options - Update options (slug, locale, parentId, sortOrder), and `headers` this one
2054
+ * request adds, as for {@link create}. `ifMatch` and `idempotencyKey` win over the same
2055
+ * header among them.
1958
2056
  */
1959
2057
  async update(id, data, options) {
1960
- const { idempotencyKey, ifMatch, ...body } = options ?? {};
2058
+ const { idempotencyKey, ifMatch, headers, ...body } = options ?? {};
1961
2059
  return this.httpClient.fetchPayload(
1962
2060
  "/v1/{contentType}/{id}",
1963
2061
  "patch",
1964
2062
  {
1965
2063
  path: { contentType: this.contentType, id },
1966
2064
  body: { content: data, ...body },
1967
- headers: writeHeaders({ idempotencyKey, ifMatch })
2065
+ headers: requestHeaders(headers, writeHeaders({ idempotencyKey, ifMatch }))
1968
2066
  }
1969
2067
  );
1970
2068
  }
@@ -2059,8 +2157,9 @@ var _ContentClient = class _ContentClient {
2059
2157
  if (options?.include != null) {
2060
2158
  params.include = options.include.join(",");
2061
2159
  }
2062
- if (options?.locale != null && options.locale !== "") {
2063
- params.locale = options.locale;
2160
+ const locale = options?.locale ?? this.defaultLocale;
2161
+ if (locale != null && locale !== "") {
2162
+ params.locale = locale;
2064
2163
  }
2065
2164
  return this.httpClient.fetchPayload(
2066
2165
  "/v1/{contentType}/{id}/children",
@@ -2718,9 +2817,10 @@ __name(isAssetEntry, "isAssetEntry");
2718
2817
 
2719
2818
  // src/content/singleton-client.ts
2720
2819
  var _SingletonClient = class _SingletonClient {
2721
- constructor(httpClient, singletonKey) {
2820
+ constructor(httpClient, singletonKey, defaultLocale) {
2722
2821
  this.httpClient = httpClient;
2723
2822
  this.singletonKey = singletonKey;
2823
+ this.defaultLocale = defaultLocale;
2724
2824
  __publicField(this, "includeFields");
2725
2825
  __publicField(this, "localeValue");
2726
2826
  }
@@ -2745,11 +2845,6 @@ var _SingletonClient = class _SingletonClient {
2745
2845
  * @param options - Optional fetch options (alternative to fluent methods)
2746
2846
  */
2747
2847
  async fetch(options) {
2748
- const includeValue = options?.include ?? this.includeFields;
2749
- options?.locale ?? this.localeValue;
2750
- if (includeValue) {
2751
- includeValue.join(",");
2752
- }
2753
2848
  try {
2754
2849
  return (await this.fetchWithTag(options)).content;
2755
2850
  } catch (error) {
@@ -2767,7 +2862,7 @@ var _SingletonClient = class _SingletonClient {
2767
2862
  async fetchWithTag(options) {
2768
2863
  const query = {};
2769
2864
  const includeValue = options?.include ?? this.includeFields;
2770
- const localeValueFinal = options?.locale ?? this.localeValue;
2865
+ const localeValueFinal = options?.locale ?? this.localeValue ?? this.defaultLocale;
2771
2866
  if (includeValue) query.include = includeValue.join(",");
2772
2867
  if (localeValueFinal != null && localeValueFinal !== "") query.locale = localeValueFinal;
2773
2868
  const response = await this.httpClient.send(
@@ -2864,9 +2959,10 @@ var SingletonClient = _SingletonClient;
2864
2959
 
2865
2960
  // src/content/settings-client.ts
2866
2961
  var _SettingsClient = class _SettingsClient {
2867
- constructor(httpClient, groupId) {
2962
+ constructor(httpClient, groupId, defaultLocale) {
2868
2963
  this.httpClient = httpClient;
2869
2964
  this.groupId = groupId;
2965
+ this.defaultLocale = defaultLocale;
2870
2966
  __publicField(this, "includeFields");
2871
2967
  __publicField(this, "localeValue");
2872
2968
  }
@@ -2891,11 +2987,6 @@ var _SettingsClient = class _SettingsClient {
2891
2987
  * @param options - Optional fetch options (alternative to fluent methods)
2892
2988
  */
2893
2989
  async fetch(options) {
2894
- const includeValue = options?.include ?? this.includeFields;
2895
- options?.locale ?? this.localeValue;
2896
- if (includeValue) {
2897
- includeValue.join(",");
2898
- }
2899
2990
  try {
2900
2991
  return (await this.fetchWithTag(options)).settings;
2901
2992
  } catch (error) {
@@ -2925,7 +3016,7 @@ var _SettingsClient = class _SettingsClient {
2925
3016
  async fetchWithTag(options) {
2926
3017
  const query = {};
2927
3018
  const includeValue = options?.include ?? this.includeFields;
2928
- const localeValueFinal = options?.locale ?? this.localeValue;
3019
+ const localeValueFinal = options?.locale ?? this.localeValue ?? this.defaultLocale;
2929
3020
  if (includeValue) query.include = includeValue.join(",");
2930
3021
  if (localeValueFinal != null && localeValueFinal !== "") query.locale = localeValueFinal;
2931
3022
  const response = await this.httpClient.send(
@@ -3027,14 +3118,28 @@ var SettingsClient = _SettingsClient;
3027
3118
 
3028
3119
  // src/search/search-client.ts
3029
3120
  var _SearchBuilder = class _SearchBuilder {
3030
- constructor(httpClient, query) {
3121
+ /**
3122
+ * @param defaultLocale - The client's locale (`withLocale()`), sent when `.locale()` names none
3123
+ */
3124
+ constructor(httpClient, query, defaultLocale) {
3031
3125
  this.httpClient = httpClient;
3126
+ this.defaultLocale = defaultLocale;
3032
3127
  __publicField(this, "query");
3033
3128
  __publicField(this, "searchOptions", {});
3129
+ __publicField(this, "requestHeaders");
3034
3130
  this.query = query;
3035
3131
  }
3036
3132
  /**
3037
- * Search within a single content type
3133
+ * Headers this search adds to the client's own, e.g. `X-RelyIO-Dataset` to search another
3134
+ * dataset the key can see. A name the client already sends is replaced for this search only.
3135
+ */
3136
+ headers(headers) {
3137
+ this.requestHeaders = headers;
3138
+ return this;
3139
+ }
3140
+ /**
3141
+ * Search within a single content type. Where a schema types the client, the name is one of its
3142
+ * content types and the results are that type's entries.
3038
3143
  * @param contentType - Content type to search in
3039
3144
  */
3040
3145
  in(contentType) {
@@ -3043,7 +3148,8 @@ var _SearchBuilder = class _SearchBuilder {
3043
3148
  return this;
3044
3149
  }
3045
3150
  /**
3046
- * Search across multiple content types
3151
+ * Search across multiple content types. Where a schema types the client, the names are its content
3152
+ * types and the results are a union of their entries — every type's, for `'*'`.
3047
3153
  * @param contentTypes - Array of content types or '*' for all
3048
3154
  */
3049
3155
  across(contentTypes) {
@@ -3078,6 +3184,16 @@ var _SearchBuilder = class _SearchBuilder {
3078
3184
  this.searchOptions.fields = projection;
3079
3185
  return this;
3080
3186
  }
3187
+ /**
3188
+ * Order the results by a field instead of by relevance. Called again, it adds a tie-breaker.
3189
+ * `_score` names relevance itself.
3190
+ * @param field - Field path to sort by (e.g. `updatedAt`, `pricing.basePrice`)
3191
+ * @param direction - `asc` or `desc` (the route's default is `desc`)
3192
+ */
3193
+ sort(field, direction = "desc") {
3194
+ this.searchOptions.sort = [...this.searchOptions.sort ?? [], { field, direction }];
3195
+ return this;
3196
+ }
3081
3197
  /**
3082
3198
  * Limit number of results
3083
3199
  * @param count - Maximum results
@@ -3138,6 +3254,9 @@ var _SearchBuilder = class _SearchBuilder {
3138
3254
  if (this.searchOptions.fields) {
3139
3255
  body.fields = this.searchOptions.fields;
3140
3256
  }
3257
+ if (this.searchOptions.sort) {
3258
+ body.sort = this.searchOptions.sort;
3259
+ }
3141
3260
  if (this.searchOptions.limit !== void 0) {
3142
3261
  body.limit = this.searchOptions.limit;
3143
3262
  }
@@ -3150,13 +3269,14 @@ var _SearchBuilder = class _SearchBuilder {
3150
3269
  if (this.searchOptions.explain === true) {
3151
3270
  body.explain = true;
3152
3271
  }
3153
- if (this.searchOptions.locale != null && this.searchOptions.locale !== "") {
3154
- body.locale = this.searchOptions.locale;
3272
+ const locale = this.searchOptions.locale ?? this.defaultLocale;
3273
+ if (locale != null && locale !== "") {
3274
+ body.locale = locale;
3155
3275
  }
3156
3276
  const response = await this.httpClient.send(
3157
3277
  "/v1/search",
3158
3278
  "post",
3159
- { body }
3279
+ { body, headers: this.requestHeaders }
3160
3280
  );
3161
3281
  return response.body;
3162
3282
  }
@@ -3176,8 +3296,12 @@ var _SearchBuilder = class _SearchBuilder {
3176
3296
  __name(_SearchBuilder, "SearchBuilder");
3177
3297
  var SearchBuilder = _SearchBuilder;
3178
3298
  var _AutocompleteBuilder = class _AutocompleteBuilder {
3179
- constructor(httpClient, query) {
3299
+ /**
3300
+ * @param defaultLocale - The client's locale (`withLocale()`), sent when `.locale()` names none
3301
+ */
3302
+ constructor(httpClient, query, defaultLocale) {
3180
3303
  this.httpClient = httpClient;
3304
+ this.defaultLocale = defaultLocale;
3181
3305
  __publicField(this, "query");
3182
3306
  __publicField(this, "autocompleteOptions", {});
3183
3307
  this.query = query;
@@ -3256,8 +3380,9 @@ var _AutocompleteBuilder = class _AutocompleteBuilder {
3256
3380
  if (this.autocompleteOptions.limit !== void 0) {
3257
3381
  body.limit = this.autocompleteOptions.limit;
3258
3382
  }
3259
- if (this.autocompleteOptions.locale != null && this.autocompleteOptions.locale !== "") {
3260
- body.locale = this.autocompleteOptions.locale;
3383
+ const locale = this.autocompleteOptions.locale ?? this.defaultLocale;
3384
+ if (locale != null && locale !== "") {
3385
+ body.locale = locale;
3261
3386
  }
3262
3387
  const response = await this.httpClient.send("/v1/search/autocomplete", "post", { body });
3263
3388
  return response.body;
@@ -3272,18 +3397,41 @@ var _AutocompleteBuilder = class _AutocompleteBuilder {
3272
3397
  __name(_AutocompleteBuilder, "AutocompleteBuilder");
3273
3398
  var AutocompleteBuilder = _AutocompleteBuilder;
3274
3399
  var _SimilarBuilder = class _SimilarBuilder {
3275
- constructor(httpClient, sourceId) {
3400
+ /**
3401
+ * @param defaultLocale - The client's locale (`withLocale()`), sent when `.locale()` names none
3402
+ */
3403
+ constructor(httpClient, sourceId, defaultLocale) {
3276
3404
  this.httpClient = httpClient;
3405
+ this.defaultLocale = defaultLocale;
3277
3406
  __publicField(this, "sourceId");
3278
3407
  __publicField(this, "similarOptions", {});
3408
+ __publicField(this, "requestHeaders");
3279
3409
  this.sourceId = sourceId;
3280
3410
  }
3411
+ /**
3412
+ * Headers this read adds to the client's own, e.g. `X-RelyIO-Dataset` to read another dataset
3413
+ * the key can see. A name the client already sends is replaced for this read only.
3414
+ */
3415
+ headers(headers) {
3416
+ this.requestHeaders = headers;
3417
+ return this;
3418
+ }
3281
3419
  /**
3282
3420
  * Find similar content within a content type
3283
3421
  * @param contentType - Content type to search
3284
3422
  */
3285
3423
  in(contentType) {
3286
3424
  this.similarOptions.contentType = contentType;
3425
+ this.similarOptions.contentTypes = void 0;
3426
+ return this;
3427
+ }
3428
+ /**
3429
+ * Find similar content across several content types, or `'*'` for every type
3430
+ * @param contentTypes - Array of content types or '*' for all
3431
+ */
3432
+ across(contentTypes) {
3433
+ this.similarOptions.contentTypes = contentTypes;
3434
+ this.similarOptions.contentType = void 0;
3287
3435
  return this;
3288
3436
  }
3289
3437
  /**
@@ -3317,26 +3465,33 @@ var _SimilarBuilder = class _SimilarBuilder {
3317
3465
  * Execute the similar query
3318
3466
  */
3319
3467
  async execute() {
3320
- if (this.similarOptions.contentType == null || this.similarOptions.contentType === "") {
3321
- throw new Error('Content type is required for similar search. Use .in("contentType")');
3468
+ const { contentType, contentTypes } = this.similarOptions;
3469
+ if ((contentType == null || contentType === "") && contentTypes == null) {
3470
+ throw new Error(
3471
+ 'Content type is required for similar search. Use .in("contentType") or .across("*")'
3472
+ );
3473
+ }
3474
+ const body = { source: this.sourceId };
3475
+ if (contentType != null && contentType !== "") {
3476
+ body.contentType = contentType;
3477
+ }
3478
+ if (contentTypes != null) {
3479
+ body.contentTypes = contentTypes;
3322
3480
  }
3323
- const body = {
3324
- source: this.sourceId,
3325
- contentType: this.similarOptions.contentType
3326
- };
3327
3481
  if (this.similarOptions.where && Object.keys(this.similarOptions.where).length > 0) {
3328
3482
  body.where = this.similarOptions.where;
3329
3483
  }
3330
3484
  if (this.similarOptions.limit !== void 0) {
3331
3485
  body.limit = this.similarOptions.limit;
3332
3486
  }
3333
- if (this.similarOptions.locale != null && this.similarOptions.locale !== "") {
3334
- body.locale = this.similarOptions.locale;
3487
+ const locale = this.similarOptions.locale ?? this.defaultLocale;
3488
+ if (locale != null && locale !== "") {
3489
+ body.locale = locale;
3335
3490
  }
3336
3491
  const response = await this.httpClient.send(
3337
3492
  "/v1/search/similar",
3338
3493
  "post",
3339
- { body }
3494
+ { body, headers: this.requestHeaders }
3340
3495
  );
3341
3496
  return response.body;
3342
3497
  }
@@ -3356,29 +3511,34 @@ var _SimilarBuilder = class _SimilarBuilder {
3356
3511
  __name(_SimilarBuilder, "SimilarBuilder");
3357
3512
  var SimilarBuilder = _SimilarBuilder;
3358
3513
  var _SearchClient = class _SearchClient {
3359
- constructor(httpClient) {
3514
+ /**
3515
+ * @param defaultLocale - The client's locale (`withLocale()`): every builder here sends it when
3516
+ * the query names none, as `content()`'s reads do
3517
+ */
3518
+ constructor(httpClient, defaultLocale) {
3360
3519
  this.httpClient = httpClient;
3520
+ this.defaultLocale = defaultLocale;
3361
3521
  }
3362
3522
  /**
3363
3523
  * Start a search query
3364
3524
  * @param query - Search query string
3365
3525
  */
3366
3526
  search(query) {
3367
- return new SearchBuilder(this.httpClient, query);
3527
+ return new SearchBuilder(this.httpClient, query, this.defaultLocale);
3368
3528
  }
3369
3529
  /**
3370
3530
  * Start an autocomplete query
3371
3531
  * @param query - Partial query to autocomplete
3372
3532
  */
3373
3533
  autocomplete(query) {
3374
- return new AutocompleteBuilder(this.httpClient, query);
3534
+ return new AutocompleteBuilder(this.httpClient, query, this.defaultLocale);
3375
3535
  }
3376
3536
  /**
3377
3537
  * Find content similar to a given entry
3378
3538
  * @param sourceId - Entry ID to find similar content for
3379
3539
  */
3380
3540
  similar(sourceId) {
3381
- return new SimilarBuilder(this.httpClient, sourceId);
3541
+ return new SimilarBuilder(this.httpClient, sourceId, this.defaultLocale);
3382
3542
  }
3383
3543
  /**
3384
3544
  * Quick search helper - searches all content types
@@ -3403,6 +3563,7 @@ var _BatchBuilder = class _BatchBuilder {
3403
3563
  returnData: true,
3404
3564
  continueOnError: true
3405
3565
  });
3566
+ __publicField(this, "requestHeaders");
3406
3567
  }
3407
3568
  // ==========================================================================
3408
3569
  // ENTRY OPERATIONS
@@ -3604,6 +3765,15 @@ var _BatchBuilder = class _BatchBuilder {
3604
3765
  this.defaultOptions.continueOnError = false;
3605
3766
  return this;
3606
3767
  }
3768
+ /**
3769
+ * Headers the batch request adds — e.g. `{ 'X-RelyIO-Dataset': 'staging' }` to write a dataset
3770
+ * other than the client's. They replace the client's own header of the same name for this
3771
+ * request alone, and apply to every operation in it: a batch is one request.
3772
+ */
3773
+ headers(headers) {
3774
+ this.requestHeaders = { ...this.requestHeaders, ...headers };
3775
+ return this;
3776
+ }
3607
3777
  // ==========================================================================
3608
3778
  // EXECUTION
3609
3779
  // ==========================================================================
@@ -3633,6 +3803,7 @@ var _BatchBuilder = class _BatchBuilder {
3633
3803
  const cloned = new _BatchBuilder(this.httpClient);
3634
3804
  cloned.operations = [...this.operations];
3635
3805
  cloned.defaultOptions = { ...this.defaultOptions };
3806
+ cloned.requestHeaders = this.requestHeaders === void 0 ? void 0 : { ...this.requestHeaders };
3636
3807
  return cloned;
3637
3808
  }
3638
3809
  /**
@@ -3650,7 +3821,8 @@ var _BatchBuilder = class _BatchBuilder {
3650
3821
  }
3651
3822
  const finalOptions = { ...this.defaultOptions, ...options };
3652
3823
  return this.httpClient.fetchPayload("/v1/batch", "post", {
3653
- body: { operations: this.operations, options: finalOptions }
3824
+ body: { operations: this.operations, options: finalOptions },
3825
+ headers: this.requestHeaders
3654
3826
  });
3655
3827
  }
3656
3828
  /**
@@ -3730,9 +3902,21 @@ __name(_BatchClient, "BatchClient");
3730
3902
  var BatchClient = _BatchClient;
3731
3903
 
3732
3904
  // src/queries/adhoc-query-client.ts
3905
+ function withClientLocale(parameters, clientLocale) {
3906
+ if (clientLocale === void 0 || clientLocale === "" || parameters?.locale != null) {
3907
+ return parameters;
3908
+ }
3909
+ return { ...parameters, locale: clientLocale };
3910
+ }
3911
+ __name(withClientLocale, "withClientLocale");
3733
3912
  var _AdhocQueryBuilder = class _AdhocQueryBuilder {
3734
- constructor(httpClient) {
3913
+ /**
3914
+ * @param defaultLocale - The client's locale (`withLocale()`), sent as the `locale` parameter
3915
+ * when `.param('locale', …)` binds none
3916
+ */
3917
+ constructor(httpClient, defaultLocale) {
3735
3918
  this.httpClient = httpClient;
3919
+ this.defaultLocale = defaultLocale;
3736
3920
  __publicField(this, "queryDef", {});
3737
3921
  __publicField(this, "parameters", {});
3738
3922
  __publicField(this, "options", {});
@@ -3972,8 +4156,9 @@ var _AdhocQueryBuilder = class _AdhocQueryBuilder {
3972
4156
  const body = {
3973
4157
  query: this.queryDef
3974
4158
  };
3975
- if (Object.keys(this.parameters).length > 0) {
3976
- body.parameters = this.parameters;
4159
+ const parameters = withClientLocale(this.parameters, this.defaultLocale);
4160
+ if (parameters !== void 0 && Object.keys(parameters).length > 0) {
4161
+ body.parameters = parameters;
3977
4162
  }
3978
4163
  if (Object.keys(this.options).length > 0) {
3979
4164
  body.options = this.options;
@@ -4004,7 +4189,7 @@ var _AdhocQueryBuilder = class _AdhocQueryBuilder {
4004
4189
  * Clone the builder
4005
4190
  */
4006
4191
  clone() {
4007
- const cloned = new _AdhocQueryBuilder(this.httpClient);
4192
+ const cloned = new _AdhocQueryBuilder(this.httpClient, this.defaultLocale);
4008
4193
  cloned.queryDef = JSON.parse(JSON.stringify(this.queryDef));
4009
4194
  cloned.parameters = { ...this.parameters };
4010
4195
  cloned.options = { ...this.options };
@@ -4014,27 +4199,36 @@ var _AdhocQueryBuilder = class _AdhocQueryBuilder {
4014
4199
  __name(_AdhocQueryBuilder, "AdhocQueryBuilder");
4015
4200
  var AdhocQueryBuilder = _AdhocQueryBuilder;
4016
4201
  var _AdhocQueryClient = class _AdhocQueryClient {
4017
- constructor(httpClient) {
4202
+ /**
4203
+ * @param defaultLocale - The client's locale (`withLocale()`), sent as the `locale` parameter of
4204
+ * every query that binds none
4205
+ */
4206
+ constructor(httpClient, defaultLocale) {
4018
4207
  this.httpClient = httpClient;
4208
+ this.defaultLocale = defaultLocale;
4019
4209
  }
4020
4210
  /**
4021
4211
  * Create a new query builder
4022
4212
  */
4023
4213
  build() {
4024
- return new AdhocQueryBuilder(this.httpClient);
4214
+ return new AdhocQueryBuilder(this.httpClient, this.defaultLocale);
4025
4215
  }
4026
4216
  /**
4027
4217
  * Execute a query definition directly
4028
4218
  * @param definition - Query definition
4029
4219
  * @param parameters - Parameter values
4030
4220
  * @param options - Execution options
4221
+ * @param request - `headers` this one request adds to the client's own, e.g. `X-RelyIO-Dataset`
4222
+ * to query another dataset the key can see. A name the client already sends is replaced for
4223
+ * this request only.
4031
4224
  */
4032
- async execute(definition, parameters, options) {
4225
+ async execute(definition, parameters, options, request) {
4033
4226
  const body = {
4034
4227
  query: definition
4035
4228
  };
4036
- if (parameters && Object.keys(parameters).length > 0) {
4037
- body.parameters = parameters;
4229
+ const sent = withClientLocale(parameters, this.defaultLocale);
4230
+ if (sent !== void 0 && Object.keys(sent).length > 0) {
4231
+ body.parameters = sent;
4038
4232
  }
4039
4233
  if (options && Object.keys(options).length > 0) {
4040
4234
  body.options = options;
@@ -4042,7 +4236,7 @@ var _AdhocQueryClient = class _AdhocQueryClient {
4042
4236
  return this.httpClient.fetchPayload(
4043
4237
  "/v1/query",
4044
4238
  "post",
4045
- { body }
4239
+ { body, headers: request?.headers }
4046
4240
  );
4047
4241
  }
4048
4242
  /**
@@ -4106,12 +4300,12 @@ async function* pollChanges(http, options = {}) {
4106
4300
  let since = options.since;
4107
4301
  let resynced;
4108
4302
  for (; ; ) {
4109
- if (aborted(signal)) return;
4303
+ if (isAborted(signal)) return;
4110
4304
  let response;
4111
4305
  try {
4112
4306
  response = await pollOnce(http, since, options);
4113
4307
  } catch (error) {
4114
- if (aborted(signal)) return;
4308
+ if (isAborted(signal)) return;
4115
4309
  if (!(error instanceof ResourceExpiredError) || since === void 0) throw error;
4116
4310
  const expired = ChangesExpiredError.of(error);
4117
4311
  const onExpired = options.onExpired ?? "throw";
@@ -4129,11 +4323,11 @@ async function* pollChanges(http, options = {}) {
4129
4323
  const expired = resynced;
4130
4324
  resynced = void 0;
4131
4325
  await options.onExpired(expired);
4132
- if (aborted(signal)) return;
4326
+ if (isAborted(signal)) return;
4133
4327
  }
4134
4328
  for (const change of data) {
4135
4329
  yield change;
4136
- if (aborted(signal)) return;
4330
+ if (isAborted(signal)) return;
4137
4331
  }
4138
4332
  if (data.length === 0) {
4139
4333
  const wait = parseRetryAfterMs(headerValue(response.headers, "Retry-After"), Date.now());
@@ -4142,6 +4336,21 @@ async function* pollChanges(http, options = {}) {
4142
4336
  }
4143
4337
  }
4144
4338
  __name(pollChanges, "pollChanges");
4339
+ async function readChangesHead(http, options = {}) {
4340
+ const response = await http.send("/v1/changes", "get", {
4341
+ query: {},
4342
+ ...options.signal === void 0 ? {} : { signal: options.signal }
4343
+ });
4344
+ const { data, page } = response.body;
4345
+ if (page.nextCursor === null) {
4346
+ throw new Error("GET /v1/changes answered without page.nextCursor");
4347
+ }
4348
+ if (data.length > 0) {
4349
+ throw new Error("GET /v1/changes without since answered changes; a head carries none");
4350
+ }
4351
+ return page.nextCursor;
4352
+ }
4353
+ __name(readChangesHead, "readChangesHead");
4145
4354
  async function pollOnce(http, since, options) {
4146
4355
  const query = {};
4147
4356
  if (since !== void 0) query.since = since;
@@ -4155,26 +4364,6 @@ async function pollOnce(http, since, options) {
4155
4364
  });
4156
4365
  }
4157
4366
  __name(pollOnce, "pollOnce");
4158
- function sleep(ms, signal) {
4159
- return new Promise((resolve) => {
4160
- if (aborted(signal)) {
4161
- resolve();
4162
- return;
4163
- }
4164
- const done = /* @__PURE__ */ __name(() => {
4165
- clearTimeout(timer);
4166
- signal?.removeEventListener("abort", done);
4167
- resolve();
4168
- }, "done");
4169
- const timer = setTimeout(done, ms);
4170
- signal?.addEventListener("abort", done, { once: true });
4171
- });
4172
- }
4173
- __name(sleep, "sleep");
4174
- function aborted(signal) {
4175
- return signal?.aborted === true;
4176
- }
4177
- __name(aborted, "aborted");
4178
4367
 
4179
4368
  // src/config/dev-detection.ts
4180
4369
  function sandboxStateOf(resolution) {
@@ -4312,10 +4501,7 @@ var DEFAULT_CONFIG = {
4312
4501
  }
4313
4502
  };
4314
4503
  function validateConfig(config) {
4315
- if (!config.apiKey || config.apiKey.length === 0) {
4316
- throw new Error("API key is required");
4317
- }
4318
- if (typeof config.apiKey !== "string") {
4504
+ if (config.apiKey != null && typeof config.apiKey !== "string") {
4319
4505
  throw new Error("API key must be a string");
4320
4506
  }
4321
4507
  if (config.baseUrl != null && typeof config.baseUrl !== "string") {
@@ -4398,9 +4584,9 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4398
4584
  this.config = resolved.config;
4399
4585
  this._sandbox = resolved.sandbox;
4400
4586
  this.httpClient = new HttpClient(this.config);
4401
- this._search = new SearchClient(this.httpClient);
4587
+ this._search = new SearchClient(this.httpClient, this.config.locale);
4402
4588
  this._batch = new BatchClient(this.httpClient);
4403
- this._adhocQuery = new AdhocQueryClient(this.httpClient);
4589
+ this._adhocQuery = new AdhocQueryClient(this.httpClient, this.config.locale);
4404
4590
  }
4405
4591
  // ============================================================================
4406
4592
  // CORE CLIENTS
@@ -4488,7 +4674,7 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4488
4674
  * ```
4489
4675
  */
4490
4676
  singleton(key) {
4491
- return new SingletonClient(this.httpClient, key);
4677
+ return new SingletonClient(this.httpClient, key, this.config.locale);
4492
4678
  }
4493
4679
  /**
4494
4680
  * Get a settings client for a specific settings group
@@ -4502,13 +4688,16 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4502
4688
  * ```
4503
4689
  */
4504
4690
  settings(groupId) {
4505
- return new SettingsClient(this.httpClient, groupId);
4691
+ return new SettingsClient(this.httpClient, groupId, this.config.locale);
4506
4692
  }
4507
4693
  // ============================================================================
4508
4694
  // SEARCH OPERATIONS
4509
4695
  // ============================================================================
4510
4696
  /**
4511
- * Start a search query with full-text, semantic, or hybrid search
4697
+ * Start a search query with full-text, semantic, or hybrid search.
4698
+ *
4699
+ * Where a schema types the client, the results are its entries — every content type's until
4700
+ * `in()` or `across()` narrows them (`INV-SS-TYPES-INFERRED`).
4512
4701
  *
4513
4702
  * @example
4514
4703
  * ```typescript
@@ -4606,6 +4795,25 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4606
4795
  changes(options = {}) {
4607
4796
  return pollChanges(this.httpClient, options);
4608
4797
  }
4798
+ /**
4799
+ * The dataset's current position in the changes feed, as a cursor to start
4800
+ * `changes({ since })` or `followChanges(…, { since })` from. One
4801
+ * `GET /v1/changes` without a cursor: answered at once, holding nothing.
4802
+ *
4803
+ * Take it **before** you read what you will follow. A write your read did not
4804
+ * see is then after the position and replayed; opening the feed afterwards
4805
+ * without a `since` starts after it and misses it.
4806
+ *
4807
+ * @example
4808
+ * ```typescript
4809
+ * const since = await relyio.changesHead()
4810
+ * const post = await relyio.content('post').findById(id).execute()
4811
+ * for await (const change of relyio.changes({ since, ids: [id] })) { … }
4812
+ * ```
4813
+ */
4814
+ changesHead(options = {}) {
4815
+ return readChangesHead(this.httpClient, options);
4816
+ }
4609
4817
  // ============================================================================
4610
4818
  // QUERY OPERATIONS
4611
4819
  // ============================================================================
@@ -4643,10 +4851,12 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4643
4851
  * ```
4644
4852
  */
4645
4853
  async executeQuery(queryName, params) {
4854
+ const clientLocale = this.config.locale;
4855
+ const namesLocale = params?.locale != null || params?.locales != null;
4646
4856
  const response = await this.httpClient.request({
4647
4857
  method: "GET",
4648
4858
  url: `/${API_VERSION_SEGMENT}/${String(queryName)}`,
4649
- params: params ?? {}
4859
+ params: namesLocale || clientLocale === void 0 || clientLocale === "" ? params ?? {} : { ...params, locale: clientLocale }
4650
4860
  });
4651
4861
  const payload = readPayload(response.body);
4652
4862
  return Array.isArray(payload) ? payload : [payload];
@@ -4718,9 +4928,9 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4718
4928
  this.config = resolved.config;
4719
4929
  this._sandbox = resolved.sandbox;
4720
4930
  this.httpClient = new HttpClient(this.config);
4721
- this._search = new SearchClient(this.httpClient);
4931
+ this._search = new SearchClient(this.httpClient, this.config.locale);
4722
4932
  this._batch = new BatchClient(this.httpClient);
4723
- this._adhocQuery = new AdhocQueryClient(this.httpClient);
4933
+ this._adhocQuery = new AdhocQueryClient(this.httpClient, this.config.locale);
4724
4934
  }
4725
4935
  /**
4726
4936
  * Get the current configuration
@@ -4746,7 +4956,10 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4746
4956
  // ============================================================================
4747
4957
  /**
4748
4958
  * Create a locale-scoped client
4749
- * All content operations on the returned client will use the specified locale
4959
+ *
4960
+ * Every read the returned client builds — `content()`, `asset()`, `singleton()`, `settings()`,
4961
+ * `search()`, `similar()`, `autocomplete()`, `executeQuery()` and the ad-hoc queries — is in this
4962
+ * locale unless the call names one of its own, which wins.
4750
4963
  *
4751
4964
  * Type-safe when schema is provided: autocompletes valid locale codes!
4752
4965
  *
@@ -4754,9 +4967,10 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4754
4967
  * ```typescript
4755
4968
  * const spanish = relyio.withLocale('es-ES') // ← Autocompletes with schema!
4756
4969
  *
4757
- * // All operations use Spanish
4970
+ * // Reads use Spanish
4758
4971
  * const products = await spanish.content('product').query().execute()
4759
4972
  * const homepage = await spanish.singleton('homepage').fetch()
4973
+ * const results = await spanish.search('zapatos').execute()
4760
4974
  *
4761
4975
  * // Original client unchanged
4762
4976
  * const englishProducts = await relyio.content('product').query().execute()
@@ -4813,6 +5027,6 @@ function createClient(config) {
4813
5027
  }
4814
5028
  __name(createClient, "createClient");
4815
5029
 
4816
- export { AdhocQueryBuilder, AdhocQueryClient, AssetClient, AssetFileBuilder, AssetUploadBuilder, AuthenticationError, AuthorizationError, AutocompleteBuilder, BaseValidationError, BatchBuilder, BatchClient, ChangesExpiredError, ConnectionError, ContentByIdBuilder, ContentClient, ContentQueryBuilder, ErrorMapper, FileUploadError, FileValidationError, HttpClient, InvalidApiKeyError, InvalidFieldTypeError, InvalidPayloadError, MethodNotAllowedError, MissingApiKeyError, NetworkError, PayloadTooLargeError, PlanFeatureUnavailableError, RateLimitError, ReadOnlyKeyError, RequiredFieldError, ResourceConflictError, ResourceExpiredError, ResourceNotFoundError, SDKError, SchemaValidationError, SearchBuilder, SearchClient, ServerError, ServiceIndeterminateError, SettingsClient, SimilarBuilder, SingletonClient, TimeoutError, TransactionRolledBackError, TypedRelyIOClient, UnknownContractError, UsageQuotaExceededError, ValidationError, createClient, entityTagOf, isAssetEntry, isResourceNotFound, matchRoute, validateFile };
4817
- //# sourceMappingURL=chunk-5AZUUV5B.js.map
4818
- //# sourceMappingURL=chunk-5AZUUV5B.js.map
5030
+ export { AdhocQueryBuilder, AdhocQueryClient, AssetClient, AssetFileBuilder, AssetUploadBuilder, AuthenticationError, AuthorizationError, AutocompleteBuilder, BaseValidationError, BatchBuilder, BatchClient, ChangesExpiredError, ConnectionError, ContentByIdBuilder, ContentClient, ContentQueryBuilder, ErrorMapper, FileUploadError, FileValidationError, HttpClient, InvalidApiKeyError, InvalidFieldTypeError, InvalidPayloadError, MethodNotAllowedError, MissingApiKeyError, NetworkError, PayloadTooLargeError, PlanFeatureUnavailableError, RateLimitError, ReadOnlyKeyError, RequiredFieldError, ResourceConflictError, ResourceExpiredError, ResourceNotFoundError, SDKError, SchemaValidationError, SearchBuilder, SearchClient, ServerError, ServiceIndeterminateError, SettingsClient, SimilarBuilder, SingletonClient, TimeoutError, TransactionRolledBackError, TypedRelyIOClient, UnknownContractError, UsageQuotaExceededError, ValidationError, createClient, entityTagOf, isAborted, isAssetEntry, isResourceNotFound, matchRoute, serviceInstructedDelayMs, sleep, validateFile };
5031
+ //# sourceMappingURL=chunk-7ABNXVWL.js.map
5032
+ //# sourceMappingURL=chunk-7ABNXVWL.js.map