@proveanything/smartlinks 1.11.1 → 1.11.2

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.1 | Generated: 2026-04-30T12:17:30.051Z
3
+ Version: 1.11.2 | Generated: 2026-04-30T13:11:24.914Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -117,14 +117,18 @@ The manifest is loaded automatically by the platform for every collection page.
117
117
 
118
118
  "records": {
119
119
  "nutrition": {
120
- "scopes": ["product", "facet", "batch"],
121
- "defaultScope": "facet",
122
- "label": "Nutrition info"
120
+ "label": "Nutrition info",
121
+ "cardinality": "singleton",
122
+ "allowFacetRules": true,
123
+ "scopes": ["collection", "rule", "product", "facet", "batch"],
124
+ "defaultScope": "product"
123
125
  },
124
126
  "cooking_steps": {
125
- "scopes": ["product", "facet"],
126
- "defaultScope": "product",
127
- "label": "Cooking steps"
127
+ "label": "Cooking steps",
128
+ "cardinality": "singleton",
129
+ "allowFacetRules": false,
130
+ "scopes": ["collection", "product"],
131
+ "defaultScope": "product"
128
132
  }
129
133
  }
130
134
  }
@@ -268,25 +272,29 @@ See the [Deep Link Discovery guide](deep-link-discovery.md) for the full dual-so
268
272
 
269
273
  Declares which `app.records` record types the app stores, and which scopes each type supports. Required for any app that follows the [Records-Based Admin Pattern](records-admin-pattern.md). Omit if the app does not use scoped records.
270
274
 
271
- The platform and the `<RecordsAdminShell>` from `@proveanything/ui-utils` read this block to render only the relevant tabs and affordances.
275
+ The platform and the `<RecordsAdminShell>` from `@proveanything/smartlinks-utils-ui` read this block to render the right scope tabs, rule editor, and cardinality-appropriate right pane.
272
276
 
273
277
  ```json
274
278
  "records": {
275
279
  "<recordType>": {
276
- "scopes": ["product", "facet", "batch"],
277
- "defaultScope": "facet",
278
- "label": "Human-readable label"
280
+ "label": "Human-readable label",
281
+ "cardinality": "singleton",
282
+ "allowFacetRules": false,
283
+ "scopes": ["collection", "product", "variant", "batch", "facet"],
284
+ "defaultScope": "product"
279
285
  }
280
286
  }
281
287
  ```
282
288
 
283
- | Field | Type | Required | Description |
284
- |----------------|----------|----------|-------------|
285
- | `scopes` | string[] | | Allowed scope kinds in resolution order. Valid values: `"product"`, `"variant"`, `"batch"`, `"facet"`, `"proof"`, `"default"`. |
286
- | `defaultScope` | string | | The scope the "Create new" button targets in the admin shell. Must be one of the declared `scopes`. |
287
- | `label` | string | | Human-readable label for the record type, used in headings and tabs. |
289
+ | Field | Type | Default | Description |
290
+ |-------------------|----------|---------|-------------|
291
+ | `label` | string | | Human-readable label for the record type, used in headings and tabs. |
292
+ | `cardinality` | string | `'singleton'` | `'singleton'` — one record wins per scope (e.g. ingredients, nutrition). `'collection'` every matching record is returned in resolution order (e.g. FAQs, recipes). Drives which hook to use on the public side (`useResolvedRecord` vs `useCollectedRecords`) and how the shell lays out the right pane. |
293
+ | `allowFacetRules` | boolean | `false` | When `true`, the shell renders a **Rule** scope tab and embeds `<FacetRuleEditor>`. Add `'rule'` to `scopes` when setting this. |
294
+ | `scopes` | string[] | — | Allowed scope kinds in resolution order. Valid values: `"collection"`, `"product"`, `"variant"`, `"batch"`, `"facet"`, `"proof"`, `"rule"`. `'rule'` is a synthetic scope holding `facetRule`-targeted records. `'collection'` replaces the legacy empty-ref catch-all — **there is no `'global'` scope**. |
295
+ | `defaultScope` | string | — | The scope the "Create new" button targets in the admin shell. Must be one of the declared `scopes`. |
288
296
 
289
- An app may declare multiple record types under different keys (e.g. `"nutrition"` and `"cooking_steps"`).
297
+ An app may declare multiple record types under different keys (e.g. `"nutrition"` and `"cooking_steps"`). See [records-admin-pattern.md](records-admin-pattern.md) for the full admin + public pattern.
290
298
 
291
299
  #### `executor`
292
300
 
