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.
Files changed (84) hide show
  1. package/README.md +98 -0
  2. package/dist/access/access.d.ts +10 -6
  3. package/dist/access/access.d.ts.map +1 -1
  4. package/dist/access/access.js +8 -1
  5. package/dist/access/access.js.map +1 -1
  6. package/dist/access/permissions.d.ts +60 -3
  7. package/dist/access/permissions.d.ts.map +1 -1
  8. package/dist/access/permissions.js +24 -0
  9. package/dist/access/permissions.js.map +1 -1
  10. package/dist/access/roles.d.ts +3 -3
  11. package/dist/access/standing.d.ts +59 -0
  12. package/dist/access/standing.d.ts.map +1 -0
  13. package/dist/access/standing.js +52 -0
  14. package/dist/access/standing.js.map +1 -0
  15. package/dist/core/errors.d.ts +47 -1
  16. package/dist/core/errors.d.ts.map +1 -1
  17. package/dist/core/errors.js +82 -1
  18. package/dist/core/errors.js.map +1 -1
  19. package/dist/core/limits.d.ts +39 -3
  20. package/dist/core/limits.d.ts.map +1 -1
  21. package/dist/core/limits.js +41 -3
  22. package/dist/core/limits.js.map +1 -1
  23. package/dist/core/responses.d.ts +55 -0
  24. package/dist/core/responses.d.ts.map +1 -1
  25. package/dist/core/responses.js +6 -0
  26. package/dist/core/responses.js.map +1 -1
  27. package/dist/core/validation.d.ts +14 -0
  28. package/dist/core/validation.d.ts.map +1 -1
  29. package/dist/core/validation.js +14 -0
  30. package/dist/core/validation.js.map +1 -1
  31. package/dist/domains/audit/audit.d.ts +28 -7
  32. package/dist/domains/audit/audit.d.ts.map +1 -1
  33. package/dist/domains/audit/audit.js +17 -0
  34. package/dist/domains/audit/audit.js.map +1 -1
  35. package/dist/domains/billing/billing.d.ts +1179 -0
  36. package/dist/domains/billing/billing.d.ts.map +1 -0
  37. package/dist/domains/billing/billing.js +393 -0
  38. package/dist/domains/billing/billing.js.map +1 -0
  39. package/dist/domains/billing/gst.d.ts +229 -0
  40. package/dist/domains/billing/gst.d.ts.map +1 -0
  41. package/dist/domains/billing/gst.js +117 -0
  42. package/dist/domains/billing/gst.js.map +1 -0
  43. package/dist/domains/chat/chat.d.ts +85 -0
  44. package/dist/domains/chat/chat.d.ts.map +1 -1
  45. package/dist/domains/chat/chat.js +62 -0
  46. package/dist/domains/chat/chat.js.map +1 -1
  47. package/dist/domains/communities/communities.d.ts +11 -11
  48. package/dist/domains/dashboard/dashboard.d.ts +9 -1
  49. package/dist/domains/dashboard/dashboard.d.ts.map +1 -1
  50. package/dist/domains/dashboard/dashboard.js +15 -0
  51. package/dist/domains/dashboard/dashboard.js.map +1 -1
  52. package/dist/domains/feed/feed.d.ts.map +1 -1
  53. package/dist/domains/feed/feed.js +7 -1
  54. package/dist/domains/feed/feed.js.map +1 -1
  55. package/dist/domains/members/members.d.ts +30 -5
  56. package/dist/domains/members/members.d.ts.map +1 -1
  57. package/dist/domains/members/members.js +23 -0
  58. package/dist/domains/members/members.js.map +1 -1
  59. package/dist/domains/membership/membership.d.ts +239 -0
  60. package/dist/domains/membership/membership.d.ts.map +1 -0
  61. package/dist/domains/membership/membership.js +153 -0
  62. package/dist/domains/membership/membership.js.map +1 -0
  63. package/dist/domains/notifications/notifications.d.ts +43 -1
  64. package/dist/domains/notifications/notifications.d.ts.map +1 -1
  65. package/dist/domains/notifications/notifications.js +19 -0
  66. package/dist/domains/notifications/notifications.js.map +1 -1
  67. package/dist/domains/products/products.d.ts +44 -12
  68. package/dist/domains/products/products.d.ts.map +1 -1
  69. package/dist/domains/products/products.js +33 -3
  70. package/dist/domains/products/products.js.map +1 -1
  71. package/dist/domains/profile/founder-profile.d.ts +178 -0
  72. package/dist/domains/profile/founder-profile.d.ts.map +1 -1
  73. package/dist/domains/profile/founder-profile.js +77 -0
  74. package/dist/domains/profile/founder-profile.js.map +1 -1
  75. package/dist/domains/reviews/reviews.d.ts +3 -0
  76. package/dist/domains/reviews/reviews.d.ts.map +1 -1
  77. package/dist/domains/reviews/reviews.js +6 -1
  78. package/dist/domains/reviews/reviews.js.map +1 -1
  79. package/dist/domains/team/team.d.ts +4 -4
  80. package/dist/index.d.ts +7 -4
  81. package/dist/index.d.ts.map +1 -1
  82. package/dist/index.js +9 -4
  83. package/dist/index.js.map +1 -1
  84. 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;AAiBxB,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4B7B,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"}
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":"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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 {\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":"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","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"]}