founder-schema 0.16.0 → 0.18.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 +263 -19
- package/dist/access/access.d.ts +1 -1
- package/dist/access/access.d.ts.map +1 -1
- package/dist/access/access.js +4 -1
- package/dist/access/access.js.map +1 -1
- package/dist/access/permissions.d.ts +27 -29
- package/dist/access/permissions.d.ts.map +1 -1
- package/dist/access/permissions.js +39 -3
- package/dist/access/permissions.js.map +1 -1
- package/dist/core/common.d.ts +117 -23
- package/dist/core/common.d.ts.map +1 -1
- package/dist/core/common.js +104 -21
- package/dist/core/common.js.map +1 -1
- package/dist/core/errors.d.ts +7 -5
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +17 -11
- package/dist/core/errors.js.map +1 -1
- package/dist/core/limits.d.ts +55 -17
- package/dist/core/limits.d.ts.map +1 -1
- package/dist/core/limits.js +59 -22
- package/dist/core/limits.js.map +1 -1
- package/dist/core/money.d.ts +33 -0
- package/dist/core/money.d.ts.map +1 -0
- package/dist/core/money.js +36 -0
- package/dist/core/money.js.map +1 -0
- package/dist/core/responses.d.ts +14 -4
- package/dist/core/responses.d.ts.map +1 -1
- package/dist/core/responses.js.map +1 -1
- package/dist/core/validation.d.ts +32 -2
- package/dist/core/validation.d.ts.map +1 -1
- package/dist/core/validation.js +32 -2
- package/dist/core/validation.js.map +1 -1
- package/dist/domains/billing/billing.d.ts +61 -8
- package/dist/domains/billing/billing.d.ts.map +1 -1
- package/dist/domains/billing/billing.js +58 -22
- package/dist/domains/billing/billing.js.map +1 -1
- package/dist/domains/billing/gst.d.ts.map +1 -1
- package/dist/domains/billing/gst.js +4 -3
- package/dist/domains/billing/gst.js.map +1 -1
- package/dist/domains/chat/chat.d.ts +0 -21
- package/dist/domains/chat/chat.d.ts.map +1 -1
- package/dist/domains/chat/chat.js +0 -20
- package/dist/domains/chat/chat.js.map +1 -1
- package/dist/domains/communities/communities.d.ts +30 -12
- package/dist/domains/communities/communities.d.ts.map +1 -1
- package/dist/domains/communities/communities.js +43 -15
- package/dist/domains/communities/communities.js.map +1 -1
- package/dist/domains/contributions/contributions.d.ts +1 -1
- package/dist/domains/contributions/contributions.js +1 -1
- package/dist/domains/contributions/contributions.js.map +1 -1
- package/dist/domains/dashboard/dashboard.d.ts +3 -1
- package/dist/domains/dashboard/dashboard.d.ts.map +1 -1
- package/dist/domains/dashboard/dashboard.js +23 -7
- package/dist/domains/dashboard/dashboard.js.map +1 -1
- package/dist/domains/discover/discover.d.ts +8 -8
- package/dist/domains/feed/feed.d.ts +55 -153
- package/dist/domains/feed/feed.d.ts.map +1 -1
- package/dist/domains/feed/feed.js +53 -58
- package/dist/domains/feed/feed.js.map +1 -1
- package/dist/domains/location/india.d.ts +215 -0
- package/dist/domains/location/india.d.ts.map +1 -0
- package/dist/domains/location/india.js +1203 -0
- package/dist/domains/location/india.js.map +1 -0
- package/dist/domains/members/members.d.ts +2 -2
- package/dist/domains/membership/membership.d.ts +26 -0
- package/dist/domains/membership/membership.d.ts.map +1 -1
- package/dist/domains/membership/membership.js +55 -1
- package/dist/domains/membership/membership.js.map +1 -1
- package/dist/domains/moderation/moderation.d.ts +2 -2
- package/dist/domains/moderation/moderation.js +3 -3
- package/dist/domains/moderation/moderation.js.map +1 -1
- package/dist/domains/notifications/notifications.d.ts +1 -10
- package/dist/domains/notifications/notifications.d.ts.map +1 -1
- package/dist/domains/notifications/notifications.js +2 -3
- package/dist/domains/notifications/notifications.js.map +1 -1
- package/dist/domains/onboarding/onboarding-answers.d.ts +112 -0
- package/dist/domains/onboarding/onboarding-answers.d.ts.map +1 -0
- package/dist/domains/onboarding/onboarding-answers.js +164 -0
- package/dist/domains/onboarding/onboarding-answers.js.map +1 -0
- package/dist/domains/onboarding/onboarding.d.ts +398 -62
- package/dist/domains/onboarding/onboarding.d.ts.map +1 -1
- package/dist/domains/onboarding/onboarding.js +229 -68
- package/dist/domains/onboarding/onboarding.js.map +1 -1
- package/dist/domains/products/products.d.ts +43 -8
- package/dist/domains/products/products.d.ts.map +1 -1
- package/dist/domains/products/products.js +57 -11
- package/dist/domains/products/products.js.map +1 -1
- package/dist/domains/profile/founder-profile.d.ts +120 -68
- package/dist/domains/profile/founder-profile.d.ts.map +1 -1
- package/dist/domains/profile/founder-profile.js +80 -29
- package/dist/domains/profile/founder-profile.js.map +1 -1
- package/dist/domains/profile/my-profile.d.ts +3 -3
- package/dist/domains/reviews/reviews.d.ts +1 -1
- package/dist/domains/reviews/reviews.d.ts.map +1 -1
- package/dist/domains/reviews/reviews.js +2 -2
- package/dist/domains/reviews/reviews.js.map +1 -1
- package/dist/domains/support/support.d.ts +22 -3
- package/dist/domains/support/support.d.ts.map +1 -1
- package/dist/domains/support/support.js +19 -5
- package/dist/domains/support/support.js.map +1 -1
- package/dist/domains/team/team.d.ts.map +1 -1
- package/dist/domains/team/team.js +3 -6
- package/dist/domains/team/team.js.map +1 -1
- package/dist/domains/uploads/uploads.d.ts +7 -22
- package/dist/domains/uploads/uploads.d.ts.map +1 -1
- package/dist/domains/uploads/uploads.js +7 -21
- package/dist/domains/uploads/uploads.js.map +1 -1
- package/dist/env.d.ts +26 -13
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +58 -23
- package/dist/env.js.map +1 -1
- package/dist/index.d.ts +18 -15
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -14
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* founder-schema — From a member's onboarding answers to what is sent (0.17.0)
|
|
3
|
+
*
|
|
4
|
+
* The answers are an `OnboardingForm` (the in-progress shape: raw text, `null`
|
|
5
|
+
* for a choice not made). Two things are built from them, and until 0.17.0
|
|
6
|
+
* each client built them by hand, so a second client (the app) would have had
|
|
7
|
+
* to copy every rule below and keep it in step:
|
|
8
|
+
*
|
|
9
|
+
* the draft a step SAVES exactly the fields that step owns, as typed
|
|
10
|
+
* (`onboardingDraftForStep`)
|
|
11
|
+
* the body FINISH sends completed answers only, cleaned, every
|
|
12
|
+
* unanswered optional field ABSENT
|
|
13
|
+
* (`completeOnboardingBodyFrom`)
|
|
14
|
+
*
|
|
15
|
+
* Neither validates: `ONBOARDING_STEP_SCHEMAS` and `completeOnboardingBodySchema`
|
|
16
|
+
* do, on what these build. A half-filled form builds a half-filled body on
|
|
17
|
+
* purpose, so the schema refuses it with the reason under each field instead
|
|
18
|
+
* of a builder hiding the gap.
|
|
19
|
+
*
|
|
20
|
+
* @module founder-schema/domains/onboarding/onboarding-answers
|
|
21
|
+
*/
|
|
22
|
+
import { type Industry, type StartupStage } from '../../core/common.js';
|
|
23
|
+
import { type CompleteOnboardingBodyInput, type OnboardingForm, type OnboardingStepKey } from './onboarding.js';
|
|
24
|
+
type DeepPartial<T> = T extends readonly (infer Item)[] ? DeepPartial<Item>[] : T extends object ? {
|
|
25
|
+
[K in keyof T]?: DeepPartial<T[K]>;
|
|
26
|
+
} : T;
|
|
27
|
+
/**
|
|
28
|
+
* A founding year as typed, kept to digits, as many as a year has (0.18.0,
|
|
29
|
+
* moved from the web's Me editor beside `teamSizeDigits`).
|
|
30
|
+
*/
|
|
31
|
+
export declare const foundedYearDigits: (raw: string) => string;
|
|
32
|
+
/**
|
|
33
|
+
* A venture body from what was typed (0.18.0): ONE builder for onboarding's
|
|
34
|
+
* business step and Me's business editor, which each built it by hand and
|
|
35
|
+
* cleaned the team size two ways. Text trimmed, the website made a link
|
|
36
|
+
* (`normalizeLinkUrl`), digits kept (`teamSizeDigits`, `foundedYearDigits`);
|
|
37
|
+
* an empty optional answer is ABSENT. A missing name, industry or stage is
|
|
38
|
+
* left missing, so `ventureSchema` refuses it under its field.
|
|
39
|
+
*/
|
|
40
|
+
export declare function ventureBodyFrom(v: {
|
|
41
|
+
name: string;
|
|
42
|
+
website: string;
|
|
43
|
+
industry: Industry | null;
|
|
44
|
+
stage: StartupStage | null;
|
|
45
|
+
teamSize: string;
|
|
46
|
+
tagline?: string;
|
|
47
|
+
foundedYear?: string;
|
|
48
|
+
}): {
|
|
49
|
+
name: string;
|
|
50
|
+
industry: Industry | undefined;
|
|
51
|
+
stage: StartupStage | undefined;
|
|
52
|
+
tagline?: string;
|
|
53
|
+
website?: string;
|
|
54
|
+
teamSize?: number;
|
|
55
|
+
foundedYear?: number;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* What `completeOnboardingBodyFrom` builds: the finish body's INPUT, any part
|
|
59
|
+
* of it possibly still missing. Not a valid body until
|
|
60
|
+
* `completeOnboardingBodySchema` says so.
|
|
61
|
+
*/
|
|
62
|
+
export type CompleteOnboardingBodyCandidate = DeepPartial<CompleteOnboardingBodyInput>;
|
|
63
|
+
/** Answers with nothing filled in: what a new member starts from. */
|
|
64
|
+
export declare function emptyOnboardingForm(): OnboardingForm;
|
|
65
|
+
/**
|
|
66
|
+
* True when the member has started describing a business: any of the
|
|
67
|
+
* venture's own answers. Then it is sent, and the schema asks for its name,
|
|
68
|
+
* industry and stage.
|
|
69
|
+
*
|
|
70
|
+
* The business type and the registration do not start it: they DESCRIBE a
|
|
71
|
+
* business. Picked without one, each is sent anyway and refused on its own
|
|
72
|
+
* field (`ventureRequired`: add the business, or clear this), never dropped
|
|
73
|
+
* without a word (hary's ruling).
|
|
74
|
+
*/
|
|
75
|
+
export declare function ventureStarted(form: Pick<OnboardingForm, 'venture'>): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* True when a place has been started: a city or a state given. A place opened
|
|
78
|
+
* and left empty is NO place (the member chose not to say), never a half
|
|
79
|
+
* answer to refuse; a place with only one of the two IS half an answer, and
|
|
80
|
+
* the schema asks for the other.
|
|
81
|
+
*/
|
|
82
|
+
export declare function locationStarted(place: {
|
|
83
|
+
city: string;
|
|
84
|
+
state?: string | null;
|
|
85
|
+
} | null): boolean;
|
|
86
|
+
/**
|
|
87
|
+
* The draft one step saves: exactly the fields that step owns
|
|
88
|
+
* (`FOUNDER_ONBOARDING_STEPS[].fields`, the same picks its validation uses),
|
|
89
|
+
* as typed. Only the step's own fields, so a save can never overwrite another
|
|
90
|
+
* step's answers (the server merges per field); raw, so a member who comes back
|
|
91
|
+
* sees what they typed. The photo step's field is its upload id, which the
|
|
92
|
+
* server holds rather than drafts.
|
|
93
|
+
*/
|
|
94
|
+
export declare function onboardingDraftForStep(form: OnboardingForm, key: OnboardingStepKey): Partial<OnboardingForm>;
|
|
95
|
+
/**
|
|
96
|
+
* What Finish sends, and what every step is CHECKED as (each step's slice
|
|
97
|
+
* reads its own fields from it).
|
|
98
|
+
*
|
|
99
|
+
* ⚠ ABSENT, NEVER A DEFAULT. The sibling gym product sent `bloodGroup ?? 'O+'`
|
|
100
|
+
* and recorded everyone who skipped the question as O-positive. An optional
|
|
101
|
+
* answer not given is left out; nothing is filled with a plausible value.
|
|
102
|
+
*
|
|
103
|
+
* Cleaned the way the schema reads it: the name tidied (`tidyFullName`), the
|
|
104
|
+
* phone to ten digits (`normalizePhone`), links given their scheme
|
|
105
|
+
* (`normalizeLinkUrl`), the place in the list's own spelling in India
|
|
106
|
+
* (`canonicalIndiaLocation`), the team size a number. A half-answered part is
|
|
107
|
+
* SENT, not hidden: a location without a city, a business without its stage,
|
|
108
|
+
* reach the schema and are refused there, under the field.
|
|
109
|
+
*/
|
|
110
|
+
export declare function completeOnboardingBodyFrom(form: OnboardingForm): CompleteOnboardingBodyCandidate;
|
|
111
|
+
export {};
|
|
112
|
+
//# sourceMappingURL=onboarding-answers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboarding-answers.d.ts","sourceRoot":"","sources":["../../../src/domains/onboarding/onboarding-answers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EACL,KAAK,QAAQ,EAGb,KAAK,YAAY,EAElB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,KAAK,2BAA2B,EAIhC,KAAK,cAAc,EACnB,KAAK,iBAAiB,EAGvB,MAAM,iBAAiB,CAAC;AAEzB,KAAK,WAAW,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS,CAAC,MAAM,IAAI,CAAC,EAAE,GACnD,WAAW,CAAC,IAAI,CAAC,EAAE,GACnB,CAAC,SAAS,MAAM,GACd;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GACtC,CAAC,CAAC;AAER;;;GAGG;AACH,eAAO,MAAM,iBAAiB,GAAI,KAAK,MAAM,KAAG,MACkB,CAAC;AAEnE;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,YAAY,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,GAAG;IACF,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC/B,KAAK,EAAE,YAAY,GAAG,SAAS,CAAC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAcA;AAED;;;;GAIG;AACH,MAAM,MAAM,+BAA+B,GAAG,WAAW,CAAC,2BAA2B,CAAC,CAAC;AAEvF,qEAAqE;AACrE,wBAAgB,mBAAmB,IAAI,cAAc,CAEpD;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,EAAE,SAAS,CAAC,GAAG,OAAO,CAG7E;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,GAAG,IAAI,GAAG,OAAO,CAE9F;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,cAAc,EACpB,GAAG,EAAE,iBAAiB,GACrB,OAAO,CAAC,cAAc,CAAC,CAKzB;AAWD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,cAAc,GAAG,+BAA+B,CA6ChG"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* founder-schema — From a member's onboarding answers to what is sent (0.17.0)
|
|
3
|
+
*
|
|
4
|
+
* The answers are an `OnboardingForm` (the in-progress shape: raw text, `null`
|
|
5
|
+
* for a choice not made). Two things are built from them, and until 0.17.0
|
|
6
|
+
* each client built them by hand, so a second client (the app) would have had
|
|
7
|
+
* to copy every rule below and keep it in step:
|
|
8
|
+
*
|
|
9
|
+
* the draft a step SAVES exactly the fields that step owns, as typed
|
|
10
|
+
* (`onboardingDraftForStep`)
|
|
11
|
+
* the body FINISH sends completed answers only, cleaned, every
|
|
12
|
+
* unanswered optional field ABSENT
|
|
13
|
+
* (`completeOnboardingBodyFrom`)
|
|
14
|
+
*
|
|
15
|
+
* Neither validates: `ONBOARDING_STEP_SCHEMAS` and `completeOnboardingBodySchema`
|
|
16
|
+
* do, on what these build. A half-filled form builds a half-filled body on
|
|
17
|
+
* purpose, so the schema refuses it with the reason under each field instead
|
|
18
|
+
* of a builder hiding the gap.
|
|
19
|
+
*
|
|
20
|
+
* @module founder-schema/domains/onboarding/onboarding-answers
|
|
21
|
+
*/
|
|
22
|
+
import { normalizeLinkUrl, normalizePhone, tidyFullName, } from '../../core/common.js';
|
|
23
|
+
import { FOUNDED_YEAR_MIN } from '../../core/limits.js';
|
|
24
|
+
import { canonicalIndiaLocation } from '../location/india.js';
|
|
25
|
+
import { FOUNDER_ONBOARDING_STEPS, GUIDELINES_VERSION, ONBOARDING_STEP_KEYS, readOnboardingDraft, teamSizeDigits, } from './onboarding.js';
|
|
26
|
+
/**
|
|
27
|
+
* A founding year as typed, kept to digits, as many as a year has (0.18.0,
|
|
28
|
+
* moved from the web's Me editor beside `teamSizeDigits`).
|
|
29
|
+
*/
|
|
30
|
+
export const foundedYearDigits = (raw) => raw.replace(/\D/g, '').slice(0, String(FOUNDED_YEAR_MIN).length);
|
|
31
|
+
/**
|
|
32
|
+
* A venture body from what was typed (0.18.0): ONE builder for onboarding's
|
|
33
|
+
* business step and Me's business editor, which each built it by hand and
|
|
34
|
+
* cleaned the team size two ways. Text trimmed, the website made a link
|
|
35
|
+
* (`normalizeLinkUrl`), digits kept (`teamSizeDigits`, `foundedYearDigits`);
|
|
36
|
+
* an empty optional answer is ABSENT. A missing name, industry or stage is
|
|
37
|
+
* left missing, so `ventureSchema` refuses it under its field.
|
|
38
|
+
*/
|
|
39
|
+
export function ventureBodyFrom(v) {
|
|
40
|
+
const tagline = v.tagline?.trim();
|
|
41
|
+
const website = normalizeLinkUrl(v.website);
|
|
42
|
+
const teamSize = teamSizeDigits(v.teamSize);
|
|
43
|
+
const foundedYear = foundedYearDigits(v.foundedYear ?? '');
|
|
44
|
+
return {
|
|
45
|
+
name: v.name.trim(),
|
|
46
|
+
industry: v.industry ?? undefined,
|
|
47
|
+
stage: v.stage ?? undefined,
|
|
48
|
+
...(tagline ? { tagline } : {}),
|
|
49
|
+
...(website ? { website } : {}),
|
|
50
|
+
...(teamSize ? { teamSize: Number(teamSize) } : {}),
|
|
51
|
+
...(foundedYear ? { foundedYear: Number(foundedYear) } : {}),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Answers with nothing filled in: what a new member starts from. */
|
|
55
|
+
export function emptyOnboardingForm() {
|
|
56
|
+
return readOnboardingDraft({});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* True when the member has started describing a business: any of the
|
|
60
|
+
* venture's own answers. Then it is sent, and the schema asks for its name,
|
|
61
|
+
* industry and stage.
|
|
62
|
+
*
|
|
63
|
+
* The business type and the registration do not start it: they DESCRIBE a
|
|
64
|
+
* business. Picked without one, each is sent anyway and refused on its own
|
|
65
|
+
* field (`ventureRequired`: add the business, or clear this), never dropped
|
|
66
|
+
* without a word (hary's ruling).
|
|
67
|
+
*/
|
|
68
|
+
export function ventureStarted(form) {
|
|
69
|
+
const v = form.venture;
|
|
70
|
+
return Boolean(v.name.trim() || v.website.trim() || v.industry || v.stage || v.teamSize.trim());
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* True when a place has been started: a city or a state given. A place opened
|
|
74
|
+
* and left empty is NO place (the member chose not to say), never a half
|
|
75
|
+
* answer to refuse; a place with only one of the two IS half an answer, and
|
|
76
|
+
* the schema asks for the other.
|
|
77
|
+
*/
|
|
78
|
+
export function locationStarted(place) {
|
|
79
|
+
return Boolean(place && (place.city.trim() || (place.state ?? '').trim()));
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The draft one step saves: exactly the fields that step owns
|
|
83
|
+
* (`FOUNDER_ONBOARDING_STEPS[].fields`, the same picks its validation uses),
|
|
84
|
+
* as typed. Only the step's own fields, so a save can never overwrite another
|
|
85
|
+
* step's answers (the server merges per field); raw, so a member who comes back
|
|
86
|
+
* sees what they typed. The photo step's field is its upload id, which the
|
|
87
|
+
* server holds rather than drafts.
|
|
88
|
+
*/
|
|
89
|
+
export function onboardingDraftForStep(form, key) {
|
|
90
|
+
const step = FOUNDER_ONBOARDING_STEPS[ONBOARDING_STEP_KEYS.indexOf(key)];
|
|
91
|
+
const draft = {};
|
|
92
|
+
for (const field of step?.fields ?? [])
|
|
93
|
+
copyField(draft, form, field);
|
|
94
|
+
return draft;
|
|
95
|
+
}
|
|
96
|
+
/** One field across, typed by its key (0.18.0: the draft is the FORM's shape, never `unknown`). */
|
|
97
|
+
function copyField(to, from, key) {
|
|
98
|
+
to[key] = from[key];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* What Finish sends, and what every step is CHECKED as (each step's slice
|
|
102
|
+
* reads its own fields from it).
|
|
103
|
+
*
|
|
104
|
+
* ⚠ ABSENT, NEVER A DEFAULT. The sibling gym product sent `bloodGroup ?? 'O+'`
|
|
105
|
+
* and recorded everyone who skipped the question as O-positive. An optional
|
|
106
|
+
* answer not given is left out; nothing is filled with a plausible value.
|
|
107
|
+
*
|
|
108
|
+
* Cleaned the way the schema reads it: the name tidied (`tidyFullName`), the
|
|
109
|
+
* phone to ten digits (`normalizePhone`), links given their scheme
|
|
110
|
+
* (`normalizeLinkUrl`), the place in the list's own spelling in India
|
|
111
|
+
* (`canonicalIndiaLocation`), the team size a number. A half-answered part is
|
|
112
|
+
* SENT, not hidden: a location without a city, a business without its stage,
|
|
113
|
+
* reach the schema and are refused there, under the field.
|
|
114
|
+
*/
|
|
115
|
+
export function completeOnboardingBodyFrom(form) {
|
|
116
|
+
const headline = form.headline.trim();
|
|
117
|
+
const about = form.about.trim();
|
|
118
|
+
const links = {};
|
|
119
|
+
for (const k of Object.keys(form.links)) {
|
|
120
|
+
const url = normalizeLinkUrl(form.links[k]);
|
|
121
|
+
if (url)
|
|
122
|
+
links[k] = url;
|
|
123
|
+
}
|
|
124
|
+
const body = {
|
|
125
|
+
displayName: tidyFullName(form.displayName),
|
|
126
|
+
gender: form.gender ?? undefined,
|
|
127
|
+
dateOfBirth: form.dateOfBirth.trim(),
|
|
128
|
+
phone: normalizePhone(form.phone),
|
|
129
|
+
goals: form.goals,
|
|
130
|
+
interests: form.interests,
|
|
131
|
+
// Only agreement to the CURRENT text is sent; anything else is absent, so
|
|
132
|
+
// the schema refuses it as not agreed. Consent to an older text is not
|
|
133
|
+
// consent to this one.
|
|
134
|
+
guidelinesVersion: form.guidelinesVersion === GUIDELINES_VERSION ? GUIDELINES_VERSION : undefined,
|
|
135
|
+
};
|
|
136
|
+
if (headline)
|
|
137
|
+
body.headline = headline;
|
|
138
|
+
if (about)
|
|
139
|
+
body.about = about;
|
|
140
|
+
if (form.avatarMediaId)
|
|
141
|
+
body.avatarMediaId = form.avatarMediaId;
|
|
142
|
+
if (form.skills.length)
|
|
143
|
+
body.skills = form.skills;
|
|
144
|
+
if (Object.keys(links).length)
|
|
145
|
+
body.links = links;
|
|
146
|
+
if (form.location && locationStarted(form.location)) {
|
|
147
|
+
const place = canonicalIndiaLocation(form.location);
|
|
148
|
+
body.location = {
|
|
149
|
+
city: place.city,
|
|
150
|
+
...(place.state ? { state: place.state } : {}),
|
|
151
|
+
country: place.country,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
if (ventureStarted(form))
|
|
155
|
+
body.venture = ventureBodyFrom(form.venture);
|
|
156
|
+
// Sent whenever given: with a business they describe it, without one the
|
|
157
|
+
// schema refuses each on its own field rather than anything vanishing.
|
|
158
|
+
if (form.businessType)
|
|
159
|
+
body.businessType = form.businessType;
|
|
160
|
+
if (form.legalStructure)
|
|
161
|
+
body.legalStructure = form.legalStructure;
|
|
162
|
+
return body;
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=onboarding-answers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboarding-answers.js","sourceRoot":"","sources":["../../../src/domains/onboarding/onboarding-answers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema — From a member's onboarding answers to what is sent (0.17.0)\n *\n * The answers are an `OnboardingForm` (the in-progress shape: raw text, `null`\n * for a choice not made). Two things are built from them, and until 0.17.0\n * each client built them by hand, so a second client (the app) would have had\n * to copy every rule below and keep it in step:\n *\n * the draft a step SAVES exactly the fields that step owns, as typed\n * (`onboardingDraftForStep`)\n * the body FINISH sends completed answers only, cleaned, every\n * unanswered optional field ABSENT\n * (`completeOnboardingBodyFrom`)\n *\n * Neither validates: `ONBOARDING_STEP_SCHEMAS` and `completeOnboardingBodySchema`\n * do, on what these build. A half-filled form builds a half-filled body on\n * purpose, so the schema refuses it with the reason under each field instead\n * of a builder hiding the gap.\n *\n * @module founder-schema/domains/onboarding/onboarding-answers\n */\n\nimport {\n type Industry,\n normalizeLinkUrl,\n normalizePhone,\n type StartupStage,\n tidyFullName,\n} from '../../core/common.js';\nimport { FOUNDED_YEAR_MIN } from '../../core/limits.js';\nimport { canonicalIndiaLocation } from '../location/india.js';\nimport {\n type CompleteOnboardingBodyInput,\n FOUNDER_ONBOARDING_STEPS,\n GUIDELINES_VERSION,\n ONBOARDING_STEP_KEYS,\n type OnboardingForm,\n type OnboardingStepKey,\n readOnboardingDraft,\n teamSizeDigits,\n} from './onboarding.js';\n\ntype DeepPartial<T> = T extends readonly (infer Item)[]\n ? DeepPartial<Item>[]\n : T extends object\n ? { [K in keyof T]?: DeepPartial<T[K]> }\n : T;\n\n/**\n * A founding year as typed, kept to digits, as many as a year has (0.18.0,\n * moved from the web's Me editor beside `teamSizeDigits`).\n */\nexport const foundedYearDigits = (raw: string): string =>\n raw.replace(/\\D/g, '').slice(0, String(FOUNDED_YEAR_MIN).length);\n\n/**\n * A venture body from what was typed (0.18.0): ONE builder for onboarding's\n * business step and Me's business editor, which each built it by hand and\n * cleaned the team size two ways. Text trimmed, the website made a link\n * (`normalizeLinkUrl`), digits kept (`teamSizeDigits`, `foundedYearDigits`);\n * an empty optional answer is ABSENT. A missing name, industry or stage is\n * left missing, so `ventureSchema` refuses it under its field.\n */\nexport function ventureBodyFrom(v: {\n name: string;\n website: string;\n industry: Industry | null;\n stage: StartupStage | null;\n teamSize: string;\n tagline?: string;\n foundedYear?: string;\n}): {\n name: string;\n industry: Industry | undefined;\n stage: StartupStage | undefined;\n tagline?: string;\n website?: string;\n teamSize?: number;\n foundedYear?: number;\n} {\n const tagline = v.tagline?.trim();\n const website = normalizeLinkUrl(v.website);\n const teamSize = teamSizeDigits(v.teamSize);\n const foundedYear = foundedYearDigits(v.foundedYear ?? '');\n return {\n name: v.name.trim(),\n industry: v.industry ?? undefined,\n stage: v.stage ?? undefined,\n ...(tagline ? { tagline } : {}),\n ...(website ? { website } : {}),\n ...(teamSize ? { teamSize: Number(teamSize) } : {}),\n ...(foundedYear ? { foundedYear: Number(foundedYear) } : {}),\n };\n}\n\n/**\n * What `completeOnboardingBodyFrom` builds: the finish body's INPUT, any part\n * of it possibly still missing. Not a valid body until\n * `completeOnboardingBodySchema` says so.\n */\nexport type CompleteOnboardingBodyCandidate = DeepPartial<CompleteOnboardingBodyInput>;\n\n/** Answers with nothing filled in: what a new member starts from. */\nexport function emptyOnboardingForm(): OnboardingForm {\n return readOnboardingDraft({});\n}\n\n/**\n * True when the member has started describing a business: any of the\n * venture's own answers. Then it is sent, and the schema asks for its name,\n * industry and stage.\n *\n * The business type and the registration do not start it: they DESCRIBE a\n * business. Picked without one, each is sent anyway and refused on its own\n * field (`ventureRequired`: add the business, or clear this), never dropped\n * without a word (hary's ruling).\n */\nexport function ventureStarted(form: Pick<OnboardingForm, 'venture'>): boolean {\n const v = form.venture;\n return Boolean(v.name.trim() || v.website.trim() || v.industry || v.stage || v.teamSize.trim());\n}\n\n/**\n * True when a place has been started: a city or a state given. A place opened\n * and left empty is NO place (the member chose not to say), never a half\n * answer to refuse; a place with only one of the two IS half an answer, and\n * the schema asks for the other.\n */\nexport function locationStarted(place: { city: string; state?: string | null } | null): boolean {\n return Boolean(place && (place.city.trim() || (place.state ?? '').trim()));\n}\n\n/**\n * The draft one step saves: exactly the fields that step owns\n * (`FOUNDER_ONBOARDING_STEPS[].fields`, the same picks its validation uses),\n * as typed. Only the step's own fields, so a save can never overwrite another\n * step's answers (the server merges per field); raw, so a member who comes back\n * sees what they typed. The photo step's field is its upload id, which the\n * server holds rather than drafts.\n */\nexport function onboardingDraftForStep(\n form: OnboardingForm,\n key: OnboardingStepKey,\n): Partial<OnboardingForm> {\n const step = FOUNDER_ONBOARDING_STEPS[ONBOARDING_STEP_KEYS.indexOf(key)];\n const draft: Partial<OnboardingForm> = {};\n for (const field of step?.fields ?? []) copyField(draft, form, field);\n return draft;\n}\n\n/** One field across, typed by its key (0.18.0: the draft is the FORM's shape, never `unknown`). */\nfunction copyField<K extends keyof OnboardingForm>(\n to: Partial<OnboardingForm>,\n from: OnboardingForm,\n key: K,\n): void {\n to[key] = from[key];\n}\n\n/**\n * What Finish sends, and what every step is CHECKED as (each step's slice\n * reads its own fields from it).\n *\n * ⚠ ABSENT, NEVER A DEFAULT. The sibling gym product sent `bloodGroup ?? 'O+'`\n * and recorded everyone who skipped the question as O-positive. An optional\n * answer not given is left out; nothing is filled with a plausible value.\n *\n * Cleaned the way the schema reads it: the name tidied (`tidyFullName`), the\n * phone to ten digits (`normalizePhone`), links given their scheme\n * (`normalizeLinkUrl`), the place in the list's own spelling in India\n * (`canonicalIndiaLocation`), the team size a number. A half-answered part is\n * SENT, not hidden: a location without a city, a business without its stage,\n * reach the schema and are refused there, under the field.\n */\nexport function completeOnboardingBodyFrom(form: OnboardingForm): CompleteOnboardingBodyCandidate {\n const headline = form.headline.trim();\n const about = form.about.trim();\n\n const links: Partial<Record<keyof OnboardingForm['links'], string>> = {};\n for (const k of Object.keys(form.links) as (keyof OnboardingForm['links'])[]) {\n const url = normalizeLinkUrl(form.links[k]);\n if (url) links[k] = url;\n }\n\n const body: CompleteOnboardingBodyCandidate = {\n displayName: tidyFullName(form.displayName),\n gender: form.gender ?? undefined,\n dateOfBirth: form.dateOfBirth.trim(),\n phone: normalizePhone(form.phone),\n goals: form.goals,\n interests: form.interests,\n // Only agreement to the CURRENT text is sent; anything else is absent, so\n // the schema refuses it as not agreed. Consent to an older text is not\n // consent to this one.\n guidelinesVersion:\n form.guidelinesVersion === GUIDELINES_VERSION ? GUIDELINES_VERSION : undefined,\n };\n if (headline) body.headline = headline;\n if (about) body.about = about;\n if (form.avatarMediaId) body.avatarMediaId = form.avatarMediaId;\n if (form.skills.length) body.skills = form.skills;\n if (Object.keys(links).length) body.links = links;\n\n if (form.location && locationStarted(form.location)) {\n const place = canonicalIndiaLocation(form.location);\n body.location = {\n city: place.city,\n ...(place.state ? { state: place.state } : {}),\n country: place.country,\n };\n }\n\n if (ventureStarted(form)) body.venture = ventureBodyFrom(form.venture);\n // Sent whenever given: with a business they describe it, without one the\n // schema refuses each on its own field rather than anything vanishing.\n if (form.businessType) body.businessType = form.businessType;\n if (form.legalStructure) body.legalStructure = form.legalStructure;\n\n return body;\n}\n"]}
|