@@ -495,7 +495,7 @@ The `ref` field is derived automatically from anchor fields when omitted:
495
495
  ```
496
496
  productId: 'prod_abc' → ref: 'product:prod_abc'
497
497
  productId: 'prod_abc', variantId: 'var_x' → ref: 'product:prod_abc/variant:var_x'
498
- (no anchor fields) → ref: '' (universal)
498
+ (no anchor fields) → ref: '' (collection-level catch-all)
499
499
  facetRule: { ... } → ref: 'rule:<ulid>'
500
500
  ```
501
501
 
@@ -513,6 +513,20 @@ When multiple scoped records match a context, they are ordered by `specificity`.
513
513
  | Per `anyOf` value | +1 |
514
514
  | No anchors / no rule | 0 |
515
515
 
516
+ ### Resolution order
517
+
518
+ When the public side of a records-based app needs "the data that applies to this product context", the platform walks a canonical chain from most-specific to least-specific:
519
+
520
+ ```
521
+ proof → batch → variant → product → rule(*) → facet(*) → collection
522
+ ```
523
+
524
+ - `rule(*)` — `facetRule`-targeted records are scored by **specificity** (number of clauses + number of constrained values). The most specific rule wins at its tier.
525
+ - `facet(*)` — legacy single-facet anchors, walked alphabetically. Prefer `facetRule` for new work.
526
+ - `collection` — the top of the chain and the catch-all for any record with no anchor fields. **There is no `'global'` tier above collection.**
527
+
528
+ For a **singleton** record type (one answer wins), use `useResolvedRecord` — it performs this walk server-side and returns the first match plus a `matchedAt` tag. For a **collection** record type (every match is shown), use `useCollectedRecords`. See [records-admin-pattern.md §2](records-admin-pattern.md#2-resolution-order-one-canonical-chain) for the full guide.
529
+
516
530
  ### Singleton Cardinality
517
531
 
518
532
  By default, `create` always inserts a new row — calling it twice produces two records with identical anchor fields. **Singleton cardinality** changes that: pass `singletonPer` on creation and the server will **upsert** instead, ensuring at most one record of a given `recordType` exists per scope boundary.
@@ -592,12 +606,11 @@ for (const entry of data) {
592
606
  case 'product': /* "Inherited from product" */ break;
593
607
  case 'facet': /* "Tier-specific" */ break;
594
608
  case 'collection': /* "Collection default" */ break;
595
- case 'universal': /* "Default" */ break;
596
609
  }
597
610
  }
598
611
  ```
599
612
 
600
- Precedence follows: `rule > proof > batch > variant > product > facet > collection > universal`.
613
+ Precedence follows: `proof > batch > variant > product > rule > facet > collection`. There is no scope above `collection` — a record with no anchor fields is a collection-level catch-all.
601
614
 
602
615
  #### React — `useResolvedRecord`
603
616
 
@@ -806,7 +819,7 @@ Examples:
806
819
  | `productId: 'prod_abc', variantId: 'var_500ml'` | `product:prod_abc/variant:var_500ml` |
807
820
  | `batchId: 'batch_q1'` | `batch:batch_q1` |
808
821
  | `facetRule: { ... }` | `rule:<ulid>` |
809
- | *(no anchor fields)* | `''` (universal) |
822
+ | *(no anchor fields)* | `''` (collection-level catch-all) |
810
823
 
811
824
  `parseRef` / `buildRef` in `data/refs.ts` should be used for **display and URL round-tripping only**, never as upsert keys. For ETL use cases, set an explicit `ref` using a stable external key (see [External ID / ETL Workflow](#external-id--etl-workflow)).
812
825
 
@@ -58,6 +58,8 @@ Your container **never** detects the host directly. It receives a `host` prop fr
58
58
 
59
59
  Every container mounted by the SmartLinks Mobile launcher receives a single `host` prop — `AdminMobileHostContext`. Do not reach for `window.SmartlinksScanner` or `window.Capacitor` directly; both are wrapped here.
60
60
 
61
+ > **SDK export** — `AdminMobileHostContext`, `AdminMobileCapability`, `AdminMobileHostId`, `AdminMobileEvent`, `ScannerEventSubscriber`, `MobileAdminComponentManifest`, and `MobileAdminBundleManifest` are all exported from `@proveanything/smartlinks`. Import via `import type { AdminMobileHostContext } from '@proveanything/smartlinks'` — no local mirror needed.
62
+
61
63
  ```typescript
62
64
  interface AdminMobileHostContext {
63
65
  // Identity
@@ -460,7 +462,7 @@ vite build --config vite.config.mobile-admin.ts
460
462
  ```typescript
461
463
  // src/mobile-admin/WarehousePickContainer.tsx
462
464
  import { useEffect, useState } from 'react'
463
- import type { AdminMobileHostContext } from '@/lib/admin-mobile-host-context'
465
+ import type { AdminMobileHostContext } from '@proveanything/smartlinks'
464
466
 
465
467
  interface Props {
466
468
  host: AdminMobileHostContext