@proveanything/smartlinks 1.16.6 → 1.17.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.
@@ -1,4 +1,4 @@
1
- import type { Attestation, CreateAttestationInput, ListAttestationsParams, ListAttestationsResponse, AttestationSummaryParams, AttestationSummaryResponse, PublicAttestationSummaryResponse, AttestationLatestParams, AttestationLatestResponse, PublicAttestationLatestResponse, AttestationVerifyParams, ChainVerifyResult, AttestationTreeSummaryParams, AttestationTreeSummaryResponse, PublicAttestationTreeSummaryResponse, AttestationTreeLatestParams, AttestationTreeLatestResponse, PublicAttestationTreeLatestResponse, PublicListAttestationsResponse, OwnerAttestationInput, CreateOwnerAttestationResponse } from "../types/attestations";
1
+ import type { Attestation, CreateAttestationInput, ListAttestationsParams, ListAttestationsResponse, AttestationSummaryParams, AttestationSummaryResponse, PublicAttestationSummaryResponse, AttestationLatestParams, AttestationLatestResponse, PublicAttestationLatestResponse, AttestationVerifyParams, ChainVerifyResult, AttestationTreeSummaryParams, AttestationTreeSummaryResponse, PublicAttestationTreeSummaryResponse, AttestationTreeLatestParams, AttestationTreeLatestResponse, PublicAttestationTreeLatestResponse, PublicListAttestationsResponse, OwnerAttestationInput, CreateOwnerAttestationResponse, ModerateAttestationInput, ModerateAttestationResponse } from "../types/attestations";
2
2
  /**
3
3
  * Postgres-backed Attestations API (v2).
4
4
  *
@@ -13,9 +13,11 @@ import type { Attestation, CreateAttestationInput, ListAttestationsParams, ListA
13
13
  * admin session or bearer token. All three data zones are returned, and the
14
14
  * business can write any zone/visibility ({@link create}).
15
15
  * - **Public** endpoints (`/public/collection/:id/attestations`) are read-only,
16
- * EXCEPT {@link publicCreate}: the proof OWNER may author an attestation on
17
- * their own item (value + ownerData, visibility public|owner never the admin
18
- * zone). Owner elevation is via `Authorization: Bearer <Firebase ID token>`.
16
+ * EXCEPT {@link publicCreate} (the proof OWNER, or a `contribute`-grant holder,
17
+ * authors an attestation value + ownerData, visibility public|owner, never the
18
+ * admin zone) and {@link moderate} (owner/admin approves or rejects a contributed
19
+ * record). Owner elevation is via `Authorization: Bearer <Firebase ID token>`;
20
+ * contributor access via a grant token set with `setGrantToken`.
19
21
  *
20
22
  * @see docs/attestations.md
21
23
  */
