@proveanything/smartlinks 1.8.0 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -35,6 +35,7 @@ export class IframeResponder {
35
35
  constructor(options) {
36
36
  this.iframe = null;
37
37
  this.uploads = new Map();
38
+ this.activeStreams = new Map();
38
39
  this.isInitialLoad = true;
39
40
  this.messageHandler = null;
40
41
  this.resizeHandler = null;
@@ -97,6 +98,8 @@ export class IframeResponder {
97
98
  this.resizeHandler = null;
98
99
  }
99
100
  this.uploads.clear();
101
+ this.activeStreams.forEach(controller => controller.abort());
102
+ this.activeStreams.clear();
100
103
  this.iframe = null;
101
104
  }
102
105
  // ===========================================================================
@@ -263,6 +266,16 @@ export class IframeResponder {
263
266
  await this.handleUpload(data, event);
264
267
  return;
265
268
  }
269
+ // Stream proxy aborts
270
+ if (data._smartlinksProxyStreamAbort) {
271
+ this.handleProxyStreamAbort(data);
272
+ return;
273
+ }
274
+ // Stream proxy requests
275
+ if (data._smartlinksProxyStreamRequest) {
276
+ await this.handleProxyStreamRequest(data, event);
277
+ return;
278
+ }
266
279
  // API proxy requests
267
280
  if (data._smartlinksProxyRequest || data._smartlinksCustomProxyRequest) {
268
281
  await this.handleProxyRequest(data, event);
@@ -426,6 +439,145 @@ export class IframeResponder {
426
439
  }
427
440
  this.sendResponse(event, response);
428
441
  }
442
+ handleProxyStreamAbort(data) {
443
+ const controller = this.activeStreams.get(data.id);
444
+ if (!controller) {
445
+ return;
446
+ }
447
+ controller.abort();
448
+ this.activeStreams.delete(data.id);
449
+ }
450
+ async handleProxyStreamRequest(data, event) {
451
+ var _a, _b, _c;
452
+ const controller = new AbortController();
453
+ this.activeStreams.set(data.id, controller);
454
+ try {
455
+ const path = data.path.startsWith('/') ? data.path.slice(1) : data.path;
456
+ const baseUrl = getBaseURL();
457
+ if (!baseUrl) {
458
+ throw new Error('SDK not initialized - call initializeApi() first');
459
+ }
460
+ const fetchOptions = {
461
+ method: data.method,
462
+ headers: data.headers,
463
+ signal: controller.signal,
464
+ };
465
+ if (data.body && data.method !== 'GET') {
466
+ fetchOptions.body = JSON.stringify(data.body);
467
+ fetchOptions.headers = Object.assign(Object.assign({}, fetchOptions.headers), { 'Content-Type': 'application/json' });
468
+ }
469
+ const response = await fetch(`${baseUrl}/${path}`, fetchOptions);
470
+ if (!response.ok) {
471
+ let responseBody = null;
472
+ try {
473
+ responseBody = await response.json();
474
+ }
475
+ catch (_d) {
476
+ responseBody = null;
477
+ }
478
+ const message = (responseBody === null || responseBody === void 0 ? void 0 : responseBody.message) || ((_a = responseBody === null || responseBody === void 0 ? void 0 : responseBody.error) === null || _a === void 0 ? void 0 : _a.message) || `Request failed with status ${response.status}`;
479
+ this.sendResponse(event, {
480
+ _smartlinksProxyStream: true,
481
+ id: data.id,
482
+ phase: 'error',
483
+ status: response.status,
484
+ error: message,
485
+ });
486
+ return;
487
+ }
488
+ if (!response.body) {
489
+ throw new Error('Streaming response body is unavailable in this environment');
490
+ }
491
+ this.sendResponse(event, {
492
+ _smartlinksProxyStream: true,
493
+ id: data.id,
494
+ phase: 'open',
495
+ });
496
+ await this.forwardProxyStream(data.id, response.body, event);
497
+ this.sendResponse(event, {
498
+ _smartlinksProxyStream: true,
499
+ id: data.id,
500
+ phase: 'end',
501
+ });
502
+ }
503
+ catch (err) {
504
+ if ((err === null || err === void 0 ? void 0 : err.name) === 'AbortError') {
505
+ return;
506
+ }
507
+ console.error('[IframeResponder] Proxy stream error:', err);
508
+ this.sendResponse(event, {
509
+ _smartlinksProxyStream: true,
510
+ id: data.id,
511
+ phase: 'error',
512
+ error: (err === null || err === void 0 ? void 0 : err.message) || 'Unknown streaming error',
513
+ });
514
+ (_c = (_b = this.options).onError) === null || _c === void 0 ? void 0 : _c.call(_b, err);
515
+ }
516
+ finally {
517
+ this.activeStreams.delete(data.id);
518
+ }
519
+ }
520
+ async forwardProxyStream(id, stream, event) {
521
+ const reader = stream.getReader();
522
+ const decoder = new TextDecoder();
523
+ let buffer = '';
524
+ let dataLines = [];
525
+ while (true) {
526
+ const { done, value } = await reader.read();
527
+ if (done) {
528
+ break;
529
+ }
530
+ buffer += decoder.decode(value, { stream: true });
531
+ const lines = buffer.split(/\r?\n/);
532
+ buffer = lines.pop() || '';
533
+ for (const rawLine of lines) {
534
+ const line = rawLine.trimEnd();
535
+ if (!line) {
536
+ if (!dataLines.length) {
537
+ continue;
538
+ }
539
+ const payload = dataLines.join('\n');
540
+ dataLines = [];
541
+ if (payload === '[DONE]') {
542
+ return;
543
+ }
544
+ try {
545
+ const parsed = JSON.parse(payload);
546
+ this.sendResponse(event, {
547
+ _smartlinksProxyStream: true,
548
+ id,
549
+ phase: 'event',
550
+ data: parsed,
551
+ });
552
+ }
553
+ catch (_a) {
554
+ // Ignore malformed event chunks
555
+ }
556
+ continue;
557
+ }
558
+ if (line.startsWith('data:')) {
559
+ dataLines.push(line.slice(5).trimStart());
560
+ }
561
+ }
562
+ }
563
+ if (dataLines.length) {
564
+ const payload = dataLines.join('\n');
565
+ if (payload !== '[DONE]') {
566
+ try {
567
+ const parsed = JSON.parse(payload);
568
+ this.sendResponse(event, {
569
+ _smartlinksProxyStream: true,
570
+ id,
571
+ phase: 'event',
572
+ data: parsed,
573
+ });
574
+ }
575
+ catch (_b) {
576
+ // Ignore malformed trailing event chunk
577
+ }
578
+ }
579
+ }
580
+ }
429
581
  getCachedResponse(path) {
430
582
  // App data endpoints should NOT be cached - they need fresh data from API
431
583
  // These are the new separated app config endpoints
package/dist/openapi.yaml CHANGED
@@ -10782,7 +10782,7 @@ components:
10782
10782
  type: number
10783
10783
  area:
10784
10784
  type: number
10785
- AnalyticsStandardMetadataFields:
10785
+ AnalyticsStandardEventFields:
10786
10786
  type: object
10787
10787
  properties:
10788
10788
  visitorId:
@@ -10834,7 +10834,7 @@ components:
10834
10834
  type: object
10835
10835
  properties:
10836
10836
  sessionId:
10837
- type: string
10837
+ $ref: "#/components/schemas/AnalyticsSessionId"
10838
10838
  eventType:
10839
10839
  $ref: "#/components/schemas/AnalyticsEventType"
10840
10840
  collectionId:
@@ -10873,7 +10873,7 @@ components:
10873
10873
  type: object
10874
10874
  properties:
10875
10875
  sessionId:
10876
- type: string
10876
+ $ref: "#/components/schemas/AnalyticsSessionId"
10877
10877
  eventType:
10878
10878
  $ref: "#/components/schemas/AnalyticsEventType"
10879
10879
  collectionId:
@@ -11048,11 +11048,11 @@ components:
11048
11048
  items:
11049
11049
  type: string
11050
11050
  sessionId:
11051
- type: string
11051
+ $ref: "#/components/schemas/AnalyticsSessionId"
11052
11052
  sessionIds:
11053
11053
  type: array
11054
11054
  items:
11055
- type: string
11055
+ $ref: "#/components/schemas/AnalyticsSessionId"
11056
11056
  country:
11057
11057
  type: string
11058
11058
  countries:
@@ -15478,6 +15478,69 @@ components:
15478
15478
  required:
15479
15479
  - _smartlinksProxyResponse
15480
15480
  - id
15481
+ ProxyStreamRequest:
15482
+ type: object
15483
+ properties:
15484
+ _smartlinksProxyStreamRequest:
15485
+ type: object
15486
+ additionalProperties: true
15487
+ id:
15488
+ type: string
15489
+ method:
15490
+ type: string
15491
+ enum:
15492
+ - GET
15493
+ - POST
15494
+ - PUT
15495
+ - PATCH
15496
+ - DELETE
15497
+ path:
15498
+ type: string
15499
+ body: {}
15500
+ headers:
15501
+ type: object
15502
+ additionalProperties:
15503
+ type: string
15504
+ required:
15505
+ - _smartlinksProxyStreamRequest
15506
+ - id
15507
+ - method
15508
+ - path
15509
+ ProxyStreamAbortMessage:
15510
+ type: object
15511
+ properties:
15512
+ _smartlinksProxyStreamAbort:
15513
+ type: object
15514
+ additionalProperties: true
15515
+ id:
15516
+ type: string
15517
+ required:
15518
+ - _smartlinksProxyStreamAbort
15519
+ - id
15520
+ ProxyStreamMessage:
15521
+ type: object
15522
+ properties:
15523
+ _smartlinksProxyStream:
15524
+ type: object
15525
+ additionalProperties: true
15526
+ id:
15527
+ type: string
15528
+ phase:
15529
+ type: string
15530
+ enum:
15531
+ - open
15532
+ - event
15533
+ - end
15534
+ - error
15535
+ data: {}
15536
+ error:
15537
+ type: string
15538
+ status:
15539
+ type: number
15540
+ required:
15541
+ - _smartlinksProxyStream
15542
+ - id
15543
+ - phase
15481
15544
  UploadStartMessage:
15482
15545
  type: object
15483
15546
  properties:
@@ -11,6 +11,7 @@ export type AnalyticsMetric = 'count' | 'uniqueSessions' | 'uniqueVisitors';
11
11
  export type AnalyticsSortOrder = 'asc' | 'desc';
12
12
  export type AnalyticsDeviceType = 'mobile' | 'tablet' | 'desktop' | 'unknown';
13
13
  export type AnalyticsStorageMode = 'local' | 'session' | false;
14
+ export type AnalyticsSessionId = number;
14
15
  export interface AnalyticsLocation {
15
16
  country?: string;
16
17
  latitude?: number;
@@ -18,7 +19,7 @@ export interface AnalyticsLocation {
18
19
  area?: number;
19
20
  [key: string]: any;
20
21
  }
21
- export interface AnalyticsStandardMetadataFields {
22
+ export interface AnalyticsStandardEventFields {
22
23
  visitorId?: string;
23
24
  referrer?: string;
24
25
  referrerHost?: string;
@@ -42,8 +43,8 @@ export interface AnalyticsStandardMetadataFields {
42
43
  qrCodeId?: string;
43
44
  scanMethod?: string;
44
45
  }
45
- export interface CollectionAnalyticsEvent extends AnalyticsStandardMetadataFields {
46
- sessionId?: string;
46
+ export interface CollectionAnalyticsEvent extends AnalyticsStandardEventFields {
47
+ sessionId?: AnalyticsSessionId;
47
48
  eventType: AnalyticsEventType;
48
49
  collectionId: string;
49
50
  productId?: string;
@@ -60,8 +61,8 @@ export interface CollectionAnalyticsEvent extends AnalyticsStandardMetadataField
60
61
  location?: AnalyticsLocation;
61
62
  metadata?: Record<string, any>;
62
63
  }
63
- export interface TagAnalyticsEvent extends AnalyticsStandardMetadataFields {
64
- sessionId?: string;
64
+ export interface TagAnalyticsEvent extends AnalyticsStandardEventFields {
65
+ sessionId?: AnalyticsSessionId;
65
66
  eventType: AnalyticsEventType;
66
67
  collectionId: string;
67
68
  productId?: string;
@@ -81,13 +82,13 @@ export interface AnalyticsTrackOptions {
81
82
  }
82
83
  export interface AnalyticsBrowserConfig {
83
84
  sessionStorageKey?: string;
84
- sessionIdFactory?: () => string;
85
+ sessionIdFactory?: () => AnalyticsSessionId;
85
86
  visitorId?: string;
86
87
  visitorStorage?: AnalyticsStorageMode;
87
88
  visitorStorageKey?: string;
88
89
  visitorIdFactory?: () => string;
89
90
  autoCaptureCampaignParams?: boolean;
90
- campaignParamMap?: Partial<Record<keyof AnalyticsStandardMetadataFields, string | string[]>>;
91
+ campaignParamMap?: Partial<Record<keyof AnalyticsStandardEventFields, string | string[]>>;
91
92
  defaultCollectionEvent?: Partial<CollectionAnalyticsEvent>;
92
93
  defaultTagEvent?: Partial<TagAnalyticsEvent>;
93
94
  getCollectionDefaults?: () => Partial<CollectionAnalyticsEvent> | undefined;
@@ -148,8 +149,8 @@ export interface AnalyticsFilterRequest {
148
149
  batchIds?: string[];
149
150
  variantId?: string;
150
151
  variantIds?: string[];
151
- sessionId?: string;
152
- sessionIds?: string[];
152
+ sessionId?: AnalyticsSessionId;
153
+ sessionIds?: AnalyticsSessionId[];
153
154
  country?: string;
154
155
  countries?: string[];
155
156
  metadata?: Record<string, any>;
@@ -68,6 +68,26 @@ export interface ProxyResponse {
68
68
  data?: any;
69
69
  error?: string;
70
70
  }
71
+ export interface ProxyStreamRequest {
72
+ _smartlinksProxyStreamRequest: true;
73
+ id: string;
74
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
75
+ path: string;
76
+ body?: any;
77
+ headers?: Record<string, string>;
78
+ }
79
+ export interface ProxyStreamAbortMessage {
80
+ _smartlinksProxyStreamAbort: true;
81
+ id: string;
82
+ }
83
+ export interface ProxyStreamMessage {
84
+ _smartlinksProxyStream: true;
85
+ id: string;
86
+ phase: 'open' | 'event' | 'end' | 'error';
87
+ data?: any;
88
+ error?: string;
89
+ status?: number;
90
+ }
71
91
  export interface UploadStartMessage {
72
92
  _smartlinksProxyUpload: true;
73
93
  phase: 'start';
@@ -1,6 +1,6 @@
1
1
  # Smartlinks API Summary
2
2
 
3
- Version: 1.8.0 | Generated: 2026-03-12T19:37:47.506Z
3
+ Version: 1.8.2 | Generated: 2026-03-14T10:21:47.666Z
4
4
 
5
5
  This is a concise summary of all available API functions and types.
6
6
 
@@ -166,6 +166,14 @@ Internal helper that performs a PATCH request to `${baseURL}${path}`, injecting
166
166
  options: RequestInit) → `Promise<T>`
167
167
  Internal helper that performs a request to `${baseURL}${path}` with custom options, injecting headers for apiKey or bearerToken if present. Returns the parsed JSON as T, or throws an Error.
168
168
 
169
+ **requestStream**(path: string,
170
+ options?: {
171
+ method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
172
+ body?: any
173
+ headers?: Record<string, string>
174
+ }) → `Promise<AsyncIterable<T>>`
175
+ Internal helper that performs a streaming request using the shared auth and proxy transport. The response is expected to be `text/event-stream` with JSON payloads in `data:` frames.
176
+
169
177
  **del**(path: string,
170
178
  extraHeaders?: Record<string, string>) → `Promise<T>`
171
179
  Internal helper that performs a DELETE request to `${baseURL}${path}`, injecting headers for apiKey or bearerToken if present. Returns the parsed JSON as T, or throws an Error.
@@ -938,9 +946,9 @@ interface AnalyticsLocation {
938
946
  }
939
947
  ```
940
948
 
941
- **AnalyticsStandardMetadataFields** (interface)
949
+ **AnalyticsStandardEventFields** (interface)
942
950
  ```typescript
943
- interface AnalyticsStandardMetadataFields {
951
+ interface AnalyticsStandardEventFields {
944
952
  visitorId?: string
945
953
  referrer?: string
946
954
  referrerHost?: string
@@ -977,13 +985,13 @@ interface AnalyticsTrackOptions {
977
985
  ```typescript
978
986
  interface AnalyticsBrowserConfig {
979
987
  sessionStorageKey?: string
980
- sessionIdFactory?: () => string
988
+ sessionIdFactory?: () => AnalyticsSessionId
981
989
  visitorId?: string
982
990
  visitorStorage?: AnalyticsStorageMode
983
991
  visitorStorageKey?: string
984
992
  visitorIdFactory?: () => string
985
993
  autoCaptureCampaignParams?: boolean
986
- campaignParamMap?: Partial<Record<keyof AnalyticsStandardMetadataFields, string | string[]>>
994
+ campaignParamMap?: Partial<Record<keyof AnalyticsStandardEventFields, string | string[]>>
987
995
  defaultCollectionEvent?: Partial<CollectionAnalyticsEvent>
988
996
  defaultTagEvent?: Partial<TagAnalyticsEvent>
989
997
  getCollectionDefaults?: () => Partial<CollectionAnalyticsEvent> | undefined
@@ -1048,8 +1056,8 @@ interface AnalyticsFilterRequest {
1048
1056
  batchIds?: string[]
1049
1057
  variantId?: string
1050
1058
  variantIds?: string[]
1051
- sessionId?: string
1052
- sessionIds?: string[]
1059
+ sessionId?: AnalyticsSessionId
1060
+ sessionIds?: AnalyticsSessionId[]
1053
1061
  country?: string
1054
1062
  countries?: string[]
1055
1063
  metadata?: Record<string, any>
@@ -1256,6 +1264,8 @@ interface AnalyticsTagsResponse {
1256
1264
 
1257
1265
  **AnalyticsStorageMode** = `'local' | 'session' | false`
1258
1266
 
1267
+ **AnalyticsSessionId** = `number`
1268
+
1259
1269
  **EventAnalyticsDimension** = ``
1260
1270
 
1261
1271
  **TagAnalyticsDimension** = ``
@@ -4203,6 +4213,38 @@ interface ProxyResponse {
4203
4213
  }
4204
4214
  ```
4205
4215
 
4216
+ **ProxyStreamRequest** (interface)
4217
+ ```typescript
4218
+ interface ProxyStreamRequest {
4219
+ _smartlinksProxyStreamRequest: true;
4220
+ id: string;
4221
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
4222
+ path: string;
4223
+ body?: any;
4224
+ headers?: Record<string, string>;
4225
+ }
4226
+ ```
4227
+
4228
+ **ProxyStreamAbortMessage** (interface)
4229
+ ```typescript
4230
+ interface ProxyStreamAbortMessage {
4231
+ _smartlinksProxyStreamAbort: true;
4232
+ id: string;
4233
+ }
4234
+ ```
4235
+
4236
+ **ProxyStreamMessage** (interface)
4237
+ ```typescript
4238
+ interface ProxyStreamMessage {
4239
+ _smartlinksProxyStream: true;
4240
+ id: string;
4241
+ phase: 'open' | 'event' | 'end' | 'error';
4242
+ data?: any;
4243
+ error?: string;
4244
+ status?: number;
4245
+ }
4246
+ ```
4247
+
4206
4248
  **UploadStartMessage** (interface)
4207
4249
  ```typescript
4208
4250
  interface UploadStartMessage {
@@ -5789,7 +5831,7 @@ Fire-and-forget tag analytics event. Uses `navigator.sendBeacon()` when availabl
5789
5831
  **configure**(config: AnalyticsBrowserConfig) → `void`
5790
5832
  Fire-and-forget tag analytics event. Uses `navigator.sendBeacon()` when available, falling back to `fetch(..., { keepalive: true })`.
5791
5833
 
5792
- **getSessionId**() → `string | undefined`
5834
+ **getSessionId**() → `AnalyticsSessionId | undefined`
5793
5835
  Fire-and-forget tag analytics event. Uses `navigator.sendBeacon()` when available, falling back to `fetch(..., { keepalive: true })`.
5794
5836
 
5795
5837
  **getVisitorId**() → `string | undefined`
@@ -1,26 +1,31 @@
1
1
  # Analytics Metadata Conventions
2
2
 
3
- Use these as the recommended standard keys for analytics event metadata.
3
+ Use these as the recommended standard analytics keys.
4
4
 
5
- You can put them in `metadata` directly today. The public analytics ingestion endpoints also accept these as top-level convenience fields and mirror them into `metadata` on the backend.
5
+ Some of these are now promoted top-level analytics fields. Others remain good metadata keys for custom dimensions.
6
6
 
7
7
  ---
8
8
 
9
9
  ## Recommended Keys
10
10
 
11
- ### Traffic and attribution
11
+ ### Promoted top-level fields
12
12
 
13
- - `referrer`
13
+ - `visitorId`
14
14
  - `referrerHost`
15
+ - `entryType`
16
+ - `pageId`
17
+ - `scanMethod`
18
+
19
+ These should be sent as top-level analytics fields, not inside `metadata`.
20
+
21
+ ### Metadata-friendly keys
22
+
23
+ - `referrer`
15
24
  - `utmSource`
16
25
  - `utmMedium`
17
26
  - `utmCampaign`
18
27
  - `utmContent`
19
28
  - `utmTerm`
20
- - `entryType` — for example `direct`, `qr`, `nfc`, `social`, `email`, `paid`, or `organic`
21
-
22
- ### Link-tree and page analytics
23
-
24
29
  - `group`
25
30
  - `tag`
26
31
  - `campaign`
@@ -31,13 +36,8 @@ You can put them in `metadata` directly today. The public analytics ingestion en
31
36
  - `linkTitle`
32
37
  - `destinationDomain`
33
38
  - `pagePath`
34
- - `pageId`
35
39
  - `qrCodeId`
36
40
 
37
- ### Physical scan analytics
38
-
39
- - `scanMethod` — for example `nfc` or `qr`
40
-
41
41
  ---
42
42
 
43
43
  ## Why These Matter
@@ -56,6 +56,7 @@ These keys give teams a shared vocabulary for:
56
56
 
57
57
  - Treat these as reserved standard keys.
58
58
  - Prefer these names before inventing custom alternatives.
59
+ - Send promoted fields at top level.
59
60
  - Keep values flat and scalar where possible so they are easier to filter and break down later.
60
61
  - Promote a field to a first-class backend column only when it becomes a hot platform-wide dimension.
61
62
 
@@ -65,14 +66,17 @@ These keys give teams a shared vocabulary for:
65
66
 
66
67
  ```typescript
67
68
  analytics.collection.track({
68
- sessionId: 'sess_123',
69
+ sessionId: 1234567890,
69
70
  eventType: 'click_link',
70
71
  collectionId: 'demo-collection',
72
+ visitorId: 'visitor_123',
71
73
  linkId: 'hero-cta',
72
74
  href: 'https://example.com/buy',
75
+ referrerHost: 'instagram.com',
73
76
  placement: 'hero',
74
77
  campaign: 'summer-launch',
75
78
  utmSource: 'email',
79
+ pageId: 'QR123',
76
80
  metadata: {
77
81
  pagePath: '/c/demo-collection',
78
82
  },