@thejob/schema 2.1.9 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +17 -0
- package/dist/index.cjs +2178 -723
- package/dist/index.d.cts +1991 -535
- package/dist/index.d.ts +1991 -535
- package/dist/index.js +2080 -698
- package/package.json +1 -1
- package/src/billing/billing.constant.ts +179 -0
- package/src/billing/billing.schema.ts +280 -0
- package/src/common/facet.schema.ts +342 -0
- package/src/feedback/feedback-filter.schema.ts +39 -0
- package/src/geo/city-filter.schema.ts +41 -0
- package/src/geo/city.schema.ts +104 -0
- package/src/geo/country-filter.schema.ts +47 -0
- package/src/geo/country.schema.ts +148 -0
- package/src/geo/geo-sync.schema.ts +116 -0
- package/src/geo/geo.constant.ts +78 -0
- package/src/geo/state-filter.schema.ts +57 -0
- package/src/geo/state.schema.ts +101 -0
- package/src/group/group-filter.schema.ts +81 -0
- package/src/group/group.constant.ts +15 -0
- package/src/index.ts +35 -0
- package/src/job/job-filter.schema.ts +266 -0
- package/src/language/language-filter.schema.ts +40 -0
- package/src/marketing/marketing.constant.ts +105 -30
- package/src/marketing/marketing.schema.ts +118 -403
- package/src/page/page-filter.schema.ts +67 -0
- package/src/skill/skill-filter.schema.ts +61 -0
- package/src/user/push-subscription.schema.ts +97 -0
- package/src/user/user-filter.schema.ts +438 -0
- package/src/user/user-interest.schema.ts +34 -2
- package/src/user/user.constant.ts +20 -8
- package/src/user/user.schema.ts +45 -0
- package/src/user/user.types.ts +35 -0
|
@@ -118,16 +118,28 @@ export enum JobAlertFrequency {
|
|
|
118
118
|
export const SupportedJobAlertFrequencies = Object.values(JobAlertFrequency);
|
|
119
119
|
|
|
120
120
|
/**
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
* verification) is governed by neither and always sends.
|
|
121
|
+
* KINDS of notification, by subject matter — what a message is ABOUT, not how
|
|
122
|
+
* it reaches the user. A notification of a given kind may be delivered over any
|
|
123
|
+
* transport (email, web push, and later whatsapp or in-app); this enum is the
|
|
124
|
+
* user's per-subject mute switch and applies across all of them.
|
|
126
125
|
*
|
|
127
|
-
*
|
|
126
|
+
* Named `NotificationType` rather than `...Channel` precisely because "channel"
|
|
127
|
+
* reads as a delivery route. Muting `groupActivity` mutes group activity
|
|
128
|
+
* everywhere, not merely in the mail.
|
|
129
|
+
*
|
|
130
|
+
* Distinct from marketing: notifications are opt-OUT (default on, see
|
|
131
|
+
* `notificationPrefs`), marketing is opt-IN (`marketingConsent.subscribed`).
|
|
132
|
+
* Transactional mail (magic-link, verification) is governed by neither and
|
|
133
|
+
* always sends.
|
|
134
|
+
*
|
|
135
|
+
* Deliberately coarse: four kinds a user will actually reason about, rather
|
|
128
136
|
* than one toggle per email template.
|
|
137
|
+
*
|
|
138
|
+
* The VALUES are stored in Mongo as the keys of `notificationPrefs`, so they
|
|
139
|
+
* are data, not style. Renaming the enum is a type-level change; renaming a
|
|
140
|
+
* value would be a data migration.
|
|
129
141
|
*/
|
|
130
|
-
export enum
|
|
142
|
+
export enum NotificationType {
|
|
131
143
|
/** Group invites, join requests, membership approved/rejected. */
|
|
132
144
|
GroupActivity = "groupActivity",
|
|
133
145
|
/** Activity on jobs the user posted, e.g. a listing expiring. */
|
|
@@ -138,7 +150,7 @@ export enum NotificationChannel {
|
|
|
138
150
|
ProductUpdates = "productUpdates",
|
|
139
151
|
}
|
|
140
152
|
|
|
141
|
-
export const
|
|
153
|
+
export const SupportedNotificationTypes = Object.values(NotificationType);
|
|
142
154
|
|
|
143
155
|
/** ISO-4217 currency codes accepted for the salary preference (extend as needed). */
|
|
144
156
|
export const SupportedSalaryCurrencies = ["USD", "EUR", "GBP", "SEK", "INR"] as const;
|
package/src/user/user.schema.ts
CHANGED
|
@@ -168,6 +168,36 @@ export const UserSchema = object({
|
|
|
168
168
|
})
|
|
169
169
|
.label("Notification Preferences"),
|
|
170
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Consent to receive WEB PUSH notifications.
|
|
173
|
+
*
|
|
174
|
+
* Push is a TRANSPORT, not a category: it carries the same notifications
|
|
175
|
+
* `notificationPrefs` already governs, delivered to a device instead of an
|
|
176
|
+
* inbox. That is why no push value is added to `NotificationType` — the user
|
|
177
|
+
* chooses whether push is used at all, and WHICH kinds are worth pushing
|
|
178
|
+
* stays a product decision.
|
|
179
|
+
*
|
|
180
|
+
* Opt-IN, and it must not be folded into `notificationPrefs`: those are
|
|
181
|
+
* opt-OUT and read `!== false`, so an absent value means ENABLED. Applied to
|
|
182
|
+
* push, every user who predates this field would read as having granted
|
|
183
|
+
* permission they were never asked for. This mirrors `marketingConsent`
|
|
184
|
+
* instead, which is the opt-in shape in this file, down to the provenance
|
|
185
|
+
* fields.
|
|
186
|
+
*
|
|
187
|
+
* Consent alone never delivers anything: a push send also needs a live
|
|
188
|
+
* subscription (see `PushSubscriptionSchema`). This flag is the user's
|
|
189
|
+
* intent, the subscription is the browser grant, and both are required.
|
|
190
|
+
*/
|
|
191
|
+
pushConsent: object({
|
|
192
|
+
subscribed: boolean().default(false).label("Subscribed to push notifications"),
|
|
193
|
+
/** Epoch ms of the last consent change (opt-in or opt-out). */
|
|
194
|
+
updatedAt: number().optional().label("Push consent updated at"),
|
|
195
|
+
/** Where the change originated, e.g. `onboarding`, `settings`, `prompt`. */
|
|
196
|
+
source: string().optional().label("Push consent source"),
|
|
197
|
+
})
|
|
198
|
+
.default({ subscribed: false })
|
|
199
|
+
.label("Push Consent"),
|
|
200
|
+
|
|
171
201
|
/**
|
|
172
202
|
* Vector embedding of the user's profile for semantic/hybrid search.
|
|
173
203
|
* Generated from a structured summary of skills, experience, education, etc.
|
|
@@ -180,6 +210,21 @@ export const UserSchema = object({
|
|
|
180
210
|
.nullable()
|
|
181
211
|
.optional()
|
|
182
212
|
.label("Embedding"),
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Epoch ms the account was created, assigned by the storing service.
|
|
216
|
+
*
|
|
217
|
+
* Declared here, matching `GroupSchema`, because it is not merely stored: all
|
|
218
|
+
* 103,608 user documents carry it, `UserFilterSchema` filters on it
|
|
219
|
+
* (`createdAtWithinDays` / `createdAtOlderThanDays`), and the admin grid sorts
|
|
220
|
+
* and displays it. It was absent, so a consumer deriving its row type from
|
|
221
|
+
* this schema could not name the column it renders, and this object is
|
|
222
|
+
* `.noUnknown().strict()` — an undeclared key is stripped on validation.
|
|
223
|
+
*
|
|
224
|
+
* `updatedAt` is deliberately NOT added alongside it: nothing derives from it
|
|
225
|
+
* yet, and an optional field is only safe to add when something needs it.
|
|
226
|
+
*/
|
|
227
|
+
createdAt: number().optional().label("Created At"),
|
|
183
228
|
})
|
|
184
229
|
.concat(UserGeneralDetailSchema)
|
|
185
230
|
.concat(UserJobPreferencesSchema)
|
package/src/user/user.types.ts
CHANGED
|
@@ -4,6 +4,41 @@ import type { LocationSchema } from "../location/location.schema.js";
|
|
|
4
4
|
import type { EducationSchema } from "./education.schema.js";
|
|
5
5
|
import type { UserGeneralDetailSchema } from "./general-detail.schema.js";
|
|
6
6
|
import type { WorkExperienceSchema } from "./work-experience.schema.js";
|
|
7
|
+
import type { PushSubscriptionSchema } from "./push-subscription.schema.js";
|
|
8
|
+
import type { UserSchema } from "./user.schema.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A user, as this package defines one.
|
|
12
|
+
*
|
|
13
|
+
* DERIVED from `UserSchema`, never hand-written, so it cannot drift from the
|
|
14
|
+
* shape the schema validates. Consumers project subsets of it (a listing
|
|
15
|
+
* returns a handful of fields, a profile read returns more), which is why
|
|
16
|
+
* every field a projection may omit is optional at the schema level.
|
|
17
|
+
*
|
|
18
|
+
* `shortId` and `slug` are assigned by the STORING service and are not part of
|
|
19
|
+
* the validated input shape, so they are added here rather than in the schema.
|
|
20
|
+
* `shortId` is the identifier to use everywhere; `authAccountId` is Better
|
|
21
|
+
* Auth's id and belongs only in auth-related paths.
|
|
22
|
+
*/
|
|
23
|
+
export type User = InferType<typeof UserSchema> & {
|
|
24
|
+
/** Public identifier, server-assigned. The id to key on. */
|
|
25
|
+
shortId?: string;
|
|
26
|
+
/** Public profile handle, server-assigned. */
|
|
27
|
+
slug?: string;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A stored push subscription, with the identifier the storing service assigns.
|
|
32
|
+
*
|
|
33
|
+
* Same split as `User`: `shortId` is server-assigned and so is not part of the
|
|
34
|
+
* validated input shape. Note that the identifier consumers actually key on
|
|
35
|
+
* here is `endpoint`, not `shortId` — it is what the browser re-sends on
|
|
36
|
+
* re-subscribe and what a dead-subscription response names.
|
|
37
|
+
*/
|
|
38
|
+
export type PushSubscriptionRecord = InferType<typeof PushSubscriptionSchema> & {
|
|
39
|
+
/** Public identifier, server-assigned. */
|
|
40
|
+
shortId?: string;
|
|
41
|
+
};
|
|
7
42
|
|
|
8
43
|
export type UserProfileOverview = {
|
|
9
44
|
name: InferType<typeof UserGeneralDetailSchema>["name"];
|