founder-schema 0.10.0 → 0.12.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 +98 -0
- package/dist/access/access.d.ts +10 -6
- package/dist/access/access.d.ts.map +1 -1
- package/dist/access/access.js +8 -1
- package/dist/access/access.js.map +1 -1
- package/dist/access/permissions.d.ts +60 -3
- package/dist/access/permissions.d.ts.map +1 -1
- package/dist/access/permissions.js +24 -0
- package/dist/access/permissions.js.map +1 -1
- package/dist/access/roles.d.ts +3 -3
- package/dist/access/standing.d.ts +59 -0
- package/dist/access/standing.d.ts.map +1 -0
- package/dist/access/standing.js +52 -0
- package/dist/access/standing.js.map +1 -0
- package/dist/core/errors.d.ts +47 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +82 -1
- package/dist/core/errors.js.map +1 -1
- package/dist/core/limits.d.ts +39 -3
- package/dist/core/limits.d.ts.map +1 -1
- package/dist/core/limits.js +41 -3
- package/dist/core/limits.js.map +1 -1
- package/dist/core/responses.d.ts +55 -0
- package/dist/core/responses.d.ts.map +1 -1
- package/dist/core/responses.js +6 -0
- package/dist/core/responses.js.map +1 -1
- package/dist/core/validation.d.ts +14 -0
- package/dist/core/validation.d.ts.map +1 -1
- package/dist/core/validation.js +14 -0
- package/dist/core/validation.js.map +1 -1
- package/dist/domains/audit/audit.d.ts +28 -7
- package/dist/domains/audit/audit.d.ts.map +1 -1
- package/dist/domains/audit/audit.js +17 -0
- package/dist/domains/audit/audit.js.map +1 -1
- package/dist/domains/billing/billing.d.ts +1179 -0
- package/dist/domains/billing/billing.d.ts.map +1 -0
- package/dist/domains/billing/billing.js +393 -0
- package/dist/domains/billing/billing.js.map +1 -0
- package/dist/domains/billing/gst.d.ts +229 -0
- package/dist/domains/billing/gst.d.ts.map +1 -0
- package/dist/domains/billing/gst.js +117 -0
- package/dist/domains/billing/gst.js.map +1 -0
- package/dist/domains/chat/chat.d.ts +85 -0
- package/dist/domains/chat/chat.d.ts.map +1 -1
- package/dist/domains/chat/chat.js +62 -0
- package/dist/domains/chat/chat.js.map +1 -1
- package/dist/domains/communities/communities.d.ts +11 -11
- package/dist/domains/dashboard/dashboard.d.ts +9 -1
- package/dist/domains/dashboard/dashboard.d.ts.map +1 -1
- package/dist/domains/dashboard/dashboard.js +15 -0
- package/dist/domains/dashboard/dashboard.js.map +1 -1
- package/dist/domains/feed/feed.d.ts.map +1 -1
- package/dist/domains/feed/feed.js +7 -1
- package/dist/domains/feed/feed.js.map +1 -1
- package/dist/domains/members/members.d.ts +30 -5
- package/dist/domains/members/members.d.ts.map +1 -1
- package/dist/domains/members/members.js +23 -0
- package/dist/domains/members/members.js.map +1 -1
- package/dist/domains/membership/membership.d.ts +239 -0
- package/dist/domains/membership/membership.d.ts.map +1 -0
- package/dist/domains/membership/membership.js +153 -0
- package/dist/domains/membership/membership.js.map +1 -0
- package/dist/domains/notifications/notifications.d.ts +43 -1
- package/dist/domains/notifications/notifications.d.ts.map +1 -1
- package/dist/domains/notifications/notifications.js +19 -0
- package/dist/domains/notifications/notifications.js.map +1 -1
- package/dist/domains/products/products.d.ts +44 -12
- package/dist/domains/products/products.d.ts.map +1 -1
- package/dist/domains/products/products.js +33 -3
- package/dist/domains/products/products.js.map +1 -1
- package/dist/domains/profile/founder-profile.d.ts +178 -0
- package/dist/domains/profile/founder-profile.d.ts.map +1 -1
- package/dist/domains/profile/founder-profile.js +77 -0
- package/dist/domains/profile/founder-profile.js.map +1 -1
- package/dist/domains/reviews/reviews.d.ts +3 -0
- package/dist/domains/reviews/reviews.d.ts.map +1 -1
- package/dist/domains/reviews/reviews.js +6 -1
- package/dist/domains/reviews/reviews.js.map +1 -1
- package/dist/domains/team/team.d.ts +4 -4
- package/dist/index.d.ts +7 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -56,9 +56,9 @@ export declare const memberDirectoryQuerySchema: z.ZodObject<{
|
|
|
56
56
|
suspended: "suspended";
|
|
57
57
|
}>>, unknown>;
|
|
58
58
|
role: z.ZodPreprocess<z.ZodOptional<z.ZodEnum<{
|
|
59
|
+
moderator: "moderator";
|
|
59
60
|
superadmin: "superadmin";
|
|
60
61
|
manager: "manager";
|
|
61
|
-
moderator: "moderator";
|
|
62
62
|
support_agent: "support_agent";
|
|
63
63
|
}>>, unknown>;
|
|
64
64
|
}, z.core.$strip>;
|
|
@@ -76,9 +76,9 @@ export declare const memberSummarySchema: z.ZodObject<{
|
|
|
76
76
|
suspended: "suspended";
|
|
77
77
|
}>;
|
|
78
78
|
roles: z.ZodArray<z.ZodEnum<{
|
|
79
|
+
moderator: "moderator";
|
|
79
80
|
superadmin: "superadmin";
|
|
80
81
|
manager: "manager";
|
|
81
|
-
moderator: "moderator";
|
|
82
82
|
support_agent: "support_agent";
|
|
83
83
|
}>>;
|
|
84
84
|
joinedAt: z.ZodISODateTime;
|
|
@@ -97,9 +97,9 @@ export declare const memberDirectoryPageSchema: z.ZodObject<{
|
|
|
97
97
|
suspended: "suspended";
|
|
98
98
|
}>;
|
|
99
99
|
roles: z.ZodArray<z.ZodEnum<{
|
|
100
|
+
moderator: "moderator";
|
|
100
101
|
superadmin: "superadmin";
|
|
101
102
|
manager: "manager";
|
|
102
|
-
moderator: "moderator";
|
|
103
103
|
support_agent: "support_agent";
|
|
104
104
|
}>>;
|
|
105
105
|
joinedAt: z.ZodISODateTime;
|
|
@@ -130,9 +130,9 @@ export declare const memberDetailSchema: z.ZodObject<{
|
|
|
130
130
|
suspended: "suspended";
|
|
131
131
|
}>;
|
|
132
132
|
roles: z.ZodArray<z.ZodEnum<{
|
|
133
|
+
moderator: "moderator";
|
|
133
134
|
superadmin: "superadmin";
|
|
134
135
|
manager: "manager";
|
|
135
|
-
moderator: "moderator";
|
|
136
136
|
support_agent: "support_agent";
|
|
137
137
|
}>>;
|
|
138
138
|
joinedAt: z.ZodISODateTime;
|
|
@@ -157,16 +157,41 @@ export declare const memberDetailSchema: z.ZodObject<{
|
|
|
157
157
|
viewer: z.ZodObject<{
|
|
158
158
|
canSuspend: z.ZodBoolean;
|
|
159
159
|
grantableRoles: z.ZodArray<z.ZodEnum<{
|
|
160
|
+
moderator: "moderator";
|
|
160
161
|
superadmin: "superadmin";
|
|
161
162
|
manager: "manager";
|
|
162
|
-
moderator: "moderator";
|
|
163
163
|
support_agent: "support_agent";
|
|
164
164
|
}>>;
|
|
165
165
|
canResetMpin: z.ZodBoolean;
|
|
166
166
|
grantableCapabilities: z.ZodArray<z.ZodEnum<{
|
|
167
167
|
create_communities: "create_communities";
|
|
168
168
|
}>>;
|
|
169
|
+
canGrantMembership: z.ZodBoolean;
|
|
170
|
+
canRevokeMembership: z.ZodBoolean;
|
|
171
|
+
canReadPayments: z.ZodBoolean;
|
|
172
|
+
canReadMembership: z.ZodBoolean;
|
|
169
173
|
}, z.core.$strip>;
|
|
174
|
+
standing: z.ZodEnum<{
|
|
175
|
+
viewer: "viewer";
|
|
176
|
+
member: "member";
|
|
177
|
+
}>;
|
|
178
|
+
membership: z.ZodNullable<z.ZodObject<{
|
|
179
|
+
kind: z.ZodEnum<{
|
|
180
|
+
founding: "founding";
|
|
181
|
+
coupon: "coupon";
|
|
182
|
+
paid: "paid";
|
|
183
|
+
granted: "granted";
|
|
184
|
+
}>;
|
|
185
|
+
since: z.ZodISODateTime;
|
|
186
|
+
foundingNumber: z.ZodNullable<z.ZodNumber>;
|
|
187
|
+
}, z.core.$strip>>;
|
|
188
|
+
plan: z.ZodNullable<z.ZodObject<{
|
|
189
|
+
tier: z.ZodEnum<{
|
|
190
|
+
premium: "premium";
|
|
191
|
+
business: "business";
|
|
192
|
+
}>;
|
|
193
|
+
endsAt: z.ZodISODateTime;
|
|
194
|
+
}, z.core.$strip>>;
|
|
170
195
|
capabilities: z.ZodArray<z.ZodEnum<{
|
|
171
196
|
create_communities: "create_communities";
|
|
172
197
|
}>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"members.d.ts","sourceRoot":"","sources":["../../../src/domains/members/members.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"members.d.ts","sourceRoot":"","sources":["../../../src/domains/members/members.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAmBxB,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,kCAAmC,CAAC;AACrE,eAAO,MAAM,kBAAkB;;;EAA+B,CAAC;AAC/D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,sCAAsC;AACtC,eAAO,MAAM,kBAAkB;;iBAAyC,CAAC;AACzE,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;;GAIG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;iBAKrC,CAAC;AACH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC9E,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAEnF,gCAAgC;AAChC,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;iBAa9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;iBAAuC,CAAC;AAC9E,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,6BAA6B;AAC7B,eAAO,MAAM,gBAAgB;;;;;;;iBAK3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,qCAAqC;AACrC,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiD7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D,sFAAsF;AACtF,eAAO,MAAM,uBAAuB;;iBAElC,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC"}
|
|
@@ -32,9 +32,11 @@
|
|
|
32
32
|
import { z } from 'zod';
|
|
33
33
|
import { capabilitySchema } from '../../access/capabilities.js';
|
|
34
34
|
import { platformRoleSchema } from '../../access/roles.js';
|
|
35
|
+
import { standingSchema } from '../../access/standing.js';
|
|
35
36
|
import { emptyStringToUndefined, objectIdSchema, optionalTrimmedString, paginationQuerySchema, usernameSchema, } from '../../core/common.js';
|
|
36
37
|
import { MEMBER_SEARCH_MAX, SUSPENSION_REASON_MAX, SUSPENSION_REASON_MIN, } from '../../core/limits.js';
|
|
37
38
|
import { paginatedSchema } from '../../core/responses.js';
|
|
39
|
+
import { membershipSchema, planTierSchema } from '../membership/membership.js';
|
|
38
40
|
/** What staff can see an account's state as. A deleted account is not listed. */
|
|
39
41
|
export const MEMBER_STATUS_VALUES = ['active', 'suspended'];
|
|
40
42
|
export const memberStatusSchema = z.enum(MEMBER_STATUS_VALUES);
|
|
@@ -98,7 +100,28 @@ export const memberDetailSchema = memberSummarySchema.extend({
|
|
|
98
100
|
canResetMpin: z.boolean(),
|
|
99
101
|
/** Capabilities the asker may grant or revoke on this member (never their own) (0.9.0). */
|
|
100
102
|
grantableCapabilities: z.array(capabilitySchema),
|
|
103
|
+
/** May give them a membership (0.12.0): they hold none, and the asker may, and it is not themselves. */
|
|
104
|
+
canGrantMembership: z.boolean(),
|
|
105
|
+
/**
|
|
106
|
+
* May take their membership away (0.12.0): founding, coupon or granted
|
|
107
|
+
* (a paid one is refunded instead), and the asker may, and not their own.
|
|
108
|
+
*/
|
|
109
|
+
canRevokeMembership: z.boolean(),
|
|
110
|
+
/** May list their payments (0.12.0): support, managers, superadmin. */
|
|
111
|
+
canReadPayments: z.boolean(),
|
|
112
|
+
/**
|
|
113
|
+
* May see their membership and plan (0.12.0): support, managers,
|
|
114
|
+
* superadmin. Moderators may not: `membership` and `plan` come null to
|
|
115
|
+
* them, which is not "not a member".
|
|
116
|
+
*/
|
|
117
|
+
canReadMembership: z.boolean(),
|
|
101
118
|
}),
|
|
119
|
+
/** Whether they take part (0.12.0). Staff always do. */
|
|
120
|
+
standing: standingSchema,
|
|
121
|
+
/** Their membership (0.12.0), or null (a viewer, staff holding none, or an asker who may not see it). */
|
|
122
|
+
membership: membershipSchema.nullable(),
|
|
123
|
+
/** The plan in force now and when all their plan time ends (0.12.0), or null (none, or not the asker's to see). */
|
|
124
|
+
plan: z.object({ tier: planTierSchema, endsAt: z.iso.datetime() }).nullable(),
|
|
102
125
|
/** The capabilities they hold (0.9.0), e.g. `create_communities`. */
|
|
103
126
|
capabilities: z.array(capabilitySchema),
|
|
104
127
|
/** Whether they have set an MPIN. Only meaningful for staff. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"members.js","sourceRoot":"","sources":["../../../src/domains/members/members.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EACL,sBAAsB,EACtB,cAAc,EACd,qBAAqB,EACrB,qBAAqB,EACrB,cAAc,GACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE1D,iFAAiF;AACjF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,QAAQ,EAAE,WAAW,CAAU,CAAC;AACrE,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;AAG/D,sCAAsC;AACtC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC;AAGzE;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,qBAAqB,CAAC,MAAM,CAAC;IACrE,CAAC,EAAE,qBAAqB,CAAC,iBAAiB,CAAC;IAC3C,MAAM,EAAE,CAAC,CAAC,UAAU,CAAC,sBAAsB,EAAE,kBAAkB,CAAC,QAAQ,EAAE,CAAC;IAC3E,2CAA2C;IAC3C,IAAI,EAAE,CAAC,CAAC,UAAU,CAAC,sBAAsB,EAAE,kBAAkB,CAAC,QAAQ,EAAE,CAAC;CAC1E,CAAC,CAAC;AAIH,gCAAgC;AAChC,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,EAAE,EAAE,cAAc;IAClB,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;IACvB,0EAA0E;IAC1E,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE;IACnC,SAAS,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC7B,gDAAgD;IAChD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,MAAM,EAAE,kBAAkB;IAC1B,qEAAqE;IACrE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC;IAClC,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE;IAC1B,YAAY,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CAC1C,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,yBAAyB,GAAG,eAAe,CAAC,mBAAmB,CAAC,CAAC;AAG9E,6BAA6B;AAC7B,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE;IACvB,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;IAClB,+CAA+C;IAC/C,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE;CACzE,CAAC,CAAC;AAGH,qCAAqC;AACrC,MAAM,CAAC,MAAM,kBAAkB,GAAG,mBAAmB,CAAC,MAAM,CAAC;IAC3D,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC/B,gDAAgD;IAChD,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QAC9B,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACjC,6EAA6E;QAC7E,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACvC,gCAAgC;QAChC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;KACjC,CAAC;IACF,UAAU,EAAE,gBAAgB,CAAC,QAAQ,EAAE;IACvC,0DAA0D;IAC1D,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACf,UAAU,EAAE,CAAC,CAAC,OAAO,EAAE;QACvB,4EAA4E;QAC5E,cAAc,EAAE,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC;QAC3C,yEAAyE;QACzE,YAAY,EAAE,CAAC,CAAC,OAAO,EAAE;QACzB,2FAA2F;QAC3F,qBAAqB,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC;KACjD,CAAC;IACF,qEAAqE;IACrE,YAAY,EAAE,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC;IACvC,gEAAgE;IAChE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;CACrB,CAAC,CAAC;AAGH,sFAAsF;AACtF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC9C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,GAAG,CAAC,qBAAqB,CAAC;CAChF,CAAC,CAAC","sourcesContent":["/**\n * founder-schema: the staff member directory\n *\n * GET /api/v1/members ?q&status&role&limit&cursor\n * GET /api/v1/members/:memberId one member, as staff see them\n * POST /api/v1/members/:memberId/suspension { reason } suspend\n * DELETE /api/v1/members/:memberId/suspension lift it\n *\n * Every staff role has a Members tab: a moderator looks up who wrote\n * something, a support agent finds the person who wrote in, a manager decides\n * on a suspension. What each sees is the server's call, per field: a\n * moderator gets no email (`email: null`), because finding a member's\n * address is support's job, not moderation's.\n *\n * A SUSPENDED member cannot use the product at all, on any device, from the\n * next request on (every call answers `ACCOUNT_SUSPENDED`); their posts stay\n * up unless moderated separately, and a staff role they were acting in is\n * switched off. Lifting it restores everything else.\n *\n * SEARCH (`q`) finds people by the start of their name or handle among listed\n * founders, or by their exact email among everyone (only for a role that may\n * see emails: a moderator's email search finds nothing). It returns up to\n * `limit` matches in one page (type more to narrow), not a pageable list. Who may suspend whom is the access policy's: never\n * yourself, never a superadmin, and a manager never a fellow manager.\n *\n * Each detail carries a `viewer` block of what the asking staff member may do\n * to THIS member, worked out by the server, so the screen shows only the\n * buttons that will work.\n *\n * @module founder-schema/domains/members\n */\n\nimport { z } from 'zod';\nimport { capabilitySchema } from '../../access/capabilities.js';\nimport { platformRoleSchema } from '../../access/roles.js';\nimport {\n emptyStringToUndefined,\n objectIdSchema,\n optionalTrimmedString,\n paginationQuerySchema,\n usernameSchema,\n} from '../../core/common.js';\nimport {\n MEMBER_SEARCH_MAX,\n SUSPENSION_REASON_MAX,\n SUSPENSION_REASON_MIN,\n} from '../../core/limits.js';\nimport { paginatedSchema } from '../../core/responses.js';\n\n/** What staff can see an account's state as. A deleted account is not listed. */\nexport const MEMBER_STATUS_VALUES = ['active', 'suspended'] as const;\nexport const memberStatusSchema = z.enum(MEMBER_STATUS_VALUES);\nexport type MemberStatus = z.infer<typeof memberStatusSchema>;\n\n/** `:memberId` in the paths above. */\nexport const memberParamsSchema = z.object({ memberId: objectIdSchema });\nexport type MemberParams = z.infer<typeof memberParamsSchema>;\n\n/**\n * The directory's query. `q` is a name or handle prefix, or a whole email\n * address (anything with an `@` is matched as an email, exactly). An emptied\n * box is \"no search\", not an error.\n */\nexport const memberDirectoryQuerySchema = paginationQuerySchema.extend({\n q: optionalTrimmedString(MEMBER_SEARCH_MAX),\n status: z.preprocess(emptyStringToUndefined, memberStatusSchema.optional()),\n /** Only people holding this staff role. */\n role: z.preprocess(emptyStringToUndefined, platformRoleSchema.optional()),\n});\nexport type MemberDirectoryQuery = z.infer<typeof memberDirectoryQuerySchema>;\nexport type MemberDirectoryQueryInput = z.input<typeof memberDirectoryQuerySchema>;\n\n/** One row of the directory. */\nexport const memberSummarySchema = z.object({\n id: objectIdSchema,\n displayName: z.string(),\n /** Null when they have no founder profile (never finished onboarding). */\n username: usernameSchema.nullable(),\n avatarUrl: z.url().nullable(),\n /** Null when the asking role may not see it. */\n email: z.string().nullable(),\n status: memberStatusSchema,\n /** Staff roles held, highest first. Empty for an ordinary member. */\n roles: z.array(platformRoleSchema),\n joinedAt: z.iso.datetime(),\n lastActiveAt: z.iso.datetime().nullable(),\n});\nexport type MemberSummary = z.infer<typeof memberSummarySchema>;\n\nexport const memberDirectoryPageSchema = paginatedSchema(memberSummarySchema);\nexport type MemberDirectoryPage = z.infer<typeof memberDirectoryPageSchema>;\n\n/** A suspension in force. */\nexport const suspensionSchema = z.object({\n since: z.iso.datetime(),\n reason: z.string(),\n /** Who suspended them, by name at the time. */\n by: z.object({ id: objectIdSchema, displayName: z.string() }).nullable(),\n});\nexport type Suspension = z.infer<typeof suspensionSchema>;\n\n/** One member, as staff see them. */\nexport const memberDetailSchema = memberSummarySchema.extend({\n headline: z.string().nullable(),\n /** Their city, as declared on their profile. */\n city: z.string().nullable(),\n counts: z.object({\n posts: z.number().int().min(0),\n polls: z.number().int().min(0),\n comments: z.number().int().min(0),\n /** Reports filed about their posts, polls and comments that are still up. */\n reportsAgainst: z.number().int().min(0),\n /** Tickets they have raised. */\n tickets: z.number().int().min(0),\n }),\n suspension: suspensionSchema.nullable(),\n /** What the asking staff member may do to this member. */\n viewer: z.object({\n canSuspend: z.boolean(),\n /** Roles the asker may grant or revoke on this member (never their own). */\n grantableRoles: z.array(platformRoleSchema),\n /** May clear this member's forgotten MPIN (superadmin, someone else). */\n canResetMpin: z.boolean(),\n /** Capabilities the asker may grant or revoke on this member (never their own) (0.9.0). */\n grantableCapabilities: z.array(capabilitySchema),\n }),\n /** The capabilities they hold (0.9.0), e.g. `create_communities`. */\n capabilities: z.array(capabilitySchema),\n /** Whether they have set an MPIN. Only meaningful for staff. */\n mpinSet: z.boolean(),\n});\nexport type MemberDetail = z.infer<typeof memberDetailSchema>;\n\n/** `POST /members/:memberId/suspension`. The reason is on record in the audit log. */\nexport const suspendMemberBodySchema = z.object({\n reason: z.string().trim().min(SUSPENSION_REASON_MIN).max(SUSPENSION_REASON_MAX),\n});\nexport type SuspendMemberBody = z.infer<typeof suspendMemberBodySchema>;\n"]}
|
|
1
|
+
{"version":3,"file":"members.js","sourceRoot":"","sources":["../../../src/domains/members/members.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema: the staff member directory\n *\n * GET /api/v1/members ?q&status&role&limit&cursor\n * GET /api/v1/members/:memberId one member, as staff see them\n * POST /api/v1/members/:memberId/suspension { reason } suspend\n * DELETE /api/v1/members/:memberId/suspension lift it\n *\n * Every staff role has a Members tab: a moderator looks up who wrote\n * something, a support agent finds the person who wrote in, a manager decides\n * on a suspension. What each sees is the server's call, per field: a\n * moderator gets no email (`email: null`), because finding a member's\n * address is support's job, not moderation's.\n *\n * A SUSPENDED member cannot use the product at all, on any device, from the\n * next request on (every call answers `ACCOUNT_SUSPENDED`); their posts stay\n * up unless moderated separately, and a staff role they were acting in is\n * switched off. Lifting it restores everything else.\n *\n * SEARCH (`q`) finds people by the start of their name or handle among listed\n * founders, or by their exact email among everyone (only for a role that may\n * see emails: a moderator's email search finds nothing). It returns up to\n * `limit` matches in one page (type more to narrow), not a pageable list. Who may suspend whom is the access policy's: never\n * yourself, never a superadmin, and a manager never a fellow manager.\n *\n * Each detail carries a `viewer` block of what the asking staff member may do\n * to THIS member, worked out by the server, so the screen shows only the\n * buttons that will work.\n *\n * @module founder-schema/domains/members\n */\n\nimport { z } from 'zod';\nimport { capabilitySchema } from '../../access/capabilities.js';\nimport { platformRoleSchema } from '../../access/roles.js';\nimport { standingSchema } from '../../access/standing.js';\nimport {\n emptyStringToUndefined,\n objectIdSchema,\n optionalTrimmedString,\n paginationQuerySchema,\n usernameSchema,\n} from '../../core/common.js';\nimport {\n MEMBER_SEARCH_MAX,\n SUSPENSION_REASON_MAX,\n SUSPENSION_REASON_MIN,\n} from '../../core/limits.js';\nimport { paginatedSchema } from '../../core/responses.js';\nimport { membershipSchema, planTierSchema } from '../membership/membership.js';\n\n/** What staff can see an account's state as. A deleted account is not listed. */\nexport const MEMBER_STATUS_VALUES = ['active', 'suspended'] as const;\nexport const memberStatusSchema = z.enum(MEMBER_STATUS_VALUES);\nexport type MemberStatus = z.infer<typeof memberStatusSchema>;\n\n/** `:memberId` in the paths above. */\nexport const memberParamsSchema = z.object({ memberId: objectIdSchema });\nexport type MemberParams = z.infer<typeof memberParamsSchema>;\n\n/**\n * The directory's query. `q` is a name or handle prefix, or a whole email\n * address (anything with an `@` is matched as an email, exactly). An emptied\n * box is \"no search\", not an error.\n */\nexport const memberDirectoryQuerySchema = paginationQuerySchema.extend({\n q: optionalTrimmedString(MEMBER_SEARCH_MAX),\n status: z.preprocess(emptyStringToUndefined, memberStatusSchema.optional()),\n /** Only people holding this staff role. */\n role: z.preprocess(emptyStringToUndefined, platformRoleSchema.optional()),\n});\nexport type MemberDirectoryQuery = z.infer<typeof memberDirectoryQuerySchema>;\nexport type MemberDirectoryQueryInput = z.input<typeof memberDirectoryQuerySchema>;\n\n/** One row of the directory. */\nexport const memberSummarySchema = z.object({\n id: objectIdSchema,\n displayName: z.string(),\n /** Null when they have no founder profile (never finished onboarding). */\n username: usernameSchema.nullable(),\n avatarUrl: z.url().nullable(),\n /** Null when the asking role may not see it. */\n email: z.string().nullable(),\n status: memberStatusSchema,\n /** Staff roles held, highest first. Empty for an ordinary member. */\n roles: z.array(platformRoleSchema),\n joinedAt: z.iso.datetime(),\n lastActiveAt: z.iso.datetime().nullable(),\n});\nexport type MemberSummary = z.infer<typeof memberSummarySchema>;\n\nexport const memberDirectoryPageSchema = paginatedSchema(memberSummarySchema);\nexport type MemberDirectoryPage = z.infer<typeof memberDirectoryPageSchema>;\n\n/** A suspension in force. */\nexport const suspensionSchema = z.object({\n since: z.iso.datetime(),\n reason: z.string(),\n /** Who suspended them, by name at the time. */\n by: z.object({ id: objectIdSchema, displayName: z.string() }).nullable(),\n});\nexport type Suspension = z.infer<typeof suspensionSchema>;\n\n/** One member, as staff see them. */\nexport const memberDetailSchema = memberSummarySchema.extend({\n headline: z.string().nullable(),\n /** Their city, as declared on their profile. */\n city: z.string().nullable(),\n counts: z.object({\n posts: z.number().int().min(0),\n polls: z.number().int().min(0),\n comments: z.number().int().min(0),\n /** Reports filed about their posts, polls and comments that are still up. */\n reportsAgainst: z.number().int().min(0),\n /** Tickets they have raised. */\n tickets: z.number().int().min(0),\n }),\n suspension: suspensionSchema.nullable(),\n /** What the asking staff member may do to this member. */\n viewer: z.object({\n canSuspend: z.boolean(),\n /** Roles the asker may grant or revoke on this member (never their own). */\n grantableRoles: z.array(platformRoleSchema),\n /** May clear this member's forgotten MPIN (superadmin, someone else). */\n canResetMpin: z.boolean(),\n /** Capabilities the asker may grant or revoke on this member (never their own) (0.9.0). */\n grantableCapabilities: z.array(capabilitySchema),\n /** May give them a membership (0.12.0): they hold none, and the asker may, and it is not themselves. */\n canGrantMembership: z.boolean(),\n /**\n * May take their membership away (0.12.0): founding, coupon or granted\n * (a paid one is refunded instead), and the asker may, and not their own.\n */\n canRevokeMembership: z.boolean(),\n /** May list their payments (0.12.0): support, managers, superadmin. */\n canReadPayments: z.boolean(),\n /**\n * May see their membership and plan (0.12.0): support, managers,\n * superadmin. Moderators may not: `membership` and `plan` come null to\n * them, which is not \"not a member\".\n */\n canReadMembership: z.boolean(),\n }),\n /** Whether they take part (0.12.0). Staff always do. */\n standing: standingSchema,\n /** Their membership (0.12.0), or null (a viewer, staff holding none, or an asker who may not see it). */\n membership: membershipSchema.nullable(),\n /** The plan in force now and when all their plan time ends (0.12.0), or null (none, or not the asker's to see). */\n plan: z.object({ tier: planTierSchema, endsAt: z.iso.datetime() }).nullable(),\n /** The capabilities they hold (0.9.0), e.g. `create_communities`. */\n capabilities: z.array(capabilitySchema),\n /** Whether they have set an MPIN. Only meaningful for staff. */\n mpinSet: z.boolean(),\n});\nexport type MemberDetail = z.infer<typeof memberDetailSchema>;\n\n/** `POST /members/:memberId/suspension`. The reason is on record in the audit log. */\nexport const suspendMemberBodySchema = z.object({\n reason: z.string().trim().min(SUSPENSION_REASON_MIN).max(SUSPENSION_REASON_MAX),\n});\nexport type SuspendMemberBody = z.infer<typeof suspendMemberBodySchema>;\n"]}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* founder-schema: membership and plans, as a member holds them (0.12.0)
|
|
3
|
+
*
|
|
4
|
+
* GET /api/v1/plans planCatalogSchema (anyone: prices and what each includes)
|
|
5
|
+
* GET /api/v1/me/membership meMembershipSchema (signed in: what I hold, what I have used)
|
|
6
|
+
*
|
|
7
|
+
* THE USER'S RULING (2026-10-11): looking is free; taking part needs a
|
|
8
|
+
* MEMBERSHIP, Rs 99 once, for life (the first 100 free with a coupon). On top,
|
|
9
|
+
* two PLANS: Premium (5 live products, 15 new people a month to message) and
|
|
10
|
+
* Business (unlimited products, 50 new people). Prices and plan contents come
|
|
11
|
+
* from the server, so a change needs no client release.
|
|
12
|
+
*
|
|
13
|
+
* A PLAN IS TIME, PREPAID, IN TWO BLOCKS: Business time first, then Premium
|
|
14
|
+
* time. Buying more APPENDS; buying Premium while on Business waits its turn;
|
|
15
|
+
* buying Business while on Premium is an upgrade that turns the Premium time
|
|
16
|
+
* left into Business for the difference (`domains/billing`). `blocks` says it
|
|
17
|
+
* as it stands, from now on.
|
|
18
|
+
*
|
|
19
|
+
* Money is integer PAISE everywhere in this package: never a float of rupees.
|
|
20
|
+
*
|
|
21
|
+
* @module founder-schema/domains/membership
|
|
22
|
+
*/
|
|
23
|
+
import { z } from 'zod';
|
|
24
|
+
/**
|
|
25
|
+
* How someone came to hold a membership. ⚠ Stored: append only.
|
|
26
|
+
*
|
|
27
|
+
* founding the founding coupon (`FIRST100`): free for life, numbered
|
|
28
|
+
* coupon another free-membership coupon
|
|
29
|
+
* paid Rs 99, captured
|
|
30
|
+
* granted staff gave it (a partner, a press account), with a reason on record
|
|
31
|
+
*/
|
|
32
|
+
export declare const MEMBERSHIP_KIND_VALUES: readonly ["founding", "coupon", "paid", "granted"];
|
|
33
|
+
export declare const membershipKindSchema: z.ZodEnum<{
|
|
34
|
+
founding: "founding";
|
|
35
|
+
coupon: "coupon";
|
|
36
|
+
paid: "paid";
|
|
37
|
+
granted: "granted";
|
|
38
|
+
}>;
|
|
39
|
+
export type MembershipKind = z.infer<typeof membershipKindSchema>;
|
|
40
|
+
/** ⚠ Stored: append only. Highest last is NOT implied: compare with `PLAN_TIER_RANK`. */
|
|
41
|
+
export declare const PLAN_TIER_VALUES: readonly ["premium", "business"];
|
|
42
|
+
export declare const planTierSchema: z.ZodEnum<{
|
|
43
|
+
premium: "premium";
|
|
44
|
+
business: "business";
|
|
45
|
+
}>;
|
|
46
|
+
export type PlanTier = z.infer<typeof planTierSchema>;
|
|
47
|
+
/** Which tier outranks which: Business above Premium. */
|
|
48
|
+
export declare const PLAN_TIER_RANK: Readonly<Record<PlanTier, number>>;
|
|
49
|
+
/** One payment buys a calendar month or a calendar year. ⚠ Stored: append only. */
|
|
50
|
+
export declare const PLAN_PERIOD_VALUES: readonly ["month", "year"];
|
|
51
|
+
export declare const planPeriodSchema: z.ZodEnum<{
|
|
52
|
+
month: "month";
|
|
53
|
+
year: "year";
|
|
54
|
+
}>;
|
|
55
|
+
export type PlanPeriod = z.infer<typeof planPeriodSchema>;
|
|
56
|
+
/** Integer paise, never negative. Rs 399 is 39900. */
|
|
57
|
+
export declare const paiseSchema: z.ZodNumber;
|
|
58
|
+
export type Paise = z.infer<typeof paiseSchema>;
|
|
59
|
+
/**
|
|
60
|
+
* What a standing or a plan allows that is COUNTED (the ability says whether;
|
|
61
|
+
* these say how many).
|
|
62
|
+
*
|
|
63
|
+
* productsLive products live at once; null is unlimited (the server
|
|
64
|
+
* still holds a safety ceiling, `PRODUCTS_LIVE_SAFETY_MAX`)
|
|
65
|
+
* newPeoplePerMonth conversations a month with people you do not know
|
|
66
|
+
*/
|
|
67
|
+
export declare const planLimitsSchema: z.ZodObject<{
|
|
68
|
+
productsLive: z.ZodNullable<z.ZodNumber>;
|
|
69
|
+
newPeoplePerMonth: z.ZodNumber;
|
|
70
|
+
}, z.core.$strip>;
|
|
71
|
+
export type PlanLimits = z.infer<typeof planLimitsSchema>;
|
|
72
|
+
/** The membership: once, for life. */
|
|
73
|
+
export declare const MEMBERSHIP_PRICE_PAISE = 9900;
|
|
74
|
+
/** What a member with no plan may: 1 live product, no conversations with people they do not know. */
|
|
75
|
+
export declare const MEMBER_LIMIT_VALUES: PlanLimits;
|
|
76
|
+
/** Each plan's name, prices and limits. Exhaustive by type. */
|
|
77
|
+
export declare const PLAN_SPECS: Readonly<Record<PlanTier, {
|
|
78
|
+
name: string;
|
|
79
|
+
prices: Readonly<Record<PlanPeriod, number>>;
|
|
80
|
+
limits: PlanLimits;
|
|
81
|
+
}>>;
|
|
82
|
+
/** Free founding memberships on the seeded `FIRST100` code. */
|
|
83
|
+
export declare const FOUNDING_PLACES = 100;
|
|
84
|
+
export declare const planOfferSchema: z.ZodObject<{
|
|
85
|
+
tier: z.ZodEnum<{
|
|
86
|
+
premium: "premium";
|
|
87
|
+
business: "business";
|
|
88
|
+
}>;
|
|
89
|
+
name: z.ZodString;
|
|
90
|
+
prices: z.ZodObject<{
|
|
91
|
+
month: z.ZodNumber;
|
|
92
|
+
year: z.ZodNumber;
|
|
93
|
+
}, z.core.$strip>;
|
|
94
|
+
limits: z.ZodObject<{
|
|
95
|
+
productsLive: z.ZodNullable<z.ZodNumber>;
|
|
96
|
+
newPeoplePerMonth: z.ZodNumber;
|
|
97
|
+
}, z.core.$strip>;
|
|
98
|
+
}, z.core.$strip>;
|
|
99
|
+
export type PlanOffer = z.infer<typeof planOfferSchema>;
|
|
100
|
+
export declare const planCatalogSchema: z.ZodObject<{
|
|
101
|
+
currency: z.ZodLiteral<"INR">;
|
|
102
|
+
membership: z.ZodObject<{
|
|
103
|
+
price: z.ZodNumber;
|
|
104
|
+
limits: z.ZodObject<{
|
|
105
|
+
productsLive: z.ZodNullable<z.ZodNumber>;
|
|
106
|
+
newPeoplePerMonth: z.ZodNumber;
|
|
107
|
+
}, z.core.$strip>;
|
|
108
|
+
}, z.core.$strip>;
|
|
109
|
+
plans: z.ZodArray<z.ZodObject<{
|
|
110
|
+
tier: z.ZodEnum<{
|
|
111
|
+
premium: "premium";
|
|
112
|
+
business: "business";
|
|
113
|
+
}>;
|
|
114
|
+
name: z.ZodString;
|
|
115
|
+
prices: z.ZodObject<{
|
|
116
|
+
month: z.ZodNumber;
|
|
117
|
+
year: z.ZodNumber;
|
|
118
|
+
}, z.core.$strip>;
|
|
119
|
+
limits: z.ZodObject<{
|
|
120
|
+
productsLive: z.ZodNullable<z.ZodNumber>;
|
|
121
|
+
newPeoplePerMonth: z.ZodNumber;
|
|
122
|
+
}, z.core.$strip>;
|
|
123
|
+
}, z.core.$strip>>;
|
|
124
|
+
refundWindowDays: z.ZodNumber;
|
|
125
|
+
paymentsEnabled: z.ZodBoolean;
|
|
126
|
+
}, z.core.$strip>;
|
|
127
|
+
export type PlanCatalog = z.infer<typeof planCatalogSchema>;
|
|
128
|
+
export declare const membershipSchema: z.ZodObject<{
|
|
129
|
+
kind: z.ZodEnum<{
|
|
130
|
+
founding: "founding";
|
|
131
|
+
coupon: "coupon";
|
|
132
|
+
paid: "paid";
|
|
133
|
+
granted: "granted";
|
|
134
|
+
}>;
|
|
135
|
+
since: z.ZodISODateTime;
|
|
136
|
+
foundingNumber: z.ZodNullable<z.ZodNumber>;
|
|
137
|
+
}, z.core.$strip>;
|
|
138
|
+
export type Membership = z.infer<typeof membershipSchema>;
|
|
139
|
+
/** A stretch of one tier. Blocks never overlap and follow each other with no gap. */
|
|
140
|
+
export declare const planBlockSchema: z.ZodObject<{
|
|
141
|
+
tier: z.ZodEnum<{
|
|
142
|
+
premium: "premium";
|
|
143
|
+
business: "business";
|
|
144
|
+
}>;
|
|
145
|
+
startsAt: z.ZodISODateTime;
|
|
146
|
+
endsAt: z.ZodISODateTime;
|
|
147
|
+
}, z.core.$strip>;
|
|
148
|
+
export type PlanBlock = z.infer<typeof planBlockSchema>;
|
|
149
|
+
export declare const meMembershipSchema: z.ZodObject<{
|
|
150
|
+
standing: z.ZodEnum<{
|
|
151
|
+
viewer: "viewer";
|
|
152
|
+
member: "member";
|
|
153
|
+
}>;
|
|
154
|
+
membership: z.ZodNullable<z.ZodObject<{
|
|
155
|
+
kind: z.ZodEnum<{
|
|
156
|
+
founding: "founding";
|
|
157
|
+
coupon: "coupon";
|
|
158
|
+
paid: "paid";
|
|
159
|
+
granted: "granted";
|
|
160
|
+
}>;
|
|
161
|
+
since: z.ZodISODateTime;
|
|
162
|
+
foundingNumber: z.ZodNullable<z.ZodNumber>;
|
|
163
|
+
}, z.core.$strip>>;
|
|
164
|
+
through: z.ZodNullable<z.ZodEnum<{
|
|
165
|
+
staff: "staff";
|
|
166
|
+
membership: "membership";
|
|
167
|
+
}>>;
|
|
168
|
+
plan: z.ZodObject<{
|
|
169
|
+
tier: z.ZodNullable<z.ZodEnum<{
|
|
170
|
+
premium: "premium";
|
|
171
|
+
business: "business";
|
|
172
|
+
}>>;
|
|
173
|
+
blocks: z.ZodArray<z.ZodObject<{
|
|
174
|
+
tier: z.ZodEnum<{
|
|
175
|
+
premium: "premium";
|
|
176
|
+
business: "business";
|
|
177
|
+
}>;
|
|
178
|
+
startsAt: z.ZodISODateTime;
|
|
179
|
+
endsAt: z.ZodISODateTime;
|
|
180
|
+
}, z.core.$strip>>;
|
|
181
|
+
}, z.core.$strip>;
|
|
182
|
+
limits: z.ZodObject<{
|
|
183
|
+
productsLive: z.ZodNullable<z.ZodNumber>;
|
|
184
|
+
newPeoplePerMonth: z.ZodNumber;
|
|
185
|
+
}, z.core.$strip>;
|
|
186
|
+
products: z.ZodObject<{
|
|
187
|
+
live: z.ZodNumber;
|
|
188
|
+
owned: z.ZodNumber;
|
|
189
|
+
}, z.core.$strip>;
|
|
190
|
+
messages: z.ZodObject<{
|
|
191
|
+
used: z.ZodNumber;
|
|
192
|
+
resetsAt: z.ZodISODateTime;
|
|
193
|
+
}, z.core.$strip>;
|
|
194
|
+
billing: z.ZodNullable<z.ZodObject<{
|
|
195
|
+
stateCode: z.ZodEnum<{
|
|
196
|
+
"01": "01";
|
|
197
|
+
"02": "02";
|
|
198
|
+
"03": "03";
|
|
199
|
+
"04": "04";
|
|
200
|
+
"05": "05";
|
|
201
|
+
"06": "06";
|
|
202
|
+
"07": "07";
|
|
203
|
+
"08": "08";
|
|
204
|
+
"09": "09";
|
|
205
|
+
10: "10";
|
|
206
|
+
11: "11";
|
|
207
|
+
12: "12";
|
|
208
|
+
13: "13";
|
|
209
|
+
14: "14";
|
|
210
|
+
15: "15";
|
|
211
|
+
16: "16";
|
|
212
|
+
17: "17";
|
|
213
|
+
18: "18";
|
|
214
|
+
19: "19";
|
|
215
|
+
20: "20";
|
|
216
|
+
21: "21";
|
|
217
|
+
22: "22";
|
|
218
|
+
23: "23";
|
|
219
|
+
24: "24";
|
|
220
|
+
26: "26";
|
|
221
|
+
27: "27";
|
|
222
|
+
29: "29";
|
|
223
|
+
30: "30";
|
|
224
|
+
31: "31";
|
|
225
|
+
32: "32";
|
|
226
|
+
33: "33";
|
|
227
|
+
34: "34";
|
|
228
|
+
35: "35";
|
|
229
|
+
36: "36";
|
|
230
|
+
37: "37";
|
|
231
|
+
38: "38";
|
|
232
|
+
}>;
|
|
233
|
+
gstin: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
|
|
234
|
+
legalName: z.ZodOptional<z.ZodString>;
|
|
235
|
+
address: z.ZodOptional<z.ZodString>;
|
|
236
|
+
}, z.core.$strip>>;
|
|
237
|
+
}, z.core.$strip>;
|
|
238
|
+
export type MeMembership = z.infer<typeof meMembershipSchema>;
|
|
239
|
+
//# sourceMappingURL=membership.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"membership.d.ts","sourceRoot":"","sources":["../../../src/domains/membership/membership.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,oDAAqD,CAAC;AACzF,eAAO,MAAM,oBAAoB;;;;;EAAiC,CAAC;AACnE,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE,yFAAyF;AACzF,eAAO,MAAM,gBAAgB,kCAAmC,CAAC;AACjE,eAAO,MAAM,cAAc;;;EAA2B,CAAC;AACvD,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD,yDAAyD;AACzD,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAA+B,CAAC;AAE9F,mFAAmF;AACnF,eAAO,MAAM,kBAAkB,4BAA6B,CAAC;AAC7D,eAAO,MAAM,gBAAgB;;;EAA6B,CAAC;AAC3D,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,sDAAsD;AACtD,eAAO,MAAM,WAAW,aAAiC,CAAC;AAC1D,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB;;;iBAG3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAW1D,sCAAsC;AACtC,eAAO,MAAM,sBAAsB,OAAQ,CAAC;AAE5C,qGAAqG;AACrG,eAAO,MAAM,mBAAmB,EAAE,UAAsD,CAAC;AAEzF,+DAA+D;AAC/D,eAAO,MAAM,UAAU,EAAE,QAAQ,CAC/B,MAAM,CACJ,QAAQ,EACR;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,CACnF,CAYF,CAAC;AAEF,+DAA+D;AAC/D,eAAO,MAAM,eAAe,MAAM,CAAC;AAInC,eAAO,MAAM,eAAe;;;;;;;;;;;;;;iBAO1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiB5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAI5D,eAAO,MAAM,gBAAgB;;;;;;;;;iBAK3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,qFAAqF;AACrF,eAAO,MAAM,eAAe;;;;;;;iBAI1B,CAAC;AACH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAExD,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA2B7B,CAAC;AACH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* founder-schema: membership and plans, as a member holds them (0.12.0)
|
|
3
|
+
*
|
|
4
|
+
* GET /api/v1/plans planCatalogSchema (anyone: prices and what each includes)
|
|
5
|
+
* GET /api/v1/me/membership meMembershipSchema (signed in: what I hold, what I have used)
|
|
6
|
+
*
|
|
7
|
+
* THE USER'S RULING (2026-10-11): looking is free; taking part needs a
|
|
8
|
+
* MEMBERSHIP, Rs 99 once, for life (the first 100 free with a coupon). On top,
|
|
9
|
+
* two PLANS: Premium (5 live products, 15 new people a month to message) and
|
|
10
|
+
* Business (unlimited products, 50 new people). Prices and plan contents come
|
|
11
|
+
* from the server, so a change needs no client release.
|
|
12
|
+
*
|
|
13
|
+
* A PLAN IS TIME, PREPAID, IN TWO BLOCKS: Business time first, then Premium
|
|
14
|
+
* time. Buying more APPENDS; buying Premium while on Business waits its turn;
|
|
15
|
+
* buying Business while on Premium is an upgrade that turns the Premium time
|
|
16
|
+
* left into Business for the difference (`domains/billing`). `blocks` says it
|
|
17
|
+
* as it stands, from now on.
|
|
18
|
+
*
|
|
19
|
+
* Money is integer PAISE everywhere in this package: never a float of rupees.
|
|
20
|
+
*
|
|
21
|
+
* @module founder-schema/domains/membership
|
|
22
|
+
*/
|
|
23
|
+
import { z } from 'zod';
|
|
24
|
+
import { standingSchema } from '../../access/standing.js';
|
|
25
|
+
import { billingDetailsSchema } from '../billing/gst.js';
|
|
26
|
+
/**
|
|
27
|
+
* How someone came to hold a membership. ⚠ Stored: append only.
|
|
28
|
+
*
|
|
29
|
+
* founding the founding coupon (`FIRST100`): free for life, numbered
|
|
30
|
+
* coupon another free-membership coupon
|
|
31
|
+
* paid Rs 99, captured
|
|
32
|
+
* granted staff gave it (a partner, a press account), with a reason on record
|
|
33
|
+
*/
|
|
34
|
+
export const MEMBERSHIP_KIND_VALUES = ['founding', 'coupon', 'paid', 'granted'];
|
|
35
|
+
export const membershipKindSchema = z.enum(MEMBERSHIP_KIND_VALUES);
|
|
36
|
+
/** ⚠ Stored: append only. Highest last is NOT implied: compare with `PLAN_TIER_RANK`. */
|
|
37
|
+
export const PLAN_TIER_VALUES = ['premium', 'business'];
|
|
38
|
+
export const planTierSchema = z.enum(PLAN_TIER_VALUES);
|
|
39
|
+
/** Which tier outranks which: Business above Premium. */
|
|
40
|
+
export const PLAN_TIER_RANK = { premium: 1, business: 2 };
|
|
41
|
+
/** One payment buys a calendar month or a calendar year. ⚠ Stored: append only. */
|
|
42
|
+
export const PLAN_PERIOD_VALUES = ['month', 'year'];
|
|
43
|
+
export const planPeriodSchema = z.enum(PLAN_PERIOD_VALUES);
|
|
44
|
+
/** Integer paise, never negative. Rs 399 is 39900. */
|
|
45
|
+
export const paiseSchema = z.number().int().nonnegative();
|
|
46
|
+
/**
|
|
47
|
+
* What a standing or a plan allows that is COUNTED (the ability says whether;
|
|
48
|
+
* these say how many).
|
|
49
|
+
*
|
|
50
|
+
* productsLive products live at once; null is unlimited (the server
|
|
51
|
+
* still holds a safety ceiling, `PRODUCTS_LIVE_SAFETY_MAX`)
|
|
52
|
+
* newPeoplePerMonth conversations a month with people you do not know
|
|
53
|
+
*/
|
|
54
|
+
export const planLimitsSchema = z.object({
|
|
55
|
+
productsLive: z.number().int().nonnegative().nullable(),
|
|
56
|
+
newPeoplePerMonth: z.number().int().nonnegative(),
|
|
57
|
+
});
|
|
58
|
+
// ─── The price list ──────────────────────────────────────────────────────────
|
|
59
|
+
//
|
|
60
|
+
// THE ONE PLACE a price or a plan's limit is written (0.12.0). The backend
|
|
61
|
+
// prices and counts from these (`modules/plans/catalog.ts`), `GET /plans`
|
|
62
|
+
// serves them, and the web's Terms of Service are written FROM them, so the
|
|
63
|
+
// Terms can never state a price the server does not charge. A change here is
|
|
64
|
+
// a schema release, then the backend, then the web. Integer PAISE, GST
|
|
65
|
+
// included. hary's India-first prices (the user: "you decide, India first").
|
|
66
|
+
/** The membership: once, for life. */
|
|
67
|
+
export const MEMBERSHIP_PRICE_PAISE = 9_900;
|
|
68
|
+
/** What a member with no plan may: 1 live product, no conversations with people they do not know. */
|
|
69
|
+
export const MEMBER_LIMIT_VALUES = { productsLive: 1, newPeoplePerMonth: 0 };
|
|
70
|
+
/** Each plan's name, prices and limits. Exhaustive by type. */
|
|
71
|
+
export const PLAN_SPECS = {
|
|
72
|
+
premium: {
|
|
73
|
+
name: 'Premium',
|
|
74
|
+
prices: { month: 39_900, year: 399_900 },
|
|
75
|
+
limits: { productsLive: 5, newPeoplePerMonth: 15 },
|
|
76
|
+
},
|
|
77
|
+
business: {
|
|
78
|
+
name: 'Business',
|
|
79
|
+
prices: { month: 99_900, year: 999_900 },
|
|
80
|
+
limits: { productsLive: null, newPeoplePerMonth: 50 },
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
/** Free founding memberships on the seeded `FIRST100` code. */
|
|
84
|
+
export const FOUNDING_PLACES = 100;
|
|
85
|
+
// ─── GET /plans ──────────────────────────────────────────────────────────────
|
|
86
|
+
export const planOfferSchema = z.object({
|
|
87
|
+
tier: planTierSchema,
|
|
88
|
+
/** "Premium", "Business": the server's word, so a rename needs no client release. */
|
|
89
|
+
name: z.string(),
|
|
90
|
+
/** GST included. */
|
|
91
|
+
prices: z.object({ month: paiseSchema, year: paiseSchema }),
|
|
92
|
+
limits: planLimitsSchema,
|
|
93
|
+
});
|
|
94
|
+
export const planCatalogSchema = z.object({
|
|
95
|
+
currency: z.literal('INR'),
|
|
96
|
+
membership: z.object({
|
|
97
|
+
/** Once, for life, GST included. */
|
|
98
|
+
price: paiseSchema,
|
|
99
|
+
limits: planLimitsSchema,
|
|
100
|
+
}),
|
|
101
|
+
/** Lowest first. */
|
|
102
|
+
plans: z.array(planOfferSchema),
|
|
103
|
+
/** Every purchase is refunded in full within this many days of paying. */
|
|
104
|
+
refundWindowDays: z.number().int().positive(),
|
|
105
|
+
/**
|
|
106
|
+
* Whether a payment can be taken here now. False when the gateway is not
|
|
107
|
+
* configured: a free coupon still works, a paid purchase answers
|
|
108
|
+
* `PAYMENTS_UNAVAILABLE`.
|
|
109
|
+
*/
|
|
110
|
+
paymentsEnabled: z.boolean(),
|
|
111
|
+
});
|
|
112
|
+
// ─── GET /me/membership ──────────────────────────────────────────────────────
|
|
113
|
+
export const membershipSchema = z.object({
|
|
114
|
+
kind: membershipKindSchema,
|
|
115
|
+
since: z.iso.datetime(),
|
|
116
|
+
/** "Founding member #N": set only for `founding`. */
|
|
117
|
+
foundingNumber: z.number().int().positive().nullable(),
|
|
118
|
+
});
|
|
119
|
+
/** A stretch of one tier. Blocks never overlap and follow each other with no gap. */
|
|
120
|
+
export const planBlockSchema = z.object({
|
|
121
|
+
tier: planTierSchema,
|
|
122
|
+
startsAt: z.iso.datetime(),
|
|
123
|
+
endsAt: z.iso.datetime(),
|
|
124
|
+
});
|
|
125
|
+
export const meMembershipSchema = z.object({
|
|
126
|
+
standing: standingSchema,
|
|
127
|
+
/** Null for a viewer, and for staff who hold none (they take part through their role). */
|
|
128
|
+
membership: membershipSchema.nullable(),
|
|
129
|
+
/** Why they take part: their membership, or a staff role (staff are members). Null for a viewer. */
|
|
130
|
+
through: z.enum(['membership', 'staff']).nullable(),
|
|
131
|
+
plan: z.object({
|
|
132
|
+
/** The tier in force now; null with no plan. */
|
|
133
|
+
tier: planTierSchema.nullable(),
|
|
134
|
+
/** From now on, Business first, then Premium. Empty with no plan. */
|
|
135
|
+
blocks: z.array(planBlockSchema),
|
|
136
|
+
}),
|
|
137
|
+
limits: planLimitsSchema,
|
|
138
|
+
products: z.object({
|
|
139
|
+
/** Products they own that are live now. */
|
|
140
|
+
live: z.number().int().nonnegative(),
|
|
141
|
+
/** Products they own, paused ones included. */
|
|
142
|
+
owned: z.number().int().nonnegative(),
|
|
143
|
+
}),
|
|
144
|
+
messages: z.object({
|
|
145
|
+
/** New people they started a conversation with this month (India time). */
|
|
146
|
+
used: z.number().int().nonnegative(),
|
|
147
|
+
/** When this month's count starts again. */
|
|
148
|
+
resetsAt: z.iso.datetime(),
|
|
149
|
+
}),
|
|
150
|
+
/** The billing details they last used, to prefill the next checkout; null before the first. */
|
|
151
|
+
billing: billingDetailsSchema.nullable(),
|
|
152
|
+
});
|
|
153
|
+
//# sourceMappingURL=membership.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"membership.js","sourceRoot":"","sources":["../../../src/domains/membership/membership.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAU,CAAC;AACzF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;AAGnE,yFAAyF;AACzF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,SAAS,EAAE,UAAU,CAAU,CAAC;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;AAGvD,yDAAyD;AACzD,MAAM,CAAC,MAAM,cAAc,GAAuC,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;AAE9F,mFAAmF;AACnF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,OAAO,EAAE,MAAM,CAAU,CAAC;AAC7D,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;AAG3D,sDAAsD;AACtD,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;AAG1D;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE;IACvD,iBAAiB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;CAClD,CAAC,CAAC;AAGH,gFAAgF;AAChF,EAAE;AACF,2EAA2E;AAC3E,0EAA0E;AAC1E,4EAA4E;AAC5E,6EAA6E;AAC7E,uEAAuE;AACvE,6EAA6E;AAE7E,sCAAsC;AACtC,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAE5C,qGAAqG;AACrG,MAAM,CAAC,MAAM,mBAAmB,GAAe,EAAE,YAAY,EAAE,CAAC,EAAE,iBAAiB,EAAE,CAAC,EAAE,CAAC;AAEzF,+DAA+D;AAC/D,MAAM,CAAC,MAAM,UAAU,GAKnB;IACF,OAAO,EAAE;QACP,IAAI,EAAE,SAAS;QACf,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE;QACxC,MAAM,EAAE,EAAE,YAAY,EAAE,CAAC,EAAE,iBAAiB,EAAE,EAAE,EAAE;KACnD;IACD,QAAQ,EAAE;QACR,IAAI,EAAE,UAAU;QAChB,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE;QACxC,MAAM,EAAE,EAAE,YAAY,EAAE,IAAI,EAAE,iBAAiB,EAAE,EAAE,EAAE;KACtD;CACF,CAAC;AAEF,+DAA+D;AAC/D,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAC;AAEnC,gFAAgF;AAEhF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,cAAc;IACpB,qFAAqF;IACrF,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,oBAAoB;IACpB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC;IAC3D,MAAM,EAAE,gBAAgB;CACzB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,QAAQ,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;IAC1B,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;QACnB,oCAAoC;QACpC,KAAK,EAAE,WAAW;QAClB,MAAM,EAAE,gBAAgB;KACzB,CAAC;IACF,oBAAoB;IACpB,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC;IAC/B,0EAA0E;IAC1E,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAC7C;;;;OAIG;IACH,eAAe,EAAE,CAAC,CAAC,OAAO,EAAE;CAC7B,CAAC,CAAC;AAGH,gFAAgF;AAEhF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,IAAI,EAAE,oBAAoB;IAC1B,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE;IACvB,qDAAqD;IACrD,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;CACvD,CAAC,CAAC;AAGH,qFAAqF;AACrF,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC,MAAM,CAAC;IACtC,IAAI,EAAE,cAAc;IACpB,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE;IAC1B,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE;CACzB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,QAAQ,EAAE,cAAc;IACxB,0FAA0F;IAC1F,UAAU,EAAE,gBAAgB,CAAC,QAAQ,EAAE;IACvC,oGAAoG;IACpG,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE;IACnD,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,gDAAgD;QAChD,IAAI,EAAE,cAAc,CAAC,QAAQ,EAAE;QAC/B,qEAAqE;QACrE,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC;KACjC,CAAC;IACF,MAAM,EAAE,gBAAgB;IACxB,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC;QACjB,2CAA2C;QAC3C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QACpC,+CAA+C;QAC/C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;KACtC,CAAC;IACF,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC;QACjB,2EAA2E;QAC3E,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE;QACpC,4CAA4C;QAC5C,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE;KAC3B,CAAC;IACF,+FAA+F;IAC/F,OAAO,EAAE,oBAAoB,CAAC,QAAQ,EAAE;CACzC,CAAC,CAAC","sourcesContent":["/**\n * founder-schema: membership and plans, as a member holds them (0.12.0)\n *\n * GET /api/v1/plans planCatalogSchema (anyone: prices and what each includes)\n * GET /api/v1/me/membership meMembershipSchema (signed in: what I hold, what I have used)\n *\n * THE USER'S RULING (2026-10-11): looking is free; taking part needs a\n * MEMBERSHIP, Rs 99 once, for life (the first 100 free with a coupon). On top,\n * two PLANS: Premium (5 live products, 15 new people a month to message) and\n * Business (unlimited products, 50 new people). Prices and plan contents come\n * from the server, so a change needs no client release.\n *\n * A PLAN IS TIME, PREPAID, IN TWO BLOCKS: Business time first, then Premium\n * time. Buying more APPENDS; buying Premium while on Business waits its turn;\n * buying Business while on Premium is an upgrade that turns the Premium time\n * left into Business for the difference (`domains/billing`). `blocks` says it\n * as it stands, from now on.\n *\n * Money is integer PAISE everywhere in this package: never a float of rupees.\n *\n * @module founder-schema/domains/membership\n */\n\nimport { z } from 'zod';\nimport { standingSchema } from '../../access/standing.js';\nimport { billingDetailsSchema } from '../billing/gst.js';\n\n/**\n * How someone came to hold a membership. ⚠ Stored: append only.\n *\n * founding the founding coupon (`FIRST100`): free for life, numbered\n * coupon another free-membership coupon\n * paid Rs 99, captured\n * granted staff gave it (a partner, a press account), with a reason on record\n */\nexport const MEMBERSHIP_KIND_VALUES = ['founding', 'coupon', 'paid', 'granted'] as const;\nexport const membershipKindSchema = z.enum(MEMBERSHIP_KIND_VALUES);\nexport type MembershipKind = z.infer<typeof membershipKindSchema>;\n\n/** ⚠ Stored: append only. Highest last is NOT implied: compare with `PLAN_TIER_RANK`. */\nexport const PLAN_TIER_VALUES = ['premium', 'business'] as const;\nexport const planTierSchema = z.enum(PLAN_TIER_VALUES);\nexport type PlanTier = z.infer<typeof planTierSchema>;\n\n/** Which tier outranks which: Business above Premium. */\nexport const PLAN_TIER_RANK: Readonly<Record<PlanTier, number>> = { premium: 1, business: 2 };\n\n/** One payment buys a calendar month or a calendar year. ⚠ Stored: append only. */\nexport const PLAN_PERIOD_VALUES = ['month', 'year'] as const;\nexport const planPeriodSchema = z.enum(PLAN_PERIOD_VALUES);\nexport type PlanPeriod = z.infer<typeof planPeriodSchema>;\n\n/** Integer paise, never negative. Rs 399 is 39900. */\nexport const paiseSchema = z.number().int().nonnegative();\nexport type Paise = z.infer<typeof paiseSchema>;\n\n/**\n * What a standing or a plan allows that is COUNTED (the ability says whether;\n * these say how many).\n *\n * productsLive products live at once; null is unlimited (the server\n * still holds a safety ceiling, `PRODUCTS_LIVE_SAFETY_MAX`)\n * newPeoplePerMonth conversations a month with people you do not know\n */\nexport const planLimitsSchema = z.object({\n productsLive: z.number().int().nonnegative().nullable(),\n newPeoplePerMonth: z.number().int().nonnegative(),\n});\nexport type PlanLimits = z.infer<typeof planLimitsSchema>;\n\n// ─── The price list ──────────────────────────────────────────────────────────\n//\n// THE ONE PLACE a price or a plan's limit is written (0.12.0). The backend\n// prices and counts from these (`modules/plans/catalog.ts`), `GET /plans`\n// serves them, and the web's Terms of Service are written FROM them, so the\n// Terms can never state a price the server does not charge. A change here is\n// a schema release, then the backend, then the web. Integer PAISE, GST\n// included. hary's India-first prices (the user: \"you decide, India first\").\n\n/** The membership: once, for life. */\nexport const MEMBERSHIP_PRICE_PAISE = 9_900;\n\n/** What a member with no plan may: 1 live product, no conversations with people they do not know. */\nexport const MEMBER_LIMIT_VALUES: PlanLimits = { productsLive: 1, newPeoplePerMonth: 0 };\n\n/** Each plan's name, prices and limits. Exhaustive by type. */\nexport const PLAN_SPECS: Readonly<\n Record<\n PlanTier,\n { name: string; prices: Readonly<Record<PlanPeriod, number>>; limits: PlanLimits }\n >\n> = {\n premium: {\n name: 'Premium',\n prices: { month: 39_900, year: 399_900 },\n limits: { productsLive: 5, newPeoplePerMonth: 15 },\n },\n business: {\n name: 'Business',\n prices: { month: 99_900, year: 999_900 },\n limits: { productsLive: null, newPeoplePerMonth: 50 },\n },\n};\n\n/** Free founding memberships on the seeded `FIRST100` code. */\nexport const FOUNDING_PLACES = 100;\n\n// ─── GET /plans ──────────────────────────────────────────────────────────────\n\nexport const planOfferSchema = z.object({\n tier: planTierSchema,\n /** \"Premium\", \"Business\": the server's word, so a rename needs no client release. */\n name: z.string(),\n /** GST included. */\n prices: z.object({ month: paiseSchema, year: paiseSchema }),\n limits: planLimitsSchema,\n});\nexport type PlanOffer = z.infer<typeof planOfferSchema>;\n\nexport const planCatalogSchema = z.object({\n currency: z.literal('INR'),\n membership: z.object({\n /** Once, for life, GST included. */\n price: paiseSchema,\n limits: planLimitsSchema,\n }),\n /** Lowest first. */\n plans: z.array(planOfferSchema),\n /** Every purchase is refunded in full within this many days of paying. */\n refundWindowDays: z.number().int().positive(),\n /**\n * Whether a payment can be taken here now. False when the gateway is not\n * configured: a free coupon still works, a paid purchase answers\n * `PAYMENTS_UNAVAILABLE`.\n */\n paymentsEnabled: z.boolean(),\n});\nexport type PlanCatalog = z.infer<typeof planCatalogSchema>;\n\n// ─── GET /me/membership ──────────────────────────────────────────────────────\n\nexport const membershipSchema = z.object({\n kind: membershipKindSchema,\n since: z.iso.datetime(),\n /** \"Founding member #N\": set only for `founding`. */\n foundingNumber: z.number().int().positive().nullable(),\n});\nexport type Membership = z.infer<typeof membershipSchema>;\n\n/** A stretch of one tier. Blocks never overlap and follow each other with no gap. */\nexport const planBlockSchema = z.object({\n tier: planTierSchema,\n startsAt: z.iso.datetime(),\n endsAt: z.iso.datetime(),\n});\nexport type PlanBlock = z.infer<typeof planBlockSchema>;\n\nexport const meMembershipSchema = z.object({\n standing: standingSchema,\n /** Null for a viewer, and for staff who hold none (they take part through their role). */\n membership: membershipSchema.nullable(),\n /** Why they take part: their membership, or a staff role (staff are members). Null for a viewer. */\n through: z.enum(['membership', 'staff']).nullable(),\n plan: z.object({\n /** The tier in force now; null with no plan. */\n tier: planTierSchema.nullable(),\n /** From now on, Business first, then Premium. Empty with no plan. */\n blocks: z.array(planBlockSchema),\n }),\n limits: planLimitsSchema,\n products: z.object({\n /** Products they own that are live now. */\n live: z.number().int().nonnegative(),\n /** Products they own, paused ones included. */\n owned: z.number().int().nonnegative(),\n }),\n messages: z.object({\n /** New people they started a conversation with this month (India time). */\n used: z.number().int().nonnegative(),\n /** When this month's count starts again. */\n resetsAt: z.iso.datetime(),\n }),\n /** The billing details they last used, to prefill the next checkout; null before the first. */\n billing: billingDetailsSchema.nullable(),\n});\nexport type MeMembership = z.infer<typeof meMembershipSchema>;\n"]}
|