@proveanything/smartlinks 1.15.4 → 1.15.6

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.
@@ -1,5 +1,6 @@
1
1
  import { CollectionWidgetsResponse, GetCollectionWidgetsOptions } from "../types/appManifest";
2
- import type { GetWidgetInstanceOptions, WidgetInstance, WidgetInstanceSummary } from "../types/appConfiguration";
2
+ import type { AppsConfigResponse } from "../types/collection";
3
+ import type { GetWidgetInstanceOptions, WidgetInstance, WidgetInstanceSummary, SystemBlock } from "../types/appConfiguration";
3
4
  /**
4
5
  * Options for collection/product-scoped app configuration.
5
6
  * This data is set by admins and applies to all users within the scope.
@@ -399,4 +400,92 @@ export declare namespace appConfiguration {
399
400
  * ```
400
401
  */
401
402
  function getWidgets(collectionId: string, options?: GetCollectionWidgetsOptions): Promise<CollectionWidgetsResponse>;
403
+ /**
404
+ * Resolve whether a feature flag is on, applying the `accountType` default:
405
+ * `enterprise` accounts default every flag to **on** unless `features[flag]`
406
+ * is explicitly `false`; `standard` accounts (or missing `accountType`)
407
+ * default every flag to **off** unless `features[flag]` is explicitly `true`.
408
+ * An explicit value always wins over the default either way.
409
+ *
410
+ * This is the same logic `isFeatureEnabled()` / `isFeatureEnabledSync()` use
411
+ * internally — call those instead in normal code. `system.features` and
412
+ * `accountType` are public data (no admin-only feature-check path exists),
413
+ * so there's no reason to fetch differently for an admin surface. This is
414
+ * exposed separately only for the rare case where you already have a
415
+ * `system` block in hand from somewhere other than `getAppConfig()` (e.g.
416
+ * server-rendered props) and want to resolve a flag from it directly.
417
+ *
418
+ * @example
419
+ * ```typescript
420
+ * const enabled = appConfiguration.resolveFeature(someSystemBlock, 'custom_domain');
421
+ * ```
422
+ */
423
+ function resolveFeature(system: SystemBlock | undefined, flag: string): boolean;
424
+ /**
425
+ * Fetch the collection's app catalog + `appConfig` entitlements data in one
426
+ * call — installed modules (`apps`), resolved entitlements (`system.features`,
427
+ * `system.meters`, etc.), and item-handling settings. This is `/public/collection/:id/app/config`
428
+ * (`collection.getAppsConfig()`), which now carries both; there's no need to
429
+ * separately call `appConfiguration.getConfig({ appId: 'appConfig' })` for
430
+ * public reads. Cached in-memory (and sessionStorage) for `ttl` so repeated
431
+ * calls across a page/session are cheap; pass `force` to bypass the cache
432
+ * after a mutation (e.g. after `entitlements-reconcile`).
433
+ *
434
+ * For admin-only data (`systemPrivate`), use
435
+ * `appConfiguration.getConfig({ collectionId, appId: 'appConfig', admin: true })`
436
+ * directly — this endpoint is public-only and never returns it.
437
+ *
438
+ * See docs/appConfig.md for the full contract.
439
+ *
440
+ * @example
441
+ * ```typescript
442
+ * const cfg = await appConfiguration.getAppConfig(collectionId);
443
+ * console.log(cfg.system?.features);
444
+ * ```
445
+ */
446
+ function getAppConfig(collectionId: string, options?: {
447
+ force?: boolean;
448
+ }): Promise<AppsConfigResponse>;
449
+ /**
450
+ * Check whether a feature flag is enabled for a collection, per
451
+ * `appConfig.system.features` — applying the `accountType` default (see
452
+ * `resolveFeature()`): enterprise accounts default every flag to on unless
453
+ * explicitly set to `false`. This is the standard way for subapps to gate
454
+ * features — it reads through the same cache as `getAppConfig`, so calling
455
+ * it repeatedly (e.g. once per component) does not re-fetch on every call.
456
+ *
457
+ * This is always async because the very first call for a collection has
458
+ * nothing to read yet. If you need a synchronous check (e.g. inside a
459
+ * render function), call this once during app init to warm the cache,
460
+ * then use `isFeatureEnabledSync()` afterwards.
461
+ *
462
+ * @example
463
+ * ```typescript
464
+ * if (await appConfiguration.isFeatureEnabled(collectionId, 'custom_domain')) {
465
+ * enableCustomDomainUI();
466
+ * }
467
+ * ```
468
+ */
469
+ function isFeatureEnabled(collectionId: string, flag: string, options?: {
470
+ force?: boolean;
471
+ }): Promise<boolean>;
472
+ /**
473
+ * Synchronous, cache-only check for a feature flag. Returns `undefined` if
474
+ * `getAppConfig()` / `isFeatureEnabled()` hasn't been called yet for this
475
+ * collection this page load (i.e. nothing to read without an await) — treat
476
+ * `undefined` as "not yet known", not as "disabled".
477
+ *
478
+ * @example
479
+ * ```typescript
480
+ * // Warm the cache once, e.g. in a top-level effect:
481
+ * useEffect(() => { appConfiguration.isFeatureEnabled(collectionId, 'custom_domain') }, [collectionId]);
482
+ *
483
+ * // Elsewhere, read synchronously without an await:
484
+ * const enabled = appConfiguration.isFeatureEnabledSync(collectionId, 'custom_domain');
485
+ * if (enabled === undefined) {
486
+ * // not warmed yet — fall back to a loading state or the async version
487
+ * }
488
+ * ```
489
+ */
490
+ function isFeatureEnabledSync(collectionId: string, flag: string): boolean | undefined;
402
491
  }
