@followgate/js 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -225,6 +225,47 @@ FollowGate.getUnlockStatus();
225
225
  // }
226
226
  ```
227
227
 
228
+ ### Server-Side Verification
229
+
230
+ When `userId` is set, completions are automatically saved to the server. You can verify users from your backend:
231
+
232
+ ```typescript
233
+ // Check if user is verified (server-side)
234
+ const isVerified = await FollowGate.isVerified();
235
+ // Returns: true | false
236
+
237
+ // Get full verification status
238
+ const status = await FollowGate.getVerificationStatus();
239
+ // Returns: {
240
+ // verified: boolean,
241
+ // userId: string,
242
+ // completedAt?: string, // ISO date string
243
+ // actions?: [{ action: 'follow', target: 'lukasvanuden' }, ...]
244
+ // }
245
+ ```
246
+
247
+ **Use Case: Conditional Features**
248
+
249
+ ```typescript
250
+ // In your app (e.g., Chrome extension, Electron app)
251
+ FollowGate.init({
252
+ appId: 'your-app-id',
253
+ apiKey: 'fg_live_xxx',
254
+ userId: clerkUserId,
255
+ twitter: { handle: 'lukasvanuden' },
256
+ });
257
+
258
+ const isVerified = await FollowGate.isVerified();
259
+
260
+ if (isVerified) {
261
+ // User completed FollowGate → Full access
262
+ enableAllFeatures();
263
+ } else {
264
+ // User hasn't completed → Limited access
265
+ enableTrialMode();
266
+ }
267
+ ```
268
+
228
269
  ### Event Listeners
229
270
 
230
271
  ```typescript
@@ -339,6 +380,7 @@ import type {
339
380
  CompleteOptions,
340
381
  UserInfo,
341
382
  UnlockStatus,
383
+ VerificationStatus,
342
384
  } from '@followgate/js';
