@ever-works/contracts 1.0.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/README.md +62 -0
- package/dist/api/index.cjs +395 -0
- package/dist/api/index.d.cts +2171 -0
- package/dist/api/index.d.ts +2171 -0
- package/dist/api/index.js +335 -0
- package/dist/domain/index.cjs +335 -0
- package/dist/domain/index.d.cts +316 -0
- package/dist/domain/index.d.ts +316 -0
- package/dist/domain/index.js +317 -0
- package/dist/feed.types-BWk3CHqj.d.cts +355 -0
- package/dist/feed.types-BWk3CHqj.d.ts +355 -0
- package/dist/form/index.cjs +2 -0
- package/dist/form/index.d.cts +104 -0
- package/dist/form/index.d.ts +104 -0
- package/dist/form/index.js +1 -0
- package/dist/index.cjs +6968 -0
- package/dist/index.d.cts +11815 -0
- package/dist/index.d.ts +11815 -0
- package/dist/index.js +6019 -0
- package/dist/item/index.cjs +2 -0
- package/dist/item/index.d.cts +195 -0
- package/dist/item/index.d.ts +195 -0
- package/dist/item/index.js +1 -0
- package/dist/telemetry/index.cjs +15 -0
- package/dist/telemetry/index.d.cts +120 -0
- package/dist/telemetry/index.d.ts +120 -0
- package/dist/telemetry/index.js +13 -0
- package/package.json +81 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identifiable entity with id and name
|
|
3
|
+
*/
|
|
4
|
+
interface Identifiable {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly name: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Category for organizing items in a work
|
|
10
|
+
*/
|
|
11
|
+
interface Category {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly description?: string;
|
|
15
|
+
readonly icon_url?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Inline SVG markup for the category icon. Populated by the
|
|
18
|
+
* AI category-icon generator (curated lookup → AI fallback).
|
|
19
|
+
* Sanitized server-side before persistence.
|
|
20
|
+
*/
|
|
21
|
+
readonly icon_svg?: string;
|
|
22
|
+
/** Lower numbers = higher priority (e.g., 1 = first, 2 = second, etc.) */
|
|
23
|
+
readonly priority?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Tag for labeling items
|
|
27
|
+
*/
|
|
28
|
+
interface Tag {
|
|
29
|
+
readonly id: string;
|
|
30
|
+
readonly name: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Collection for curated item lists that span across categories
|
|
34
|
+
*/
|
|
35
|
+
interface Collection {
|
|
36
|
+
readonly id: string;
|
|
37
|
+
readonly name: string;
|
|
38
|
+
readonly description?: string;
|
|
39
|
+
readonly icon_url?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Inline SVG markup for the collection icon. Same generator path
|
|
42
|
+
* as `Category.icon_svg`; sanitized server-side.
|
|
43
|
+
*/
|
|
44
|
+
readonly icon_svg?: string;
|
|
45
|
+
/** Lower numbers = higher priority (e.g., 1 = first, 2 = second, etc.) */
|
|
46
|
+
readonly priority?: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Brand information for items
|
|
50
|
+
*/
|
|
51
|
+
interface Brand {
|
|
52
|
+
readonly id: string;
|
|
53
|
+
readonly name: string;
|
|
54
|
+
readonly logo_url?: string;
|
|
55
|
+
readonly website?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Badge representing an evaluated attribute of an item
|
|
59
|
+
*/
|
|
60
|
+
interface Badge {
|
|
61
|
+
readonly value: string;
|
|
62
|
+
readonly evaluated_at?: string;
|
|
63
|
+
readonly details?: string | null;
|
|
64
|
+
/** Legacy field for backward compatibility */
|
|
65
|
+
readonly type?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Collection of badges for an item, keyed by badge name
|
|
69
|
+
*/
|
|
70
|
+
type ItemBadges = Record<string, Badge>;
|
|
71
|
+
/**
|
|
72
|
+
* Result of badge evaluation for an item
|
|
73
|
+
*/
|
|
74
|
+
interface BadgeEvaluationResult {
|
|
75
|
+
readonly badges: ItemBadges;
|
|
76
|
+
readonly evaluation_summary: string;
|
|
77
|
+
readonly evaluated_at: string;
|
|
78
|
+
readonly domain_type?: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Health status for an item's source URL.
|
|
82
|
+
*/
|
|
83
|
+
type ItemHealthStatus = 'unchecked' | 'healthy' | 'unknown' | 'warning' | 'broken';
|
|
84
|
+
/**
|
|
85
|
+
* Result of the latest source URL health check for an item.
|
|
86
|
+
*/
|
|
87
|
+
interface ItemHealth {
|
|
88
|
+
readonly status: ItemHealthStatus;
|
|
89
|
+
readonly checked_at?: string;
|
|
90
|
+
readonly status_code?: number | null;
|
|
91
|
+
readonly message?: string | null;
|
|
92
|
+
readonly failure_count?: number;
|
|
93
|
+
readonly checked_via?: 'manual' | 'schedule';
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Reachability status for an item's source URL.
|
|
97
|
+
*/
|
|
98
|
+
type ItemSourceReachabilityStatus = 'reachable' | 'broken' | 'unknown';
|
|
99
|
+
/**
|
|
100
|
+
* AI-evaluated source accuracy status for whether a source URL is a good source for the item.
|
|
101
|
+
*/
|
|
102
|
+
type ItemSourceAccuracyStatus = 'accurate' | 'generic' | 'weak' | 'unknown';
|
|
103
|
+
/**
|
|
104
|
+
* Result of validating both source URL reachability and source accuracy.
|
|
105
|
+
*/
|
|
106
|
+
interface ItemSourceValidation {
|
|
107
|
+
readonly reachability_status: ItemSourceReachabilityStatus;
|
|
108
|
+
readonly accuracy_status: ItemSourceAccuracyStatus;
|
|
109
|
+
readonly checked_at?: string;
|
|
110
|
+
readonly confidence_score?: number | null;
|
|
111
|
+
readonly is_relevant?: boolean;
|
|
112
|
+
readonly is_specific?: boolean;
|
|
113
|
+
readonly is_official?: boolean;
|
|
114
|
+
readonly reason?: string | null;
|
|
115
|
+
readonly suggested_source_url?: string | null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Core item data structure for work entries
|
|
119
|
+
*/
|
|
120
|
+
interface ItemData {
|
|
121
|
+
readonly name: string;
|
|
122
|
+
readonly description: string;
|
|
123
|
+
readonly featured?: boolean;
|
|
124
|
+
readonly order?: number;
|
|
125
|
+
readonly source_url: string;
|
|
126
|
+
readonly category: string | readonly string[];
|
|
127
|
+
readonly slug?: string;
|
|
128
|
+
readonly tags: readonly string[] | readonly Tag[];
|
|
129
|
+
readonly collection?: string;
|
|
130
|
+
readonly markdown?: string;
|
|
131
|
+
readonly badges?: ItemBadges;
|
|
132
|
+
readonly brand?: string | Brand;
|
|
133
|
+
readonly brand_logo_url?: string | null;
|
|
134
|
+
readonly images?: readonly string[];
|
|
135
|
+
readonly health?: ItemHealth;
|
|
136
|
+
readonly source_validation?: ItemSourceValidation;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Mutable version of ItemData for use during processing
|
|
140
|
+
*/
|
|
141
|
+
interface MutableItemData {
|
|
142
|
+
name: string;
|
|
143
|
+
description: string;
|
|
144
|
+
featured?: boolean;
|
|
145
|
+
order?: number;
|
|
146
|
+
source_url: string;
|
|
147
|
+
category: string | string[];
|
|
148
|
+
slug?: string;
|
|
149
|
+
tags: string[] | Tag[];
|
|
150
|
+
collection?: string;
|
|
151
|
+
markdown?: string;
|
|
152
|
+
badges?: ItemBadges;
|
|
153
|
+
brand?: string | Brand;
|
|
154
|
+
brand_logo_url?: string | null;
|
|
155
|
+
images?: string[];
|
|
156
|
+
health?: ItemHealth;
|
|
157
|
+
source_validation?: ItemSourceValidation;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* A single dimension/axis used to compare two items (e.g. "Performance", "Pricing")
|
|
161
|
+
*/
|
|
162
|
+
interface ComparisonDimension {
|
|
163
|
+
readonly name: string;
|
|
164
|
+
readonly item_a_summary: string;
|
|
165
|
+
readonly item_b_summary: string;
|
|
166
|
+
readonly item_a_score?: number;
|
|
167
|
+
readonly item_b_score?: number;
|
|
168
|
+
readonly winner?: 'item_a' | 'item_b' | 'tie';
|
|
169
|
+
}
|
|
170
|
+
interface ComparisonSource {
|
|
171
|
+
readonly title: string;
|
|
172
|
+
readonly url: string;
|
|
173
|
+
readonly note?: string;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Full comparison data for an "A vs B" comparison page
|
|
177
|
+
*/
|
|
178
|
+
interface ComparisonData {
|
|
179
|
+
readonly id: string;
|
|
180
|
+
readonly slug: string;
|
|
181
|
+
readonly title: string;
|
|
182
|
+
readonly item_a_slug: string;
|
|
183
|
+
readonly item_b_slug: string;
|
|
184
|
+
readonly item_a_name: string;
|
|
185
|
+
readonly item_b_name: string;
|
|
186
|
+
readonly category: string;
|
|
187
|
+
readonly summary: string;
|
|
188
|
+
readonly verdict: string;
|
|
189
|
+
readonly verdict_winner?: 'item_a' | 'item_b' | 'tie';
|
|
190
|
+
readonly dimensions: readonly ComparisonDimension[];
|
|
191
|
+
readonly sources: readonly ComparisonSource[];
|
|
192
|
+
readonly generated_at: string;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export type { Badge, BadgeEvaluationResult, Brand, Category, Collection, ComparisonData, ComparisonDimension, ComparisonSource, Identifiable, ItemBadges, ItemData, ItemHealth, ItemHealthStatus, ItemSourceAccuracyStatus, ItemSourceReachabilityStatus, ItemSourceValidation, MutableItemData, Tag };
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identifiable entity with id and name
|
|
3
|
+
*/
|
|
4
|
+
interface Identifiable {
|
|
5
|
+
readonly id: string;
|
|
6
|
+
readonly name: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Category for organizing items in a work
|
|
10
|
+
*/
|
|
11
|
+
interface Category {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly name: string;
|
|
14
|
+
readonly description?: string;
|
|
15
|
+
readonly icon_url?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Inline SVG markup for the category icon. Populated by the
|
|
18
|
+
* AI category-icon generator (curated lookup → AI fallback).
|
|
19
|
+
* Sanitized server-side before persistence.
|
|
20
|
+
*/
|
|
21
|
+
readonly icon_svg?: string;
|
|
22
|
+
/** Lower numbers = higher priority (e.g., 1 = first, 2 = second, etc.) */
|
|
23
|
+
readonly priority?: number;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Tag for labeling items
|
|
27
|
+
*/
|
|
28
|
+
interface Tag {
|
|
29
|
+
readonly id: string;
|
|
30
|
+
readonly name: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Collection for curated item lists that span across categories
|
|
34
|
+
*/
|
|
35
|
+
interface Collection {
|
|
36
|
+
readonly id: string;
|
|
37
|
+
readonly name: string;
|
|
38
|
+
readonly description?: string;
|
|
39
|
+
readonly icon_url?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Inline SVG markup for the collection icon. Same generator path
|
|
42
|
+
* as `Category.icon_svg`; sanitized server-side.
|
|
43
|
+
*/
|
|
44
|
+
readonly icon_svg?: string;
|
|
45
|
+
/** Lower numbers = higher priority (e.g., 1 = first, 2 = second, etc.) */
|
|
46
|
+
readonly priority?: number;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Brand information for items
|
|
50
|
+
*/
|
|
51
|
+
interface Brand {
|
|
52
|
+
readonly id: string;
|
|
53
|
+
readonly name: string;
|
|
54
|
+
readonly logo_url?: string;
|
|
55
|
+
readonly website?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Badge representing an evaluated attribute of an item
|
|
59
|
+
*/
|
|
60
|
+
interface Badge {
|
|
61
|
+
readonly value: string;
|
|
62
|
+
readonly evaluated_at?: string;
|
|
63
|
+
readonly details?: string | null;
|
|
64
|
+
/** Legacy field for backward compatibility */
|
|
65
|
+
readonly type?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Collection of badges for an item, keyed by badge name
|
|
69
|
+
*/
|
|
70
|
+
type ItemBadges = Record<string, Badge>;
|
|
71
|
+
/**
|
|
72
|
+
* Result of badge evaluation for an item
|
|
73
|
+
*/
|
|
74
|
+
interface BadgeEvaluationResult {
|
|
75
|
+
readonly badges: ItemBadges;
|
|
76
|
+
readonly evaluation_summary: string;
|
|
77
|
+
readonly evaluated_at: string;
|
|
78
|
+
readonly domain_type?: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Health status for an item's source URL.
|
|
82
|
+
*/
|
|
83
|
+
type ItemHealthStatus = 'unchecked' | 'healthy' | 'unknown' | 'warning' | 'broken';
|
|
84
|
+
/**
|
|
85
|
+
* Result of the latest source URL health check for an item.
|
|
86
|
+
*/
|
|
87
|
+
interface ItemHealth {
|
|
88
|
+
readonly status: ItemHealthStatus;
|
|
89
|
+
readonly checked_at?: string;
|
|
90
|
+
readonly status_code?: number | null;
|
|
91
|
+
readonly message?: string | null;
|
|
92
|
+
readonly failure_count?: number;
|
|
93
|
+
readonly checked_via?: 'manual' | 'schedule';
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Reachability status for an item's source URL.
|
|
97
|
+
*/
|
|
98
|
+
type ItemSourceReachabilityStatus = 'reachable' | 'broken' | 'unknown';
|
|
99
|
+
/**
|
|
100
|
+
* AI-evaluated source accuracy status for whether a source URL is a good source for the item.
|
|
101
|
+
*/
|
|
102
|
+
type ItemSourceAccuracyStatus = 'accurate' | 'generic' | 'weak' | 'unknown';
|
|
103
|
+
/**
|
|
104
|
+
* Result of validating both source URL reachability and source accuracy.
|
|
105
|
+
*/
|
|
106
|
+
interface ItemSourceValidation {
|
|
107
|
+
readonly reachability_status: ItemSourceReachabilityStatus;
|
|
108
|
+
readonly accuracy_status: ItemSourceAccuracyStatus;
|
|
109
|
+
readonly checked_at?: string;
|
|
110
|
+
readonly confidence_score?: number | null;
|
|
111
|
+
readonly is_relevant?: boolean;
|
|
112
|
+
readonly is_specific?: boolean;
|
|
113
|
+
readonly is_official?: boolean;
|
|
114
|
+
readonly reason?: string | null;
|
|
115
|
+
readonly suggested_source_url?: string | null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Core item data structure for work entries
|
|
119
|
+
*/
|
|
120
|
+
interface ItemData {
|
|
121
|
+
readonly name: string;
|
|
122
|
+
readonly description: string;
|
|
123
|
+
readonly featured?: boolean;
|
|
124
|
+
readonly order?: number;
|
|
125
|
+
readonly source_url: string;
|
|
126
|
+
readonly category: string | readonly string[];
|
|
127
|
+
readonly slug?: string;
|
|
128
|
+
readonly tags: readonly string[] | readonly Tag[];
|
|
129
|
+
readonly collection?: string;
|
|
130
|
+
readonly markdown?: string;
|
|
131
|
+
readonly badges?: ItemBadges;
|
|
132
|
+
readonly brand?: string | Brand;
|
|
133
|
+
readonly brand_logo_url?: string | null;
|
|
134
|
+
readonly images?: readonly string[];
|
|
135
|
+
readonly health?: ItemHealth;
|
|
136
|
+
readonly source_validation?: ItemSourceValidation;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Mutable version of ItemData for use during processing
|
|
140
|
+
*/
|
|
141
|
+
interface MutableItemData {
|
|
142
|
+
name: string;
|
|
143
|
+
description: string;
|
|
144
|
+
featured?: boolean;
|
|
145
|
+
order?: number;
|
|
146
|
+
source_url: string;
|
|
147
|
+
category: string | string[];
|
|
148
|
+
slug?: string;
|
|
149
|
+
tags: string[] | Tag[];
|
|
150
|
+
collection?: string;
|
|
151
|
+
markdown?: string;
|
|
152
|
+
badges?: ItemBadges;
|
|
153
|
+
brand?: string | Brand;
|
|
154
|
+
brand_logo_url?: string | null;
|
|
155
|
+
images?: string[];
|
|
156
|
+
health?: ItemHealth;
|
|
157
|
+
source_validation?: ItemSourceValidation;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* A single dimension/axis used to compare two items (e.g. "Performance", "Pricing")
|
|
161
|
+
*/
|
|
162
|
+
interface ComparisonDimension {
|
|
163
|
+
readonly name: string;
|
|
164
|
+
readonly item_a_summary: string;
|
|
165
|
+
readonly item_b_summary: string;
|
|
166
|
+
readonly item_a_score?: number;
|
|
167
|
+
readonly item_b_score?: number;
|
|
168
|
+
readonly winner?: 'item_a' | 'item_b' | 'tie';
|
|
169
|
+
}
|
|
170
|
+
interface ComparisonSource {
|
|
171
|
+
readonly title: string;
|
|
172
|
+
readonly url: string;
|
|
173
|
+
readonly note?: string;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Full comparison data for an "A vs B" comparison page
|
|
177
|
+
*/
|
|
178
|
+
interface ComparisonData {
|
|
179
|
+
readonly id: string;
|
|
180
|
+
readonly slug: string;
|
|
181
|
+
readonly title: string;
|
|
182
|
+
readonly item_a_slug: string;
|
|
183
|
+
readonly item_b_slug: string;
|
|
184
|
+
readonly item_a_name: string;
|
|
185
|
+
readonly item_b_name: string;
|
|
186
|
+
readonly category: string;
|
|
187
|
+
readonly summary: string;
|
|
188
|
+
readonly verdict: string;
|
|
189
|
+
readonly verdict_winner?: 'item_a' | 'item_b' | 'tie';
|
|
190
|
+
readonly dimensions: readonly ComparisonDimension[];
|
|
191
|
+
readonly sources: readonly ComparisonSource[];
|
|
192
|
+
readonly generated_at: string;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export type { Badge, BadgeEvaluationResult, Brand, Category, Collection, ComparisonData, ComparisonDimension, ComparisonSource, Identifiable, ItemBadges, ItemData, ItemHealth, ItemHealthStatus, ItemSourceAccuracyStatus, ItemSourceReachabilityStatus, ItemSourceValidation, MutableItemData, Tag };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/telemetry/zero-friction-funnel.ts
|
|
4
|
+
var ZERO_FRICTION_FUNNEL_EVENTS = {
|
|
5
|
+
LANDING_PROMPT_SUBMIT: "zero_friction.landing_prompt_submit",
|
|
6
|
+
ANON_USER_CREATED: "zero_friction.anon_user_created",
|
|
7
|
+
WIZARD_FINISHED: "zero_friction.wizard_finished",
|
|
8
|
+
WORK_CREATED: "zero_friction.work_created",
|
|
9
|
+
REPOS_PUSHED: "zero_friction.repos_pushed",
|
|
10
|
+
DEPLOY_STARTED: "zero_friction.deploy_started",
|
|
11
|
+
DEPLOY_READY: "zero_friction.deploy_ready",
|
|
12
|
+
CLAIM_ACCOUNT: "zero_friction.claim_account"
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
exports.ZERO_FRICTION_FUNNEL_EVENTS = ZERO_FRICTION_FUNNEL_EVENTS;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EW-617 G8 — wire format for the zero-friction prompt → deployed Work
|
|
3
|
+
* funnel events.
|
|
4
|
+
*
|
|
5
|
+
* These types pin the canonical event names + payload shapes so the
|
|
6
|
+
* emit sites (G1 landing form, G2 anon auth, G3 claim, G4 quick-create,
|
|
7
|
+
* deploy.service.ts, etc.) all agree on the schema and downstream
|
|
8
|
+
* consumers (PostHog dashboards, log queries, the OpenTelemetry sink)
|
|
9
|
+
* can rely on stable fields.
|
|
10
|
+
*
|
|
11
|
+
* Emit sites in scope:
|
|
12
|
+
*
|
|
13
|
+
* landing-prompt-submit — website (G1) — apps/web/components/global/LandingPromptForm.tsx
|
|
14
|
+
* anon-user-created — platform (G2) — apps/api/.../anonymous-auth.service.ts
|
|
15
|
+
* wizard-finished — platform (G4) — apps/web/components/onboarding/EverWorksOnboardingWizard.tsx
|
|
16
|
+
* work-created — platform (G4) — apps/api/.../works.controller.ts (quickCreateWork)
|
|
17
|
+
* repos-pushed — platform — packages/agent/src/services/work-lifecycle.service.ts (EW-614 path)
|
|
18
|
+
* deploy-started — platform — apps/api/.../deploy.service.ts (deploy())
|
|
19
|
+
* deploy-ready — platform — apps/api/.../tasks/deployment-verifier.service.ts
|
|
20
|
+
* claim-account — platform (G3) — apps/api/.../claim-account.service.ts
|
|
21
|
+
*
|
|
22
|
+
* Bumping the schema version is a follow-up PR; for now treat the
|
|
23
|
+
* shapes as additive (clients should ignore unknown fields).
|
|
24
|
+
*/
|
|
25
|
+
declare const ZERO_FRICTION_FUNNEL_EVENTS: {
|
|
26
|
+
readonly LANDING_PROMPT_SUBMIT: "zero_friction.landing_prompt_submit";
|
|
27
|
+
readonly ANON_USER_CREATED: "zero_friction.anon_user_created";
|
|
28
|
+
readonly WIZARD_FINISHED: "zero_friction.wizard_finished";
|
|
29
|
+
readonly WORK_CREATED: "zero_friction.work_created";
|
|
30
|
+
readonly REPOS_PUSHED: "zero_friction.repos_pushed";
|
|
31
|
+
readonly DEPLOY_STARTED: "zero_friction.deploy_started";
|
|
32
|
+
readonly DEPLOY_READY: "zero_friction.deploy_ready";
|
|
33
|
+
readonly CLAIM_ACCOUNT: "zero_friction.claim_account";
|
|
34
|
+
};
|
|
35
|
+
type ZeroFrictionFunnelEvent = (typeof ZERO_FRICTION_FUNNEL_EVENTS)[keyof typeof ZERO_FRICTION_FUNNEL_EVENTS];
|
|
36
|
+
interface FunnelEventBase {
|
|
37
|
+
/** ISO 8601 timestamp the event was emitted. */
|
|
38
|
+
readonly timestamp: string;
|
|
39
|
+
/**
|
|
40
|
+
* Correlation id that survives across the full funnel. Origin: G1
|
|
41
|
+
* mints a UUID v4 on submit, carries it via the URL fragment
|
|
42
|
+
* (`#prompt=…&corrId=…`), G4 reads it into wizard state, every
|
|
43
|
+
* downstream emit reuses it. Lets ops trace a single user across
|
|
44
|
+
* multiple services + asynchronous task runs.
|
|
45
|
+
*/
|
|
46
|
+
readonly correlationId: string;
|
|
47
|
+
/**
|
|
48
|
+
* Numeric step (1..8) so PostHog can build a funnel without
|
|
49
|
+
* hard-coding event ordering. Pinned at the top of each interface
|
|
50
|
+
* below.
|
|
51
|
+
*/
|
|
52
|
+
readonly funnelStep: number;
|
|
53
|
+
}
|
|
54
|
+
interface LandingPromptSubmitEvent extends FunnelEventBase {
|
|
55
|
+
readonly funnelStep: 1;
|
|
56
|
+
readonly event: typeof ZERO_FRICTION_FUNNEL_EVENTS.LANDING_PROMPT_SUBMIT;
|
|
57
|
+
readonly promptLength: number;
|
|
58
|
+
/** UA family for top-of-funnel segmentation; never raw UA string. */
|
|
59
|
+
readonly clientKind: 'browser' | 'crawler' | 'unknown';
|
|
60
|
+
readonly referer?: string | null;
|
|
61
|
+
}
|
|
62
|
+
interface AnonUserCreatedEvent extends FunnelEventBase {
|
|
63
|
+
readonly funnelStep: 2;
|
|
64
|
+
readonly event: typeof ZERO_FRICTION_FUNNEL_EVENTS.ANON_USER_CREATED;
|
|
65
|
+
readonly anonUserId: string;
|
|
66
|
+
readonly anonymousExpiresAt: string;
|
|
67
|
+
/** Truncated /24 (IPv4) or /48 (IPv6) so we never persist raw IPs. */
|
|
68
|
+
readonly ipPrefix: string | null;
|
|
69
|
+
}
|
|
70
|
+
interface WizardFinishedEvent extends FunnelEventBase {
|
|
71
|
+
readonly funnelStep: 3;
|
|
72
|
+
readonly event: typeof ZERO_FRICTION_FUNNEL_EVENTS.WIZARD_FINISHED;
|
|
73
|
+
readonly userId: string;
|
|
74
|
+
readonly isAnonymous: boolean;
|
|
75
|
+
/** Choices captured at the moment Generate-now was clicked. */
|
|
76
|
+
readonly aiChoice: string;
|
|
77
|
+
readonly storageChoice: string;
|
|
78
|
+
readonly deployChoice: string;
|
|
79
|
+
}
|
|
80
|
+
interface WorkCreatedEvent extends FunnelEventBase {
|
|
81
|
+
readonly funnelStep: 4;
|
|
82
|
+
readonly event: typeof ZERO_FRICTION_FUNNEL_EVENTS.WORK_CREATED;
|
|
83
|
+
readonly userId: string;
|
|
84
|
+
readonly workId: string;
|
|
85
|
+
readonly workSlug: string;
|
|
86
|
+
/** True when the row was created via /api/works/quick-create. */
|
|
87
|
+
readonly viaQuickCreate: boolean;
|
|
88
|
+
}
|
|
89
|
+
interface ReposPushedEvent extends FunnelEventBase {
|
|
90
|
+
readonly funnelStep: 5;
|
|
91
|
+
readonly event: typeof ZERO_FRICTION_FUNNEL_EVENTS.REPOS_PUSHED;
|
|
92
|
+
readonly workId: string;
|
|
93
|
+
/** `ever-works-cloud/<slug>-data`, `<slug>-website`, `<slug>-mcp`. */
|
|
94
|
+
readonly repos: readonly string[];
|
|
95
|
+
}
|
|
96
|
+
interface DeployStartedEvent extends FunnelEventBase {
|
|
97
|
+
readonly funnelStep: 6;
|
|
98
|
+
readonly event: typeof ZERO_FRICTION_FUNNEL_EVENTS.DEPLOY_STARTED;
|
|
99
|
+
readonly workId: string;
|
|
100
|
+
readonly deployProvider: string;
|
|
101
|
+
readonly ingressHost: string | null;
|
|
102
|
+
}
|
|
103
|
+
interface DeployReadyEvent extends FunnelEventBase {
|
|
104
|
+
readonly funnelStep: 7;
|
|
105
|
+
readonly event: typeof ZERO_FRICTION_FUNNEL_EVENTS.DEPLOY_READY;
|
|
106
|
+
readonly workId: string;
|
|
107
|
+
readonly websiteUrl: string;
|
|
108
|
+
/** Milliseconds from `deploy-started` to `deploy-ready`. */
|
|
109
|
+
readonly elapsedMs: number;
|
|
110
|
+
}
|
|
111
|
+
interface ClaimAccountEvent extends FunnelEventBase {
|
|
112
|
+
readonly funnelStep: 8;
|
|
113
|
+
readonly event: typeof ZERO_FRICTION_FUNNEL_EVENTS.CLAIM_ACCOUNT;
|
|
114
|
+
readonly userId: string;
|
|
115
|
+
/** True if the user came in via the zero-friction flow + claimed. */
|
|
116
|
+
readonly viaZeroFriction: boolean;
|
|
117
|
+
}
|
|
118
|
+
type ZeroFrictionFunnelPayload = LandingPromptSubmitEvent | AnonUserCreatedEvent | WizardFinishedEvent | WorkCreatedEvent | ReposPushedEvent | DeployStartedEvent | DeployReadyEvent | ClaimAccountEvent;
|
|
119
|
+
|
|
120
|
+
export { type AnonUserCreatedEvent, type ClaimAccountEvent, type DeployReadyEvent, type DeployStartedEvent, type FunnelEventBase, type LandingPromptSubmitEvent, type ReposPushedEvent, type WizardFinishedEvent, type WorkCreatedEvent, ZERO_FRICTION_FUNNEL_EVENTS, type ZeroFrictionFunnelEvent, type ZeroFrictionFunnelPayload };
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EW-617 G8 — wire format for the zero-friction prompt → deployed Work
|
|
3
|
+
* funnel events.
|
|
4
|
+
*
|
|
5
|
+
* These types pin the canonical event names + payload shapes so the
|
|
6
|
+
* emit sites (G1 landing form, G2 anon auth, G3 claim, G4 quick-create,
|
|
7
|
+
* deploy.service.ts, etc.) all agree on the schema and downstream
|
|
8
|
+
* consumers (PostHog dashboards, log queries, the OpenTelemetry sink)
|
|
9
|
+
* can rely on stable fields.
|
|
10
|
+
*
|
|
11
|
+
* Emit sites in scope:
|
|
12
|
+
*
|
|
13
|
+
* landing-prompt-submit — website (G1) — apps/web/components/global/LandingPromptForm.tsx
|
|
14
|
+
* anon-user-created — platform (G2) — apps/api/.../anonymous-auth.service.ts
|
|
15
|
+
* wizard-finished — platform (G4) — apps/web/components/onboarding/EverWorksOnboardingWizard.tsx
|
|
16
|
+
* work-created — platform (G4) — apps/api/.../works.controller.ts (quickCreateWork)
|
|
17
|
+
* repos-pushed — platform — packages/agent/src/services/work-lifecycle.service.ts (EW-614 path)
|
|
18
|
+
* deploy-started — platform — apps/api/.../deploy.service.ts (deploy())
|
|
19
|
+
* deploy-ready — platform — apps/api/.../tasks/deployment-verifier.service.ts
|
|
20
|
+
* claim-account — platform (G3) — apps/api/.../claim-account.service.ts
|
|
21
|
+
*
|
|
22
|
+
* Bumping the schema version is a follow-up PR; for now treat the
|
|
23
|
+
* shapes as additive (clients should ignore unknown fields).
|
|
24
|
+
*/
|
|
25
|
+
declare const ZERO_FRICTION_FUNNEL_EVENTS: {
|
|
26
|
+
readonly LANDING_PROMPT_SUBMIT: "zero_friction.landing_prompt_submit";
|
|
27
|
+
readonly ANON_USER_CREATED: "zero_friction.anon_user_created";
|
|
28
|
+
readonly WIZARD_FINISHED: "zero_friction.wizard_finished";
|
|
29
|
+
readonly WORK_CREATED: "zero_friction.work_created";
|
|
30
|
+
readonly REPOS_PUSHED: "zero_friction.repos_pushed";
|
|
31
|
+
readonly DEPLOY_STARTED: "zero_friction.deploy_started";
|
|
32
|
+
readonly DEPLOY_READY: "zero_friction.deploy_ready";
|
|
33
|
+
readonly CLAIM_ACCOUNT: "zero_friction.claim_account";
|
|
34
|
+
};
|
|
35
|
+
type ZeroFrictionFunnelEvent = (typeof ZERO_FRICTION_FUNNEL_EVENTS)[keyof typeof ZERO_FRICTION_FUNNEL_EVENTS];
|
|
36
|
+
interface FunnelEventBase {
|
|
37
|
+
/** ISO 8601 timestamp the event was emitted. */
|
|
38
|
+
readonly timestamp: string;
|
|
39
|
+
/**
|
|
40
|
+
* Correlation id that survives across the full funnel. Origin: G1
|
|
41
|
+
* mints a UUID v4 on submit, carries it via the URL fragment
|
|
42
|
+
* (`#prompt=…&corrId=…`), G4 reads it into wizard state, every
|
|
43
|
+
* downstream emit reuses it. Lets ops trace a single user across
|
|
44
|
+
* multiple services + asynchronous task runs.
|
|
45
|
+
*/
|
|
46
|
+
readonly correlationId: string;
|
|
47
|
+
/**
|
|
48
|
+
* Numeric step (1..8) so PostHog can build a funnel without
|
|
49
|
+
* hard-coding event ordering. Pinned at the top of each interface
|
|
50
|
+
* below.
|
|
51
|
+
*/
|
|
52
|
+
readonly funnelStep: number;
|
|
53
|
+
}
|
|
54
|
+
interface LandingPromptSubmitEvent extends FunnelEventBase {
|
|
55
|
+
readonly funnelStep: 1;
|
|
56
|
+
readonly event: typeof ZERO_FRICTION_FUNNEL_EVENTS.LANDING_PROMPT_SUBMIT;
|
|
57
|
+
readonly promptLength: number;
|
|
58
|
+
/** UA family for top-of-funnel segmentation; never raw UA string. */
|
|
59
|
+
readonly clientKind: 'browser' | 'crawler' | 'unknown';
|
|
60
|
+
readonly referer?: string | null;
|
|
61
|
+
}
|
|
62
|
+
interface AnonUserCreatedEvent extends FunnelEventBase {
|
|
63
|
+
readonly funnelStep: 2;
|
|
64
|
+
readonly event: typeof ZERO_FRICTION_FUNNEL_EVENTS.ANON_USER_CREATED;
|
|
65
|
+
readonly anonUserId: string;
|
|
66
|
+
readonly anonymousExpiresAt: string;
|
|
67
|
+
/** Truncated /24 (IPv4) or /48 (IPv6) so we never persist raw IPs. */
|
|
68
|
+
readonly ipPrefix: string | null;
|
|
69
|
+
}
|
|
70
|
+
interface WizardFinishedEvent extends FunnelEventBase {
|
|
71
|
+
readonly funnelStep: 3;
|
|
72
|
+
readonly event: typeof ZERO_FRICTION_FUNNEL_EVENTS.WIZARD_FINISHED;
|
|
73
|
+
readonly userId: string;
|
|
74
|
+
readonly isAnonymous: boolean;
|
|
75
|
+
/** Choices captured at the moment Generate-now was clicked. */
|
|
76
|
+
readonly aiChoice: string;
|
|
77
|
+
readonly storageChoice: string;
|
|
78
|
+
readonly deployChoice: string;
|
|
79
|
+
}
|
|
80
|
+
interface WorkCreatedEvent extends FunnelEventBase {
|
|
81
|
+
readonly funnelStep: 4;
|
|
82
|
+
readonly event: typeof ZERO_FRICTION_FUNNEL_EVENTS.WORK_CREATED;
|
|
83
|
+
readonly userId: string;
|
|
84
|
+
readonly workId: string;
|
|
85
|
+
readonly workSlug: string;
|
|
86
|
+
/** True when the row was created via /api/works/quick-create. */
|
|
87
|
+
readonly viaQuickCreate: boolean;
|
|
88
|
+
}
|
|
89
|
+
interface ReposPushedEvent extends FunnelEventBase {
|
|
90
|
+
readonly funnelStep: 5;
|
|
91
|
+
readonly event: typeof ZERO_FRICTION_FUNNEL_EVENTS.REPOS_PUSHED;
|
|
92
|
+
readonly workId: string;
|
|
93
|
+
/** `ever-works-cloud/<slug>-data`, `<slug>-website`, `<slug>-mcp`. */
|
|
94
|
+
readonly repos: readonly string[];
|
|
95
|
+
}
|
|
96
|
+
interface DeployStartedEvent extends FunnelEventBase {
|
|
97
|
+
readonly funnelStep: 6;
|
|
98
|
+
readonly event: typeof ZERO_FRICTION_FUNNEL_EVENTS.DEPLOY_STARTED;
|
|
99
|
+
readonly workId: string;
|
|
100
|
+
readonly deployProvider: string;
|
|
101
|
+
readonly ingressHost: string | null;
|
|
102
|
+
}
|
|
103
|
+
interface DeployReadyEvent extends FunnelEventBase {
|
|
104
|
+
readonly funnelStep: 7;
|
|
105
|
+
readonly event: typeof ZERO_FRICTION_FUNNEL_EVENTS.DEPLOY_READY;
|
|
106
|
+
readonly workId: string;
|
|
107
|
+
readonly websiteUrl: string;
|
|
108
|
+
/** Milliseconds from `deploy-started` to `deploy-ready`. */
|
|
109
|
+
readonly elapsedMs: number;
|
|
110
|
+
}
|
|
111
|
+
interface ClaimAccountEvent extends FunnelEventBase {
|
|
112
|
+
readonly funnelStep: 8;
|
|
113
|
+
readonly event: typeof ZERO_FRICTION_FUNNEL_EVENTS.CLAIM_ACCOUNT;
|
|
114
|
+
readonly userId: string;
|
|
115
|
+
/** True if the user came in via the zero-friction flow + claimed. */
|
|
116
|
+
readonly viaZeroFriction: boolean;
|
|
117
|
+
}
|
|
118
|
+
type ZeroFrictionFunnelPayload = LandingPromptSubmitEvent | AnonUserCreatedEvent | WizardFinishedEvent | WorkCreatedEvent | ReposPushedEvent | DeployStartedEvent | DeployReadyEvent | ClaimAccountEvent;
|
|
119
|
+
|
|
120
|
+
export { type AnonUserCreatedEvent, type ClaimAccountEvent, type DeployReadyEvent, type DeployStartedEvent, type FunnelEventBase, type LandingPromptSubmitEvent, type ReposPushedEvent, type WizardFinishedEvent, type WorkCreatedEvent, ZERO_FRICTION_FUNNEL_EVENTS, type ZeroFrictionFunnelEvent, type ZeroFrictionFunnelPayload };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/telemetry/zero-friction-funnel.ts
|
|
2
|
+
var ZERO_FRICTION_FUNNEL_EVENTS = {
|
|
3
|
+
LANDING_PROMPT_SUBMIT: "zero_friction.landing_prompt_submit",
|
|
4
|
+
ANON_USER_CREATED: "zero_friction.anon_user_created",
|
|
5
|
+
WIZARD_FINISHED: "zero_friction.wizard_finished",
|
|
6
|
+
WORK_CREATED: "zero_friction.work_created",
|
|
7
|
+
REPOS_PUSHED: "zero_friction.repos_pushed",
|
|
8
|
+
DEPLOY_STARTED: "zero_friction.deploy_started",
|
|
9
|
+
DEPLOY_READY: "zero_friction.deploy_ready",
|
|
10
|
+
CLAIM_ACCOUNT: "zero_friction.claim_account"
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export { ZERO_FRICTION_FUNNEL_EVENTS };
|