@@ -11,6 +11,8 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  };
12
12
  // src/api/appConfiguration.ts
13
13
  import { request, post, del } from "../http";
14
+ import * as cache from "../cache";
15
+ import { collection as collectionApi } from "./collection";
14
16
  function getWidgetsMap(config) {
15
17
  if (!config || typeof config !== 'object' || Array.isArray(config))
16
18
  return {};
@@ -508,4 +510,119 @@ export var appConfiguration;
508
510
  return request(path);
509
511
  }
510
512
  appConfiguration.getWidgets = getWidgets;
513
+ const APP_CONFIG_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
514
+ function appConfigCacheKey(collectionId) {
515
+ // Same key IframeResponder uses for this endpoint — sharing the cache
516
+ // entry means whichever caller hits it first warms it for the other.
517
+ return `apps:${collectionId}`;
518
+ }
519
+ /**
520
+ * Resolve whether a feature flag is on, applying the `accountType` default:
521
+ * `enterprise` accounts default every flag to **on** unless `features[flag]`
522
+ * is explicitly `false`; `standard` accounts (or missing `accountType`)
523
+ * default every flag to **off** unless `features[flag]` is explicitly `true`.
524
+ * An explicit value always wins over the default either way.
525
+ *
526
+ * This is the same logic `isFeatureEnabled()` / `isFeatureEnabledSync()` use
527
+ * internally — call those instead in normal code. `system.features` and
528
+ * `accountType` are public data (no admin-only feature-check path exists),
529
+ * so there's no reason to fetch differently for an admin surface. This is
530
+ * exposed separately only for the rare case where you already have a
531
+ * `system` block in hand from somewhere other than `getAppConfig()` (e.g.
532
+ * server-rendered props) and want to resolve a flag from it directly.
533
+ *
534
+ * @example
535
+ * ```typescript
536
+ * const enabled = appConfiguration.resolveFeature(someSystemBlock, 'custom_domain');
537
+ * ```
538
+ */
539
+ function resolveFeature(system, flag) {
540
+ var _a;
541
+ const value = (_a = system === null || system === void 0 ? void 0 : system.features) === null || _a === void 0 ? void 0 : _a[flag];
542
+ if (value === true)
543
+ return true;
544
+ if (value === false)
545
+ return false;
546
+ return (system === null || system === void 0 ? void 0 : system.accountType) === 'enterprise';
547
+ }
548
+ appConfiguration.resolveFeature = resolveFeature;
549
+ /**
550
+ * Fetch the collection's app catalog + `appConfig` entitlements data in one
551
+ * call — installed modules (`apps`), resolved entitlements (`system.features`,
552
+ * `system.meters`, etc.), and item-handling settings. This is `/public/collection/:id/app/config`
553
+ * (`collection.getAppsConfig()`), which now carries both; there's no need to
554
+ * separately call `appConfiguration.getConfig({ appId: 'appConfig' })` for
555
+ * public reads. Cached in-memory (and sessionStorage) for `ttl` so repeated
556
+ * calls across a page/session are cheap; pass `force` to bypass the cache
557
+ * after a mutation (e.g. after `entitlements-reconcile`).
558
+ *
559
+ * For admin-only data (`systemPrivate`), use
560
+ * `appConfiguration.getConfig({ collectionId, appId: 'appConfig', admin: true })`
561
+ * directly — this endpoint is public-only and never returns it.
562
+ *
563
+ * See docs/appConfig.md for the full contract.
564
+ *
565
+ * @example
566
+ * ```typescript
567
+ * const cfg = await appConfiguration.getAppConfig(collectionId);
568
+ * console.log(cfg.system?.features);
569
+ * ```
570
+ */
571
+ async function getAppConfig(collectionId, options) {
572
+ const key = appConfigCacheKey(collectionId);
573
+ if (options === null || options === void 0 ? void 0 : options.force)
574
+ cache.invalidate(key);
575
+ return cache.getOrFetch(key, () => collectionApi.getAppsConfig(collectionId), { ttl: APP_CONFIG_CACHE_TTL, storage: 'session' });
576
+ }
577
+ appConfiguration.getAppConfig = getAppConfig;
578
+ /**
579
+ * Check whether a feature flag is enabled for a collection, per
580
+ * `appConfig.system.features` — applying the `accountType` default (see
581
+ * `resolveFeature()`): enterprise accounts default every flag to on unless
582
+ * explicitly set to `false`. This is the standard way for subapps to gate
583
+ * features — it reads through the same cache as `getAppConfig`, so calling
584
+ * it repeatedly (e.g. once per component) does not re-fetch on every call.
585
+ *
586
+ * This is always async because the very first call for a collection has
587
+ * nothing to read yet. If you need a synchronous check (e.g. inside a
588
+ * render function), call this once during app init to warm the cache,
589
+ * then use `isFeatureEnabledSync()` afterwards.
590
+ *
591
+ * @example
592
+ * ```typescript
593
+ * if (await appConfiguration.isFeatureEnabled(collectionId, 'custom_domain')) {
594
+ * enableCustomDomainUI();
595
+ * }
596
+ * ```
597
+ */
598
+ async function isFeatureEnabled(collectionId, flag, options) {
599
+ const cfg = await getAppConfig(collectionId, options);
600
+ return resolveFeature(cfg === null || cfg === void 0 ? void 0 : cfg.system, flag);
601
+ }
602
+ appConfiguration.isFeatureEnabled = isFeatureEnabled;
603
+ /**
604
+ * Synchronous, cache-only check for a feature flag. Returns `undefined` if
605
+ * `getAppConfig()` / `isFeatureEnabled()` hasn't been called yet for this
606
+ * collection this page load (i.e. nothing to read without an await) — treat
607
+ * `undefined` as "not yet known", not as "disabled".
608
+ *
609
+ * @example
610
+ * ```typescript
611
+ * // Warm the cache once, e.g. in a top-level effect:
612
+ * useEffect(() => { appConfiguration.isFeatureEnabled(collectionId, 'custom_domain') }, [collectionId]);
613
+ *
614
+ * // Elsewhere, read synchronously without an await:
615
+ * const enabled = appConfiguration.isFeatureEnabledSync(collectionId, 'custom_domain');
616
+ * if (enabled === undefined) {
617
+ * // not warmed yet — fall back to a loading state or the async version
618
+ * }
619
+ * ```
620
+ */
621
+ function isFeatureEnabledSync(collectionId, flag) {
622
+ const cfg = cache.peek(appConfigCacheKey(collectionId));
623
+ if (!cfg)
624
+ return undefined;
625
+ return resolveFeature(cfg.system, flag);
626
+ }
627
+ appConfiguration.isFeatureEnabledSync = isFeatureEnabledSync;
511
628
  })(appConfiguration || (appConfiguration = {}));