343
385
  ```
344
386
 
package/dist/index.d.mts CHANGED
@@ -66,6 +66,18 @@ interface UnlockStatus {
66
66
  username?: string;
67
67
  completedActions?: CompleteOptions[];
68
68
  }
69
+ /**
70
+ * Server-side verification status
71
+ */
72
+ interface VerificationStatus {
73
+ verified: boolean;
74
+ userId: string;
75
+ completedAt?: string;
76
+ actions?: Array<{
77
+ action: string;
78
+ target: string;
79
+ }>;
80
+ }
69
81
  /**
70
82
  * FollowGate SDK Client
71
83
  */
@@ -130,6 +142,18 @@ declare class FollowGateClient {
130
142
  isUnlocked(): boolean;
131
143
  getUnlockStatus(): UnlockStatus;
132
144
  getCompletedActions(): CompleteOptions[];
145
+ /**
146
+ * Check if user is verified server-side
147
+ * Requires userId to be set in config
148
+ * @returns Promise<boolean> - true if verified, false otherwise
149
+ */
150
+ isVerified(): Promise<boolean>;
151
+ /**
152
+ * Get full verification status from server
153
+ * Requires userId to be set in config
154
+ * @returns Promise<VerificationStatus> - full verification details
155
+ */
156
+ getVerificationStatus(): Promise<VerificationStatus>;
133
157
  on(event: EventType, callback: EventCallback): void;
134
158
  off(event: EventType, callback: EventCallback): void;
135
159
  /**
@@ -139,6 +163,11 @@ declare class FollowGateClient {
139
163
  private getStorageKey;
140
164
  private restoreSession;
141
165
  private saveCompletedActions;
166
+ /**
167
+ * Save completion to server for verification
168
+ * Only works if userId is set in config
169
+ */
170
+ private saveCompletion;
142
171
  private buildIntentUrl;
143
172
  private buildTwitterUrl;
144
173
  private buildBlueskyUrl;
@@ -148,4 +177,4 @@ declare class FollowGateClient {
148
177
  }
149
178
  declare const FollowGate: FollowGateClient;
150
179
 
151
- export { type CompleteOptions, type EventCallback, type EventType, FollowGate, FollowGateClient, type FollowGateConfig, FollowGateError, type Platform, type SocialAction, type TwitterConfig, type UnlockStatus, type UserInfo };
180
+ export { type CompleteOptions, type EventCallback, type EventType, FollowGate, FollowGateClient, type FollowGateConfig, FollowGateError, type Platform, type SocialAction, type TwitterConfig, type UnlockStatus, type UserInfo, type VerificationStatus };
package/dist/index.d.ts CHANGED
@@ -66,6 +66,18 @@ interface UnlockStatus {
66
66
  username?: string;
67
67
  completedActions?: CompleteOptions[];
68
68
  }
69
+ /**
70
+ * Server-side verification status
71
+ */
72
+ interface VerificationStatus {
73
+ verified: boolean;
74
+ userId: string;
75
+ completedAt?: string;
76
+ actions?: Array<{
77
+ action: string;
78
+ target: string;
79
+ }>;
80
+ }
69
81
  /**
70
82
  * FollowGate SDK Client
71
83
  */
@@ -130,6 +142,18 @@ declare class FollowGateClient {
130
142
  isUnlocked(): boolean;
131
143
  getUnlockStatus(): UnlockStatus;
132
144
  getCompletedActions(): CompleteOptions[];
145
+ /**
146
+ * Check if user is verified server-side
147
+ * Requires userId to be set in config
148
+ * @returns Promise<boolean> - true if verified, false otherwise
149
+ */
150
+ isVerified(): Promise<boolean>;
151
+ /**
152
+ * Get full verification status from server
153
+ * Requires userId to be set in config
154
+ * @returns Promise<VerificationStatus> - full verification details
155
+ */
156
+ getVerificationStatus(): Promise<VerificationStatus>;
133
157
  on(event: EventType, callback: EventCallback): void;
134
158
  off(event: EventType, callback: EventCallback): void;
135
159
  /**
@@ -139,6 +163,11 @@ declare class FollowGateClient {
139
163
  private getStorageKey;
140
164
  private restoreSession;
141
165
  private saveCompletedActions;
166
+ /**
167
+ * Save completion to server for verification
168
+ * Only works if userId is set in config
169
+ */
170
+ private saveCompletion;
142
171
  private buildIntentUrl;
143
172
  private buildTwitterUrl;
144
173
  private buildBlueskyUrl;
@@ -148,4 +177,4 @@ declare class FollowGateClient {
148
177
  }
149
178
  declare const FollowGate: FollowGateClient;
150
179
 
151
- export { type CompleteOptions, type EventCallback, type EventType, FollowGate, FollowGateClient, type FollowGateConfig, FollowGateError, type Platform, type SocialAction, type TwitterConfig, type UnlockStatus, type UserInfo };
180
+ export { type CompleteOptions, type EventCallback, type EventType, FollowGate, FollowGateClient, type FollowGateConfig, FollowGateError, type Platform, type SocialAction, type TwitterConfig, type UnlockStatus, type UserInfo, type VerificationStatus };
package/dist/index.js CHANGED
@@ -865,7 +865,10 @@ var FollowGateClient = class {
865
865
  platform
866
866
  };
867
867
  if (typeof localStorage !== "undefined") {
868
- localStorage.setItem(this.getStorageKey("followgate_user"), JSON.stringify(this.currentUser));
868
+ localStorage.setItem(
869
+ this.getStorageKey("followgate_user"),
870
+ JSON.stringify(this.currentUser)
871
+ );
869
872
  }
870
873
  if (this.config.debug) {
871
874
  console.log("[FollowGate] Username set:", normalizedUsername);
@@ -959,6 +962,7 @@ var FollowGateClient = class {
959
962
  if (typeof localStorage !== "undefined") {
960
963
  localStorage.setItem(this.getStorageKey("followgate_unlocked"), "true");
961
964
  }
965
+ await this.saveCompletion();
962
966
  await this.trackEvent("gate_unlocked", {
963
967
  username: this.currentUser?.username,
964
968
  actions: this.completedActions
@@ -986,6 +990,90 @@ var FollowGateClient = class {
986
990
  return [...this.completedActions];
987
991
  }
988
992
  // ============================================
993
+ // Server-Side Verification
994
+ // ============================================
995
+ /**
996
+ * Check if user is verified server-side
997
+ * Requires userId to be set in config
998
+ * @returns Promise<boolean> - true if verified, false otherwise
999
+ */
1000
+ async isVerified() {
1001
+ if (!this.config) {
1002
+ throw new Error("[FollowGate] SDK not initialized. Call init() first.");
1003
+ }
1004
+ if (!this.config.userId) {
1005
+ if (this.config.debug) {
1006
+ console.warn("[FollowGate] isVerified() requires userId to be set");
1007
+ }
1008
+ return false;
1009
+ }
1010
+ try {
1011
+ const response = await fetch(
1012
+ `${this.config.apiUrl}/api/v1/completions/verify/${encodeURIComponent(this.config.userId)}`,
1013
+ {
1014
+ headers: {
1015
+ Authorization: `Bearer ${this.config.apiKey}`
1016
+ }
1017
+ }
1018
+ );
1019
+ if (!response.ok) {
1020
+ if (this.config.debug) {
1021
+ console.warn("[FollowGate] Verification check failed:", response.status);
1022
+ }
1023
+ return false;
1024
+ }
1025
+ const data = await response.json();
1026
+ return data.data?.verified === true;
1027
+ } catch (error) {
1028
+ if (this.config.debug) {
1029
+ console.warn("[FollowGate] Verification check error:", error);
1030
+ }
1031
+ return false;
1032
+ }
1033
+ }
1034
+ /**
1035
+ * Get full verification status from server
1036
+ * Requires userId to be set in config
1037
+ * @returns Promise<VerificationStatus> - full verification details
1038
+ */
1039
+ async getVerificationStatus() {
1040
+ if (!this.config) {
1041
+ throw new Error("[FollowGate] SDK not initialized. Call init() first.");
1042
+ }
1043
+ if (!this.config.userId) {
1044
+ return {
1045
+ verified: false,
1046
+ userId: ""
1047
+ };
1048
+ }
1049
+ try {
1050
+ const response = await fetch(
1051
+ `${this.config.apiUrl}/api/v1/completions/verify/${encodeURIComponent(this.config.userId)}`,
1052
+ {
1053
+ headers: {
1054
+ Authorization: `Bearer ${this.config.apiKey}`
1055
+ }
1056
+ }
1057
+ );
1058
+ if (!response.ok) {
1059
+ return {
1060
+ verified: false,
1061
+ userId: this.config.userId
1062
+ };
1063
+ }
1064
+ const data = await response.json();
1065
+ return data.data;
1066
+ } catch (error) {
1067
+ if (this.config.debug) {
1068
+ console.warn("[FollowGate] getVerificationStatus error:", error);
1069
+ }
1070
+ return {
1071
+ verified: false,
1072
+ userId: this.config.userId
1073
+ };
1074
+ }
1075
+ }
1076
+ // ============================================
989
1077
  // Event System
990
1078
  // ============================================
991
1079
  on(event, callback) {
@@ -1012,7 +1100,9 @@ var FollowGateClient = class {
1012
1100
  }
1013
1101
  restoreSession() {
1014
1102
  if (typeof localStorage === "undefined") return;
1015
- const userJson = localStorage.getItem(this.getStorageKey("followgate_user"));
1103
+ const userJson = localStorage.getItem(
1104
+ this.getStorageKey("followgate_user")
1105
+ );
1016
1106
  if (userJson) {
1017
1107
  try {
1018
1108
  this.currentUser = JSON.parse(userJson);
@@ -1020,7 +1110,9 @@ var FollowGateClient = class {
1020
1110
  localStorage.removeItem(this.getStorageKey("followgate_user"));
1021
1111
  }
1022
1112
  }
1023
- const actionsJson = localStorage.getItem(this.getStorageKey("followgate_actions"));
1113
+ const actionsJson = localStorage.getItem(
1114
+ this.getStorageKey("followgate_actions")
1115
+ );
1024
1116
  if (actionsJson) {
1025
1117
  try {
1026
1118
  this.completedActions = JSON.parse(actionsJson);
@@ -1037,6 +1129,43 @@ var FollowGateClient = class {
1037
1129
  );
1038
1130
  }
1039
1131
  }
1132
+ /**
1133
+ * Save completion to server for verification
1134
+ * Only works if userId is set in config
1135
+ */
1136
+ async saveCompletion() {
1137
+ if (!this.config?.userId) {
1138
+ return;
1139
+ }
1140
+ try {
1141
+ const response = await fetch(`${this.config.apiUrl}/api/v1/completions`, {
1142
+ method: "POST",
1143
+ headers: {
1144
+ "Content-Type": "application/json",
1145
+ Authorization: `Bearer ${this.config.apiKey}`
1146
+ },
1147
+ body: JSON.stringify({
1148
+ userId: this.config.userId,
1149
+ platform: this.currentUser?.platform || "twitter",
1150
+ actions: this.completedActions.map((a) => ({
1151
+ action: a.action,
1152
+ target: a.target
1153
+ }))
1154
+ })
1155
+ });
1156
+ if (this.config.debug) {
1157
+ if (response.ok) {
1158
+ console.log("[FollowGate] Completion saved to server");
1159
+ } else {
1160
+ console.warn("[FollowGate] Failed to save completion:", response.status);
1161
+ }
1162
+ }
1163
+ } catch (error) {
1164
+ if (this.config.debug) {
1165
+ console.warn("[FollowGate] Failed to save completion:", error);
1166
+ }
1167
+ }
1168
+ }
1040
1169
  buildIntentUrl(options) {
1041
1170
  const { platform, action, target } = options;
1042
1171
  switch (platform) {
package/dist/index.mjs CHANGED
@@ -839,7 +839,10 @@ var FollowGateClient = class {
839
839
  platform
840
840
  };
841
841
  if (typeof localStorage !== "undefined") {
842
- localStorage.setItem(this.getStorageKey("followgate_user"), JSON.stringify(this.currentUser));
842
+ localStorage.setItem(
843
+ this.getStorageKey("followgate_user"),
844
+ JSON.stringify(this.currentUser)
845
+ );
843
846
  }
844
847
  if (this.config.debug) {
845
848
  console.log("[FollowGate] Username set:", normalizedUsername);
@@ -933,6 +936,7 @@ var FollowGateClient = class {
933
936
  if (typeof localStorage !== "undefined") {
934
937
  localStorage.setItem(this.getStorageKey("followgate_unlocked"), "true");
935
938
  }
939
+ await this.saveCompletion();
936
940
  await this.trackEvent("gate_unlocked", {
937
941
  username: this.currentUser?.username,
938
942
  actions: this.completedActions
@@ -960,6 +964,90 @@ var FollowGateClient = class {
960
964
  return [...this.completedActions];
961
965
  }
962
966
  // ============================================
967
+ // Server-Side Verification
968
+ // ============================================
969
+ /**
970
+ * Check if user is verified server-side
971
+ * Requires userId to be set in config
972
+ * @returns Promise<boolean> - true if verified, false otherwise
973
+ */
974
+ async isVerified() {
975
+ if (!this.config) {
976
+ throw new Error("[FollowGate] SDK not initialized. Call init() first.");
977
+ }
978
+ if (!this.config.userId) {
979
+ if (this.config.debug) {
980
+ console.warn("[FollowGate] isVerified() requires userId to be set");
981
+ }
982
+ return false;
983
+ }
984
+ try {
985
+ const response = await fetch(
986
+ `${this.config.apiUrl}/api/v1/completions/verify/${encodeURIComponent(this.config.userId)}`,
987
+ {
988
+ headers: {
989
+ Authorization: `Bearer ${this.config.apiKey}`
990
+ }
991
+ }
992
+ );
993
+ if (!response.ok) {
994
+ if (this.config.debug) {
995
+ console.warn("[FollowGate] Verification check failed:", response.status);
996
+ }
997
+ return false;
998
+ }
999
+ const data = await response.json();
1000
+ return data.data?.verified === true;
1001
+ } catch (error) {
1002
+ if (this.config.debug) {
1003
+ console.warn("[FollowGate] Verification check error:", error);
1004
+ }
1005
+ return false;
1006
+ }
1007
+ }
1008
+ /**
1009
+ * Get full verification status from server
1010
+ * Requires userId to be set in config
1011
+ * @returns Promise<VerificationStatus> - full verification details
1012
+ */
1013
+ async getVerificationStatus() {
1014
+ if (!this.config) {
1015
+ throw new Error("[FollowGate] SDK not initialized. Call init() first.");
1016
+ }
1017
+ if (!this.config.userId) {
1018
+ return {
1019
+ verified: false,
1020
+ userId: ""
1021
+ };
1022
+ }
1023
+ try {
1024
+ const response = await fetch(
1025
+ `${this.config.apiUrl}/api/v1/completions/verify/${encodeURIComponent(this.config.userId)}`,
1026
+ {
1027
+ headers: {
1028
+ Authorization: `Bearer ${this.config.apiKey}`
1029
+ }
1030
+ }
1031
+ );
1032
+ if (!response.ok) {
1033
+ return {
1034
+ verified: false,
1035
+ userId: this.config.userId
1036
+ };
1037
+ }
1038
+ const data = await response.json();
1039
+ return data.data;
1040
+ } catch (error) {
1041
+ if (this.config.debug) {
1042
+ console.warn("[FollowGate] getVerificationStatus error:", error);
1043
+ }
1044
+ return {
1045
+ verified: false,
1046
+ userId: this.config.userId
1047
+ };
1048
+ }
1049
+ }
1050
+ // ============================================
963
1051
  // Event System
964
1052
  // ============================================
965
1053
  on(event, callback) {
@@ -986,7 +1074,9 @@ var FollowGateClient = class {
986
1074
  }
987
1075
  restoreSession() {
988
1076
  if (typeof localStorage === "undefined") return;
989
- const userJson = localStorage.getItem(this.getStorageKey("followgate_user"));
1077
+ const userJson = localStorage.getItem(
1078
+ this.getStorageKey("followgate_user")
1079
+ );
990
1080
  if (userJson) {
991
1081
  try {
992
1082
  this.currentUser = JSON.parse(userJson);
@@ -994,7 +1084,9 @@ var FollowGateClient = class {
994
1084
  localStorage.removeItem(this.getStorageKey("followgate_user"));
995
1085
  }
996
1086
  }
997
- const actionsJson = localStorage.getItem(this.getStorageKey("followgate_actions"));
1087
+ const actionsJson = localStorage.getItem(
1088
+ this.getStorageKey("followgate_actions")
1089
+ );
998
1090
  if (actionsJson) {
999
1091
  try {
1000
1092
  this.completedActions = JSON.parse(actionsJson);
@@ -1011,6 +1103,43 @@ var FollowGateClient = class {
1011
1103
  );
1012
1104
  }
1013
1105
  }
1106
+ /**
1107
+ * Save completion to server for verification
1108
+ * Only works if userId is set in config
1109
+ */
1110
+ async saveCompletion() {
1111
+ if (!this.config?.userId) {
1112
+ return;
1113
+ }
1114
+ try {
1115
+ const response = await fetch(`${this.config.apiUrl}/api/v1/completions`, {
1116
+ method: "POST",
1117
+ headers: {
1118
+ "Content-Type": "application/json",
1119
+ Authorization: `Bearer ${this.config.apiKey}`
1120
+ },
1121
+ body: JSON.stringify({
1122
+ userId: this.config.userId,
1123
+ platform: this.currentUser?.platform || "twitter",
1124
+ actions: this.completedActions.map((a) => ({
1125
+ action: a.action,
1126
+ target: a.target
1127
+ }))
1128
+ })
1129
+ });
1130
+ if (this.config.debug) {
1131
+ if (response.ok) {
1132
+ console.log("[FollowGate] Completion saved to server");
1133
+ } else {
1134
+ console.warn("[FollowGate] Failed to save completion:", response.status);
1135
+ }
1136
+ }
1137
+ } catch (error) {
1138
+ if (this.config.debug) {
1139
+ console.warn("[FollowGate] Failed to save completion:", error);
1140
+ }
1141
+ }
1142
+ }
1014
1143
  buildIntentUrl(options) {
1015
1144
  const { platform, action, target } = options;
1016
1145
  switch (platform) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@followgate/js",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "FollowGate SDK - Grow your audience with every download. Require social actions (follow, repost) before users can access your app.",
5
5
  "author": "FollowGate <hello@followgate.app>",
6
6
  "homepage": "https://followgate.app",