@agent-native/core 0.68.3 → 0.70.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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +54 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/changelog/parse.ts +245 -0
- package/corpus/core/src/cli/changelog.ts +199 -0
- package/corpus/core/src/cli/index.ts +19 -0
- package/corpus/core/src/cli/recap.ts +24 -1
- package/corpus/core/src/cli/skills.ts +72 -45
- package/corpus/core/src/client/CommandMenu.tsx +183 -75
- package/corpus/core/src/client/agent-chat-adapter.ts +44 -7
- package/corpus/core/src/client/analytics.ts +181 -0
- package/corpus/core/src/client/changelog/Changelog.tsx +321 -0
- package/corpus/core/src/client/index.ts +11 -0
- package/corpus/core/src/client/mcp-apps/McpAppRenderer.tsx +54 -0
- package/corpus/core/src/client/settings/useBuilderStatus.ts +82 -20
- package/corpus/core/src/extensions/web-search-tool.ts +45 -4
- package/corpus/core/src/mcp/build-server.ts +1 -1
- package/corpus/core/src/mcp/embed-app.ts +112 -6
- package/corpus/core/src/secrets/register-framework-secrets.ts +7 -0
- package/corpus/core/src/server/attribution.ts +211 -0
- package/corpus/core/src/server/auth.ts +6 -0
- package/corpus/core/src/server/better-auth-instance.ts +49 -5
- package/corpus/core/src/server/builder-browser.ts +27 -1
- package/corpus/core/src/server/core-routes-plugin.ts +58 -0
- package/corpus/core/src/server/google-oauth.ts +19 -6
- package/corpus/core/src/shared/mcp-embed-headers.ts +1 -1
- package/corpus/core/src/styles/agent-conversation.css +21 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +111 -0
- package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +46 -14
- package/corpus/templates/analytics/CHANGELOG.md +10 -0
- package/corpus/templates/analytics/actions/install-dashboard-template.ts +2 -2
- package/corpus/templates/analytics/actions/open-traffic-dashboard.ts +1 -1
- package/corpus/templates/analytics/actions/update-dashboard.ts +5 -3
- package/corpus/templates/analytics/app/components/layout/CommandPalette.tsx +180 -144
- package/corpus/templates/analytics/app/components/layout/Header.tsx +2 -2
- package/corpus/templates/analytics/app/components/layout/Layout.tsx +7 -5
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +221 -213
- package/corpus/templates/analytics/app/hooks/use-navigation-state.ts +6 -3
- package/corpus/templates/analytics/app/lib/demo-dashboard-path.ts +1 -1
- package/corpus/templates/analytics/app/lib/last-opened.ts +2 -1
- package/corpus/templates/analytics/app/pages/Settings.tsx +4 -0
- package/corpus/templates/analytics/app/pages/adhoc/explorer-dashboard/index.tsx +3 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/DashboardFilterBar.tsx +2 -2
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +101 -99
- package/corpus/templates/analytics/app/pages/overview/OverviewPage.tsx +1 -1
- package/corpus/templates/analytics/app/routes/adhoc.$id.tsx +18 -5
- package/corpus/templates/analytics/app/routes/catalog.tsx +1 -1
- package/corpus/templates/analytics/app/routes/dashboards.$id.tsx +9 -0
- package/corpus/templates/analytics/app/routes/traffic.tsx +1 -1
- package/corpus/templates/analytics/changelog/2026-06-23-the-dashboards-and-analyses-sidebar-settings-buttons-now-sta.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/agent-native-templates-first-party.json +145 -0
- package/corpus/templates/analytics/server/db/index.ts +1 -1
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +9 -2
- package/corpus/templates/analytics/server/lib/demo-dashboards.ts +1 -1
- package/corpus/templates/analytics/server/plugins/agent-chat.ts +2 -2
- package/corpus/templates/analytics/server/plugins/core-routes.ts +6 -6
- package/corpus/templates/assets/CHANGELOG.md +10 -0
- package/corpus/templates/assets/app/root.tsx +7 -1
- package/corpus/templates/assets/app/routes/settings.tsx +6 -0
- package/corpus/templates/brain/CHANGELOG.md +10 -0
- package/corpus/templates/brain/app/root.tsx +7 -1
- package/corpus/templates/brain/app/routes/settings.tsx +8 -1
- package/corpus/templates/calendar/CHANGELOG.md +10 -0
- package/corpus/templates/calendar/app/pages/Settings.tsx +4 -0
- package/corpus/templates/calendar/app/root.tsx +7 -1
- package/corpus/templates/chat/CHANGELOG.md +10 -0
- package/corpus/templates/chat/app/root.tsx +7 -1
- package/corpus/templates/clips/CHANGELOG.md +10 -0
- package/corpus/templates/clips/app/components/player/share-dialog.tsx +33 -7
- package/corpus/templates/clips/app/components/player/sign-in-prompt-dialog.tsx +8 -0
- package/corpus/templates/clips/app/components/player/video-player.tsx +150 -20
- package/corpus/templates/clips/app/components/recorder/pre-record-panel.tsx +72 -20
- package/corpus/templates/clips/app/components/recorder/recorder-engine.ts +69 -2
- package/corpus/templates/clips/app/components/sharing/slack-share-hint.tsx +71 -0
- package/corpus/templates/clips/app/lib/recorder-preferences.ts +67 -0
- package/corpus/templates/clips/app/root.tsx +7 -1
- package/corpus/templates/clips/app/routes/_app.settings._index.tsx +15 -1
- package/corpus/templates/clips/app/routes/_app.trash.tsx +21 -0
- package/corpus/templates/clips/app/routes/record.tsx +92 -8
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +86 -5
- package/corpus/templates/clips/changelog/2026-06-23-clips-shared-in-slack-no-longer-show-a-could-not-start-playb.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-made-the-play-button-on-shared-and-embedded-clips-scale-with.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-public-clip-share-links-now-show-whether-they-ll-play-inline.md +6 -0
- package/corpus/templates/clips/changelog/2026-06-23-screen-camera-recordings-now-keep-the-presenter-s-face-in-th.md +6 -0
- package/corpus/templates/clips/desktop/src/lib/recorder.ts +20 -0
- package/corpus/templates/clips/desktop/src/main.tsx +3 -0
- package/corpus/templates/clips/desktop/src/overlays/region-record-border.tsx +76 -0
- package/corpus/templates/clips/desktop/src/styles.css +49 -0
- package/corpus/templates/clips/desktop/src-tauri/capabilities/default.json +2 -1
- package/corpus/templates/clips/desktop/src-tauri/src/clips/mod.rs +67 -1
- package/corpus/templates/clips/desktop/src-tauri/src/lib.rs +2 -0
- package/corpus/templates/clips/desktop/src-tauri/src/util.rs +4 -1
- package/corpus/templates/clips/server/routes/api/video/[recordingId].get.ts +51 -1
- package/corpus/templates/clips/shared/share-attribution.ts +79 -0
- package/corpus/templates/content/CHANGELOG.md +10 -0
- package/corpus/templates/content/app/root.tsx +7 -1
- package/corpus/templates/content/vitest.config.ts +3 -0
- package/corpus/templates/design/CHANGELOG.md +10 -0
- package/corpus/templates/design/actions/generate-design.ts +4 -0
- package/corpus/templates/design/app/root.tsx +7 -1
- package/corpus/templates/design/app/routes/settings.tsx +10 -2
- package/corpus/templates/dispatch/CHANGELOG.md +10 -0
- package/corpus/templates/dispatch/app/root.tsx +7 -1
- package/corpus/templates/forms/CHANGELOG.md +10 -0
- package/corpus/templates/forms/app/root.tsx +7 -1
- package/corpus/templates/macros/CHANGELOG.md +10 -0
- package/corpus/templates/macros/app/root.tsx +7 -1
- package/corpus/templates/mail/CHANGELOG.md +10 -0
- package/corpus/templates/mail/app/components/layout/CommandPalette.tsx +3 -0
- package/corpus/templates/mail/app/pages/SettingsPage.tsx +27 -0
- package/corpus/templates/plan/.agents/skills/visual-plan/SKILL.md +31 -17
- package/corpus/templates/plan/.agents/skills/visual-plan/references/wireframe.md +7 -4
- package/corpus/templates/plan/.agents/skills/visual-recap/SKILL.md +34 -24
- package/corpus/templates/plan/.agents/skills/visual-recap/references/wireframe.md +7 -4
- package/corpus/templates/plan/AGENTS.md +4 -0
- package/corpus/templates/plan/CHANGELOG.md +10 -0
- package/corpus/templates/plan/actions/view-screen.ts +5 -0
- package/corpus/templates/plan/app/components/layout/Sidebar.tsx +98 -1
- package/corpus/templates/plan/app/pages/PlansPage.tsx +107 -7
- package/corpus/templates/plan/app/root.tsx +7 -1
- package/corpus/templates/plan/shared/share-attribution.ts +72 -0
- package/corpus/templates/slides/CHANGELOG.md +10 -0
- package/corpus/templates/slides/app/root.tsx +7 -1
- package/corpus/templates/videos/CHANGELOG.md +4 -151
- package/corpus/templates/videos/app/root.tsx +7 -1
- package/dist/changelog/parse.d.ts +70 -0
- package/dist/changelog/parse.d.ts.map +1 -0
- package/dist/changelog/parse.js +200 -0
- package/dist/changelog/parse.js.map +1 -0
- package/dist/cli/changelog.d.ts +4 -0
- package/dist/cli/changelog.d.ts.map +1 -0
- package/dist/cli/changelog.js +166 -0
- package/dist/cli/changelog.js.map +1 -0
- package/dist/cli/index.js +18 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/recap.d.ts.map +1 -1
- package/dist/cli/recap.js +22 -1
- package/dist/cli/recap.js.map +1 -1
- package/dist/cli/skills.d.ts +3 -3
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +72 -45
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/CommandMenu.d.ts +17 -1
- package/dist/client/CommandMenu.d.ts.map +1 -1
- package/dist/client/CommandMenu.js +49 -13
- package/dist/client/CommandMenu.js.map +1 -1
- package/dist/client/agent-chat-adapter.d.ts.map +1 -1
- package/dist/client/agent-chat-adapter.js +43 -6
- package/dist/client/agent-chat-adapter.js.map +1 -1
- package/dist/client/analytics.d.ts +18 -0
- package/dist/client/analytics.d.ts.map +1 -1
- package/dist/client/analytics.js +177 -0
- package/dist/client/analytics.js.map +1 -1
- package/dist/client/changelog/Changelog.d.ts +47 -0
- package/dist/client/changelog/Changelog.d.ts.map +1 -0
- package/dist/client/changelog/Changelog.js +138 -0
- package/dist/client/changelog/Changelog.js.map +1 -0
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.d.ts +1 -0
- package/dist/client/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.js +42 -1
- package/dist/client/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/client/settings/useBuilderStatus.d.ts +2 -0
- package/dist/client/settings/useBuilderStatus.d.ts.map +1 -1
- package/dist/client/settings/useBuilderStatus.js +58 -10
- package/dist/client/settings/useBuilderStatus.js.map +1 -1
- package/dist/extensions/web-search-tool.d.ts +5 -3
- package/dist/extensions/web-search-tool.d.ts.map +1 -1
- package/dist/extensions/web-search-tool.js +36 -4
- package/dist/extensions/web-search-tool.js.map +1 -1
- package/dist/mcp/build-server.js +1 -1
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/embed-app.d.ts.map +1 -1
- package/dist/mcp/embed-app.js +112 -6
- package/dist/mcp/embed-app.js.map +1 -1
- package/dist/secrets/register-framework-secrets.d.ts.map +1 -1
- package/dist/secrets/register-framework-secrets.js +6 -0
- package/dist/secrets/register-framework-secrets.js.map +1 -1
- package/dist/server/attribution.d.ts +83 -0
- package/dist/server/attribution.d.ts.map +1 -0
- package/dist/server/attribution.js +178 -0
- package/dist/server/attribution.js.map +1 -0
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +7 -0
- package/dist/server/auth.js.map +1 -1
- package/dist/server/better-auth-instance.d.ts +9 -1
- package/dist/server/better-auth-instance.d.ts.map +1 -1
- package/dist/server/better-auth-instance.js +32 -2
- package/dist/server/better-auth-instance.js.map +1 -1
- package/dist/server/builder-browser.d.ts +4 -0
- package/dist/server/builder-browser.d.ts.map +1 -1
- package/dist/server/builder-browser.js +20 -1
- package/dist/server/builder-browser.js.map +1 -1
- package/dist/server/core-routes-plugin.d.ts +25 -0
- package/dist/server/core-routes-plugin.d.ts.map +1 -1
- package/dist/server/core-routes-plugin.js +38 -0
- package/dist/server/core-routes-plugin.js.map +1 -1
- package/dist/server/google-oauth.d.ts.map +1 -1
- package/dist/server/google-oauth.js +16 -2
- package/dist/server/google-oauth.js.map +1 -1
- package/dist/shared/mcp-embed-headers.js +1 -1
- package/dist/shared/mcp-embed-headers.js.map +1 -1
- package/dist/styles/agent-conversation.css +21 -0
- package/dist/templates/workspace-core/.agents/skills/changelog/SKILL.md +111 -0
- package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +46 -14
- package/package.json +1 -1
- package/src/templates/workspace-core/.agents/skills/changelog/SKILL.md +111 -0
- package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +46 -14
- /package/corpus/templates/analytics/app/routes/{dashboards.tsx → dashboards._index.tsx} +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* First-touch referral attribution — server side.
|
|
3
|
+
*
|
|
4
|
+
* The browser captures an anonymous visitor's *first* landing context (referral
|
|
5
|
+
* source, UTM params, referring host, landing path) and persists it across the
|
|
6
|
+
* signup boundary in a first-party cookie named `an_ft` (see
|
|
7
|
+
* `client/analytics.ts`). On signup, the auth hook reads that cookie off the
|
|
8
|
+
* request and enriches the canonical server-side `signup` event so we can
|
|
9
|
+
* measure where new users came from and how apps spread (virality).
|
|
10
|
+
*
|
|
11
|
+
* This module is intentionally pure and dependency-free so it is trivially
|
|
12
|
+
* unit-testable and can never throw into the signup path. The single hard rule:
|
|
13
|
+
* parsing untrusted cookie input must NEVER throw — every accessor is defensive.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* The decoded first-touch attribution object. Mirrors the compact JSON the
|
|
17
|
+
* client writes into the `an_ft` cookie / `an_attribution` localStorage key.
|
|
18
|
+
* Every field is optional — the client omits empty fields to keep the cookie
|
|
19
|
+
* small, and a malformed/absent cookie yields `null`.
|
|
20
|
+
*/
|
|
21
|
+
export interface FirstTouchAttribution {
|
|
22
|
+
/** Referral source bucket, e.g. "clip_share", "plan_share". */
|
|
23
|
+
ref?: string;
|
|
24
|
+
/** Referrer's stable user id (the clip/plan owner who shared the link). */
|
|
25
|
+
via?: string;
|
|
26
|
+
utm_source?: string;
|
|
27
|
+
utm_medium?: string;
|
|
28
|
+
utm_campaign?: string;
|
|
29
|
+
utm_content?: string;
|
|
30
|
+
utm_term?: string;
|
|
31
|
+
/** `window.location.pathname` of the first page the visitor landed on. */
|
|
32
|
+
landing_path?: string;
|
|
33
|
+
/** Host of `document.referrer` (scrubbed; host only, never a full URL). */
|
|
34
|
+
landing_referrer?: string;
|
|
35
|
+
/** ISO timestamp of when the visitor first landed. */
|
|
36
|
+
landed_at?: string;
|
|
37
|
+
}
|
|
38
|
+
/** Cookie name written by the client (non-HttpOnly; non-sensitive). */
|
|
39
|
+
export declare const FIRST_TOUCH_COOKIE_NAME = "an_ft";
|
|
40
|
+
/**
|
|
41
|
+
* Parse a raw `Cookie:` header into a flat name→value map. Tolerates missing
|
|
42
|
+
* input, extra whitespace, `=` inside values, and malformed pairs. Never throws.
|
|
43
|
+
*/
|
|
44
|
+
export declare function parseCookieHeader(cookieHeader: string | null | undefined): Record<string, string>;
|
|
45
|
+
/**
|
|
46
|
+
* Decode a single cookie value into a `FirstTouchAttribution`. The value is the
|
|
47
|
+
* URL-encoded compact JSON written by the client. Returns `null` for empty,
|
|
48
|
+
* malformed, or non-object input. Only known string fields are copied through,
|
|
49
|
+
* each clamped to a sane max length as a defense against an oversized cookie.
|
|
50
|
+
*/
|
|
51
|
+
export declare function decodeFirstTouchValue(value: string | null | undefined): FirstTouchAttribution | null;
|
|
52
|
+
/**
|
|
53
|
+
* Read the `an_ft` first-touch attribution out of a raw `Cookie:` header.
|
|
54
|
+
* Returns `null` when the cookie is absent or unparseable. Never throws.
|
|
55
|
+
*/
|
|
56
|
+
export declare function readFirstTouchAttribution(cookieHeader: string | null | undefined): FirstTouchAttribution | null;
|
|
57
|
+
/**
|
|
58
|
+
* Derive the `referral_source` bucket from first-touch attribution per the
|
|
59
|
+
* contract:
|
|
60
|
+
* 1. explicit `ref` wins;
|
|
61
|
+
* 2. landing path under `/share/` => "clip_share";
|
|
62
|
+
* 3. landing path that looks like a public plan page
|
|
63
|
+
* (contains `/p/`, `/plan/`, or `/share-plan/`) => "plan_share";
|
|
64
|
+
* 4. a non-empty external referring host => "external";
|
|
65
|
+
* 5. otherwise => "direct".
|
|
66
|
+
*/
|
|
67
|
+
export declare function deriveReferralSource(ft: FirstTouchAttribution | null): string;
|
|
68
|
+
/**
|
|
69
|
+
* Compute the snake_case signup-event properties from first-touch attribution.
|
|
70
|
+
* Returns a clean object with `undefined` values omitted, ready to merge into
|
|
71
|
+
* the `signup` track call. Always sets `referral_source` (defaults to "direct").
|
|
72
|
+
*
|
|
73
|
+
* Pure and total — given any (or no) input it returns a well-formed object and
|
|
74
|
+
* never throws.
|
|
75
|
+
*/
|
|
76
|
+
export declare function deriveSignupAttribution(ft: FirstTouchAttribution | null): Record<string, string>;
|
|
77
|
+
/**
|
|
78
|
+
* Convenience: read the cookie header and derive signup attribution in one
|
|
79
|
+
* call. Never throws; falls back to `{ referral_source: "direct" }` on any
|
|
80
|
+
* error.
|
|
81
|
+
*/
|
|
82
|
+
export declare function signupAttributionFromCookieHeader(cookieHeader: string | null | undefined): Record<string, string>;
|
|
83
|
+
//# sourceMappingURL=attribution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribution.d.ts","sourceRoot":"","sources":["../../src/server/attribution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,+DAA+D;IAC/D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,2EAA2E;IAC3E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,uEAAuE;AACvE,eAAO,MAAM,uBAAuB,UAAU,CAAC;AAe/C;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACtC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAcxB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC/B,qBAAqB,GAAG,IAAI,CA8B9B;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACtC,qBAAqB,GAAG,IAAI,CAO9B;AAOD;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,qBAAqB,GAAG,IAAI,GAAG,MAAM,CAa7E;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,EAAE,EAAE,qBAAqB,GAAG,IAAI,GAC/B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAuBxB;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,CAC/C,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACtC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAMxB"}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* First-touch referral attribution — server side.
|
|
3
|
+
*
|
|
4
|
+
* The browser captures an anonymous visitor's *first* landing context (referral
|
|
5
|
+
* source, UTM params, referring host, landing path) and persists it across the
|
|
6
|
+
* signup boundary in a first-party cookie named `an_ft` (see
|
|
7
|
+
* `client/analytics.ts`). On signup, the auth hook reads that cookie off the
|
|
8
|
+
* request and enriches the canonical server-side `signup` event so we can
|
|
9
|
+
* measure where new users came from and how apps spread (virality).
|
|
10
|
+
*
|
|
11
|
+
* This module is intentionally pure and dependency-free so it is trivially
|
|
12
|
+
* unit-testable and can never throw into the signup path. The single hard rule:
|
|
13
|
+
* parsing untrusted cookie input must NEVER throw — every accessor is defensive.
|
|
14
|
+
*/
|
|
15
|
+
/** Cookie name written by the client (non-HttpOnly; non-sensitive). */
|
|
16
|
+
export const FIRST_TOUCH_COOKIE_NAME = "an_ft";
|
|
17
|
+
const STRING_FIELDS = [
|
|
18
|
+
"ref",
|
|
19
|
+
"via",
|
|
20
|
+
"utm_source",
|
|
21
|
+
"utm_medium",
|
|
22
|
+
"utm_campaign",
|
|
23
|
+
"utm_content",
|
|
24
|
+
"utm_term",
|
|
25
|
+
"landing_path",
|
|
26
|
+
"landing_referrer",
|
|
27
|
+
"landed_at",
|
|
28
|
+
];
|
|
29
|
+
/**
|
|
30
|
+
* Parse a raw `Cookie:` header into a flat name→value map. Tolerates missing
|
|
31
|
+
* input, extra whitespace, `=` inside values, and malformed pairs. Never throws.
|
|
32
|
+
*/
|
|
33
|
+
export function parseCookieHeader(cookieHeader) {
|
|
34
|
+
const out = {};
|
|
35
|
+
if (!cookieHeader || typeof cookieHeader !== "string")
|
|
36
|
+
return out;
|
|
37
|
+
for (const part of cookieHeader.split(";")) {
|
|
38
|
+
const eq = part.indexOf("=");
|
|
39
|
+
if (eq === -1)
|
|
40
|
+
continue;
|
|
41
|
+
const name = part.slice(0, eq).trim();
|
|
42
|
+
if (!name)
|
|
43
|
+
continue;
|
|
44
|
+
const rawValue = part.slice(eq + 1).trim();
|
|
45
|
+
// First write wins so a duplicate cookie name can't clobber the first.
|
|
46
|
+
if (name in out)
|
|
47
|
+
continue;
|
|
48
|
+
out[name] = rawValue;
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Decode a single cookie value into a `FirstTouchAttribution`. The value is the
|
|
54
|
+
* URL-encoded compact JSON written by the client. Returns `null` for empty,
|
|
55
|
+
* malformed, or non-object input. Only known string fields are copied through,
|
|
56
|
+
* each clamped to a sane max length as a defense against an oversized cookie.
|
|
57
|
+
*/
|
|
58
|
+
export function decodeFirstTouchValue(value) {
|
|
59
|
+
if (!value || typeof value !== "string")
|
|
60
|
+
return null;
|
|
61
|
+
let decoded = value;
|
|
62
|
+
try {
|
|
63
|
+
decoded = decodeURIComponent(value);
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Not valid percent-encoding — fall back to the raw value and let the JSON
|
|
67
|
+
// parse below decide whether it's usable.
|
|
68
|
+
decoded = value;
|
|
69
|
+
}
|
|
70
|
+
let parsed;
|
|
71
|
+
try {
|
|
72
|
+
parsed = JSON.parse(decoded);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
const source = parsed;
|
|
81
|
+
const result = {};
|
|
82
|
+
let any = false;
|
|
83
|
+
for (const field of STRING_FIELDS) {
|
|
84
|
+
const raw = source[field];
|
|
85
|
+
if (typeof raw === "string" && raw.length > 0) {
|
|
86
|
+
result[field] = raw.slice(0, 120);
|
|
87
|
+
any = true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return any ? result : null;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Read the `an_ft` first-touch attribution out of a raw `Cookie:` header.
|
|
94
|
+
* Returns `null` when the cookie is absent or unparseable. Never throws.
|
|
95
|
+
*/
|
|
96
|
+
export function readFirstTouchAttribution(cookieHeader) {
|
|
97
|
+
try {
|
|
98
|
+
const cookies = parseCookieHeader(cookieHeader);
|
|
99
|
+
return decodeFirstTouchValue(cookies[FIRST_TOUCH_COOKIE_NAME]);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
function isExternalReferrerHost(host) {
|
|
106
|
+
const trimmed = host?.trim();
|
|
107
|
+
return !!trimmed && trimmed.length > 0;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Derive the `referral_source` bucket from first-touch attribution per the
|
|
111
|
+
* contract:
|
|
112
|
+
* 1. explicit `ref` wins;
|
|
113
|
+
* 2. landing path under `/share/` => "clip_share";
|
|
114
|
+
* 3. landing path that looks like a public plan page
|
|
115
|
+
* (contains `/p/`, `/plan/`, or `/share-plan/`) => "plan_share";
|
|
116
|
+
* 4. a non-empty external referring host => "external";
|
|
117
|
+
* 5. otherwise => "direct".
|
|
118
|
+
*/
|
|
119
|
+
export function deriveReferralSource(ft) {
|
|
120
|
+
if (ft?.ref && ft.ref.trim())
|
|
121
|
+
return ft.ref.trim();
|
|
122
|
+
const path = ft?.landing_path ?? "";
|
|
123
|
+
if (path.startsWith("/share/"))
|
|
124
|
+
return "clip_share";
|
|
125
|
+
if (path.includes("/p/") ||
|
|
126
|
+
path.includes("/plan/") ||
|
|
127
|
+
path.includes("/share-plan/")) {
|
|
128
|
+
return "plan_share";
|
|
129
|
+
}
|
|
130
|
+
if (isExternalReferrerHost(ft?.landing_referrer))
|
|
131
|
+
return "external";
|
|
132
|
+
return "direct";
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Compute the snake_case signup-event properties from first-touch attribution.
|
|
136
|
+
* Returns a clean object with `undefined` values omitted, ready to merge into
|
|
137
|
+
* the `signup` track call. Always sets `referral_source` (defaults to "direct").
|
|
138
|
+
*
|
|
139
|
+
* Pure and total — given any (or no) input it returns a well-formed object and
|
|
140
|
+
* never throws.
|
|
141
|
+
*/
|
|
142
|
+
export function deriveSignupAttribution(ft) {
|
|
143
|
+
const out = {
|
|
144
|
+
referral_source: deriveReferralSource(ft),
|
|
145
|
+
};
|
|
146
|
+
if (!ft)
|
|
147
|
+
return out;
|
|
148
|
+
const setIf = (key, value) => {
|
|
149
|
+
const trimmed = value?.trim();
|
|
150
|
+
if (trimmed)
|
|
151
|
+
out[key] = trimmed;
|
|
152
|
+
};
|
|
153
|
+
setIf("referrer_user", ft.via);
|
|
154
|
+
setIf("referral_medium", ft.utm_medium);
|
|
155
|
+
setIf("referral_campaign", ft.utm_campaign);
|
|
156
|
+
setIf("utm_source", ft.utm_source);
|
|
157
|
+
setIf("utm_medium", ft.utm_medium);
|
|
158
|
+
setIf("utm_campaign", ft.utm_campaign);
|
|
159
|
+
setIf("utm_content", ft.utm_content);
|
|
160
|
+
setIf("utm_term", ft.utm_term);
|
|
161
|
+
setIf("first_touch_path", ft.landing_path);
|
|
162
|
+
setIf("landing_referrer", ft.landing_referrer);
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Convenience: read the cookie header and derive signup attribution in one
|
|
167
|
+
* call. Never throws; falls back to `{ referral_source: "direct" }` on any
|
|
168
|
+
* error.
|
|
169
|
+
*/
|
|
170
|
+
export function signupAttributionFromCookieHeader(cookieHeader) {
|
|
171
|
+
try {
|
|
172
|
+
return deriveSignupAttribution(readFirstTouchAttribution(cookieHeader));
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return { referral_source: "direct" };
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=attribution.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attribution.js","sourceRoot":"","sources":["../../src/server/attribution.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AA0BH,uEAAuE;AACvE,MAAM,CAAC,MAAM,uBAAuB,GAAG,OAAO,CAAC;AAE/C,MAAM,aAAa,GAAuC;IACxD,KAAK;IACL,KAAK;IACL,YAAY;IACZ,YAAY;IACZ,cAAc;IACd,aAAa;IACb,UAAU;IACV,cAAc;IACd,kBAAkB;IAClB,WAAW;CACZ,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,YAAuC;IAEvC,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,IAAI,CAAC,YAAY,IAAI,OAAO,YAAY,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IAClE,KAAK,MAAM,IAAI,IAAI,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3C,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,IAAI,EAAE,KAAK,CAAC,CAAC;YAAE,SAAS;QACxB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACtC,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3C,uEAAuE;QACvE,IAAI,IAAI,IAAI,GAAG;YAAE,SAAS;QAC1B,GAAG,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC;IACvB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAgC;IAEhC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACrD,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,CAAC;QACH,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,2EAA2E;QAC3E,0CAA0C;QAC1C,OAAO,GAAG,KAAK,CAAC;IAClB,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACnE,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,MAAM,GAAG,MAAiC,CAAC;IACjD,MAAM,MAAM,GAA0B,EAAE,CAAC;IACzC,IAAI,GAAG,GAAG,KAAK,CAAC;IAChB,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9C,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YAClC,GAAG,GAAG,IAAI,CAAC;QACb,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,yBAAyB,CACvC,YAAuC;IAEvC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,iBAAiB,CAAC,YAAY,CAAC,CAAC;QAChD,OAAO,qBAAqB,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,sBAAsB,CAAC,IAAwB;IACtD,MAAM,OAAO,GAAG,IAAI,EAAE,IAAI,EAAE,CAAC;IAC7B,OAAO,CAAC,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,oBAAoB,CAAC,EAAgC;IACnE,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE;QAAE,OAAO,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;IACnD,MAAM,IAAI,GAAG,EAAE,EAAE,YAAY,IAAI,EAAE,CAAC;IACpC,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,OAAO,YAAY,CAAC;IACpD,IACE,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QACpB,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC;QACvB,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC,EAC7B,CAAC;QACD,OAAO,YAAY,CAAC;IACtB,CAAC;IACD,IAAI,sBAAsB,CAAC,EAAE,EAAE,gBAAgB,CAAC;QAAE,OAAO,UAAU,CAAC;IACpE,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CACrC,EAAgC;IAEhC,MAAM,GAAG,GAA2B;QAClC,eAAe,EAAE,oBAAoB,CAAC,EAAE,CAAC;KAC1C,CAAC;IACF,IAAI,CAAC,EAAE;QAAE,OAAO,GAAG,CAAC;IAEpB,MAAM,KAAK,GAAG,CAAC,GAAW,EAAE,KAAyB,EAAE,EAAE;QACvD,MAAM,OAAO,GAAG,KAAK,EAAE,IAAI,EAAE,CAAC;QAC9B,IAAI,OAAO;YAAE,GAAG,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC;IAClC,CAAC,CAAC;IAEF,KAAK,CAAC,eAAe,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;IAC/B,KAAK,CAAC,iBAAiB,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC;IACxC,KAAK,CAAC,mBAAmB,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC;IAC5C,KAAK,CAAC,YAAY,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC;IACnC,KAAK,CAAC,YAAY,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC;IACnC,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC;IACvC,KAAK,CAAC,aAAa,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC;IACrC,KAAK,CAAC,UAAU,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC;IAC/B,KAAK,CAAC,kBAAkB,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC;IAC3C,KAAK,CAAC,kBAAkB,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC;IAE/C,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iCAAiC,CAC/C,YAAuC;IAEvC,IAAI,CAAC;QACH,OAAO,uBAAuB,CAAC,yBAAyB,CAAC,YAAY,CAAC,CAAC,CAAC;IAC1E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,CAAC;IACvC,CAAC;AACH,CAAC","sourcesContent":["/**\n * First-touch referral attribution — server side.\n *\n * The browser captures an anonymous visitor's *first* landing context (referral\n * source, UTM params, referring host, landing path) and persists it across the\n * signup boundary in a first-party cookie named `an_ft` (see\n * `client/analytics.ts`). On signup, the auth hook reads that cookie off the\n * request and enriches the canonical server-side `signup` event so we can\n * measure where new users came from and how apps spread (virality).\n *\n * This module is intentionally pure and dependency-free so it is trivially\n * unit-testable and can never throw into the signup path. The single hard rule:\n * parsing untrusted cookie input must NEVER throw — every accessor is defensive.\n */\n\n/**\n * The decoded first-touch attribution object. Mirrors the compact JSON the\n * client writes into the `an_ft` cookie / `an_attribution` localStorage key.\n * Every field is optional — the client omits empty fields to keep the cookie\n * small, and a malformed/absent cookie yields `null`.\n */\nexport interface FirstTouchAttribution {\n /** Referral source bucket, e.g. \"clip_share\", \"plan_share\". */\n ref?: string;\n /** Referrer's stable user id (the clip/plan owner who shared the link). */\n via?: string;\n utm_source?: string;\n utm_medium?: string;\n utm_campaign?: string;\n utm_content?: string;\n utm_term?: string;\n /** `window.location.pathname` of the first page the visitor landed on. */\n landing_path?: string;\n /** Host of `document.referrer` (scrubbed; host only, never a full URL). */\n landing_referrer?: string;\n /** ISO timestamp of when the visitor first landed. */\n landed_at?: string;\n}\n\n/** Cookie name written by the client (non-HttpOnly; non-sensitive). */\nexport const FIRST_TOUCH_COOKIE_NAME = \"an_ft\";\n\nconst STRING_FIELDS: Array<keyof FirstTouchAttribution> = [\n \"ref\",\n \"via\",\n \"utm_source\",\n \"utm_medium\",\n \"utm_campaign\",\n \"utm_content\",\n \"utm_term\",\n \"landing_path\",\n \"landing_referrer\",\n \"landed_at\",\n];\n\n/**\n * Parse a raw `Cookie:` header into a flat name→value map. Tolerates missing\n * input, extra whitespace, `=` inside values, and malformed pairs. Never throws.\n */\nexport function parseCookieHeader(\n cookieHeader: string | null | undefined,\n): Record<string, string> {\n const out: Record<string, string> = {};\n if (!cookieHeader || typeof cookieHeader !== \"string\") return out;\n for (const part of cookieHeader.split(\";\")) {\n const eq = part.indexOf(\"=\");\n if (eq === -1) continue;\n const name = part.slice(0, eq).trim();\n if (!name) continue;\n const rawValue = part.slice(eq + 1).trim();\n // First write wins so a duplicate cookie name can't clobber the first.\n if (name in out) continue;\n out[name] = rawValue;\n }\n return out;\n}\n\n/**\n * Decode a single cookie value into a `FirstTouchAttribution`. The value is the\n * URL-encoded compact JSON written by the client. Returns `null` for empty,\n * malformed, or non-object input. Only known string fields are copied through,\n * each clamped to a sane max length as a defense against an oversized cookie.\n */\nexport function decodeFirstTouchValue(\n value: string | null | undefined,\n): FirstTouchAttribution | null {\n if (!value || typeof value !== \"string\") return null;\n let decoded = value;\n try {\n decoded = decodeURIComponent(value);\n } catch {\n // Not valid percent-encoding — fall back to the raw value and let the JSON\n // parse below decide whether it's usable.\n decoded = value;\n }\n let parsed: unknown;\n try {\n parsed = JSON.parse(decoded);\n } catch {\n return null;\n }\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return null;\n }\n const source = parsed as Record<string, unknown>;\n const result: FirstTouchAttribution = {};\n let any = false;\n for (const field of STRING_FIELDS) {\n const raw = source[field];\n if (typeof raw === \"string\" && raw.length > 0) {\n result[field] = raw.slice(0, 120);\n any = true;\n }\n }\n return any ? result : null;\n}\n\n/**\n * Read the `an_ft` first-touch attribution out of a raw `Cookie:` header.\n * Returns `null` when the cookie is absent or unparseable. Never throws.\n */\nexport function readFirstTouchAttribution(\n cookieHeader: string | null | undefined,\n): FirstTouchAttribution | null {\n try {\n const cookies = parseCookieHeader(cookieHeader);\n return decodeFirstTouchValue(cookies[FIRST_TOUCH_COOKIE_NAME]);\n } catch {\n return null;\n }\n}\n\nfunction isExternalReferrerHost(host: string | undefined): boolean {\n const trimmed = host?.trim();\n return !!trimmed && trimmed.length > 0;\n}\n\n/**\n * Derive the `referral_source` bucket from first-touch attribution per the\n * contract:\n * 1. explicit `ref` wins;\n * 2. landing path under `/share/` => \"clip_share\";\n * 3. landing path that looks like a public plan page\n * (contains `/p/`, `/plan/`, or `/share-plan/`) => \"plan_share\";\n * 4. a non-empty external referring host => \"external\";\n * 5. otherwise => \"direct\".\n */\nexport function deriveReferralSource(ft: FirstTouchAttribution | null): string {\n if (ft?.ref && ft.ref.trim()) return ft.ref.trim();\n const path = ft?.landing_path ?? \"\";\n if (path.startsWith(\"/share/\")) return \"clip_share\";\n if (\n path.includes(\"/p/\") ||\n path.includes(\"/plan/\") ||\n path.includes(\"/share-plan/\")\n ) {\n return \"plan_share\";\n }\n if (isExternalReferrerHost(ft?.landing_referrer)) return \"external\";\n return \"direct\";\n}\n\n/**\n * Compute the snake_case signup-event properties from first-touch attribution.\n * Returns a clean object with `undefined` values omitted, ready to merge into\n * the `signup` track call. Always sets `referral_source` (defaults to \"direct\").\n *\n * Pure and total — given any (or no) input it returns a well-formed object and\n * never throws.\n */\nexport function deriveSignupAttribution(\n ft: FirstTouchAttribution | null,\n): Record<string, string> {\n const out: Record<string, string> = {\n referral_source: deriveReferralSource(ft),\n };\n if (!ft) return out;\n\n const setIf = (key: string, value: string | undefined) => {\n const trimmed = value?.trim();\n if (trimmed) out[key] = trimmed;\n };\n\n setIf(\"referrer_user\", ft.via);\n setIf(\"referral_medium\", ft.utm_medium);\n setIf(\"referral_campaign\", ft.utm_campaign);\n setIf(\"utm_source\", ft.utm_source);\n setIf(\"utm_medium\", ft.utm_medium);\n setIf(\"utm_campaign\", ft.utm_campaign);\n setIf(\"utm_content\", ft.utm_content);\n setIf(\"utm_term\", ft.utm_term);\n setIf(\"first_touch_path\", ft.landing_path);\n setIf(\"landing_referrer\", ft.landing_referrer);\n\n return out;\n}\n\n/**\n * Convenience: read the cookie header and derive signup attribution in one\n * call. Never throws; falls back to `{ referral_source: \"direct\" }` on any\n * error.\n */\nexport function signupAttributionFromCookieHeader(\n cookieHeader: string | null | undefined,\n): Record<string, string> {\n try {\n return deriveSignupAttribution(readFirstTouchAttribution(cookieHeader));\n } catch {\n return { referral_source: \"direct\" };\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"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"}
|
package/dist/server/auth.js
CHANGED
|
@@ -1312,6 +1312,13 @@ function createAuthGuardFn() {
|
|
|
1312
1312
|
return;
|
|
1313
1313
|
if (p === "/_agent-native/speculation-rules.json")
|
|
1314
1314
|
return;
|
|
1315
|
+
// Liveness probe: always public so uptime monitors and the keep-warm cron
|
|
1316
|
+
// can reach the DB-warmup route without a session. It exposes no per-user
|
|
1317
|
+
// data (just ok/db/ms) and runs a trivial `SELECT 1`. Without this bypass
|
|
1318
|
+
// the gate below 401s anonymous /_agent-native/* requests before any DB
|
|
1319
|
+
// query, so the database would never get warmed.
|
|
1320
|
+
if (p === "/_agent-native/health")
|
|
1321
|
+
return;
|
|
1315
1322
|
if (isPublicPath(normalizedUrl, publicPaths))
|
|
1316
1323
|
return;
|
|
1317
1324
|
if (shouldBypassAuthForBuilderConnect(event, p))
|