@relyio/sdk 0.3.0 → 0.4.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.
@@ -736,6 +736,110 @@ function fillPath(template, params) {
736
736
  return filled;
737
737
  }
738
738
  chunkMWMSFIWN_cjs.__name(fillPath, "fillPath");
739
+ var DECLARED_PATHS = {
740
+ "/health": true,
741
+ "/health/cache": true,
742
+ "/health/live": true,
743
+ "/health/ready": true,
744
+ "/internal/media/claim": true,
745
+ "/internal/media/file-complete": true,
746
+ "/internal/media/progress": true,
747
+ "/internal/media/resolve": true,
748
+ "/internal/usage/cycle": true,
749
+ "/internal/usage/ingest": true,
750
+ "/v1/{contentType}": true,
751
+ "/v1/{contentType}/{id}": true,
752
+ "/v1/{contentType}/{id}/children": true,
753
+ "/v1/{contentType}/{id}/file": true,
754
+ "/v1/{contentType}/{id}/file/confirm": true,
755
+ "/v1/{contentType}/{id}/file/multipart/abort": true,
756
+ "/v1/{contentType}/{id}/file/multipart/complete": true,
757
+ "/v1/{contentType}/{id}/file/multipart/create": true,
758
+ "/v1/{contentType}/{id}/file/multipart/list-parts": true,
759
+ "/v1/{contentType}/{id}/file/multipart/sign-part": true,
760
+ "/v1/{contentType}/{id}/file/presign": true,
761
+ "/v1/{contentType}/{id}/file/url": true,
762
+ "/v1/{contentType}/{id}/versions": true,
763
+ "/v1/{contentType}/{id}/versions/{versionNumber}": true,
764
+ "/v1/{contentType}/{id}/versions/{versionNumber}/restore": true,
765
+ "/v1/batch": true,
766
+ "/v1/changes": true,
767
+ "/v1/meta/content-types": true,
768
+ "/v1/meta/info": true,
769
+ "/v1/meta/locales": true,
770
+ "/v1/meta/schema": true,
771
+ "/v1/meta/schema/{contentType}": true,
772
+ "/v1/meta/schema/{contentType}/fields": true,
773
+ "/v1/query": true,
774
+ "/v1/search": true,
775
+ "/v1/search/autocomplete": true,
776
+ "/v1/search/similar": true,
777
+ "/v1/settings": true,
778
+ "/v1/settings/{groupId}": true,
779
+ "/v1/settings/{groupId}/defaults": true,
780
+ "/v1/settings/{groupId}/reset": true,
781
+ "/v1/settings/{groupId}/versions": true,
782
+ "/v1/settings/{groupId}/versions/{versionNumber}": true,
783
+ "/v1/singletons/{key}": true,
784
+ "/v1/singletons/{key}/exists": true,
785
+ "/v1/singletons/{key}/versions": true,
786
+ "/v1/singletons/{key}/versions/{versionNumber}": true
787
+ };
788
+ function compile(path) {
789
+ const segments = path.split("/").slice(1).map((part) => {
790
+ const param = /^\{(\w+)\}$/.exec(part);
791
+ return param?.[1] === void 0 ? { kind: "static", value: part } : { kind: "param", name: param[1] };
792
+ });
793
+ return { path, segments };
794
+ }
795
+ chunkMWMSFIWN_cjs.__name(compile, "compile");
796
+ function byRouterPrecedence(a, b) {
797
+ const shared = Math.min(a.segments.length, b.segments.length);
798
+ for (let index = 0; index < shared; index += 1) {
799
+ const left = a.segments[index]?.kind;
800
+ const right = b.segments[index]?.kind;
801
+ if (left !== right) return left === "static" ? -1 : 1;
802
+ }
803
+ return 0;
804
+ }
805
+ chunkMWMSFIWN_cjs.__name(byRouterPrecedence, "byRouterPrecedence");
806
+ var COMPILED_ROUTES = Object.keys(DECLARED_PATHS).map(compile).sort(byRouterPrecedence);
807
+ function matchRoute(url, options = {}) {
808
+ const parsed = new URL(url, "http://relative.invalid");
809
+ let pathname = parsed.pathname;
810
+ if (options.baseUrl !== void 0) {
811
+ const prefix = new URL(options.baseUrl, "http://relative.invalid").pathname.replace(/\/+$/, "");
812
+ if (prefix !== "") {
813
+ if (pathname !== prefix && !pathname.startsWith(`${prefix}/`)) return null;
814
+ pathname = pathname.slice(prefix.length);
815
+ }
816
+ }
817
+ const parts = pathname.split("/").slice(1);
818
+ for (const route of COMPILED_ROUTES) {
819
+ if (route.segments.length !== parts.length) continue;
820
+ const params = {};
821
+ const matched = route.segments.every((segment, index) => {
822
+ const part = parts[index];
823
+ if (part === void 0) return false;
824
+ if (segment.kind === "static") return segment.value === part;
825
+ if (part === "") return false;
826
+ params[segment.name] = decodeURIComponent(part);
827
+ return true;
828
+ });
829
+ if (matched) return { path: route.path, params, query: readQuery(parsed.searchParams) };
830
+ }
831
+ return null;
832
+ }
833
+ chunkMWMSFIWN_cjs.__name(matchRoute, "matchRoute");
834
+ function readQuery(searchParams) {
835
+ const query = {};
836
+ for (const key of new Set(searchParams.keys())) {
837
+ const values = searchParams.getAll(key);
838
+ query[key] = values.length === 1 && values[0] !== void 0 ? values[0] : values;
839
+ }
840
+ return query;
841
+ }
842
+ chunkMWMSFIWN_cjs.__name(readQuery, "readQuery");
739
843
 
