founder-schema 0.8.0 → 0.10.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 (77) hide show
  1. package/README.md +68 -0
  2. package/dist/access/capabilities.d.ts +34 -0
  3. package/dist/access/capabilities.d.ts.map +1 -0
  4. package/dist/access/capabilities.js +35 -0
  5. package/dist/access/capabilities.js.map +1 -0
  6. package/dist/access/permissions.d.ts +20 -1
  7. package/dist/access/permissions.d.ts.map +1 -1
  8. package/dist/access/permissions.js +4 -0
  9. package/dist/access/permissions.js.map +1 -1
  10. package/dist/core/common.d.ts +8 -0
  11. package/dist/core/common.d.ts.map +1 -1
  12. package/dist/core/common.js +10 -1
  13. package/dist/core/common.js.map +1 -1
  14. package/dist/core/errors.d.ts +33 -1
  15. package/dist/core/errors.d.ts.map +1 -1
  16. package/dist/core/errors.js +57 -0
  17. package/dist/core/errors.js.map +1 -1
  18. package/dist/core/limits.d.ts +12 -0
  19. package/dist/core/limits.d.ts.map +1 -1
  20. package/dist/core/limits.js +13 -0
  21. package/dist/core/limits.js.map +1 -1
  22. package/dist/core/responses.d.ts +47 -0
  23. package/dist/core/responses.d.ts.map +1 -1
  24. package/dist/core/responses.js +7 -0
  25. package/dist/core/responses.js.map +1 -1
  26. package/dist/core/validation.d.ts +6 -0
  27. package/dist/core/validation.d.ts.map +1 -1
  28. package/dist/core/validation.js +6 -0
  29. package/dist/core/validation.js.map +1 -1
  30. package/dist/domains/audit/audit.d.ts +47 -3
  31. package/dist/domains/audit/audit.d.ts.map +1 -1
  32. package/dist/domains/audit/audit.js +40 -4
  33. package/dist/domains/audit/audit.js.map +1 -1
  34. package/dist/domains/communities/communities.d.ts +1537 -0
  35. package/dist/domains/communities/communities.d.ts.map +1 -0
  36. package/dist/domains/communities/communities.js +464 -0
  37. package/dist/domains/communities/communities.js.map +1 -0
  38. package/dist/domains/contributions/contributions.d.ts +53 -4
  39. package/dist/domains/contributions/contributions.d.ts.map +1 -1
  40. package/dist/domains/contributions/contributions.js +39 -5
  41. package/dist/domains/contributions/contributions.js.map +1 -1
  42. package/dist/domains/feed/feed.d.ts +479 -0
  43. package/dist/domains/feed/feed.d.ts.map +1 -1
  44. package/dist/domains/feed/feed.js +45 -1
  45. package/dist/domains/feed/feed.js.map +1 -1
  46. package/dist/domains/media/media.d.ts +7 -1
  47. package/dist/domains/media/media.d.ts.map +1 -1
  48. package/dist/domains/media/media.js +5 -0
  49. package/dist/domains/media/media.js.map +1 -1
  50. package/dist/domains/members/members.d.ts +6 -0
  51. package/dist/domains/members/members.d.ts.map +1 -1
  52. package/dist/domains/members/members.js +5 -0
  53. package/dist/domains/members/members.js.map +1 -1
  54. package/dist/domains/moderation/moderation.d.ts +4 -4
  55. package/dist/domains/notifications/notifications.d.ts +82 -2
  56. package/dist/domains/notifications/notifications.d.ts.map +1 -1
  57. package/dist/domains/notifications/notifications.js +45 -0
  58. package/dist/domains/notifications/notifications.js.map +1 -1
  59. package/dist/domains/onboarding/onboarding.d.ts +11 -11
  60. package/dist/domains/onboarding/onboarding.d.ts.map +1 -1
  61. package/dist/domains/onboarding/onboarding.js +2 -2
  62. package/dist/domains/onboarding/onboarding.js.map +1 -1
  63. package/dist/domains/profile/founder-profile.d.ts +60 -4
  64. package/dist/domains/profile/founder-profile.d.ts.map +1 -1
  65. package/dist/domains/profile/founder-profile.js +66 -5
  66. package/dist/domains/profile/founder-profile.js.map +1 -1
  67. package/dist/domains/team/team.d.ts +22 -0
  68. package/dist/domains/team/team.d.ts.map +1 -1
  69. package/dist/domains/team/team.js +22 -0
  70. package/dist/domains/team/team.js.map +1 -1
  71. package/dist/domains/uploads/uploads.d.ts +1 -0
  72. package/dist/domains/uploads/uploads.d.ts.map +1 -1
  73. package/dist/index.d.ts +8 -6
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +9 -6
  76. package/dist/index.js.map +1 -1
  77. package/package.json +1 -1
