@proveanything/smartlinks 1.15.23 → 1.16.0
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/index.d.ts +1 -0
- package/dist/api/index.js +1 -0
- package/dist/api/lots.d.ts +37 -0
- package/dist/api/lots.js +97 -0
- package/dist/docs/API_SUMMARY.md +184 -1
- package/dist/docs/appConfig.md +2 -1
- package/dist/docs/lots.md +94 -0
- package/dist/docs/utils.md +2 -1
- package/dist/index.d.ts +1 -0
- package/dist/openapi.yaml +548 -0
- package/dist/types/lots.d.ts +95 -0
- package/dist/types/lots.js +7 -0
- package/dist/types/proof.d.ts +6 -0
- package/dist/types/segments.d.ts +25 -0
- package/dist/utils/paths.d.ts +9 -2
- package/docs/API_SUMMARY.md +184 -1
- package/docs/appConfig.md +2 -1
- package/docs/lots.md +94 -0
- package/docs/utils.md +2 -1
- package/openapi.yaml +548 -0
- package/package.json +1 -1
package/dist/api/index.d.ts
CHANGED
|
@@ -34,6 +34,7 @@ export { order } from "./order";
|
|
|
34
34
|
export { app } from "./appObjects";
|
|
35
35
|
export { attestations } from "./attestations";
|
|
36
36
|
export { containers } from "./containers";
|
|
37
|
+
export { lots } from "./lots";
|
|
37
38
|
export { loyalty } from "./loyalty";
|
|
38
39
|
export { translations } from "./translations";
|
|
39
40
|
export { config } from "./config";
|
package/dist/api/index.js
CHANGED
|
@@ -37,6 +37,7 @@ export { order } from "./order";
|
|
|
37
37
|
export { app } from "./appObjects";
|
|
38
38
|
export { attestations } from "./attestations";
|
|
39
39
|
export { containers } from "./containers";
|
|
40
|
+
export { lots } from "./lots";
|
|
40
41
|
export { loyalty } from "./loyalty";
|
|
41
42
|
export { translations } from "./translations";
|
|
42
43
|
export { config } from "./config";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Lot, LotCreateInput, LotUpdateInput, ListLotsParams, ResolveLotResponse, ListLotProductsResponse } from "../types/lots";
|
|
2
|
+
/**
|
|
3
|
+
* Lots — collection-scoped production groupings that span one or more products.
|
|
4
|
+
* Writes and admin reads hit `/admin/collection/:cid/lots`; the `public*` reads hit
|
|
5
|
+
* `/public/collection/:cid/lots` for cross-app consumers. Admin vs public is the path
|
|
6
|
+
* prefix (auth is the ambient bearer token); there is no `admin` flag in this SDK.
|
|
7
|
+
*/
|
|
8
|
+
export declare namespace lots {
|
|
9
|
+
/** Create a lot (resolves its selector into members). */
|
|
10
|
+
function create(collectionId: string, lot: LotCreateInput): Promise<Lot>;
|
|
11
|
+
/** List lots (summary rows; `payload`/`productIds` omitted). Filter by status, search, or containing productId. */
|
|
12
|
+
function list(collectionId: string, params?: ListLotsParams): Promise<Lot[]>;
|
|
13
|
+
/** Get the full lot record. */
|
|
14
|
+
function get(collectionId: string, lotId: string): Promise<Lot>;
|
|
15
|
+
/** Look up a lot by its number (case-insensitive) — used by scan/resolver flows. */
|
|
16
|
+
function getByNumber(collectionId: string, lotNumber: string): Promise<Lot>;
|
|
17
|
+
/** Update a lot. Re-resolves members if the selector changed (response then carries `diff`). */
|
|
18
|
+
function update(collectionId: string, lotId: string, lot: LotUpdateInput): Promise<Lot>;
|
|
19
|
+
/** Soft-archive a lot (never deletes members). */
|
|
20
|
+
function archive(collectionId: string, lotId: string): Promise<{
|
|
21
|
+
success: boolean;
|
|
22
|
+
}>;
|
|
23
|
+
/** Re-resolve members from the current selector; returns the lot + a member diff. */
|
|
24
|
+
function resolve(collectionId: string, lotId: string): Promise<ResolveLotResponse>;
|
|
25
|
+
/** Paginated member product summaries. */
|
|
26
|
+
function listProducts(collectionId: string, lotId: string, opts?: {
|
|
27
|
+
page?: number;
|
|
28
|
+
limit?: number;
|
|
29
|
+
}): Promise<ListLotProductsResponse>;
|
|
30
|
+
function publicList(collectionId: string, params?: ListLotsParams): Promise<Lot[]>;
|
|
31
|
+
function publicGet(collectionId: string, lotId: string): Promise<Lot>;
|
|
32
|
+
function publicGetByNumber(collectionId: string, lotNumber: string): Promise<Lot>;
|
|
33
|
+
function publicListProducts(collectionId: string, lotId: string, opts?: {
|
|
34
|
+
page?: number;
|
|
35
|
+
limit?: number;
|
|
36
|
+
}): Promise<ListLotProductsResponse>;
|
|
37
|
+
}
|
package/dist/api/lots.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// src/api/lots.ts
|
|
2
|
+
import { request, post, put, del } from "../http";
|
|
3
|
+
/**
|
|
4
|
+
* Lots — collection-scoped production groupings that span one or more products.
|
|
5
|
+
* Writes and admin reads hit `/admin/collection/:cid/lots`; the `public*` reads hit
|
|
6
|
+
* `/public/collection/:cid/lots` for cross-app consumers. Admin vs public is the path
|
|
7
|
+
* prefix (auth is the ambient bearer token); there is no `admin` flag in this SDK.
|
|
8
|
+
*/
|
|
9
|
+
export var lots;
|
|
10
|
+
(function (lots) {
|
|
11
|
+
function adminBase(collectionId) {
|
|
12
|
+
return `/admin/collection/${encodeURIComponent(collectionId)}/lots`;
|
|
13
|
+
}
|
|
14
|
+
function publicBase(collectionId) {
|
|
15
|
+
return `/public/collection/${encodeURIComponent(collectionId)}/lots`;
|
|
16
|
+
}
|
|
17
|
+
function listQuery(params = {}) {
|
|
18
|
+
const qs = new URLSearchParams();
|
|
19
|
+
if (params.status)
|
|
20
|
+
qs.append('status', params.status);
|
|
21
|
+
if (params.search)
|
|
22
|
+
qs.append('search', params.search);
|
|
23
|
+
if (params.productId)
|
|
24
|
+
qs.append('productId', params.productId);
|
|
25
|
+
const s = qs.toString();
|
|
26
|
+
return s ? `?${s}` : '';
|
|
27
|
+
}
|
|
28
|
+
function pageQuery(opts = {}) {
|
|
29
|
+
const qs = new URLSearchParams();
|
|
30
|
+
if (opts.page)
|
|
31
|
+
qs.append('page', String(opts.page));
|
|
32
|
+
if (opts.limit)
|
|
33
|
+
qs.append('limit', String(opts.limit));
|
|
34
|
+
const s = qs.toString();
|
|
35
|
+
return s ? `?${s}` : '';
|
|
36
|
+
}
|
|
37
|
+
// ── Admin (writes + admin reads) ──
|
|
38
|
+
/** Create a lot (resolves its selector into members). */
|
|
39
|
+
async function create(collectionId, lot) {
|
|
40
|
+
return post(adminBase(collectionId), lot);
|
|
41
|
+
}
|
|
42
|
+
lots.create = create;
|
|
43
|
+
/** List lots (summary rows; `payload`/`productIds` omitted). Filter by status, search, or containing productId. */
|
|
44
|
+
async function list(collectionId, params = {}) {
|
|
45
|
+
const res = await request(`${adminBase(collectionId)}${listQuery(params)}`);
|
|
46
|
+
return res.lots;
|
|
47
|
+
}
|
|
48
|
+
lots.list = list;
|
|
49
|
+
/** Get the full lot record. */
|
|
50
|
+
async function get(collectionId, lotId) {
|
|
51
|
+
return request(`${adminBase(collectionId)}/${encodeURIComponent(lotId)}`);
|
|
52
|
+
}
|
|
53
|
+
lots.get = get;
|
|
54
|
+
/** Look up a lot by its number (case-insensitive) — used by scan/resolver flows. */
|
|
55
|
+
async function getByNumber(collectionId, lotNumber) {
|
|
56
|
+
return request(`${adminBase(collectionId)}/by-number/${encodeURIComponent(lotNumber)}`);
|
|
57
|
+
}
|
|
58
|
+
lots.getByNumber = getByNumber;
|
|
59
|
+
/** Update a lot. Re-resolves members if the selector changed (response then carries `diff`). */
|
|
60
|
+
async function update(collectionId, lotId, lot) {
|
|
61
|
+
return put(`${adminBase(collectionId)}/${encodeURIComponent(lotId)}`, lot);
|
|
62
|
+
}
|
|
63
|
+
lots.update = update;
|
|
64
|
+
/** Soft-archive a lot (never deletes members). */
|
|
65
|
+
async function archive(collectionId, lotId) {
|
|
66
|
+
return del(`${adminBase(collectionId)}/${encodeURIComponent(lotId)}`);
|
|
67
|
+
}
|
|
68
|
+
lots.archive = archive;
|
|
69
|
+
/** Re-resolve members from the current selector; returns the lot + a member diff. */
|
|
70
|
+
async function resolve(collectionId, lotId) {
|
|
71
|
+
return post(`${adminBase(collectionId)}/${encodeURIComponent(lotId)}/resolve`, {});
|
|
72
|
+
}
|
|
73
|
+
lots.resolve = resolve;
|
|
74
|
+
/** Paginated member product summaries. */
|
|
75
|
+
async function listProducts(collectionId, lotId, opts = {}) {
|
|
76
|
+
return request(`${adminBase(collectionId)}/${encodeURIComponent(lotId)}/products${pageQuery(opts)}`);
|
|
77
|
+
}
|
|
78
|
+
lots.listProducts = listProducts;
|
|
79
|
+
// ── Public (cross-app reads) ──
|
|
80
|
+
async function publicList(collectionId, params = {}) {
|
|
81
|
+
const res = await request(`${publicBase(collectionId)}${listQuery(params)}`);
|
|
82
|
+
return res.lots;
|
|
83
|
+
}
|
|
84
|
+
lots.publicList = publicList;
|
|
85
|
+
async function publicGet(collectionId, lotId) {
|
|
86
|
+
return request(`${publicBase(collectionId)}/${encodeURIComponent(lotId)}`);
|
|
87
|
+
}
|
|
88
|
+
lots.publicGet = publicGet;
|
|
89
|
+
async function publicGetByNumber(collectionId, lotNumber) {
|
|
90
|
+
return request(`${publicBase(collectionId)}/by-number/${encodeURIComponent(lotNumber)}`);
|
|
91
|
+
}
|
|
92
|
+
lots.publicGetByNumber = publicGetByNumber;
|
|
93
|
+
async function publicListProducts(collectionId, lotId, opts = {}) {
|
|
94
|
+
return request(`${publicBase(collectionId)}/${encodeURIComponent(lotId)}/products${pageQuery(opts)}`);
|
|
95
|
+
}
|
|
96
|
+
lots.publicListProducts = publicListProducts;
|
|
97
|
+
})(lots || (lots = {}));
|
package/dist/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.16.0 | Generated: 2026-09-01T12:28:06.435Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -35,6 +35,7 @@ For detailed guides on specific features:
|
|
|
35
35
|
- **[Proof Claiming Methods](proof-claiming-methods.md)** - All methods for claiming/registering product ownership (NFC tags, serial numbers, auto-generated claims)
|
|
36
36
|
- **[Proof Share Grants](proof-share-grants.md)** - Delegated, scoped, revocable bearer access to a single proof (read/comment/verify-owner links)
|
|
37
37
|
- **[Proof Ownership Transfer](proof-ownership-transfer.md)** - Moving a proof to a new owner: directed transfer, open release, accept/cancel, and the state machine
|
|
38
|
+
- **[Lots](lots.md)** - Collection-scoped production groupings spanning many SKUs; facet/product selectors, member resolution, and GS1 AI(10) batch-then-lot resolution
|
|
38
39
|
- **[Item Context](item-context.md)** - The `itemContext` container prop derived from a serial-proof URL or NFC tap (what item the URL points at)
|
|
39
40
|
- **[Product Facets SDK](PRODUCT_FACETS_SDK.md)** - Admin and public product facet endpoints and TypeScript interfaces
|
|
40
41
|
- **[Attestations](attestations.md)** - Append-only fact log with cryptographic chain integrity, time-series analytics, and public/owner/admin visibility
|
|
@@ -135,6 +136,7 @@ The Smartlinks SDK is organized into the following namespaces:
|
|
|
135
136
|
- **jobs** - Functions for jobs operations
|
|
136
137
|
- **journeysAnalytics** - Functions for journeysAnalytics operations
|
|
137
138
|
- **location** - Functions for location operations
|
|
139
|
+
- **lots** - Functions for lots operations
|
|
138
140
|
- **navigation** - Functions for navigation operations
|
|
139
141
|
- **order** - Functions for order operations
|
|
140
142
|
- **products** - Functions for products operations
|
|
@@ -6355,6 +6357,120 @@ interface LocationSearchResponse {
|
|
|
6355
6357
|
|
|
6356
6358
|
**LocationPayload** = `Omit<`
|
|
6357
6359
|
|
|
6360
|
+
### lots
|
|
6361
|
+
|
|
6362
|
+
**LotPayload** (interface)
|
|
6363
|
+
```typescript
|
|
6364
|
+
interface LotPayload {
|
|
6365
|
+
manufacturedAt?: string | null
|
|
6366
|
+
expiresAt?: string | null
|
|
6367
|
+
location?: string | null
|
|
6368
|
+
custom?: Record<string, any>
|
|
6369
|
+
[key: string]: any
|
|
6370
|
+
}
|
|
6371
|
+
```
|
|
6372
|
+
|
|
6373
|
+
**Lot** (interface)
|
|
6374
|
+
```typescript
|
|
6375
|
+
interface Lot {
|
|
6376
|
+
id: string
|
|
6377
|
+
collectionId: string
|
|
6378
|
+
lotNumber: string
|
|
6379
|
+
name?: string | null
|
|
6380
|
+
description?: string | null
|
|
6381
|
+
status: LotStatus
|
|
6382
|
+
selector: LotSelector
|
|
6383
|
+
payload?: LotPayload
|
|
6384
|
+
destination?: Record<string, any> | null
|
|
6385
|
+
productCount: number
|
|
6386
|
+
productIds?: string[]
|
|
6387
|
+
resolvedAt?: string | null
|
|
6388
|
+
createdBy?: string | null
|
|
6389
|
+
updatedBy?: string | null
|
|
6390
|
+
createdAt: string
|
|
6391
|
+
updatedAt: string
|
|
6392
|
+
}
|
|
6393
|
+
```
|
|
6394
|
+
|
|
6395
|
+
**LotCreateInput** (interface)
|
|
6396
|
+
```typescript
|
|
6397
|
+
interface LotCreateInput {
|
|
6398
|
+
lotNumber: string
|
|
6399
|
+
name?: string
|
|
6400
|
+
description?: string
|
|
6401
|
+
selector?: LotSelector
|
|
6402
|
+
payload?: LotPayload
|
|
6403
|
+
destination?: Record<string, any> | null
|
|
6404
|
+
status?: LotStatus
|
|
6405
|
+
id?: string
|
|
6406
|
+
}
|
|
6407
|
+
```
|
|
6408
|
+
|
|
6409
|
+
**ListLotsParams** (interface)
|
|
6410
|
+
```typescript
|
|
6411
|
+
interface ListLotsParams {
|
|
6412
|
+
status?: LotStatus
|
|
6413
|
+
search?: string
|
|
6414
|
+
productId?: string
|
|
6415
|
+
}
|
|
6416
|
+
```
|
|
6417
|
+
|
|
6418
|
+
**ListLotsResponse** (interface)
|
|
6419
|
+
```typescript
|
|
6420
|
+
interface ListLotsResponse {
|
|
6421
|
+
lots: Lot[]
|
|
6422
|
+
}
|
|
6423
|
+
```
|
|
6424
|
+
|
|
6425
|
+
**LotMemberDiff** (interface)
|
|
6426
|
+
```typescript
|
|
6427
|
+
interface LotMemberDiff {
|
|
6428
|
+
added: string[]; removed: string[]
|
|
6429
|
+
}
|
|
6430
|
+
```
|
|
6431
|
+
|
|
6432
|
+
**ResolveLotResponse** (interface)
|
|
6433
|
+
```typescript
|
|
6434
|
+
interface ResolveLotResponse {
|
|
6435
|
+
lot: Lot; diff: LotMemberDiff
|
|
6436
|
+
}
|
|
6437
|
+
```
|
|
6438
|
+
|
|
6439
|
+
**LotProductSummary** (interface)
|
|
6440
|
+
```typescript
|
|
6441
|
+
interface LotProductSummary {
|
|
6442
|
+
id: string; name?: string; gtin?: string | null
|
|
6443
|
+
}
|
|
6444
|
+
```
|
|
6445
|
+
|
|
6446
|
+
**ListLotProductsResponse** (interface)
|
|
6447
|
+
```typescript
|
|
6448
|
+
interface ListLotProductsResponse {
|
|
6449
|
+
products: LotProductSummary[]
|
|
6450
|
+
total: number
|
|
6451
|
+
page: number
|
|
6452
|
+
limit: number
|
|
6453
|
+
}
|
|
6454
|
+
```
|
|
6455
|
+
|
|
6456
|
+
**LotResolutionResult** (interface)
|
|
6457
|
+
```typescript
|
|
6458
|
+
interface LotResolutionResult {
|
|
6459
|
+
match: 'batch' | 'lot' | 'product' | 'none'
|
|
6460
|
+
productId: string
|
|
6461
|
+
batchId: string | null
|
|
6462
|
+
lotId: string | null
|
|
6463
|
+
ai10: string | null
|
|
6464
|
+
destination?: any
|
|
6465
|
+
}
|
|
6466
|
+
```
|
|
6467
|
+
|
|
6468
|
+
**LotStatus** = `'open' | 'closed' | 'recalled' | 'archived'`
|
|
6469
|
+
|
|
6470
|
+
**LotSelector** = ``
|
|
6471
|
+
|
|
6472
|
+
**LotUpdateInput** = `Partial<LotCreateInput>`
|
|
6473
|
+
|
|
6358
6474
|
### loyalty
|
|
6359
6475
|
|
|
6360
6476
|
**LoyaltyScheme** (interface)
|
|
@@ -7329,6 +7445,9 @@ interface Proof {
|
|
|
7329
7445
|
productId: string
|
|
7330
7446
|
tokenId: string
|
|
7331
7447
|
userId: string
|
|
7448
|
+
batchId?: string | null
|
|
7449
|
+
variantId?: string | null
|
|
7450
|
+
lotId?: string | null
|
|
7332
7451
|
claimable?: boolean
|
|
7333
7452
|
virtual?: boolean
|
|
7334
7453
|
data?: Record<string, JsonValue>
|
|
@@ -7531,6 +7650,23 @@ interface InteractionFilterValue {
|
|
|
7531
7650
|
}
|
|
7532
7651
|
```
|
|
7533
7652
|
|
|
7653
|
+
**OwnershipAttributePredicate** (interface)
|
|
7654
|
+
```typescript
|
|
7655
|
+
interface OwnershipAttributePredicate {
|
|
7656
|
+
path: string
|
|
7657
|
+
operator: OwnershipAttributeOperator
|
|
7658
|
+
value?: string | number
|
|
7659
|
+
}
|
|
7660
|
+
```
|
|
7661
|
+
|
|
7662
|
+
**OwnershipFacetSelector** (interface)
|
|
7663
|
+
```typescript
|
|
7664
|
+
interface OwnershipFacetSelector {
|
|
7665
|
+
key: string
|
|
7666
|
+
value: string
|
|
7667
|
+
}
|
|
7668
|
+
```
|
|
7669
|
+
|
|
7534
7670
|
**SegmentRecord** (interface)
|
|
7535
7671
|
```typescript
|
|
7536
7672
|
interface SegmentRecord {
|
|
@@ -7591,6 +7727,10 @@ interface SegmentRecipientsResponse {
|
|
|
7591
7727
|
}
|
|
7592
7728
|
```
|
|
7593
7729
|
|
|
7730
|
+
**CustomFieldOperator** = ``
|
|
7731
|
+
|
|
7732
|
+
**OwnershipAttributeOperator** = `'equals' | 'exists' | 'gt' | 'gte' | 'lt' | 'lte'`
|
|
7733
|
+
|
|
7594
7734
|
**SegmentFilterRule** = ``
|
|
7595
7735
|
|
|
7596
7736
|
### tags
|
|
@@ -8283,7 +8423,12 @@ interface Gs1DigitalLinkParams {
|
|
|
8283
8423
|
gtin?: string
|
|
8284
8424
|
product?: Product
|
|
8285
8425
|
ownGtin?: boolean
|
|
8426
|
+
* A real GS1 **Consumer Product Variant** code (AI 22). Use this when the brand has a
|
|
8427
|
+
* genuine CPV. Takes precedence over `variant` when both are given.
|
|
8286
8428
|
cpv?: string | { id: string }
|
|
8429
|
+
* Internal variant id, emitted as AI 22 (the SmartLinks resolver reads path segment 22
|
|
8430
|
+
* as the variant). Prefer `cpv` when you have a real GS1 CPV code — a non-CPV variant id
|
|
8431
|
+
* in AI 22 is only meaningful to the SmartLinks resolver, not to third-party GS1 resolvers.
|
|
8287
8432
|
variant?: string | { id: string }
|
|
8288
8433
|
lot?: string | { id: string }
|
|
8289
8434
|
batch?: BatchResponse | string
|
|
@@ -9745,6 +9890,44 @@ Public: Fetch a global location by ID GET /public/location/:locationId
|
|
|
9745
9890
|
locationId: string) → `Promise<Location>`
|
|
9746
9891
|
Public: Fetch a location for a collection; returns either a collection-owned or global fallback GET /public/collection/:collectionId/location/:locationId
|
|
9747
9892
|
|
|
9893
|
+
### lots
|
|
9894
|
+
|
|
9895
|
+
**create**(collectionId: string, lot: LotCreateInput) → `Promise<Lot>`
|
|
9896
|
+
Create a lot (resolves its selector into members).
|
|
9897
|
+
|
|
9898
|
+
**list**(collectionId: string, params: ListLotsParams = {}) → `Promise<Lot[]>`
|
|
9899
|
+
List lots (summary rows; `payload`/`productIds` omitted). Filter by status, search, or containing productId.
|
|
9900
|
+
|
|
9901
|
+
**get**(collectionId: string, lotId: string) → `Promise<Lot>`
|
|
9902
|
+
Get the full lot record.
|
|
9903
|
+
|
|
9904
|
+
**getByNumber**(collectionId: string, lotNumber: string) → `Promise<Lot>`
|
|
9905
|
+
Look up a lot by its number (case-insensitive) — used by scan/resolver flows.
|
|
9906
|
+
|
|
9907
|
+
**update**(collectionId: string, lotId: string, lot: LotUpdateInput) → `Promise<Lot>`
|
|
9908
|
+
Update a lot. Re-resolves members if the selector changed (response then carries `diff`).
|
|
9909
|
+
|
|
9910
|
+
**archive**(collectionId: string, lotId: string) → `Promise<`
|
|
9911
|
+
Soft-archive a lot (never deletes members).
|
|
9912
|
+
|
|
9913
|
+
**resolve**(collectionId: string, lotId: string) → `Promise<ResolveLotResponse>`
|
|
9914
|
+
Re-resolve members from the current selector; returns the lot + a member diff.
|
|
9915
|
+
|
|
9916
|
+
**listProducts**(collectionId: string, lotId: string, opts: { page?: number; limit?: number } = {}) → `Promise<ListLotProductsResponse>`
|
|
9917
|
+
Paginated member product summaries.
|
|
9918
|
+
|
|
9919
|
+
**publicList**(collectionId: string, params: ListLotsParams = {}) → `Promise<Lot[]>`
|
|
9920
|
+
Paginated member product summaries.
|
|
9921
|
+
|
|
9922
|
+
**publicGet**(collectionId: string, lotId: string) → `Promise<Lot>`
|
|
9923
|
+
Paginated member product summaries.
|
|
9924
|
+
|
|
9925
|
+
**publicGetByNumber**(collectionId: string, lotNumber: string) → `Promise<Lot>`
|
|
9926
|
+
Paginated member product summaries.
|
|
9927
|
+
|
|
9928
|
+
**publicListProducts**(collectionId: string, lotId: string, opts: { page?: number; limit?: number } = {}) → `Promise<ListLotProductsResponse>`
|
|
9929
|
+
Paginated member product summaries.
|
|
9930
|
+
|
|
9748
9931
|
### loyalty
|
|
9749
9932
|
|
|
9750
9933
|
Loyalty programmes built on top of collections. Configure schemes and earning rules; contacts earn points automatically via interaction events. See the [Loyalty guide](loyalty.md) for the full walkthrough.
|
package/dist/docs/appConfig.md
CHANGED
|
@@ -298,8 +298,9 @@ about:
|
|
|
298
298
|
| `hub` | The Hub module. |
|
|
299
299
|
| `portal` | QR code scan portals. |
|
|
300
300
|
| `virtualItems` | Virtual items — algorithmic per-item IDs with no persistent row (battery serials, scan-to-collect points, bulk QR sheets). Replaces the old root-level `virtualItemsEnabled` boolean, which has been removed (§8) — resolve this like any other feature flag, not as a separate config key. Independent of `itemRecordMode`. |
|
|
301
|
-
| `batches` | Batch support. Used to be the `Collection.batches` boolean; that field has been removed — this flag is now the only source of truth. |
|
|
301
|
+
| `batches` | Batch support. Used to be the `Collection.batches` boolean; that field has been removed — this flag is now the only source of truth. Also gates AI(10) **batch** resolution on a GS1 Digital Link scan (§ see `lots.md`). |
|
|
302
302
|
| `variants` | Variant support. Used to be the `Collection.variants` boolean; that field has been removed — this flag is now the only source of truth. |
|
|
303
|
+
| `lots` | Lot support — collection-scoped production groupings spanning many SKUs (see `lots.md`). Gates AI(10) **lot** resolution on a GS1 Digital Link scan. Independent of `batches`; when both are on, a scanned AI(10) value resolves batch-first (specific SKU) then lot (broad). |
|
|
303
304
|
|
|
304
305
|
Same resolution rule as any flag (§4.4) — don't read these off
|
|
305
306
|
`cfg.system.features` directly, always go through `isFeatureEnabled()`.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Lots
|
|
2
|
+
|
|
3
|
+
A **Lot** is a collection-scoped production grouping that spans one or more products
|
|
4
|
+
(SKUs) — a single identifier applied across many SKUs and/or many production runs. It's
|
|
5
|
+
the right tool when a manufacturer wants one lot number (e.g. `LOT-2026-09`) across a whole
|
|
6
|
+
range, rather than a per-product **batch** (a single run of a single product).
|
|
7
|
+
|
|
8
|
+
Lots are a first-class entity (not app records): cross-app readable, admin-written, with a
|
|
9
|
+
real lifecycle. They are **never fanned out into batches** — the lot is the single source of
|
|
10
|
+
truth for its shared data.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Concepts
|
|
15
|
+
|
|
16
|
+
- **Selector** — how member products are matched. Two modes:
|
|
17
|
+
- `{ mode: 'facets', rules: [{ key, values }] }` — AND across rules, OR within a rule's values (resolved against the facet index, so it scales to thousands of SKUs).
|
|
18
|
+
- `{ mode: 'products', productIds: [...] }` — an explicit list.
|
|
19
|
+
- **`productIds` / `productCount`** — the materialised snapshot of resolved members (re-resolved on create, on selector change, and on demand via `resolve`).
|
|
20
|
+
- **`payload`** — shared lot data (dates, supplier ref, custom fields). Lives only on the lot.
|
|
21
|
+
- **`status`** — `open` → `closed` → `recalled` → `archived`. Archiving never deletes anything.
|
|
22
|
+
- **`destination`** — optional lot-level redirect; wins over the product's on a lot-scoped scan.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## SDK — `SL.lots.*`
|
|
27
|
+
|
|
28
|
+
Writes and admin reads hit `/admin/collection/:cid/lots`; the `public*` reads hit
|
|
29
|
+
`/public/collection/:cid/lots` for cross-app consumers (auth is the ambient bearer token —
|
|
30
|
+
there's no `admin` flag).
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { lots } from '@proveanything/smartlinks'
|
|
34
|
+
|
|
35
|
+
// Create — facet-targeted lot
|
|
36
|
+
const lot = await lots.create(collectionId, {
|
|
37
|
+
lotNumber: 'LOT-2026-09',
|
|
38
|
+
name: 'September Oak run',
|
|
39
|
+
selector: { mode: 'facets', rules: [
|
|
40
|
+
{ key: 'supplier', values: ['Acme Timber'] },
|
|
41
|
+
{ key: 'range', values: ['Oslo', 'Bergen'] },
|
|
42
|
+
]},
|
|
43
|
+
payload: { manufacturedAt: '2026-09-01', custom: { supplierBatchRef: 'ACM-7741' } },
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
const list = await lots.list(collectionId, { status: 'open' })
|
|
47
|
+
const byId = await lots.get(collectionId, lot.id)
|
|
48
|
+
const byNumber = await lots.getByNumber(collectionId, 'LOT-2026-09') // case-insensitive
|
|
49
|
+
const containing = await lots.list(collectionId, { productId: 'prd_abc' }) // reverse lookup
|
|
50
|
+
const updated = await lots.update(collectionId, lot.id, { status: 'closed' })
|
|
51
|
+
const { diff } = await lots.resolve(collectionId, lot.id) // { added, removed }
|
|
52
|
+
const members = await lots.listProducts(collectionId, lot.id, { page: 1, limit: 50 })
|
|
53
|
+
await lots.archive(collectionId, lot.id)
|
|
54
|
+
|
|
55
|
+
// Cross-app reads
|
|
56
|
+
const publicLots = await lots.publicList(collectionId)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Exported types: `Lot`, `LotStatus`, `LotSelector`, `LotPayload`, `LotCreateInput`,
|
|
60
|
+
`LotUpdateInput`, `ListLotsParams`, `ResolveLotResponse`, `ListLotProductsResponse`,
|
|
61
|
+
`LotResolutionResult`.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## GS1 Digital Link resolution — AI(10)
|
|
66
|
+
|
|
67
|
+
GS1 gives batch and lot a **single** slot: AI(10) ("Batch or Lot Number"), in a Digital Link
|
|
68
|
+
as `/01/{gtin}/10/{value}`. A physical code carries exactly one value there, so the server
|
|
69
|
+
decides which namespace it belongs to, driven by two **feature flags** — `batches` and `lots`
|
|
70
|
+
— in the platform feature-flag system (`appConfig.system.features`), resolved the standard way
|
|
71
|
+
(`appConfiguration.isFeatureEnabled(collectionId, 'lots')`):
|
|
72
|
+
|
|
73
|
+
- Explicit `true`/`false` in `system.features` wins; otherwise **enterprise** accounts default
|
|
74
|
+
a flag on and everyone else defaults off. So AI(10) batch/lot resolution is opt-in — no
|
|
75
|
+
existing scan changes until `batches`/`lots` is enabled for the collection.
|
|
76
|
+
|
|
77
|
+
Resolution order — **batch first, then lot** (a hierarchy, not a collision):
|
|
78
|
+
|
|
79
|
+
1. **Batch** is the narrowest scope (one product). If the scanned product has a batch whose id
|
|
80
|
+
or name matches the AI(10) value, it wins — a batch is a **specific-SKU override**.
|
|
81
|
+
2. **Lot** is broad (many products). If no batch matches and the value is a lot number *and the
|
|
82
|
+
scanned product is a member*, the lot resolves.
|
|
83
|
+
3. Otherwise it resolves at the product level.
|
|
84
|
+
|
|
85
|
+
This means a range can share one lot code, and a single SKU can be given richer/overriding
|
|
86
|
+
detail by creating a batch with the **same** identifier — the batch simply takes precedence for
|
|
87
|
+
that SKU. A recalled lot (`status: 'recalled'`) still resolves so the destination page can show
|
|
88
|
+
a recall notice (a `&recall=1` context param is added).
|
|
89
|
+
|
|
90
|
+
The server returns a typed shape ({@link LotResolutionResult}); the front end renders it — clients
|
|
91
|
+
should not implement their own fallback.
|
|
92
|
+
|
|
93
|
+
See also [proof-product-data-scoping.md](proof-product-data-scoping.md) and the GS1 link
|
|
94
|
+
generator in [utils.md](utils.md) (`buildGs1DigitalLink`).
|
package/dist/docs/utils.md
CHANGED
|
@@ -233,7 +233,8 @@ otherwise).
|
|
|
233
233
|
| Input | AI | Notes |
|
|
234
234
|
|-------|----|-------|
|
|
235
235
|
| `gtin` / `product.gtin` | 01 | Primary identifier (required) |
|
|
236
|
-
| `cpv`
|
|
236
|
+
| `cpv` | 22 | Real GS1 Consumer Product Variant code (path). Wins over `variant`. |
|
|
237
|
+
| `variant` | 22 | Internal variant id, emitted as AI 22 for the SmartLinks resolver. Use `cpv` for a genuine GS1 CPV. |
|
|
237
238
|
| `lot` / `batch` | 10 | Batch/lot (path); a batch object also supplies its expiry |
|
|
238
239
|
| `serial` | 21 | Specific item — a string, or an object (`serialNumber` ?? `id`, e.g. a proof / serial / virtual id) |
|
|
239
240
|
| `expiry` | 17 | Date → `YYMMDD` (query) |
|
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,7 @@ export type { LoginResponse, VerifyTokenResponse, AccountInfoResponse, AuthLocat
|
|
|
10
10
|
export type { UserAccountRegistrationRequest, } from "./types/auth";
|
|
11
11
|
export type { CommunicationEvent, CommsQueryByUser, CommsRecipientIdsQuery, CommsRecipientsWithoutActionQuery, CommsRecipientsWithActionQuery, RecipientId, RecipientWithOutcome, LogCommunicationEventBody, LogBulkCommunicationEventsBody, AppendResult, AppendBulkResult, CommsSettings, TopicConfig, CommsSettingsGetResponse, CommsSettingsPatchBody, CommsPublicTopicsResponse, UnsubscribeQuery, UnsubscribeResponse, CommsConsentUpsertRequest, CommsPreferencesUpsertRequest, CommsSubscribeRequest, CommsSubscribeResponse, CommsSubscriptionCheckQuery, CommsSubscriptionCheckResponse, CommsListMethodsQuery, CommsListMethodsResponse, RegisterEmailMethodRequest, RegisterSmsMethodRequest, RegisterMethodResponse, SubscriptionsResolveRequest, SubscriptionsResolveResponse, } from "./types/comms";
|
|
12
12
|
export type { BatchResponse, BatchCreateRequest, BatchUpdateRequest, } from "./types/batch";
|
|
13
|
+
export type { Lot, LotStatus, LotSelector, LotPayload, LotCreateInput, LotUpdateInput, ListLotsParams, ListLotsResponse, LotMemberDiff, ResolveLotResponse, LotProductSummary, ListLotProductsResponse, LotResolutionResult, } from "./types/lots";
|
|
13
14
|
export type { VariantResponse, VariantCreateRequest, VariantUpdateRequest, } from "./types/variant";
|
|
14
15
|
export type { BroadcastSendRequest } from "./types/broadcasts";
|
|
15
16
|
export type { AppConfigOptions } from "./api/appConfiguration";
|