founder-schema 0.3.0 → 0.4.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 (102) hide show
  1. package/README.md +18 -15
  2. package/dist/access/access.d.ts +76 -0
  3. package/dist/access/access.d.ts.map +1 -0
  4. package/dist/access/access.js +66 -0
  5. package/dist/access/access.js.map +1 -0
  6. package/dist/access/permissions.d.ts +153 -0
  7. package/dist/access/permissions.d.ts.map +1 -0
  8. package/dist/access/permissions.js +80 -0
  9. package/dist/access/permissions.js.map +1 -0
  10. package/dist/access/roles.d.ts +71 -0
  11. package/dist/access/roles.d.ts.map +1 -0
  12. package/dist/access/roles.js +72 -0
  13. package/dist/access/roles.js.map +1 -0
  14. package/dist/chat.d.ts +4 -4
  15. package/dist/chat.d.ts.map +1 -1
  16. package/dist/chat.js +7 -2
  17. package/dist/chat.js.map +1 -1
  18. package/dist/core/common.d.ts +304 -0
  19. package/dist/core/common.d.ts.map +1 -0
  20. package/dist/core/common.js +340 -0
  21. package/dist/core/common.js.map +1 -0
  22. package/dist/core/errors.d.ts +105 -0
  23. package/dist/core/errors.d.ts.map +1 -0
  24. package/dist/core/errors.js +115 -0
  25. package/dist/core/errors.js.map +1 -0
  26. package/dist/core/limits.d.ts +99 -0
  27. package/dist/core/limits.d.ts.map +1 -0
  28. package/dist/core/limits.js +109 -0
  29. package/dist/core/limits.js.map +1 -0
  30. package/dist/core/responses.d.ts +195 -0
  31. package/dist/core/responses.d.ts.map +1 -0
  32. package/dist/core/responses.js +73 -0
  33. package/dist/core/responses.js.map +1 -0
  34. package/dist/core/validation.d.ts +144 -0
  35. package/dist/core/validation.d.ts.map +1 -0
  36. package/dist/core/validation.js +150 -0
  37. package/dist/core/validation.js.map +1 -0
  38. package/dist/domains/account/account.d.ts +40 -0
  39. package/dist/domains/account/account.d.ts.map +1 -0
  40. package/dist/domains/account/account.js +39 -0
  41. package/dist/domains/account/account.js.map +1 -0
  42. package/dist/domains/audit/audit.d.ts +169 -0
  43. package/dist/domains/audit/audit.d.ts.map +1 -0
  44. package/dist/domains/audit/audit.js +84 -0
  45. package/dist/domains/audit/audit.js.map +1 -0
  46. package/dist/domains/chat/chat.d.ts +187 -0
  47. package/dist/domains/chat/chat.d.ts.map +1 -0
  48. package/dist/domains/chat/chat.js +189 -0
  49. package/dist/domains/chat/chat.js.map +1 -0
  50. package/dist/domains/dashboard/dashboard.d.ts +48 -0
  51. package/dist/domains/dashboard/dashboard.d.ts.map +1 -0
  52. package/dist/domains/dashboard/dashboard.js +58 -0
  53. package/dist/domains/dashboard/dashboard.js.map +1 -0
  54. package/dist/domains/discover/discover.d.ts +245 -0
  55. package/dist/domains/discover/discover.d.ts.map +1 -0
  56. package/dist/domains/discover/discover.js +68 -0
  57. package/dist/domains/discover/discover.js.map +1 -0
  58. package/dist/domains/feed/feed.d.ts +1658 -0
  59. package/dist/domains/feed/feed.d.ts.map +1 -0
  60. package/dist/domains/feed/feed.js +623 -0
  61. package/dist/domains/feed/feed.js.map +1 -0
  62. package/dist/domains/members/members.d.ts +175 -0
  63. package/dist/domains/members/members.d.ts.map +1 -0
  64. package/dist/domains/members/members.js +106 -0
  65. package/dist/domains/members/members.js.map +1 -0
  66. package/dist/domains/moderation/moderation.d.ts +184 -0
  67. package/dist/domains/moderation/moderation.d.ts.map +1 -0
  68. package/dist/domains/moderation/moderation.js +90 -0
  69. package/dist/domains/moderation/moderation.js.map +1 -0
  70. package/dist/domains/onboarding/onboarding.d.ts +1018 -0
  71. package/dist/domains/onboarding/onboarding.d.ts.map +1 -0
  72. package/dist/domains/onboarding/onboarding.js +717 -0
  73. package/dist/domains/onboarding/onboarding.js.map +1 -0
  74. package/dist/domains/profile/founder-profile.d.ts +387 -0
  75. package/dist/domains/profile/founder-profile.d.ts.map +1 -0
  76. package/dist/domains/profile/founder-profile.js +180 -0
  77. package/dist/domains/profile/founder-profile.js.map +1 -0
  78. package/dist/domains/support/support.d.ts +305 -0
  79. package/dist/domains/support/support.d.ts.map +1 -0
  80. package/dist/domains/support/support.js +129 -0
  81. package/dist/domains/support/support.js.map +1 -0
  82. package/dist/domains/team/team.d.ts +79 -0
  83. package/dist/domains/team/team.d.ts.map +1 -0
  84. package/dist/domains/team/team.js +54 -0
  85. package/dist/domains/team/team.js.map +1 -0
  86. package/dist/domains/uploads/uploads.d.ts +113 -0
  87. package/dist/domains/uploads/uploads.d.ts.map +1 -0
  88. package/dist/domains/uploads/uploads.js +106 -0
  89. package/dist/domains/uploads/uploads.js.map +1 -0
  90. package/dist/env.d.ts +85 -0
  91. package/dist/env.d.ts.map +1 -0
  92. package/dist/env.js +150 -0
  93. package/dist/env.js.map +1 -0
  94. package/dist/index.d.ts +21 -12
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +29 -18
  97. package/dist/index.js.map +1 -1
  98. package/dist/onboarding.d.ts +58 -32
  99. package/dist/onboarding.d.ts.map +1 -1
  100. package/dist/onboarding.js +155 -32
  101. package/dist/onboarding.js.map +1 -1
  102. package/package.json +14 -2
