founder-schema 0.17.0 → 0.19.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 (120) hide show
  1. package/README.md +186 -17
  2. package/dist/access/access.d.ts +1 -1
  3. package/dist/access/access.d.ts.map +1 -1
  4. package/dist/access/access.js +4 -1
  5. package/dist/access/access.js.map +1 -1
  6. package/dist/access/permissions.d.ts +27 -29
  7. package/dist/access/permissions.d.ts.map +1 -1
  8. package/dist/access/permissions.js +39 -3
  9. package/dist/access/permissions.js.map +1 -1
  10. package/dist/core/common.d.ts +54 -23
  11. package/dist/core/common.d.ts.map +1 -1
  12. package/dist/core/common.js +49 -24
  13. package/dist/core/common.js.map +1 -1
  14. package/dist/core/errors.d.ts +7 -5
  15. package/dist/core/errors.d.ts.map +1 -1
  16. package/dist/core/errors.js +17 -11
  17. package/dist/core/errors.js.map +1 -1
  18. package/dist/core/limits.d.ts +73 -17
  19. package/dist/core/limits.d.ts.map +1 -1
  20. package/dist/core/limits.js +77 -22
  21. package/dist/core/limits.js.map +1 -1
  22. package/dist/core/money.d.ts +33 -0
  23. package/dist/core/money.d.ts.map +1 -0
  24. package/dist/core/money.js +36 -0
  25. package/dist/core/money.js.map +1 -0
  26. package/dist/core/responses.d.ts +14 -4
  27. package/dist/core/responses.d.ts.map +1 -1
  28. package/dist/core/responses.js.map +1 -1
  29. package/dist/core/validation.d.ts +19 -2
  30. package/dist/core/validation.d.ts.map +1 -1
  31. package/dist/core/validation.js +19 -2
  32. package/dist/core/validation.js.map +1 -1
  33. package/dist/domains/billing/billing.d.ts +61 -8
  34. package/dist/domains/billing/billing.d.ts.map +1 -1
  35. package/dist/domains/billing/billing.js +58 -22
  36. package/dist/domains/billing/billing.js.map +1 -1
  37. package/dist/domains/billing/gst.d.ts.map +1 -1
  38. package/dist/domains/billing/gst.js +4 -3
  39. package/dist/domains/billing/gst.js.map +1 -1
  40. package/dist/domains/chat/chat.d.ts +0 -21
  41. package/dist/domains/chat/chat.d.ts.map +1 -1
  42. package/dist/domains/chat/chat.js +0 -20
  43. package/dist/domains/chat/chat.js.map +1 -1
  44. package/dist/domains/communities/communities.d.ts +30 -12
  45. package/dist/domains/communities/communities.d.ts.map +1 -1
  46. package/dist/domains/communities/communities.js +43 -15
  47. package/dist/domains/communities/communities.js.map +1 -1
  48. package/dist/domains/contributions/contributions.d.ts +1 -1
  49. package/dist/domains/contributions/contributions.js +1 -1
  50. package/dist/domains/contributions/contributions.js.map +1 -1
  51. package/dist/domains/dashboard/dashboard.d.ts +3 -1
  52. package/dist/domains/dashboard/dashboard.d.ts.map +1 -1
  53. package/dist/domains/dashboard/dashboard.js +23 -7
  54. package/dist/domains/dashboard/dashboard.js.map +1 -1
  55. package/dist/domains/discover/discover.d.ts +8 -8
  56. package/dist/domains/feed/feed.d.ts +55 -153
  57. package/dist/domains/feed/feed.d.ts.map +1 -1
  58. package/dist/domains/feed/feed.js +53 -58
  59. package/dist/domains/feed/feed.js.map +1 -1
  60. package/dist/domains/location/areas.d.ts +331 -0
  61. package/dist/domains/location/areas.d.ts.map +1 -0
  62. package/dist/domains/location/areas.js +238 -0
  63. package/dist/domains/location/areas.js.map +1 -0
  64. package/dist/domains/location/india.d.ts +41 -123
  65. package/dist/domains/location/india.d.ts.map +1 -1
  66. package/dist/domains/location/india.js +121 -59
  67. package/dist/domains/location/india.js.map +1 -1
  68. package/dist/domains/members/members.d.ts +2 -2
  69. package/dist/domains/membership/membership.d.ts +26 -0
  70. package/dist/domains/membership/membership.d.ts.map +1 -1
  71. package/dist/domains/membership/membership.js +55 -1
  72. package/dist/domains/membership/membership.js.map +1 -1
  73. package/dist/domains/moderation/moderation.d.ts +2 -2
  74. package/dist/domains/moderation/moderation.js +3 -3
  75. package/dist/domains/moderation/moderation.js.map +1 -1
  76. package/dist/domains/notifications/notifications.d.ts +1 -10
  77. package/dist/domains/notifications/notifications.d.ts.map +1 -1
  78. package/dist/domains/notifications/notifications.js +2 -3
  79. package/dist/domains/notifications/notifications.js.map +1 -1
  80. package/dist/domains/onboarding/onboarding-answers.d.ts +42 -14
  81. package/dist/domains/onboarding/onboarding-answers.d.ts.map +1 -1
  82. package/dist/domains/onboarding/onboarding-answers.js +49 -33
  83. package/dist/domains/onboarding/onboarding-answers.js.map +1 -1
  84. package/dist/domains/onboarding/onboarding.d.ts +332 -138
  85. package/dist/domains/onboarding/onboarding.d.ts.map +1 -1
  86. package/dist/domains/onboarding/onboarding.js +47 -59
  87. package/dist/domains/onboarding/onboarding.js.map +1 -1
  88. package/dist/domains/products/products.d.ts +43 -8
  89. package/dist/domains/products/products.d.ts.map +1 -1
  90. package/dist/domains/products/products.js +57 -11
  91. package/dist/domains/products/products.js.map +1 -1
  92. package/dist/domains/profile/founder-profile.d.ts +123 -143
  93. package/dist/domains/profile/founder-profile.d.ts.map +1 -1
  94. package/dist/domains/profile/founder-profile.js +77 -35
  95. package/dist/domains/profile/founder-profile.js.map +1 -1
  96. package/dist/domains/profile/my-profile.d.ts +3 -3
  97. package/dist/domains/reviews/reviews.d.ts +1 -1
  98. package/dist/domains/reviews/reviews.d.ts.map +1 -1
  99. package/dist/domains/reviews/reviews.js +2 -2
  100. package/dist/domains/reviews/reviews.js.map +1 -1
  101. package/dist/domains/support/support.d.ts +22 -3
  102. package/dist/domains/support/support.d.ts.map +1 -1
  103. package/dist/domains/support/support.js +19 -5
  104. package/dist/domains/support/support.js.map +1 -1
  105. package/dist/domains/team/team.d.ts.map +1 -1
  106. package/dist/domains/team/team.js +3 -6
  107. package/dist/domains/team/team.js.map +1 -1
  108. package/dist/domains/uploads/uploads.d.ts +7 -22
  109. package/dist/domains/uploads/uploads.d.ts.map +1 -1
  110. package/dist/domains/uploads/uploads.js +7 -21
  111. package/dist/domains/uploads/uploads.js.map +1 -1
  112. package/dist/env.d.ts +26 -13
  113. package/dist/env.d.ts.map +1 -1
  114. package/dist/env.js +58 -23
  115. package/dist/env.js.map +1 -1
  116. package/dist/index.d.ts +19 -17
  117. package/dist/index.d.ts.map +1 -1
  118. package/dist/index.js +21 -17
  119. package/dist/index.js.map +1 -1
  120. package/package.json +1 -1
