founder-schema 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAsBxB;;;;;;;;;GASG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAQ9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAS/B,CAAC;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
@@ -0,0 +1,54 @@
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/discover
9
+ */
10
+ import { z } from 'zod';
11
+ import { industrySchema, optionalTrimmedString, paginationQuerySchema, skillSchema, startupStageSchema, } from './common.js';
12
+ /**
13
+ * Accepts a repeated query param (`?skill=growth&skill=sales`) or a single
14
+ * value, and always produces an array.
15
+ *
16
+ * Express gives `string` for one occurrence and `string[]` for several, so
17
+ * without this the same URL shape parses two different ways.
18
+ */
19
+ const multiValue = (inner) => z.preprocess((value) => {
20
+ if (value === undefined || value === null)
21
+ return undefined;
22
+ return Array.isArray(value) ? value : [value];
23
+ }, z.array(inner).max(10).optional());
24
+ /**
25
+ * Every free-text filter here is `optionalTrimmedString`, so an EMPTY one is
26
+ * treated as "not provided" rather than as a validation failure.
27
+ *
28
+ * A search box the member has just cleared sends `?q=`, and a select they reset
29
+ * sends `?city=`. Refusing those turns an ordinary interaction into a 400 with
30
+ * nothing useful to show for it: the member asked for fewer filters, not for an
31
+ * error. This is the one place the create-body rule and the patch-body rule
32
+ * agree, because a query has nothing to erase.
33
+ */
34
+ export const discoverQuerySchema = paginationQuerySchema.extend({
35
+ /** Free-text name search. Matched against display name and username. */
36
+ q: optionalTrimmedString(80),
37
+ industry: multiValue(industrySchema),
38
+ skill: multiValue(skillSchema),
39
+ stage: multiValue(startupStageSchema),
40
+ city: optionalTrimmedString(80),
41
+ country: optionalTrimmedString(80),
42
+ });
43
+ /**
44
+ * The filter set the Discover UI renders. Served by the backend rather than
45
+ * hard-coded in each client, so adding an industry does not need three
46
+ * releases.
47
+ */
48
+ export const discoverFacetsSchema = z.object({
49
+ industries: z.array(z.object({ value: industrySchema, label: z.string(), count: z.number().int() })),
50
+ skills: z.array(z.object({ value: skillSchema, label: z.string(), count: z.number().int() })),
51
+ stages: z.array(z.object({ value: startupStageSchema, label: z.string(), count: z.number().int() })),
52
+ cities: z.array(z.object({ value: z.string(), count: z.number().int() })),
53
+ });
54
+ //# sourceMappingURL=discover.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../src/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,aAAa,CAAC;AAErB;;;;;;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,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;AAExC;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,qBAAqB,CAAC,MAAM,CAAC;IAC9D,wEAAwE;IACxE,CAAC,EAAE,qBAAqB,CAAC,EAAE,CAAC;IAC5B,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,EAAE,CAAC;IAC/B,OAAO,EAAE,qBAAqB,CAAC,EAAE,CAAC;CACnC,CAAC,CAAC;AAGH;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3C,UAAU,EAAE,CAAC,CAAC,KAAK,CACjB,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAChF;IACD,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;IAC7F,MAAM,EAAE,CAAC,CAAC,KAAK,CACb,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CACpF;IACD,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;CAC1E,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/discover\n */\n\nimport { z } from 'zod';\nimport {\n industrySchema,\n optionalTrimmedString,\n paginationQuerySchema,\n skillSchema,\n startupStageSchema,\n} from './common.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(10).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(80),\n industry: multiValue(industrySchema),\n skill: multiValue(skillSchema),\n stage: multiValue(startupStageSchema),\n city: optionalTrimmedString(80),\n country: optionalTrimmedString(80),\n});\nexport type DiscoverQuery = z.infer<typeof discoverQuerySchema>;\n\n/**\n * The filter set the Discover UI renders. Served by the backend rather than\n * hard-coded in each client, so adding an industry does not need three\n * releases.\n */\nexport const discoverFacetsSchema = z.object({\n industries: z.array(\n z.object({ value: industrySchema, label: z.string(), count: z.number().int() }),\n ),\n skills: z.array(z.object({ value: skillSchema, label: z.string(), count: z.number().int() })),\n stages: z.array(\n z.object({ value: startupStageSchema, label: z.string(), count: z.number().int() }),\n ),\n cities: z.array(z.object({ value: z.string(), count: z.number().int() })),\n});\nexport type DiscoverFacets = z.infer<typeof discoverFacetsSchema>;\n"]}