@proveanything/smartlinks 2.0.46 → 2.0.48
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/comms.d.ts +16 -1
- package/dist/api/comms.js +26 -4
- package/dist/api/functions.js +21 -3
- package/dist/context.d.ts +5 -0
- package/dist/context.js +1 -0
- package/dist/docs/API_SUMMARY.md +81 -34
- package/dist/docs/comms.md +22 -1
- package/dist/docs/deploying-apps.md +11 -0
- package/dist/docs/widgets.md +72 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/openapi.yaml +71 -33
- package/dist/types/appManifest.d.ts +20 -0
- package/dist/types/comms.d.ts +24 -7
- package/dist/widgetStyle.d.ts +37 -0
- package/dist/widgetStyle.js +64 -0
- package/docs/API_SUMMARY.md +81 -34
- package/docs/comms.md +22 -1
- package/docs/deploying-apps.md +11 -0
- package/docs/widgets.md +72 -0
- package/openapi.yaml +71 -33
- package/package.json +1 -1
package/dist/api/comms.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { CommunicationEvent, CommsQueryByUser, CommsRecipientIdsQuery, CommsRecipientsWithoutActionQuery, CommsRecipientsWithActionQuery, RecipientId, RecipientWithOutcome, LogCommunicationEventBody, LogBulkCommunicationEventsBody, AppendResult, AppendBulkResult, TransactionalSendRequest, TransactionalSendResult } from "../types/comms.js";
|
|
2
2
|
/**
|
|
3
|
-
* Communications namespace for sending notifications and managing user communications
|
|
3
|
+
* Communications namespace for sending notifications and managing user communications.
|
|
4
|
+
*
|
|
5
|
+
* The public preference / consent / subscription / method endpoints act only for the SIGNED-IN contact
|
|
6
|
+
* (their own contact in the collection), or for any contact when the caller is a collection admin —
|
|
7
|
+
* signed out they answer 401, someone else's contactId 403. `contactId` may be omitted to mean "me".
|
|
8
|
+
* `unsubscribe` also works signed out with a valid signed unsubscribe `token`. An email/phone
|
|
9
|
+
* registered here is only marked verified when it's the one the user signed in with.
|
|
4
10
|
*/
|
|
5
11
|
export declare namespace comms {
|
|
6
12
|
/**
|
|
@@ -37,6 +43,15 @@ export declare namespace comms {
|
|
|
37
43
|
* GET /public/collection/:collectionId/comm/unsubscribe
|
|
38
44
|
*/
|
|
39
45
|
function unsubscribe(collectionId: string, query: import("../types/comms.js").UnsubscribeQuery): Promise<import("../types/comms.js").UnsubscribeResponse>;
|
|
46
|
+
/**
|
|
47
|
+
* Public: sign an email address up for topics (a newsletter, updates) — works signed out.
|
|
48
|
+
* Double opt-in: nothing is consented until the address's owner clicks the confirmation link
|
|
49
|
+
* SmartLinks emails them. Always answers `{ ok: true, pending: true }` (it never reveals whether
|
|
50
|
+
* the address is already known). Topics must be ones configured in the collection's comm settings.
|
|
51
|
+
* Set comm settings `signup.confirmedUrl` to send people to your own page after confirming.
|
|
52
|
+
* POST /public/collection/:collectionId/comm/signup
|
|
53
|
+
*/
|
|
54
|
+
function signup(collectionId: string, body: import("../types/comms.js").CommsSignupRequest): Promise<import("../types/comms.js").CommsSignupResponse>;
|
|
40
55
|
/**
|
|
41
56
|
* Public: Upsert default consent for a contact.
|
|
42
57
|
* POST /public/collection/:collectionId/comm/consent
|
package/dist/api/comms.js
CHANGED
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
// Communications and notifications API for Smartlinks
|
|
3
3
|
import { post, request, patch } from "../http.js";
|
|
4
4
|
/**
|
|
5
|
-
* Communications namespace for sending notifications and managing user communications
|
|
5
|
+
* Communications namespace for sending notifications and managing user communications.
|
|
6
|
+
*
|
|
7
|
+
* The public preference / consent / subscription / method endpoints act only for the SIGNED-IN contact
|
|
8
|
+
* (their own contact in the collection), or for any contact when the caller is a collection admin —
|
|
9
|
+
* signed out they answer 401, someone else's contactId 403. `contactId` may be omitted to mean "me".
|
|
10
|
+
* `unsubscribe` also works signed out with a valid signed unsubscribe `token`. An email/phone
|
|
11
|
+
* registered here is only marked verified when it's the one the user signed in with.
|
|
6
12
|
*/
|
|
7
13
|
export var comms;
|
|
8
14
|
(function (comms) {
|
|
@@ -64,7 +70,8 @@ export var comms;
|
|
|
64
70
|
*/
|
|
65
71
|
async function unsubscribe(collectionId, query) {
|
|
66
72
|
const params = new URLSearchParams();
|
|
67
|
-
|
|
73
|
+
if (query.contactId)
|
|
74
|
+
params.set('contactId', query.contactId);
|
|
68
75
|
if (query.topic)
|
|
69
76
|
params.set('topic', query.topic);
|
|
70
77
|
if (query.channel)
|
|
@@ -76,6 +83,19 @@ export var comms;
|
|
|
76
83
|
return request(path);
|
|
77
84
|
}
|
|
78
85
|
comms.unsubscribe = unsubscribe;
|
|
86
|
+
/**
|
|
87
|
+
* Public: sign an email address up for topics (a newsletter, updates) — works signed out.
|
|
88
|
+
* Double opt-in: nothing is consented until the address's owner clicks the confirmation link
|
|
89
|
+
* SmartLinks emails them. Always answers `{ ok: true, pending: true }` (it never reveals whether
|
|
90
|
+
* the address is already known). Topics must be ones configured in the collection's comm settings.
|
|
91
|
+
* Set comm settings `signup.confirmedUrl` to send people to your own page after confirming.
|
|
92
|
+
* POST /public/collection/:collectionId/comm/signup
|
|
93
|
+
*/
|
|
94
|
+
async function signup(collectionId, body) {
|
|
95
|
+
const path = `/public/collection/${encodeURIComponent(collectionId)}/comm/signup`;
|
|
96
|
+
return post(path, body);
|
|
97
|
+
}
|
|
98
|
+
comms.signup = signup;
|
|
79
99
|
/**
|
|
80
100
|
* Public: Upsert default consent for a contact.
|
|
81
101
|
* POST /public/collection/:collectionId/comm/consent
|
|
@@ -109,7 +129,8 @@ export var comms;
|
|
|
109
129
|
*/
|
|
110
130
|
async function checkSubscription(collectionId, query) {
|
|
111
131
|
const params = new URLSearchParams();
|
|
112
|
-
|
|
132
|
+
if (query.contactId)
|
|
133
|
+
params.set('contactId', query.contactId);
|
|
113
134
|
params.set('subjectType', query.subjectType);
|
|
114
135
|
params.set('subjectId', query.subjectId);
|
|
115
136
|
if (query.productId)
|
|
@@ -125,7 +146,8 @@ export var comms;
|
|
|
125
146
|
*/
|
|
126
147
|
async function listMethods(collectionId, query) {
|
|
127
148
|
const params = new URLSearchParams();
|
|
128
|
-
|
|
149
|
+
if (query.contactId)
|
|
150
|
+
params.set('contactId', query.contactId);
|
|
129
151
|
if (query.type)
|
|
130
152
|
params.set('type', query.type);
|
|
131
153
|
const qs = `?${params.toString()}`;
|
package/dist/api/functions.js
CHANGED
|
@@ -27,7 +27,9 @@
|
|
|
27
27
|
// installed release regardless.
|
|
28
28
|
import { post, request, getAppContext, getAppChannel } from "../http.js";
|
|
29
29
|
import { readContext } from "../context.js";
|
|
30
|
-
|
|
30
|
+
// 'preview' = the app's in-progress server code inside Forge's preview (needs the preview pass Forge
|
|
31
|
+
// puts in the context as `slPreview`; SmartLinks refuses preview calls without it).
|
|
32
|
+
const CHANNELS = ['dev', 'alpha', 'beta', 'stable', 'preview'];
|
|
31
33
|
/**
|
|
32
34
|
* The release channel a call targets, or undefined for "the collection's installed release".
|
|
33
35
|
* A host context `appChannel` can be scoped to ONE app with `appChannelApp` — needed where several
|
|
@@ -52,6 +54,16 @@ export function resolveFunctionChannel(opts = {}, appId) {
|
|
|
52
54
|
throw new Error(`Unknown release channel "${raw}" (expected ${CHANNELS.join(' | ')})`);
|
|
53
55
|
return ch;
|
|
54
56
|
}
|
|
57
|
+
/** Add Forge's preview pass to a preview-channel URL (a query param, so host API proxies keep it). */
|
|
58
|
+
function withPreviewPass(url, ch, appId) {
|
|
59
|
+
if (ch !== 'preview')
|
|
60
|
+
return url;
|
|
61
|
+
const ctx = readContext();
|
|
62
|
+
if (ctx.appChannelApp && appId && ctx.appChannelApp !== appId)
|
|
63
|
+
return url;
|
|
64
|
+
const pass = ctx.slPreview;
|
|
65
|
+
return pass ? `${url}${url.includes('?') ? '&' : '?'}slPreview=${encodeURIComponent(pass)}` : url;
|
|
66
|
+
}
|
|
55
67
|
function appBase(surface, collectionId, opts) {
|
|
56
68
|
var _a;
|
|
57
69
|
const c = encodeURIComponent(collectionId);
|
|
@@ -63,7 +75,10 @@ function appBase(surface, collectionId, opts) {
|
|
|
63
75
|
}
|
|
64
76
|
/** The API path a function call goes to (exported for hosts/tests that need the exact URL). */
|
|
65
77
|
export function functionPath(surface, collectionId, name, opts = {}) {
|
|
66
|
-
|
|
78
|
+
var _a;
|
|
79
|
+
const app = (_a = opts.appId) !== null && _a !== void 0 ? _a : getAppContext();
|
|
80
|
+
const ch = app ? resolveFunctionChannel(opts, app) : undefined;
|
|
81
|
+
return withPreviewPass(`${appBase(surface, collectionId, opts)}/functions/${encodeURIComponent(name)}`, ch, app);
|
|
67
82
|
}
|
|
68
83
|
const fnPath = functionPath;
|
|
69
84
|
export var functions;
|
|
@@ -123,7 +138,10 @@ export var functions;
|
|
|
123
138
|
* appId is given (or set as the SDK app context): `GET /public/collection/:c[/app/:appId]/functions`.
|
|
124
139
|
*/
|
|
125
140
|
async function list(collectionId, opts = {}) {
|
|
126
|
-
|
|
141
|
+
var _a;
|
|
142
|
+
const app = (_a = opts.appId) !== null && _a !== void 0 ? _a : getAppContext();
|
|
143
|
+
const ch = app ? resolveFunctionChannel(opts, app) : undefined;
|
|
144
|
+
return request(withPreviewPass(`${appBase('public', collectionId, opts)}/functions`, ch, app));
|
|
127
145
|
}
|
|
128
146
|
functions.list = list;
|
|
129
147
|
})(functions || (functions = {}));
|
package/dist/context.d.ts
CHANGED
|
@@ -10,6 +10,11 @@ export interface SmartLinksContext {
|
|
|
10
10
|
appChannel?: string;
|
|
11
11
|
/** Limits `appChannel` to one app (where several apps share a page, e.g. a portal preview). */
|
|
12
12
|
appChannelApp?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Forge's preview pass: lets this preview run the app's in-progress server code
|
|
15
|
+
* (`appChannel: 'preview'`) on this collection. Set by Forge; never present elsewhere.
|
|
16
|
+
*/
|
|
17
|
+
slPreview?: string;
|
|
13
18
|
/** Any other declared view params (e.g. pageId, voteId, orientation, tvMode). */
|
|
14
19
|
[key: string]: string | undefined;
|
|
15
20
|
}
|
package/dist/context.js
CHANGED
package/dist/docs/API_SUMMARY.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Smartlinks API Summary
|
|
2
2
|
|
|
3
|
-
Version: 2.0.
|
|
3
|
+
Version: 2.0.48 | Generated: 2026-10-09T09:58:35.560Z
|
|
4
4
|
|
|
5
5
|
This is a concise summary of all available API functions and types.
|
|
6
6
|
|
|
@@ -2401,6 +2401,17 @@ interface AppWidgetComponent {
|
|
|
2401
2401
|
optional?: string[];
|
|
2402
2402
|
};
|
|
2403
2403
|
settings?: Record<string, any>;
|
|
2404
|
+
styleSupport?: WidgetStyleSupport;
|
|
2405
|
+
}
|
|
2406
|
+
```
|
|
2407
|
+
|
|
2408
|
+
**WidgetStyleSupport** (interface)
|
|
2409
|
+
```typescript
|
|
2410
|
+
interface WidgetStyleSupport {
|
|
2411
|
+
accent?: boolean;
|
|
2412
|
+
text?: boolean;
|
|
2413
|
+
textAlign?: boolean;
|
|
2414
|
+
background?: 'transparent' | 'own';
|
|
2404
2415
|
}
|
|
2405
2416
|
```
|
|
2406
2417
|
|
|
@@ -5140,7 +5151,7 @@ interface PushSubscribeResponse {
|
|
|
5140
5151
|
**RegisterPushMethodRequest** (interface)
|
|
5141
5152
|
```typescript
|
|
5142
5153
|
interface RegisterPushMethodRequest {
|
|
5143
|
-
contactId
|
|
5154
|
+
contactId?: string
|
|
5144
5155
|
endpoint: string
|
|
5145
5156
|
keys: { p256dh: string; auth: string }
|
|
5146
5157
|
meta?: Record<string, any>
|
|
@@ -5200,7 +5211,7 @@ interface CommsPublicTopicsResponse {
|
|
|
5200
5211
|
**UnsubscribeQuery** (interface)
|
|
5201
5212
|
```typescript
|
|
5202
5213
|
interface UnsubscribeQuery {
|
|
5203
|
-
contactId
|
|
5214
|
+
contactId?: string
|
|
5204
5215
|
topic?: string
|
|
5205
5216
|
channel?: BroadcastChannel
|
|
5206
5217
|
token?: string
|
|
@@ -5214,10 +5225,26 @@ interface UnsubscribeResponse {
|
|
|
5214
5225
|
}
|
|
5215
5226
|
```
|
|
5216
5227
|
|
|
5228
|
+
**CommsSignupRequest** (interface)
|
|
5229
|
+
```typescript
|
|
5230
|
+
interface CommsSignupRequest {
|
|
5231
|
+
email: string
|
|
5232
|
+
topics?: string[]
|
|
5233
|
+
firstName?: string
|
|
5234
|
+
}
|
|
5235
|
+
```
|
|
5236
|
+
|
|
5237
|
+
**CommsSignupResponse** (interface)
|
|
5238
|
+
```typescript
|
|
5239
|
+
interface CommsSignupResponse {
|
|
5240
|
+
ok: true; pending: true
|
|
5241
|
+
}
|
|
5242
|
+
```
|
|
5243
|
+
|
|
5217
5244
|
**CommsConsentUpsertRequest** (interface)
|
|
5218
5245
|
```typescript
|
|
5219
5246
|
interface CommsConsentUpsertRequest {
|
|
5220
|
-
contactId
|
|
5247
|
+
contactId?: string
|
|
5221
5248
|
channels?: ConsentChannels
|
|
5222
5249
|
topics?: Record<string, boolean>
|
|
5223
5250
|
topicsByChannel?: Partial<Record<BroadcastChannel, Record<string, boolean>>>
|
|
@@ -5227,7 +5254,7 @@ interface CommsConsentUpsertRequest {
|
|
|
5227
5254
|
**CommsPreferencesUpsertRequest** (interface)
|
|
5228
5255
|
```typescript
|
|
5229
5256
|
interface CommsPreferencesUpsertRequest {
|
|
5230
|
-
contactId
|
|
5257
|
+
contactId?: string
|
|
5231
5258
|
subject?: { type: SubjectType; id: string; productId?: string }
|
|
5232
5259
|
channels?: ConsentChannels
|
|
5233
5260
|
topics?: Record<string, boolean>
|
|
@@ -5238,7 +5265,7 @@ interface CommsPreferencesUpsertRequest {
|
|
|
5238
5265
|
**CommsSubscribeRequest** (interface)
|
|
5239
5266
|
```typescript
|
|
5240
5267
|
interface CommsSubscribeRequest {
|
|
5241
|
-
contactId
|
|
5268
|
+
contactId?: string
|
|
5242
5269
|
subject: { type: SubjectType; id: string; productId?: string }
|
|
5243
5270
|
subscribe: boolean
|
|
5244
5271
|
source?: string
|
|
@@ -5255,7 +5282,7 @@ interface CommsSubscribeResponse {
|
|
|
5255
5282
|
**CommsSubscriptionCheckQuery** (interface)
|
|
5256
5283
|
```typescript
|
|
5257
5284
|
interface CommsSubscriptionCheckQuery {
|
|
5258
|
-
contactId
|
|
5285
|
+
contactId?: string
|
|
5259
5286
|
subjectType: SubjectType
|
|
5260
5287
|
subjectId: string
|
|
5261
5288
|
productId?: string
|
|
@@ -5272,7 +5299,7 @@ interface CommsSubscriptionCheckResponse {
|
|
|
5272
5299
|
**CommsListMethodsQuery** (interface)
|
|
5273
5300
|
```typescript
|
|
5274
5301
|
interface CommsListMethodsQuery {
|
|
5275
|
-
contactId
|
|
5302
|
+
contactId?: string
|
|
5276
5303
|
type?: BroadcastChannel
|
|
5277
5304
|
}
|
|
5278
5305
|
```
|
|
@@ -5502,7 +5529,8 @@ export interface PushSubscribeResponse { ok: true; id: string }
|
|
|
5502
5529
|
|
|
5503
5530
|
// Public: register a push contact method
|
|
5504
5531
|
export interface RegisterPushMethodRequest {
|
|
5505
|
-
|
|
5532
|
+
/** Optional: defaults to the signed-in user's own contact. Only collection admins may name another contact. */
|
|
5533
|
+
contactId?: string
|
|
5506
5534
|
endpoint: string
|
|
5507
5535
|
keys: { p256dh: string; auth: string }
|
|
5508
5536
|
meta?: Record<string, any>
|
|
@@ -5557,7 +5585,8 @@ export type CommsSettingsPatchBody = Partial<CommsSettings>
|
|
|
5557
5585
|
export interface CommsPublicTopicsResponse { ok: true; topics: Record<string, TopicConfig> }
|
|
5558
5586
|
|
|
5559
5587
|
export interface UnsubscribeQuery {
|
|
5560
|
-
|
|
5588
|
+
/** Optional: defaults to the signed-in user's own contact. Only collection admins may name another contact. */
|
|
5589
|
+
contactId?: string
|
|
5561
5590
|
topic?: string
|
|
5562
5591
|
channel?: BroadcastChannel
|
|
5563
5592
|
token?: string
|
|
@@ -5565,19 +5594,30 @@ export interface UnsubscribeQuery {
|
|
|
5565
5594
|
|
|
5566
5595
|
export interface UnsubscribeResponse { ok: true; applied?: { channels?: Record<string, boolean>; topics?: Record<string, boolean> } }
|
|
5567
5596
|
|
|
5597
|
+
// Public sign-up (double opt-in)
|
|
5598
|
+
export interface CommsSignupRequest {
|
|
5599
|
+
email: string
|
|
5600
|
+
/** Topic ids from the collection's comm settings (e.g. ["newsletter"]). */
|
|
5601
|
+
topics?: string[]
|
|
5602
|
+
firstName?: string
|
|
5603
|
+
}
|
|
5604
|
+
export interface CommsSignupResponse { ok: true; pending: true }
|
|
5605
|
+
|
|
5568
5606
|
// Public consent/preferences/subscribe
|
|
5569
5607
|
export type ConsentChannels = Partial<Record<BroadcastChannel, boolean>>
|
|
5570
5608
|
type SubjectType = import('./contact').SubjectType
|
|
5571
5609
|
|
|
5572
5610
|
export interface CommsConsentUpsertRequest {
|
|
5573
|
-
|
|
5611
|
+
/** Optional: defaults to the signed-in user's own contact. Only collection admins may name another contact. */
|
|
5612
|
+
contactId?: string
|
|
5574
5613
|
channels?: ConsentChannels
|
|
5575
5614
|
topics?: Record<string, boolean>
|
|
5576
5615
|
topicsByChannel?: Partial<Record<BroadcastChannel, Record<string, boolean>>>
|
|
5577
5616
|
}
|
|
5578
5617
|
|
|
5579
5618
|
export interface CommsPreferencesUpsertRequest {
|
|
5580
|
-
|
|
5619
|
+
/** Optional: defaults to the signed-in user's own contact. Only collection admins may name another contact. */
|
|
5620
|
+
contactId?: string
|
|
5581
5621
|
subject?: { type: SubjectType; id: string; productId?: string }
|
|
5582
5622
|
channels?: ConsentChannels
|
|
5583
5623
|
topics?: Record<string, boolean>
|
|
@@ -5585,7 +5625,8 @@ export interface CommsPreferencesUpsertRequest {
|
|
|
5585
5625
|
}
|
|
5586
5626
|
|
|
5587
5627
|
export interface CommsSubscribeRequest {
|
|
5588
|
-
|
|
5628
|
+
/** Optional: defaults to the signed-in user's own contact. Only collection admins may name another contact. */
|
|
5629
|
+
contactId?: string
|
|
5589
5630
|
subject: { type: SubjectType; id: string; productId?: string }
|
|
5590
5631
|
subscribe: boolean
|
|
5591
5632
|
source?: string
|
|
@@ -5593,7 +5634,8 @@ export interface CommsSubscribeRequest {
|
|
|
5593
5634
|
export interface CommsSubscribeResponse { ok: true; subscriptionId: string }
|
|
5594
5635
|
|
|
5595
5636
|
export interface CommsSubscriptionCheckQuery {
|
|
5596
|
-
|
|
5637
|
+
/** Optional: defaults to the signed-in user's own contact. Only collection admins may name another contact. */
|
|
5638
|
+
contactId?: string
|
|
5597
5639
|
subjectType: SubjectType
|
|
5598
5640
|
subjectId: string
|
|
5599
5641
|
productId?: string
|
|
@@ -5601,7 +5643,8 @@ export interface CommsSubscriptionCheckQuery {
|
|
|
5601
5643
|
export interface CommsSubscriptionCheckResponse { ok: true; subscribed: boolean }
|
|
5602
5644
|
|
|
5603
5645
|
export interface CommsListMethodsQuery {
|
|
5604
|
-
|
|
5646
|
+
/** Optional: defaults to the signed-in user's own contact. Only collection admins may name another contact. */
|
|
5647
|
+
contactId?: string
|
|
5605
5648
|
type?: BroadcastChannel
|
|
5606
5649
|
}
|
|
5607
5650
|
export interface CommsListMethodsResponse { ok: true; methods: import('./contact').CommMethod[] }
|
|
@@ -10921,82 +10964,86 @@ Invite a user to a collection by email (admin only). Creates or links their acco
|
|
|
10921
10964
|
**getPushVapidPublicKey**(collectionId: string) → `Promise<import("../types/comms").PushVapidResponse>`
|
|
10922
10965
|
Public: Get VAPID public key used for Web Push subscriptions. GET /public/collection/:collectionId/comm/push/vapidPublicKey Note: Key may be global; path is collection-scoped for consistency.
|
|
10923
10966
|
|
|
10924
|
-
**registerPush**(collectionId: string,
|
|
10967
|
+
**registerPush**(collectionId: string,
|
|
10925
10968
|
body: import("../types/comms") → `void`
|
|
10926
10969
|
Public: Register a Web Push subscription under unified comms. POST /public/collection/:collectionId/comm/push/register
|
|
10927
10970
|
|
|
10928
|
-
**getSettings**(collectionId: string,
|
|
10971
|
+
**getSettings**(collectionId: string,
|
|
10929
10972
|
opts: { includeSecret?: boolean } = {}) → `Promise<import("../types/comms").CommsSettingsGetResponse>`
|
|
10930
10973
|
Admin: Get current comms settings for a collection. GET /admin/collection/:collectionId/comm.settings Optional query: includeSecret=true to include unsub.secret in response.
|
|
10931
10974
|
|
|
10932
|
-
**patchSettings**(collectionId: string,
|
|
10975
|
+
**patchSettings**(collectionId: string,
|
|
10933
10976
|
body: import("../types/comms") → `void`
|
|
10934
10977
|
Admin: Patch comms settings for a collection. PATCH /admin/collection/:collectionId/comm.settings
|
|
10935
10978
|
|
|
10936
10979
|
**getPublicTopics**(collectionId: string) → `Promise<import("../types/comms").CommsPublicTopicsResponse>`
|
|
10937
10980
|
Public: Fetch configured topics for a collection. GET /public/collection/:collectionId/comm/topics
|
|
10938
10981
|
|
|
10939
|
-
**unsubscribe**(collectionId: string,
|
|
10982
|
+
**unsubscribe**(collectionId: string,
|
|
10940
10983
|
query: import("../types/comms") → `void`
|
|
10941
10984
|
Public: Unsubscribe a contact from a category or channel. GET /public/collection/:collectionId/comm/unsubscribe
|
|
10942
10985
|
|
|
10943
|
-
**
|
|
10986
|
+
**signup**(collectionId: string,
|
|
10987
|
+
body: import("../types/comms") → `void`
|
|
10988
|
+
Public: sign an email address up for topics (a newsletter, updates) — works signed out. Double opt-in: nothing is consented until the address's owner clicks the confirmation link SmartLinks emails them. Always answers `{ ok: true, pending: true }` (it never reveals whether the address is already known). Topics must be ones configured in the collection's comm settings. Set comm settings `signup.confirmedUrl` to send people to your own page after confirming. POST /public/collection/:collectionId/comm/signup
|
|
10989
|
+
|
|
10990
|
+
**upsertConsent**(collectionId: string,
|
|
10944
10991
|
body: import("../types/comms") → `void`
|
|
10945
10992
|
Public: Upsert default consent for a contact. POST /public/collection/:collectionId/comm/consent
|
|
10946
10993
|
|
|
10947
|
-
**upsertPreferences**(collectionId: string,
|
|
10994
|
+
**upsertPreferences**(collectionId: string,
|
|
10948
10995
|
body: import("../types/comms") → `void`
|
|
10949
10996
|
Public: Upsert preferences for a specific subject (or default if subject omitted). POST /public/collection/:collectionId/comm/preferences
|
|
10950
10997
|
|
|
10951
|
-
**subscribe**(collectionId: string,
|
|
10998
|
+
**subscribe**(collectionId: string,
|
|
10952
10999
|
body: import("../types/comms") → `void`
|
|
10953
11000
|
Public: Subscribe/unsubscribe contact to a subject. POST /public/collection/:collectionId/comm/subscribe
|
|
10954
11001
|
|
|
10955
|
-
**checkSubscription**(collectionId: string,
|
|
11002
|
+
**checkSubscription**(collectionId: string,
|
|
10956
11003
|
query: import("../types/comms") → `void`
|
|
10957
11004
|
Public: Check subscription status for a subject. GET /public/collection/:collectionId/comm/subscription/check
|
|
10958
11005
|
|
|
10959
|
-
**listMethods**(collectionId: string,
|
|
11006
|
+
**listMethods**(collectionId: string,
|
|
10960
11007
|
query: import("../types/comms") → `void`
|
|
10961
11008
|
Public: List registered contact methods. GET /public/collection/:collectionId/comm/methods
|
|
10962
11009
|
|
|
10963
|
-
**registerEmail**(collectionId: string,
|
|
11010
|
+
**registerEmail**(collectionId: string,
|
|
10964
11011
|
body: import("../types/comms") → `void`
|
|
10965
11012
|
Public: Register email method for a contact. POST /public/collection/:collectionId/comm/email/register
|
|
10966
11013
|
|
|
10967
|
-
**registerSms**(collectionId: string,
|
|
11014
|
+
**registerSms**(collectionId: string,
|
|
10968
11015
|
body: import("../types/comms") → `void`
|
|
10969
11016
|
Public: Register SMS method for a contact. POST /public/collection/:collectionId/comm/sms/register
|
|
10970
11017
|
|
|
10971
|
-
**resolveSubscriptions**(collectionId: string,
|
|
11018
|
+
**resolveSubscriptions**(collectionId: string,
|
|
10972
11019
|
body: import("../types/comms") → `void`
|
|
10973
11020
|
Public: Resolve contacts for a subject with identity hints. POST /public/collection/:collectionId/comm/subscriptions/resolve
|
|
10974
11021
|
|
|
10975
|
-
**queryByUser**(collectionId: string,
|
|
11022
|
+
**queryByUser**(collectionId: string,
|
|
10976
11023
|
body: CommsQueryByUser = {}) → `Promise<CommunicationEvent[]>`
|
|
10977
11024
|
Analytics: Query communication events by user or contact. POST /admin/collection/:collectionId/comm/query/by-user
|
|
10978
11025
|
|
|
10979
|
-
**queryRecipientIds**(collectionId: string,
|
|
11026
|
+
**queryRecipientIds**(collectionId: string,
|
|
10980
11027
|
body: CommsRecipientIdsQuery) → `Promise<RecipientId[]>`
|
|
10981
11028
|
Analytics: Recipient IDs for a communication source. POST /admin/collection/:collectionId/comm/query/recipient-ids
|
|
10982
11029
|
|
|
10983
|
-
**queryRecipientsWithoutAction**(collectionId: string,
|
|
11030
|
+
**queryRecipientsWithoutAction**(collectionId: string,
|
|
10984
11031
|
body: CommsRecipientsWithoutActionQuery) → `Promise<RecipientId[]>`
|
|
10985
11032
|
Analytics: Recipients who did not perform an action. POST /admin/collection/:collectionId/comm/query/recipients/without-action
|
|
10986
11033
|
|
|
10987
|
-
**queryRecipientsWithAction**(collectionId: string,
|
|
11034
|
+
**queryRecipientsWithAction**(collectionId: string,
|
|
10988
11035
|
body: CommsRecipientsWithActionQuery) → `Promise<RecipientId[] | RecipientWithOutcome[]>`
|
|
10989
11036
|
Analytics: Recipients who performed an action, optionally with outcome. POST /admin/collection/:collectionId/comm/query/recipients/with-action
|
|
10990
11037
|
|
|
10991
|
-
**sendTransactional**(collectionId: string,
|
|
11038
|
+
**sendTransactional**(collectionId: string,
|
|
10992
11039
|
body: TransactionalSendRequest) → `Promise<TransactionalSendResult>`
|
|
10993
11040
|
Send a single transactional message to one contact using a template. No broadcast record is created. The send is logged to the contact's communication history with sourceType: 'transactional'. POST /admin/collection/:collectionId/comm/send ```typescript const result = await comms.sendTransactional(collectionId, { contactId: 'e4f2a1b0-...', templateId: 'warranty-update', channel: 'preferred', props: { claimRef: 'CLM-0042', decision: 'approved' }, include: { productId: 'prod-abc123', appCase: 'c9d1e2f3-...' }, ref: 'warranty-decision-notification', appId: 'warrantyApp', }) if (result.ok) { console.log(`Sent via ${result.channel}`, result.messageId) } else { console.error('Send failed:', result.error) } ```
|
|
10994
11041
|
|
|
10995
|
-
**logCommunicationEvent**(collectionId: string,
|
|
11042
|
+
**logCommunicationEvent**(collectionId: string,
|
|
10996
11043
|
body: LogCommunicationEventBody) → `Promise<AppendResult>`
|
|
10997
11044
|
Logging: Append a single communication event. POST /admin/collection/:collectionId/comm/log
|
|
10998
11045
|
|
|
10999
|
-
**logBulkCommunicationEvents**(collectionId: string,
|
|
11046
|
+
**logBulkCommunicationEvents**(collectionId: string,
|
|
11000
11047
|
body: LogBulkCommunicationEventsBody | ({ sourceId: string; ids: string[]; idField?: 'userId'|'contactId'; [k: string]: any }) → `void`
|
|
11001
11048
|
Logging: Append many communication events for a list of IDs. POST /admin/collection/:collectionId/comm/log/bulk
|
|
11002
11049
|
|
package/dist/docs/comms.md
CHANGED
|
@@ -313,9 +313,30 @@ function effectivePolicy(topic: any, channel: 'email' | 'sms' | 'push' | 'wallet
|
|
|
313
313
|
}
|
|
314
314
|
```
|
|
315
315
|
|
|
316
|
+
### Sign-up with double opt-in (newsletters)
|
|
317
|
+
|
|
318
|
+
For a sign-up form — works signed out. SmartLinks emails the address a "confirm your subscription"
|
|
319
|
+
link; nothing is consented until it's clicked. The answer is always the same, so the form can't be
|
|
320
|
+
used to find out who's registered.
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
await comms.signup('collectionId', { email: 'sam@example.com', topics: ['newsletter'] })
|
|
324
|
+
// → { ok: true, pending: true } — tell the visitor to check their inbox
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
- Topics must exist in the collection's comm settings. Give a newsletter topic
|
|
328
|
+
`defaults.policy: 'opt-in'`: broadcasts on an opt-in topic only reach people who said yes
|
|
329
|
+
(confirmed sign-ups), never the whole contact list.
|
|
330
|
+
- Confirming turns on the email channel and the topics, and marks the address verified.
|
|
331
|
+
- Set comm settings `signup.confirmedUrl` (https) to send people to your own "thanks" page after
|
|
332
|
+
they confirm (`?confirmed=1`, or `0` for an expired link); otherwise SmartLinks shows a short page.
|
|
333
|
+
|
|
316
334
|
### Consent (Default)
|
|
317
335
|
|
|
318
|
-
Record a contact's default channel and topic opt-ins.
|
|
336
|
+
Record a contact's default channel and topic opt-ins. These calls (consent, preferences, subscribe,
|
|
337
|
+
methods, push/email/SMS registration) act for the **signed-in** contact — `contactId` may be omitted
|
|
338
|
+
to mean "me"; only collection admins may name another contact. An email registered here is verified
|
|
339
|
+
only if it's the one the user signed in with; any other address is sent a confirmation link.
|
|
319
340
|
|
|
320
341
|
```typescript
|
|
321
342
|
await comms.upsertConsent('collectionId', {
|
|
@@ -165,6 +165,17 @@ The canonical channels are **`dev` · `alpha` · `beta` · `stable`**. Legacy `p
|
|
|
165
165
|
A collection chooses which channel it follows, so you can point a test collection at `dev`
|
|
166
166
|
and exercise a build before it reaches `prod`.
|
|
167
167
|
|
|
168
|
+
### `preview` — server code while you build (Forge)
|
|
169
|
+
|
|
170
|
+
Forge publishes an app's **server functions only** to a private `preview` channel whenever the
|
|
171
|
+
agent changes them, so the app's own preview can call the new code straight away. It is not a
|
|
172
|
+
channel a collection can follow, release to or promote from. A preview call runs only with the
|
|
173
|
+
**preview pass** Forge gives its preview — for one app on one collection, for a few hours — so
|
|
174
|
+
nobody else ever runs in-progress server code, while the builder can preview against any
|
|
175
|
+
collection they administer. The SDK sends the pass automatically (`SL.functions` with
|
|
176
|
+
`appChannel: 'preview'` and `slPreview` in the context); apps need no code for it. `dev` only
|
|
177
|
+
changes when you choose to release to it.
|
|
178
|
+
|
|
168
179
|
---
|
|
169
180
|
|
|
170
181
|
## Deploy keys
|
package/dist/docs/widgets.md
CHANGED
|
@@ -651,6 +651,78 @@ Widgets use semantic class names that reference these variables:
|
|
|
651
651
|
<Button className="bg-blue-500 text-white">
|
|
652
652
|
```
|
|
653
653
|
|
|
654
|
+
### Style support — how a widget takes styling from where it's placed
|
|
655
|
+
|
|
656
|
+
Hosts that place widgets (Link Page, Hub, …) offer per-widget styling: width, alignment, padding,
|
|
657
|
+
scale, corners, border, background, and colours. The host only ever styles the **box** around the
|
|
658
|
+
widget; it never reaches inside (no selector tricks, no `!important`). So a widget **declares** what
|
|
659
|
+
it honours, the host shows only those controls, and colours reach the widget through variables.
|
|
660
|
+
|
|
661
|
+
**Declare it** on the widget's entry in `app.manifest.json` (`widgets.components[]`):
|
|
662
|
+
|
|
663
|
+
```jsonc
|
|
664
|
+
{
|
|
665
|
+
"name": "TapToDonate",
|
|
666
|
+
"styleSupport": {
|
|
667
|
+
"accent": true, // reads --sl-widget-accent / --sl-widget-accent-contrast
|
|
668
|
+
"text": true, // reads --sl-widget-text
|
|
669
|
+
"textAlign": true, // titles/messages use text-align: inherit
|
|
670
|
+
"background": "transparent" // or "own" if it paints its own card
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
```
|
|
674
|
+
|
|
675
|
+
List only what you actually implemented. No `styleSupport` (older widgets) → the host offers layout
|
|
676
|
+
controls only and says the widget has no colour customisation; nothing breaks.
|
|
677
|
+
|
|
678
|
+
| Setting | Owner | Works when |
|
|
679
|
+
|---|---|---|
|
|
680
|
+
| width, alignment, padding, scale | Host | always (scale: keep 60–130%) |
|
|
681
|
+
| radius, border | Host | always — visible when the widget is transparent or fills its box |
|
|
682
|
+
| background | Host | the widget is transparent (hidden when `background: "own"`) |
|
|
683
|
+
| text | Widget | declared, and the widget reads `--sl-widget-text` |
|
|
684
|
+
| accent | Widget | declared, and the widget reads `--sl-widget-accent` + `--sl-widget-accent-contrast` |
|
|
685
|
+
| textAlign | Widget | declared, and the widget uses `text-align: inherit` |
|
|
686
|
+
| anything else (shapes, button style…) | Widget | the widget's own `settings` schema — not host styling |
|
|
687
|
+
|
|
688
|
+
**Variables the host sets** on the box (only the ones chosen):
|
|
689
|
+
|
|
690
|
+
```css
|
|
691
|
+
--sl-widget-accent /* hex */
|
|
692
|
+
--sl-widget-accent-contrast /* readable text on the accent: #111111 or #ffffff */
|
|
693
|
+
--sl-widget-text /* hex */
|
|
694
|
+
--sl-widget-radius /* e.g. 12px — use it on your own rounded elements */
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
**Widget rules**
|
|
698
|
+
|
|
699
|
+
1. **Transparent by default** — the root element has no background, border or padding of its own.
|
|
700
|
+
Need a card look? Declare `background: "own"` and make the card one of the widget's own settings.
|
|
701
|
+
2. **Read the variables with fallbacks** to the theme tokens, for every colour that should follow:
|
|
702
|
+
`background: var(--sl-widget-accent, var(--sl-color-accent));`
|
|
703
|
+
`color: var(--sl-widget-accent-contrast, var(--sl-color-on-accent));`
|
|
704
|
+
`color: var(--sl-widget-text, inherit);`
|
|
705
|
+
3. **Inherit text** — `color: inherit` / `text-align: inherit` on headings and messages if you declare
|
|
706
|
+
`text` / `textAlign`.
|
|
707
|
+
4. **No hardcoded colours** for buttons, links or focus rings — accent + accent-contrast.
|
|
708
|
+
5. **No global CSS** — ship scoped styles; nothing may affect elements outside the widget's box.
|
|
709
|
+
|
|
710
|
+
**Hosts** use the SDK helpers so every host behaves the same:
|
|
711
|
+
|
|
712
|
+
```ts
|
|
713
|
+
import { widgetStyleControls, widgetStyleVars } from '@proveanything/smartlinks'
|
|
714
|
+
|
|
715
|
+
const controls = widgetStyleControls(component.styleSupport) // which controls to show
|
|
716
|
+
const vars = widgetStyleVars({ accent, text, radius }, component.styleSupport) // CSS vars for the box
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
Conformance checklist (per widget, in the host's editor preview and live): unstyled looks as before
|
|
720
|
+
(light and dark); Background fills the whole box (unless `own`); Text recolours all text; Accent
|
|
721
|
+
recolours every button, link, focus ring and active state, and labels stay readable (try yellow and
|
|
722
|
+
near-black); undeclared controls are absent; padding 0 and 40 don't clip or double up; radius 0 and
|
|
723
|
+
32 with a border clip cleanly; width 50% at each alignment; scale 60% and 130% don't overlap
|
|
724
|
+
neighbours; dialogs opened from the widget are unaffected; nothing outside the box changes.
|
|
725
|
+
|
|
654
726
|
---
|
|
655
727
|
|
|
656
728
|
## Best Practices
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export * from "./api/index.js";
|
|
|
3
3
|
export * from "./types/index.js";
|
|
4
4
|
export { iframe } from "./iframe.js";
|
|
5
5
|
export { readContext } from "./context.js";
|
|
6
|
+
export { widgetStyleVars, widgetStyleControls, readableOn, WIDGET_STYLE_VARS } from "./widgetStyle.js";
|
|
7
|
+
export type { WidgetStyling } from "./widgetStyle.js";
|
|
6
8
|
export type { SmartLinksContext } from "./context.js";
|
|
7
9
|
export type { InvalidateCacheOptions } from "./http.js";
|
|
8
10
|
export * as cache from './cache.js';
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export * from "./types/index.js";
|
|
|
7
7
|
export { iframe } from "./iframe.js";
|
|
8
8
|
// App context reader (props → hash → search), for pages and containers alike
|
|
9
9
|
export { readContext } from "./context.js";
|
|
10
|
+
export { widgetStyleVars, widgetStyleControls, readableOn, WIDGET_STYLE_VARS } from "./widgetStyle.js";
|
|
10
11
|
import * as cache_1 from './cache.js';
|
|
11
12
|
export { cache_1 as cache };
|
|
12
13
|
// IframeResponder (also exported via iframe namespace)
|