@@ -101,9 +101,13 @@ export declare namespace collection {
101
101
  */
102
102
  function getSettings(collectionId: string, settingGroup: string, admin?: boolean): Promise<any>;
103
103
  /**
104
- * Retrieve all configured app module definitions for a collection (public endpoint).
104
+ * Retrieve all configured app module definitions for a collection (public endpoint),
105
+ * plus the collection's `appConfig` entitlements data (`system.features`, `system.meters`,
106
+ * `entitledAppGroups`, `itemRecordMode`, etc.) in the same response. See docs/appConfig.md.
107
+ * Prefer `appConfiguration.getAppConfig()` / `isFeatureEnabled()` for entitlement reads —
108
+ * they call this endpoint under the hood and cache the result.
105
109
  * @param collectionId – Identifier of the collection
106
- * @returns Promise resolving to an AppsConfigResponse containing all app configurations
110
+ * @returns Promise resolving to an AppsConfigResponse containing the app catalog and entitlements
107
111
  * @throws ErrorResponse if the request fails
108
112
  */
109
113
  function getAppsConfig(collectionId: string): Promise<AppsConfigResponse>;
@@ -148,9 +148,13 @@ export var collection;
148
148
  }
149
149
  collection.getSettings = getSettings;
150
150
  /**
151
- * Retrieve all configured app module definitions for a collection (public endpoint).
151
+ * Retrieve all configured app module definitions for a collection (public endpoint),
152
+ * plus the collection's `appConfig` entitlements data (`system.features`, `system.meters`,
153
+ * `entitledAppGroups`, `itemRecordMode`, etc.) in the same response. See docs/appConfig.md.
154
+ * Prefer `appConfiguration.getAppConfig()` / `isFeatureEnabled()` for entitlement reads —
155
+ * they call this endpoint under the hood and cache the result.
152
156
  * @param collectionId – Identifier of the collection
153
- * @returns Promise resolving to an AppsConfigResponse containing all app configurations
157
+ * @returns Promise resolving to an AppsConfigResponse containing the app catalog and entitlements
154
158
  * @throws ErrorResponse if the request fails
155
159
  */