@@ -27,7 +27,8 @@
27
27
  *
28
28
  * Routes (all signed in):
29
29
  * GET /api/v1/contributions/me paginationQuerySchema -> contributionPageSchema
30
- * GET /api/v1/contributions/leaderboard -> contributionLeaderboardSchema
30
+ * GET /api/v1/contributions/leaderboard contributionLeaderboardQuerySchema
31
+ * -> contributionLeaderboardSchema
31
32
  *
32
33
  * The score itself is on the profile and the card (`contributionSummarySchema`).
33
34
  *
@@ -130,7 +131,27 @@ export declare const contributionPageSchema: z.ZodObject<{
130
131
  nextCursor: z.ZodNullable<z.ZodString>;
131
132
  }, z.core.$strip>;
132
133
  export type ContributionPage = z.infer<typeof contributionPageSchema>;
133
- /** One row of the top helpers of the last `CONTRIBUTION_RECENT_DAYS` days. */
134
+ /**
135
+ * Which points a board ranks by (0.10.0): `recent`, the last
136
+ * `CONTRIBUTION_RECENT_DAYS` days (the default, so a newer member can still
137
+ * climb), or `all`, every point ever earned. Every board is GLOBAL: one list
138
+ * for the whole circle (the user's ruling, 2026-10-11).
139
+ */
140
+ export declare const CONTRIBUTION_PERIOD_VALUES: readonly ["recent", "all"];
141
+ export declare const contributionPeriodSchema: z.ZodEnum<{
142
+ all: "all";
143
+ recent: "recent";
144
+ }>;
145
+ export type ContributionPeriod = z.infer<typeof contributionPeriodSchema>;
146
+ /** `GET /api/v1/contributions/leaderboard?period=` (0.10.0). A query string, so the default is the server's. */
147
+ export declare const contributionLeaderboardQuerySchema: z.ZodObject<{
148
+ period: z.ZodDefault<z.ZodEnum<{
149
+ all: "all";
150
+ recent: "recent";
151
+ }>>;
152
+ }, z.core.$strip>;
153
+ export type ContributionLeaderboardQuery = z.infer<typeof contributionLeaderboardQuerySchema>;
154
+ /** One row of a board: its rank, who, and their points in the period. */
134
155
  export declare const contributionLeaderEntrySchema: z.ZodObject<{
135
156
  rank: z.ZodNumber;
136
157
  member: z.ZodObject<{
@@ -144,10 +165,34 @@ export declare const contributionLeaderEntrySchema: z.ZodObject<{
144
165
  }, z.core.$strip>;
145
166
  export type ContributionLeaderEntry = z.infer<typeof contributionLeaderEntrySchema>;
146
167
  /**
147
- * Top helpers, best first, at most `CONTRIBUTION_LEADERBOARD_SIZE`. Recomputed
148
- * hourly; `computedAt` says when (null before the first run).
168
+ * The signed-in member's own place on the board (0.10.0), on its first page or
169
+ * not. Ranked in the SAME order, among the SAME members, as the list, so their
170
+ * rank is always their position. `rank` is null while they are not on the
171
+ * board: no points in the period (a board ranks help given, and nobody is
172
+ * 40th for nothing), or a profile no longer listed. `points` are theirs either
173
+ * way.
174
+ */
175
+ export declare const contributionYouSchema: z.ZodObject<{
176
+ rank: z.ZodNullable<z.ZodNumber>;
177
+ points: z.ZodNumber;
178
+ }, z.core.$strip>;
179
+ export type ContributionYou = z.infer<typeof contributionYouSchema>;
180
+ /**
181
+ * Top helpers, best first, at most `CONTRIBUTION_LEADERBOARD_SIZE`. Read from
182
+ * the live counters, which move the moment help is recognised; only points
183
+ * leaving the `recent` window wait for the hourly recount. `computedAt` is the
184
+ * latest recount among the rows shown (null before the first run, and for an
185
+ * empty board).
186
+ *
187
+ * `period` and `you` (0.10.0) are OPTIONAL on read, so this client keeps
188
+ * parsing a server from before them: without `you` the board simply shows no
189
+ * row of the member's own.
149
190
  */
150
191
  export declare const contributionLeaderboardSchema: z.ZodObject<{
192
+ period: z.ZodOptional<z.ZodEnum<{
193
+ all: "all";
194
+ recent: "recent";
195
+ }>>;
151
196
  items: z.ZodArray<z.ZodObject<{
152
197
  rank: z.ZodNumber;
153
198
  member: z.ZodObject<{
@@ -159,6 +204,10 @@ export declare const contributionLeaderboardSchema: z.ZodObject<{
159
204
  }, z.core.$strip>;
160
205
  points: z.ZodNumber;
161
206
  }, z.core.$strip>>;
207
+ you: z.ZodOptional<z.ZodObject<{
208
+ rank: z.ZodNullable<z.ZodNumber>;
209
+ points: z.ZodNumber;
210
+ }, z.core.$strip>>;
162
211
  computedAt: z.ZodNullable<z.ZodISODateTime>;
163
212
  }, z.core.$strip>;
164
213
  export type ContributionLeaderboard = z.infer<typeof contributionLeaderboardSchema>;
@@ -1 +1 @@
1
- {"version":3,"file":"contributions.d.ts","sourceRoot":"","sources":["../../../src/domains/contributions/contributions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,4CAA4C;AAC5C,eAAO,MAAM,wBAAwB,uFAK3B,CAAC;AACX,eAAO,MAAM,sBAAsB;;;;;EAAmC,CAAC;AACvE;;;GAGG;AACH,eAAO,MAAM,gCAAgC,gEAG5C,CAAC;AACF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;CAKqB,CAAC;AAEtD,4CAA4C;AAC5C,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoBlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAA2C,CAAC;AAC/E,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,8EAA8E;AAC9E,eAAO,MAAM,6BAA6B;;;;;;;;;;iBAKxC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF;;;GAGG;AACH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;iBAGxC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC"}
1
+ {"version":3,"file":"contributions.d.ts","sourceRoot":"","sources":["../../../src/domains/contributions/contributions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,4CAA4C;AAC5C,eAAO,MAAM,wBAAwB,uFAK3B,CAAC;AACX,eAAO,MAAM,sBAAsB;;;;;EAAmC,CAAC;AACvE;;;GAGG;AACH,eAAO,MAAM,gCAAgC,gEAG5C,CAAC;AACF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;GAGG;AACH,eAAO,MAAM,mBAAmB;;;;;CAKqB,CAAC;AAEtD,4CAA4C;AAC5C,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAoBlC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAA2C,CAAC;AAC/E,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;;GAKG;AACH,eAAO,MAAM,0BAA0B,4BAA6B,CAAC;AACrE,eAAO,MAAM,wBAAwB;;;EAAqC,CAAC;AAC3E,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,gHAAgH;AAChH,eAAO,MAAM,kCAAkC;;;;;iBAE7C,CAAC;AACH,MAAM,MAAM,4BAA4B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kCAAkC,CAAC,CAAC;AAE9F,yEAAyE;AACzE,eAAO,MAAM,6BAA6B;;;;;;;;;;iBAKxC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC;AAEpF;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB;;;iBAGhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;;;;;;;;GAUG;AACH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;iBAKxC,CAAC;AACH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC"}
@@ -27,7 +27,8 @@
27
27
  *
28
28
  * Routes (all signed in):
29
29
  * GET /api/v1/contributions/me paginationQuerySchema -> contributionPageSchema
30
- * GET /api/v1/contributions/leaderboard -> contributionLeaderboardSchema
30
+ * GET /api/v1/contributions/leaderboard contributionLeaderboardQuerySchema
31
+ * -> contributionLeaderboardSchema
31
32
  *
32
33
  * The score itself is on the profile and the card (`contributionSummarySchema`).
33
34
  *
@@ -85,19 +86,52 @@ export const contributionEntrySchema = z.object({
85
86
  createdAt: z.iso.datetime(),
86
87
  });
87
88
  export const contributionPageSchema = paginatedSchema(contributionEntrySchema);
88
- /** One row of the top helpers of the last `CONTRIBUTION_RECENT_DAYS` days. */
89
+ /**
90
+ * Which points a board ranks by (0.10.0): `recent`, the last
91
+ * `CONTRIBUTION_RECENT_DAYS` days (the default, so a newer member can still
92
+ * climb), or `all`, every point ever earned. Every board is GLOBAL: one list
93
+ * for the whole circle (the user's ruling, 2026-10-11).
94
+ */
95
+ export const CONTRIBUTION_PERIOD_VALUES = ['recent', 'all'];
96
+ export const contributionPeriodSchema = z.enum(CONTRIBUTION_PERIOD_VALUES);
97
+ /** `GET /api/v1/contributions/leaderboard?period=` (0.10.0). A query string, so the default is the server's. */
98
+ export const contributionLeaderboardQuerySchema = z.object({
99
+ period: contributionPeriodSchema.default('recent'),
100
+ });
101
+ /** One row of a board: its rank, who, and their points in the period. */
89
102
  export const contributionLeaderEntrySchema = z.object({
90
103
  rank: z.number().int().positive(),
91
104
  member: memberBylineSchema,
92
- /** Points earned in the window. */
105
+ /** Points earned in the period. */
93
106
  points: z.number().int().positive(),
94
107
  });
95
108
  /**
96
- * Top helpers, best first, at most `CONTRIBUTION_LEADERBOARD_SIZE`. Recomputed
97
- * hourly; `computedAt` says when (null before the first run).
109
+ * The signed-in member's own place on the board (0.10.0), on its first page or
110
+ * not. Ranked in the SAME order, among the SAME members, as the list, so their
111
+ * rank is always their position. `rank` is null while they are not on the
112
+ * board: no points in the period (a board ranks help given, and nobody is
113
+ * 40th for nothing), or a profile no longer listed. `points` are theirs either
114
+ * way.
115
+ */
116
+ export const contributionYouSchema = z.object({
117
+ rank: z.number().int().positive().nullable(),
118
+ points: z.number().int().nonnegative(),
119
+ });
120
+ /**
121
+ * Top helpers, best first, at most `CONTRIBUTION_LEADERBOARD_SIZE`. Read from
122
+ * the live counters, which move the moment help is recognised; only points
123
+ * leaving the `recent` window wait for the hourly recount. `computedAt` is the
124
+ * latest recount among the rows shown (null before the first run, and for an
125
+ * empty board).
126
+ *
127
+ * `period` and `you` (0.10.0) are OPTIONAL on read, so this client keeps
128
+ * parsing a server from before them: without `you` the board simply shows no
129
+ * row of the member's own.
98
130
  */
99
131
  export const contributionLeaderboardSchema = z.object({
132
+ period: contributionPeriodSchema.optional(),
100
133
  items: z.array(contributionLeaderEntrySchema),
134
+ you: contributionYouSchema.optional(),
101
135
  computedAt: z.iso.datetime().nullable(),
102
136
  });
103
137
  //# sourceMappingURL=contributions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"contributions.js","sourceRoot":"","sources":["../../../src/domains/contributions/contributions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAEnE,4CAA4C;AAC5C,MAAM,CAAC,MAAM,wBAAwB,GAAG;IACtC,kBAAkB;IAClB,kBAAkB;IAClB,gBAAgB;IAChB,gBAAgB;CACR,CAAC;AACX,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;AACvE;;;GAGG;AACH,MAAM,CAAC,MAAM,gCAAgC,GAAG,wBAAwB,CAAC,MAAM,CAC7E,CAAC,CAAC,EAA6E,EAAE,CAC/E,CAAC,KAAK,gBAAgB,CACzB,CAAC;AAGF;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,gBAAgB,EAAE,EAAE;IACpB,gBAAgB,EAAE,EAAE;IACpB,cAAc,EAAE,CAAC;IACjB,cAAc,EAAE,CAAC;CACkC,CAAC;AAEtD,4CAA4C;AAC5C,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,EAAE,EAAE,cAAc;IAClB,IAAI,EAAE,sBAAsB;IAC5B,gFAAgF;IAChF,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACnC;;;OAGG;IACH,KAAK,EAAE,kBAAkB,CAAC,QAAQ,EAAE;IACpC,iDAAiD;IACjD,OAAO,EAAE,iBAAiB,CAAC,QAAQ,EAAE;IACrC;;;OAGG;IACH,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,iBAAiB,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC,QAAQ,EAAE;IAC7E,SAAS,EAAE,cAAc,CAAC,QAAQ,EAAE;IACpC,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE;IACnC,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE;CAC5B,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,sBAAsB,GAAG,eAAe,CAAC,uBAAuB,CAAC,CAAC;AAG/E,8EAA8E;AAC9E,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC,MAAM,CAAC;IACpD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACjC,MAAM,EAAE,kBAAkB;IAC1B,mCAAmC;IACnC,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;CACpC,CAAC,CAAC;AAGH;;;GAGG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAAC,CAAC,MAAM,CAAC;IACpD,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,6BAA6B,CAAC;IAC7C,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACxC,CAAC,CAAC","sourcesContent":["/**\n * founder-schema — Contribution: the help a member gives, recognised\n *\n * The score counts HELP GIVEN, and only what OTHER members recognise:\n *\n * feedback_helpful a product's team marks your comment \"this helped\" +10\n * tester_confirmed a product's team confirms you tested their product +15\n * review_helpful another member finds your review helpful +2\n * (at most `REVIEW_HELPFUL_POINTS_MAX` per review)\n *\n * `review_written` is still listed so older rows and clients parse, but it is\n * NEVER awarded (0.7.0): writing a review is your own act, and the score\n * counts only what others recognise. A review earns through Helpful votes.\n *\n * Nothing a member does for their OWN product counts, and nothing their own\n * product receives, so the score cannot be bought with a popular product.\n *\n * Against farming: one member can earn at most `CONTRIBUTION_PAIR_POINTS_MAX`\n * points from any ONE other member over 30 days, so two founders cannot trade\n * Helpful marks into a high score.\n *\n * EVERY POINT IS A ROW (the ledger), so a score can be explained (\"how did I\n * earn this?\"), checked and re-tuned. A point stands while what earned it\n * stands: unmarking, withdrawing a vote, deleting the review or comment, or\n * staff removing it takes the point back. An hourly job recounts every score\n * from the ledger, so a total can never drift for long.\n *\n * Routes (all signed in):\n * GET /api/v1/contributions/me paginationQuerySchema -> contributionPageSchema\n * GET /api/v1/contributions/leaderboard -> contributionLeaderboardSchema\n *\n * The score itself is on the profile and the card (`contributionSummarySchema`).\n *\n * @module founder-schema/domains/contributions\n */\n\nimport { z } from 'zod';\nimport { objectIdSchema } from '../../core/common.js';\nimport { paginatedSchema } from '../../core/responses.js';\nimport { contentKindSchema } from '../feed/feed.js';\nimport { productChipSchema } from '../products/products.js';\nimport { memberBylineSchema } from '../profile/founder-profile.js';\n\n/** ⚠ Stored per ledger row: append only. */\nexport const CONTRIBUTION_KIND_VALUES = [\n 'feedback_helpful',\n 'tester_confirmed',\n 'review_helpful',\n 'review_written',\n] as const;\nexport const contributionKindSchema = z.enum(CONTRIBUTION_KIND_VALUES);\n/**\n * The kinds that are actually awarded (0.7.0): every kind but `review_written`,\n * which is never awarded any more. \"How points are earned\" lists these.\n */\nexport const CONTRIBUTION_AWARDED_KIND_VALUES = CONTRIBUTION_KIND_VALUES.filter(\n (k): k is Exclude<(typeof CONTRIBUTION_KIND_VALUES)[number], 'review_written'> =>\n k !== 'review_written',\n);\nexport type ContributionKind = z.infer<typeof contributionKindSchema>;\n\n/**\n * Points per kind. Exhaustive by type. Changing a number changes FUTURE rows;\n * the hourly recount sums what each row was actually awarded.\n */\nexport const CONTRIBUTION_POINTS = {\n feedback_helpful: 10,\n tester_confirmed: 15,\n review_helpful: 2,\n review_written: 3,\n} as const satisfies Record<ContributionKind, number>;\n\n/** One line of \"how I earned my points\". */\nexport const contributionEntrySchema = z.object({\n id: objectIdSchema,\n kind: contributionKindSchema,\n /** What it was actually awarded: below the kind's points when a cap applied. */\n points: z.number().int().positive(),\n /**\n * Who recognised it; null for a deleted account, and for `review_helpful`\n * (a Helpful vote is anonymous: the review shows only a count).\n */\n actor: memberBylineSchema.nullable(),\n /** The product it was about, while it exists. */\n product: productChipSchema.optional(),\n /**\n * What earned it, so the row opens it: the post or poll and the comment for\n * `feedback_helpful`, the review for `review_helpful`.\n */\n content: z.object({ kind: contentKindSchema, id: objectIdSchema }).optional(),\n commentId: objectIdSchema.optional(),\n reviewId: objectIdSchema.optional(),\n createdAt: z.iso.datetime(),\n});\nexport type ContributionEntry = z.infer<typeof contributionEntrySchema>;\n\nexport const contributionPageSchema = paginatedSchema(contributionEntrySchema);\nexport type ContributionPage = z.infer<typeof contributionPageSchema>;\n\n/** One row of the top helpers of the last `CONTRIBUTION_RECENT_DAYS` days. */\nexport const contributionLeaderEntrySchema = z.object({\n rank: z.number().int().positive(),\n member: memberBylineSchema,\n /** Points earned in the window. */\n points: z.number().int().positive(),\n});\nexport type ContributionLeaderEntry = z.infer<typeof contributionLeaderEntrySchema>;\n\n/**\n * Top helpers, best first, at most `CONTRIBUTION_LEADERBOARD_SIZE`. Recomputed\n * hourly; `computedAt` says when (null before the first run).\n */\nexport const contributionLeaderboardSchema = z.object({\n items: z.array(contributionLeaderEntrySchema),\n computedAt: z.iso.datetime().nullable(),\n});\nexport type ContributionLeaderboard = z.infer<typeof contributionLeaderboardSchema>;\n"]}
1
+ {"version":3,"file":"contributions.js","sourceRoot":"","sources":["../../../src/domains/contributions/contributions.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema — Contribution: the help a member gives, recognised\n *\n * The score counts HELP GIVEN, and only what OTHER members recognise:\n *\n * feedback_helpful a product's team marks your comment \"this helped\" +10\n * tester_confirmed a product's team confirms you tested their product +15\n * review_helpful another member finds your review helpful +2\n * (at most `REVIEW_HELPFUL_POINTS_MAX` per review)\n *\n * `review_written` is still listed so older rows and clients parse, but it is\n * NEVER awarded (0.7.0): writing a review is your own act, and the score\n * counts only what others recognise. A review earns through Helpful votes.\n *\n * Nothing a member does for their OWN product counts, and nothing their own\n * product receives, so the score cannot be bought with a popular product.\n *\n * Against farming: one member can earn at most `CONTRIBUTION_PAIR_POINTS_MAX`\n * points from any ONE other member over 30 days, so two founders cannot trade\n * Helpful marks into a high score.\n *\n * EVERY POINT IS A ROW (the ledger), so a score can be explained (\"how did I\n * earn this?\"), checked and re-tuned. A point stands while what earned it\n * stands: unmarking, withdrawing a vote, deleting the review or comment, or\n * staff removing it takes the point back. An hourly job recounts every score\n * from the ledger, so a total can never drift for long.\n *\n * Routes (all signed in):\n * GET /api/v1/contributions/me paginationQuerySchema -> contributionPageSchema\n * GET /api/v1/contributions/leaderboard contributionLeaderboardQuerySchema\n * -> contributionLeaderboardSchema\n *\n * The score itself is on the profile and the card (`contributionSummarySchema`).\n *\n * @module founder-schema/domains/contributions\n */\n\nimport { z } from 'zod';\nimport { objectIdSchema } from '../../core/common.js';\nimport { paginatedSchema } from '../../core/responses.js';\nimport { contentKindSchema } from '../feed/feed.js';\nimport { productChipSchema } from '../products/products.js';\nimport { memberBylineSchema } from '../profile/founder-profile.js';\n\n/** ⚠ Stored per ledger row: append only. */\nexport const CONTRIBUTION_KIND_VALUES = [\n 'feedback_helpful',\n 'tester_confirmed',\n 'review_helpful',\n 'review_written',\n] as const;\nexport const contributionKindSchema = z.enum(CONTRIBUTION_KIND_VALUES);\n/**\n * The kinds that are actually awarded (0.7.0): every kind but `review_written`,\n * which is never awarded any more. \"How points are earned\" lists these.\n */\nexport const CONTRIBUTION_AWARDED_KIND_VALUES = CONTRIBUTION_KIND_VALUES.filter(\n (k): k is Exclude<(typeof CONTRIBUTION_KIND_VALUES)[number], 'review_written'> =>\n k !== 'review_written',\n);\nexport type ContributionKind = z.infer<typeof contributionKindSchema>;\n\n/**\n * Points per kind. Exhaustive by type. Changing a number changes FUTURE rows;\n * the hourly recount sums what each row was actually awarded.\n */\nexport const CONTRIBUTION_POINTS = {\n feedback_helpful: 10,\n tester_confirmed: 15,\n review_helpful: 2,\n review_written: 3,\n} as const satisfies Record<ContributionKind, number>;\n\n/** One line of \"how I earned my points\". */\nexport const contributionEntrySchema = z.object({\n id: objectIdSchema,\n kind: contributionKindSchema,\n /** What it was actually awarded: below the kind's points when a cap applied. */\n points: z.number().int().positive(),\n /**\n * Who recognised it; null for a deleted account, and for `review_helpful`\n * (a Helpful vote is anonymous: the review shows only a count).\n */\n actor: memberBylineSchema.nullable(),\n /** The product it was about, while it exists. */\n product: productChipSchema.optional(),\n /**\n * What earned it, so the row opens it: the post or poll and the comment for\n * `feedback_helpful`, the review for `review_helpful`.\n */\n content: z.object({ kind: contentKindSchema, id: objectIdSchema }).optional(),\n commentId: objectIdSchema.optional(),\n reviewId: objectIdSchema.optional(),\n createdAt: z.iso.datetime(),\n});\nexport type ContributionEntry = z.infer<typeof contributionEntrySchema>;\n\nexport const contributionPageSchema = paginatedSchema(contributionEntrySchema);\nexport type ContributionPage = z.infer<typeof contributionPageSchema>;\n\n/**\n * Which points a board ranks by (0.10.0): `recent`, the last\n * `CONTRIBUTION_RECENT_DAYS` days (the default, so a newer member can still\n * climb), or `all`, every point ever earned. Every board is GLOBAL: one list\n * for the whole circle (the user's ruling, 2026-10-11).\n */\nexport const CONTRIBUTION_PERIOD_VALUES = ['recent', 'all'] as const;\nexport const contributionPeriodSchema = z.enum(CONTRIBUTION_PERIOD_VALUES);\nexport type ContributionPeriod = z.infer<typeof contributionPeriodSchema>;\n\n/** `GET /api/v1/contributions/leaderboard?period=` (0.10.0). A query string, so the default is the server's. */\nexport const contributionLeaderboardQuerySchema = z.object({\n period: contributionPeriodSchema.default('recent'),\n});\nexport type ContributionLeaderboardQuery = z.infer<typeof contributionLeaderboardQuerySchema>;\n\n/** One row of a board: its rank, who, and their points in the period. */\nexport const contributionLeaderEntrySchema = z.object({\n rank: z.number().int().positive(),\n member: memberBylineSchema,\n /** Points earned in the period. */\n points: z.number().int().positive(),\n});\nexport type ContributionLeaderEntry = z.infer<typeof contributionLeaderEntrySchema>;\n\n/**\n * The signed-in member's own place on the board (0.10.0), on its first page or\n * not. Ranked in the SAME order, among the SAME members, as the list, so their\n * rank is always their position. `rank` is null while they are not on the\n * board: no points in the period (a board ranks help given, and nobody is\n * 40th for nothing), or a profile no longer listed. `points` are theirs either\n * way.\n */\nexport const contributionYouSchema = z.object({\n rank: z.number().int().positive().nullable(),\n points: z.number().int().nonnegative(),\n});\nexport type ContributionYou = z.infer<typeof contributionYouSchema>;\n\n/**\n * Top helpers, best first, at most `CONTRIBUTION_LEADERBOARD_SIZE`. Read from\n * the live counters, which move the moment help is recognised; only points\n * leaving the `recent` window wait for the hourly recount. `computedAt` is the\n * latest recount among the rows shown (null before the first run, and for an\n * empty board).\n *\n * `period` and `you` (0.10.0) are OPTIONAL on read, so this client keeps\n * parsing a server from before them: without `you` the board simply shows no\n * row of the member's own.\n */\nexport const contributionLeaderboardSchema = z.object({\n period: contributionPeriodSchema.optional(),\n items: z.array(contributionLeaderEntrySchema),\n you: contributionYouSchema.optional(),\n computedAt: z.iso.datetime().nullable(),\n});\nexport type ContributionLeaderboard = z.infer<typeof contributionLeaderboardSchema>;\n"]}