@clipin/convex-wearables 0.0.1

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 (143) hide show
  1. package/LICENSE +203 -0
  2. package/README.md +616 -0
  3. package/dist/client/_generated/_ignore.d.ts +1 -0
  4. package/dist/client/_generated/_ignore.d.ts.map +1 -0
  5. package/dist/client/_generated/_ignore.js +4 -0
  6. package/dist/client/_generated/_ignore.js.map +1 -0
  7. package/dist/client/index.d.ts +244 -0
  8. package/dist/client/index.d.ts.map +1 -0
  9. package/dist/client/index.js +555 -0
  10. package/dist/client/index.js.map +1 -0
  11. package/dist/client/types.d.ts +689 -0
  12. package/dist/client/types.d.ts.map +1 -0
  13. package/dist/client/types.js +112 -0
  14. package/dist/client/types.js.map +1 -0
  15. package/dist/component/_generated/_ignore.d.ts +1 -0
  16. package/dist/component/_generated/_ignore.d.ts.map +1 -0
  17. package/dist/component/_generated/_ignore.js +4 -0
  18. package/dist/component/_generated/_ignore.js.map +1 -0
  19. package/dist/component/_generated/api.d.ts +13 -0
  20. package/dist/component/_generated/api.d.ts.map +1 -0
  21. package/dist/component/_generated/api.js +14 -0
  22. package/dist/component/_generated/api.js.map +1 -0
  23. package/dist/component/_generated/dataModel.d.ts +28 -0
  24. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  25. package/dist/component/_generated/dataModel.js +11 -0
  26. package/dist/component/_generated/dataModel.js.map +1 -0
  27. package/dist/component/_generated/server.d.ts +23 -0
  28. package/dist/component/_generated/server.d.ts.map +1 -0
  29. package/dist/component/_generated/server.js +18 -0
  30. package/dist/component/_generated/server.js.map +1 -0
  31. package/dist/component/backfillJobs.d.ts +121 -0
  32. package/dist/component/backfillJobs.d.ts.map +1 -0
  33. package/dist/component/backfillJobs.js +233 -0
  34. package/dist/component/backfillJobs.js.map +1 -0
  35. package/dist/component/connections.d.ts +159 -0
  36. package/dist/component/connections.d.ts.map +1 -0
  37. package/dist/component/connections.js +288 -0
  38. package/dist/component/connections.js.map +1 -0
  39. package/dist/component/convex.config.d.ts +3 -0
  40. package/dist/component/convex.config.d.ts.map +1 -0
  41. package/dist/component/convex.config.js +6 -0
  42. package/dist/component/convex.config.js.map +1 -0
  43. package/dist/component/dataPoints.d.ts +81 -0
  44. package/dist/component/dataPoints.d.ts.map +1 -0
  45. package/dist/component/dataPoints.js +258 -0
  46. package/dist/component/dataPoints.js.map +1 -0
  47. package/dist/component/dataSources.d.ts +56 -0
  48. package/dist/component/dataSources.d.ts.map +1 -0
  49. package/dist/component/dataSources.js +95 -0
  50. package/dist/component/dataSources.js.map +1 -0
  51. package/dist/component/events.d.ts +203 -0
  52. package/dist/component/events.d.ts.map +1 -0
  53. package/dist/component/events.js +251 -0
  54. package/dist/component/events.js.map +1 -0
  55. package/dist/component/garminBackfill.d.ts +40 -0
  56. package/dist/component/garminBackfill.d.ts.map +1 -0
  57. package/dist/component/garminBackfill.js +296 -0
  58. package/dist/component/garminBackfill.js.map +1 -0
  59. package/dist/component/garminWebhooks.d.ts +17 -0
  60. package/dist/component/garminWebhooks.d.ts.map +1 -0
  61. package/dist/component/garminWebhooks.js +505 -0
  62. package/dist/component/garminWebhooks.js.map +1 -0
  63. package/dist/component/httpHandlers.d.ts +32 -0
  64. package/dist/component/httpHandlers.d.ts.map +1 -0
  65. package/dist/component/httpHandlers.js +131 -0
  66. package/dist/component/httpHandlers.js.map +1 -0
  67. package/dist/component/lifecycle.d.ts +12 -0
  68. package/dist/component/lifecycle.d.ts.map +1 -0
  69. package/dist/component/lifecycle.js +79 -0
  70. package/dist/component/lifecycle.js.map +1 -0
  71. package/dist/component/menstrualCycles.d.ts +98 -0
  72. package/dist/component/menstrualCycles.d.ts.map +1 -0
  73. package/dist/component/menstrualCycles.js +112 -0
  74. package/dist/component/menstrualCycles.js.map +1 -0
  75. package/dist/component/oauthActions.d.ts +52 -0
  76. package/dist/component/oauthActions.d.ts.map +1 -0
  77. package/dist/component/oauthActions.js +208 -0
  78. package/dist/component/oauthActions.js.map +1 -0
  79. package/dist/component/oauthStates.d.ts +47 -0
  80. package/dist/component/oauthStates.d.ts.map +1 -0
  81. package/dist/component/oauthStates.js +77 -0
  82. package/dist/component/oauthStates.js.map +1 -0
  83. package/dist/component/providerSettings.d.ts +15 -0
  84. package/dist/component/providerSettings.d.ts.map +1 -0
  85. package/dist/component/providerSettings.js +57 -0
  86. package/dist/component/providerSettings.js.map +1 -0
  87. package/dist/component/providers/garmin.d.ts +306 -0
  88. package/dist/component/providers/garmin.d.ts.map +1 -0
  89. package/dist/component/providers/garmin.js +675 -0
  90. package/dist/component/providers/garmin.js.map +1 -0
  91. package/dist/component/providers/oauth.d.ts +42 -0
  92. package/dist/component/providers/oauth.d.ts.map +1 -0
  93. package/dist/component/providers/oauth.js +181 -0
  94. package/dist/component/providers/oauth.js.map +1 -0
  95. package/dist/component/providers/polar.d.ts +6 -0
  96. package/dist/component/providers/polar.d.ts.map +1 -0
  97. package/dist/component/providers/polar.js +175 -0
  98. package/dist/component/providers/polar.js.map +1 -0
  99. package/dist/component/providers/registry.d.ts +14 -0
  100. package/dist/component/providers/registry.d.ts.map +1 -0
  101. package/dist/component/providers/registry.js +32 -0
  102. package/dist/component/providers/registry.js.map +1 -0
  103. package/dist/component/providers/strava.d.ts +45 -0
  104. package/dist/component/providers/strava.d.ts.map +1 -0
  105. package/dist/component/providers/strava.js +182 -0
  106. package/dist/component/providers/strava.js.map +1 -0
  107. package/dist/component/providers/suunto.d.ts +5 -0
  108. package/dist/component/providers/suunto.d.ts.map +1 -0
  109. package/dist/component/providers/suunto.js +502 -0
  110. package/dist/component/providers/suunto.js.map +1 -0
  111. package/dist/component/providers/types.d.ts +139 -0
  112. package/dist/component/providers/types.d.ts.map +1 -0
  113. package/dist/component/providers/types.js +5 -0
  114. package/dist/component/providers/types.js.map +1 -0
  115. package/dist/component/providers/whoop.d.ts +4 -0
  116. package/dist/component/providers/whoop.d.ts.map +1 -0
  117. package/dist/component/providers/whoop.js +439 -0
  118. package/dist/component/providers/whoop.js.map +1 -0
  119. package/dist/component/schema.d.ts +429 -0
  120. package/dist/component/schema.d.ts.map +1 -0
  121. package/dist/component/schema.js +282 -0
  122. package/dist/component/schema.js.map +1 -0
  123. package/dist/component/sdkPush.d.ts +143 -0
  124. package/dist/component/sdkPush.d.ts.map +1 -0
  125. package/dist/component/sdkPush.js +338 -0
  126. package/dist/component/sdkPush.js.map +1 -0
  127. package/dist/component/summaries.d.ts +129 -0
  128. package/dist/component/summaries.d.ts.map +1 -0
  129. package/dist/component/summaries.js +129 -0
  130. package/dist/component/summaries.js.map +1 -0
  131. package/dist/component/syncJobs.d.ts +142 -0
  132. package/dist/component/syncJobs.d.ts.map +1 -0
  133. package/dist/component/syncJobs.js +136 -0
  134. package/dist/component/syncJobs.js.map +1 -0
  135. package/dist/component/syncWorkflow.d.ts +99 -0
  136. package/dist/component/syncWorkflow.d.ts.map +1 -0
  137. package/dist/component/syncWorkflow.js +579 -0
  138. package/dist/component/syncWorkflow.js.map +1 -0
  139. package/dist/component/workflowManager.d.ts +3 -0
  140. package/dist/component/workflowManager.d.ts.map +1 -0
  141. package/dist/component/workflowManager.js +17 -0
  142. package/dist/component/workflowManager.js.map +1 -0
  143. package/package.json +84 -0
