ad2app-lib 1.5.0 → 1.6.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/index.ts +2 -1
- package/package.json +6 -2
- package/src/analytics/index.ts +91 -0
- package/src/legal/meta.ts +1 -1
- package/src/legal/privacy.ts +9 -6
- package/src/legal/terms.ts +1 -0
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ad2app-lib",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
"./utils": "./dist/utils/index.js",
|
|
9
9
|
"./types": "./dist/types/index.js",
|
|
10
10
|
"./api": "./dist/api/index.js",
|
|
11
|
-
"./legal": "./dist/legal/index.js"
|
|
11
|
+
"./legal": "./dist/legal/index.js",
|
|
12
|
+
"./analytics": "./dist/analytics/index.js"
|
|
12
13
|
},
|
|
13
14
|
"typesVersions": {
|
|
14
15
|
"*": {
|
|
@@ -24,6 +25,9 @@
|
|
|
24
25
|
"legal": [
|
|
25
26
|
"dist/legal/index.d.ts"
|
|
26
27
|
],
|
|
28
|
+
"analytics": [
|
|
29
|
+
"dist/analytics/index.d.ts"
|
|
30
|
+
],
|
|
27
31
|
"*": [
|
|
28
32
|
"dist/index.d.ts"
|
|
29
33
|
]
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Canonical PostHog analytics taxonomy for ad2app.
|
|
3
|
+
*
|
|
4
|
+
* Single source of truth for event names, event property shapes, and person
|
|
5
|
+
* property keys across ALL surfaces (frontend, backend, landing, future MCP).
|
|
6
|
+
* Spec: ad2appus/docs/posthog-implementation.md §3.
|
|
7
|
+
*
|
|
8
|
+
* Naming convention: snake_case, object_verb_past_tense.
|
|
9
|
+
* Do NOT rename events after they ship — historical data does not migrate.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Canonical PostHog event names. */
|
|
13
|
+
export const EVENTS = {
|
|
14
|
+
// Acquisition (landing)
|
|
15
|
+
LANDING_CTA_CLICKED: 'landing_cta_clicked',
|
|
16
|
+
|
|
17
|
+
// Activation (web app)
|
|
18
|
+
SIGNED_UP: 'signed_up', // server-owned (backend, on user creation)
|
|
19
|
+
PROFILE_COMPLETED: 'profile_completed', // the /complete-profile step (influencers)
|
|
20
|
+
LOGGED_IN: 'logged_in',
|
|
21
|
+
SOCIAL_ACCOUNT_CONNECTED: 'social_account_connected',
|
|
22
|
+
COMPOSE_STARTED: 'compose_started',
|
|
23
|
+
POST_SCHEDULED: 'post_scheduled',
|
|
24
|
+
POST_PUBLISHED: 'post_published', // server-confirmed (backend)
|
|
25
|
+
ANALYTICS_VIEWED: 'analytics_viewed',
|
|
26
|
+
|
|
27
|
+
// Revenue (backend = source of truth)
|
|
28
|
+
TRIAL_STARTED: 'trial_started',
|
|
29
|
+
LIMIT_REACHED: 'limit_reached', // cap hit (e.g. X link-post cap) — the upsell trigger
|
|
30
|
+
UPGRADE_CLICKED: 'upgrade_clicked', // client intent
|
|
31
|
+
CHECKOUT_STARTED: 'checkout_started',
|
|
32
|
+
SUBSCRIPTION_STARTED: 'subscription_started',
|
|
33
|
+
SUBSCRIPTION_CANCELED: 'subscription_canceled',
|
|
34
|
+
} as const;
|
|
35
|
+
|
|
36
|
+
export type EventName = (typeof EVENTS)[keyof typeof EVENTS];
|
|
37
|
+
|
|
38
|
+
/** Property shape per event. Keeps emitters honest across repos. */
|
|
39
|
+
export interface EventProperties {
|
|
40
|
+
[EVENTS.LANDING_CTA_CLICKED]: {
|
|
41
|
+
location: 'hero' | 'pricing' | 'final_cta' | 'nav';
|
|
42
|
+
};
|
|
43
|
+
[EVENTS.SIGNED_UP]: { method: 'email' | 'google'; role: string };
|
|
44
|
+
[EVENTS.PROFILE_COMPLETED]: { role: string };
|
|
45
|
+
[EVENTS.LOGGED_IN]: { method?: 'email' | 'google' };
|
|
46
|
+
[EVENTS.SOCIAL_ACCOUNT_CONNECTED]: {
|
|
47
|
+
platform: string;
|
|
48
|
+
account_count?: number;
|
|
49
|
+
};
|
|
50
|
+
[EVENTS.COMPOSE_STARTED]: { source?: 'wizard' | 'quick' };
|
|
51
|
+
[EVENTS.POST_SCHEDULED]: {
|
|
52
|
+
platforms: string[];
|
|
53
|
+
platform_count: number;
|
|
54
|
+
scheduled_for?: string;
|
|
55
|
+
};
|
|
56
|
+
[EVENTS.POST_PUBLISHED]: {
|
|
57
|
+
platforms: string[];
|
|
58
|
+
platform_count: number;
|
|
59
|
+
status: 'success' | 'partial' | 'failed';
|
|
60
|
+
};
|
|
61
|
+
[EVENTS.ANALYTICS_VIEWED]: { scope?: 'overview' | 'account' | 'post' };
|
|
62
|
+
[EVENTS.TRIAL_STARTED]: { plan: string };
|
|
63
|
+
[EVENTS.LIMIT_REACHED]: {
|
|
64
|
+
limit: 'x_link_posts' | 'accounts';
|
|
65
|
+
plan: string;
|
|
66
|
+
cap: number;
|
|
67
|
+
};
|
|
68
|
+
[EVENTS.UPGRADE_CLICKED]: {
|
|
69
|
+
from_plan?: string;
|
|
70
|
+
to_plan?: string;
|
|
71
|
+
surface: string;
|
|
72
|
+
};
|
|
73
|
+
[EVENTS.CHECKOUT_STARTED]: { plan: string };
|
|
74
|
+
[EVENTS.SUBSCRIPTION_STARTED]: {
|
|
75
|
+
plan: string;
|
|
76
|
+
mrr?: number;
|
|
77
|
+
accounts_included?: number;
|
|
78
|
+
};
|
|
79
|
+
[EVENTS.SUBSCRIPTION_CANCELED]: { plan: string; reason?: string };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Canonical person property keys (set via identify / $set). */
|
|
83
|
+
export const PERSON_PROPS = {
|
|
84
|
+
EMAIL: 'email',
|
|
85
|
+
ROLE: 'role',
|
|
86
|
+
PLAN: 'plan', // 'trial' | 'starter' | 'all_platforms' | 'free'
|
|
87
|
+
ACCOUNT_COUNT: 'account_count',
|
|
88
|
+
TRIAL_ENDS_AT: 'trial_ends_at',
|
|
89
|
+
} as const;
|
|
90
|
+
|
|
91
|
+
export type PersonPropKey = (typeof PERSON_PROPS)[keyof typeof PERSON_PROPS];
|
package/src/legal/meta.ts
CHANGED
package/src/legal/privacy.ts
CHANGED
|
@@ -46,7 +46,8 @@ export const PRIVACY_SECTIONS: LegalSection[] = [
|
|
|
46
46
|
{ text: '**Post content & scheduled posts:** the content you compose, schedule, and publish through the Service — including post text, captions, hashtags, links, scheduling times, and the images, videos, and other media you upload for those posts — together with the publishing status and metadata of each post.' },
|
|
47
47
|
{ text: '**Published-post analytics:** performance and engagement metrics for posts you have published through the Service to your connected accounts (e.g. views, impressions, likes, comments, shares, reach, and other statistics), as provided by the connected platform\'s API.' },
|
|
48
48
|
{ text: '**Media & content:** files you upload (images, videos, documents) for posts, campaigns, or your profile.' },
|
|
49
|
-
{ text: '**Technical & usage data:** IP address, browser type and version, operating system, pages visited, referral URLs, timestamps, crash reports, and usage events (e.g. feature interactions tracked via
|
|
49
|
+
{ text: '**Technical & usage data:** anonymised IP address, browser type and version, operating system, pages visited, referral URLs, timestamps, error and crash reports, and usage events (e.g. feature interactions such as button clicks and page views, tracked via PostHog — see Sections 6 and 9). Usage tracking starts only after you give consent via the cookie banner.' },
|
|
50
|
+
{ text: '**Session replay data:** with your consent (see Section 9), we record replays of your interactions with the Service (mouse movement, scrolling, clicks, page navigation) to diagnose usability problems and errors. All text you type into input fields is masked before recording and never leaves your browser in readable form. Recordings are retained for 30 days.' },
|
|
50
51
|
{ text: '**Billing data:** billing address and payment reference. Payment card details are handled exclusively by Stripe and are never stored by ad2app.' },
|
|
51
52
|
{ text: '**Waitlist data:** if you submit your email address via our waitlist form before registering, we store that email address to notify you when access is available.' },
|
|
52
53
|
{ text: '**Feedback data:** free-text feedback submitted via the in-app feedback form. This may incidentally contain personal data you choose to include.' },
|
|
@@ -73,7 +74,7 @@ export const PRIVACY_SECTIONS: LegalSection[] = [
|
|
|
73
74
|
['Complying with legal obligations (tax, accounting, record-keeping)', 'Art. 6(1)(c) — legal obligation: Polish Accounting Act (Ustawa o rachunkowości), Tax Ordinance (Ordynacja podatkowa), VAT Act (Ustawa o VAT).'],
|
|
74
75
|
['Waitlist email: notifying you when platform access is available', 'Art. 6(1)(a) — consent (given at the point of waitlist submission; withdrawable at any time).'],
|
|
75
76
|
['Processing in-app feedback', 'Art. 6(1)(f) — legitimate interests: ad2app\'s interest in improving the Service through user feedback.'],
|
|
76
|
-
['Improving and developing the Service (usage analytics
|
|
77
|
+
['Improving and developing the Service (usage analytics, session replay, and error tracking via PostHog)', 'Art. 6(1)(a) — consent, given via the cookie consent banner and withdrawable at any time. No analytics events are captured and no analytics cookies are set before you make a choice. Supplemented by Art. 6(1)(f) — legitimate interests — for aggregate, pseudonymised product statistics.'],
|
|
77
78
|
['Security, fraud prevention, and abuse detection', 'Art. 6(1)(f) — legitimate interests: ad2app\'s interest in maintaining platform integrity and protecting users from harm, which overrides the minimal intrusiveness of security logging.'],
|
|
78
79
|
['Transfer of personal data in a merger, acquisition, or business asset sale', 'Art. 6(1)(f) — legitimate interests: ad2app\'s legitimate interest in completing lawful business restructuring, balanced against data subjects\' interests. Data subjects will be notified before their data is subject to a materially different privacy policy.'],
|
|
79
80
|
['Sending marketing communications', 'Art. 6(1)(a) — consent (withdrawable at any time without affecting prior processing).'],
|
|
@@ -97,6 +98,8 @@ export const PRIVACY_SECTIONS: LegalSection[] = [
|
|
|
97
98
|
{ text: '**Inbox data (DMs and comments):** retained for the duration of your account; deleted with your account data.' },
|
|
98
99
|
{ text: '**Invoices and billing records:** 5 years from the end of the fiscal year (Polish Accounting Act).' },
|
|
99
100
|
{ text: '**Technical logs:** up to 90 days.' },
|
|
101
|
+
{ text: '**Product analytics events (PostHog):** retained in pseudonymised form for the operation of our analytics; deleted within 30 days of an erasure request or consent withdrawal.' },
|
|
102
|
+
{ text: '**Session replay recordings (PostHog):** 30 days, then automatically deleted.' },
|
|
100
103
|
{ text: '**Marketing consent records:** until consent is withdrawn plus 3 years for compliance evidence.' },
|
|
101
104
|
{ text: '**Waitlist emails:** until you register for an account or request deletion, or 24 months from submission if you do not register — whichever comes first.' },
|
|
102
105
|
{ text: '**In-app feedback:** up to 24 months from submission.' },
|
|
@@ -119,7 +122,7 @@ export const PRIVACY_SECTIONS: LegalSection[] = [
|
|
|
119
122
|
sub: [
|
|
120
123
|
{ text: '**Zernio (ARBICHAT, S.L.)** — social media API aggregation: we pass OAuth tokens, post content, media files, and inbox data to Zernio solely to execute publishing and inbox operations on your behalf. Zernio is incorporated in Spain (EEA); however, data is processed on infrastructure with residency in North America (United States). This constitutes an international data transfer covered by Standard Contractual Clauses (Commission Decision 2021/914).' },
|
|
121
124
|
{ text: '**Stripe** — payment processing: billing address, email, and payment reference are shared with Stripe to process subscription payments. Stripe is located in the United States and operates under the EU–US Data Privacy Framework.' },
|
|
122
|
-
{ text: '**
|
|
125
|
+
{ text: '**PostHog (PostHog, Inc.)** — product analytics, session replay, and error tracking: pseudonymised usage event data (feature interactions, page views, device/browser info), masked session recordings, and error reports are processed only after you have given explicit analytics consent via the cookie banner. Our PostHog instance is **PostHog Cloud EU, hosted in Frankfurt, Germany** — analytics data is stored and processed within the EEA. PostHog, Inc. is incorporated in the United States; any residual access from outside the EEA is governed by a Data Processing Agreement incorporating Standard Contractual Clauses (Commission Decision 2021/914).' },
|
|
123
126
|
{ text: '**Vercel Inc.** — backend API hosting and compute: server-side application code, API requests, and associated request logs are processed on Vercel\'s infrastructure. Vercel is located in the United States and transfers are covered by Standard Contractual Clauses (Commission Decision 2021/914).' },
|
|
124
127
|
{ text: '**Neon Inc.** — PostgreSQL database hosting: all structured platform data (accounts, campaigns, collaborations, social account metadata) is stored in a Neon-hosted PostgreSQL database. Neon is located in the United States and transfers are covered by Standard Contractual Clauses (Commission Decision 2021/914).' },
|
|
125
128
|
{ text: '**Google Firebase (Firebase Authentication)** — authentication token verification: authentication tokens issued to users may be verified against Firebase Authentication to validate active sessions. Firebase is a Google service located in the United States and operates under the EU–US Data Privacy Framework.' },
|
|
@@ -136,7 +139,7 @@ export const PRIVACY_SECTIONS: LegalSection[] = [
|
|
|
136
139
|
id: 's7',
|
|
137
140
|
title: '7. International Data Transfers',
|
|
138
141
|
blocks: [
|
|
139
|
-
{ kind: 'p', text: 'Your data is primarily processed within the European Economic Area (EEA). We use certain processors located outside the EEA, including processors based in the United States (currently: Vercel, Neon, Google Firebase, Stripe,
|
|
142
|
+
{ kind: 'p', text: 'Your data is primarily processed within the European Economic Area (EEA). Product analytics data (PostHog) is stored and processed on EU servers in Frankfurt, Germany. We use certain processors located outside the EEA, including processors based in the United States (currently: Vercel, Neon, Google Firebase, Stripe, and Zernio; plus residual support access by PostHog, Inc.). For all such transfers we ensure adequate safeguards through one or more of the following mechanisms:' },
|
|
140
143
|
{
|
|
141
144
|
kind: 'ul',
|
|
142
145
|
items: [
|
|
@@ -180,10 +183,10 @@ export const PRIVACY_SECTIONS: LegalSection[] = [
|
|
|
180
183
|
items: [
|
|
181
184
|
{ text: '**Strictly necessary cookies:** required for authentication sessions and core platform functionality. Cannot be disabled without breaking the Service. Legal basis: Art. 6(1)(b) — contract performance; no consent required. Duration: session cookies expire when you close your browser; authentication cookies expire after 30 days of inactivity.' },
|
|
182
185
|
{ text: '**Functional cookies:** set only in direct response to an action you take (e.g. selecting a language or theme preference), and strictly necessary to deliver that specific function you have requested. They do not track you across sessions beyond preserving your chosen setting. Legal basis: strictly necessary to fulfil your explicit request under Art. 173 of the Polish Telecommunications Act (ePrivacy); no separate consent required. Duration: up to 12 months, or cleared when you clear your browser data.' },
|
|
183
|
-
{ text: '**Analytics cookies (
|
|
186
|
+
{ text: '**Analytics cookies (PostHog):** collect pseudonymised usage event data, enable session replay (with all typed input masked), and capture error reports to help us understand and improve how the Service is used. Legal basis: Art. 6(1)(a) — consent. **No analytics cookies are set and no analytics events are captured before you make a choice** in the cookie consent banner shown on first visit. If you accept, PostHog sets a first-party cookie (name beginning `ph_`) on the `ad2.app` domain, valid for up to 1 year, shared between our website and the app so you are not asked twice. If you decline, no analytics cookie is set and no events are collected. Analytics data is processed on PostHog Cloud EU servers in Frankfurt, Germany (see Section 6).' },
|
|
184
187
|
],
|
|
185
188
|
},
|
|
186
|
-
{ kind: 'p', text: 'You may withdraw or update your cookie consent at any time
|
|
189
|
+
{ kind: 'p', text: 'You may withdraw or update your cookie consent at any time via the "Cookie settings" link in the footer of our website, or on this Privacy Policy page in the app. Withdrawing analytics consent does not affect platform functionality.' },
|
|
187
190
|
{ kind: 'subheading', text: 'Browser local storage' },
|
|
188
191
|
{ kind: 'p', text: 'In addition to cookies, we use browser local storage to preserve application state between sessions. This includes: your language and theme preferences; a cached copy of your subscription tier and status (retained for up to 30 days then invalidated); and draft campaign deadline data. Local storage data is stored on your device only and is not transmitted to our servers independently of your normal usage. It is cleared when you clear your browser data or log out.' },
|
|
189
192
|
],
|
package/src/legal/terms.ts
CHANGED
|
@@ -216,6 +216,7 @@ export const TERMS_SECTIONS: LegalSection[] = [
|
|
|
216
216
|
title: '13. Data Protection',
|
|
217
217
|
blocks: [
|
|
218
218
|
{ kind: 'p', text: 'Our collection and use of personal data is governed by our {PRIVACY}, which forms part of these Terms. By accepting these Terms, you acknowledge that you have read and understood our Privacy Policy.' },
|
|
219
|
+
{ kind: 'p', text: 'The Service uses third-party data processors — including EU-hosted product analytics (PostHog Cloud EU), payment processing (Stripe), and social media API aggregation (Zernio) — as listed and described in the Privacy Policy. Usage analytics and session replay operate only with your consent, which you may give or withdraw at any time via the cookie consent controls described in the Privacy Policy.' },
|
|
219
220
|
{ kind: 'p', text: 'Where you, as an Advertiser, use the Service to access, communicate with, or manage Influencer personal data through the platform, ad2app acts as a **data processor** on your behalf under Article 28 GDPR, and you act as the **data controller** for that processing. Our Data Processing Agreement (available on request at kontakt@ad2.app) governs that relationship. You remain solely responsible for ensuring you have a valid legal basis for any personal data processing you direct through the platform and for your own compliance with GDPR in your capacity as controller.' },
|
|
220
221
|
],
|
|
221
222
|
},
|