@@ -0,0 +1,245 @@
1
+ /**
2
+ * founder-schema — Discover search and filters
3
+ *
4
+ * Search is by name; the filters are industry, skill, startup stage and
5
+ * location. The community surface the Discover tab will grow later is not
6
+ * modelled yet.
7
+ *
8
+ * @module founder-schema/domains/discover
9
+ */
10
+ import { z } from 'zod';
11
+ /**
12
+ * Every free-text filter here is `optionalTrimmedString`, so an EMPTY one is
13
+ * treated as "not provided" rather than as a validation failure.
14
+ *
15
+ * A search box the member has just cleared sends `?q=`, and a select they reset
16
+ * sends `?city=`. Refusing those turns an ordinary interaction into a 400 with
17
+ * nothing useful to show for it: the member asked for fewer filters, not for an
18
+ * error. This is the one place the create-body rule and the patch-body rule
19
+ * agree, because a query has nothing to erase.
20
+ */
21
+ export declare const discoverQuerySchema: z.ZodObject<{
22
+ limit: z.ZodPreprocess<z.ZodDefault<z.ZodCoercedNumber<unknown>>, unknown>;
23
+ cursor: z.ZodOptional<z.ZodString>;
24
+ q: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
25
+ industry: z.ZodPreprocess<z.ZodOptional<z.ZodArray<z.ZodEnum<{
26
+ agritech: "agritech";
27
+ ai_ml: "ai_ml";
28
+ b2b_saas: "b2b_saas";
29
+ consumer: "consumer";
30
+ climate: "climate";
31
+ d2c: "d2c";
32
+ deeptech: "deeptech";
33
+ edtech: "edtech";
34
+ fintech: "fintech";
35
+ gaming: "gaming";
36
+ healthtech: "healthtech";
37
+ logistics: "logistics";
38
+ manufacturing: "manufacturing";
39
+ marketplace: "marketplace";
40
+ media: "media";
41
+ mobility: "mobility";
42
+ proptech: "proptech";
43
+ robotics: "robotics";
44
+ services: "services";
45
+ space: "space";
46
+ other: "other";
47
+ }>>>, unknown>;
48
+ skill: z.ZodPreprocess<z.ZodOptional<z.ZodArray<z.ZodEnum<{
49
+ engineering: "engineering";
50
+ product: "product";
51
+ design: "design";
52
+ data: "data";
53
+ growth: "growth";
54
+ sales: "sales";
55
+ marketing: "marketing";
56
+ operations: "operations";
57
+ finance: "finance";
58
+ legal: "legal";
59
+ hr_people: "hr_people";
60
+ supply_chain: "supply_chain";
61
+ research: "research";
62
+ fundraising: "fundraising";
63
+ community: "community";
64
+ }>>>, unknown>;
65
+ stage: z.ZodPreprocess<z.ZodOptional<z.ZodArray<z.ZodEnum<{
66
+ idea: "idea";
67
+ validation: "validation";
68
+ mvp: "mvp";
69
+ pre_seed: "pre_seed";
70
+ seed: "seed";
71
+ series_a_plus: "series_a_plus";
72
+ profitable: "profitable";
73
+ exited: "exited";
74
+ }>>>, unknown>;
75
+ city: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
76
+ country: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
77
+ }, z.core.$strip>;
78
+ export type DiscoverQuery = z.infer<typeof discoverQuerySchema>;
79
+ export type DiscoverQueryInput = z.input<typeof discoverQuerySchema>;
80
+ /** A page of founder cards, most recently updated first. */
81
+ export declare const discoverPageSchema: z.ZodObject<{
82
+ items: z.ZodArray<z.ZodObject<{
83
+ id: z.ZodString;
84
+ location: z.ZodOptional<z.ZodObject<{
85
+ city: z.ZodString;
86
+ state: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
87
+ country: z.ZodString;
88
+ }, z.core.$strip>>;
89
+ displayName: z.ZodString;
90
+ username: z.ZodString;
91
+ avatarUrl: z.ZodOptional<z.ZodURL>;
92
+ headline: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
93
+ venture: z.ZodOptional<z.ZodObject<{
94
+ name: z.ZodString;
95
+ tagline: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
96
+ website: z.ZodOptional<z.ZodURL>;
97
+ industry: z.ZodEnum<{
98
+ agritech: "agritech";
99
+ ai_ml: "ai_ml";
100
+ b2b_saas: "b2b_saas";
101
+ consumer: "consumer";
102
+ climate: "climate";
103
+ d2c: "d2c";
104
+ deeptech: "deeptech";
105
+ edtech: "edtech";
106
+ fintech: "fintech";
107
+ gaming: "gaming";
108
+ healthtech: "healthtech";
109
+ logistics: "logistics";
110
+ manufacturing: "manufacturing";
111
+ marketplace: "marketplace";
112
+ media: "media";
113
+ mobility: "mobility";
114
+ proptech: "proptech";
115
+ robotics: "robotics";
116
+ services: "services";
117
+ space: "space";
118
+ other: "other";
119
+ }>;
120
+ stage: z.ZodEnum<{
121
+ idea: "idea";
122
+ validation: "validation";
123
+ mvp: "mvp";
124
+ pre_seed: "pre_seed";
125
+ seed: "seed";
126
+ series_a_plus: "series_a_plus";
127
+ profitable: "profitable";
128
+ exited: "exited";
129
+ }>;
130
+ teamSize: z.ZodOptional<z.ZodNumber>;
131
+ foundedYear: z.ZodOptional<z.ZodNumber>;
132
+ }, z.core.$strip>>;
133
+ skills: z.ZodDefault<z.ZodArray<z.ZodEnum<{
134
+ engineering: "engineering";
135
+ product: "product";
136
+ design: "design";
137
+ data: "data";
138
+ growth: "growth";
139
+ sales: "sales";
140
+ marketing: "marketing";
141
+ operations: "operations";
142
+ finance: "finance";
143
+ legal: "legal";
144
+ hr_people: "hr_people";
145
+ supply_chain: "supply_chain";
146
+ research: "research";
147
+ fundraising: "fundraising";
148
+ community: "community";
149
+ }>>>;
150
+ goals: z.ZodDefault<z.ZodArray<z.ZodEnum<{
151
+ find_cofounder: "find_cofounder";
152
+ find_investors: "find_investors";
153
+ find_customers: "find_customers";
154
+ hiring: "hiring";
155
+ partnerships: "partnerships";
156
+ mentorship: "mentorship";
157
+ grow_network: "grow_network";
158
+ business_ideas: "business_ideas";
159
+ market_insights: "market_insights";
160
+ learn_startup: "learn_startup";
161
+ start_company: "start_company";
162
+ }>>>;
163
+ }, z.core.$strip>>;
164
+ nextCursor: z.ZodNullable<z.ZodString>;
165
+ }, z.core.$strip>;
166
+ export type DiscoverPage = z.infer<typeof discoverPageSchema>;
167
+ /**
168
+ * The filter options the Discover drawer offers, with counts. The VALUES come
169
+ * from the enums here, so the options cannot disagree with what the query
170
+ * accepts.
171
+ *
172
+ * ⚠ NO LABELS. The value IS the translation key (`industries.<value>`,
173
+ * `stages.<value>`, `skills.<value>`), and a client words it in the member's
174
+ * language. Until 0.2.0 the server also sent an English `label` per option,
175
+ * built by capitalising the value ("Ai ml"), which no client read and which
176
+ * would have been wrong in Hindi if one had. Cities are member-typed place
177
+ * names, so their value is shown as it is.
178
+ */
179
+ export declare const discoverFacetsSchema: z.ZodObject<{
180
+ industries: z.ZodArray<z.ZodObject<{
181
+ value: z.ZodEnum<{
182
+ agritech: "agritech";
183
+ ai_ml: "ai_ml";
184
+ b2b_saas: "b2b_saas";
185
+ consumer: "consumer";
186
+ climate: "climate";
187
+ d2c: "d2c";
188
+ deeptech: "deeptech";
189
+ edtech: "edtech";
190
+ fintech: "fintech";
191
+ gaming: "gaming";
192
+ healthtech: "healthtech";
193
+ logistics: "logistics";
194
+ manufacturing: "manufacturing";
195
+ marketplace: "marketplace";
196
+ media: "media";
197
+ mobility: "mobility";
198
+ proptech: "proptech";
199
+ robotics: "robotics";
200
+ services: "services";
201
+ space: "space";
202
+ other: "other";
203
+ }>;
204
+ count: z.ZodNumber;
205
+ }, z.core.$strip>>;
206
+ skills: z.ZodArray<z.ZodObject<{
207
+ value: z.ZodEnum<{
208
+ engineering: "engineering";
209
+ product: "product";
210
+ design: "design";
211
+ data: "data";
212
+ growth: "growth";
213
+ sales: "sales";
214
+ marketing: "marketing";
215
+ operations: "operations";
216
+ finance: "finance";
217
+ legal: "legal";
218
+ hr_people: "hr_people";
219
+ supply_chain: "supply_chain";
220
+ research: "research";
221
+ fundraising: "fundraising";
222
+ community: "community";
223
+ }>;
224
+ count: z.ZodNumber;
225
+ }, z.core.$strip>>;
226
+ stages: z.ZodArray<z.ZodObject<{
227
+ value: z.ZodEnum<{
228
+ idea: "idea";
229
+ validation: "validation";
230
+ mvp: "mvp";
231
+ pre_seed: "pre_seed";
232
+ seed: "seed";
233
+ series_a_plus: "series_a_plus";
234
+ profitable: "profitable";
235
+ exited: "exited";
236
+ }>;
237
+ count: z.ZodNumber;
238
+ }, z.core.$strip>>;
239
+ cities: z.ZodArray<z.ZodObject<{
240
+ value: z.ZodString;
241
+ count: z.ZodNumber;
242
+ }, z.core.$strip>>;
243
+ }, z.core.$strip>;
244
+ export type DiscoverFacets = z.infer<typeof discoverFacetsSchema>;
245
+ //# sourceMappingURL=discover.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../../src/domains/discover/discover.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAyBxB;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAQ9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAErE,4DAA4D;AAC5D,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAqC,CAAC;AACrE,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAM9D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAK/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * founder-schema — Discover search and filters
3
+ *
4
+ * Search is by name; the filters are industry, skill, startup stage and
5
+ * location. The community surface the Discover tab will grow later is not
6
+ * modelled yet.
7
+ *
8
+ * @module founder-schema/domains/discover
9
+ */
10
+ import { z } from 'zod';
11
+ import { industrySchema, optionalTrimmedString, paginationQuerySchema, skillSchema, startupStageSchema, } from '../../core/common.js';
12
+ import { DISCOVER_FILTER_VALUES_MAX, DISCOVER_TEXT_MAX } from '../../core/limits.js';
13
+ import { paginatedSchema } from '../../core/responses.js';
14
+ import { founderCardSchema } from '../profile/founder-profile.js';
15
+ /**
16
+ * Accepts a repeated query param (`?skill=growth&skill=sales`) or a single
17
+ * value, and always produces an array.
18
+ *
19
+ * Express gives `string` for one occurrence and `string[]` for several, so
20
+ * without this the same URL shape parses two different ways.
21
+ */
22
+ const multiValue = (inner) => z.preprocess((value) => {
23
+ if (value === undefined || value === null)
24
+ return undefined;
25
+ return Array.isArray(value) ? value : [value];
26
+ }, z.array(inner).max(DISCOVER_FILTER_VALUES_MAX).optional());
27
+ /**
28
+ * Every free-text filter here is `optionalTrimmedString`, so an EMPTY one is
29
+ * treated as "not provided" rather than as a validation failure.
30
+ *
31
+ * A search box the member has just cleared sends `?q=`, and a select they reset
32
+ * sends `?city=`. Refusing those turns an ordinary interaction into a 400 with
33
+ * nothing useful to show for it: the member asked for fewer filters, not for an
34
+ * error. This is the one place the create-body rule and the patch-body rule
35
+ * agree, because a query has nothing to erase.
36
+ */
37
+ export const discoverQuerySchema = paginationQuerySchema.extend({
38
+ /** Free-text name search. Matched against display name and username. */
39
+ q: optionalTrimmedString(DISCOVER_TEXT_MAX),
40
+ industry: multiValue(industrySchema),
41
+ skill: multiValue(skillSchema),
42
+ stage: multiValue(startupStageSchema),
43
+ city: optionalTrimmedString(DISCOVER_TEXT_MAX),
44
+ country: optionalTrimmedString(DISCOVER_TEXT_MAX),
45
+ });
46
+ /** A page of founder cards, most recently updated first. */
47
+ export const discoverPageSchema = paginatedSchema(founderCardSchema);
48
+ /** One filter option and how many listed founders match it. */
49
+ const facetOption = (value) => z.object({ value, count: z.number().int().nonnegative() });
50
+ /**
51
+ * The filter options the Discover drawer offers, with counts. The VALUES come
52
+ * from the enums here, so the options cannot disagree with what the query
53
+ * accepts.
54
+ *
55
+ * ⚠ NO LABELS. The value IS the translation key (`industries.<value>`,
56
+ * `stages.<value>`, `skills.<value>`), and a client words it in the member's
57
+ * language. Until 0.2.0 the server also sent an English `label` per option,
58
+ * built by capitalising the value ("Ai ml"), which no client read and which
59
+ * would have been wrong in Hindi if one had. Cities are member-typed place
60
+ * names, so their value is shown as it is.
61
+ */
62
+ export const discoverFacetsSchema = z.object({
63
+ industries: z.array(facetOption(industrySchema)),
64
+ skills: z.array(facetOption(skillSchema)),
65
+ stages: z.array(facetOption(startupStageSchema)),
66
+ cities: z.array(facetOption(z.string())),
67
+ });
68
+ //# sourceMappingURL=discover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../../../src/domains/discover/discover.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,cAAc,EACd,qBAAqB,EACrB,qBAAqB,EACrB,WAAW,EACX,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACrF,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAElE;;;;;;GAMG;AACH,MAAM,UAAU,GAAG,CAAsB,KAAQ,EAAE,EAAE,CACnD,CAAC,CAAC,UAAU,CAAC,CAAC,KAAK,EAAE,EAAE;IACrB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5D,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;AAChD,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;AAEhE;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,qBAAqB,CAAC,MAAM,CAAC;IAC9D,wEAAwE;IACxE,CAAC,EAAE,qBAAqB,CAAC,iBAAiB,CAAC;IAC3C,QAAQ,EAAE,UAAU,CAAC,cAAc,CAAC;IACpC,KAAK,EAAE,UAAU,CAAC,WAAW,CAAC;IAC9B,KAAK,EAAE,UAAU,CAAC,kBAAkB,CAAC;IACrC,IAAI,EAAE,qBAAqB,CAAC,iBAAiB,CAAC;IAC9C,OAAO,EAAE,qBAAqB,CAAC,iBAAiB,CAAC;CAClD,CAAC,CAAC;AAIH,4DAA4D;AAC5D,MAAM,CAAC,MAAM,kBAAkB,GAAG,eAAe,CAAC,iBAAiB,CAAC,CAAC;AAGrE,+DAA+D;AAC/D,MAAM,WAAW,GAAG,CAAsB,KAAQ,EAAE,EAAE,CACpD,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AAE7D;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,UAAU,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;IAChD,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IACzC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,kBAAkB,CAAC,CAAC;IAChD,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;CACzC,CAAC,CAAC","sourcesContent":["/**\n * founder-schema — Discover search and filters\n *\n * Search is by name; the filters are industry, skill, startup stage and\n * location. The community surface the Discover tab will grow later is not\n * modelled yet.\n *\n * @module founder-schema/domains/discover\n */\n\nimport { z } from 'zod';\nimport {\n industrySchema,\n optionalTrimmedString,\n paginationQuerySchema,\n skillSchema,\n startupStageSchema,\n} from '../../core/common.js';\nimport { DISCOVER_FILTER_VALUES_MAX, DISCOVER_TEXT_MAX } from '../../core/limits.js';\nimport { paginatedSchema } from '../../core/responses.js';\nimport { founderCardSchema } from '../profile/founder-profile.js';\n\n/**\n * Accepts a repeated query param (`?skill=growth&skill=sales`) or a single\n * value, and always produces an array.\n *\n * Express gives `string` for one occurrence and `string[]` for several, so\n * without this the same URL shape parses two different ways.\n */\nconst multiValue = <T extends z.ZodType>(inner: T) =>\n z.preprocess((value) => {\n if (value === undefined || value === null) return undefined;\n return Array.isArray(value) ? value : [value];\n }, z.array(inner).max(DISCOVER_FILTER_VALUES_MAX).optional());\n\n/**\n * Every free-text filter here is `optionalTrimmedString`, so an EMPTY one is\n * treated as \"not provided\" rather than as a validation failure.\n *\n * A search box the member has just cleared sends `?q=`, and a select they reset\n * sends `?city=`. Refusing those turns an ordinary interaction into a 400 with\n * nothing useful to show for it: the member asked for fewer filters, not for an\n * error. This is the one place the create-body rule and the patch-body rule\n * agree, because a query has nothing to erase.\n */\nexport const discoverQuerySchema = paginationQuerySchema.extend({\n /** Free-text name search. Matched against display name and username. */\n q: optionalTrimmedString(DISCOVER_TEXT_MAX),\n industry: multiValue(industrySchema),\n skill: multiValue(skillSchema),\n stage: multiValue(startupStageSchema),\n city: optionalTrimmedString(DISCOVER_TEXT_MAX),\n country: optionalTrimmedString(DISCOVER_TEXT_MAX),\n});\nexport type DiscoverQuery = z.infer<typeof discoverQuerySchema>;\nexport type DiscoverQueryInput = z.input<typeof discoverQuerySchema>;\n\n/** A page of founder cards, most recently updated first. */\nexport const discoverPageSchema = paginatedSchema(founderCardSchema);\nexport type DiscoverPage = z.infer<typeof discoverPageSchema>;\n\n/** One filter option and how many listed founders match it. */\nconst facetOption = <T extends z.ZodType>(value: T) =>\n z.object({ value, count: z.number().int().nonnegative() });\n\n/**\n * The filter options the Discover drawer offers, with counts. The VALUES come\n * from the enums here, so the options cannot disagree with what the query\n * accepts.\n *\n * ⚠ NO LABELS. The value IS the translation key (`industries.<value>`,\n * `stages.<value>`, `skills.<value>`), and a client words it in the member's\n * language. Until 0.2.0 the server also sent an English `label` per option,\n * built by capitalising the value (\"Ai ml\"), which no client read and which\n * would have been wrong in Hindi if one had. Cities are member-typed place\n * names, so their value is shown as it is.\n */\nexport const discoverFacetsSchema = z.object({\n industries: z.array(facetOption(industrySchema)),\n skills: z.array(facetOption(skillSchema)),\n stages: z.array(facetOption(startupStageSchema)),\n cities: z.array(facetOption(z.string())),\n});\nexport type DiscoverFacets = z.infer<typeof discoverFacetsSchema>;\n"]}