@proveanything/smartlinks 1.15.16 → 1.15.18
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/README.md +8 -0
- package/dist/api/appObjects.d.ts +6 -0
- package/dist/api/appObjects.js +10 -0
- package/dist/api/navigation.js +3 -2
- package/dist/api/proof.d.ts +19 -1
- package/dist/api/proof.js +44 -0
- package/dist/docs/API_SUMMARY.md +90 -1
- package/dist/docs/app-objects.md +65 -0
- package/dist/docs/assets.md +81 -0
- package/dist/docs/proof-share-grants.md +232 -0
- package/dist/http.d.ts +19 -0
- package/dist/http.js +49 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/openapi.yaml +231 -0
- package/dist/types/appObjects.d.ts +14 -0
- package/dist/types/navigation.d.ts +5 -0
- package/dist/types/proof.d.ts +55 -0
- package/docs/API_SUMMARY.md +90 -1
- package/docs/app-objects.md +65 -0
- package/docs/assets.md +81 -0
- package/docs/proof-share-grants.md +232 -0
- package/openapi.yaml +231 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,6 +22,9 @@ If you're new to the SDK, this is the easiest path:
|
|
|
22
22
|
|
|
23
23
|
- [docs/ai.md](docs/ai.md) — AI responses, chat, RAG, voice, streaming, and product assistants
|
|
24
24
|
- [docs/analytics.md](docs/analytics.md) — fire-and-forget web analytics, tag scan telemetry, and dashboard queries
|
|
25
|
+
- [docs/proof-share-grants.md](docs/proof-share-grants.md) — delegated, scoped, revocable share links for a proof (album sharing, guest comments, proof-of-ownership)
|
|
26
|
+
- [docs/assets.md](docs/assets.md) — asset uploads, including resumable uploads for large files (video)
|
|
27
|
+
- [docs/app-objects.md](docs/app-objects.md) — cases, threads (comments), and records
|
|
25
28
|
- [docs/translations.md](docs/translations.md) — runtime translation lookup, browser-side caching, and translation admin flows
|
|
26
29
|
- [docs/widgets.md](docs/widgets.md) — embeddable React components
|
|
27
30
|
- [docs/realtime.md](docs/realtime.md) — subscriptions and live updates
|
|
@@ -33,6 +36,8 @@ If you're new to the SDK, this is the easiest path:
|
|
|
33
36
|
- **Build an AI assistant** → start with [docs/ai.md](docs/ai.md)
|
|
34
37
|
- **Track page views, clicks, or tag scans** → start with [docs/analytics.md](docs/analytics.md)
|
|
35
38
|
- **Translate dynamic content with local browser caching** → start with [docs/translations.md](docs/translations.md)
|
|
39
|
+
- **Share a proof / album by link, or add guest comments** → start with [docs/proof-share-grants.md](docs/proof-share-grants.md)
|
|
40
|
+
- **Upload large video with resume-on-reconnect** → see [Resumable uploads](docs/assets.md#resumable-uploads-large-files-eg-video)
|
|
36
41
|
- **Fetch collections/products** → see [Quick start](README.md#quick-start)
|
|
37
42
|
- **Authenticate admins or end users** → see [Authentication](README.md#authentication)
|
|
38
43
|
- **Upload and manage files** → see [Assets](README.md#assets)
|
|
@@ -44,6 +49,9 @@ For the full list of functions and types, see the API summary:
|
|
|
44
49
|
**Documentation:**
|
|
45
50
|
- [AI & Chat Completions](docs/ai.md) - Chat completions, RAG, voice integration
|
|
46
51
|
- [Analytics](docs/analytics.md) - Fire-and-forget analytics tracking, tag scans, and dashboard queries
|
|
52
|
+
- [Proof Share Grants](docs/proof-share-grants.md) - Delegated, revocable share links; guest comments; proof-of-ownership
|
|
53
|
+
- [Assets](docs/assets.md) - Uploads, resumable large-file uploads, token uploads, replace/versioning
|
|
54
|
+
- [App Objects](docs/app-objects.md) - Cases, threads (comments), records
|
|
47
55
|
- [Translations](docs/translations.md) - Runtime translation lookup, browser-side IndexedDB caching, and admin translation management
|
|
48
56
|
- [Widgets](docs/widgets.md) - Embeddable React components
|
|
49
57
|
- [Realtime](docs/realtime.md) - Realtime data updates
|
package/dist/api/appObjects.d.ts
CHANGED
|
@@ -85,6 +85,12 @@ export declare namespace app {
|
|
|
85
85
|
* Atomically appends to replies array, increments replyCount, updates lastReplyAt
|
|
86
86
|
*/
|
|
87
87
|
function reply(collectionId: string, appId: string, threadId: string, input: ReplyInput, admin?: boolean): Promise<AppThread>;
|
|
88
|
+
/**
|
|
89
|
+
* Delete a single reply from a thread by its reply id (moderation).
|
|
90
|
+
* DELETE /threads/:threadId/reply/:replyId
|
|
91
|
+
* Authorised for the reply's author, the proof owner, or a collection admin.
|
|
92
|
+
*/
|
|
93
|
+
function deleteReply(collectionId: string, appId: string, threadId: string, replyId: string, admin?: boolean): Promise<AppThread>;
|
|
88
94
|
/**
|
|
89
95
|
* Get aggregate statistics for threads
|
|
90
96
|
* POST /threads/aggregate
|
package/dist/api/appObjects.js
CHANGED
|
@@ -164,6 +164,16 @@ export var app;
|
|
|
164
164
|
return post(path, input);
|
|
165
165
|
}
|
|
166
166
|
threads.reply = reply;
|
|
167
|
+
/**
|
|
168
|
+
* Delete a single reply from a thread by its reply id (moderation).
|
|
169
|
+
* DELETE /threads/:threadId/reply/:replyId
|
|
170
|
+
* Authorised for the reply's author, the proof owner, or a collection admin.
|
|
171
|
+
*/
|
|
172
|
+
async function deleteReply(collectionId, appId, threadId, replyId, admin = false) {
|
|
173
|
+
const path = `${basePath(collectionId, appId, admin)}/${encodeURIComponent(threadId)}/reply/${encodeURIComponent(replyId)}`;
|
|
174
|
+
return del(path);
|
|
175
|
+
}
|
|
176
|
+
threads.deleteReply = deleteReply;
|
|
167
177
|
/**
|
|
168
178
|
* Get aggregate statistics for threads
|
|
169
179
|
* POST /threads/aggregate
|
package/dist/api/navigation.js
CHANGED
|
@@ -122,7 +122,7 @@ export var navigation;
|
|
|
122
122
|
type: 'smartlinks-navigate',
|
|
123
123
|
appId: link.appId,
|
|
124
124
|
path: link.kind === 'deep' ? deepPath(link.deepLinkId) : '/',
|
|
125
|
-
params: link.kind === 'deep' ? ((_b = link.params) !== null && _b !== void 0 ? _b : {}) : {},
|
|
125
|
+
params: (link.kind === 'deep' || link.kind === 'app') ? ((_b = link.params) !== null && _b !== void 0 ? _b : {}) : {},
|
|
126
126
|
target: (_c = link.target) !== null && _c !== void 0 ? _c : '_self',
|
|
127
127
|
}, '*');
|
|
128
128
|
return;
|
|
@@ -134,7 +134,8 @@ export var navigation;
|
|
|
134
134
|
const hash = link.kind === 'deep'
|
|
135
135
|
? `#${deepPath(link.deepLinkId)}${qs(link.params)}`
|
|
136
136
|
: `#/`;
|
|
137
|
-
const
|
|
137
|
+
const appParams = link.kind === 'app' && link.params ? qs(link.params).replace(/^\?/, '&') : '';
|
|
138
|
+
const url = `${win.location.pathname}?appId=${encodeURIComponent(link.appId)}${appParams}${hash}`;
|
|
138
139
|
if (link.target === '_blank') {
|
|
139
140
|
win.open(url, '_blank', windowFeatures());
|
|
140
141
|
}
|
package/dist/api/proof.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ProofResponse, ProofCreateRequest, ProofUpdateRequest, ProofClaimRequest } from "../types/proof";
|
|
1
|
+
import { ProofResponse, ProofCreateRequest, ProofUpdateRequest, ProofClaimRequest, ProofGrant, CreateGrantOptions, RedeemGrantOptions, RedeemGrantResult } from "../types/proof";
|
|
2
2
|
export declare namespace proof {
|
|
3
3
|
/**
|
|
4
4
|
* Retrieves a single Proof by Collection ID, Product ID, and Proof ID.
|
|
@@ -104,4 +104,22 @@ export declare namespace proof {
|
|
|
104
104
|
data: {
|
|
105
105
|
targetProductId: string;
|
|
106
106
|
}): Promise<ProofResponse>;
|
|
107
|
+
/**
|
|
108
|
+
* Create a share grant on a proof (owner / collection admin only). The returned
|
|
109
|
+
* grant includes `token` — the opaque bearer secret, available ONLY on this
|
|
110
|
+
* response. Embed it in a share link and hand recipients {@link redeemGrant} /
|
|
111
|
+
* {@link setGrantToken}.
|
|
112
|
+
*/
|
|
113
|
+
function createGrant(collectionId: string, productId: string, proofId: string, options: CreateGrantOptions): Promise<ProofGrant>;
|
|
114
|
+
/** List the active + past grants on a proof (owner / collection admin only). Tokens are never returned here. */
|
|
115
|
+
function listGrants(collectionId: string, productId: string, proofId: string): Promise<ProofGrant[]>;
|
|
116
|
+
/** Revoke a grant by id (owner / collection admin only). Takes effect immediately. */
|
|
117
|
+
function revokeGrant(collectionId: string, productId: string, proofId: string, grantId: string): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* Redeem a grant token (anonymous or signed-in). Records the redemption and
|
|
120
|
+
* returns the granted scope, or — for a `verify_owner` grant — an ownership
|
|
121
|
+
* assertion (never the account). After redeeming, call
|
|
122
|
+
* {@link setGrantToken} so subsequent data requests carry the token.
|
|
123
|
+
*/
|
|
124
|
+
function redeemGrant(collectionId: string, productId: string, proofId: string, token: string, options?: RedeemGrantOptions): Promise<RedeemGrantResult>;
|
|
107
125
|
}
|
package/dist/api/proof.js
CHANGED
|
@@ -156,4 +156,48 @@ export var proof;
|
|
|
156
156
|
return post(path, data);
|
|
157
157
|
}
|
|
158
158
|
proof.migrate = migrate;
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
// Share grants — delegated, scoped, revocable bearer access to this proof
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
function grantBase(collectionId, productId, proofId) {
|
|
163
|
+
return `/public/collection/${encodeURIComponent(collectionId)}/product/${encodeURIComponent(productId)}/proof/${encodeURIComponent(proofId)}/grant`;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Create a share grant on a proof (owner / collection admin only). The returned
|
|
167
|
+
* grant includes `token` — the opaque bearer secret, available ONLY on this
|
|
168
|
+
* response. Embed it in a share link and hand recipients {@link redeemGrant} /
|
|
169
|
+
* {@link setGrantToken}.
|
|
170
|
+
*/
|
|
171
|
+
async function createGrant(collectionId, productId, proofId, options) {
|
|
172
|
+
const body = { scope: options.scope };
|
|
173
|
+
if (options.audience)
|
|
174
|
+
body.audience = options.audience;
|
|
175
|
+
if (options.expiresAt)
|
|
176
|
+
body.expiresAt = options.expiresAt instanceof Date ? options.expiresAt.toISOString() : options.expiresAt;
|
|
177
|
+
return post(grantBase(collectionId, productId, proofId), body);
|
|
178
|
+
}
|
|
179
|
+
proof.createGrant = createGrant;
|
|
180
|
+
/** List the active + past grants on a proof (owner / collection admin only). Tokens are never returned here. */
|
|
181
|
+
async function listGrants(collectionId, productId, proofId) {
|
|
182
|
+
return request(grantBase(collectionId, productId, proofId));
|
|
183
|
+
}
|
|
184
|
+
proof.listGrants = listGrants;
|
|
185
|
+
/** Revoke a grant by id (owner / collection admin only). Takes effect immediately. */
|
|
186
|
+
async function revokeGrant(collectionId, productId, proofId, grantId) {
|
|
187
|
+
return del(`${grantBase(collectionId, productId, proofId)}/${encodeURIComponent(grantId)}`);
|
|
188
|
+
}
|
|
189
|
+
proof.revokeGrant = revokeGrant;
|
|
190
|
+
/**
|
|
191
|
+
* Redeem a grant token (anonymous or signed-in). Records the redemption and
|
|
192
|
+
* returns the granted scope, or — for a `verify_owner` grant — an ownership
|
|
193
|
+
* assertion (never the account). After redeeming, call
|
|
194
|
+
* {@link setGrantToken} so subsequent data requests carry the token.
|
|
195
|
+
*/
|
|
196
|
+
async function redeemGrant(collectionId, productId, proofId, token, options) {
|
|
197
|
+
const body = { token };
|
|
198
|
+
if (options === null || options === void 0 ? void 0 : options.guestName)
|
|
199
|
+
body.guestName = options.guestName;
|
|
200
|
+
return post(`${grantBase(collectionId, productId, proofId)}/redeem`, body);
|
|
201
|
+
}
|
|
202
|
+
proof.redeemGrant = redeemGrant;
|
|
159
203
|
})(proof || (proof = {}));
|
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.18 | Generated: 2026-08-19T06:40:47.505Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -170,6 +170,12 @@ Replace or augment globally applied custom headers.
|
|
|
170
170
|
**setBearerToken**(token: string | undefined) → `void`
|
|
171
171
|
Allows setting the bearerToken at runtime (e.g. after login/logout). Clears the HTTP cache whenever the token actually changes so that stale user-scoped responses (e.g. /account/profile) are not served after a login or logout event.
|
|
172
172
|
|
|
173
|
+
**setGrantToken**(token: string | undefined) → `void`
|
|
174
|
+
Set (or clear) the per-proof share-grant token. When set, it is attached as the `X-Grant-Token` header on every request, so a recipient who has opened a shared link can read/comment on the granted proof's data. Pass `undefined` to clear it. The server re-checks the grant against the database on every request, so calling `revokeGrant` invalidates an in-flight token immediately. Clears the GET cache on change so grant-tier responses are not served after the token changes. ```ts // On opening ?proofId=…&shareToken=abc setGrantToken(shareToken) const { attestations } = await proof.get(...) // now sees owner-tier memories ```
|
|
175
|
+
|
|
176
|
+
**getGrantToken**() → `string | undefined`
|
|
177
|
+
Returns the currently-set share-grant token, or `undefined`.
|
|
178
|
+
|
|
173
179
|
**getBearerToken**() → `string | undefined`
|
|
174
180
|
Returns the bearer token currently held by the SDK, or `undefined` if none is set. In proxy mode, credentials are held by the parent frame, not the local SDK, so this returns `undefined` even when the caller is authenticated.
|
|
175
181
|
|
|
@@ -1987,6 +1993,10 @@ interface CreateThreadInput {
|
|
|
1987
1993
|
data?: Record<string, unknown>
|
|
1988
1994
|
owner?: Record<string, unknown>
|
|
1989
1995
|
admin?: Record<string, unknown> // admin only
|
|
1996
|
+
* Optional atomic first reply. Posting a comment no longer needs a separate
|
|
1997
|
+
* create-thread-then-reply round trip (which could orphan an empty thread on
|
|
1998
|
+
* partial failure). The reply is stored with a generated `id` and timestamp.
|
|
1999
|
+
firstReply?: ReplyInput
|
|
1990
2000
|
}
|
|
1991
2001
|
```
|
|
1992
2002
|
|
|
@@ -7287,6 +7297,50 @@ interface ProofFieldsConfig {
|
|
|
7287
7297
|
}
|
|
7288
7298
|
```
|
|
7289
7299
|
|
|
7300
|
+
**GrantAudience** (interface)
|
|
7301
|
+
```typescript
|
|
7302
|
+
interface GrantAudience {
|
|
7303
|
+
kind: 'public_link' | 'named'
|
|
7304
|
+
email?: string
|
|
7305
|
+
userId?: string
|
|
7306
|
+
}
|
|
7307
|
+
```
|
|
7308
|
+
|
|
7309
|
+
**ProofGrant** (interface)
|
|
7310
|
+
```typescript
|
|
7311
|
+
interface ProofGrant {
|
|
7312
|
+
grantId: string
|
|
7313
|
+
proofId: string
|
|
7314
|
+
productId?: string | null
|
|
7315
|
+
scope: GrantScope[]
|
|
7316
|
+
audience: GrantAudience
|
|
7317
|
+
createdBy: string
|
|
7318
|
+
expiresAt?: string | null
|
|
7319
|
+
revokedAt?: string | null
|
|
7320
|
+
redeemedBy?: { userId?: string; guestName?: string; redeemedAt: string }
|
|
7321
|
+
redeemCount: number
|
|
7322
|
+
createdAt: string
|
|
7323
|
+
updatedAt: string
|
|
7324
|
+
token?: string
|
|
7325
|
+
}
|
|
7326
|
+
```
|
|
7327
|
+
|
|
7328
|
+
**CreateGrantOptions** (interface)
|
|
7329
|
+
```typescript
|
|
7330
|
+
interface CreateGrantOptions {
|
|
7331
|
+
scope: GrantScope[]
|
|
7332
|
+
audience?: GrantAudience
|
|
7333
|
+
expiresAt?: Date | string
|
|
7334
|
+
}
|
|
7335
|
+
```
|
|
7336
|
+
|
|
7337
|
+
**RedeemGrantOptions** (interface)
|
|
7338
|
+
```typescript
|
|
7339
|
+
interface RedeemGrantOptions {
|
|
7340
|
+
guestName?: string
|
|
7341
|
+
}
|
|
7342
|
+
```
|
|
7343
|
+
|
|
7290
7344
|
**ProofResponse** = `Proof`
|
|
7291
7345
|
|
|
7292
7346
|
**ProofUpdateRequest** = `Partial<ProofCreateRequest>`
|
|
@@ -7297,6 +7351,10 @@ interface ProofFieldsConfig {
|
|
|
7297
7351
|
|
|
7298
7352
|
**ProofFieldDef** = `ScopedFieldDef & { scope?: ProofFieldScope }`
|
|
7299
7353
|
|
|
7354
|
+
**GrantScope** = `'read' | 'comment' | 'admin' | 'verify_owner'`
|
|
7355
|
+
|
|
7356
|
+
**RedeemGrantResult** = ``
|
|
7357
|
+
|
|
7300
7358
|
### qr
|
|
7301
7359
|
|
|
7302
7360
|
**QrShortCodeLookupResponse** (interface)
|
|
@@ -8381,6 +8439,13 @@ Soft delete a thread DELETE /threads/:threadId
|
|
|
8381
8439
|
admin: boolean = false) → `Promise<AppThread>`
|
|
8382
8440
|
Add a reply to a thread POST /threads/:threadId/reply Atomically appends to replies array, increments replyCount, updates lastReplyAt
|
|
8383
8441
|
|
|
8442
|
+
**deleteReply**(collectionId: string,
|
|
8443
|
+
appId: string,
|
|
8444
|
+
threadId: string,
|
|
8445
|
+
replyId: string,
|
|
8446
|
+
admin: boolean = false) → `Promise<AppThread>`
|
|
8447
|
+
Delete a single reply from a thread by its reply id (moderation). DELETE /threads/:threadId/reply/:replyId Authorised for the reply's author, the proof owner, or a collection admin.
|
|
8448
|
+
|
|
8384
8449
|
**aggregate**(collectionId: string,
|
|
8385
8450
|
appId: string,
|
|
8386
8451
|
request: AggregateRequest,
|
|
@@ -9937,6 +10002,30 @@ Get proofs for a batch (admin only). GET /admin/collection/:collectionId/product
|
|
|
9937
10002
|
data: { targetProductId: string }) → `Promise<ProofResponse>`
|
|
9938
10003
|
Migrate a proof to a different product within the same collection (admin only). Because the Firestore ledger document ID is `{productId}-{proofId}`, a proof cannot simply be re-assigned to another product by updating a field — the document must be re-keyed. This endpoint handles that atomically: 1. Reads the source ledger document (`{sourceProductId}-{proofId}`). 2. Writes a new document (`{targetProductId}-{proofId}`) with `productId` and `proofGroup` updated. The short `proofId` (nanoid) is unchanged. 3. Writes a migration history entry to the new document's `history` subcollection (snapshot of the original proof + migration metadata). 4. Copies all subcollections — `assets`, `attestations`, `history` — from the old document to the new one. 5. Deletes the old subcollections and then the old document. Repeated migrations are safe — each one appends a history record; no migration metadata is stored on the proof document itself. ```typescript const migrated = await proof.migrate('coll_123', 'prod_old', 'proof_abc', { targetProductId: 'prod_new', }) console.log(migrated.productId) // 'prod_new' ```
|
|
9939
10004
|
|
|
10005
|
+
**createGrant**(collectionId: string,
|
|
10006
|
+
productId: string,
|
|
10007
|
+
proofId: string,
|
|
10008
|
+
options: CreateGrantOptions) → `Promise<ProofGrant>`
|
|
10009
|
+
Create a share grant on a proof (owner / collection admin only). The returned grant includes `token` — the opaque bearer secret, available ONLY on this response. Embed it in a share link and hand recipients {@link redeemGrant} / {@link setGrantToken}.
|
|
10010
|
+
|
|
10011
|
+
**listGrants**(collectionId: string,
|
|
10012
|
+
productId: string,
|
|
10013
|
+
proofId: string) → `Promise<ProofGrant[]>`
|
|
10014
|
+
List the active + past grants on a proof (owner / collection admin only). Tokens are never returned here.
|
|
10015
|
+
|
|
10016
|
+
**revokeGrant**(collectionId: string,
|
|
10017
|
+
productId: string,
|
|
10018
|
+
proofId: string,
|
|
10019
|
+
grantId: string) → `Promise<void>`
|
|
10020
|
+
Revoke a grant by id (owner / collection admin only). Takes effect immediately.
|
|
10021
|
+
|
|
10022
|
+
**redeemGrant**(collectionId: string,
|
|
10023
|
+
productId: string,
|
|
10024
|
+
proofId: string,
|
|
10025
|
+
token: string,
|
|
10026
|
+
options?: RedeemGrantOptions) → `Promise<RedeemGrantResult>`
|
|
10027
|
+
Redeem a grant token (anonymous or signed-in). Records the redemption and returns the granted scope, or — for a `verify_owner` grant — an ownership assertion (never the account). After redeeming, call {@link setGrantToken} so subsequent data requests carry the token.
|
|
10028
|
+
|
|
9940
10029
|
### publicClient
|
|
9941
10030
|
|
|
9942
10031
|
**chat**(collectionId: string,
|
package/dist/docs/app-objects.md
CHANGED
|
@@ -429,6 +429,71 @@ const productComments = await app.threads.list(collectionId, appId, {
|
|
|
429
429
|
});
|
|
430
430
|
```
|
|
431
431
|
|
|
432
|
+
### Anchoring to app entities and proofs
|
|
433
|
+
|
|
434
|
+
Anchor a thread to your own entity with `parentType` + `parentId`. `parentId` is a
|
|
435
|
+
free-form string (SmartLinks short ids, not just UUIDs), so you can use your app's
|
|
436
|
+
native ids directly — no need to stash them in `body` or a tag.
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
await app.threads.create(collectionId, appId, {
|
|
440
|
+
parentType: 'memory',
|
|
441
|
+
parentId: memoryId, // e.g. "dVdthBQLAjQEnitU7aWy" — text, not a UUID
|
|
442
|
+
proofId, // anchor to a proof (enables grant-tier reads)
|
|
443
|
+
body: { text: 'Lovely photo' },
|
|
444
|
+
});
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
List filters that pair with this (see `ThreadListQueryParams`):
|
|
448
|
+
|
|
449
|
+
| Param | Purpose |
|
|
450
|
+
|-------|---------|
|
|
451
|
+
| `parentType` / `parentId` | one app entity's threads |
|
|
452
|
+
| `parentIds` | **batch** — every thread for many entities in one call (e.g. a feed) |
|
|
453
|
+
| `proofId` / `productId` | threads anchored to a proof / product |
|
|
454
|
+
|
|
455
|
+
```typescript
|
|
456
|
+
// One request for the 30 memories currently on screen:
|
|
457
|
+
const feed = await app.threads.list(collectionId, appId, {
|
|
458
|
+
parentType: 'memory',
|
|
459
|
+
parentIds: visibleMemoryIds,
|
|
460
|
+
sort: 'createdAt:desc',
|
|
461
|
+
});
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
### Atomic first comment (`firstReply`)
|
|
465
|
+
|
|
466
|
+
Posting the first comment no longer needs a create-thread-then-reply two-step (which
|
|
467
|
+
could leave an orphan empty thread if the reply failed). Pass `firstReply` to create
|
|
468
|
+
the thread and its first reply in one atomic call:
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
await app.threads.create(collectionId, appId, {
|
|
472
|
+
parentType: 'memory', parentId: memoryId, proofId,
|
|
473
|
+
firstReply: { text: 'Lovely photo', authorName: 'Sam' },
|
|
474
|
+
});
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### Deleting a reply (moderation)
|
|
478
|
+
|
|
479
|
+
Replies carry a stable `id`. Remove a single one — authorised for the reply's author,
|
|
480
|
+
the proof owner, or a collection admin:
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
await app.threads.deleteReply(collectionId, appId, threadId, replyId);
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Grant-tier access (private, shareable comments)
|
|
487
|
+
|
|
488
|
+
A comment thread on a shared album should be `visibility: 'owner'` (private to the
|
|
489
|
+
proof), with access coming from a **share grant** rather than opening the data to the
|
|
490
|
+
world. A holder of an active `read`/`comment` grant on the proof is treated as
|
|
491
|
+
owner-tier **for that proof only**. Enable it with the `publicCreate.threads.grant`
|
|
492
|
+
policy branch and carry the token via `setGrantToken`.
|
|
493
|
+
|
|
494
|
+
See **[Proof Share Grants](proof-share-grants.md)** for the full flow (create/redeem
|
|
495
|
+
grants, guest commenting, revocation, and the app config).
|
|
496
|
+
|
|
432
497
|
---
|
|
433
498
|
|
|
434
499
|
## Records
|
package/dist/docs/assets.md
CHANGED
|
@@ -243,6 +243,87 @@ await Api.asset.bulkDelete({
|
|
|
243
243
|
|
|
244
244
|
---
|
|
245
245
|
|
|
246
|
+
## Resumable uploads (large files, e.g. video)
|
|
247
|
+
|
|
248
|
+
`asset.upload()` is a single request — if the connection drops, the whole file
|
|
249
|
+
restarts. For large files on flaky connections (e.g. phone video), use a
|
|
250
|
+
**resumable** upload: the file is chunked directly to storage and can be paused,
|
|
251
|
+
resumed, and — crucially — **continued after a page reload or app restart**.
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
import { asset } from '@proveanything/smartlinks'
|
|
255
|
+
|
|
256
|
+
// 1. Open a resumable upload.
|
|
257
|
+
const handle = await asset.createResumableUpload({
|
|
258
|
+
file, // a File (input[type=file] / drag-drop)
|
|
259
|
+
scope: { type: 'proof', collectionId, productId, proofId },
|
|
260
|
+
appId: 'photo-memory',
|
|
261
|
+
// token: uploadToken, // for public/token uploads (see below)
|
|
262
|
+
})
|
|
263
|
+
|
|
264
|
+
// 2. Persist handle.id so the upload survives a reload.
|
|
265
|
+
localStorage.setItem('pendingUpload', handle.id)
|
|
266
|
+
|
|
267
|
+
// 3. Upload. Resumes automatically from the offset storage already holds.
|
|
268
|
+
const uploaded = await handle.start({
|
|
269
|
+
onProgress: (pct) => setProgress(pct), // 0–100
|
|
270
|
+
signal: abortController.signal, // optional: cancel a stalled upload
|
|
271
|
+
})
|
|
272
|
+
localStorage.removeItem('pendingUpload')
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Pause / resume, and resume after a reload
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
handle.pause() // stop after the current chunk
|
|
279
|
+
await handle.resume({ onProgress }) // continue
|
|
280
|
+
|
|
281
|
+
// After a reload / app kill — rehydrate from the persisted id and the same file:
|
|
282
|
+
const saved = localStorage.getItem('pendingUpload')
|
|
283
|
+
if (saved) {
|
|
284
|
+
const handle = await asset.resumeUpload(saved, file)
|
|
285
|
+
await handle.start({ onProgress }) // continues, does not restart
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### API
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
namespace asset {
|
|
293
|
+
createResumableUpload(options: CreateResumableUploadOptions): Promise<ResumableUploadHandle>
|
|
294
|
+
resumeUpload(handleId: string, file: File): Promise<ResumableUploadHandle>
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
interface CreateResumableUploadOptions {
|
|
298
|
+
file: File
|
|
299
|
+
scope: { type: 'collection'; collectionId: string }
|
|
300
|
+
| { type: 'product'; collectionId: string; productId: string }
|
|
301
|
+
| { type: 'proof'; collectionId: string; productId: string; proofId: string }
|
|
302
|
+
name?: string
|
|
303
|
+
metadata?: Record<string, any>
|
|
304
|
+
appId?: string
|
|
305
|
+
admin?: boolean // admin route (default is the public route)
|
|
306
|
+
token?: string // upload token for public/unauthenticated uploads
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
interface ResumableUploadHandle {
|
|
310
|
+
readonly id: string // durable, persistable — pass to resumeUpload() after a reload
|
|
311
|
+
readonly size: number // total bytes
|
|
312
|
+
start(opts?: { onProgress?: (pct: number) => void; signal?: AbortSignal }): Promise<Asset>
|
|
313
|
+
pause(): void
|
|
314
|
+
resume(opts?: { onProgress?: (pct: number) => void; signal?: AbortSignal }): Promise<Asset>
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Notes**
|
|
319
|
+
|
|
320
|
+
- `handle.id` is an opaque string that carries everything needed to resume — persist it as-is.
|
|
321
|
+
- On completion, `start()`/`resume()` resolves to the finalized `Asset` record.
|
|
322
|
+
- `pause()` causes the in-flight `start()`/`resume()` promise to reject with an `UploadPausedError`; call `resume()` to continue.
|
|
323
|
+
- `asset.upload()` also now accepts an `AbortSignal` (`upload({ ..., signal })`) for cancelling a stalled single-shot upload.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
246
327
|
## Public (token-based) uploads
|
|
247
328
|
|
|
248
329
|
For anonymous or contact-initiated uploads from the portal — no admin auth required.
|