740
844
  // src/http/retry-policy.ts
741
845
  var IDEMPOTENT_METHODS = /* @__PURE__ */ new Set([
@@ -883,6 +987,7 @@ var _HttpClient = class _HttpClient {
883
987
  return response;
884
988
  } catch (error) {
885
989
  const failure = error;
990
+ if (config.signal?.aborted === true) throw failure;
886
991
  this.log("warn", "Request failed", {
887
992
  error: error instanceof Error ? error.message : "Unknown error",
888
993
  attempt,
@@ -891,7 +996,7 @@ var _HttpClient = class _HttpClient {
891
996
  if (this.retryPolicy.shouldRetry(attempt, failure, mergedConfig)) {
892
997
  const delay = this.retryPolicy.delayFor(attempt, failure);
893
998
  this.log("debug", "Retrying request", { attempt, delay });
894
- await this.sleep(delay);
999
+ await this.sleep(delay, config.signal);
895
1000
  continue;
896
1001
  }
897
1002
  const sdkError = ErrorMapper.mapGenericError(failure, {
@@ -990,20 +1095,41 @@ var _HttpClient = class _HttpClient {
990
1095
  getBaseUrl() {
991
1096
  return this.baseUrl;
992
1097
  }
1098
+ /**
1099
+ * One attempt.
1100
+ *
1101
+ * The caller's `signal` and the timeout both end it, through one controller.
1102
+ * The incumbent handed `fetch` the caller's signal **instead of** its own, so a
1103
+ * request that passed a signal had no timeout at all — and a caller abort then
1104
+ * surfaced as `Request timed out`. `timedOut` is what tells the two apart: a
1105
+ * timeout is a `TimeoutError`, a caller abort is the platform's `AbortError`,
1106
+ * rethrown as it came.
1107
+ */
993
1108
  async makeRequest(url, config) {
1109
+ const caller = config.signal;
1110
+ if (caller?.aborted === true) {
1111
+ throw new DOMException("The request was aborted before it was sent", "AbortError");
1112
+ }
994
1113
  const controller = new AbortController();
995
- const timeoutId = setTimeout(() => controller.abort(), config.timeout ?? this.timeout);
1114
+ let timedOut = false;
1115
+ const timeoutId = setTimeout(() => {
1116
+ timedOut = true;
1117
+ controller.abort();
1118
+ }, config.timeout ?? this.timeout);
1119
+ const onCallerAbort = /* @__PURE__ */ chunkMWMSFIWN_cjs.__name(() => controller.abort(), "onCallerAbort");
1120
+ caller?.addEventListener("abort", onCallerAbort, { once: true });
996
1121
  try {
997
1122
  const filteredHeaders = this.filterHeadersForBrowser(config.headers ?? {});
998
1123
  const requestInit = {
999
1124
  method: config.method,
1000
1125
  headers: filteredHeaders,
1001
1126
  body: this.prepareRequestBody(config),
1002
- signal: config.signal ?? controller.signal,
1127
+ signal: controller.signal,
1003
1128
  // Include credentials for CORS requests if in browser and configured
1004
1129
  ...typeof window !== "undefined" && this.config.browser.includeCredentials != null && { credentials: "include" }
1005
1130
  };
1006
- const response = await fetch(url, requestInit);
1131
+ const send = this.config.fetch ?? globalThis.fetch;
1132
+ const response = await send(url, requestInit);
1007
1133
  clearTimeout(timeoutId);
1008
1134
  const responseBody = await this.parseResponse(response);
1009
1135
  if (!response.ok) {
@@ -1034,6 +1160,7 @@ var _HttpClient = class _HttpClient {
1034
1160
  throw error;
1035
1161
  }
1036
1162
  if (error instanceof DOMException && error.name === "AbortError") {
1163
+ if (!timedOut && isAborted(caller)) throw error;
1037
1164
  throw new TimeoutError(
1038
1165
  `Request timed out after ${config.timeout ?? this.timeout}ms`,
1039
1166
  config.timeout ?? this.timeout
@@ -1065,6 +1192,8 @@ var _HttpClient = class _HttpClient {
1065
1192
  throw corsError;
1066
1193
  }
1067
1194
  throw error;
1195
+ } finally {
1196
+ caller?.removeEventListener("abort", onCallerAbort);
1068
1197
  }
1069
1198
  }
1070
1199
  /**
@@ -1153,8 +1282,21 @@ var _HttpClient = class _HttpClient {
1153
1282
  });
1154
1283
  return obj;
1155
1284
  }
1156
- sleep(ms) {
1157
- return new Promise((resolve) => setTimeout(resolve, ms));
1285
+ /** A backoff the caller's abort cuts short; the next attempt then refuses to send. */
1286
+ sleep(ms, signal) {
1287
+ return new Promise((resolve) => {
1288
+ if (isAborted(signal)) {
1289
+ resolve();
1290
+ return;
1291
+ }
1292
+ const done = /* @__PURE__ */ chunkMWMSFIWN_cjs.__name(() => {
1293
+ clearTimeout(timer);
1294
+ signal?.removeEventListener("abort", done);
1295
+ resolve();
1296
+ }, "done");
1297
+ const timer = setTimeout(done, ms);
1298
+ signal?.addEventListener("abort", done, { once: true });
1299
+ });
1158
1300
  }
1159
1301
  log(level, message, data) {
1160
1302
  if (!this.debug) return;
@@ -1167,6 +1309,10 @@ var _HttpClient = class _HttpClient {
1167
1309
  };
1168
1310
  chunkMWMSFIWN_cjs.__name(_HttpClient, "HttpClient");
1169
1311
  var HttpClient = _HttpClient;
1312
+ function isAborted(signal) {
1313
+ return signal?.aborted === true;
1314
+ }
1315
+ chunkMWMSFIWN_cjs.__name(isAborted, "isAborted");
1170
1316
 
1171
1317
  // src/content/query-builder.ts
1172
1318
  var _ContentQueryBuilder = class _ContentQueryBuilder {
@@ -3934,6 +4080,103 @@ var _AdhocQueryClient = class _AdhocQueryClient {
3934
4080
  chunkMWMSFIWN_cjs.__name(_AdhocQueryClient, "AdhocQueryClient");
3935
4081
  var AdhocQueryClient = _AdhocQueryClient;
3936
4082
 
4083
+ // src/changes/changes.ts
4084
+ var CHANGES_POLL_TIMEOUT_MS = 25e3 + 5e3;
4085
+ var _ChangesExpiredError = class _ChangesExpiredError extends ResourceExpiredError {
4086
+ /** The base class's signature, so `withContext` can rebuild one. */
4087
+ constructor(message, statusCode = 410, cause, context) {
4088
+ super(message, statusCode, cause, context);
4089
+ chunkMWMSFIWN_cjs.__publicField(this, "reason");
4090
+ this.name = "ChangesExpiredError";
4091
+ this.reason = expiredReasonOf(message);
4092
+ }
4093
+ static of(expired) {
4094
+ return new _ChangesExpiredError(expired.message, expired.statusCode, expired, expired.context);
4095
+ }
4096
+ };
4097
+ chunkMWMSFIWN_cjs.__name(_ChangesExpiredError, "ChangesExpiredError");
4098
+ var ChangesExpiredError = _ChangesExpiredError;
4099
+ function expiredReasonOf(message) {
4100
+ if (message.includes("(retention)")) return "retention";
4101
+ if (message.includes("(dataset-replaced)")) return "dataset-replaced";
4102
+ return "unknown";
4103
+ }
4104
+ chunkMWMSFIWN_cjs.__name(expiredReasonOf, "expiredReasonOf");
4105
+ async function* pollChanges(http, options = {}) {
4106
+ const { signal } = options;
4107
+ let since = options.since;
4108
+ let resynced;
4109
+ for (; ; ) {
4110
+ if (aborted(signal)) return;
4111
+ let response;
4112
+ try {
4113
+ response = await pollOnce(http, since, options);
4114
+ } catch (error) {
4115
+ if (aborted(signal)) return;
4116
+ if (!(error instanceof ResourceExpiredError) || since === void 0) throw error;
4117
+ const expired = ChangesExpiredError.of(error);
4118
+ const onExpired = options.onExpired ?? "throw";
4119
+ if (onExpired === "throw") throw expired;
4120
+ since = void 0;
4121
+ resynced = typeof onExpired === "function" ? expired : void 0;
4122
+ continue;
4123
+ }
4124
+ const { data, page } = response.body;
4125
+ if (page.nextCursor === null) {
4126
+ throw new Error("GET /v1/changes answered without page.nextCursor");
4127
+ }
4128
+ since = page.nextCursor;
4129
+ if (resynced !== void 0 && typeof options.onExpired === "function") {
4130
+ const expired = resynced;
4131
+ resynced = void 0;
4132
+ await options.onExpired(expired);
4133
+ if (aborted(signal)) return;
4134
+ }
4135
+ for (const change of data) {
4136
+ yield change;
4137
+ if (aborted(signal)) return;
4138
+ }
4139
+ if (data.length === 0) {
4140
+ const wait = parseRetryAfterMs(headerValue(response.headers, "Retry-After"), Date.now());
4141
+ if (wait !== void 0 && wait > 0) await sleep(wait, signal);
4142
+ }
4143
+ }
4144
+ }
4145
+ chunkMWMSFIWN_cjs.__name(pollChanges, "pollChanges");
4146
+ async function pollOnce(http, since, options) {
4147
+ const query = {};
4148
+ if (since !== void 0) query.since = since;
4149
+ if (options.types !== void 0) query.types = options.types.join(",");
4150
+ if (options.ids !== void 0) query.ids = options.ids.join(",");
4151
+ if (options.limit !== void 0) query.limit = options.limit;
4152
+ return http.send("/v1/changes", "get", {
4153
+ query,
4154
+ timeout: CHANGES_POLL_TIMEOUT_MS,
4155
+ ...options.signal === void 0 ? {} : { signal: options.signal }
4156
+ });
4157
+ }
4158
+ chunkMWMSFIWN_cjs.__name(pollOnce, "pollOnce");
4159
+ function sleep(ms, signal) {
4160
+ return new Promise((resolve) => {
4161
+ if (aborted(signal)) {
4162
+ resolve();
4163
+ return;
4164
+ }
4165
+ const done = /* @__PURE__ */ chunkMWMSFIWN_cjs.__name(() => {
4166
+ clearTimeout(timer);
4167
+ signal?.removeEventListener("abort", done);
4168
+ resolve();
4169
+ }, "done");
4170
+ const timer = setTimeout(done, ms);
4171
+ signal?.addEventListener("abort", done, { once: true });
4172
+ });
4173
+ }
4174
+ chunkMWMSFIWN_cjs.__name(sleep, "sleep");
4175
+ function aborted(signal) {
4176
+ return signal?.aborted === true;
4177
+ }
4178
+ chunkMWMSFIWN_cjs.__name(aborted, "aborted");
4179
+
3937
4180
  // src/config/dev-detection.ts
3938
4181
  function getProcess() {
3939
4182
  return typeof process !== "undefined" ? process : void 0;
@@ -4304,6 +4547,37 @@ var _TypedRelyIOClient = class _TypedRelyIOClient {
4304
4547
  return this._batch.create();
4305
4548
  }
4306
4549
  // ============================================================================
4550
+ // CHANGES FEED
4551
+ // ============================================================================
4552
+ /**
4553
+ * Follow the dataset's changes feed: each write this key can see, as ids, in
4554
+ * order, long-polled from `GET /v1/changes`.
4555
+ *
4556
+ * Lazy — nothing is requested until the first `next()`. It runs until
4557
+ * `signal` aborts, which ends the `for await` cleanly rather than throwing.
4558
+ * A change names what changed; refetch it through the ordinary read path
4559
+ * (`INV-SS-CHANGE-IS-IDS`).
4560
+ *
4561
+ * @example
4562
+ * ```typescript
4563
+ * const controller = new AbortController()
4564
+ * for await (const change of relyio.changes({ since: cursor, types: ['post'], signal: controller.signal })) {
4565
+ * // { entryId, contentType, locale, kind, cursor }
4566
+ * }
4567
+ * ```
4568
+ *
4569
+ * @example Resync when the cursor has expired
4570
+ * ```typescript
4571
+ * for await (const change of relyio.changes({
4572
+ * since: cursor,
4573
+ * onExpired: async () => invalidateEverything(),
4574
+ * })) { … }
4575
+ * ```
4576
+ */
4577
+ changes(options = {}) {
4578
+ return pollChanges(this.httpClient, options);
4579
+ }
4580
+ // ============================================================================
4307
4581
  // QUERY OPERATIONS
4308
4582
  // ============================================================================
4309
4583
  /**
@@ -4502,6 +4776,7 @@ exports.AutocompleteBuilder = AutocompleteBuilder;
4502
4776
  exports.BaseValidationError = BaseValidationError;
4503
4777
  exports.BatchBuilder = BatchBuilder;
4504
4778
  exports.BatchClient = BatchClient;
4779
+ exports.ChangesExpiredError = ChangesExpiredError;
4505
4780
  exports.ConnectionError = ConnectionError;
4506
4781
  exports.ContentByIdBuilder = ContentByIdBuilder;
4507
4782
  exports.ContentClient = ContentClient;
@@ -4543,6 +4818,7 @@ exports.createClient = createClient;
4543
4818
  exports.entityTagOf = entityTagOf;
4544
4819
  exports.isAssetEntry = isAssetEntry;
4545
4820
  exports.isResourceNotFound = isResourceNotFound;
4821
+ exports.matchRoute = matchRoute;
4546
4822
  exports.validateFile = validateFile;
4547
- //# sourceMappingURL=chunk-54WP2PWG.cjs.map
4548
- //# sourceMappingURL=chunk-54WP2PWG.cjs.map
4823
+ //# sourceMappingURL=chunk-XS32U2UP.cjs.map
4824
+ //# sourceMappingURL=chunk-XS32U2UP.cjs.map
@@ -672,5 +672,5 @@ __name(component, "component");
672
672
  var fields = fields_exports;
673
673
 
674
674
  export { BUILT_IN_ROLES, BUILT_IN_ROLE_ACTIONS, ROLE_ACTIONS, buildFallbackChain, defineAssetType, defineComponent, defineContentType, defineContentTypeWorkflow, defineLocale, defineQuery, defineRole, defineSchema, defineSettingsGroup, defineSingleton, defineSingletonContentType, defineView, defineWorkflow, defineWorkflowStage, fields, fieldset, findDefaultLocale, findWorkflowStage, getActiveLocales, getBuiltInRoleActions, getFirstWorkflowStage, getLocaleCodes, getNextWorkflowStage, getWorkflowStageIds, isBuiltInRole, isFinalWorkflowStage, isLocaleDefinition, isRoleDefinition, isWorkflowDefinition, isWorkflowStageDefinition };
675
- //# sourceMappingURL=chunk-LYRYCIX5.js.map
676
- //# sourceMappingURL=chunk-LYRYCIX5.js.map
675
+ //# sourceMappingURL=chunk-XY5KFEFY.js.map
676
+ //# sourceMappingURL=chunk-XY5KFEFY.js.map
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunk54WP2PWG_cjs = require('../chunk-54WP2PWG.cjs');
3
+ var chunkXS32U2UP_cjs = require('../chunk-XS32U2UP.cjs');
4
4
  require('../chunk-LSRPMNPT.cjs');
5
5
  require('../chunk-MWMSFIWN.cjs');
6
6
 
@@ -8,11 +8,11 @@ require('../chunk-MWMSFIWN.cjs');
8
8
 
9
9
  Object.defineProperty(exports, "TypedRelyIOClient", {
10
10
  enumerable: true,
11
- get: function () { return chunk54WP2PWG_cjs.TypedRelyIOClient; }
11
+ get: function () { return chunkXS32U2UP_cjs.TypedRelyIOClient; }
12
12
  });
13
13
  Object.defineProperty(exports, "createClient", {
14
14
  enumerable: true,
15
- get: function () { return chunk54WP2PWG_cjs.createClient; }
15
+ get: function () { return chunkXS32U2UP_cjs.createClient; }
16
16
  });
17
17
  //# sourceMappingURL=index.cjs.map
18
18
  //# sourceMappingURL=index.cjs.map
@@ -1,4 +1,4 @@
1
- export { C as ClientSchema, a as TypedClientConfig, T as TypedRelyIOClient, c as createClient } from '../index-BDpar3FO.cjs';
2
- import '../index-ulOrcfPa.cjs';
1
+ export { C as ClientSchema, R as Register, b as RegisteredSchema, a as TypedClientConfig, T as TypedRelyIOClient, c as createClient } from '../index-BvO6HDtY.cjs';
2
+ import '../index-DFehx5sI.cjs';
3
3
  import '../define-content-type-CBfnTWSS.cjs';
4
- import '../define-schema-BfXeR6K8.cjs';
4
+ import '../define-schema-2xbHgtK-.cjs';
@@ -1,4 +1,4 @@
1
- export { C as ClientSchema, a as TypedClientConfig, T as TypedRelyIOClient, c as createClient } from '../index-Dx01iwaf.js';
2
- import '../index-DuEhsGdE.js';
1
+ export { C as ClientSchema, R as Register, b as RegisteredSchema, a as TypedClientConfig, T as TypedRelyIOClient, c as createClient } from '../index-BzOFJJKS.js';
2
+ import '../index-FDak5ofk.js';
3
3
  import '../define-content-type-CBfnTWSS.js';
4
- import '../define-schema-BD6HSPnG.js';
4
+ import '../define-schema-DzYXk4HL.js';
@@ -1,4 +1,4 @@
1
- export { TypedRelyIOClient, createClient } from '../chunk-QIZOBT5F.js';
1
+ export { TypedRelyIOClient, createClient } from '../chunk-43VPZT2G.js';
2
2
  import '../chunk-BBUB7C2Z.js';
3
3
  import '../chunk-XQ4QHWZE.js';
4
4
  //# sourceMappingURL=index.js.map
@@ -1419,16 +1419,20 @@ type SchemaSingletonNames<T extends AnySchemaDefinition> = keyof T['singletonCon
1419
1419
  */
1420
1420
  type SchemaQueryNames<T extends AnySchemaDefinition> = keyof T['queries'] & string;
1421
1421
  /**
1422
- * Get settings group names from a schema
1422
+ * Get settings group names from a schema.
1423
+ *
1424
+ * `settingsGroups`, `assetTypes` and `locales` are optional members of `SchemaDefinition`, so
1425
+ * indexing one yields `X | undefined`, which never extends the record — every typed client's
1426
+ * `settings()`, `asset()` and `withLocale()` took `never`. The three read through `NonNullable`.
1423
1427
  */
1424
- type SchemaSettingsGroupNames<T extends AnySchemaDefinition> = T['settingsGroups'] extends Record<string, SettingsGroupDefinition> ? keyof T['settingsGroups'] & string : never;
1428
+ type SchemaSettingsGroupNames<T extends AnySchemaDefinition> = NonNullable<T['settingsGroups']> extends Record<string, SettingsGroupDefinition> ? keyof NonNullable<T['settingsGroups']> & string : never;
1425
1429
  /**
1426
1430
  * Get asset type names from a schema
1427
1431
  */
1428
- type SchemaAssetTypeNames<T extends AnySchemaDefinition> = T['assetTypes'] extends Record<string, AssetTypeDefinition> ? keyof T['assetTypes'] & string : never;
1432
+ type SchemaAssetTypeNames<T extends AnySchemaDefinition> = NonNullable<T['assetTypes']> extends Record<string, AssetTypeDefinition> ? keyof NonNullable<T['assetTypes']> & string : never;
1429
1433
  /**
1430
1434
  * Get locale codes from a schema
1431
1435
  */
1432
- type SchemaLocaleCodes<T extends AnySchemaDefinition> = T['locales'] extends readonly LocaleDefinition[] ? T['locales'][number]['code'] : never;
1436
+ type SchemaLocaleCodes<T extends AnySchemaDefinition> = NonNullable<T['locales']> extends readonly LocaleDefinition[] ? NonNullable<T['locales']>[number]['code'] : never;
1433
1437
 
1434
1438
  export { type VideoCaptionsConfig as $, type SettingsGroupConfig as A, type SettingsGroupDefinition as B, type ComponentConfig as C, type SettingsGroupMetadata as D, type InferSettingsGroup as E, defineAssetType as F, type AssetTypeConfig as G, type AssetTypeConfigWithFields as H, type InferSingletonContentType as I, type AssetTypeConfigWithFieldsets as J, type AssetTypeDefinition as K, type LocaleOptions as L, type InferAssetType as M, type ImageKindConfig as N, type FileKindConfig as O, type ImageTransform as P, type ImageFitMode as Q, type ImageOutputFormat as R, type SchemaConfig as S, type TextDirection as T, type ImageUploadConfig as U, type VideoKindConfig as V, type ImageSourceConfig as W, type ImageExifConfig as X, type VariantDefinition as Y, type VideoUploadConfig as Z, type VideoPosterConfig as _, type SchemaDefinition as a, type VideoDownloadConfig as a0, type VideoWatermarkConfig as a1, type FileUploadConfig as a2, type FileScanConfig as a3, defineRole as a4, isRoleDefinition as a5, isBuiltInRole as a6, getBuiltInRoleActions as a7, BUILT_IN_ROLES as a8, ROLE_ACTIONS as a9, type WhereCondition as aA, type WhereValue as aB, type WhereOperator as aC, type SortCondition as aD, type SortDirection as aE, type IncludeDefinition as aF, type SelectMapping as aG, type FilterDefinition as aH, type CachePolicy as aI, type CachePolicyObject as aJ, type IncludeSubQuery as aK, type IncludeSubQueries as aL, type Parameterizable as aM, type QueryParameter as aN, type QueryParameterType as aO, type QueryCardinality as aP, type InferQueryResponse as aQ, type InferQueryParams as aR, BUILT_IN_ROLE_ACTIONS as aa, type BuiltInRole as ab, type RoleAction as ac, type ResourcePermission as ad, type FieldVisibility as ae, type RoleConfig as af, type RoleDefinition as ag, defineView as ah, type ViewScopeConfig as ai, type ContentTypeScopeConfig as aj, type ContentTypesScopeConfig as ak, type EntryTypeScopeConfig as al, type AllScopeConfig as am, type ViewFilterOperator as an, type ViewFilterCondition as ao, type ViewFilterGroup as ap, type ViewFilter as aq, type ViewConfig as ar, type ViewDefinition as as, defineQuery as at, type QueryConfig as au, type QueryDefinition as av, type FetchBlock as aw, type AggregateBlock as ax, type SearchBlock as ay, type SimilarBlock as az, type SchemaContentTypeNames as b, type SchemaSingletonNames as c, defineSchema as d, type SchemaQueryNames as e, type SchemaLocaleCodes as f, type SchemaSettingsGroupNames as g, type SchemaAssetTypeNames as h, defineLocale as i, isLocaleDefinition as j, getLocaleCodes as k, findDefaultLocale as l, getActiveLocales as m, buildFallbackChain as n, type LocaleDefinition as o, defineSingletonContentType as p, defineSingleton as q, type SingletonContentTypeDefinition as r, type SingletonMetadata as s, type InferSingleton as t, defineComponent as u, type ComponentDefinition as v, type ComponentMetadata as w, type ComponentReusabilityMetadata as x, type ComponentValidationMetadata as y, defineSettingsGroup as z };
@@ -1419,16 +1419,20 @@ type SchemaSingletonNames<T extends AnySchemaDefinition> = keyof T['singletonCon
1419
1419
  */
1420
1420
  type SchemaQueryNames<T extends AnySchemaDefinition> = keyof T['queries'] & string;
1421
1421
  /**
1422
- * Get settings group names from a schema
1422
+ * Get settings group names from a schema.
1423
+ *
1424
+ * `settingsGroups`, `assetTypes` and `locales` are optional members of `SchemaDefinition`, so
1425
+ * indexing one yields `X | undefined`, which never extends the record — every typed client's
1426
+ * `settings()`, `asset()` and `withLocale()` took `never`. The three read through `NonNullable`.
1423
1427
  */
1424
- type SchemaSettingsGroupNames<T extends AnySchemaDefinition> = T['settingsGroups'] extends Record<string, SettingsGroupDefinition> ? keyof T['settingsGroups'] & string : never;
1428
+ type SchemaSettingsGroupNames<T extends AnySchemaDefinition> = NonNullable<T['settingsGroups']> extends Record<string, SettingsGroupDefinition> ? keyof NonNullable<T['settingsGroups']> & string : never;
1425
1429
  /**
1426
1430
  * Get asset type names from a schema
1427
1431
  */
1428
- type SchemaAssetTypeNames<T extends AnySchemaDefinition> = T['assetTypes'] extends Record<string, AssetTypeDefinition> ? keyof T['assetTypes'] & string : never;
1432
+ type SchemaAssetTypeNames<T extends AnySchemaDefinition> = NonNullable<T['assetTypes']> extends Record<string, AssetTypeDefinition> ? keyof NonNullable<T['assetTypes']> & string : never;
1429
1433
  /**
1430
1434
  * Get locale codes from a schema
1431
1435
  */
1432
- type SchemaLocaleCodes<T extends AnySchemaDefinition> = T['locales'] extends readonly LocaleDefinition[] ? T['locales'][number]['code'] : never;
1436
+ type SchemaLocaleCodes<T extends AnySchemaDefinition> = NonNullable<T['locales']> extends readonly LocaleDefinition[] ? NonNullable<T['locales']>[number]['code'] : never;
1433
1437
 
1434
1438
  export { type VideoCaptionsConfig as $, type SettingsGroupConfig as A, type SettingsGroupDefinition as B, type ComponentConfig as C, type SettingsGroupMetadata as D, type InferSettingsGroup as E, defineAssetType as F, type AssetTypeConfig as G, type AssetTypeConfigWithFields as H, type InferSingletonContentType as I, type AssetTypeConfigWithFieldsets as J, type AssetTypeDefinition as K, type LocaleOptions as L, type InferAssetType as M, type ImageKindConfig as N, type FileKindConfig as O, type ImageTransform as P, type ImageFitMode as Q, type ImageOutputFormat as R, type SchemaConfig as S, type TextDirection as T, type ImageUploadConfig as U, type VideoKindConfig as V, type ImageSourceConfig as W, type ImageExifConfig as X, type VariantDefinition as Y, type VideoUploadConfig as Z, type VideoPosterConfig as _, type SchemaDefinition as a, type VideoDownloadConfig as a0, type VideoWatermarkConfig as a1, type FileUploadConfig as a2, type FileScanConfig as a3, defineRole as a4, isRoleDefinition as a5, isBuiltInRole as a6, getBuiltInRoleActions as a7, BUILT_IN_ROLES as a8, ROLE_ACTIONS as a9, type WhereCondition as aA, type WhereValue as aB, type WhereOperator as aC, type SortCondition as aD, type SortDirection as aE, type IncludeDefinition as aF, type SelectMapping as aG, type FilterDefinition as aH, type CachePolicy as aI, type CachePolicyObject as aJ, type IncludeSubQuery as aK, type IncludeSubQueries as aL, type Parameterizable as aM, type QueryParameter as aN, type QueryParameterType as aO, type QueryCardinality as aP, type InferQueryResponse as aQ, type InferQueryParams as aR, BUILT_IN_ROLE_ACTIONS as aa, type BuiltInRole as ab, type RoleAction as ac, type ResourcePermission as ad, type FieldVisibility as ae, type RoleConfig as af, type RoleDefinition as ag, defineView as ah, type ViewScopeConfig as ai, type ContentTypeScopeConfig as aj, type ContentTypesScopeConfig as ak, type EntryTypeScopeConfig as al, type AllScopeConfig as am, type ViewFilterOperator as an, type ViewFilterCondition as ao, type ViewFilterGroup as ap, type ViewFilter as aq, type ViewConfig as ar, type ViewDefinition as as, defineQuery as at, type QueryConfig as au, type QueryDefinition as av, type FetchBlock as aw, type AggregateBlock as ax, type SearchBlock as ay, type SimilarBlock as az, type SchemaContentTypeNames as b, type SchemaSingletonNames as c, defineSchema as d, type SchemaQueryNames as e, type SchemaLocaleCodes as f, type SchemaSettingsGroupNames as g, type SchemaAssetTypeNames as h, defineLocale as i, isLocaleDefinition as j, getLocaleCodes as k, findDefaultLocale as l, getActiveLocales as m, buildFallbackChain as n, type LocaleDefinition as o, defineSingletonContentType as p, defineSingleton as q, type SingletonContentTypeDefinition as r, type SingletonMetadata as s, type InferSingleton as t, defineComponent as u, type ComponentDefinition as v, type ComponentMetadata as w, type ComponentReusabilityMetadata as x, type ComponentValidationMetadata as y, defineSettingsGroup as z };