@@ -0,0 +1,331 @@
1
+ /**
2
+ * Where a member is and where they live: Jansathi's location model (0.19.0)
3
+ * =========================================================================
4
+ *
5
+ * The user, 2026-10-21: Founder Circle takes Jansathi's location drawer
6
+ * (state, then district, then locality, from `master_db.areas`) and its whole
7
+ * location model ("just copy paste things from jansathi"). The plan, with the
8
+ * Jansathi analysis it copies: founder-circle/LOCATION-PLAN-2026-10-21.md.
9
+ *
10
+ * Two axes, as in Jansathi, never mixed:
11
+ * - the DECLARED location (where the member lives): picked in the drawer, or
12
+ * filled from GPS by the member, then saved. A client sends AREA IDS only
13
+ * (`locationPickSchema`); the server reads the names, the public city and
14
+ * the coordinates from `master_db`, so no client can store a place the area
15
+ * tree does not have.
16
+ * - the GPS axis (where the member is): the daily sync and "Use my location"
17
+ * send a fix (`geolocationFixSchema`); the server keeps it, the area it
18
+ * falls in, and the last `LOCATION_HISTORY_MAX` of each. Nothing on this
19
+ * axis is ever shown to another member.
20
+ *
21
+ * Area ids are master_db's ObjectIds: stable for the life of the area tree.
22
+ *
23
+ * @module domains/location/areas
24
+ */
25
+ import { z } from 'zod';
26
+ /** The levels of `master_db.areas` a member's location can name. */
27
+ export declare const AREA_TYPE_VALUES: readonly ["COUNTRY", "STATE", "DISTRICT", "SUB_DISTRICT", "BLOCK", "GRAM_PANCHAYAT", "VILLAGE", "URBAN_BODY", "URBAN_WARD"];
28
+ export declare const areaTypeSchema: z.ZodEnum<{
29
+ COUNTRY: "COUNTRY";
30
+ STATE: "STATE";
31
+ DISTRICT: "DISTRICT";
32
+ SUB_DISTRICT: "SUB_DISTRICT";
33
+ BLOCK: "BLOCK";
34
+ GRAM_PANCHAYAT: "GRAM_PANCHAYAT";
35
+ VILLAGE: "VILLAGE";
36
+ URBAN_BODY: "URBAN_BODY";
37
+ URBAN_WARD: "URBAN_WARD";
38
+ }>;
39
+ export type AreaType = z.infer<typeof areaTypeSchema>;
40
+ /**
41
+ * The drawer's third step lists these, everything below a district that a
42
+ * member can live in: Jansathi's `LOCALITY_TYPE_TO_ADMIN_KEY`, each one a
43
+ * stored level. Jansathi's drawer also lists HAMLET and LOCALITY and then
44
+ * refuses them on save (no stored level); here they are never offered.
45
+ * RURAL_WARD is not offered either (Jansathi neither): it has no point.
46
+ */
47
+ export declare const LOCALITY_AREA_TYPES: readonly ["SUB_DISTRICT", "BLOCK", "GRAM_PANCHAYAT", "VILLAGE", "URBAN_BODY", "URBAN_WARD"];
48
+ export type LocalityAreaType = (typeof LOCALITY_AREA_TYPES)[number];
49
+ /** How a locality row names its level (Jansathi's `addressForm.areaTypes`). */
50
+ export declare const LOCALITY_TYPE_LABEL: Record<LocalityAreaType, string>;
51
+ /**
52
+ * An urban body's kind, as the area builder writes it (reform-backend's
53
+ * `URBAN_BODY_TYPES`, LGD). Read leniently: a kind added there later shows as
54
+ * "Town or city" rather than breaking the list.
55
+ */
56
+ export declare const URBAN_BODY_TYPE_VALUES: readonly ["Municipal Corporation", "Municipality", "Notified Area Council", "Town Panchayat", "Nagar Panchayat", "Municipal Council", "NCT Municipal Council", "City Municipal Council", "Town Municipal Council", "Cantonment Board"];
57
+ /** One area as a list row and as part of a place: what a client may show. */
58
+ export declare const areaOptionSchema: z.ZodObject<{
59
+ id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
60
+ name: z.ZodString;
61
+ type: z.ZodEnum<{
62
+ COUNTRY: "COUNTRY";
63
+ STATE: "STATE";
64
+ DISTRICT: "DISTRICT";
65
+ SUB_DISTRICT: "SUB_DISTRICT";
66
+ BLOCK: "BLOCK";
67
+ GRAM_PANCHAYAT: "GRAM_PANCHAYAT";
68
+ VILLAGE: "VILLAGE";
69
+ URBAN_BODY: "URBAN_BODY";
70
+ URBAN_WARD: "URBAN_WARD";
71
+ }>;
72
+ urbanBodyType: z.ZodOptional<z.ZodString>;
73
+ within: z.ZodOptional<z.ZodString>;
74
+ }, z.core.$strip>;
75
+ export type AreaOption = z.infer<typeof areaOptionSchema>;
76
+ /**
77
+ * An area as a member reads it: a ward the tree names by its number alone
78
+ * ("25") is "Ward 25"; anything else by its name.
79
+ */
80
+ export declare function areaWords(area: Pick<AreaOption, 'name' | 'type'>): string;
81
+ /** `GET /location/areas/states` and `/districts`: the whole list, sorted by name. */
82
+ export declare const areaListSchema: z.ZodObject<{
83
+ items: z.ZodArray<z.ZodObject<{
84
+ id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
85
+ name: z.ZodString;
86
+ type: z.ZodEnum<{
87
+ COUNTRY: "COUNTRY";
88
+ STATE: "STATE";
89
+ DISTRICT: "DISTRICT";
90
+ SUB_DISTRICT: "SUB_DISTRICT";
91
+ BLOCK: "BLOCK";
92
+ GRAM_PANCHAYAT: "GRAM_PANCHAYAT";
93
+ VILLAGE: "VILLAGE";
94
+ URBAN_BODY: "URBAN_BODY";
95
+ URBAN_WARD: "URBAN_WARD";
96
+ }>;
97
+ urbanBodyType: z.ZodOptional<z.ZodString>;
98
+ within: z.ZodOptional<z.ZodString>;
99
+ }, z.core.$strip>>;
100
+ }, z.core.$strip>;
101
+ export type AreaList = z.infer<typeof areaListSchema>;
102
+ /** `GET /location/areas/localities`: one page (`LOCALITY_PAGE_SIZE`), sorted by name. */
103
+ export declare const areaPageSchema: z.ZodObject<{
104
+ items: z.ZodArray<z.ZodObject<{
105
+ id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
106
+ name: z.ZodString;
107
+ type: z.ZodEnum<{
108
+ COUNTRY: "COUNTRY";
109
+ STATE: "STATE";
110
+ DISTRICT: "DISTRICT";
111
+ SUB_DISTRICT: "SUB_DISTRICT";
112
+ BLOCK: "BLOCK";
113
+ GRAM_PANCHAYAT: "GRAM_PANCHAYAT";
114
+ VILLAGE: "VILLAGE";
115
+ URBAN_BODY: "URBAN_BODY";
116
+ URBAN_WARD: "URBAN_WARD";
117
+ }>;
118
+ urbanBodyType: z.ZodOptional<z.ZodString>;
119
+ within: z.ZodOptional<z.ZodString>;
120
+ }, z.core.$strip>>;
121
+ hasMore: z.ZodBoolean;
122
+ }, z.core.$strip>;
123
+ export type AreaPage = z.infer<typeof areaPageSchema>;
124
+ /** `GET /location/areas/districts?stateId=` */
125
+ export declare const districtsQuerySchema: z.ZodObject<{
126
+ stateId: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
127
+ }, z.core.$strip>;
128
+ export type DistrictsQuery = z.infer<typeof districtsQuerySchema>;
129
+ /**
130
+ * `GET /location/areas/localities?districtId=&q=&skip=`: every locality level
131
+ * anywhere inside the district (a ward sits under its town, not under the
132
+ * district). `q` matches anywhere in the name, as Jansathi's does; an emptied
133
+ * box is "no search".
134
+ */
135
+ export declare const localitiesQuerySchema: z.ZodObject<{
136
+ districtId: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
137
+ q: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
138
+ skip: z.ZodPreprocess<z.ZodDefault<z.ZodCoercedNumber<unknown>>, unknown>;
139
+ }, z.core.$strip>;
140
+ export type LocalitiesQuery = z.infer<typeof localitiesQuerySchema>;
141
+ export type LocalitiesQueryInput = z.input<typeof localitiesQuerySchema>;
142
+ /**
143
+ * A location as it is WRITTEN (0.19.0): onboarding's finish and Me's personal
144
+ * details. Ids only. `localityId: null` is the member's "my locality is not
145
+ * listed": the whole district (the user's choice, 2026-10-21).
146
+ *
147
+ * The server refuses ids that are not one chain (a district outside the
148
+ * state, a locality outside the district) and a state Founder Circle's list
149
+ * does not name (`findIndiaState`).
150
+ */
151
+ export declare const locationPickSchema: z.ZodObject<{
152
+ stateId: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
153
+ districtId: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
154
+ localityId: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
155
+ }, z.core.$strict>;
156
+ export type LocationPick = z.infer<typeof locationPickSchema>;
157
+ /**
158
+ * A place with its names: the member's own location as Me reads it back, the
159
+ * area GPS matched, and the onboarding draft. What the location field shows
160
+ * ("Locality, District, State") and where the drawer reopens. Owner-only:
161
+ * other members read `locationSchema` (city, state, country).
162
+ */
163
+ export declare const placeSchema: z.ZodObject<{
164
+ state: z.ZodObject<{
165
+ id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
166
+ name: z.ZodString;
167
+ type: z.ZodEnum<{
168
+ COUNTRY: "COUNTRY";
169
+ STATE: "STATE";
170
+ DISTRICT: "DISTRICT";
171
+ SUB_DISTRICT: "SUB_DISTRICT";
172
+ BLOCK: "BLOCK";
173
+ GRAM_PANCHAYAT: "GRAM_PANCHAYAT";
174
+ VILLAGE: "VILLAGE";
175
+ URBAN_BODY: "URBAN_BODY";
176
+ URBAN_WARD: "URBAN_WARD";
177
+ }>;
178
+ urbanBodyType: z.ZodOptional<z.ZodString>;
179
+ within: z.ZodOptional<z.ZodString>;
180
+ }, z.core.$strip>;
181
+ district: z.ZodObject<{
182
+ id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
183
+ name: z.ZodString;
184
+ type: z.ZodEnum<{
185
+ COUNTRY: "COUNTRY";
186
+ STATE: "STATE";
187
+ DISTRICT: "DISTRICT";
188
+ SUB_DISTRICT: "SUB_DISTRICT";
189
+ BLOCK: "BLOCK";
190
+ GRAM_PANCHAYAT: "GRAM_PANCHAYAT";
191
+ VILLAGE: "VILLAGE";
192
+ URBAN_BODY: "URBAN_BODY";
193
+ URBAN_WARD: "URBAN_WARD";
194
+ }>;
195
+ urbanBodyType: z.ZodOptional<z.ZodString>;
196
+ within: z.ZodOptional<z.ZodString>;
197
+ }, z.core.$strip>;
198
+ locality: z.ZodNullable<z.ZodObject<{
199
+ id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
200
+ name: z.ZodString;
201
+ type: z.ZodEnum<{
202
+ COUNTRY: "COUNTRY";
203
+ STATE: "STATE";
204
+ DISTRICT: "DISTRICT";
205
+ SUB_DISTRICT: "SUB_DISTRICT";
206
+ BLOCK: "BLOCK";
207
+ GRAM_PANCHAYAT: "GRAM_PANCHAYAT";
208
+ VILLAGE: "VILLAGE";
209
+ URBAN_BODY: "URBAN_BODY";
210
+ URBAN_WARD: "URBAN_WARD";
211
+ }>;
212
+ urbanBodyType: z.ZodOptional<z.ZodString>;
213
+ within: z.ZodOptional<z.ZodString>;
214
+ }, z.core.$strip>>;
215
+ }, z.core.$strip>;
216
+ export type Place = z.infer<typeof placeSchema>;
217
+ /** The pick a place is (what saving it sends). */
218
+ export declare function pickOfPlace(place: Place): LocationPick;
219
+ /**
220
+ * "Locality, what it sits in, District, State", the deepest first; a name
221
+ * repeated is said once ("Ward 25, Bassi, Jaipur, Rajasthan"; "Rajpur,
222
+ * Dehradun, Uttarakhand").
223
+ */
224
+ export declare function placeWords(place: Place): string;
225
+ /**
226
+ * `POST /me/geolocation`: one GPS fix, from the daily sync or a member's
227
+ * "Use my location" (Jansathi's body). Plain degrees, accuracy in metres. A
228
+ * fix coarser than `GEO_ACCURACY_MAX_M` is refused: it cannot name a place.
229
+ */
230
+ export declare const geolocationFixSchema: z.ZodObject<{
231
+ latitude: z.ZodNumber;
232
+ longitude: z.ZodNumber;
233
+ accuracy: z.ZodNumber;
234
+ }, z.core.$strict>;
235
+ export type GeolocationFix = z.infer<typeof geolocationFixSchema>;
236
+ /**
237
+ * The answer: the place the fix falls in, for "Use my location" to put in the
238
+ * field (the member still saves it), or `null` when the area tree has no
239
+ * confident match (outside India, or nothing within reach). The daily sync
240
+ * ignores it. Never the coordinates themselves.
241
+ */
242
+ export declare const geolocationSyncResultSchema: z.ZodObject<{
243
+ synced: z.ZodLiteral<true>;
244
+ match: z.ZodNullable<z.ZodObject<{
245
+ state: z.ZodObject<{
246
+ id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
247
+ name: z.ZodString;
248
+ type: z.ZodEnum<{
249
+ COUNTRY: "COUNTRY";
250
+ STATE: "STATE";
251
+ DISTRICT: "DISTRICT";
252
+ SUB_DISTRICT: "SUB_DISTRICT";
253
+ BLOCK: "BLOCK";
254
+ GRAM_PANCHAYAT: "GRAM_PANCHAYAT";
255
+ VILLAGE: "VILLAGE";
256
+ URBAN_BODY: "URBAN_BODY";
257
+ URBAN_WARD: "URBAN_WARD";
258
+ }>;
259
+ urbanBodyType: z.ZodOptional<z.ZodString>;
260
+ within: z.ZodOptional<z.ZodString>;
261
+ }, z.core.$strip>;
262
+ district: z.ZodObject<{
263
+ id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
264
+ name: z.ZodString;
265
+ type: z.ZodEnum<{
266
+ COUNTRY: "COUNTRY";
267
+ STATE: "STATE";
268
+ DISTRICT: "DISTRICT";
269
+ SUB_DISTRICT: "SUB_DISTRICT";
270
+ BLOCK: "BLOCK";
271
+ GRAM_PANCHAYAT: "GRAM_PANCHAYAT";
272
+ VILLAGE: "VILLAGE";
273
+ URBAN_BODY: "URBAN_BODY";
274
+ URBAN_WARD: "URBAN_WARD";
275
+ }>;
276
+ urbanBodyType: z.ZodOptional<z.ZodString>;
277
+ within: z.ZodOptional<z.ZodString>;
278
+ }, z.core.$strip>;
279
+ locality: z.ZodNullable<z.ZodObject<{
280
+ id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
281
+ name: z.ZodString;
282
+ type: z.ZodEnum<{
283
+ COUNTRY: "COUNTRY";
284
+ STATE: "STATE";
285
+ DISTRICT: "DISTRICT";
286
+ SUB_DISTRICT: "SUB_DISTRICT";
287
+ BLOCK: "BLOCK";
288
+ GRAM_PANCHAYAT: "GRAM_PANCHAYAT";
289
+ VILLAGE: "VILLAGE";
290
+ URBAN_BODY: "URBAN_BODY";
291
+ URBAN_WARD: "URBAN_WARD";
292
+ }>;
293
+ urbanBodyType: z.ZodOptional<z.ZodString>;
294
+ within: z.ZodOptional<z.ZodString>;
295
+ }, z.core.$strip>>;
296
+ }, z.core.$strip>>;
297
+ }, z.core.$strip>;
298
+ export type GeolocationSyncResult = z.infer<typeof geolocationSyncResultSchema>;
299
+ /**
300
+ * Whether Founder Circle keeps the member's positions (the user, 2026-10-22:
301
+ * "Add stop + clear", India's DPDP right to withdraw consent). On by default,
302
+ * as Jansathi; it only matters once the browser allows location at all.
303
+ *
304
+ * OFF: the server keeps NO position: a fix (the daily check or a tap on "Use
305
+ * my location") is still answered with the place it falls in, so the field can
306
+ * be filled, but nothing of it is stored, and no history row is written, not
307
+ * even for a saved place. The web stops the daily check. The member's chosen
308
+ * place is not touched: removing it is the location field's Remove.
309
+ *
310
+ * `GET /api/v1/me/location-settings`, and `PATCH` with
311
+ * `updateLocationSettingsBodySchema`, both answer this.
312
+ */
313
+ export declare const locationSettingsSchema: z.ZodObject<{
314
+ dailyLocation: z.ZodBoolean;
315
+ }, z.core.$strip>;
316
+ export type LocationSettings = z.infer<typeof locationSettingsSchema>;
317
+ /** `PATCH /api/v1/me/location-settings` */
318
+ export declare const updateLocationSettingsBodySchema: z.ZodObject<{
319
+ dailyLocation: z.ZodBoolean;
320
+ }, z.core.$strict>;
321
+ export type UpdateLocationSettingsBody = z.infer<typeof updateLocationSettingsBodySchema>;
322
+ /**
323
+ * `DELETE /api/v1/me/location-history`: every position and area the member's
324
+ * device sent, and the position kept with their saved place, deleted at once.
325
+ * The chosen place itself (what others see as city and state) stays.
326
+ */
327
+ export declare const clearLocationHistoryResultSchema: z.ZodObject<{
328
+ cleared: z.ZodLiteral<true>;
329
+ }, z.core.$strip>;
330
+ export type ClearLocationHistoryResult = z.infer<typeof clearLocationHistoryResultSchema>;
331
+ //# sourceMappingURL=areas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"areas.d.ts","sourceRoot":"","sources":["../../../src/domains/location/areas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,oEAAoE;AACpE,eAAO,MAAM,gBAAgB,6HAUnB,CAAC;AACX,eAAO,MAAM,cAAc;;;;;;;;;;EAA2B,CAAC;AACvD,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,6FAOQ,CAAC;AACzC,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,mBAAmB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEpE,+EAA+E;AAC/E,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAOhE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,wOAWzB,CAAC;AAEX,6EAA6E;AAC7E,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;iBAa3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC,GAAG,MAAM,CAIzE;AAED,qFAAqF;AACrF,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;iBAAiD,CAAC;AAC7E,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD,yFAAyF;AACzF,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;iBAGzB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD,+CAA+C;AAC/C,eAAO,MAAM,oBAAoB;;iBAAwC,CAAC;AAC1E,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;;;;iBAIhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAYzE;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB;;;;kBAYpB,CAAC;AACZ,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;;;GAKG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAKtB,CAAC;AACH,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD,kDAAkD;AAClD,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,YAAY,CAMtD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAQ/C;AAID;;;;GAIG;AACH,eAAO,MAAM,oBAAoB;;;;kBAMtB,CAAC;AACZ,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAElE;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAGtC,CAAC;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,2BAA2B,CAAC,CAAC;AAIhF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB;;iBAA2C,CAAC;AAC/E,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,2CAA2C;AAC3C,eAAO,MAAM,gCAAgC;;kBAAoD,CAAC;AAClG,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC;AAE1F;;;;GAIG;AACH,eAAO,MAAM,gCAAgC;;iBAAyC,CAAC;AACvF,MAAM,MAAM,0BAA0B,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gCAAgC,CAAC,CAAC"}
@@ -0,0 +1,238 @@
1
+ /**
2
+ * Where a member is and where they live: Jansathi's location model (0.19.0)
3
+ * =========================================================================
4
+ *
5
+ * The user, 2026-10-21: Founder Circle takes Jansathi's location drawer
6
+ * (state, then district, then locality, from `master_db.areas`) and its whole
7
+ * location model ("just copy paste things from jansathi"). The plan, with the
8
+ * Jansathi analysis it copies: founder-circle/LOCATION-PLAN-2026-10-21.md.
9
+ *
10
+ * Two axes, as in Jansathi, never mixed:
11
+ * - the DECLARED location (where the member lives): picked in the drawer, or
12
+ * filled from GPS by the member, then saved. A client sends AREA IDS only
13
+ * (`locationPickSchema`); the server reads the names, the public city and
14
+ * the coordinates from `master_db`, so no client can store a place the area
15
+ * tree does not have.
16
+ * - the GPS axis (where the member is): the daily sync and "Use my location"
17
+ * send a fix (`geolocationFixSchema`); the server keeps it, the area it
18
+ * falls in, and the last `LOCATION_HISTORY_MAX` of each. Nothing on this
19
+ * axis is ever shown to another member.
20
+ *
21
+ * Area ids are master_db's ObjectIds: stable for the life of the area tree.
22
+ *
23
+ * @module domains/location/areas
24
+ */
25
+ import { z } from 'zod';
26
+ import { emptyStringToUndefined, objectIdSchema, queryInt } from '../../core/common.js';
27
+ import { AREA_NAME_MAX, AREA_SEARCH_MAX, GEO_ACCURACY_MAX_M, LOCALITY_SKIP_MAX, } from '../../core/limits.js';
28
+ import { VALIDATION_MESSAGE } from '../../core/validation.js';
29
+ // ─── The area tree ───────────────────────────────────────────────────────────
30
+ /** The levels of `master_db.areas` a member's location can name. */
31
+ export const AREA_TYPE_VALUES = [
32
+ 'COUNTRY',
33
+ 'STATE',
34
+ 'DISTRICT',
35
+ 'SUB_DISTRICT',
36
+ 'BLOCK',
37
+ 'GRAM_PANCHAYAT',
38
+ 'VILLAGE',
39
+ 'URBAN_BODY',
40
+ 'URBAN_WARD',
41
+ ];
42
+ export const areaTypeSchema = z.enum(AREA_TYPE_VALUES);
43
+ /**
44
+ * The drawer's third step lists these, everything below a district that a
45
+ * member can live in: Jansathi's `LOCALITY_TYPE_TO_ADMIN_KEY`, each one a
46
+ * stored level. Jansathi's drawer also lists HAMLET and LOCALITY and then
47
+ * refuses them on save (no stored level); here they are never offered.
48
+ * RURAL_WARD is not offered either (Jansathi neither): it has no point.
49
+ */
50
+ export const LOCALITY_AREA_TYPES = [
51
+ 'SUB_DISTRICT',
52
+ 'BLOCK',
53
+ 'GRAM_PANCHAYAT',
54
+ 'VILLAGE',
55
+ 'URBAN_BODY',
56
+ 'URBAN_WARD',
57
+ ];
58
+ /** How a locality row names its level (Jansathi's `addressForm.areaTypes`). */
59
+ export const LOCALITY_TYPE_LABEL = {
60
+ SUB_DISTRICT: 'Tehsil',
61
+ BLOCK: 'Block',
62
+ GRAM_PANCHAYAT: 'Gram Panchayat',
63
+ VILLAGE: 'Village',
64
+ URBAN_BODY: 'Town or city',
65
+ URBAN_WARD: 'Ward',
66
+ };
67
+ /**
68
+ * An urban body's kind, as the area builder writes it (reform-backend's
69
+ * `URBAN_BODY_TYPES`, LGD). Read leniently: a kind added there later shows as
70
+ * "Town or city" rather than breaking the list.
71
+ */
72
+ export const URBAN_BODY_TYPE_VALUES = [
73
+ 'Municipal Corporation',
74
+ 'Municipality',
75
+ 'Notified Area Council',
76
+ 'Town Panchayat',
77
+ 'Nagar Panchayat',
78
+ 'Municipal Council',
79
+ 'NCT Municipal Council',
80
+ 'City Municipal Council',
81
+ 'Town Municipal Council',
82
+ 'Cantonment Board',
83
+ ];
84
+ /** One area as a list row and as part of a place: what a client may show. */
85
+ export const areaOptionSchema = z.object({
86
+ id: objectIdSchema,
87
+ name: z.string().trim().min(1).max(AREA_NAME_MAX),
88
+ type: areaTypeSchema,
89
+ /** Only on an URBAN_BODY, when the tree knows its kind. */
90
+ urbanBodyType: z.string().trim().min(1).max(AREA_NAME_MAX).optional(),
91
+ /**
92
+ * What a locality sits in, to tell same-named places apart (0.19.0, the
93
+ * review: one district has 12 villages called "Pali", a town's wards are
94
+ * named "1" to "35"): a ward's town, a village's or panchayat's tehsil or
95
+ * block. Absent on states, districts and towns.
96
+ */
97
+ within: z.string().trim().min(1).max(AREA_NAME_MAX).optional(),
98
+ });
99
+ /**
100
+ * An area as a member reads it: a ward the tree names by its number alone
101
+ * ("25") is "Ward 25"; anything else by its name.
102
+ */
103
+ export function areaWords(area) {
104
+ return area.type === 'URBAN_WARD' && /^\d+$/.test(area.name.trim())
105
+ ? `Ward ${area.name.trim()}`
106
+ : area.name;
107
+ }
108
+ /** `GET /location/areas/states` and `/districts`: the whole list, sorted by name. */
109
+ export const areaListSchema = z.object({ items: z.array(areaOptionSchema) });
110
+ /** `GET /location/areas/localities`: one page (`LOCALITY_PAGE_SIZE`), sorted by name. */
111
+ export const areaPageSchema = z.object({
112
+ items: z.array(areaOptionSchema),
113
+ hasMore: z.boolean(),
114
+ });
115
+ /** `GET /location/areas/districts?stateId=` */
116
+ export const districtsQuerySchema = z.object({ stateId: objectIdSchema });
117
+ /**
118
+ * `GET /location/areas/localities?districtId=&q=&skip=`: every locality level
119
+ * anywhere inside the district (a ward sits under its town, not under the
120
+ * district). `q` matches anywhere in the name, as Jansathi's does; an emptied
121
+ * box is "no search".
122
+ */
123
+ export const localitiesQuerySchema = z.object({
124
+ districtId: objectIdSchema,
125
+ q: z.preprocess(emptyStringToUndefined, z.string().trim().max(AREA_SEARCH_MAX).optional()),
126
+ skip: queryInt(0, LOCALITY_SKIP_MAX, 0),
127
+ });
128
+ // ─── The declared location ───────────────────────────────────────────────────
129
+ /** An area id a picker must have chosen: unchosen is worded as a choice. */
130
+ const chosenAreaId = (unchosen) => z
131
+ .string({ error: unchosen })
132
+ .min(1, { error: unchosen, abort: true })
133
+ .regex(/^[0-9a-f]{24}$/i, VALIDATION_MESSAGE.invalidId)
134
+ .transform((v) => v.toLowerCase());
135
+ /**
136
+ * A location as it is WRITTEN (0.19.0): onboarding's finish and Me's personal
137
+ * details. Ids only. `localityId: null` is the member's "my locality is not
138
+ * listed": the whole district (the user's choice, 2026-10-21).
139
+ *
140
+ * The server refuses ids that are not one chain (a district outside the
141
+ * state, a locality outside the district) and a state Founder Circle's list
142
+ * does not name (`findIndiaState`).
143
+ */
144
+ export const locationPickSchema = z
145
+ .object({
146
+ stateId: chosenAreaId(VALIDATION_MESSAGE.stateRequired),
147
+ districtId: chosenAreaId(VALIDATION_MESSAGE.districtRequired),
148
+ // Said explicitly: an id, or `null` for the whole district. Left out is a
149
+ // choice not made (`localityRequired`), never Zod's own English.
150
+ localityId: z
151
+ .string({ error: VALIDATION_MESSAGE.localityRequired })
152
+ .regex(/^[0-9a-f]{24}$/i, VALIDATION_MESSAGE.invalidId)
153
+ .transform((v) => v.toLowerCase())
154
+ .nullable(),
155
+ })
156
+ .strict();
157
+ /**
158
+ * A place with its names: the member's own location as Me reads it back, the
159
+ * area GPS matched, and the onboarding draft. What the location field shows
160
+ * ("Locality, District, State") and where the drawer reopens. Owner-only:
161
+ * other members read `locationSchema` (city, state, country).
162
+ */
163
+ export const placeSchema = z.object({
164
+ state: areaOptionSchema,
165
+ district: areaOptionSchema,
166
+ /** `null`: the whole district. */
167
+ locality: areaOptionSchema.nullable(),
168
+ });
169
+ /** The pick a place is (what saving it sends). */
170
+ export function pickOfPlace(place) {
171
+ return {
172
+ stateId: place.state.id,
173
+ districtId: place.district.id,
174
+ localityId: place.locality?.id ?? null,
175
+ };
176
+ }
177
+ /**
178
+ * "Locality, what it sits in, District, State", the deepest first; a name
179
+ * repeated is said once ("Ward 25, Bassi, Jaipur, Rajasthan"; "Rajpur,
180
+ * Dehradun, Uttarakhand").
181
+ */
182
+ export function placeWords(place) {
183
+ const parts = [
184
+ place.locality ? areaWords(place.locality) : undefined,
185
+ place.locality?.within,
186
+ place.district.name,
187
+ place.state.name,
188
+ ].filter((p) => Boolean(p));
189
+ return parts.filter((p, i) => parts.indexOf(p) === i).join(', ');
190
+ }
191
+ // ─── The GPS axis ────────────────────────────────────────────────────────────
192
+ /**
193
+ * `POST /me/geolocation`: one GPS fix, from the daily sync or a member's
194
+ * "Use my location" (Jansathi's body). Plain degrees, accuracy in metres. A
195
+ * fix coarser than `GEO_ACCURACY_MAX_M` is refused: it cannot name a place.
196
+ */
197
+ export const geolocationFixSchema = z
198
+ .object({
199
+ latitude: z.number().min(-90).max(90),
200
+ longitude: z.number().min(-180).max(180),
201
+ accuracy: z.number().min(0).max(GEO_ACCURACY_MAX_M),
202
+ })
203
+ .strict();
204
+ /**
205
+ * The answer: the place the fix falls in, for "Use my location" to put in the
206
+ * field (the member still saves it), or `null` when the area tree has no
207
+ * confident match (outside India, or nothing within reach). The daily sync
208
+ * ignores it. Never the coordinates themselves.
209
+ */
210
+ export const geolocationSyncResultSchema = z.object({
211
+ synced: z.literal(true),
212
+ match: placeSchema.nullable(),
213
+ });
214
+ // ─── The member's own control (0.19.0) ───────────────────────────────────────
215
+ /**
216
+ * Whether Founder Circle keeps the member's positions (the user, 2026-10-22:
217
+ * "Add stop + clear", India's DPDP right to withdraw consent). On by default,
218
+ * as Jansathi; it only matters once the browser allows location at all.
219
+ *
220
+ * OFF: the server keeps NO position: a fix (the daily check or a tap on "Use
221
+ * my location") is still answered with the place it falls in, so the field can
222
+ * be filled, but nothing of it is stored, and no history row is written, not
223
+ * even for a saved place. The web stops the daily check. The member's chosen
224
+ * place is not touched: removing it is the location field's Remove.
225
+ *
226
+ * `GET /api/v1/me/location-settings`, and `PATCH` with
227
+ * `updateLocationSettingsBodySchema`, both answer this.
228
+ */
229
+ export const locationSettingsSchema = z.object({ dailyLocation: z.boolean() });
230
+ /** `PATCH /api/v1/me/location-settings` */
231
+ export const updateLocationSettingsBodySchema = z.object({ dailyLocation: z.boolean() }).strict();
232
+ /**
233
+ * `DELETE /api/v1/me/location-history`: every position and area the member's
234
+ * device sent, and the position kept with their saved place, deleted at once.
235
+ * The chosen place itself (what others see as city and state) stays.
236
+ */
237
+ export const clearLocationHistoryResultSchema = z.object({ cleared: z.literal(true) });
238
+ //# sourceMappingURL=areas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"areas.js","sourceRoot":"","sources":["../../../src/domains/location/areas.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Where a member is and where they live: Jansathi's location model (0.19.0)\n * =========================================================================\n *\n * The user, 2026-10-21: Founder Circle takes Jansathi's location drawer\n * (state, then district, then locality, from `master_db.areas`) and its whole\n * location model (\"just copy paste things from jansathi\"). The plan, with the\n * Jansathi analysis it copies: founder-circle/LOCATION-PLAN-2026-10-21.md.\n *\n * Two axes, as in Jansathi, never mixed:\n * - the DECLARED location (where the member lives): picked in the drawer, or\n * filled from GPS by the member, then saved. A client sends AREA IDS only\n * (`locationPickSchema`); the server reads the names, the public city and\n * the coordinates from `master_db`, so no client can store a place the area\n * tree does not have.\n * - the GPS axis (where the member is): the daily sync and \"Use my location\"\n * send a fix (`geolocationFixSchema`); the server keeps it, the area it\n * falls in, and the last `LOCATION_HISTORY_MAX` of each. Nothing on this\n * axis is ever shown to another member.\n *\n * Area ids are master_db's ObjectIds: stable for the life of the area tree.\n *\n * @module domains/location/areas\n */\n\nimport { z } from 'zod';\nimport { emptyStringToUndefined, objectIdSchema, queryInt } from '../../core/common.js';\nimport {\n AREA_NAME_MAX,\n AREA_SEARCH_MAX,\n GEO_ACCURACY_MAX_M,\n LOCALITY_SKIP_MAX,\n} from '../../core/limits.js';\nimport { VALIDATION_MESSAGE } from '../../core/validation.js';\n\n// ─── The area tree ───────────────────────────────────────────────────────────\n\n/** The levels of `master_db.areas` a member's location can name. */\nexport const AREA_TYPE_VALUES = [\n 'COUNTRY',\n 'STATE',\n 'DISTRICT',\n 'SUB_DISTRICT',\n 'BLOCK',\n 'GRAM_PANCHAYAT',\n 'VILLAGE',\n 'URBAN_BODY',\n 'URBAN_WARD',\n] as const;\nexport const areaTypeSchema = z.enum(AREA_TYPE_VALUES);\nexport type AreaType = z.infer<typeof areaTypeSchema>;\n\n/**\n * The drawer's third step lists these, everything below a district that a\n * member can live in: Jansathi's `LOCALITY_TYPE_TO_ADMIN_KEY`, each one a\n * stored level. Jansathi's drawer also lists HAMLET and LOCALITY and then\n * refuses them on save (no stored level); here they are never offered.\n * RURAL_WARD is not offered either (Jansathi neither): it has no point.\n */\nexport const LOCALITY_AREA_TYPES = [\n 'SUB_DISTRICT',\n 'BLOCK',\n 'GRAM_PANCHAYAT',\n 'VILLAGE',\n 'URBAN_BODY',\n 'URBAN_WARD',\n] as const satisfies readonly AreaType[];\nexport type LocalityAreaType = (typeof LOCALITY_AREA_TYPES)[number];\n\n/** How a locality row names its level (Jansathi's `addressForm.areaTypes`). */\nexport const LOCALITY_TYPE_LABEL: Record<LocalityAreaType, string> = {\n SUB_DISTRICT: 'Tehsil',\n BLOCK: 'Block',\n GRAM_PANCHAYAT: 'Gram Panchayat',\n VILLAGE: 'Village',\n URBAN_BODY: 'Town or city',\n URBAN_WARD: 'Ward',\n};\n\n/**\n * An urban body's kind, as the area builder writes it (reform-backend's\n * `URBAN_BODY_TYPES`, LGD). Read leniently: a kind added there later shows as\n * \"Town or city\" rather than breaking the list.\n */\nexport const URBAN_BODY_TYPE_VALUES = [\n 'Municipal Corporation',\n 'Municipality',\n 'Notified Area Council',\n 'Town Panchayat',\n 'Nagar Panchayat',\n 'Municipal Council',\n 'NCT Municipal Council',\n 'City Municipal Council',\n 'Town Municipal Council',\n 'Cantonment Board',\n] as const;\n\n/** One area as a list row and as part of a place: what a client may show. */\nexport const areaOptionSchema = z.object({\n id: objectIdSchema,\n name: z.string().trim().min(1).max(AREA_NAME_MAX),\n type: areaTypeSchema,\n /** Only on an URBAN_BODY, when the tree knows its kind. */\n urbanBodyType: z.string().trim().min(1).max(AREA_NAME_MAX).optional(),\n /**\n * What a locality sits in, to tell same-named places apart (0.19.0, the\n * review: one district has 12 villages called \"Pali\", a town's wards are\n * named \"1\" to \"35\"): a ward's town, a village's or panchayat's tehsil or\n * block. Absent on states, districts and towns.\n */\n within: z.string().trim().min(1).max(AREA_NAME_MAX).optional(),\n});\nexport type AreaOption = z.infer<typeof areaOptionSchema>;\n\n/**\n * An area as a member reads it: a ward the tree names by its number alone\n * (\"25\") is \"Ward 25\"; anything else by its name.\n */\nexport function areaWords(area: Pick<AreaOption, 'name' | 'type'>): string {\n return area.type === 'URBAN_WARD' && /^\\d+$/.test(area.name.trim())\n ? `Ward ${area.name.trim()}`\n : area.name;\n}\n\n/** `GET /location/areas/states` and `/districts`: the whole list, sorted by name. */\nexport const areaListSchema = z.object({ items: z.array(areaOptionSchema) });\nexport type AreaList = z.infer<typeof areaListSchema>;\n\n/** `GET /location/areas/localities`: one page (`LOCALITY_PAGE_SIZE`), sorted by name. */\nexport const areaPageSchema = z.object({\n items: z.array(areaOptionSchema),\n hasMore: z.boolean(),\n});\nexport type AreaPage = z.infer<typeof areaPageSchema>;\n\n/** `GET /location/areas/districts?stateId=` */\nexport const districtsQuerySchema = z.object({ stateId: objectIdSchema });\nexport type DistrictsQuery = z.infer<typeof districtsQuerySchema>;\n\n/**\n * `GET /location/areas/localities?districtId=&q=&skip=`: every locality level\n * anywhere inside the district (a ward sits under its town, not under the\n * district). `q` matches anywhere in the name, as Jansathi's does; an emptied\n * box is \"no search\".\n */\nexport const localitiesQuerySchema = z.object({\n districtId: objectIdSchema,\n q: z.preprocess(emptyStringToUndefined, z.string().trim().max(AREA_SEARCH_MAX).optional()),\n skip: queryInt(0, LOCALITY_SKIP_MAX, 0),\n});\nexport type LocalitiesQuery = z.infer<typeof localitiesQuerySchema>;\nexport type LocalitiesQueryInput = z.input<typeof localitiesQuerySchema>;\n\n// ─── The declared location ───────────────────────────────────────────────────\n\n/** An area id a picker must have chosen: unchosen is worded as a choice. */\nconst chosenAreaId = (unchosen: string) =>\n z\n .string({ error: unchosen })\n .min(1, { error: unchosen, abort: true })\n .regex(/^[0-9a-f]{24}$/i, VALIDATION_MESSAGE.invalidId)\n .transform((v) => v.toLowerCase());\n\n/**\n * A location as it is WRITTEN (0.19.0): onboarding's finish and Me's personal\n * details. Ids only. `localityId: null` is the member's \"my locality is not\n * listed\": the whole district (the user's choice, 2026-10-21).\n *\n * The server refuses ids that are not one chain (a district outside the\n * state, a locality outside the district) and a state Founder Circle's list\n * does not name (`findIndiaState`).\n */\nexport const locationPickSchema = z\n .object({\n stateId: chosenAreaId(VALIDATION_MESSAGE.stateRequired),\n districtId: chosenAreaId(VALIDATION_MESSAGE.districtRequired),\n // Said explicitly: an id, or `null` for the whole district. Left out is a\n // choice not made (`localityRequired`), never Zod's own English.\n localityId: z\n .string({ error: VALIDATION_MESSAGE.localityRequired })\n .regex(/^[0-9a-f]{24}$/i, VALIDATION_MESSAGE.invalidId)\n .transform((v) => v.toLowerCase())\n .nullable(),\n })\n .strict();\nexport type LocationPick = z.infer<typeof locationPickSchema>;\n\n/**\n * A place with its names: the member's own location as Me reads it back, the\n * area GPS matched, and the onboarding draft. What the location field shows\n * (\"Locality, District, State\") and where the drawer reopens. Owner-only:\n * other members read `locationSchema` (city, state, country).\n */\nexport const placeSchema = z.object({\n state: areaOptionSchema,\n district: areaOptionSchema,\n /** `null`: the whole district. */\n locality: areaOptionSchema.nullable(),\n});\nexport type Place = z.infer<typeof placeSchema>;\n\n/** The pick a place is (what saving it sends). */\nexport function pickOfPlace(place: Place): LocationPick {\n return {\n stateId: place.state.id,\n districtId: place.district.id,\n localityId: place.locality?.id ?? null,\n };\n}\n\n/**\n * \"Locality, what it sits in, District, State\", the deepest first; a name\n * repeated is said once (\"Ward 25, Bassi, Jaipur, Rajasthan\"; \"Rajpur,\n * Dehradun, Uttarakhand\").\n */\nexport function placeWords(place: Place): string {\n const parts = [\n place.locality ? areaWords(place.locality) : undefined,\n place.locality?.within,\n place.district.name,\n place.state.name,\n ].filter((p): p is string => Boolean(p));\n return parts.filter((p, i) => parts.indexOf(p) === i).join(', ');\n}\n\n// ─── The GPS axis ────────────────────────────────────────────────────────────\n\n/**\n * `POST /me/geolocation`: one GPS fix, from the daily sync or a member's\n * \"Use my location\" (Jansathi's body). Plain degrees, accuracy in metres. A\n * fix coarser than `GEO_ACCURACY_MAX_M` is refused: it cannot name a place.\n */\nexport const geolocationFixSchema = z\n .object({\n latitude: z.number().min(-90).max(90),\n longitude: z.number().min(-180).max(180),\n accuracy: z.number().min(0).max(GEO_ACCURACY_MAX_M),\n })\n .strict();\nexport type GeolocationFix = z.infer<typeof geolocationFixSchema>;\n\n/**\n * The answer: the place the fix falls in, for \"Use my location\" to put in the\n * field (the member still saves it), or `null` when the area tree has no\n * confident match (outside India, or nothing within reach). The daily sync\n * ignores it. Never the coordinates themselves.\n */\nexport const geolocationSyncResultSchema = z.object({\n synced: z.literal(true),\n match: placeSchema.nullable(),\n});\nexport type GeolocationSyncResult = z.infer<typeof geolocationSyncResultSchema>;\n\n// ─── The member's own control (0.19.0) ───────────────────────────────────────\n\n/**\n * Whether Founder Circle keeps the member's positions (the user, 2026-10-22:\n * \"Add stop + clear\", India's DPDP right to withdraw consent). On by default,\n * as Jansathi; it only matters once the browser allows location at all.\n *\n * OFF: the server keeps NO position: a fix (the daily check or a tap on \"Use\n * my location\") is still answered with the place it falls in, so the field can\n * be filled, but nothing of it is stored, and no history row is written, not\n * even for a saved place. The web stops the daily check. The member's chosen\n * place is not touched: removing it is the location field's Remove.\n *\n * `GET /api/v1/me/location-settings`, and `PATCH` with\n * `updateLocationSettingsBodySchema`, both answer this.\n */\nexport const locationSettingsSchema = z.object({ dailyLocation: z.boolean() });\nexport type LocationSettings = z.infer<typeof locationSettingsSchema>;\n\n/** `PATCH /api/v1/me/location-settings` */\nexport const updateLocationSettingsBodySchema = z.object({ dailyLocation: z.boolean() }).strict();\nexport type UpdateLocationSettingsBody = z.infer<typeof updateLocationSettingsBodySchema>;\n\n/**\n * `DELETE /api/v1/me/location-history`: every position and area the member's\n * device sent, and the position kept with their saved place, deleted at once.\n * The chosen place itself (what others see as city and state) stays.\n */\nexport const clearLocationHistoryResultSchema = z.object({ cleared: z.literal(true) });\nexport type ClearLocationHistoryResult = z.infer<typeof clearLocationHistoryResultSchema>;\n"]}