@codaijs/keel 0.1.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.
Files changed (116) hide show
  1. package/dist/__tests__/cli.test.d.ts +2 -0
  2. package/dist/__tests__/cli.test.d.ts.map +1 -0
  3. package/dist/__tests__/cli.test.js +173 -0
  4. package/dist/__tests__/cli.test.js.map +1 -0
  5. package/dist/__tests__/registry.test.d.ts +2 -0
  6. package/dist/__tests__/registry.test.d.ts.map +1 -0
  7. package/dist/__tests__/registry.test.js +86 -0
  8. package/dist/__tests__/registry.test.js.map +1 -0
  9. package/dist/__tests__/sail-installer.test.d.ts +2 -0
  10. package/dist/__tests__/sail-installer.test.d.ts.map +1 -0
  11. package/dist/__tests__/sail-installer.test.js +158 -0
  12. package/dist/__tests__/sail-installer.test.js.map +1 -0
  13. package/dist/create-runner.d.ts +11 -0
  14. package/dist/create-runner.d.ts.map +1 -0
  15. package/dist/create-runner.js +63 -0
  16. package/dist/create-runner.js.map +1 -0
  17. package/dist/create.d.ts +10 -0
  18. package/dist/create.d.ts.map +1 -0
  19. package/dist/create.js +15 -0
  20. package/dist/create.js.map +1 -0
  21. package/dist/manage.d.ts +24 -0
  22. package/dist/manage.d.ts.map +1 -0
  23. package/dist/manage.js +1461 -0
  24. package/dist/manage.js.map +1 -0
  25. package/dist/prompts.d.ts +36 -0
  26. package/dist/prompts.d.ts.map +1 -0
  27. package/dist/prompts.js +208 -0
  28. package/dist/prompts.js.map +1 -0
  29. package/dist/sail-installer.d.ts +37 -0
  30. package/dist/sail-installer.d.ts.map +1 -0
  31. package/dist/sail-installer.js +935 -0
  32. package/dist/sail-installer.js.map +1 -0
  33. package/dist/scaffold.d.ts +10 -0
  34. package/dist/scaffold.d.ts.map +1 -0
  35. package/dist/scaffold.js +297 -0
  36. package/dist/scaffold.js.map +1 -0
  37. package/package.json +57 -0
  38. package/sails/_template/addon.json +20 -0
  39. package/sails/_template/install.ts +402 -0
  40. package/sails/admin-dashboard/README.md +117 -0
  41. package/sails/admin-dashboard/addon.json +28 -0
  42. package/sails/admin-dashboard/files/backend/middleware/admin.ts +34 -0
  43. package/sails/admin-dashboard/files/backend/routes/admin.ts +243 -0
  44. package/sails/admin-dashboard/files/frontend/components/admin/StatsCard.tsx +40 -0
  45. package/sails/admin-dashboard/files/frontend/components/admin/UsersTable.tsx +240 -0
  46. package/sails/admin-dashboard/files/frontend/hooks/useAdmin.ts +149 -0
  47. package/sails/admin-dashboard/files/frontend/pages/admin/Dashboard.tsx +173 -0
  48. package/sails/admin-dashboard/files/frontend/pages/admin/UserDetail.tsx +203 -0
  49. package/sails/admin-dashboard/install.ts +305 -0
  50. package/sails/analytics/README.md +178 -0
  51. package/sails/analytics/addon.json +27 -0
  52. package/sails/analytics/files/frontend/components/AnalyticsProvider.tsx +58 -0
  53. package/sails/analytics/files/frontend/hooks/useAnalytics.ts +64 -0
  54. package/sails/analytics/files/frontend/lib/analytics.ts +103 -0
  55. package/sails/analytics/install.ts +297 -0
  56. package/sails/file-uploads/README.md +191 -0
  57. package/sails/file-uploads/addon.json +30 -0
  58. package/sails/file-uploads/files/backend/routes/files.ts +198 -0
  59. package/sails/file-uploads/files/backend/schema/files.ts +36 -0
  60. package/sails/file-uploads/files/backend/services/file-storage.ts +128 -0
  61. package/sails/file-uploads/files/frontend/components/FileList.tsx +248 -0
  62. package/sails/file-uploads/files/frontend/components/FileUploadButton.tsx +147 -0
  63. package/sails/file-uploads/files/frontend/hooks/useFileUpload.ts +106 -0
  64. package/sails/file-uploads/files/frontend/hooks/useFiles.ts +118 -0
  65. package/sails/file-uploads/files/frontend/pages/Files.tsx +37 -0
  66. package/sails/file-uploads/install.ts +466 -0
  67. package/sails/gdpr/README.md +174 -0
  68. package/sails/gdpr/addon.json +27 -0
  69. package/sails/gdpr/files/backend/routes/gdpr.ts +140 -0
  70. package/sails/gdpr/files/backend/services/gdpr.ts +293 -0
  71. package/sails/gdpr/files/frontend/components/auth/ConsentCheckboxes.tsx +97 -0
  72. package/sails/gdpr/files/frontend/components/gdpr/AccountDeletionRequest.tsx +192 -0
  73. package/sails/gdpr/files/frontend/components/gdpr/DataExportButton.tsx +75 -0
  74. package/sails/gdpr/files/frontend/pages/PrivacyPolicy.tsx +186 -0
  75. package/sails/gdpr/install.ts +756 -0
  76. package/sails/google-oauth/README.md +121 -0
  77. package/sails/google-oauth/addon.json +22 -0
  78. package/sails/google-oauth/files/GoogleButton.tsx +50 -0
  79. package/sails/google-oauth/install.ts +252 -0
  80. package/sails/i18n/README.md +193 -0
  81. package/sails/i18n/addon.json +30 -0
  82. package/sails/i18n/files/frontend/components/LanguageSwitcher.tsx +108 -0
  83. package/sails/i18n/files/frontend/hooks/useLanguage.ts +31 -0
  84. package/sails/i18n/files/frontend/lib/i18n.ts +32 -0
  85. package/sails/i18n/files/frontend/locales/de/common.json +44 -0
  86. package/sails/i18n/files/frontend/locales/en/common.json +44 -0
  87. package/sails/i18n/install.ts +407 -0
  88. package/sails/push-notifications/README.md +163 -0
  89. package/sails/push-notifications/addon.json +31 -0
  90. package/sails/push-notifications/files/backend/routes/notifications.ts +153 -0
  91. package/sails/push-notifications/files/backend/schema/notifications.ts +31 -0
  92. package/sails/push-notifications/files/backend/services/notifications.ts +117 -0
  93. package/sails/push-notifications/files/frontend/components/PushNotificationInit.tsx +12 -0
  94. package/sails/push-notifications/files/frontend/hooks/usePushNotifications.ts +154 -0
  95. package/sails/push-notifications/install.ts +384 -0
  96. package/sails/r2-storage/README.md +101 -0
  97. package/sails/r2-storage/addon.json +29 -0
  98. package/sails/r2-storage/files/backend/services/storage.ts +71 -0
  99. package/sails/r2-storage/files/frontend/components/ProfilePictureUpload.tsx +167 -0
  100. package/sails/r2-storage/install.ts +412 -0
  101. package/sails/rate-limiting/README.md +145 -0
  102. package/sails/rate-limiting/addon.json +20 -0
  103. package/sails/rate-limiting/files/backend/middleware/rate-limit-store.ts +104 -0
  104. package/sails/rate-limiting/files/backend/middleware/rate-limit.ts +137 -0
  105. package/sails/rate-limiting/install.ts +300 -0
  106. package/sails/registry.json +107 -0
  107. package/sails/stripe/README.md +214 -0
  108. package/sails/stripe/addon.json +24 -0
  109. package/sails/stripe/files/backend/routes/stripe.ts +154 -0
  110. package/sails/stripe/files/backend/schema/stripe.ts +74 -0
  111. package/sails/stripe/files/backend/services/stripe.ts +224 -0
  112. package/sails/stripe/files/frontend/components/SubscriptionStatus.tsx +135 -0
  113. package/sails/stripe/files/frontend/hooks/useSubscription.ts +86 -0
  114. package/sails/stripe/files/frontend/pages/Checkout.tsx +116 -0
  115. package/sails/stripe/files/frontend/pages/Pricing.tsx +226 -0
  116. package/sails/stripe/install.ts +378 -0
