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.
- package/README.md +68 -0
- package/dist/access/capabilities.d.ts +34 -0
- package/dist/access/capabilities.d.ts.map +1 -0
- package/dist/access/capabilities.js +35 -0
- package/dist/access/capabilities.js.map +1 -0
- package/dist/access/permissions.d.ts +20 -1
- package/dist/access/permissions.d.ts.map +1 -1
- package/dist/access/permissions.js +4 -0
- package/dist/access/permissions.js.map +1 -1
- package/dist/core/common.d.ts +8 -0
- package/dist/core/common.d.ts.map +1 -1
- package/dist/core/common.js +10 -1
- package/dist/core/common.js.map +1 -1
- package/dist/core/errors.d.ts +33 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +57 -0
- package/dist/core/errors.js.map +1 -1
- package/dist/core/limits.d.ts +12 -0
- package/dist/core/limits.d.ts.map +1 -1
- package/dist/core/limits.js +13 -0
- package/dist/core/limits.js.map +1 -1
- package/dist/core/responses.d.ts +47 -0
- package/dist/core/responses.d.ts.map +1 -1
- package/dist/core/responses.js +7 -0
- package/dist/core/responses.js.map +1 -1
- package/dist/core/validation.d.ts +6 -0
- package/dist/core/validation.d.ts.map +1 -1
- package/dist/core/validation.js +6 -0
- package/dist/core/validation.js.map +1 -1
- package/dist/domains/audit/audit.d.ts +47 -3
- package/dist/domains/audit/audit.d.ts.map +1 -1
- package/dist/domains/audit/audit.js +40 -4
- package/dist/domains/audit/audit.js.map +1 -1
- package/dist/domains/communities/communities.d.ts +1537 -0
- package/dist/domains/communities/communities.d.ts.map +1 -0
- package/dist/domains/communities/communities.js +464 -0
- package/dist/domains/communities/communities.js.map +1 -0
- package/dist/domains/contributions/contributions.d.ts +53 -4
- package/dist/domains/contributions/contributions.d.ts.map +1 -1
- package/dist/domains/contributions/contributions.js +39 -5
- package/dist/domains/contributions/contributions.js.map +1 -1
- package/dist/domains/feed/feed.d.ts +479 -0
- package/dist/domains/feed/feed.d.ts.map +1 -1
- package/dist/domains/feed/feed.js +45 -1
- package/dist/domains/feed/feed.js.map +1 -1
- package/dist/domains/media/media.d.ts +7 -1
- package/dist/domains/media/media.d.ts.map +1 -1
- package/dist/domains/media/media.js +5 -0
- package/dist/domains/media/media.js.map +1 -1
- package/dist/domains/members/members.d.ts +6 -0
- package/dist/domains/members/members.d.ts.map +1 -1
- package/dist/domains/members/members.js +5 -0
- package/dist/domains/members/members.js.map +1 -1
- package/dist/domains/moderation/moderation.d.ts +4 -4
- package/dist/domains/notifications/notifications.d.ts +82 -2
- package/dist/domains/notifications/notifications.d.ts.map +1 -1
- package/dist/domains/notifications/notifications.js +45 -0
- package/dist/domains/notifications/notifications.js.map +1 -1
- package/dist/domains/onboarding/onboarding.d.ts +11 -11
- package/dist/domains/onboarding/onboarding.d.ts.map +1 -1
- package/dist/domains/onboarding/onboarding.js +2 -2
- package/dist/domains/onboarding/onboarding.js.map +1 -1
- package/dist/domains/profile/founder-profile.d.ts +60 -4
- package/dist/domains/profile/founder-profile.d.ts.map +1 -1
- package/dist/domains/profile/founder-profile.js +66 -5
- package/dist/domains/profile/founder-profile.js.map +1 -1
- package/dist/domains/team/team.d.ts +22 -0
- package/dist/domains/team/team.d.ts.map +1 -1
- package/dist/domains/team/team.js +22 -0
- package/dist/domains/team/team.js.map +1 -1
- package/dist/domains/uploads/uploads.d.ts +1 -0
- package/dist/domains/uploads/uploads.d.ts.map +1 -1
- package/dist/index.d.ts +8 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -6
- package/dist/index.js.map +1 -1
- 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
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
148
|
-
*
|
|
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
|
|
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
|
|
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
|
-
/**
|
|
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
|
|
105
|
+
/** Points earned in the period. */
|
|
93
106
|
points: z.number().int().positive(),
|
|
94
107
|
});
|
|
95
108
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
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
|
|
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"]}
|