@@ -211,22 +213,49 @@ export declare namespace attestations {
211
213
  */
212
214
  function publicList(collectionId: string, params: ListAttestationsParams): Promise<PublicListAttestationsResponse>;
213
215
  /**
214
- * Create an OWNER-authored attestation (public write) — the counterpart to the
215
- * admin {@link create}. The authenticated caller must OWN the linked proof
216
- * (identity, not a read grant). Guardrails enforced server-side: they may write
217
- * `value` + `ownerData` only (`adminData` is dropped), `visibility` is clamped
218
- * to `'public' | 'owner'`, and `authorId` is forced to the caller. The record
219
- * joins the same tamper-evident hash chain.
216
+ * Create a public attestation — the counterpart to the admin {@link create}.
217
+ * Authorised two ways, same call (the server decides from the request):
218
+ * 1. the proof OWNER (identity, via `Authorization: Bearer <Firebase ID token>`)
219
+ * adds an attestation to their own item; or
220
+ * 2. a holder of a `contribute`-scope grant adds one call
221
+ * {@link setGrantToken} with the grant token first; for a public-link
222
+ * (anonymous) grant, pass `guestName` for attribution.
223
+ *
224
+ * Guardrails (server-enforced): `value` + `ownerData` only (`adminData` dropped),
225
+ * `visibility` clamped to `'public' | 'owner'`, `authorId`/`grantId` server-stamped.
226
+ * If the contribute grant was issued with `moderate: true`, the returned record
227
+ * has `moderationStatus: 'pending'` — held to the owner until {@link moderate}.
228
+ * The record joins the same tamper-evident hash chain.
220
229
  * POST /public/collection/:collectionId/attestations
221
230
  * ```ts
231
+ * // Owner:
222
232
  * await attestations.publicCreate('coll_123', {
223
233
  * subjectType: 'proof', subjectId: 'proof_1',
224
234
  * attestationType: 'condition-report',
225
235
  * value: { grade: 'excellent' }, visibility: 'public',
226
236
  * })
237
+ * // Contributor on a shared link:
238
+ * setGrantToken(shareToken)
239
+ * await attestations.publicCreate('coll_123', {
240
+ * subjectType: 'proof', subjectId: 'proof_1',
241
+ * attestationType: 'photo', value: { url }, visibility: 'public',
242
+ * guestName: 'Sam',
243
+ * })
227
244
  * ```
228
245
  */
229
246
  function publicCreate(collectionId: string, data: OwnerAttestationInput): Promise<CreateOwnerAttestationResponse>;
247
+ /**
248
+ * Moderate a contributed attestation (proof OWNER by identity, or collection
249
+ * admin). `'approve'` releases it to its declared visibility; `'reject'` keeps
250
+ * it author + admin only. Only the `moderationStatus` changes — the hashed fact
251
+ * and its chain are untouched. Find pending items with
252
+ * {@link publicList} + `moderationStatus: 'pending'`.
253
+ * POST /public/collection/:collectionId/attestations/:attestationId/moderate
254
+ * ```ts
255
+ * await attestations.moderate('coll_123', 'att_uuid', { decision: 'approve' })
256
+ * ```
257
+ */
258
+ function moderate(collectionId: string, attestationId: string, input: ModerateAttestationInput): Promise<ModerateAttestationResponse>;
230
259
  /**
231
260
  * Time-series summary (public).
232
261
  *
@@ -26,9 +26,11 @@ function buildAttestationQuery(params) {
26
26
  * admin session or bearer token. All three data zones are returned, and the
27
27
  * business can write any zone/visibility ({@link create}).
28
28
  * - **Public** endpoints (`/public/collection/:id/attestations`) are read-only,
29
- * EXCEPT {@link publicCreate}: the proof OWNER may author an attestation on
30
- * their own item (value + ownerData, visibility public|owner never the admin
31
- * zone). Owner elevation is via `Authorization: Bearer <Firebase ID token>`.
29
+ * EXCEPT {@link publicCreate} (the proof OWNER, or a `contribute`-grant holder,
30
+ * authors an attestation value + ownerData, visibility public|owner, never the
31
+ * admin zone) and {@link moderate} (owner/admin approves or rejects a contributed
32
+ * record). Owner elevation is via `Authorization: Bearer <Firebase ID token>`;
33
+ * contributor access via a grant token set with `setGrantToken`.
32
34
  *
33
35
  * @see docs/attestations.md
34
36
  */
@@ -277,19 +279,34 @@ export var attestations;
277
279
  }
278
280
  attestations.publicList = publicList;
279
281
  /**
280
- * Create an OWNER-authored attestation (public write) — the counterpart to the
281
- * admin {@link create}. The authenticated caller must OWN the linked proof
282
- * (identity, not a read grant). Guardrails enforced server-side: they may write
283
- * `value` + `ownerData` only (`adminData` is dropped), `visibility` is clamped
284
- * to `'public' | 'owner'`, and `authorId` is forced to the caller. The record
285
- * joins the same tamper-evident hash chain.
282
+ * Create a public attestation — the counterpart to the admin {@link create}.
283
+ * Authorised two ways, same call (the server decides from the request):
284
+ * 1. the proof OWNER (identity, via `Authorization: Bearer <Firebase ID token>`)
285
+ * adds an attestation to their own item; or
286
+ * 2. a holder of a `contribute`-scope grant adds one call
287
+ * {@link setGrantToken} with the grant token first; for a public-link
288
+ * (anonymous) grant, pass `guestName` for attribution.
289
+ *
290
+ * Guardrails (server-enforced): `value` + `ownerData` only (`adminData` dropped),
291
+ * `visibility` clamped to `'public' | 'owner'`, `authorId`/`grantId` server-stamped.
292
+ * If the contribute grant was issued with `moderate: true`, the returned record
293
+ * has `moderationStatus: 'pending'` — held to the owner until {@link moderate}.
294
+ * The record joins the same tamper-evident hash chain.
286
295
  * POST /public/collection/:collectionId/attestations
287
296
  * ```ts
297
+ * // Owner:
288
298
  * await attestations.publicCreate('coll_123', {
289
299
  * subjectType: 'proof', subjectId: 'proof_1',
290
300
  * attestationType: 'condition-report',
291
301
  * value: { grade: 'excellent' }, visibility: 'public',
292
302
  * })
303
+ * // Contributor on a shared link:
304
+ * setGrantToken(shareToken)
305
+ * await attestations.publicCreate('coll_123', {
306
+ * subjectType: 'proof', subjectId: 'proof_1',
307
+ * attestationType: 'photo', value: { url }, visibility: 'public',
308
+ * guestName: 'Sam',
309
+ * })
293
310
  * ```
294
311
  */
