@proveanything/smartlinks 1.11.8 → 1.11.10

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,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.11.8 | Generated: 2026-05-03T08:12:27.170Z
3
+ Version: 1.11.10 | Generated: 2026-05-03T11:56:35.287Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -2695,6 +2695,7 @@ interface AccountInfoResponse {
2695
2695
  sites?: Record<string, boolean>
2696
2696
  whitelabel?: Record<string, any>
2697
2697
  location?: AuthLocation | null
2698
+ prefs?: Record<string, any>
2698
2699
  [key: string]: any
2699
2700
  }
2700
2701
  ```
@@ -6,7 +6,8 @@
6
6
  >
7
7
  > Status: **standard**. New apps MUST follow this contract; existing apps SHOULD migrate.
8
8
  >
9
- > SDK: `@proveanything/smartlinks` ≥ **1.11**, `@proveanything/smartlinks-utils-ui` ≥ **0.7.6**.
9
+ > SDK: `@proveanything/smartlinks` ≥ **1.11**.
10
+ > Admin shell (React only): `@proveanything/smartlinks-utils-ui` ≥ **0.7.6** — required for the admin side if using the React shell; not needed in public widgets.
10
11
 
11
12
  ---
12
13
 
@@ -22,10 +23,10 @@ Every records-based app fits into a 2×2:
22
23
  That choice drives **three** things and nothing else:
23
24
 
24
25
  1. **Manifest:** `cardinality: 'singleton' | 'collection'` and `allowFacetRules: boolean`.
25
- 2. **Admin (`<RecordsAdminShell>`):** pass `cardinality` + include `'rule'` in `scopes` if `allowFacetRules`.
26
- 3. **Public hook:** `useResolvedRecord` (best match) **or** `useCollectedRecords` / `useResolveAllRecords` (all matches).
26
+ 2. **Admin:** use `<RecordsAdminShell>` (React) or call the admin SDK functions directly. Pass `cardinality` + include `'rule'` in `scopes` if `allowFacetRules`.
27
+ 3. **Public widget:** call `app.records.match()` (best match / singleton) or `app.records.resolveAll()` (all matches / collection). These are plain SDK calls with no framework dependency.
27
28
 
28
- If you only remember one rule: **never write your own resolver**. The shell, the hooks, and the SDK already agree on resolution order. Reimplementing it is how apps drift.
29
+ If you only remember one rule: **never write your own resolution loop**. The server already walks the chain correctly calling `match()` or `resolveAll()` is the entire public-side implementation.
29
30
 
30
31
  ---
31
32
 
@@ -115,7 +116,11 @@ Declare each record type once in `app.admin.json`. The shell and the platform re
115
116
 
116
117
  ---
117
118
 
118
- ## 4. Admin side — `<RecordsAdminShell>` (the only thing you should be writing)
119
+ ## 4. Admin side
120
+
121
+ > The admin shell and rule editor are part of `@proveanything/smartlinks-utils-ui`, which is a **React-only library**. It is only needed in admin dashboards — never import it in a public widget.
122
+
123
+ ### `<RecordsAdminShell>` (React)
119
124
 
120
125
  The shell owns: scope tabs, browser pane, rule editor, save/discard, dirty navigation, inheritance markers, deletion, CSV, bulk apply, deep linking. **You only supply the editor for one record's `data`.**
121
126
 
@@ -170,7 +175,18 @@ import { FacetRuleEditor } from '@proveanything/smartlinks-utils-ui/facet-rule-e
170
175
 
171
176
  ---
172
177
 
173
- ## 5. Public side — pick the right hook (this is where apps go wrong)
178
+ ## 5. Public side
179
+
180
+ > **Do not import `@proveanything/smartlinks-utils-ui` in a public widget.** It is a React admin library. Public widgets only need `@proveanything/smartlinks`.
181
+
182
+ The SDK is framework-agnostic. Public widgets call two endpoints depending on cardinality:
183
+
184
+ | Cardinality | Call | What it does |
185
+ |---|---|---|
186
+ | **Singleton** (one answer) | `app.records.match()` | Server walks the chain, returns the best-matching record |
187
+ | **Collection** (all answers) | `app.records.resolveAll()` | Server walks the chain, returns every matching record |
188
+
189
+ Neither call requires React or any other framework — wrap them in whatever async pattern your widget uses.
174
190
 
175
191
  > **Admin vs public — the rule is simple:**
176
192
  >
@@ -189,65 +205,48 @@ import { FacetRuleEditor } from '@proveanything/smartlinks-utils-ui/facet-rule-e
189
205
  > | `app.records.bulkDelete()` | ❌ admin only — no public path | ✅ |
190
206
  > | `app.records.restore()` | ❌ admin only — no public path | ✅ |
191
207
  > | `app.records.previewRule()` | ❌ admin only — no public path | ✅ |
192
- >
193
- > The `admin` parameter on `match()` and `resolveAll()` defaults to `false`, so public widgets that omit it are fine. **Never hardcode `true` in a public widget.** The hooks below always use the public path and are the recommended approach — prefer them over raw SDK calls in widget code.
194
-
195
- There is exactly **one decision** to make on the public side, and it follows from the manifest's `cardinality`:
196
208
 
197
- ### 5a. Singleton `useResolvedRecord` (best match wins)
209
+ ### 5a. Singleton `app.records.match()` (best match wins)
198
210
 
199
- Use this when the app shows **one** answer for the current product (ingredients, nutrition, washing instructions, warranty terms).
211
+ Use when the widget shows **one** answer for the current product (ingredients, nutrition, warranty terms, washing instructions).
200
212
 
201
- ```tsx
213
+ ```ts
202
214
  import * as SL from '@proveanything/smartlinks';
203
- import { useResolvedRecord } from '@proveanything/smartlinks-utils-ui/records-admin';
204
-
205
- const { data, source, sourceRef, matchedAt, matchedRule, isLoading } =
206
- useResolvedRecord<IngredientsConfig>({
207
- SL,
208
- appId,
209
- collectionId,
210
- recordType: 'ingredients',
211
- productId,
212
- variantId, // optional
213
- batchId, // optional
214
- proofId, // optional
215
- });
216
- ```
217
215
 