@@ -0,0 +1,178 @@
1
+ # PostHog Analytics Sail
2
+
3
+ Adds privacy-friendly analytics to your keel application using PostHog. Supports automatic page view tracking, user identification, custom events, and more.
4
+
5
+ ## Features
6
+
7
+ - Automatic page view tracking on SPA route changes
8
+ - User identification tied to BetterAuth sessions
9
+ - Custom event tracking API
10
+ - Session recording and heatmaps (configurable in PostHog)
11
+ - Feature flags support
12
+ - Works with PostHog Cloud or self-hosted instances
13
+ - GDPR-compatible (supports cookie-less tracking and consent)
14
+ - No backend dependencies — everything runs client-side
15
+
16
+ ## Prerequisites
17
+
18
+ - A PostHog account (cloud or self-hosted)
19
+ - Cloud: https://app.posthog.com (US) or https://eu.posthog.com (EU)
20
+ - Self-hosted: https://posthog.com/docs/self-host
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ npx tsx sails/analytics/install.ts
26
+ ```
27
+
28
+ The installer will ask whether you are using PostHog Cloud or self-hosted and collect your API key.
29
+
30
+ ## Manual Setup
31
+
32
+ ### 1. Get Your API Key
33
+
34
+ #### PostHog Cloud
35
+ 1. Sign up at https://app.posthog.com
36
+ 2. Create a project
37
+ 3. Go to **Project Settings**
38
+ 4. Copy the **Project API Key** (starts with `phc_`)
39
+
40
+ #### Self-Hosted
41
+ 1. Log into your PostHog instance
42
+ 2. Go to **Project Settings**
43
+ 3. Copy the **Project API Key**
44
+
45
+ ### 2. Environment Variables
46
+
47
+ ```env
48
+ VITE_POSTHOG_KEY=phc_your_project_api_key
49
+ VITE_POSTHOG_HOST=https://us.i.posthog.com
50
+ ```
51
+
52
+ For EU cloud, use `https://eu.i.posthog.com`. For self-hosted, use your instance URL.
53
+
54
+ ## Architecture
55
+
56
+ ### Analytics Service (`src/lib/analytics.ts`)
57
+
58
+ Singleton service that wraps the PostHog SDK:
59
+
60
+ ```typescript
61
+ import analytics from "@/lib/analytics";
62
+
63
+ // Initialize (done automatically by AnalyticsProvider)
64
+ analytics.init();
65
+
66
+ // Identify user after login
67
+ analytics.identify("user-123", { email: "user@example.com", plan: "pro" });
68
+
69
+ // Track custom event
70
+ analytics.trackEvent("feature_used", { feature: "dark-mode" });
71
+
72
+ // Track page view (done automatically on route changes)
73
+ analytics.trackPageView();
74
+
75
+ // Reset on logout
76
+ analytics.reset();
77
+ ```
78
+
79
+ When `VITE_POSTHOG_KEY` is not set (local development), all calls become no-ops.
80
+
81
+ ### AnalyticsProvider Component
82
+
83
+ Wraps your app and handles:
84
+ - PostHog initialization on mount
85
+ - User identification when logged in
86
+ - Identity reset on logout
87
+ - Page view tracking on route changes
88
+
89
+ ```tsx
90
+ // In App.tsx (added automatically by installer)
91
+ import { AnalyticsProvider } from "./components/AnalyticsProvider";
92
+
93
+ export default function App() {
94
+ return (
95
+ <AnalyticsProvider>
96
+ <AppRouter />
97
+ </AnalyticsProvider>
98
+ );
99
+ }
100
+ ```
101
+
102
+ ### useAnalytics Hook
103
+
104
+ For tracking events in components:
105
+
106
+ ```tsx
107
+ import { useAnalytics } from "@/hooks/useAnalytics";
108
+
109
+ function PricingPage() {
110
+ const { trackEvent } = useAnalytics();
111
+
112
+ const handlePlanSelect = (plan: string) => {
113
+ trackEvent("plan_selected", { plan });
114
+ };
115
+
116
+ return (
117
+ <button onClick={() => handlePlanSelect("pro")}>
118
+ Select Pro Plan
119
+ </button>
120
+ );
121
+ }
122
+ ```
123
+
124
+ The hook also auto-handles user identification and page views, so you can use it in any component that needs event tracking.
125
+
126
+ ## Common Events to Track
127
+
128
+ | Event | When | Properties |
129
+ |-------|------|------------|
130
+ | `signup_completed` | After successful signup | `{ method: "email" }` |
131
+ | `plan_selected` | User clicks a pricing plan | `{ plan: "pro" }` |
132
+ | `feature_used` | User interacts with a feature | `{ feature: "export" }` |
133
+ | `error_occurred` | An error happens | `{ page: "/settings", type: "validation" }` |
134
+ | `onboarding_step` | User progresses in onboarding | `{ step: 3, total: 5 }` |
135
+
136
+ ## PostHog Features
137
+
138
+ ### Session Recording
139
+
140
+ Enable in PostHog > Project Settings > Session Recording. Records user sessions as videos for debugging and UX research.
141
+
142
+ ### Feature Flags
143
+
144
+ ```typescript
145
+ import { getPostHog } from "@/lib/analytics";
146
+
147
+ const posthog = getPostHog();
148
+ if (posthog?.isFeatureEnabled("new-dashboard")) {
149
+ // Show new dashboard
150
+ }
151
+ ```
152
+
153
+ ### Surveys
154
+
155
+ Create in-app surveys directly from the PostHog dashboard. They appear automatically when conditions are met.
156
+
157
+ ## GDPR Compliance
158
+
159
+ PostHog offers several privacy-friendly options:
160
+
161
+ 1. **Cookie-less tracking**: PostHog can work without cookies using localStorage
162
+ 2. **Consent management**: Disable autocapture until consent is given:
163
+ ```typescript
164
+ // In analytics.ts, change persistence to "memory" and disable autocapture
165
+ // Then enable after consent:
166
+ posthog.opt_in_capturing();
167
+ ```
168
+ 3. **EU hosting**: Use `https://eu.i.posthog.com` for EU data residency
169
+ 4. **Self-hosting**: Full data control with a self-hosted instance
170
+
171
+ If you have the GDPR sail installed, consider integrating analytics initialization with the consent tracking system.
172
+
173
+ ## Disabling in Development
174
+
175
+ Analytics is automatically disabled when `VITE_POSTHOG_KEY` is not set. In local development, you can either:
176
+
177
+ - Leave `VITE_POSTHOG_KEY` empty in your `.env` (analytics disabled)
178
+ - Set it to your dev/staging PostHog project key (analytics enabled)
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "analytics",
3
+ "displayName": "PostHog Analytics",
4
+ "description": "Privacy-friendly analytics with PostHog — auto page views, user identification, and custom event tracking",
5
+ "version": "1.0.0",
6
+ "compatibility": ">=1.0.0",
7
+ "requiredEnvVars": [
8
+ { "key": "VITE_POSTHOG_KEY", "description": "PostHog project API key" },
9
+ { "key": "VITE_POSTHOG_HOST", "description": "PostHog instance URL (e.g., https://us.i.posthog.com)" }
10
+ ],
11
+ "dependencies": {
12
+ "backend": {},
13
+ "frontend": { "posthog-js": "^1.200.0" }
14
+ },
15
+ "modifies": {
16
+ "backend": [],
17
+ "frontend": ["src/App.tsx"]
18
+ },
19
+ "adds": {
20
+ "backend": [],
21
+ "frontend": [
22
+ "src/lib/analytics.ts",
23
+ "src/hooks/useAnalytics.ts",
24
+ "src/components/AnalyticsProvider.tsx"
25
+ ]
26
+ }
27
+ }
@@ -0,0 +1,58 @@
1
+ import { useEffect, type ReactNode } from "react";
2
+ import { useLocation } from "react-router";
3
+ import { useAuth } from "@/hooks/useAuth.js";
4
+ import {
5
+ initAnalytics,
6
+ identify,
7
+ reset,
8
+ trackPageView,
9
+ } from "@/lib/analytics.js";
10
+
11
+ interface AnalyticsProviderProps {
12
+ children: ReactNode;
13
+ }
14
+
15
+ /**
16
+ * Provider component that initializes PostHog analytics and handles
17
+ * automatic user identification and page view tracking.
18
+ *
19
+ * Wrap your app (or router) with this component:
20
+ *
21
+ * <AnalyticsProvider>
22
+ * <AppRouter />
23
+ * </AnalyticsProvider>
24
+ *
25
+ * Behavior:
26
+ * - Initializes PostHog on mount
27
+ * - Identifies the user when they are logged in
28
+ * - Resets identity on logout
29
+ * - Tracks page views on every route change
30
+ */
31
+ export function AnalyticsProvider({ children }: AnalyticsProviderProps) {
32
+ const { user, isAuthenticated } = useAuth();
33
+ const location = useLocation();
34
+
35
+ // Initialize PostHog on mount
36
+ useEffect(() => {
37
+ initAnalytics();
38
+ }, []);
39
+
40
+ // Identify or reset user when auth state changes
41
+ useEffect(() => {
42
+ if (isAuthenticated && user) {
43
+ identify(user.id, {
44
+ email: user.email,
45
+ name: user.name,
46
+ });
47
+ } else {
48
+ reset();
49
+ }
50
+ }, [isAuthenticated, user]);
51
+
52
+ // Track page views on route changes
53
+ useEffect(() => {
54
+ trackPageView();
55
+ }, [location.pathname]);
56
+
57
+ return <>{children}</>;
58
+ }
@@ -0,0 +1,64 @@
1
+ import { useEffect, useCallback } from "react";
2
+ import { useLocation } from "react-router";
3
+ import { useAuth } from "@/hooks/useAuth.js";
4
+ import { identify, reset, trackEvent, trackPageView } from "@/lib/analytics.js";
5
+
6
+ interface UseAnalyticsResult {
7
+ trackEvent: (name: string, properties?: Record<string, string | number | boolean>) => void;
8
+ identify: (userId: string, traits?: Record<string, string | number | boolean>) => void;
9
+ reset: () => void;
10
+ }
11
+
12
+ /**
13
+ * React hook that integrates PostHog analytics with auth state and routing.
14
+ *
15
+ * - Auto-identifies the user when they log in (using useAuth)
16
+ * - Auto-resets identity when the user logs out
17
+ * - Auto-tracks page views on every route change (using useLocation)
18
+ * - Returns functions for manual event tracking
19
+ */
20
+ export function useAnalytics(): UseAnalyticsResult {
21
+ const { user, isAuthenticated } = useAuth();
22
+ const location = useLocation();
23
+
24
+ // Auto-identify / reset when auth state changes
25
+ useEffect(() => {
26
+ if (isAuthenticated && user) {
27
+ identify(user.id, {
28
+ email: user.email,
29
+ name: user.name,
30
+ });
31
+ } else {
32
+ reset();
33
+ }
34
+ }, [isAuthenticated, user]);
35
+
36
+ // Auto-track page views on route changes
37
+ useEffect(() => {
38
+ trackPageView();
39
+ }, [location.pathname]);
40
+
41
+ const track = useCallback(
42
+ (name: string, properties?: Record<string, string | number | boolean>) => {
43
+ trackEvent(name, properties);
44
+ },
45
+ [],
46
+ );
47
+
48
+ const identifyUser = useCallback(
49
+ (userId: string, traits?: Record<string, string | number | boolean>) => {
50
+ identify(userId, traits);
51
+ },
52
+ [],
53
+ );
54
+
55
+ const resetIdentity = useCallback(() => {
56
+ reset();
57
+ }, []);
58
+
59
+ return {
60
+ trackEvent: track,
61
+ identify: identifyUser,
62
+ reset: resetIdentity,
63
+ };
64
+ }
@@ -0,0 +1,103 @@
1
+ import posthog from "posthog-js";
2
+
3
+ // ---------------------------------------------------------------------------
4
+ // PostHog Analytics Service
5
+ // ---------------------------------------------------------------------------
6
+
7
+ const POSTHOG_KEY = import.meta.env.VITE_POSTHOG_KEY as string | undefined;
8
+ const POSTHOG_HOST = import.meta.env.VITE_POSTHOG_HOST as string | undefined;
9
+
10
+ /** Whether PostHog is configured and initialized. */
11
+ let isInitialized = false;
12
+
13
+ /**
14
+ * Initialize PostHog analytics.
15
+ *
16
+ * If VITE_POSTHOG_KEY is not set (common in local dev), all calls become
17
+ * no-ops so analytics never interfere with development.
18
+ */
19
+ export function initAnalytics(): void {
20
+ if (isInitialized) return;
21
+
22
+ if (!POSTHOG_KEY) {
23
+ if (import.meta.env.DEV) {
24
+ console.log("[analytics] VITE_POSTHOG_KEY not set — analytics disabled in dev");
25
+ }
26
+ return;
27
+ }
28
+
29
+ posthog.init(POSTHOG_KEY, {
30
+ api_host: POSTHOG_HOST || "https://us.i.posthog.com",
31
+ autocapture: true,
32
+ capture_pageview: false, // We handle page views manually for SPA routing
33
+ capture_pageleave: true,
34
+ persistence: "localStorage",
35
+ person_profiles: "identified_only",
36
+ });
37
+
38
+ isInitialized = true;
39
+ }
40
+
41
+ /**
42
+ * Identify a user after login. Associates all future events with this user.
43
+ *
44
+ * @param userId - The user's unique ID
45
+ * @param traits - Optional user properties (email, name, plan, etc.)
46
+ */
47
+ export function identify(
48
+ userId: string,
49
+ traits?: Record<string, string | number | boolean>,
50
+ ): void {
51
+ if (!isInitialized) return;
52
+ posthog.identify(userId, traits);
53
+ }
54
+
55
+ /**
56
+ * Reset analytics identity after logout.
57
+ * Creates a new anonymous ID for subsequent events.
58
+ */
59
+ export function reset(): void {
60
+ if (!isInitialized) return;
61
+ posthog.reset();
62
+ }
63
+
64
+ /**
65
+ * Track a custom event.
66
+ *
67
+ * @param name - Event name (e.g., "subscription_started", "feature_used")
68
+ * @param properties - Optional event properties
69
+ */
70
+ export function trackEvent(
71
+ name: string,
72
+ properties?: Record<string, string | number | boolean>,
73
+ ): void {
74
+ if (!isInitialized) return;
75
+ posthog.capture(name, properties);
76
+ }
77
+
78
+ /**
79
+ * Track a page view. Call this on every route change in an SPA.
80
+ */
81
+ export function trackPageView(): void {
82
+ if (!isInitialized) return;
83
+ posthog.capture("$pageview");
84
+ }
85
+
86
+ /**
87
+ * Get the PostHog instance for advanced usage.
88
+ * Returns null if not initialized.
89
+ */
90
+ export function getPostHog(): typeof posthog | null {
91
+ return isInitialized ? posthog : null;
92
+ }
93
+
94
+ export const analytics = {
95
+ init: initAnalytics,
96
+ identify,
97
+ reset,
98
+ trackEvent,
99
+ trackPageView,
100
+ getPostHog,
101
+ };
102
+
103
+ export default analytics;