295
312
  async function publicCreate(collectionId, data) {
@@ -297,6 +314,22 @@ export var attestations;
297
314
  return post(path, data);
298
315
  }
299
316
  attestations.publicCreate = publicCreate;
317
+ /**
318
+ * Moderate a contributed attestation (proof OWNER by identity, or collection
319
+ * admin). `'approve'` releases it to its declared visibility; `'reject'` keeps
320
+ * it author + admin only. Only the `moderationStatus` changes — the hashed fact
321
+ * and its chain are untouched. Find pending items with
322
+ * {@link publicList} + `moderationStatus: 'pending'`.
323
+ * POST /public/collection/:collectionId/attestations/:attestationId/moderate
324
+ * ```ts
325
+ * await attestations.moderate('coll_123', 'att_uuid', { decision: 'approve' })
326
+ * ```
327
+ */
328
+ async function moderate(collectionId, attestationId, input) {
329
+ const path = `/public/collection/${encodeURIComponent(collectionId)}/attestations/${encodeURIComponent(attestationId)}/moderate`;
330
+ return post(path, input);
331
+ }
332
+ attestations.moderate = moderate;
300
333
  /**
301
334
  * Time-series summary (public).
302
335
  *
@@ -37,6 +37,8 @@ export { containers } from "./containers";
37
37
  export { lots } from "./lots";
38
38
  export { loyalty } from "./loyalty";
39
39
  export { translations } from "./translations";
40
+ export { integrations } from "./integrations";
41
+ export { secrets } from "./secrets";
40
42
  export { config } from "./config";
41
43
  export { http } from "./http";
42
44
  export { navigation } from "./navigation";
package/dist/api/index.js CHANGED
@@ -40,6 +40,8 @@ export { containers } from "./containers";
40
40
  export { lots } from "./lots";
41
41
  export { loyalty } from "./loyalty";
42
42
  export { translations } from "./translations";
43
+ export { integrations } from "./integrations";
44
+ export { secrets } from "./secrets";
43
45
  export { config } from "./config";
44
46
  export { http } from "./http";
45
47
  export { navigation } from "./navigation";
@@ -0,0 +1,28 @@
1
+ import type { IntegrationFlow, CreateFlowInput, UpdateFlowInput, ListFlowsQuery, FlowList, RunFlowInput, RunFlowResult, RunFlowSummary, RunFlowEnqueued } from "../types/integrations";
2
+ export declare namespace integrations {
3
+ /** List flows in a collection. GET /integrations/flows */
4
+ function listFlows(collectionId: string, query?: ListFlowsQuery): Promise<FlowList>;
5
+ /** Create a flow. POST /integrations/flows */
6
+ function createFlow(collectionId: string, input: CreateFlowInput): Promise<IntegrationFlow>;
7
+ /** Get one flow. GET /integrations/flows/:id */
8
+ function getFlow(collectionId: string, id: string): Promise<IntegrationFlow>;
9
+ /** Update whitelisted fields. PUT /integrations/flows/:id */
10
+ function updateFlow(collectionId: string, id: string, input: UpdateFlowInput): Promise<IntegrationFlow>;
11
+ /** Soft-delete a flow. DELETE /integrations/flows/:id */
12
+ function deleteFlow(collectionId: string, id: string): Promise<{
13
+ deleted: boolean;
14
+ }>;
15
+ /**
16
+ * Run a flow now. POST /integrations/flows/:id/run
17
+ * - inline (default): resolves and returns the run summary.
18
+ * - options.async: enqueue on the worker, returns { enqueued: true }.
19
+ * Pass options.entityId to run for a single source entity.
20
+ */
21
+ function runFlow(collectionId: string, id: string, options?: RunFlowInput & {
22
+ async?: boolean;
23
+ }): Promise<RunFlowResult>;
24
+ /** Type guard: the run executed inline and returned a summary. */
25
+ function isRunSummary(r: RunFlowResult): r is RunFlowSummary;
26
+ /** Type guard: the run was enqueued (async). */
27
+ function isRunEnqueued(r: RunFlowResult): r is RunFlowEnqueued;
28
+ }
@@ -0,0 +1,82 @@
1
+ // src/api/integrations.ts
2
+ //
3
+ // Integration flow management + execution. Flows model input/output pipelines
4
+ // (inbound: fetch external -> write entity; outbound: read entity -> transform ->
5
+ // send). Credentials live in the secret store (see the `secrets` namespace); a flow
6
+ // only carries an opaque credentialRef in config.connection.auth.
7
+ //
8
+ // Endpoints: /admin/collection/:collectionId/integrations/flows
9
+ var __rest = (this && this.__rest) || function (s, e) {
10
+ var t = {};
11
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
12
+ t[p] = s[p];
13
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
14
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
15
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
16
+ t[p[i]] = s[p[i]];
17
+ }
18
+ return t;
19
+ };
20
+ import { request, post, put, del } from "../http";
21
+ function enc(v) { return encodeURIComponent(v); }
22
+ function encodeQuery(params = {}) {
23
+ const search = new URLSearchParams();
24
+ for (const [key, value] of Object.entries(params)) {
25
+ if (value === undefined || value === null || value === "")
26
+ continue;
27
+ search.set(key, typeof value === "boolean" ? (value ? "true" : "false") : String(value));
28
+ }
29
+ const qs = search.toString();
30
+ return qs ? `?${qs}` : "";
31
+ }
32
+ export var integrations;
33
+ (function (integrations) {
34
+ const base = (collectionId) => `/admin/collection/${enc(collectionId)}/integrations/flows`;
35
+ /** List flows in a collection. GET /integrations/flows */
36
+ async function listFlows(collectionId, query = {}) {
37
+ return request(`${base(collectionId)}${encodeQuery(query)}`);
38
+ }
39
+ integrations.listFlows = listFlows;
40
+ /** Create a flow. POST /integrations/flows */
41
+ async function createFlow(collectionId, input) {
42
+ return post(base(collectionId), input);
43
+ }
44
+ integrations.createFlow = createFlow;
45
+ /** Get one flow. GET /integrations/flows/:id */
46
+ async function getFlow(collectionId, id) {
47
+ return request(`${base(collectionId)}/${enc(id)}`);
48
+ }
49
+ integrations.getFlow = getFlow;
50
+ /** Update whitelisted fields. PUT /integrations/flows/:id */
51
+ async function updateFlow(collectionId, id, input) {
52
+ return put(`${base(collectionId)}/${enc(id)}`, input);
53
+ }
54
+ integrations.updateFlow = updateFlow;
55
+ /** Soft-delete a flow. DELETE /integrations/flows/:id */
56
+ async function deleteFlow(collectionId, id) {
57
+ return del(`${base(collectionId)}/${enc(id)}`);
58
+ }
59
+ integrations.deleteFlow = deleteFlow;
60
+ /**
61
+ * Run a flow now. POST /integrations/flows/:id/run
62
+ * - inline (default): resolves and returns the run summary.
63
+ * - options.async: enqueue on the worker, returns { enqueued: true }.
64
+ * Pass options.entityId to run for a single source entity.
65
+ */
66
+ async function runFlow(collectionId, id, options = {}) {
67
+ const { async: runAsync } = options, body = __rest(options, ["async"]);
68
+ const qs = runAsync ? "?async=true" : "";
69
+ return post(`${base(collectionId)}/${enc(id)}/run${qs}`, body);
70
+ }
71
+ integrations.runFlow = runFlow;
72
+ /** Type guard: the run executed inline and returned a summary. */
73
+ function isRunSummary(r) {
74
+ return r.status !== undefined;
75
+ }
76
+ integrations.isRunSummary = isRunSummary;
77
+ /** Type guard: the run was enqueued (async). */
78
+ function isRunEnqueued(r) {
79
+ return r.enqueued === true;
80
+ }
81
+ integrations.isRunEnqueued = isRunEnqueued;
82
+ })(integrations || (integrations = {}));
package/dist/api/proof.js CHANGED
@@ -222,6 +222,9 @@ export var proof;
222
222
  body.audience = options.audience;