218
- The resolver walks `proof → batch → variant → product → rule → facet → collection` and returns the **first match**, plus `matchedAt` so you can render _"From the product record"_ vs _"Matched by rule"_ badges if useful.
216
+ const result = await SL.app.records.match(collectionId, appId, {
217
+ target: { productId, variantId, batchId }, // pass whatever context you have
218
+ strategy: 'best',
219
+ recordType: 'ingredients',
220
+ });
221
+
222
+ // result.best.ingredients → the single highest-specificity record
223
+ // result.best.ingredients.matchedAt → 'product' | 'rule' | 'facet' | 'collection' | …
224
+ ```
219
225
 
220
- > Wrap this once in your app (e.g. `useResolvedIngredientSet`) so the rest of the codebase reads `{ data, isLoading }` and never sees the resolver.
226
+ The server walks `proof batch variant product rule facet collection` and returns the first match.
221
227
 
222
- ### 5b. Collection `useCollectedRecords` (every match, ordered)
228
+ ### 5b. Collection `app.records.resolveAll()` (every match, ordered)
223
229
 
224
- Use this when the app shows **many** answers aggregated across the chain (FAQs, recipes, SOPs, care tips). Most-specific first by default; pass `sort` to override.
230
+ Use when the widget shows **many** answers across the chain (FAQs, recipes, care tips, SOPs).
225
231
 
226
- ```tsx
227
- import { useCollectedRecords } from '@proveanything/smartlinks-utils-ui/records-admin';
228
-
229
- const { items, isLoading } = useCollectedRecords<FaqEntry>({
230
- SL,
231
- appId,
232
- collectionId,
233
- recordType: 'faq',
234
- productId,
235
- // sort: { kind: 'field', field: 'order', direction: 'asc' },
232
+ ```ts
233
+ const result = await SL.app.records.resolveAll(collectionId, appId, {
234
+ target: { productId },
235
+ recordTypes: ['faq'],
236
236
  });
237
237
 
238
- // items: CollectedRecord<FaqEntry>[] each has { data, scope, ref, depth }
238
+ // result.records → AppRecord[] sorted most-specific first
239
+ // each record has .matchedAt, .data, .scope
239
240
  ```
240
241
 
241
- ### 5c. Multi-type aggregate `useResolveAllRecords`
242
-
243
- When you need every record of every type that applies to a context (rare; mostly executors and SEO surfaces).
242
+ ### 5c. Multi-type `app.records.resolveAll()` with multiple record types
244
243
 
245
- ```tsx
246
- import { useResolveAllRecords } from '@proveanything/smartlinks-utils-ui/records-admin';
244
+ When you need records of several types in one call (rare; executors, SEO surfaces):
247
245
 
248
- const { entries, isLoading } = useResolveAllRecords({
249
- SL, collectionId, appId,
250
- context: { productId, facets: { brand: 'acme', category: ['bread'] } },
246
+ ```ts
247
+ const result = await SL.app.records.resolveAll(collectionId, appId, {
248
+ target: { productId, facets: { brand: 'acme' } },
249
+ recordTypes: ['ingredients', 'nutrition', 'allergens'],
251
250
  });
252
251
  ```
253
252
 
@@ -255,15 +254,15 @@ const { entries, isLoading } = useResolveAllRecords({
255
254
 
256
255
  | ❌ Anti-pattern | ✅ Do this instead |
257
256
  | ---------------------------------------------------------- | ----------------------------------------------------------------- |
258
- | Calling `SL.app.records.list()` and filtering client-side | `useResolvedRecord` (singleton) or `useCollectedRecords` (collection). The server already knows the chain. |
259
- | Calling `SL.app.records.list(…, true)` from a public widget | Omit the `admin` flag (defaults to `false`). `list()` has a public path just don't pass `true`. |
260
- | Calling `SL.app.records.match(…, true)` from a public widget | Omit the `admin` flag (defaults to `false`) or use `useResolvedRecord` / `useCollectedRecords`. |
261
- | Calling `SL.app.records.resolveAll(…, true)` from a public widget | Omit the `admin` flag (defaults to `false`) or use `useResolveAllRecords`. |
262
- | Calling `upsert`, `bulkUpsert`, `bulkDelete`, or `previewRule` from widget code | Those are admin-only. Widget code reads data — it never writes records directly. |
263
- | Walking the chain by hand with multiple `getConfig` calls | One hook call. The resolver is tested, cached, and includes rules. |
257
+ | Importing anything from `@proveanything/smartlinks-utils-ui` in a public widget | That package is React-only and admin-only. Public widgets only use `@proveanything/smartlinks`. |
258
+ | Calling `SL.app.records.list()` and filtering client-side | `app.records.match()` (singleton) or `app.records.resolveAll()` (collection). The server walks the chain. |
259
+ | Calling `SL.app.records.list(…, true)` from a public widget | Omit the `admin` flag — it defaults to `false`. |
260
+ | Calling `SL.app.records.match(…, true)` from a public widget | Omit the `admin` flag — it defaults to `false`. |
261
+ | Calling `SL.app.records.resolveAll(…, true)` from a public widget | Omit the `admin` flag — it defaults to `false`. |
262
+ | Calling `upsert`, `bulkUpsert`, `bulkDelete`, or `previewRule` from widget code | Those are admin-only. Widget code reads data; it never writes records. |
263
+ | Walking the chain by hand with multiple `get` / `list` calls | One `match()` or `resolveAll()` call. The server handles the resolution order including rules. |
264
264
  | Treating `facet:key:value` refs as the rule mechanism | Use `facetRule` (`{ all: [{ facetKey, anyOf: [...] }] }`). Multi-condition, scored by specificity. |
265
265
  | Reading `matchedAt === 'global'` | There is no `'global'`. The top of the chain is `'collection'`. |
266
- | Building your own `<FacetRuleEditor>` | Use the one from `@proveanything/smartlinks-utils-ui/facet-rule-editor`. |
267
266
 
268
267
  ---
269
268
 
@@ -305,24 +304,36 @@ interface EditorContext<TData> {
305
304
  2. **Add `cardinality` and `allowFacetRules`** to every entry under `records` in `app.admin.json`.
306
305
  3. **Add `'rule'` (and `'collection'` if missing) to `scopes`** wherever `allowFacetRules: true`.
307
306
  4. **Pass `cardinality`** to `<RecordsAdminShell>`.
308
- 5. **Replace any handwritten chain walking** with `useResolvedRecord` (singleton) or `useCollectedRecords` (collection).
309
- 6. **Delete any code that constructs `facet:key:value` refs** for matching. Use `facetRule` via the shell or `<FacetRuleEditor>`.
307
+ 5. **Replace any handwritten chain walking** with `app.records.match()` (singleton) or `app.records.resolveAll()` (collection). If you are using React, the `useResolvedRecord` / `useCollectedRecords` hooks from `@proveanything/smartlinks-utils-ui` wrap these calls — but they are **admin-side React helpers**, not for public widgets.
308
+ 6. **Delete any code that constructs `facet:key:value` refs** for matching. Use `facetRule` via the shell or `<FacetRuleEditor>` (React admin) or pass `facetRule` directly in `upsert()` calls.
310
309
  7. **Search for the word "global"** in your code/docs and rename to "collection" — this is the most common source of confusion.
311
310
 
312
311
  ---
313
312
 
314
313
  ## 8. Where the canonical exports live
315
314
 
316
- | Need | Import from |
317
- | ----------------------------- | ---------------------------------------------------------------------- |
318
- | Admin shell | `@proveanything/smartlinks-utils-ui/records-admin` `RecordsAdminShell` |
319
- | Standalone rule editor | `@proveanything/smartlinks-utils-ui/facet-rule-editor` → `FacetRuleEditor` |
315
+ ### Public widgets (any framework)
316
+
317
+ | Need | Import from |
318
+ | ---- | ----------- |
319
+ | Best-match resolution (singleton) | `@proveanything/smartlinks` → `SL.app.records.match()` |
320
+ | All-matches resolution (collection) | `@proveanything/smartlinks` → `SL.app.records.resolveAll()` |
321
+ | Record CRUD (public path) | `@proveanything/smartlinks` → `SL.app.records.{list, get, create, update, remove, aggregate}` |
322
+
323
+ ### Admin dashboards (React)
324
+
325
+ > All of the following are from `@proveanything/smartlinks-utils-ui`, a **React-only** package. Do not use in public widgets.
326
+
327
+ | Need | Import from |
328
+ | ---- | ----------- |
329
+ | Admin shell | `@proveanything/smartlinks-utils-ui/records-admin` → `RecordsAdminShell` |
330
+ | Standalone rule editor | `@proveanything/smartlinks-utils-ui/facet-rule-editor` → `FacetRuleEditor` |
320
331
  | Conditions editor (non-facet) | `@proveanything/smartlinks-utils-ui/conditions-editor` → `ConditionsEditor` |
321
- | Best-match resolver hook | `@proveanything/smartlinks-utils-ui/records-admin` → `useResolvedRecord` |
322
- | All-matches hook | `@proveanything/smartlinks-utils-ui/records-admin` → `useCollectedRecords` |
323
- | Multi-type aggregate hook | `@proveanything/smartlinks-utils-ui/records-admin` → `useResolveAllRecords` |
324
- | Rule preview ("matches N") | `@proveanything/smartlinks-utils-ui/records-admin` → `useRulePreview` |
325
- | Server-side record CRUD | `@proveanything/smartlinks` → `SL.app.records.{upsert, list, remove, match, resolveAll}` |
332
+ | Best-match hook (React convenience wrapper) | `@proveanything/smartlinks-utils-ui/records-admin` → `useResolvedRecord` |
333
+ | All-matches hook (React convenience wrapper) | `@proveanything/smartlinks-utils-ui/records-admin` → `useCollectedRecords` |
334
+ | Multi-type hook (React convenience wrapper) | `@proveanything/smartlinks-utils-ui/records-admin` → `useResolveAllRecords` |
335
+ | Rule preview hook | `@proveanything/smartlinks-utils-ui/records-admin` → `useRulePreview` |
336
+ | Admin record CRUD (admin path) | `@proveanything/smartlinks` → `SL.app.records.{upsert, bulkUpsert, bulkDelete, restore, previewRule}` |
326
337
 
327
338
  ---
328
339
 
package/dist/openapi.yaml CHANGED
@@ -16919,6 +16919,9 @@ components:
16919
16919
  additionalProperties: true
16920
16920
  location:
16921
16921
  $ref: "#/components/schemas/AuthLocation"
16922
+ prefs:
16923
+ type: object
16924
+ additionalProperties: true
16922
16925
  required:
16923
16926
  - id
16924
16927
  - uid
@@ -79,5 +79,6 @@ export interface AccountInfoResponse {
79
79
  sites?: Record<string, boolean>;
80
80
  whitelabel?: Record<string, any>;
81
81
  location?: AuthLocation | null;
82
+ prefs?: Record<string, any>;
82
83
  [key: string]: any;
83
84
  }
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.11.8 | Generated: 2026-05-03T08:12:27.170Z
3
+ Version: 1.11.10 | Generated: 2026-05-03T11:56:35.287Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -2695,6 +2695,7 @@ interface AccountInfoResponse {
2695
2695
  sites?: Record<string, boolean>
2696
2696
  whitelabel?: Record<string, any>
2697
2697
  location?: AuthLocation | null
2698
+ prefs?: Record<string, any>
2698
2699
  [key: string]: any
2699
2700
  }
2700
2701
  ```
@@ -6,7 +6,8 @@
6
6
  >
7
7
  > Status: **standard**. New apps MUST follow this contract; existing apps SHOULD migrate.
8
8
  >
9
- > SDK: `@proveanything/smartlinks` ≥ **1.11**, `@proveanything/smartlinks-utils-ui` ≥ **0.7.6**.
9
+ > SDK: `@proveanything/smartlinks` ≥ **1.11**.
10
+ > Admin shell (React only): `@proveanything/smartlinks-utils-ui` ≥ **0.7.6** — required for the admin side if using the React shell; not needed in public widgets.
10
11
 
11
12
  ---
12
13
 
@@ -22,10 +23,10 @@ Every records-based app fits into a 2×2:
22
23
  That choice drives **three** things and nothing else:
23
24
 
24
25
  1. **Manifest:** `cardinality: 'singleton' | 'collection'` and `allowFacetRules: boolean`.
25
- 2. **Admin (`<RecordsAdminShell>`):** pass `cardinality` + include `'rule'` in `scopes` if `allowFacetRules`.
26
- 3. **Public hook:** `useResolvedRecord` (best match) **or** `useCollectedRecords` / `useResolveAllRecords` (all matches).
26
+ 2. **Admin:** use `<RecordsAdminShell>` (React) or call the admin SDK functions directly. Pass `cardinality` + include `'rule'` in `scopes` if `allowFacetRules`.
27
+ 3. **Public widget:** call `app.records.match()` (best match / singleton) or `app.records.resolveAll()` (all matches / collection). These are plain SDK calls with no framework dependency.
27
28
 
28
- If you only remember one rule: **never write your own resolver**. The shell, the hooks, and the SDK already agree on resolution order. Reimplementing it is how apps drift.
29
+ If you only remember one rule: **never write your own resolution loop**. The server already walks the chain correctly calling `match()` or `resolveAll()` is the entire public-side implementation.
29
30
 
30
31
  ---
31
32
 
@@ -115,7 +116,11 @@ Declare each record type once in `app.admin.json`. The shell and the platform re
115
116
 
116
117
  ---
117
118
 
118
- ## 4. Admin side — `<RecordsAdminShell>` (the only thing you should be writing)
119
+ ## 4. Admin side
120
+
121
+ > The admin shell and rule editor are part of `@proveanything/smartlinks-utils-ui`, which is a **React-only library**. It is only needed in admin dashboards — never import it in a public widget.
122
+
123
+ ### `<RecordsAdminShell>` (React)
119
124
 
120
125
  The shell owns: scope tabs, browser pane, rule editor, save/discard, dirty navigation, inheritance markers, deletion, CSV, bulk apply, deep linking. **You only supply the editor for one record's `data`.**
121
126
 
@@ -170,7 +175,18 @@ import { FacetRuleEditor } from '@proveanything/smartlinks-utils-ui/facet-rule-e
170
175
 
171
176
  ---
172
177
 
173
- ## 5. Public side — pick the right hook (this is where apps go wrong)
178
+ ## 5. Public side
179
+
180
+ > **Do not import `@proveanything/smartlinks-utils-ui` in a public widget.** It is a React admin library. Public widgets only need `@proveanything/smartlinks`.
181
+
182
+ The SDK is framework-agnostic. Public widgets call two endpoints depending on cardinality:
183
+
184
+ | Cardinality | Call | What it does |
185
+ |---|---|---|
186
+ | **Singleton** (one answer) | `app.records.match()` | Server walks the chain, returns the best-matching record |
187
+ | **Collection** (all answers) | `app.records.resolveAll()` | Server walks the chain, returns every matching record |
188
+
189
+ Neither call requires React or any other framework — wrap them in whatever async pattern your widget uses.
174
190
 
175
191
  > **Admin vs public — the rule is simple:**
176
192
  >
@@ -189,65 +205,48 @@ import { FacetRuleEditor } from '@proveanything/smartlinks-utils-ui/facet-rule-e
189
205
  > | `app.records.bulkDelete()` | ❌ admin only — no public path | ✅ |
190
206
  > | `app.records.restore()` | ❌ admin only — no public path | ✅ |
191
207
  > | `app.records.previewRule()` | ❌ admin only — no public path | ✅ |
192
- >
193
- > The `admin` parameter on `match()` and `resolveAll()` defaults to `false`, so public widgets that omit it are fine. **Never hardcode `true` in a public widget.** The hooks below always use the public path and are the recommended approach — prefer them over raw SDK calls in widget code.
194
-
195
- There is exactly **one decision** to make on the public side, and it follows from the manifest's `cardinality`:
196
208
 
197
- ### 5a. Singleton `useResolvedRecord` (best match wins)
209
+ ### 5a. Singleton `app.records.match()` (best match wins)
198
210
 
199
- Use this when the app shows **one** answer for the current product (ingredients, nutrition, washing instructions, warranty terms).
211
+ Use when the widget shows **one** answer for the current product (ingredients, nutrition, warranty terms, washing instructions).
200
212
 
201
- ```tsx
213
+ ```ts
202
214
  import * as SL from '@proveanything/smartlinks';
203
- import { useResolvedRecord } from '@proveanything/smartlinks-utils-ui/records-admin';
204
-
205
- const { data, source, sourceRef, matchedAt, matchedRule, isLoading } =
206
- useResolvedRecord<IngredientsConfig>({
207
- SL,
208
- appId,
209
- collectionId,
210
- recordType: 'ingredients',
211
- productId,
212
- variantId, // optional
213
- batchId, // optional
214
- proofId, // optional
215
- });
216
- ```
217
215
 
218
- The resolver walks `proof → batch → variant → product → rule → facet → collection` and returns the **first match**, plus `matchedAt` so you can render _"From the product record"_ vs _"Matched by rule"_ badges if useful.
216
+ const result = await SL.app.records.match(collectionId, appId, {
217
+ target: { productId, variantId, batchId }, // pass whatever context you have
218
+ strategy: 'best',
219
+ recordType: 'ingredients',
220
+ });
221
+
222
+ // result.best.ingredients → the single highest-specificity record
223
+ // result.best.ingredients.matchedAt → 'product' | 'rule' | 'facet' | 'collection' | …
224
+ ```
219
225
 
220
- > Wrap this once in your app (e.g. `useResolvedIngredientSet`) so the rest of the codebase reads `{ data, isLoading }` and never sees the resolver.
226
+ The server walks `proof batch variant product rule facet collection` and returns the first match.
221
227
 
222
- ### 5b. Collection `useCollectedRecords` (every match, ordered)
228
+ ### 5b. Collection `app.records.resolveAll()` (every match, ordered)
223
229
 
224
- Use this when the app shows **many** answers aggregated across the chain (FAQs, recipes, SOPs, care tips). Most-specific first by default; pass `sort` to override.
230
+ Use when the widget shows **many** answers across the chain (FAQs, recipes, care tips, SOPs).
225
231
 
226
- ```tsx
227
- import { useCollectedRecords } from '@proveanything/smartlinks-utils-ui/records-admin';
228
-
229
- const { items, isLoading } = useCollectedRecords<FaqEntry>({
230
- SL,
231
- appId,
232
- collectionId,
233
- recordType: 'faq',
234
- productId,
235
- // sort: { kind: 'field', field: 'order', direction: 'asc' },
232
+ ```ts
233
+ const result = await SL.app.records.resolveAll(collectionId, appId, {
234
+ target: { productId },
235
+ recordTypes: ['faq'],
236
236
  });
237
237
 
238
- // items: CollectedRecord<FaqEntry>[] each has { data, scope, ref, depth }
238
+ // result.records → AppRecord[] sorted most-specific first
239
+ // each record has .matchedAt, .data, .scope
239
240
  ```
240
241
 
241
- ### 5c. Multi-type aggregate `useResolveAllRecords`
242
-
243
- When you need every record of every type that applies to a context (rare; mostly executors and SEO surfaces).
242
+ ### 5c. Multi-type `app.records.resolveAll()` with multiple record types
244
243
 
245
- ```tsx
246
- import { useResolveAllRecords } from '@proveanything/smartlinks-utils-ui/records-admin';
244
+ When you need records of several types in one call (rare; executors, SEO surfaces):
247
245
 
248
- const { entries, isLoading } = useResolveAllRecords({
249
- SL, collectionId, appId,
250
- context: { productId, facets: { brand: 'acme', category: ['bread'] } },
246
+ ```ts
247
+ const result = await SL.app.records.resolveAll(collectionId, appId, {
248
+ target: { productId, facets: { brand: 'acme' } },
249
+ recordTypes: ['ingredients', 'nutrition', 'allergens'],
251
250
  });
252
251
  ```
253
252
 
@@ -255,15 +254,15 @@ const { entries, isLoading } = useResolveAllRecords({
255
254
 
256
255
  | ❌ Anti-pattern | ✅ Do this instead |
257
256
  | ---------------------------------------------------------- | ----------------------------------------------------------------- |
258
- | Calling `SL.app.records.list()` and filtering client-side | `useResolvedRecord` (singleton) or `useCollectedRecords` (collection). The server already knows the chain. |
259
- | Calling `SL.app.records.list(…, true)` from a public widget | Omit the `admin` flag (defaults to `false`). `list()` has a public path just don't pass `true`. |
260
- | Calling `SL.app.records.match(…, true)` from a public widget | Omit the `admin` flag (defaults to `false`) or use `useResolvedRecord` / `useCollectedRecords`. |
261
- | Calling `SL.app.records.resolveAll(…, true)` from a public widget | Omit the `admin` flag (defaults to `false`) or use `useResolveAllRecords`. |
262
- | Calling `upsert`, `bulkUpsert`, `bulkDelete`, or `previewRule` from widget code | Those are admin-only. Widget code reads data — it never writes records directly. |
263
- | Walking the chain by hand with multiple `getConfig` calls | One hook call. The resolver is tested, cached, and includes rules. |
257
+ | Importing anything from `@proveanything/smartlinks-utils-ui` in a public widget | That package is React-only and admin-only. Public widgets only use `@proveanything/smartlinks`. |
258
+ | Calling `SL.app.records.list()` and filtering client-side | `app.records.match()` (singleton) or `app.records.resolveAll()` (collection). The server walks the chain. |
259
+ | Calling `SL.app.records.list(…, true)` from a public widget | Omit the `admin` flag — it defaults to `false`. |
260
+ | Calling `SL.app.records.match(…, true)` from a public widget | Omit the `admin` flag — it defaults to `false`. |
261
+ | Calling `SL.app.records.resolveAll(…, true)` from a public widget | Omit the `admin` flag — it defaults to `false`. |
262
+ | Calling `upsert`, `bulkUpsert`, `bulkDelete`, or `previewRule` from widget code | Those are admin-only. Widget code reads data; it never writes records. |
263
+ | Walking the chain by hand with multiple `get` / `list` calls | One `match()` or `resolveAll()` call. The server handles the resolution order including rules. |
264
264
  | Treating `facet:key:value` refs as the rule mechanism | Use `facetRule` (`{ all: [{ facetKey, anyOf: [...] }] }`). Multi-condition, scored by specificity. |
265
265
  | Reading `matchedAt === 'global'` | There is no `'global'`. The top of the chain is `'collection'`. |
266
- | Building your own `<FacetRuleEditor>` | Use the one from `@proveanything/smartlinks-utils-ui/facet-rule-editor`. |
267
266
 
268
267
  ---
269
268
 
@@ -305,24 +304,36 @@ interface EditorContext<TData> {
305
304
  2. **Add `cardinality` and `allowFacetRules`** to every entry under `records` in `app.admin.json`.
306
305
  3. **Add `'rule'` (and `'collection'` if missing) to `scopes`** wherever `allowFacetRules: true`.
307
306
  4. **Pass `cardinality`** to `<RecordsAdminShell>`.
308
- 5. **Replace any handwritten chain walking** with `useResolvedRecord` (singleton) or `useCollectedRecords` (collection).
309
- 6. **Delete any code that constructs `facet:key:value` refs** for matching. Use `facetRule` via the shell or `<FacetRuleEditor>`.
307
+ 5. **Replace any handwritten chain walking** with `app.records.match()` (singleton) or `app.records.resolveAll()` (collection). If you are using React, the `useResolvedRecord` / `useCollectedRecords` hooks from `@proveanything/smartlinks-utils-ui` wrap these calls — but they are **admin-side React helpers**, not for public widgets.
308
+ 6. **Delete any code that constructs `facet:key:value` refs** for matching. Use `facetRule` via the shell or `<FacetRuleEditor>` (React admin) or pass `facetRule` directly in `upsert()` calls.
310
309
  7. **Search for the word "global"** in your code/docs and rename to "collection" — this is the most common source of confusion.
311
310
 
312
311
  ---
313
312
 
314
313
  ## 8. Where the canonical exports live
315
314
 
316
- | Need | Import from |
317
- | ----------------------------- | ---------------------------------------------------------------------- |
318
- | Admin shell | `@proveanything/smartlinks-utils-ui/records-admin` `RecordsAdminShell` |
319
- | Standalone rule editor | `@proveanything/smartlinks-utils-ui/facet-rule-editor` → `FacetRuleEditor` |
315
+ ### Public widgets (any framework)
316
+
317
+ | Need | Import from |
318
+ | ---- | ----------- |
319
+ | Best-match resolution (singleton) | `@proveanything/smartlinks` → `SL.app.records.match()` |
320
+ | All-matches resolution (collection) | `@proveanything/smartlinks` → `SL.app.records.resolveAll()` |
321
+ | Record CRUD (public path) | `@proveanything/smartlinks` → `SL.app.records.{list, get, create, update, remove, aggregate}` |
322
+
323
+ ### Admin dashboards (React)
324
+
325
+ > All of the following are from `@proveanything/smartlinks-utils-ui`, a **React-only** package. Do not use in public widgets.
326
+
327
+ | Need | Import from |
328
+ | ---- | ----------- |
329
+ | Admin shell | `@proveanything/smartlinks-utils-ui/records-admin` → `RecordsAdminShell` |
330
+ | Standalone rule editor | `@proveanything/smartlinks-utils-ui/facet-rule-editor` → `FacetRuleEditor` |
320
331
  | Conditions editor (non-facet) | `@proveanything/smartlinks-utils-ui/conditions-editor` → `ConditionsEditor` |
321
- | Best-match resolver hook | `@proveanything/smartlinks-utils-ui/records-admin` → `useResolvedRecord` |
322
- | All-matches hook | `@proveanything/smartlinks-utils-ui/records-admin` → `useCollectedRecords` |
323
- | Multi-type aggregate hook | `@proveanything/smartlinks-utils-ui/records-admin` → `useResolveAllRecords` |
324
- | Rule preview ("matches N") | `@proveanything/smartlinks-utils-ui/records-admin` → `useRulePreview` |
325
- | Server-side record CRUD | `@proveanything/smartlinks` → `SL.app.records.{upsert, list, remove, match, resolveAll}` |
332
+ | Best-match hook (React convenience wrapper) | `@proveanything/smartlinks-utils-ui/records-admin` → `useResolvedRecord` |
333
+ | All-matches hook (React convenience wrapper) | `@proveanything/smartlinks-utils-ui/records-admin` → `useCollectedRecords` |
334
+ | Multi-type hook (React convenience wrapper) | `@proveanything/smartlinks-utils-ui/records-admin` → `useResolveAllRecords` |
335
+ | Rule preview hook | `@proveanything/smartlinks-utils-ui/records-admin` → `useRulePreview` |
336
+ | Admin record CRUD (admin path) | `@proveanything/smartlinks` → `SL.app.records.{upsert, bulkUpsert, bulkDelete, restore, previewRule}` |
326
337
 
327
338
  ---
328
339
 
package/openapi.yaml CHANGED
@@ -16919,6 +16919,9 @@ components:
16919
16919
  additionalProperties: true
16920
16920
  location:
16921
16921
  $ref: "#/components/schemas/AuthLocation"
16922
+ prefs:
16923
+ type: object
16924
+ additionalProperties: true
16922
16925
  required:
16923
16926
  - id
16924
16927
  - uid
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@proveanything/smartlinks",
3
- "version": "1.11.8",
3
+ "version": "1.11.10",
4
4
  "description": "Official JavaScript/TypeScript SDK for the Smartlinks API",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",