@proveanything/smartlinks 1.10.0 → 1.10.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.
@@ -285,30 +285,30 @@ export interface ThreadListQueryParams extends ListQueryParams {
285
285
  contactId?: string;
286
286
  }
287
287
  /**
288
- * Facet clause within a RecordScope.
289
- * Values within a single clause are ORed; multiple clauses are ANDed.
288
+ * A single clause in a FacetRule. Tests one facet key against one or more values (OR semantics).
290
289
  */
291
- export interface ScopeFacetClause {
292
- /** Facet key, e.g. "tier", "region" */
293
- key: string;
294
- /** One or more values that satisfy this clause (OR semantics) */
295
- valueKeys: string[];
290
+ export interface FacetRuleClause {
291
+ /**
292
+ * Facet key this clause tests, e.g. "brand", "type", "bread-type".
293
+ * Must reference a defined facet on the collection.
294
+ */
295
+ facetKey: string;
296
+ /**
297
+ * One or more facet value keys that satisfy the clause (OR semantics).
298
+ * At least one value required. Server deduplicates and sorts.
299
+ */
300
+ anyOf: string[];
296
301
  }
297
302
  /**
298
- * Structured scope definition for a record.
299
- * Describes the audience/context the record applies to.
300
- * An empty object `{}` means universal (no restrictions).
303
+ * Multi-clause boolean facet rule: AND across clauses, OR within each clause's anyOf.
304
+ * Mutually exclusive with `scope` on a record.
301
305
  */
302
- export interface RecordScope {
303
- productId?: string;
304
- variantId?: string;
305
- proofId?: string;
306
- batchId?: string;
306
+ export interface FacetRule {
307
307
  /**
308
- * Arbitrary facet clauses.
309
- * Clauses are ANDed together; valueKeys within a clause are ORed.
308
+ * All clauses must be satisfied (AND semantics).
309
+ * Must be non-empty; no duplicate facetKey entries.
310
310
  */
311
- facets?: ScopeFacetClause[];
311
+ all: FacetRuleClause[];
312
312
  }
313
313
  /**
314
314
  * Runtime context passed to the match endpoint.
@@ -320,8 +320,10 @@ export interface RecordTarget {
320
320
  proofId?: string;
321
321
  batchId?: string;
322
322
  /**
323
- * Facet values the caller possesses, keyed by facet key.
324
- * A scope clause is satisfied if ANY of the clause's valueKeys appears here.
323
+ * Facet assignments for the product (e.g. `{ brand: ['samsung'], type: ['tv'] }`).
324
+ * Used exclusively to match FacetRule records via GIN-indexed containment check.
325
+ * Does NOT filter legacy scope.facets arrays (that system is removed in SDK 1.12).
326
+ * Omit to exclude rule records from results.
325
327
  */
326
328
  facets?: Record<string, string[]>;
327
329
  }
@@ -332,14 +334,19 @@ export interface BulkUpsertItem {
332
334
  /** Required — logical identifier used as the upsert key */
333
335
  ref: string;
334
336
  recordType?: string;
335
- customId?: string;
336
- sourceSystem?: string;
337
+ productId?: string | null;
338
+ variantId?: string | null;
339
+ batchId?: string | null;
340
+ proofId?: string | null;
341
+ customId?: string | null;
342
+ sourceSystem?: string | null;
337
343
  startsAt?: string | null;
338
344
  expiresAt?: string | null;
339
345
  status?: string | null;
340
- scope?: RecordScope;
341
346
  data?: Record<string, unknown> | null;
342
347
  metadata?: Record<string, unknown> | null;
348
+ /** Facet rule (rule records only). Mutually exclusive with anchor IDs. */
349
+ facetRule?: FacetRule | null;
343
350
  }
344
351
  /**
345
352
  * Response from the bulk-upsert endpoint.
@@ -381,38 +388,42 @@ export type BulkDeleteInput = {
381
388
  recordType?: string;
382
389
  scope?: never;
383
390
  } | {
384
- scope: Omit<RecordScope, 'facets'>;
391
+ scope: {
392
+ productId?: string;
393
+ variantId?: string;
394
+ batchId?: string;
395
+ proofId?: string;
396
+ };
385
397
  recordType?: string;
386
398
  refs?: never;
387
399
  };
388
400
  /**
389
- * Indicates which scope dimension caused a record to match during `match()`.
390
- * Follows specificity order: proof > batch > variant > product > facet > universal.
401
+ * Which resolution tier caused a record to be selected in `match()` or `resolveAll()`.
402
+ * Precedence (highest first): rule > proof > batch > variant > product > facet > collection > universal.
391
403
  */
392
- export type MatchedAtLevel = 'proof' | 'batch' | 'variant' | 'product' | 'facet' | 'universal';
404
+ export type MatchedAt = 'rule' | 'proof' | 'batch' | 'variant' | 'product' | 'facet' | 'collection' | 'universal';
393
405
  /**
394
- * An AppRecord augmented with `matchedAt` — present only on records returned
395
- * by the `match` endpoint. Use this to display attribution such as
396
- * "Inherited from product" or "Batch-specific" without inspecting scope fields.
406
+ * Entry in `match()` results the record fields plus resolution metadata.
407
+ * Extends AppRecord so all record fields are directly accessible.
397
408
  */
398
- export interface MatchedRecord extends AppRecord {
399
- /**
400
- * The most specific scope dimension that caused this record to match.
401
- * 'universal' means the record has an empty scope and matches all contexts.
402
- */
403
- matchedAt: MatchedAtLevel;
409
+ export interface MatchEntry extends AppRecord {
410
+ /** Which resolution tier caused this record to be selected. */
411
+ matchedAt: MatchedAt;
412
+ /** The rule that fired. Present only when matchedAt === 'rule'. */
413
+ matchedRule?: FacetRule;
414
+ /** Number of clauses in the rule that fired. Present only when matchedAt === 'rule'. */
415
+ matchedClauseCount?: number;
404
416
  }
405
417
  /**
406
418
  * Response from the match endpoint.
407
419
  */
408
420
  export interface MatchResult {
409
421
  /** Matched records ordered by specificity descending (most specific first) */
410
- records: MatchedRecord[];
411
- /**
412
- * Only present when strategy is 'best'.
413
- * The single highest-specificity record per recordType.
414
- */
415
- best?: Record<string, MatchedRecord>;
422
+ data: MatchEntry[];
423
+ /** Total count of matched records */
424
+ total: number;
425
+ /** Strategy used for this result */
426
+ strategy: 'all' | 'best';
416
427
  }
417
428
  /**
418
429
  * Request body for the upsert endpoint.
@@ -421,14 +432,19 @@ export interface UpsertRecordInput {
421
432
  /** Required — used as the lookup key */
422
433
  ref: string;
423
434
  recordType?: string;
424
- customId?: string;
425
- sourceSystem?: string;
435
+ productId?: string | null;
436
+ variantId?: string | null;
437
+ batchId?: string | null;
438
+ proofId?: string | null;
439
+ customId?: string | null;
440
+ sourceSystem?: string | null;
426
441
  startsAt?: string | null;
427
442
  expiresAt?: string | null;
428
443
  status?: string | null;
429
- scope?: RecordScope;
430
444
  data?: Record<string, unknown> | null;
431
445
  metadata?: Record<string, unknown> | null;
446
+ /** Facet rule (rule records only). Mutually exclusive with anchor IDs. */
447
+ facetRule?: FacetRule | null;
432
448
  }
433
449
  /**
434
450
  * Response from the upsert endpoint — includes AppRecord plus a created flag.
@@ -470,12 +486,18 @@ export interface AppRecord {
470
486
  visibility: Visibility;
471
487
  recordType: string | null;
472
488
  ref: string | null;
489
+ scopeType: string | null;
490
+ scopeId: string | null;
473
491
  customId: string | null;
492
+ customIdNormalized: string | null;
474
493
  sourceSystem: string | null;
475
494
  status: string | null;
476
- /** @deprecated use scope.productId instead */
495
+ /** Flat anchor IDs. null = wildcard (matches any value). */
477
496
  productId: string | null;
478
- /** @deprecated use scope.proofId instead */
497
+ /** Flat anchor ID, promoted from scope.variantId in SDK 1.12. */
498
+ variantId: string | null;
499
+ /** Flat anchor ID, promoted from scope.batchId in SDK 1.12. */
500
+ batchId: string | null;
479
501
  proofId: string | null;
480
502
  contactId: string | null;
481
503
  authorId: string | null;
@@ -488,15 +510,17 @@ export interface AppRecord {
488
510
  expiresAt: string | null;
489
511
  deletedAt: string | null;
490
512
  /**
491
- * Structured scope definition. Empty object means universal.
492
- * Platform-canonicalized on write (keys sorted, valueKeys deduplicated).
513
+ * Numeric specificity score. Server-computed from anchor IDs and facetRule.
514
+ * Higher = more specific. 0 = universal (no anchors, no rule).
493
515
  */
494
- scope: RecordScope;
516
+ specificity: number;
495
517
  /**
496
- * Numeric specificity score computed from scope.
497
- * Higher = more specific. 0 = universal scope.
518
+ * Facet rule for rule records (ref starts with "rule:").
519
+ * null on all other record types. Mutually exclusive with anchor IDs.
498
520
  */
499
- specificity: number;
521
+ facetRule: FacetRule | null;
522
+ /** Singleton cardinality key. Server-assigned; opaque to clients. SDK 1.11. */
523
+ singletonKey: string | null;
500
524
  data: Record<string, unknown>;
501
525
  owner: Record<string, unknown>;
502
526
  admin: Record<string, unknown>;
@@ -506,27 +530,36 @@ export interface AppRecord {
506
530
  * Input for creating a new record
507
531
  */
508
532
  export interface CreateRecordInput {
509
- recordType: string;
533
+ recordType?: string;
510
534
  visibility?: Visibility;
511
535
  ref?: string;
512
536
  status?: string;
513
- productId?: string;
514
- proofId?: string;
537
+ productId?: string | null;
538
+ variantId?: string | null;
539
+ batchId?: string | null;
540
+ proofId?: string | null;
515
541
  contactId?: string;
516
542
  authorId?: string;
517
543
  authorType?: string;
518
544
  parentType?: string;
519
545
  parentId?: string;
520
- startsAt?: string;
521
- expiresAt?: string;
522
- /** Structured scope. Canonicalized on write; ref derived if not supplied. */
523
- scope?: RecordScope;
524
- customId?: string;
525
- sourceSystem?: string;
546
+ startsAt?: string | null;
547
+ expiresAt?: string | null;
548
+ scopeType?: string | null;
549
+ scopeId?: string | null;
550
+ customId?: string | null;
551
+ sourceSystem?: string | null;
552
+ /**
553
+ * Opt-in singleton cardinality. When set, the server upserts rather than
554
+ * inserting a duplicate. Values: 'collection' | 'product' | 'variant' | 'batch' | 'proof'
555
+ */
556
+ singletonPer?: string;
526
557
  data?: Record<string, unknown>;
527
558
  owner?: Record<string, unknown>;
528
559
  admin?: Record<string, unknown>;
529
560
  metadata?: Record<string, unknown>;
561
+ /** Facet rule (rule records only). Mutually exclusive with anchor IDs. */
562
+ facetRule?: FacetRule | null;
530
563
  }
531
564
  /**
532
565
  * Input for updating a record
@@ -539,13 +572,19 @@ export interface UpdateRecordInput {
539
572
  visibility?: Visibility;
540
573
  ref?: string;
541
574
  recordType?: string;
542
- startsAt?: string;
543
- expiresAt?: string;
544
- /** Updating scope recomputes specificity and ref. */
545
- scope?: RecordScope;
546
- customId?: string;
547
- sourceSystem?: string;
575
+ productId?: string | null;
576
+ variantId?: string | null;
577
+ batchId?: string | null;
578
+ proofId?: string | null;
579
+ startsAt?: string | null;
580
+ expiresAt?: string | null;
581
+ scopeType?: string | null;
582
+ scopeId?: string | null;
583
+ customId?: string | null;
584
+ sourceSystem?: string | null;
548
585
  metadata?: Record<string, unknown>;
586
+ /** Set/clear facet rule. Send null to remove. */
587
+ facetRule?: FacetRule | null;
549
588
  }
550
589
  /**
551
590
  * Query parameters for listing records
@@ -558,9 +597,9 @@ export interface RecordListQueryParams extends ListQueryParams {
558
597
  customId?: string;
559
598
  sourceSystem?: string;
560
599
  proofId?: string;
561
- /** Filter by scope.variantId (JSONB lookup) */
600
+ /** Filter by variantId (indexed flat column) */
562
601
  variantId?: string;
563
- /** Filter by scope.batchId (JSONB lookup) */
602
+ /** Filter by batchId (indexed flat column) */
564
603
  batchId?: string;
565
604
  /** Full-text filter on data.label (case-insensitive substring) */
566
605
  q?: string;
@@ -582,6 +621,90 @@ export interface RecordListQueryParams extends ListQueryParams {
582
621
  /** Include soft-deleted records (non-null deletedAt). Admin only. Default false. */
583
622
  includeDeleted?: boolean;
584
623
  }
624
+ /**
625
+ * Request body for the resolve-all endpoint.
626
+ * Returns every applicable record for a product context across all tiers.
627
+ */
628
+ export interface ResolveAllParams {
629
+ /** Product context to evaluate records against. */
630
+ context: {
631
+ productId?: string;
632
+ variantId?: string;
633
+ batchId?: string;
634
+ proofId?: string;
635
+ /**
636
+ * Facet assignments for the product — used for both legacy facet-ref matching
637
+ * and facetRule evaluation.
638
+ * e.g. { "brand": "samsung", "type": ["tv", "laptop"] }
639
+ */
640
+ facets?: Record<string, string | string[]>;
641
+ };
642
+ /** Limit to a specific record type. Omit to return all types. */
643
+ recordType?: string;
644
+ /** Only return records belonging to these tiers. */
645
+ tiers?: Array<'proof' | 'batch' | 'variant' | 'product' | 'rule' | 'facet' | 'collection'>;
646
+ /** Safety cap. Default 500, max 5000. */
647
+ limit?: number;
648
+ /** Point-in-time for scheduling evaluation (ISO 8601). Defaults to now. */
649
+ at?: string;
650
+ /** Include records whose startsAt is in the future. Default false. */
651
+ includeScheduled?: boolean;
652
+ /** Include records whose expiresAt is in the past. Default false. */
653
+ includeExpired?: boolean;
654
+ }
655
+ /**
656
+ * Response from the resolve-all endpoint.
657
+ */
658
+ export interface ResolveAllResult {
659
+ /** Every applicable record sorted by precedence (most-specific first). Each appears at most once. */
660
+ records: ResolveAllEntry[];
661
+ /** Total count of returned records. */
662
+ total: number;
663
+ /** The context echoed back from the request (for verification). */
664
+ context: ResolveAllContext;
665
+ /** true if the result was capped at the safety limit. */
666
+ truncated: boolean;
667
+ }
668
+ export interface ResolveAllEntry {
669
+ record: AppRecord;
670
+ matchedAt: MatchedAt;
671
+ specificity: number;
672
+ matchedRule?: FacetRule;
673
+ matchedClauseCount?: number;
674
+ }
675
+ export interface ResolveAllContext {
676
+ productId?: string;
677
+ variantId?: string;
678
+ batchId?: string;
679
+ proofId?: string;
680
+ facets?: Record<string, string[]>;
681
+ }
682
+ /**
683
+ * Request body for the preview-rule endpoint.
684
+ */
685
+ export interface PreviewRuleParams {
686
+ /** The facet rule to evaluate (same validation as on record create). */
687
+ facetRule: FacetRule;
688
+ /** Filter to a specific record type for context. */
689
+ recordType?: string;
690
+ /** Max matching products to return in sample. Default 50, max 200. */
691
+ limit?: number;
692
+ }
693
+ /**
694
+ * Response from the preview-rule endpoint.
695
+ */
696
+ export interface PreviewRuleResult {
697
+ /** Sample of products whose facet assignments satisfy the rule. */
698
+ matchingProducts: Array<{
699
+ productId: string;
700
+ name?: string;
701
+ facets: Record<string, string[]>;
702
+ }>;
703
+ /** Total products in the collection matching the rule. */
704
+ total: number;
705
+ /** Server-canonicalized rule (sorted keys, deduped values) — for display. */
706
+ rule: FacetRule;
707
+ }
585
708
  /**
586
709
  * Response from case related endpoint
587
710
  */