223
223
  if (options.expiresAt)
224
224
  body.expiresAt = options.expiresAt instanceof Date ? options.expiresAt.toISOString() : options.expiresAt;
225
+ // Only applied server-side when scope includes 'contribute'.
226
+ if (options.moderate !== undefined)
227
+ body.moderate = options.moderate;
225
228
  return post(grantBase(collectionId, productId, proofId), body);
226
229
  }
227
230
  proof.createGrant = createGrant;
@@ -0,0 +1,15 @@
1
+ import type { SecretMeta, SecretList, SetSecretInput, SetSecretResult, ListSecretsQuery } from "../types/integrations";
2
+ export declare namespace secrets {
3
+ /** List secrets as refs + masked hints + metadata (never values). GET /secrets */
4
+ function list(collectionId: string, query?: ListSecretsQuery): Promise<SecretList>;
5
+ /** Create a secret. POST /secrets → { ref, hint }. Store the ref on a flow. */
6
+ function set(collectionId: string, input: SetSecretInput): Promise<SetSecretResult>;
7
+ /** Metadata for one secret (never the value). GET /secrets/:ref */
8
+ function get(collectionId: string, ref: string): Promise<SecretMeta>;
9
+ /** Rotate/update a secret's value (and optionally name/purpose). PUT /secrets/:ref → { ref, hint } */
10
+ function rotate(collectionId: string, ref: string, input: SetSecretInput): Promise<SetSecretResult>;
11
+ /** Soft-delete a secret. DELETE /secrets/:ref */
12
+ function remove(collectionId: string, ref: string): Promise<{
13
+ deleted: boolean;
14
+ }>;
15
+ }
@@ -0,0 +1,52 @@
1
+ // src/api/secrets.ts
2
+ //
3
+ // Sealed-secret store — the credentials that back integration flows and other
4
+ // server-side handlers. WRITE-ONLY from the client: you can set, rotate, list
5
+ // (refs + masked hints + metadata) and delete, but a value NEVER comes back over the
6
+ // API. It is sealed at rest and resolved server-side only, at execution time.
7
+ //
8
+ // Typical use: `set` a credential, take the returned `ref`, and put it on a flow's
9
+ // config.connection.auth.credentialRef.
10
+ //
11
+ // Endpoints: /admin/collection/:collectionId/secrets
12
+ import { request, post, put, del } from "../http";
13
+ function enc(v) { return encodeURIComponent(v); }
14
+ function encodeQuery(params = {}) {
15
+ const search = new URLSearchParams();
16
+ for (const [key, value] of Object.entries(params)) {
17
+ if (value === undefined || value === null || value === "")
18
+ continue;
19
+ search.set(key, String(value));
20
+ }
21
+ const qs = search.toString();
22
+ return qs ? `?${qs}` : "";
23
+ }
24
+ export var secrets;
25
+ (function (secrets) {
26
+ const base = (collectionId) => `/admin/collection/${enc(collectionId)}/secrets`;
27
+ /** List secrets as refs + masked hints + metadata (never values). GET /secrets */
28
+ async function list(collectionId, query = {}) {
29
+ return request(`${base(collectionId)}${encodeQuery(query)}`);
30
+ }
31
+ secrets.list = list;
32
+ /** Create a secret. POST /secrets → { ref, hint }. Store the ref on a flow. */
33
+ async function set(collectionId, input) {
34
+ return post(base(collectionId), input);
35
+ }
36
+ secrets.set = set;
37
+ /** Metadata for one secret (never the value). GET /secrets/:ref */
38
+ async function get(collectionId, ref) {
39
+ return request(`${base(collectionId)}/${enc(ref)}`);
40
+ }
41
+ secrets.get = get;
42
+ /** Rotate/update a secret's value (and optionally name/purpose). PUT /secrets/:ref → { ref, hint } */
43
+ async function rotate(collectionId, ref, input) {
44
+ return put(`${base(collectionId)}/${enc(ref)}`, input);
45
+ }
46
+ secrets.rotate = rotate;
47
+ /** Soft-delete a secret. DELETE /secrets/:ref */
48
+ async function remove(collectionId, ref) {
49
+ return del(`${base(collectionId)}/${enc(ref)}`);
50
+ }
51
+ secrets.remove = remove;
52
+ })(secrets || (secrets = {}));