@sonordev/contracts 2.13.0 → 2.16.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.
@@ -0,0 +1,576 @@
1
+ import { type PublicationColors } from './publication-branding';
2
+ export { normalizeCampaignSlug } from './campaign-normalize';
3
+ export declare const FUNNEL_SCHEMA_VERSION: 1;
4
+ export declare const MANAGED_FUNNEL_PATH_PREFIX: '/f';
5
+ export declare const FUNNEL_TEMPLATES: readonly ['newsletter', 'requested_guide', 'inquiry'];
6
+ export type FunnelTemplate = typeof FUNNEL_TEMPLATES[number];
7
+ export type FunnelLifecycle = 'draft' | 'active' | 'paused';
8
+ export type FunnelHealth = 'healthy' | 'waiting' | 'needs_attention' | 'unknown';
9
+ export type FunnelBodyBlock = {
10
+ type: 'paragraph';
11
+ text: string;
12
+ } | {
13
+ type: 'bullets';
14
+ items: string[];
15
+ };
16
+ export interface FunnelBrandPresentation {
17
+ displayName: string;
18
+ tagline: string;
19
+ fullLogoUrl: string | null;
20
+ squareLogoUrl: string | null;
21
+ colors: Required<PublicationColors>;
22
+ typography: {
23
+ headingFamily: string;
24
+ bodyFamily: string;
25
+ };
26
+ scheme: 'light' | 'dark';
27
+ }
28
+ export interface FunnelBrandOverride {
29
+ displayName?: string | null;
30
+ tagline?: string | null;
31
+ fullLogoAssetId?: string | null;
32
+ squareLogoAssetId?: string | null;
33
+ colors?: {
34
+ [K in keyof PublicationColors]?: string | null;
35
+ };
36
+ typography?: {
37
+ headingFamily?: string | null;
38
+ bodyFamily?: string | null;
39
+ };
40
+ }
41
+ export type FunnelBrandSource = 'project' | 'brand_profile' | 'site_design' | 'publication_branding' | 'platforms.publication' | 'platforms.email' | 'funnel_override' | 'default';
42
+ export interface FunnelBrandSnapshot {
43
+ page: FunnelBrandPresentation;
44
+ email: FunnelBrandPresentation;
45
+ provenance: Record<string, FunnelBrandSource>;
46
+ assetRefs: {
47
+ id: string | null;
48
+ version: string | null;
49
+ }[];
50
+ capturedAt: string;
51
+ fingerprint: string;
52
+ }
53
+ export type FunnelStep = {
54
+ id: string;
55
+ type: 'wait';
56
+ durationSeconds: number;
57
+ } | {
58
+ id: string;
59
+ type: 'send_email';
60
+ templateId: string;
61
+ purpose: 'requested_fulfillment' | 'marketing';
62
+ guideAssetId?: string | null;
63
+ } | {
64
+ id: string;
65
+ type: 'add_tag';
66
+ target: 'subscriber';
67
+ tag: string;
68
+ } | {
69
+ id: string;
70
+ type: 'create_task';
71
+ ownerContactId: string;
72
+ title: string;
73
+ dueAfterSeconds: number;
74
+ };
75
+ export interface FunnelDefinition {
76
+ schemaVersion: 1;
77
+ name: string;
78
+ template: FunnelTemplate;
79
+ site: string;
80
+ entry: {
81
+ formId: string;
82
+ mode: 'future_accepted_form_only';
83
+ };
84
+ goal: {
85
+ type: 'newsletter_confirmed' | 'guide_requested' | 'inquiry_received' | 'booking_confirmed';
86
+ bookingTypeId?: string | null;
87
+ };
88
+ page: {
89
+ mode: 'existing' | 'managed';
90
+ path: string;
91
+ slug?: string | null;
92
+ headline: string;
93
+ body: FunnelBodyBlock[];
94
+ formId: string;
95
+ offerAssetId?: string | null;
96
+ };
97
+ permission: {
98
+ mode: 'requested_only' | 'marketing_opt_in';
99
+ optInFieldSlug?: string | null;
100
+ };
101
+ brandOverride?: FunnelBrandOverride;
102
+ steps: FunnelStep[];
103
+ exits: {
104
+ unsubscribe: true;
105
+ manualStop: true;
106
+ confirmedBooking?: {
107
+ enabled: boolean;
108
+ bookingTypeId: string | null;
109
+ };
110
+ };
111
+ }
112
+ export interface FunnelIssue {
113
+ code: string;
114
+ path: string;
115
+ message: string;
116
+ fixRef?: {
117
+ section: string;
118
+ field?: string;
119
+ resourceId?: string;
120
+ };
121
+ }
122
+ export interface FunnelReadiness {
123
+ definitionRevision: number;
124
+ brandFingerprint: string;
125
+ resourceFingerprint: string;
126
+ ready: boolean;
127
+ blockers: FunnelIssue[];
128
+ warnings: FunnelIssue[];
129
+ capabilities: {
130
+ managedPage: boolean;
131
+ };
132
+ }
133
+ export interface PublishedFunnelPage {
134
+ schemaVersion: 1;
135
+ id: string;
136
+ versionId: string;
137
+ revision: number;
138
+ site: string;
139
+ path: string;
140
+ content: {
141
+ headline: string;
142
+ body: FunnelBodyBlock[];
143
+ offer?: {
144
+ label: string;
145
+ href: string;
146
+ };
147
+ };
148
+ brand: FunnelBrandPresentation;
149
+ conversion: {
150
+ formSlug: string;
151
+ };
152
+ tracking: {
153
+ campaignSlug: string;
154
+ };
155
+ }
156
+ export interface FunnelOperationReceipt {
157
+ operationId: string;
158
+ projectId: string;
159
+ orgId: string;
160
+ funnelId: string;
161
+ operation: 'create' | 'update' | 'launch' | 'pause' | 'resume' | 'retry' | 'rollback';
162
+ idempotencyKey: string;
163
+ requestDigest: string;
164
+ versionId: string | null;
165
+ lifecycle: FunnelLifecycle;
166
+ replayed: boolean;
167
+ armedFutureEntrantsOnly: true;
168
+ enrolledExisting: 0;
169
+ messagesSent: 0;
170
+ pagePublication: 'not_requested' | 'published' | 'revalidation_pending';
171
+ health: FunnelHealth;
172
+ revision: number;
173
+ commandState: 'committed';
174
+ funnel: FunnelDetail | null;
175
+ actor: string;
176
+ armedAt: string | null;
177
+ effectScope: 'configuration_only' | 'safe_retry_armed';
178
+ target: FunnelCommandTarget;
179
+ ledgerProposalId: string;
180
+ }
181
+ export interface FunnelCommandTarget {
182
+ funnelId: string | null;
183
+ enrollmentId: string | null;
184
+ executionId: string | null;
185
+ targetVersionId: string | null;
186
+ }
187
+ export interface FunnelLaunchArgs {
188
+ expectedRevision: number;
189
+ previewId: string;
190
+ idempotencyKey: string;
191
+ confirm: true;
192
+ }
193
+ export interface FunnelLifecycleArgs {
194
+ expectedActiveVersionId: string;
195
+ idempotencyKey: string;
196
+ confirm: true;
197
+ targetVersionId?: string;
198
+ }
199
+ /** Small closed JSON-schema vocabulary shared by the exported schema and its runtime parser. */
200
+ type Shape = {
201
+ type?: 'object' | 'array' | 'string' | 'integer' | 'boolean' | 'null';
202
+ properties?: Record<string, Shape>;
203
+ required?: readonly string[];
204
+ additionalProperties?: false;
205
+ items?: Shape;
206
+ minItems?: number;
207
+ maxItems?: number;
208
+ minLength?: number;
209
+ maxLength?: number;
210
+ pattern?: string;
211
+ minimum?: number;
212
+ maximum?: number;
213
+ enum?: readonly unknown[];
214
+ const?: unknown;
215
+ oneOf?: readonly Shape[];
216
+ };
217
+ export type FunnelDraftStep = Extract<FunnelStep, {
218
+ type: 'wait' | 'add_tag';
219
+ }> | (Omit<Extract<FunnelStep, {
220
+ type: 'send_email';
221
+ }>, 'templateId' | 'guideAssetId'> & {
222
+ templateId: string | null;
223
+ guideAssetId?: string | null;
224
+ }) | (Omit<Extract<FunnelStep, {
225
+ type: 'create_task';
226
+ }>, 'ownerContactId'> & {
227
+ ownerContactId: string | null;
228
+ });
229
+ export interface FunnelDraftDefinition extends Omit<FunnelDefinition, 'site' | 'entry' | 'page' | 'steps'> {
230
+ site: string | null;
231
+ entry: {
232
+ formId: string | null;
233
+ mode: 'future_accepted_form_only';
234
+ };
235
+ page: Omit<FunnelDefinition['page'], 'path' | 'formId'> & {
236
+ path: string | null;
237
+ formId: string | null;
238
+ };
239
+ steps: FunnelDraftStep[];
240
+ }
241
+ export declare const FUNNEL_DRAFT_DEFINITION_JSON_SCHEMA: {
242
+ readonly type?: 'object' | 'array' | 'string' | 'integer' | 'boolean' | 'null';
243
+ readonly properties?: Record<string, Shape>;
244
+ readonly required?: readonly string[];
245
+ readonly additionalProperties?: false;
246
+ readonly items?: Shape;
247
+ readonly minItems?: number;
248
+ readonly maxItems?: number;
249
+ readonly minLength?: number;
250
+ readonly maxLength?: number;
251
+ readonly pattern?: string;
252
+ readonly minimum?: number;
253
+ readonly maximum?: number;
254
+ readonly enum?: readonly unknown[];
255
+ readonly const?: unknown;
256
+ readonly oneOf?: readonly Shape[];
257
+ readonly $schema: 'http://json-schema.org/draft-07/schema#';
258
+ };
259
+ export declare function parseFunnelDraftDefinition(value: unknown): FunnelDraftDefinition;
260
+ export declare function createFunnelDraft(template: FunnelTemplate): FunnelDraftDefinition;
261
+ export declare const FUNNEL_DEFINITION_JSON_SCHEMA: {
262
+ readonly type?: 'object' | 'array' | 'string' | 'integer' | 'boolean' | 'null';
263
+ readonly properties?: Record<string, Shape>;
264
+ readonly required?: readonly string[];
265
+ readonly additionalProperties?: false;
266
+ readonly items?: Shape;
267
+ readonly minItems?: number;
268
+ readonly maxItems?: number;
269
+ readonly minLength?: number;
270
+ readonly maxLength?: number;
271
+ readonly pattern?: string;
272
+ readonly minimum?: number;
273
+ readonly maximum?: number;
274
+ readonly enum?: readonly unknown[];
275
+ readonly const?: unknown;
276
+ readonly oneOf?: readonly Shape[];
277
+ readonly $schema: 'http://json-schema.org/draft-07/schema#';
278
+ };
279
+ export declare const PUBLISHED_FUNNEL_PAGE_JSON_SCHEMA: {
280
+ readonly type?: 'object' | 'array' | 'string' | 'integer' | 'boolean' | 'null';
281
+ readonly properties?: Record<string, Shape>;
282
+ readonly required?: readonly string[];
283
+ readonly additionalProperties?: false;
284
+ readonly items?: Shape;
285
+ readonly minItems?: number;
286
+ readonly maxItems?: number;
287
+ readonly minLength?: number;
288
+ readonly maxLength?: number;
289
+ readonly pattern?: string;
290
+ readonly minimum?: number;
291
+ readonly maximum?: number;
292
+ readonly enum?: readonly unknown[];
293
+ readonly const?: unknown;
294
+ readonly oneOf?: readonly Shape[];
295
+ readonly $schema: 'http://json-schema.org/draft-07/schema#';
296
+ };
297
+ export declare const FUNNEL_LAUNCH_JSON_SCHEMA: Shape;
298
+ export declare const FUNNEL_LIFECYCLE_JSON_SCHEMA: Shape;
299
+ export declare const FUNNEL_ROLLBACK_JSON_SCHEMA: Shape;
300
+ export declare class FunnelValidationError extends Error {
301
+ readonly path: string;
302
+ constructor(path: string, reason: string);
303
+ }
304
+ /** No DNS/network access. Consumers fetching URLs still enforce their connection policy. */
305
+ export declare function isSafeFunnelPublicUrl(value: string): boolean;
306
+ export declare function managedFunnelPath(value: string): string;
307
+ export declare function parseFunnelDefinition(value: unknown): FunnelDefinition;
308
+ export declare function parsePublishedFunnelPage(value: unknown): PublishedFunnelPage;
309
+ export declare function parseFunnelLaunchArgs(value: unknown): FunnelLaunchArgs;
310
+ export declare function parseFunnelLifecycleArgs(value: unknown, operation?: 'pause' | 'resume' | 'rollback'): FunnelLifecycleArgs;
311
+ /** Apply presentation-only overrides to an already resolved canonical brand. Null means inherit. */
312
+ export declare function applyFunnelBrandOverride(inherited: FunnelBrandPresentation, patch?: FunnelBrandOverride, resolvedAssets?: Record<string, {
313
+ url: string;
314
+ version?: string;
315
+ }>): {
316
+ brand: FunnelBrandPresentation;
317
+ overridePaths: string[];
318
+ assetRefs: {
319
+ id: string;
320
+ version: string | null;
321
+ }[];
322
+ };
323
+ export interface FunnelCreateArgs {
324
+ idempotencyKey: string;
325
+ definition: FunnelDraftDefinition;
326
+ }
327
+ export interface FunnelUpdateArgs extends FunnelCreateArgs {
328
+ expectedRevision: number;
329
+ }
330
+ export declare const FUNNEL_CREATE_JSON_SCHEMA: Shape;
331
+ export declare const FUNNEL_UPDATE_JSON_SCHEMA: Shape;
332
+ export declare function parseFunnelCreateArgs(value: unknown): FunnelCreateArgs;
333
+ export declare function parseFunnelUpdateArgs(value: unknown): FunnelUpdateArgs;
334
+ export interface FunnelPreview {
335
+ previewId: string;
336
+ versionId: string;
337
+ expiresAt: string;
338
+ definitionRevision: number;
339
+ brand: FunnelBrandSnapshot;
340
+ readiness: FunnelReadiness;
341
+ page: PublishedFunnelPage | null;
342
+ launchEvidence: FunnelLaunchEvidence;
343
+ emailPreviews: {
344
+ stepId: string;
345
+ ready: boolean;
346
+ subject: string | null;
347
+ contentHtml: string | null;
348
+ issues: FunnelIssue[];
349
+ }[];
350
+ }
351
+ export interface FunnelSummary {
352
+ id: string;
353
+ name: string;
354
+ template: FunnelTemplate;
355
+ site: string | null;
356
+ revision: number;
357
+ activeVersionId: string | null;
358
+ lifecycle: FunnelLifecycle;
359
+ health: FunnelHealth;
360
+ hasDraftChanges: boolean;
361
+ counts: {
362
+ entered: number;
363
+ waiting: number;
364
+ running: number;
365
+ completed: number;
366
+ exited: number;
367
+ needsAttention: number;
368
+ };
369
+ countsBasis: 'all_versions_all_time';
370
+ lastEventAt: string | null;
371
+ }
372
+ export type FunnelDraftArgs = FunnelCreateArgs | FunnelUpdateArgs;
373
+ /** Select create/update explicitly at the HTTP boundary; a missing revision never downgrades an update. */
374
+ export declare function parseFunnelDraftArgs(value: unknown, mode: 'create' | 'update'): FunnelDraftArgs;
375
+ export interface FunnelRevisionArgs {
376
+ expectedRevision: number;
377
+ }
378
+ export interface FunnelRetryArgs {
379
+ expectedAttempt: number;
380
+ idempotencyKey: string;
381
+ confirm: true;
382
+ }
383
+ export declare const FUNNEL_REVISION_JSON_SCHEMA: Shape;
384
+ export declare const FUNNEL_RETRY_JSON_SCHEMA: Shape;
385
+ export declare const FUNNEL_DRAFT_JSON_SCHEMAS: {
386
+ readonly create: Shape;
387
+ readonly update: Shape;
388
+ };
389
+ export declare function parseFunnelRevisionArgs(value: unknown): FunnelRevisionArgs;
390
+ export declare function parseFunnelRetryArgs(value: unknown): FunnelRetryArgs;
391
+ export declare const FUNNEL_READINESS_JSON_SCHEMA: Shape;
392
+ export declare function parseFunnelReadiness(value: unknown): FunnelReadiness;
393
+ export interface FunnelDetail {
394
+ id: string;
395
+ revision: number;
396
+ definition: FunnelDraftDefinition;
397
+ activeVersionId: string | null;
398
+ lifecycle: FunnelLifecycle;
399
+ health: FunnelHealth;
400
+ hasDraftChanges: boolean;
401
+ readiness: FunnelReadiness;
402
+ }
403
+ export declare const FUNNEL_DETAIL_JSON_SCHEMA: Shape;
404
+ export declare function parseFunnelDetail(value: unknown): FunnelDetail;
405
+ export declare const FUNNEL_OPERATION_RECEIPT_JSON_SCHEMA: Shape;
406
+ export declare function parseFunnelOperationReceipt(value: unknown): FunnelOperationReceipt;
407
+ export declare const FUNNEL_BRAND_SNAPSHOT_JSON_SCHEMA: Shape;
408
+ export declare function parseFunnelBrandSnapshot(value: unknown): FunnelBrandSnapshot;
409
+ export interface FunnelCatalogItem {
410
+ id: string;
411
+ label: string;
412
+ }
413
+ export interface FunnelFormChoice extends FunnelCatalogItem {
414
+ slug: string;
415
+ site: string | null;
416
+ formType: string;
417
+ active: boolean;
418
+ newsletterListIds: string[];
419
+ fields: {
420
+ slug: string;
421
+ type: string;
422
+ label: string;
423
+ required: boolean;
424
+ }[];
425
+ }
426
+ export interface FunnelListChoice extends FunnelCatalogItem {
427
+ active: boolean;
428
+ doubleOptIn: boolean;
429
+ }
430
+ export interface FunnelEmailTemplateChoice extends FunnelCatalogItem {
431
+ subject: string;
432
+ updatedAt: string | null;
433
+ }
434
+ export interface FunnelAssetChoice extends FunnelCatalogItem {
435
+ mimeType: string;
436
+ sizeBytes: number;
437
+ visibility: 'public';
438
+ publicUrl: string;
439
+ version: string | null;
440
+ }
441
+ export interface FunnelOwnerChoice extends FunnelCatalogItem {
442
+ active: true;
443
+ }
444
+ export interface FunnelBookingChoice extends FunnelCatalogItem {
445
+ site: string | null;
446
+ active: true;
447
+ requiresConfirmation: boolean;
448
+ }
449
+ export interface FunnelChoicePage<T> {
450
+ items: T[];
451
+ nextCursor: string | null;
452
+ }
453
+ export interface FunnelCatalog {
454
+ templates: {
455
+ id: FunnelTemplate;
456
+ label: string;
457
+ defaults: FunnelDraftDefinition;
458
+ }[];
459
+ sites: {
460
+ host: string;
461
+ label: string;
462
+ }[];
463
+ brand: FunnelBrandSnapshot | null;
464
+ choices: {
465
+ forms?: FunnelChoicePage<FunnelFormChoice>;
466
+ lists?: FunnelChoicePage<FunnelListChoice>;
467
+ emailTemplates?: FunnelChoicePage<FunnelEmailTemplateChoice>;
468
+ assets?: FunnelChoicePage<FunnelAssetChoice>;
469
+ owners?: FunnelChoicePage<FunnelOwnerChoice>;
470
+ bookingTypes?: FunnelChoicePage<FunnelBookingChoice>;
471
+ };
472
+ capabilities: {
473
+ managedPage: boolean;
474
+ };
475
+ }
476
+ export declare const FUNNEL_CATALOG_JSON_SCHEMA: Shape;
477
+ export declare function parseFunnelCatalog(value: unknown): FunnelCatalog;
478
+ export interface FunnelLaunchEvidence {
479
+ projectId: string;
480
+ orgId: string;
481
+ funnelId: string;
482
+ previewId: string;
483
+ versionId: string;
484
+ definitionRevision: number;
485
+ definitionDigest: string;
486
+ brandFingerprint: string;
487
+ resourceFingerprint: string;
488
+ entryDigest: string;
489
+ permissionDigest: string;
490
+ expiresAt: string;
491
+ }
492
+ export interface FunnelRollbackEvidence {
493
+ orgId: string;
494
+ projectId: string;
495
+ funnelId: string;
496
+ targetVersionId: string;
497
+ definitionRevision: number;
498
+ definitionDigest: string;
499
+ brandFingerprint: string;
500
+ resourceFingerprint: string;
501
+ }
502
+ export declare const FUNNEL_LAUNCH_EVIDENCE_JSON_SCHEMA: Shape;
503
+ export declare function parseFunnelLaunchEvidence(value: unknown): FunnelLaunchEvidence;
504
+ export declare const FUNNEL_ROLLBACK_EVIDENCE_JSON_SCHEMA: Shape;
505
+ export declare function parseFunnelRollbackEvidence(value: unknown): FunnelRollbackEvidence;
506
+ export declare const FUNNEL_PREVIEW_JSON_SCHEMA: Shape;
507
+ export declare function parseFunnelPreview(value: unknown): FunnelPreview;
508
+ export type FunnelEnrollmentState = 'waiting' | 'running' | 'completed' | 'exited' | 'needs_attention';
509
+ export interface FunnelEnrollmentSummary {
510
+ id: string;
511
+ versionId: string;
512
+ state: FunnelEnrollmentState;
513
+ effectiveStatus: FunnelEnrollmentState | 'paused';
514
+ source: {
515
+ submissionId: string;
516
+ acceptedAt: string;
517
+ };
518
+ subject: {
519
+ kind: 'subscriber' | 'contact' | 'submission';
520
+ id: string;
521
+ displayLabel: string;
522
+ };
523
+ enteredAt: string;
524
+ nextRunAt: string | null;
525
+ exitCode: string | null;
526
+ stepsCompleted: number;
527
+ stepsTotal: number;
528
+ lastReceiptAt: string | null;
529
+ }
530
+ export interface FunnelTimeline {
531
+ items: {
532
+ executionId: string;
533
+ stepId: string;
534
+ type: FunnelStep['type'];
535
+ state: 'pending' | 'claimed' | 'queued' | 'admitted' | 'succeeded' | 'skipped' | 'uncertain' | 'failed';
536
+ dueAt: string | null;
537
+ occurredAt: string | null;
538
+ attempt: number;
539
+ reasonCode: string | null;
540
+ message: string;
541
+ receipt: {
542
+ domain: 'campaign_send' | 'subscriber_tag' | 'crm_task' | 'wait';
543
+ id: string | null;
544
+ state: string;
545
+ } | null;
546
+ canRetry: boolean;
547
+ }[];
548
+ nextCursor: string | null;
549
+ exit: {
550
+ code: string;
551
+ sourceType: string;
552
+ sourceId: string | null;
553
+ occurredAt: string;
554
+ } | null;
555
+ }
556
+ export declare const FUNNEL_SUMMARY_JSON_SCHEMA: Shape;
557
+ export declare function parseFunnelSummary(value: unknown): FunnelSummary;
558
+ export declare const FUNNEL_ENROLLMENT_JSON_SCHEMA: Shape;
559
+ export declare function parseFunnelEnrollmentSummary(value: unknown): FunnelEnrollmentSummary;
560
+ export declare const FUNNEL_TIMELINE_JSON_SCHEMA: Shape;
561
+ export declare function parseFunnelTimeline(value: unknown): FunnelTimeline;
562
+ export interface FunnelPublishedVersion {
563
+ versionId: string;
564
+ definitionRevision: number;
565
+ publishedAt: string;
566
+ brandFingerprint: string;
567
+ resourceFingerprint: string;
568
+ active: boolean;
569
+ }
570
+ export interface FunnelPublishedVersionDetail extends FunnelPublishedVersion {
571
+ rollbackEvidence: FunnelRollbackEvidence;
572
+ }
573
+ export declare const FUNNEL_VERSION_JSON_SCHEMA: Shape;
574
+ export declare const FUNNEL_VERSION_DETAIL_JSON_SCHEMA: Shape;
575
+ export declare function parseFunnelPublishedVersion(value: unknown): FunnelPublishedVersion;
576
+ export declare function parseFunnelPublishedVersionDetail(value: unknown): FunnelPublishedVersionDetail;