156
160
  async function getAppsConfig(collectionId) {
package/dist/cache.d.ts CHANGED
@@ -22,6 +22,25 @@ export interface CacheOptions {
22
22
  * ```
23
23
  */
24
24
  export declare function getOrFetch<T>(key: string, fetcher: () => Promise<T>, options?: CacheOptions): Promise<T>;
25
+ /**
26
+ * Synchronously read a cached value without fetching. Returns `undefined` if
27
+ * the key was never cached, or if its TTL has expired.
28
+ *
29
+ * Every write also mirrors into the in-memory store regardless of its
30
+ * `storage` backend, so the default `storage: 'memory'` here finds entries
31
+ * cached via `getOrFetch(..., { storage: 'session' | 'local' })` too, as
32
+ * long as it's the same page load.
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * // After warming the cache once with getOrFetch(...):
37
+ * const cached = cache.peek<AppConfig>(`appConfig:${collectionId}:public`);
38
+ * if (cached) {
39
+ * // render synchronously — no await needed
40
+ * }
41
+ * ```
42
+ */
43
+ export declare function peek<T>(key: string, storage?: 'memory' | 'session' | 'local'): T | undefined;
25
44
  /**
26
45
  * Invalidate a cached key.
27
46
  */
package/dist/cache.js CHANGED
@@ -59,6 +59,31 @@ export async function getOrFetch(key, fetcher, options = {}) {
59
59
  setToStorage(key, entry, storage);
60
60
  return value;
61
61
  }
62
+ /**
63
+ * Synchronously read a cached value without fetching. Returns `undefined` if
64
+ * the key was never cached, or if its TTL has expired.
65
+ *
66
+ * Every write also mirrors into the in-memory store regardless of its
67
+ * `storage` backend, so the default `storage: 'memory'` here finds entries
68
+ * cached via `getOrFetch(..., { storage: 'session' | 'local' })` too, as
69
+ * long as it's the same page load.
70
+ *
71
+ * @example
72
+ * ```typescript
73
+ * // After warming the cache once with getOrFetch(...):
74
+ * const cached = cache.peek<AppConfig>(`appConfig:${collectionId}:public`);
75
+ * if (cached) {
76
+ * // render synchronously — no await needed
77
+ * }
78
+ * ```
79
+ */
80
+ export function peek(key, storage = 'memory') {
81
+ const cached = getFromStorage(key, storage);
82
+ if (cached && cached.expiresAt > Date.now()) {
83
+ return cached.value;
84
+ }
85
+ return undefined;
86
+ }
62
87
  /**
63
88
  * Invalidate a cached key.
64
89
  */
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.15.4 | Generated: 2026-07-14T15:01:53.978Z
3
+ Version: 1.15.6 | Generated: 2026-07-19T08:21:33.985Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -43,6 +43,7 @@ For detailed guides on specific features:
43
43
  - **[Contact Search](contact-search.md)** - Admin contact search: free-text, typeahead, identity/tag/JSONB filters, and pagination
44
44
  - **[App Data Storage](app-data-storage.md)** - User-specific and collection-scoped app data storage
45
45
  - **[Product/Proof Data Scoping](proof-product-data-scoping.md)** - Canonical spec for `product.data`/`.admin` and `proof.data`/`.admin`/`.values` (owner/personal) buckets, read/write authority, and the `productFields`/`proofFields` collection-settings schemas
46
+ - **[appConfig / Feature Flags](appConfig.md)** - `appConfig` settings contract, entitlements (`system.features`/`entitledAppGroups`/`meters`), and the `isFeatureEnabled()` / `isFeatureEnabledSync()` SDK helpers
46
47
  - **[Forms](forms.md)** - Platform-managed form definitions, submissions, and schema-driven React form UI
47
48
  - **[App Objects: Cases, Threads & Records](app-objects.md)** - Generic app-scoped building blocks for support cases, discussions, bookings, registrations, and more
48
49
  - **[App Records Pattern](app-records-pattern.md)** - Canonical pattern for storing per-product, per-facet, or rule-targeted app data
@@ -1376,6 +1377,81 @@ interface WidgetInstanceSummary {
1376
1377
  }
1377
1378
  ```
1378
1379
 
1380
+ **AppEntry** (interface)
1381
+ ```typescript
1382
+ interface AppEntry {
1383
+ id: string
1384
+ uniqueName: string
1385
+ customName?: string
1386
+ active: boolean
1387
+ all?: boolean
1388
+ live?: boolean
1389
+ faIcon?: string
1390
+ versionChannel?: 'stable' | 'beta' | 'dev'
1391
+ reason?: 'not_entitled'
1392
+ [key: string]: unknown
1393
+ }
1394
+ ```
1395
+
1396
+ **MeterEntry** (interface)
1397
+ ```typescript
1398
+ interface MeterEntry {
1399
+ included: number
1400
+ stripePriceId?: string
1401
+ stripeMeterId?: string
1402
+ }
1403
+ ```
1404
+
1405
+ **AppliedOverridesSummary** (interface)
1406
+ ```typescript
1407
+ interface AppliedOverridesSummary {
1408
+ features?: string[]
1409
+ meters?: string[]
1410
+ entitledAppGroups?: string[]
1411
+ addOnKeys?: string[]
1412
+ apps?: string[]
1413
+ accountType?: boolean
1414
+ note?: string
1415
+ }
1416
+ ```
1417
+
1418
+ **SystemBlock** (interface)
1419
+ ```typescript
1420
+ interface SystemBlock {
1421
+ basePlanId?: string
1422
+ addOnKeys?: string[]
1423
+ apps?: string[]
1424
+ * Explicit overrides only — an absent key is NOT "off". Resolve with
1425
+ * `resolveFeature()` / `isFeatureEnabled()`, which apply the accountType
1426
+ * default: `enterprise` defaults every flag to on unless explicitly
1427
+ * `false` here; `standard` defaults every flag to off unless explicitly
1428
+ * `true` here.
1429
+ features?: Record<string, boolean>
1430
+ meters?: Record<string, MeterEntry>
1431
+ entitledAppGroups?: string[]
1432
+ * Explicit account tier. `'enterprise'` flips the default for every
1433
+ * feature flag to on (see `features`), not just an "unlimited baseline" —
1434
+ * absence of a flag no longer means disabled for enterprise accounts.
1435
+ accountType?: 'enterprise' | 'standard'
1436
+ syncedAt?: string
1437
+ syncedFromSubscriptionId?: string
1438
+ appliedOverrides?: AppliedOverridesSummary
1439
+ }
1440
+ ```
1441
+
1442
+ **AppConfigSettings** (interface)
1443
+ ```typescript
1444
+ interface AppConfigSettings {
1445
+ id: 'appConfig'
1446
+ apps: AppEntry[]
1447
+ addOns?: string[]
1448
+ requestedBasePlanId?: string
1449
+ itemRecordMode?: 'registered' | 'owned' | null
1450
+ virtualItemsEnabled?: boolean
1451
+ system?: SystemBlock
1452
+ }
1453
+ ```
1454
+
1379
1455
  ### appManifest
1380
1456
 
1381
1457
  **AppBundle** (interface)
@@ -3662,13 +3738,6 @@ interface AppConfig {
3662
3738
  }
3663
3739
  ```
3664
3740
 
3665
- **AppsConfigResponse** (interface)
3666
- ```typescript
3667
- interface AppsConfigResponse {
3668
- apps: AppConfig[]
3669
- }
3670
- ```
3671
-
3672
3741
  **CollectionResponse** = `Collection`
3673
3742
 
3674
3743
  **CollectionCreateRequest** = `Omit<Collection, 'id' | 'shortId'>`
@@ -8171,6 +8240,21 @@ Delete a keyed data item by ID within a scope. Requires admin authentication. ``
8171
8240
  options?: GetCollectionWidgetsOptions) → `Promise<CollectionWidgetsResponse>`
8172
8241
  Fetches ALL widget data (manifests + bundle files) for a collection in one call. Returns everything needed to render widgets with zero additional requests. This solves N+1 query problems by fetching manifests, JavaScript bundles, and CSS files in parallel on the server. ```typescript // Fetch all widget data for a collection const { apps } = await Api.AppConfiguration.getWidgets(collectionId); // Returns: [{ appId, manifestUrl, manifest, bundleSource, bundleCss }, ...] // Convert bundle source to dynamic imports for (const app of apps) { const blob = new Blob([app.bundleSource], { type: 'application/javascript' }); const blobUrl = URL.createObjectURL(blob); const widgetModule = await import(blobUrl); // Inject CSS if present if (app.bundleCss) { const styleTag = document.createElement('style'); styleTag.textContent = app.bundleCss; document.head.appendChild(styleTag); } } // Force refresh all widgets const { apps } = await Api.AppConfiguration.getWidgets(collectionId, { force: true }); ```
8173
8242
 
8243
+ **resolveFeature**(system: SystemBlock | undefined, flag: string) → `boolean`
8244
+ Resolve whether a feature flag is on, applying the `accountType` default: `enterprise` accounts default every flag to **on** unless `features[flag]` is explicitly `false`; `standard` accounts (or missing `accountType`) default every flag to **off** unless `features[flag]` is explicitly `true`. An explicit value always wins over the default either way. This is the same logic `isFeatureEnabled()` / `isFeatureEnabledSync()` use internally — call those instead in normal code. `system.features` and `accountType` are public data (no admin-only feature-check path exists), so there's no reason to fetch differently for an admin surface. This is exposed separately only for the rare case where you already have a `system` block in hand from somewhere other than `getAppConfig()` (e.g. server-rendered props) and want to resolve a flag from it directly. ```typescript const enabled = appConfiguration.resolveFeature(someSystemBlock, 'custom_domain'); ```
8245
+
8246
+ **getAppConfig**(collectionId: string,
8247
+ options?: { force?: boolean }) → `Promise<AppsConfigResponse>`
8248
+ Fetch the collection's app catalog + `appConfig` entitlements data in one call — installed modules (`apps`), resolved entitlements (`system.features`, `system.meters`, etc.), and item-handling settings. This is `/public/collection/:id/app/config` (`collection.getAppsConfig()`), which now carries both; there's no need to separately call `appConfiguration.getConfig({ appId: 'appConfig' })` for public reads. Cached in-memory (and sessionStorage) for `ttl` so repeated calls across a page/session are cheap; pass `force` to bypass the cache after a mutation (e.g. after `entitlements-reconcile`). For admin-only data (`systemPrivate`), use `appConfiguration.getConfig({ collectionId, appId: 'appConfig', admin: true })` directly — this endpoint is public-only and never returns it. See docs/appConfig.md for the full contract. ```typescript const cfg = await appConfiguration.getAppConfig(collectionId); console.log(cfg.system?.features); ```
8249
+
8250
+ **isFeatureEnabled**(collectionId: string,
8251
+ flag: string,
8252
+ options?: { force?: boolean }) → `Promise<boolean>`
8253
+ Check whether a feature flag is enabled for a collection, per `appConfig.system.features` — applying the `accountType` default (see `resolveFeature()`): enterprise accounts default every flag to on unless explicitly set to `false`. This is the standard way for subapps to gate features — it reads through the same cache as `getAppConfig`, so calling it repeatedly (e.g. once per component) does not re-fetch on every call. This is always async because the very first call for a collection has nothing to read yet. If you need a synchronous check (e.g. inside a render function), call this once during app init to warm the cache, then use `isFeatureEnabledSync()` afterwards. ```typescript if (await appConfiguration.isFeatureEnabled(collectionId, 'custom_domain')) { enableCustomDomainUI(); } ```
8254
+
8255
+ **isFeatureEnabledSync**(collectionId: string, flag: string) → `boolean | undefined`
8256
+ Synchronous, cache-only check for a feature flag. Returns `undefined` if `getAppConfig()` / `isFeatureEnabled()` hasn't been called yet for this collection this page load (i.e. nothing to read without an await) — treat `undefined` as "not yet known", not as "disabled". ```typescript // Warm the cache once, e.g. in a top-level effect: useEffect(() => { appConfiguration.isFeatureEnabled(collectionId, 'custom_domain') }, [collectionId]); // Elsewhere, read synchronously without an await: const enabled = appConfiguration.isFeatureEnabledSync(collectionId, 'custom_domain'); if (enabled === undefined) { // not warmed yet — fall back to a loading state or the async version } ```
8257
+
8174
8258
  ### asset
8175
8259
 
8176
8260
  **upload**(options: UploadAssetOptions) → `Promise<Asset>`
@@ -8719,7 +8803,7 @@ Get the managed-certificate status for a collection's custom domain (admin only)
8719
8803
  Retrieve a specific settings group for a collection. Public reads return the public view of the settings group. If the stored payload contains a top-level `admin` object, that block is omitted from public responses and included when `admin === true`.
8720
8804
 
8721
8805
  **getAppsConfig**(collectionId: string) → `Promise<AppsConfigResponse>`
8722
- Retrieve all configured app module definitions for a collection (public endpoint).
8806
+ Retrieve all configured app module definitions for a collection (public endpoint), plus the collection's `appConfig` entitlements data (`system.features`, `system.meters`, `entitledAppGroups`, `itemRecordMode`, etc.) in the same response. See docs/appConfig.md. Prefer `appConfiguration.getAppConfig()` / `isFeatureEnabled()` for entitlement reads — they call this endpoint under the hood and cache the result.
8723
8807
 
8724
8808
  **updateSettings**(collectionId: string, settingGroup: string, settings: any) → `Promise<any>`
8725
8809
  Update a specific settings group for a collection (admin endpoint). This writes through the admin endpoint, but root-level fields are still part of the public settings payload. Put confidential values under `settings.admin` if they should only be returned on admin reads.