@@ -0,0 +1,131 @@
1
+ /**
2
+ * HTTP action handlers for OAuth callbacks and provider webhooks.
3
+ *
4
+ * These are Convex httpAction endpoints that handle:
5
+ * 1. OAuth callback redirects (GET /oauth/callback)
6
+ * 2. Provider webhook pushes (POST /webhooks/:provider)
7
+ */
8
+ import { internal } from "./_generated/api";
9
+ import { httpAction } from "./_generated/server";
10
+ // ---------------------------------------------------------------------------
11
+ // OAuth callback handler
12
+ // ---------------------------------------------------------------------------
13
+ /**
14
+ * Handles the OAuth redirect callback from a provider.
15
+ *
16
+ * Expected query params:
17
+ * - state: The OAuth state token
18
+ * - code: The authorization code
19
+ *
20
+ * The host app must mount this at a route and pass client credentials
21
+ * via the component configuration.
22
+ */
23
+ export const oauthCallback = httpAction(async (ctx, request) => {
24
+ const url = new URL(request.url);
25
+ const state = url.searchParams.get("state");
26
+ const code = url.searchParams.get("code");
27
+ const error = url.searchParams.get("error");
28
+ if (error) {
29
+ return new Response(JSON.stringify({ error: `OAuth error: ${error}` }), {
30
+ status: 400,
31
+ headers: { "Content-Type": "application/json" },
32
+ });
33
+ }
34
+ if (!state || !code) {
35
+ return new Response(JSON.stringify({ error: "Missing state or code parameter" }), {
36
+ status: 400,
37
+ headers: { "Content-Type": "application/json" },
38
+ });
39
+ }
40
+ // Look up the state to find which provider and user this is for
41
+ const oauthState = await ctx.runQuery(internal.oauthStates.getByState, {
42
+ state,
43
+ });
44
+ if (!oauthState) {
45
+ return new Response(JSON.stringify({ error: "Invalid or expired OAuth state" }), {
46
+ status: 400,
47
+ headers: { "Content-Type": "application/json" },
48
+ });
49
+ }
50
+ // Return the state and code to be processed by the client-side handler.
51
+ // The host app will call handleCallback action with credentials.
52
+ return new Response(JSON.stringify({
53
+ state,
54
+ code,
55
+ provider: oauthState.provider,
56
+ userId: oauthState.userId,
57
+ }), { status: 200, headers: { "Content-Type": "application/json" } });
58
+ });
59
+ // ---------------------------------------------------------------------------
60
+ // Strava webhook verification (GET)
61
+ // ---------------------------------------------------------------------------
62
+ /**
63
+ * Strava webhook subscription verification.
64
+ * Strava sends a GET request with hub.challenge to verify the endpoint.
65
+ */
66
+ export const stravaWebhookVerify = httpAction(async (_ctx, request) => {
67
+ const url = new URL(request.url);
68
+ const mode = url.searchParams.get("hub.mode");
69
+ const challenge = url.searchParams.get("hub.challenge");
70
+ const _verifyToken = url.searchParams.get("hub.verify_token");
71
+ if (mode !== "subscribe" || !challenge) {
72
+ return new Response("Invalid request", { status: 400 });
73
+ }
74
+ // The verify token should match what was set during subscription creation.
75
+ // For now, accept any verify token — the host app should validate this
76
+ // in their configuration.
77
+ return new Response(JSON.stringify({ "hub.challenge": challenge }), {
78
+ status: 200,
79
+ headers: { "Content-Type": "application/json" },
80
+ });
81
+ });
82
+ // ---------------------------------------------------------------------------
83
+ // Strava webhook events (POST)
84
+ // ---------------------------------------------------------------------------
85
+ /**
86
+ * Handles incoming Strava webhook events.
87
+ *
88
+ * Strava pushes activity create/update/delete events here.
89
+ * We process activity creates/updates by fetching the full activity
90
+ * and storing it. Deletes are handled by removing the corresponding event.
91
+ */
92
+ export const stravaWebhookEvent = httpAction(async (_ctx, request) => {
93
+ try {
94
+ const body = await request.json();
95
+ // Strava webhook payload format:
96
+ // { object_type: "activity", object_id: 123, aspect_type: "create"|"update"|"delete",
97
+ // owner_id: 456, subscription_id: 789 }
98
+ const { object_type, object_id, aspect_type, owner_id } = body;
99
+ if (object_type !== "activity") {
100
+ // We only handle activity events for now
101
+ return new Response("OK", { status: 200 });
102
+ }
103
+ const ownerId = String(owner_id);
104
+ const connection = await _ctx.runQuery(internal.connections.getByProviderUser, {
105
+ provider: "strava",
106
+ providerUserId: ownerId,
107
+ });
108
+ if (!connection) {
109
+ return new Response("OK", { status: 200 });
110
+ }
111
+ if (aspect_type === "delete") {
112
+ await _ctx.runMutation(internal.events.deleteByExternalId, {
113
+ externalId: `strava-${object_id}`,
114
+ });
115
+ return new Response("OK", { status: 200 });
116
+ }
117
+ const now = Date.now();
118
+ await _ctx.runMutation(internal.syncWorkflow.requestConnectionSync, {
119
+ connectionId: connection._id,
120
+ mode: "webhook",
121
+ triggerSource: `strava:${aspect_type}:${object_id}`,
122
+ windowStart: now - 30 * 24 * 60 * 60 * 1000,
123
+ windowEnd: now + 5 * 60 * 1000,
124
+ });
125
+ return new Response("OK", { status: 200 });
126
+ }
127
+ catch {
128
+ return new Response("Internal error", { status: 500 });
129
+ }
130
+ });
131
+ //# sourceMappingURL=httpHandlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpHandlers.js","sourceRoot":"","sources":["../../src/component/httpHandlers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Delete ALL data for a user across all component tables.
3
+ * Used for GDPR compliance and account deletion.
4
+ *
5
+ * This is a "best-effort" deletion within a single mutation.
6
+ * For users with very large amounts of data, the host app should
7
+ * call this repeatedly or use a workflow.
8
+ */
9
+ export declare const deleteAllUserData: import("convex/server").RegisteredMutation<"public", {
10
+ userId: string;
11
+ }, Promise<void>>;
12
+ //# sourceMappingURL=lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/component/lifecycle.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB;;iBAwE5B,CAAC"}
@@ -0,0 +1,79 @@
1
+ import { v } from "convex/values";
2
+ import { mutation } from "./_generated/server";
3
+ /**
4
+ * Delete ALL data for a user across all component tables.
5
+ * Used for GDPR compliance and account deletion.
6
+ *
7
+ * This is a "best-effort" deletion within a single mutation.
8
+ * For users with very large amounts of data, the host app should
9
+ * call this repeatedly or use a workflow.
10
+ */
11
+ export const deleteAllUserData = mutation({
12
+ args: { userId: v.string() },
13
+ handler: async (ctx, args) => {
14
+ const { userId } = args;
15
+ // 1. Delete connections
16
+ const connections = await ctx.db
17
+ .query("connections")
18
+ .withIndex("by_user", (idx) => idx.eq("userId", userId))
19
+ .collect();
20
+ for (const conn of connections) {
21
+ // Delete backfill jobs for this connection
22
+ const backfills = await ctx.db
23
+ .query("backfillJobs")
24
+ .withIndex("by_connection", (idx) => idx.eq("connectionId", conn._id))
25
+ .collect();
26
+ for (const bf of backfills) {
27
+ await ctx.db.delete(bf._id);
28
+ }
29
+ await ctx.db.delete(conn._id);
30
+ }
31
+ // 2. Delete data sources and their data points
32
+ const sources = await ctx.db
33
+ .query("dataSources")
34
+ .withIndex("by_user_provider", (idx) => idx.eq("userId", userId))
35
+ .collect();
36
+ for (const source of sources) {
37
+ // Delete data points in batches
38
+ let points = await ctx.db
39
+ .query("dataPoints")
40
+ .withIndex("by_source_type_time", (idx) => idx.eq("dataSourceId", source._id))
41
+ .take(500);
42
+ while (points.length > 0) {
43
+ for (const p of points) {
44
+ await ctx.db.delete(p._id);
45
+ }
46
+ points = await ctx.db
47
+ .query("dataPoints")
48
+ .withIndex("by_source_type_time", (idx) => idx.eq("dataSourceId", source._id))
49
+ .take(500);
50
+ }
51
+ await ctx.db.delete(source._id);
52
+ }
53
+ // 3. Delete events
54
+ const events = await ctx.db
55
+ .query("events")
56
+ .withIndex("by_user_category_time", (idx) => idx.eq("userId", userId))
57
+ .collect();
58
+ for (const event of events) {
59
+ await ctx.db.delete(event._id);
60
+ }
61
+ // 4. Delete daily summaries
62
+ const summaries = await ctx.db
63
+ .query("dailySummaries")
64
+ .withIndex("by_user_date", (idx) => idx.eq("userId", userId))
65
+ .collect();
66
+ for (const summary of summaries) {
67
+ await ctx.db.delete(summary._id);
68
+ }
69
+ // 5. Delete sync jobs
70
+ const jobs = await ctx.db
71
+ .query("syncJobs")
72
+ .withIndex("by_user", (idx) => idx.eq("userId", userId))
73
+ .collect();
74
+ for (const job of jobs) {
75
+ await ctx.db.delete(job._id);
76
+ }
77
+ },
78
+ });
79
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/component/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Get menstrual cycle records for a user within a date range.
3
+ */
4
+ export declare const getByUserDateRange: import("convex/server").RegisteredQuery<"public", {
5
+ userId: string;
6
+ startDate: string;
7
+ endDate: string;
8
+ }, Promise<{
9
+ _id: import("convex/values").GenericId<"menstrualCycles">;
10
+ _creationTime: number;
11
+ externalId?: string | undefined;
12
+ currentPhase?: number | undefined;
13
+ dayInCycle?: number | undefined;
14
+ cycleLength?: number | undefined;
15
+ predictedCycleLength?: number | undefined;
16
+ periodLength?: number | undefined;
17
+ currentPhaseType?: string | undefined;
18
+ lengthOfCurrentPhase?: number | undefined;
19
+ daysUntilNextPhase?: number | undefined;
20
+ isPredictedCycle?: boolean | undefined;
21
+ fertileWindowStart?: number | undefined;
22
+ lengthOfFertileWindow?: number | undefined;
23
+ lastUpdatedAt?: number | undefined;
24
+ isPregnant?: boolean | undefined;
25
+ pregnancyDueDate?: string | undefined;
26
+ pregnancyOriginalDueDate?: string | undefined;
27
+ pregnancyCycleStartDate?: string | undefined;
28
+ pregnancyTitle?: string | undefined;
29
+ numberOfBabies?: string | undefined;
30
+ provider: "garmin" | "suunto" | "polar" | "whoop" | "strava" | "apple" | "samsung" | "google";
31
+ userId: string;
32
+ periodStartDate: string;
33
+ }[]>>;
34
+ /**
35
+ * Get the latest menstrual cycle record for a user.
36
+ */
37
+ export declare const getLatest: import("convex/server").RegisteredQuery<"public", {
38
+ userId: string;
39
+ }, Promise<{
40
+ _id: import("convex/values").GenericId<"menstrualCycles">;
41
+ _creationTime: number;
42
+ externalId?: string | undefined;
43
+ currentPhase?: number | undefined;
44
+ dayInCycle?: number | undefined;
45
+ cycleLength?: number | undefined;
46
+ predictedCycleLength?: number | undefined;
47
+ periodLength?: number | undefined;
48
+ currentPhaseType?: string | undefined;
49
+ lengthOfCurrentPhase?: number | undefined;
50
+ daysUntilNextPhase?: number | undefined;
51
+ isPredictedCycle?: boolean | undefined;
52
+ fertileWindowStart?: number | undefined;
53
+ lengthOfFertileWindow?: number | undefined;
54
+ lastUpdatedAt?: number | undefined;
55
+ isPregnant?: boolean | undefined;
56
+ pregnancyDueDate?: string | undefined;
57
+ pregnancyOriginalDueDate?: string | undefined;
58
+ pregnancyCycleStartDate?: string | undefined;
59
+ pregnancyTitle?: string | undefined;
60
+ numberOfBabies?: string | undefined;
61
+ provider: "garmin" | "suunto" | "polar" | "whoop" | "strava" | "apple" | "samsung" | "google";
62
+ userId: string;
63
+ periodStartDate: string;
64
+ } | null>>;
65
+ /**
66
+ * Upsert a menstrual cycle record. Deduplicates by externalId.
67
+ */
68
+ export declare const upsert: import("convex/server").RegisteredMutation<"internal", {
69
+ externalId?: string | undefined;
70
+ currentPhase?: number | undefined;
71
+ dayInCycle?: number | undefined;
72
+ cycleLength?: number | undefined;
73
+ predictedCycleLength?: number | undefined;
74
+ periodLength?: number | undefined;
75
+ currentPhaseType?: string | undefined;
76
+ lengthOfCurrentPhase?: number | undefined;
77
+ daysUntilNextPhase?: number | undefined;
78
+ isPredictedCycle?: boolean | undefined;
79
+ fertileWindowStart?: number | undefined;
80
+ lengthOfFertileWindow?: number | undefined;
81
+ lastUpdatedAt?: number | undefined;
82
+ isPregnant?: boolean | undefined;
83
+ pregnancyDueDate?: string | undefined;
84
+ pregnancyOriginalDueDate?: string | undefined;
85
+ pregnancyCycleStartDate?: string | undefined;
86
+ pregnancyTitle?: string | undefined;
87
+ numberOfBabies?: string | undefined;
88
+ provider: "garmin" | "suunto" | "polar" | "whoop" | "strava" | "apple" | "samsung" | "google";
89
+ userId: string;
90
+ periodStartDate: string;
91
+ }, Promise<import("convex/values").GenericId<"menstrualCycles">>>;
92
+ /**
93
+ * Delete all menstrual cycle data for a user.
94
+ */
95
+ export declare const deleteByUser: import("convex/server").RegisteredMutation<"internal", {
96
+ userId: string;
97
+ }, Promise<void>>;
98
+ //# sourceMappingURL=menstrualCycles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menstrualCycles.d.ts","sourceRoot":"","sources":["../../src/component/menstrualCycles.ts"],"names":[],"mappings":"AAQA;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAkB7B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;UAUpB,CAAC;AAMH;;GAEG;AACH,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;iEAsDjB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,YAAY;;iBAWvB,CAAC"}
@@ -0,0 +1,112 @@
1
+ import { v } from "convex/values";
2
+ import { internalMutation, query } from "./_generated/server";
3
+ import { providerName } from "./schema";
4
+ // ---------------------------------------------------------------------------
5
+ // Queries
6
+ // ---------------------------------------------------------------------------
7
+ /**
8
+ * Get menstrual cycle records for a user within a date range.
9
+ */
10
+ export const getByUserDateRange = query({
11
+ args: {
12
+ userId: v.string(),
13
+ startDate: v.string(), // ISO date "2026-01-01"
14
+ endDate: v.string(),
15
+ },
16
+ returns: v.array(v.any()),
17
+ handler: async (ctx, args) => {
18
+ return await ctx.db
19
+ .query("menstrualCycles")
20
+ .withIndex("by_user_date", (idx) => idx
21
+ .eq("userId", args.userId)
22
+ .gte("periodStartDate", args.startDate)
23
+ .lte("periodStartDate", args.endDate))
24
+ .collect();
25
+ },
26
+ });
27
+ /**
28
+ * Get the latest menstrual cycle record for a user.
29
+ */
30
+ export const getLatest = query({
31
+ args: { userId: v.string() },
32
+ returns: v.any(),
33
+ handler: async (ctx, args) => {
34
+ return await ctx.db
35
+ .query("menstrualCycles")
36
+ .withIndex("by_user_date", (idx) => idx.eq("userId", args.userId))
37
+ .order("desc")
38
+ .first();
39
+ },
40
+ });
41
+ // ---------------------------------------------------------------------------
42
+ // Mutations
43
+ // ---------------------------------------------------------------------------
44
+ /**
45
+ * Upsert a menstrual cycle record. Deduplicates by externalId.
46
+ */
47
+ export const upsert = internalMutation({
48
+ args: {
49
+ userId: v.string(),
50
+ provider: providerName,
51
+ externalId: v.optional(v.string()),
52
+ periodStartDate: v.string(),
53
+ dayInCycle: v.optional(v.number()),
54
+ cycleLength: v.optional(v.number()),
55
+ predictedCycleLength: v.optional(v.number()),
56
+ periodLength: v.optional(v.number()),
57
+ currentPhase: v.optional(v.number()),
58
+ currentPhaseType: v.optional(v.string()),
59
+ lengthOfCurrentPhase: v.optional(v.number()),
60
+ daysUntilNextPhase: v.optional(v.number()),
61
+ isPredictedCycle: v.optional(v.boolean()),
62
+ fertileWindowStart: v.optional(v.number()),
63
+ lengthOfFertileWindow: v.optional(v.number()),
64
+ lastUpdatedAt: v.optional(v.number()),
65
+ isPregnant: v.optional(v.boolean()),
66
+ pregnancyDueDate: v.optional(v.string()),
67
+ pregnancyOriginalDueDate: v.optional(v.string()),
68
+ pregnancyCycleStartDate: v.optional(v.string()),
69
+ pregnancyTitle: v.optional(v.string()),
70
+ numberOfBabies: v.optional(v.string()),
71
+ },
72
+ returns: v.id("menstrualCycles"),
73
+ handler: async (ctx, args) => {
74
+ // Dedup by externalId
75
+ if (args.externalId) {
76
+ const existing = await ctx.db
77
+ .query("menstrualCycles")
78
+ .withIndex("by_external_id", (idx) => idx.eq("externalId", args.externalId))
79
+ .first();
80
+ if (existing) {
81
+ await ctx.db.patch(existing._id, args);
82
+ return existing._id;
83
+ }
84
+ }
85
+ // Dedup by user + periodStartDate
86
+ const existing = await ctx.db
87
+ .query("menstrualCycles")
88
+ .withIndex("by_user_date", (idx) => idx.eq("userId", args.userId).eq("periodStartDate", args.periodStartDate))
89
+ .first();
90
+ if (existing) {
91
+ await ctx.db.patch(existing._id, args);
92
+ return existing._id;
93
+ }
94
+ return await ctx.db.insert("menstrualCycles", args);
95
+ },
96
+ });
97
+ /**
98
+ * Delete all menstrual cycle data for a user.
99
+ */
100
+ export const deleteByUser = internalMutation({
101
+ args: { userId: v.string() },
102
+ handler: async (ctx, args) => {
103
+ const records = await ctx.db
104
+ .query("menstrualCycles")
105
+ .withIndex("by_user_provider", (idx) => idx.eq("userId", args.userId))
106
+ .collect();
107
+ for (const r of records) {
108
+ await ctx.db.delete(r._id);
109
+ }
110
+ },
111
+ });
112
+ //# sourceMappingURL=menstrualCycles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menstrualCycles.js","sourceRoot":"","sources":["../../src/component/menstrualCycles.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * OAuth flow Convex actions.
3
+ *
4
+ * Actions (not mutations) because they make external HTTP calls to
5
+ * provider token endpoints and APIs.
6
+ */
7
+ /**
8
+ * Generate an OAuth authorization URL for a provider.
9
+ *
10
+ * Stores the state token in the oauthStates table and returns the URL
11
+ * the client should redirect the user to.
12
+ */
13
+ export declare const generateAuthUrl: import("convex/server").RegisteredAction<"public", {
14
+ subscriptionKey?: string | undefined;
15
+ provider: "garmin" | "suunto" | "polar" | "whoop" | "strava" | "apple" | "samsung" | "google";
16
+ userId: string;
17
+ redirectUri: string;
18
+ clientId: string;
19
+ clientSecret: string;
20
+ }, Promise<string>>;
21
+ /**
22
+ * Handle the OAuth callback. Consumes the state token, exchanges the
23
+ * authorization code for access/refresh tokens, fetches the user's
24
+ * provider profile, and creates/updates the connection.
25
+ */
26
+ export declare const handleCallback: import("convex/server").RegisteredAction<"public", {
27
+ subscriptionKey?: string | undefined;
28
+ state: string;
29
+ clientId: string;
30
+ clientSecret: string;
31
+ code: string;
32
+ }, Promise<{
33
+ provider: any;
34
+ userId: any;
35
+ connectionId: string;
36
+ }>>;
37
+ /**
38
+ * Ensure a connection has a valid (non-expired) access token.
39
+ * If expired, refreshes the token and updates the connection.
40
+ * Returns the valid access token.
41
+ */
42
+ export declare const ensureValidToken: import("convex/server").RegisteredAction<"internal", {
43
+ refreshToken?: string | undefined;
44
+ tokenExpiresAt?: number | undefined;
45
+ subscriptionKey?: string | undefined;
46
+ provider: "garmin" | "suunto" | "polar" | "whoop" | "strava" | "apple" | "samsung" | "google";
47
+ accessToken: string;
48
+ connectionId: import("convex/values").GenericId<"connections">;
49
+ clientId: string;
50
+ clientSecret: string;
51
+ }, Promise<string>>;
52
+ //# sourceMappingURL=oauthActions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauthActions.d.ts","sourceRoot":"","sources":["../../src/component/oauthActions.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAoBH;;;;;GAKG;AACH,eAAO,MAAM,eAAe;;;;;;;mBA4D1B,CAAC;AAMH;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;GAkGzB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;mBAqD3B,CAAC"}