@bunizao/contracts 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/admin.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { ClientFingerprint, CommentAuthAtWrite, CommentClaimMethod, CommentsMode, CommentStatus, CommentSurface, Interaction } from './comments';
2
2
  import type { MessageLocale, MessageState } from './messages';
3
- import type { DeliveryMode, NotifyAuditEventType, NotifyChannel, SubscriberRecord, SubscriberStatus } from './notify';
3
+ import type { DeliveryMode, NotifyAuditEventType, NotifyChannel, SubscriberRecord, SubscriberStatus, SubscriptionSource } from './notify';
4
4
  export interface SubscriberChannelCount {
5
5
  total: number;
6
6
  pendingCount: number;
@@ -24,6 +24,8 @@ export interface SubscriberListResult {
24
24
  activeCount: number;
25
25
  unsubscribedCount: number;
26
26
  channelCounts?: SubscriberChannelCounts;
27
+ /** Acquisition counts, kept separate from the subscribed channels. */
28
+ acquisitionCounts?: Record<SubscriptionSource | 'unknown', SubscriberChannelCount>;
27
29
  }
28
30
  export interface AuditEntry {
29
31
  id: number;
@@ -206,3 +206,156 @@ export interface BlogAnalyticsArticleDetailResult {
206
206
  countries: BlogAnalyticsBreakdown[];
207
207
  scrollBuckets: BlogAnalyticsScrollBucket[];
208
208
  }
209
+ export declare const SITE_ANALYTICS_COLLECT_ENDPOINT: '/api/analytics/collect';
210
+ export declare const SITE_ANALYTICS_REPORT_ENDPOINT: '/api/analytics/site';
211
+ export declare const SITE_ANALYTICS_ENGAGED_DWELL_MS = 10000;
212
+ export declare const SITE_ANALYTICS_SESSION_IDLE_MS: number;
213
+ export declare const SITE_ANALYTICS_SURFACES: readonly ['home', 'legacy', 'not_found', 'blog_index', 'blog_tags', 'blog_tag', 'blog_post', 'mood_feed', 'mood_post', 'mood_embed', 'projects', 'message', 'privacy', 'subscribe_manage', 'reader', 'docs', 'components', 'other'];
214
+ export type SiteAnalyticsSurface = typeof SITE_ANALYTICS_SURFACES[number];
215
+ export type SiteAnalyticsClickKind = 'link_internal' | 'link_outbound' | 'button' | 'panel';
216
+ export type SiteAnalyticsTrafficClass = 'human' | 'bot' | 'owner';
217
+ export interface SiteAnalyticsClick {
218
+ name: string;
219
+ kind: SiteAnalyticsClickKind;
220
+ href: string;
221
+ region: string;
222
+ label: string;
223
+ position: number;
224
+ tMs: number;
225
+ }
226
+ export interface SiteAnalyticsCollectInput {
227
+ v: 1;
228
+ viewId: string;
229
+ visitorId: string;
230
+ sessionId: string;
231
+ seq: number;
232
+ owner?: true;
233
+ page: {
234
+ surface: SiteAnalyticsSurface;
235
+ entity: string;
236
+ path: string;
237
+ locale: string;
238
+ referrer: string;
239
+ utm: [string, string, string];
240
+ };
241
+ client: {
242
+ tz: string;
243
+ langs: string[];
244
+ vw: number;
245
+ vh: number;
246
+ dpr: number;
247
+ navType: 0 | 1 | 2 | 3;
248
+ isEntry: boolean;
249
+ isNewVisitor: boolean;
250
+ webdriver: boolean;
251
+ };
252
+ progress: {
253
+ dwellMs: number;
254
+ scrollDepth: number;
255
+ interactions: number;
256
+ firstInputMs: number;
257
+ hiddenCount: number;
258
+ metric1: number;
259
+ metric2: number;
260
+ };
261
+ clicks: SiteAnalyticsClick[];
262
+ }
263
+ export declare const SITE_ANALYTICS_REPORTS: readonly ['overview', 'pages', 'clicks', 'sources', 'audience', 'quality', 'log', 'visitor'];
264
+ export type SiteAnalyticsReportName = typeof SITE_ANALYTICS_REPORTS[number];
265
+ export interface SiteAnalyticsMetrics {
266
+ views: number;
267
+ engaged: number;
268
+ visitors: number;
269
+ sessions: number;
270
+ bounces: number;
271
+ entries: number;
272
+ exits: number;
273
+ reads: number;
274
+ completions: number;
275
+ clicks: number;
276
+ medianDwellMs: number;
277
+ scrollDepth: number;
278
+ metric1: number;
279
+ metric2: number;
280
+ }
281
+ export interface SiteAnalyticsPageRow extends SiteAnalyticsMetrics {
282
+ day: string;
283
+ surface: string;
284
+ entity: string;
285
+ }
286
+ export interface SiteAnalyticsClickRow {
287
+ day: string;
288
+ surface: string;
289
+ entity: string;
290
+ name: string;
291
+ href: string;
292
+ clicks: number;
293
+ visitors: number;
294
+ medianTimeMs: number | null;
295
+ clickThrough: number;
296
+ }
297
+ export interface SiteAnalyticsDimensionRow {
298
+ day: string;
299
+ surface: string;
300
+ dim: string;
301
+ key: string;
302
+ views: number;
303
+ visitors: number;
304
+ engaged: number;
305
+ dwellMsSum: number;
306
+ }
307
+ export interface SiteAnalyticsTrafficRow {
308
+ day: string;
309
+ surface: string;
310
+ class: SiteAnalyticsTrafficClass;
311
+ reason: string;
312
+ views: number;
313
+ visitors: number;
314
+ }
315
+ export interface SiteAnalyticsViewRecord {
316
+ viewId: string;
317
+ visitorId: string;
318
+ sessionId: string;
319
+ startedAt: string;
320
+ updatedAt: string;
321
+ class: SiteAnalyticsTrafficClass;
322
+ reason: string;
323
+ weight: number;
324
+ page: SiteAnalyticsCollectInput['page'];
325
+ client: SiteAnalyticsCollectInput['client'];
326
+ progress: SiteAnalyticsCollectInput['progress'];
327
+ clicks: SiteAnalyticsClick[];
328
+ country: string;
329
+ region: string;
330
+ city: string;
331
+ ip: string;
332
+ ua: string;
333
+ browser: string;
334
+ os: string;
335
+ device: string;
336
+ platform: string;
337
+ network: string;
338
+ refSource: string;
339
+ refHost: string;
340
+ clickCount: number;
341
+ }
342
+ export interface SiteAnalyticsReportResult {
343
+ report: SiteAnalyticsReportName;
344
+ from: string;
345
+ to: string;
346
+ timezone: 'Australia/Melbourne';
347
+ sampled: boolean;
348
+ visitors: number | null;
349
+ visitorBasis: 'distinct' | 'sampled_distinct' | 'sum_monthly' | 'unavailable';
350
+ pages: SiteAnalyticsPageRow[];
351
+ clicks: SiteAnalyticsClickRow[];
352
+ dimensions: SiteAnalyticsDimensionRow[];
353
+ traffic: SiteAnalyticsTrafficRow[];
354
+ events: SiteAnalyticsViewRecord[];
355
+ nextCursor: string | null;
356
+ rum: Array<{
357
+ surface: string;
358
+ loads: number;
359
+ }> | null;
360
+ notices: string[];
361
+ }
package/dist/analytics.js CHANGED
@@ -10,6 +10,31 @@ var BLOG_ANALYTICS_EVENTS_DEFAULT_LIMIT = 50;
10
10
  var BLOG_ANALYTICS_READ_THRESHOLD_MS = 5000;
11
11
  var BLOG_ANALYTICS_COMPLETION_SCROLL_DEPTH = 0.9;
12
12
  var BLOG_ANALYTICS_RANGE_OPTIONS = [7, 30, 90];
13
+ var SITE_ANALYTICS_COLLECT_ENDPOINT = "/api/analytics/collect";
14
+ var SITE_ANALYTICS_REPORT_ENDPOINT = "/api/analytics/site";
15
+ var SITE_ANALYTICS_ENGAGED_DWELL_MS = 1e4;
16
+ var SITE_ANALYTICS_SESSION_IDLE_MS = 30 * 60000;
17
+ var SITE_ANALYTICS_SURFACES = [
18
+ "home",
19
+ "legacy",
20
+ "not_found",
21
+ "blog_index",
22
+ "blog_tags",
23
+ "blog_tag",
24
+ "blog_post",
25
+ "mood_feed",
26
+ "mood_post",
27
+ "mood_embed",
28
+ "projects",
29
+ "message",
30
+ "privacy",
31
+ "subscribe_manage",
32
+ "reader",
33
+ "docs",
34
+ "components",
35
+ "other"
36
+ ];
37
+ var SITE_ANALYTICS_REPORTS = ["overview", "pages", "clicks", "sources", "audience", "quality", "log", "visitor"];
13
38
  export {
14
39
  BLOG_ANALYTICS_ARTICLE_ENDPOINT,
15
40
  BLOG_ANALYTICS_COMPLETION_SCROLL_DEPTH,
@@ -21,5 +46,11 @@ export {
21
46
  BLOG_ANALYTICS_SUMMARY_ENDPOINT,
22
47
  LISTENING_ANALYTICS_EVENT_ENDPOINT,
23
48
  NEWSLETTER_ANALYTICS_CLICK_ENDPOINT,
24
- NEWSLETTER_ANALYTICS_OPEN_ENDPOINT
49
+ NEWSLETTER_ANALYTICS_OPEN_ENDPOINT,
50
+ SITE_ANALYTICS_COLLECT_ENDPOINT,
51
+ SITE_ANALYTICS_ENGAGED_DWELL_MS,
52
+ SITE_ANALYTICS_REPORTS,
53
+ SITE_ANALYTICS_REPORT_ENDPOINT,
54
+ SITE_ANALYTICS_SESSION_IDLE_MS,
55
+ SITE_ANALYTICS_SURFACES
25
56
  };
package/dist/index.js CHANGED
@@ -10,6 +10,31 @@ var BLOG_ANALYTICS_EVENTS_DEFAULT_LIMIT = 50;
10
10
  var BLOG_ANALYTICS_READ_THRESHOLD_MS = 5000;
11
11
  var BLOG_ANALYTICS_COMPLETION_SCROLL_DEPTH = 0.9;
12
12
  var BLOG_ANALYTICS_RANGE_OPTIONS = [7, 30, 90];
13
+ var SITE_ANALYTICS_COLLECT_ENDPOINT = "/api/analytics/collect";
14
+ var SITE_ANALYTICS_REPORT_ENDPOINT = "/api/analytics/site";
15
+ var SITE_ANALYTICS_ENGAGED_DWELL_MS = 1e4;
16
+ var SITE_ANALYTICS_SESSION_IDLE_MS = 30 * 60000;
17
+ var SITE_ANALYTICS_SURFACES = [
18
+ "home",
19
+ "legacy",
20
+ "not_found",
21
+ "blog_index",
22
+ "blog_tags",
23
+ "blog_tag",
24
+ "blog_post",
25
+ "mood_feed",
26
+ "mood_post",
27
+ "mood_embed",
28
+ "projects",
29
+ "message",
30
+ "privacy",
31
+ "subscribe_manage",
32
+ "reader",
33
+ "docs",
34
+ "components",
35
+ "other"
36
+ ];
37
+ var SITE_ANALYTICS_REPORTS = ["overview", "pages", "clicks", "sources", "audience", "quality", "log", "visitor"];
13
38
  // src/admin.ts
14
39
  var ADMIN_COMMENT_REJECT_REASONS = ["spam", "promotional", "abuse", "off_topic", "personal_info"];
15
40
  var ADMIN_ACTIVITY_EVENTS = [
@@ -131,6 +156,7 @@ var MOOD_AI_MODELS = ["gpt-5.5", "gpt-5", "claude-sonnet-4.6"];
131
156
  var MOOD_COMMENT_ORIGINS = ["telegram", "web"];
132
157
  // src/notify.ts
133
158
  var NOTIFY_CHANNELS = ["mood", "blog", "privacy", "announcement"];
159
+ var SUBSCRIPTION_SOURCES = ["desk", "blog", "mood"];
134
160
  // src/routes.ts
135
161
  var API_PREFIX = "/api";
136
162
  var HEALTH_PATH = "/health";
@@ -319,6 +345,13 @@ export {
319
345
  READER_RESEND_PATH,
320
346
  READER_VERIFY_OUTCOMES,
321
347
  READER_VERIFY_PATH,
348
+ SITE_ANALYTICS_COLLECT_ENDPOINT,
349
+ SITE_ANALYTICS_ENGAGED_DWELL_MS,
350
+ SITE_ANALYTICS_REPORTS,
351
+ SITE_ANALYTICS_REPORT_ENDPOINT,
352
+ SITE_ANALYTICS_SESSION_IDLE_MS,
353
+ SITE_ANALYTICS_SURFACES,
354
+ SUBSCRIPTION_SOURCES,
322
355
  TELEGRAM_OPS_BASE_PATH,
323
356
  TELEGRAM_OPS_BROADCASTS_PATH,
324
357
  TELEGRAM_OPS_BROADCAST_CHANNELS,
@@ -1,3 +1,18 @@
1
+ /** Site playback totals in a rolling seven-day window, not Apple Music history. */
2
+ export interface ListeningStats {
3
+ generatedAt: string;
4
+ window: {
5
+ from: string;
6
+ to: string;
7
+ };
8
+ totals: {
9
+ plays: number;
10
+ };
11
+ topArtist: {
12
+ name: string;
13
+ plays: number;
14
+ } | null;
15
+ }
1
16
  export interface ListeningAccent {
2
17
  hue: number;
3
18
  chromaLight: number;
package/dist/notify.d.ts CHANGED
@@ -4,6 +4,16 @@ export type DeliveryMode = 'immediate' | 'every_5h' | 'daily';
4
4
  export type NotifyChannel = 'mood' | 'blog' | 'privacy' | 'announcement';
5
5
  export type NotifyAuditEventType = 'subscribe_requested' | 'subscription_confirmed' | 'unsubscribed' | 'email_change_requested' | 'email_changed' | 'admin_create' | 'admin_update' | 'admin_delete' | 'admin_resend_confirm' | 'broadcast_sent';
6
6
  export declare const NOTIFY_CHANNELS: readonly NotifyChannel[];
7
+ export declare const SUBSCRIPTION_SOURCES: readonly ['desk', 'blog', 'mood'];
8
+ export type SubscriptionSource = (typeof SUBSCRIPTION_SOURCES)[number];
9
+ /** Public counts of active, confirmed, deliverable email subscriptions. */
10
+ export interface SubscriberCounts {
11
+ generatedAt: string;
12
+ channels: {
13
+ blog: number;
14
+ mood: number;
15
+ };
16
+ }
7
17
  /** Optional risk evidence preserves compatibility with existing clients. */
8
18
  export interface SubscribeRequest extends ClientEvidence {
9
19
  email: string;
@@ -14,6 +24,8 @@ export interface SubscribeRequest extends ClientEvidence {
14
24
  dailyHour?: number;
15
25
  website?: string;
16
26
  dwellToken?: string;
27
+ /** First subscription surface. Unknown values are ignored by the server. */
28
+ source?: SubscriptionSource;
17
29
  }
18
30
  export interface SubscriberRecord {
19
31
  email: string;
@@ -32,6 +44,8 @@ export interface SubscriberRecord {
32
44
  updatedAt: string;
33
45
  confirmedAt?: string;
34
46
  lastConfirmSentAt?: string;
47
+ /** First recorded subscription surface; absent for historical subscribers. */
48
+ source?: SubscriptionSource;
35
49
  }
36
50
  export interface RetryRecord {
37
51
  postId: string;
package/dist/notify.js CHANGED
@@ -1,5 +1,7 @@
1
1
  // src/notify.ts
2
2
  var NOTIFY_CHANNELS = ["mood", "blog", "privacy", "announcement"];
3
+ var SUBSCRIPTION_SOURCES = ["desk", "blog", "mood"];
3
4
  export {
4
- NOTIFY_CHANNELS
5
+ NOTIFY_CHANNELS,
6
+ SUBSCRIPTION_SOURCES
5
7
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bunizao/contracts",
3
- "version": "0.12.0",
3
+ "version": "0.14.0",
4
4
  "description": "Shared TypeScript contracts for buxx.me services and clients.",
5
5
  "type": "module",
6
6
  "sideEffects": false,