@proveanything/smartlinks 1.15.3 → 1.15.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/appConfiguration.d.ts +90 -1
- package/dist/api/appConfiguration.js +117 -0
- package/dist/api/collection.d.ts +6 -2
- package/dist/api/collection.js +6 -2
- package/dist/cache.d.ts +19 -0
- package/dist/cache.js +25 -0
- package/dist/docs/API_SUMMARY.md +157 -11
- package/dist/docs/app-data-storage.md +2 -0
- package/dist/docs/appConfig.md +483 -0
- package/dist/docs/overview.md +20 -0
- package/dist/docs/proof-product-data-scoping.md +459 -0
- package/dist/openapi.yaml +222 -4
- package/dist/types/appConfiguration.d.ts +83 -0
- package/dist/types/collection.d.ts +11 -2
- package/dist/types/product.d.ts +37 -0
- package/dist/types/proof.d.ts +39 -3
- package/docs/API_SUMMARY.md +157 -11
- package/docs/app-data-storage.md +2 -0
- package/docs/appConfig.md +483 -0
- package/docs/overview.md +20 -0
- package/docs/proof-product-data-scoping.md +459 -0
- package/openapi.yaml +222 -4
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CollectionWidgetsResponse, GetCollectionWidgetsOptions } from "../types/appManifest";
|
|
2
|
-
import type {
|
|
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 = {}));
|
package/dist/api/collection.d.ts
CHANGED
|
@@ -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
|
|
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>;
|
package/dist/api/collection.js
CHANGED
|
@@ -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
|
|
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
|
*/
|
package/dist/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 1.15.
|
|
3
|
+
Version: 1.15.5 | Generated: 2026-07-18T14:07:37.915Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -42,6 +42,8 @@ For detailed guides on specific features:
|
|
|
42
42
|
- **[Portal Back Button](portal-back-button.md)** - `parentPath` contract for hierarchy-aware "up" navigation; `useDeepLinkSync` integration
|
|
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
|
+
- **[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
|
|
45
47
|
- **[Forms](forms.md)** - Platform-managed form definitions, submissions, and schema-driven React form UI
|
|
46
48
|
- **[App Objects: Cases, Threads & Records](app-objects.md)** - Generic app-scoped building blocks for support cases, discussions, bookings, registrations, and more
|
|
47
49
|
- **[App Records Pattern](app-records-pattern.md)** - Canonical pattern for storing per-product, per-facet, or rule-targeted app data
|
|
@@ -1375,6 +1377,81 @@ interface WidgetInstanceSummary {
|
|
|
1375
1377
|
}
|
|
1376
1378
|
```
|
|
1377
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
|
+
|
|
1378
1455
|
### appManifest
|
|
1379
1456
|
|
|
1380
1457
|
**AppBundle** (interface)
|
|
@@ -3661,13 +3738,6 @@ interface AppConfig {
|
|
|
3661
3738
|
}
|
|
3662
3739
|
```
|
|
3663
3740
|
|
|
3664
|
-
**AppsConfigResponse** (interface)
|
|
3665
|
-
```typescript
|
|
3666
|
-
interface AppsConfigResponse {
|
|
3667
|
-
apps: AppConfig[]
|
|
3668
|
-
}
|
|
3669
|
-
```
|
|
3670
|
-
|
|
3671
3741
|
**CollectionResponse** = `Collection`
|
|
3672
3742
|
|
|
3673
3743
|
**CollectionCreateRequest** = `Omit<Collection, 'id' | 'shortId'>`
|
|
@@ -6946,6 +7016,37 @@ interface ProductQueryResponse {
|
|
|
6946
7016
|
}
|
|
6947
7017
|
```
|
|
6948
7018
|
|
|
7019
|
+
**ScopedFieldOption** (interface)
|
|
7020
|
+
```typescript
|
|
7021
|
+
interface ScopedFieldOption {
|
|
7022
|
+
value: string
|
|
7023
|
+
label: string
|
|
7024
|
+
}
|
|
7025
|
+
```
|
|
7026
|
+
|
|
7027
|
+
**ScopedFieldDef** (interface)
|
|
7028
|
+
```typescript
|
|
7029
|
+
interface ScopedFieldDef {
|
|
7030
|
+
key: string
|
|
7031
|
+
label: string
|
|
7032
|
+
type: ScopedFieldType
|
|
7033
|
+
required?: boolean
|
|
7034
|
+
placeholder?: string
|
|
7035
|
+
help?: string
|
|
7036
|
+
options?: ScopedFieldOption[]
|
|
7037
|
+
defaultValue?: JsonValue
|
|
7038
|
+
showInTable?: boolean
|
|
7039
|
+
showInDetail?: boolean
|
|
7040
|
+
}
|
|
7041
|
+
```
|
|
7042
|
+
|
|
7043
|
+
**ProductFieldsConfig** (interface)
|
|
7044
|
+
```typescript
|
|
7045
|
+
interface ProductFieldsConfig {
|
|
7046
|
+
fields: ProductFieldDef[]
|
|
7047
|
+
}
|
|
7048
|
+
```
|
|
7049
|
+
|
|
6949
7050
|
**JsonPrimitive** = `string | number | boolean | null`
|
|
6950
7051
|
|
|
6951
7052
|
**JsonValue** = ``
|
|
@@ -6962,8 +7063,23 @@ interface ProductQueryResponse {
|
|
|
6962
7063
|
|
|
6963
7064
|
**ProductUpdateRequest** = `Partial<Omit<ProductWriteInput, 'id'>>`
|
|
6964
7065
|
|
|
7066
|
+
**ScopedFieldType** = ``
|
|
7067
|
+
|
|
7068
|
+
**ProductFieldScope** = `'public' | 'admin'`
|
|
7069
|
+
|
|
7070
|
+
**ProductFieldDef** = `ScopedFieldDef & { scope?: ProductFieldScope }`
|
|
7071
|
+
|
|
6965
7072
|
### proof
|
|
6966
7073
|
|
|
7074
|
+
**ProofValues** (interface)
|
|
7075
|
+
```typescript
|
|
7076
|
+
interface ProofValues {
|
|
7077
|
+
[key: string]: JsonValue | Record<string, JsonValue> | Record<string, Record<string, JsonValue>> | undefined
|
|
7078
|
+
owner?: Record<string, JsonValue>
|
|
7079
|
+
personal?: Record<string, Record<string, JsonValue>>
|
|
7080
|
+
}
|
|
7081
|
+
```
|
|
7082
|
+
|
|
6967
7083
|
**Proof** (interface)
|
|
6968
7084
|
```typescript
|
|
6969
7085
|
interface Proof {
|
|
@@ -6975,25 +7091,40 @@ interface Proof {
|
|
|
6975
7091
|
userId: string
|
|
6976
7092
|
claimable?: boolean
|
|
6977
7093
|
virtual?: boolean
|
|
6978
|
-
|
|
7094
|
+
data?: Record<string, JsonValue>
|
|
7095
|
+
admin?: Record<string, JsonValue>
|
|
7096
|
+
values: ProofValues
|
|
6979
7097
|
}
|
|
6980
7098
|
```
|
|
6981
7099
|
|
|
6982
7100
|
**ProofCreateRequest** (interface)
|
|
6983
7101
|
```typescript
|
|
6984
7102
|
interface ProofCreateRequest {
|
|
6985
|
-
values:
|
|
7103
|
+
values: ProofValues
|
|
7104
|
+
data?: Record<string, JsonValue>
|
|
7105
|
+
admin?: Record<string, JsonValue>
|
|
6986
7106
|
claimable?: boolean
|
|
6987
7107
|
virtual?: boolean
|
|
6988
7108
|
}
|
|
6989
7109
|
```
|
|
6990
7110
|
|
|
7111
|
+
**ProofFieldsConfig** (interface)
|
|
7112
|
+
```typescript
|
|
7113
|
+
interface ProofFieldsConfig {
|
|
7114
|
+
fields: ProofFieldDef[]
|
|
7115
|
+
}
|
|
7116
|
+
```
|
|
7117
|
+
|
|
6991
7118
|
**ProofResponse** = `Proof`
|
|
6992
7119
|
|
|
6993
7120
|
**ProofUpdateRequest** = `Partial<ProofCreateRequest>`
|
|
6994
7121
|
|
|
6995
7122
|
**ProofClaimRequest** = `Record<string, any>`
|
|
6996
7123
|
|
|
7124
|
+
**ProofFieldScope** = `'public' | 'owner' | 'personal' | 'admin'`
|
|
7125
|
+
|
|
7126
|
+
**ProofFieldDef** = `ScopedFieldDef & { scope?: ProofFieldScope }`
|
|
7127
|
+
|
|
6997
7128
|
### qr
|
|
6998
7129
|
|
|
6999
7130
|
**QrShortCodeLookupResponse** (interface)
|
|
@@ -8109,6 +8240,21 @@ Delete a keyed data item by ID within a scope. Requires admin authentication. ``
|
|
|
8109
8240
|
options?: GetCollectionWidgetsOptions) → `Promise<CollectionWidgetsResponse>`
|
|
8110
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 }); ```
|
|
8111
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
|
+
|
|
8112
8258
|
### asset
|
|
8113
8259
|
|
|
8114
8260
|
**upload**(options: UploadAssetOptions) → `Promise<Asset>`
|
|
@@ -8657,7 +8803,7 @@ Get the managed-certificate status for a collection's custom domain (admin only)
|
|
|
8657
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`.
|
|
8658
8804
|
|
|
8659
8805
|
**getAppsConfig**(collectionId: string) → `Promise<AppsConfigResponse>`
|
|
8660
|
-
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.
|
|
8661
8807
|
|
|
8662
8808
|
**updateSettings**(collectionId: string, settingGroup: string, settings: any) → `Promise<any>`
|
|
8663
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.
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
The SmartLinks platform provides two distinct types of data storage for apps:
|
|
4
4
|
|
|
5
|
+
> **Not what you're looking for?** This guide covers app-owned config/records scoped to a collection or product. For the core product/proof documents themselves — `product.data`/`.admin`, `proof.data`/`.admin`/`.values` (owner/personal) — see [proof-product-data-scoping.md](proof-product-data-scoping.md).
|
|
6
|
+
|
|
5
7
|
## Choose the Right Model First
|
|
6
8
|
|
|
7
9
|
There are now two different families of flexible app data in SmartLinks, and they solve different problems:
|