@bunizao/contracts 0.6.1 → 0.7.1
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 +105 -7
- package/dist/comments.d.ts +28 -1
- package/dist/comments.js +2 -1
- package/dist/index.js +2 -7
- package/package.json +1 -1
package/dist/admin.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ClientFingerprint, CommentStatus, Interaction } from './comments';
|
|
1
|
+
import type { ClientFingerprint, CommentAuthAtWrite, CommentClaimMethod, CommentStatus, Interaction } from './comments';
|
|
2
2
|
import type { DeliveryMode, NotifyAuditEventType, NotifyChannel, SubscriberRecord, SubscriberStatus } from './notify';
|
|
3
3
|
export interface SubscriberChannelCount {
|
|
4
4
|
total: number;
|
|
@@ -211,6 +211,10 @@ export interface AdminClusterCount {
|
|
|
211
211
|
and no body hash. */
|
|
212
212
|
export interface AdminCommentActor {
|
|
213
213
|
readerId: string | null;
|
|
214
|
+
/** Missing on older responses means unknown, not authenticated. */
|
|
215
|
+
authAtWrite?: CommentAuthAtWrite;
|
|
216
|
+
claimedAt?: string | null;
|
|
217
|
+
claimMethod?: CommentClaimMethod | null;
|
|
214
218
|
/** Plaintext while the row still holds it (verified: with the row;
|
|
215
219
|
unverified: seven days). */
|
|
216
220
|
email: string | null;
|
|
@@ -297,10 +301,60 @@ export interface AdminBanResult {
|
|
|
297
301
|
comments: number;
|
|
298
302
|
reactions: number;
|
|
299
303
|
};
|
|
304
|
+
operation?: AdminBanOperation | null;
|
|
305
|
+
}
|
|
306
|
+
export interface AdminBanPreview {
|
|
307
|
+
accounts: number;
|
|
308
|
+
sessions: number;
|
|
309
|
+
comments: Record<AdminCommentStatus | 'total', number>;
|
|
310
|
+
reactions: number;
|
|
311
|
+
purge: {
|
|
312
|
+
comments: number;
|
|
313
|
+
reactions: number;
|
|
314
|
+
};
|
|
315
|
+
windowDays: 90;
|
|
316
|
+
purgeLimit: number;
|
|
317
|
+
purgeAllowed: boolean;
|
|
318
|
+
}
|
|
319
|
+
export interface AdminBanOperation {
|
|
320
|
+
id: string;
|
|
321
|
+
createdAt: string;
|
|
322
|
+
source: AdminBanSource;
|
|
323
|
+
keys: AdminBanInput['keys'];
|
|
324
|
+
note: string | null;
|
|
325
|
+
purged: {
|
|
326
|
+
comments: number;
|
|
327
|
+
reactions: number;
|
|
328
|
+
};
|
|
329
|
+
restoredAt: string | null;
|
|
330
|
+
restorableUntil: string;
|
|
331
|
+
restored: {
|
|
332
|
+
comments: number;
|
|
333
|
+
reactions: number;
|
|
334
|
+
};
|
|
335
|
+
skipped: {
|
|
336
|
+
comments: number;
|
|
337
|
+
reactions: number;
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
export interface AdminBanOperationListResult {
|
|
341
|
+
operations: AdminBanOperation[];
|
|
342
|
+
}
|
|
343
|
+
export interface AdminBanRestoreResult {
|
|
344
|
+
operation: AdminBanOperation;
|
|
300
345
|
}
|
|
301
346
|
export interface AdminBanListResult {
|
|
302
347
|
bans: AdminBan[];
|
|
303
348
|
}
|
|
349
|
+
export interface AdminBannedReader {
|
|
350
|
+
readerId: string;
|
|
351
|
+
email: string;
|
|
352
|
+
displayName: string | null;
|
|
353
|
+
updatedAt: string;
|
|
354
|
+
}
|
|
355
|
+
export interface AdminBannedReaderListResult {
|
|
356
|
+
readers: AdminBannedReader[];
|
|
357
|
+
}
|
|
304
358
|
/** Everything about one key in one response --
|
|
305
359
|
`GET /admin/sources/:type/:value`. */
|
|
306
360
|
export interface AdminSourceProfile {
|
|
@@ -308,6 +362,13 @@ export interface AdminSourceProfile {
|
|
|
308
362
|
type: AdminSourceKeyType;
|
|
309
363
|
value: string;
|
|
310
364
|
};
|
|
365
|
+
identitySummary?: {
|
|
366
|
+
verifiedAccounts: number;
|
|
367
|
+
anonymousSessions: number;
|
|
368
|
+
claimedComments: number;
|
|
369
|
+
sharedStorage: boolean;
|
|
370
|
+
sharedFingerprint: boolean;
|
|
371
|
+
};
|
|
311
372
|
firstSeenAt: string | null;
|
|
312
373
|
lastSeenAt: string | null;
|
|
313
374
|
comments: {
|
|
@@ -327,9 +388,7 @@ export interface AdminSourceProfile {
|
|
|
327
388
|
/** Newest 50. */
|
|
328
389
|
rows: AdminReactionRecord[];
|
|
329
390
|
};
|
|
330
|
-
/** Distinct values
|
|
331
|
-
the churn: one fingerprint over 40 sessions and 12 IPs is a bot; one
|
|
332
|
-
session over 3 IPs is a phone that changed networks. */
|
|
391
|
+
/** Distinct observed values. Shared environments do not establish a person. */
|
|
333
392
|
spread: Record<'session' | 'ip' | 'ip24' | 'fp' | 'clientFp' | 'clientFpStable' | 'storageId' | 'email' | 'asn' | 'ua', number>;
|
|
334
393
|
/** Every bot and vpn hint this source has tripped, with how often. */
|
|
335
394
|
hints: Array<{
|
|
@@ -337,9 +396,8 @@ export interface AdminSourceProfile {
|
|
|
337
396
|
kind: 'bot' | 'vpn';
|
|
338
397
|
count: number;
|
|
339
398
|
}>;
|
|
340
|
-
/**
|
|
341
|
-
|
|
342
|
-
carried. Never `ip24`, `asn` or `ua` as a hop. */
|
|
399
|
+
/** Links describe verified-at-write account evidence or shared storage.
|
|
400
|
+
clientFpStable remains zero for compatibility; it never creates links. */
|
|
343
401
|
linked: {
|
|
344
402
|
sessions: number;
|
|
345
403
|
via: Record<'email' | 'clientFpStable' | 'storageId', number>;
|
|
@@ -374,12 +432,52 @@ export interface AdminInsightRow {
|
|
|
374
432
|
}
|
|
375
433
|
export type AdminCommentInsightsWindow = '7d' | '30d' | '90d';
|
|
376
434
|
export type AdminReactionInsightsWindow = '48h' | '7d';
|
|
435
|
+
export interface AdminCommentQuality {
|
|
436
|
+
since: string;
|
|
437
|
+
collectedSince: string | null;
|
|
438
|
+
available: {
|
|
439
|
+
moderation: boolean;
|
|
440
|
+
requests: boolean;
|
|
441
|
+
clientReports: boolean;
|
|
442
|
+
};
|
|
443
|
+
/** A cohort of automatically held comments and the first later owner decision. */
|
|
444
|
+
moderation: {
|
|
445
|
+
held: number;
|
|
446
|
+
reviewed: number;
|
|
447
|
+
released: number;
|
|
448
|
+
releasedShare: number | null;
|
|
449
|
+
};
|
|
450
|
+
/** Request outcomes include retries. Authenticated does not mean known benign. */
|
|
451
|
+
requests: {
|
|
452
|
+
attempts: number;
|
|
453
|
+
failures: number;
|
|
454
|
+
failureShare: number | null;
|
|
455
|
+
authenticatedAttempts: number;
|
|
456
|
+
authenticatedFailures: number;
|
|
457
|
+
authenticatedFailureShare: number | null;
|
|
458
|
+
outcomes: Array<{
|
|
459
|
+
kind: 'comment' | 'reaction';
|
|
460
|
+
outcome: string;
|
|
461
|
+
count: number;
|
|
462
|
+
}>;
|
|
463
|
+
};
|
|
464
|
+
/** Self-reported and incomplete when the browser cannot deliver telemetry. */
|
|
465
|
+
clientReports: {
|
|
466
|
+
reports: number;
|
|
467
|
+
failures: number;
|
|
468
|
+
networkFailures: number;
|
|
469
|
+
challengedAttempts: number;
|
|
470
|
+
repeatedChallenges: number;
|
|
471
|
+
untrusted: true;
|
|
472
|
+
};
|
|
473
|
+
}
|
|
377
474
|
/** `GET /admin/comments/insights?window=`. Every table carries a held rate,
|
|
378
475
|
because a source is only interesting relative to how the automatic pass
|
|
379
476
|
treats it. NULL groups show as their own row, never dropped. */
|
|
380
477
|
export interface AdminCommentInsights {
|
|
381
478
|
window: AdminCommentInsightsWindow;
|
|
382
479
|
since: string;
|
|
480
|
+
quality?: AdminCommentQuality;
|
|
383
481
|
networks: Array<AdminInsightRow & {
|
|
384
482
|
asn: number | null;
|
|
385
483
|
asOrg: string | null;
|
package/dist/comments.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export declare function commentAnchorToken(commentId: string): Promise<string>;
|
|
|
42
42
|
export declare const COMMENT_ANCHOR_PATTERN: RegExp;
|
|
43
43
|
export declare const MODERATION_ACTIONS: readonly ['publish', 'hold', 'reject', 'unsure'];
|
|
44
44
|
export type ModerationAction = (typeof MODERATION_ACTIONS)[number];
|
|
45
|
-
export declare const MODERATION_REASONS: readonly ['ok', 'spam', 'promotional', 'abuse', 'off_topic', 'personal_info'];
|
|
45
|
+
export declare const MODERATION_REASONS: readonly ['ok', 'spam', 'promotional', 'abuse', 'off_topic', 'personal_info', 'dwell_expired'];
|
|
46
46
|
export type ModerationReason = (typeof MODERATION_REASONS)[number];
|
|
47
47
|
/** Never sent to a reader. Rides along on the owner's Telegram notification. */
|
|
48
48
|
export interface ModerationVerdict {
|
|
@@ -491,3 +491,30 @@ export declare function commentPolicyFromTags(tags: readonly CommentPolicyTagLik
|
|
|
491
491
|
`readonly` and `off` answer no; the difference between them is drawn, not
|
|
492
492
|
enforced. */
|
|
493
493
|
export declare const acceptsComments: (policy: CommentPolicy) => boolean;
|
|
494
|
+
/** Evidence recorded when the comment was written, never upgraded by claiming. */
|
|
495
|
+
export type CommentAuthAtWrite = 'unknown' | 'anonymous' | 'verified';
|
|
496
|
+
export type CommentClaimMethod = 'session' | 'confirmed';
|
|
497
|
+
export interface ReaderClaimCandidate {
|
|
498
|
+
id: string;
|
|
499
|
+
surface: CommentSurface;
|
|
500
|
+
postId: string;
|
|
501
|
+
body: string;
|
|
502
|
+
createdAt: string;
|
|
503
|
+
authorName: string;
|
|
504
|
+
}
|
|
505
|
+
export interface ReaderClaimsResult {
|
|
506
|
+
comments: ReaderClaimCandidate[];
|
|
507
|
+
hasMore: boolean;
|
|
508
|
+
}
|
|
509
|
+
export interface ReaderClaimsInput {
|
|
510
|
+
commentIds: string[];
|
|
511
|
+
}
|
|
512
|
+
export interface ReaderClaimResult {
|
|
513
|
+
claimedIds: string[];
|
|
514
|
+
}
|
|
515
|
+
/** Optional client reports are operational observations, never identity evidence. */
|
|
516
|
+
export interface CommentTelemetryInput {
|
|
517
|
+
kind: 'comment' | 'reaction';
|
|
518
|
+
outcome: 'accepted' | 'http_error' | 'network_error' | 'challenge_failed';
|
|
519
|
+
challenges: number;
|
|
520
|
+
}
|
package/dist/comments.js
CHANGED
|
@@ -18,7 +18,8 @@ var MODERATION_REASONS = [
|
|
|
18
18
|
"promotional",
|
|
19
19
|
"abuse",
|
|
20
20
|
"off_topic",
|
|
21
|
-
"personal_info"
|
|
21
|
+
"personal_info",
|
|
22
|
+
"dwell_expired"
|
|
22
23
|
];
|
|
23
24
|
var READER_VERIFY_OUTCOMES = ["confirmed", "already_confirmed", "expired", "invalid"];
|
|
24
25
|
var READER_MUTE_OUTCOMES = ["muted", "unmuted", "invalid"];
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,6 @@ 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
|
-
|
|
14
13
|
// src/comments.ts
|
|
15
14
|
var READER_PROVIDERS = ["email", "github", "google"];
|
|
16
15
|
var COMMENT_LOCALES = ["zh", "en"];
|
|
@@ -31,7 +30,8 @@ var MODERATION_REASONS = [
|
|
|
31
30
|
"promotional",
|
|
32
31
|
"abuse",
|
|
33
32
|
"off_topic",
|
|
34
|
-
"personal_info"
|
|
33
|
+
"personal_info",
|
|
34
|
+
"dwell_expired"
|
|
35
35
|
];
|
|
36
36
|
var READER_VERIFY_OUTCOMES = ["confirmed", "already_confirmed", "expired", "invalid"];
|
|
37
37
|
var READER_MUTE_OUTCOMES = ["muted", "unmuted", "invalid"];
|
|
@@ -73,7 +73,6 @@ function commentPolicyFromTags(tags, base = DEFAULT_COMMENT_POLICY) {
|
|
|
73
73
|
return policy;
|
|
74
74
|
}
|
|
75
75
|
var acceptsComments = (policy) => policy.mode === "open";
|
|
76
|
-
|
|
77
76
|
// src/content.ts
|
|
78
77
|
var CONTENT_DOCUMENT_SOURCES = ["mood", "post"];
|
|
79
78
|
var POST_LOCALE_RE = /^[a-z]{2,8}(?:-[a-z0-9]{1,8})*$/;
|
|
@@ -99,15 +98,12 @@ var MESSAGE_MIN_BODY_LENGTH = 2;
|
|
|
99
98
|
var MESSAGE_MAX_BODY_LENGTH = 4000;
|
|
100
99
|
var MESSAGE_MAX_NAME_LENGTH = 32;
|
|
101
100
|
var MESSAGE_STATES = ["new", "read", "replied", "archived", "spam"];
|
|
102
|
-
|
|
103
101
|
// src/mood.ts
|
|
104
102
|
var MOOD_SENTIMENT_LABELS = ["joy", "calm", "melancholy", "anger", "anxiety", "neutral"];
|
|
105
103
|
var MOOD_AI_MODELS = ["gpt-5.5", "gpt-5", "claude-sonnet-4.6"];
|
|
106
104
|
var MOOD_COMMENT_ORIGINS = ["telegram", "web"];
|
|
107
|
-
|
|
108
105
|
// src/notify.ts
|
|
109
106
|
var NOTIFY_CHANNELS = ["mood", "blog", "privacy", "announcement"];
|
|
110
|
-
|
|
111
107
|
// src/routes.ts
|
|
112
108
|
var API_PREFIX = "/api";
|
|
113
109
|
var HEALTH_PATH = "/health";
|
|
@@ -147,7 +143,6 @@ var LEGACY_NOTIFY_BASE_PATH = "/v2/notify";
|
|
|
147
143
|
var LEGACY_GHOST_WEBHOOK_PATH = "/v2/ghost/webhook";
|
|
148
144
|
var LEGACY_MUSICKIT_TOKEN_PATH = "/v2/musickit/token";
|
|
149
145
|
var LEGACY_HEALTH_PATH = "/v2/health";
|
|
150
|
-
|
|
151
146
|
// src/telegram-ops.ts
|
|
152
147
|
var TELEGRAM_OPS_WEBHOOK_PATH = "/webhooks/telegram-ops";
|
|
153
148
|
var TELEGRAM_OPS_COMMENT_CALLBACK_PREFIXES = {
|