founder-schema 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +125 -17
- package/dist/access/access.d.ts +1 -1
- package/dist/access/access.d.ts.map +1 -1
- package/dist/access/access.js +4 -1
- package/dist/access/access.js.map +1 -1
- package/dist/access/permissions.d.ts +27 -29
- package/dist/access/permissions.d.ts.map +1 -1
- package/dist/access/permissions.js +39 -3
- package/dist/access/permissions.js.map +1 -1
- package/dist/core/common.d.ts +47 -10
- package/dist/core/common.d.ts.map +1 -1
- package/dist/core/common.js +42 -13
- package/dist/core/common.js.map +1 -1
- package/dist/core/errors.d.ts +7 -5
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +17 -11
- package/dist/core/errors.js.map +1 -1
- package/dist/core/limits.d.ts +48 -17
- package/dist/core/limits.d.ts.map +1 -1
- package/dist/core/limits.js +52 -22
- package/dist/core/limits.js.map +1 -1
- package/dist/core/money.d.ts +33 -0
- package/dist/core/money.d.ts.map +1 -0
- package/dist/core/money.js +36 -0
- package/dist/core/money.js.map +1 -0
- package/dist/core/responses.d.ts +14 -4
- package/dist/core/responses.d.ts.map +1 -1
- package/dist/core/responses.js.map +1 -1
- package/dist/core/validation.d.ts +6 -2
- package/dist/core/validation.d.ts.map +1 -1
- package/dist/core/validation.js +6 -2
- package/dist/core/validation.js.map +1 -1
- package/dist/domains/billing/billing.d.ts +61 -8
- package/dist/domains/billing/billing.d.ts.map +1 -1
- package/dist/domains/billing/billing.js +58 -22
- package/dist/domains/billing/billing.js.map +1 -1
- package/dist/domains/billing/gst.d.ts.map +1 -1
- package/dist/domains/billing/gst.js +4 -3
- package/dist/domains/billing/gst.js.map +1 -1
- package/dist/domains/chat/chat.d.ts +0 -21
- package/dist/domains/chat/chat.d.ts.map +1 -1
- package/dist/domains/chat/chat.js +0 -20
- package/dist/domains/chat/chat.js.map +1 -1
- package/dist/domains/communities/communities.d.ts +30 -12
- package/dist/domains/communities/communities.d.ts.map +1 -1
- package/dist/domains/communities/communities.js +43 -15
- package/dist/domains/communities/communities.js.map +1 -1
- package/dist/domains/contributions/contributions.d.ts +1 -1
- package/dist/domains/contributions/contributions.js +1 -1
- package/dist/domains/contributions/contributions.js.map +1 -1
- package/dist/domains/dashboard/dashboard.d.ts +3 -1
- package/dist/domains/dashboard/dashboard.d.ts.map +1 -1
- package/dist/domains/dashboard/dashboard.js +23 -7
- package/dist/domains/dashboard/dashboard.js.map +1 -1
- package/dist/domains/discover/discover.d.ts +8 -8
- package/dist/domains/feed/feed.d.ts +55 -153
- package/dist/domains/feed/feed.d.ts.map +1 -1
- package/dist/domains/feed/feed.js +53 -58
- package/dist/domains/feed/feed.js.map +1 -1
- package/dist/domains/location/india.d.ts +14 -4
- package/dist/domains/location/india.d.ts.map +1 -1
- package/dist/domains/location/india.js +36 -8
- package/dist/domains/location/india.js.map +1 -1
- package/dist/domains/members/members.d.ts +2 -2
- package/dist/domains/membership/membership.d.ts +26 -0
- package/dist/domains/membership/membership.d.ts.map +1 -1
- package/dist/domains/membership/membership.js +55 -1
- package/dist/domains/membership/membership.js.map +1 -1
- package/dist/domains/moderation/moderation.d.ts +2 -2
- package/dist/domains/moderation/moderation.js +3 -3
- package/dist/domains/moderation/moderation.js.map +1 -1
- package/dist/domains/notifications/notifications.d.ts +1 -10
- package/dist/domains/notifications/notifications.d.ts.map +1 -1
- package/dist/domains/notifications/notifications.js +2 -3
- package/dist/domains/notifications/notifications.js.map +1 -1
- package/dist/domains/onboarding/onboarding-answers.d.ts +33 -2
- package/dist/domains/onboarding/onboarding-answers.d.ts.map +1 -1
- package/dist/domains/onboarding/onboarding-answers.js +38 -15
- package/dist/domains/onboarding/onboarding-answers.js.map +1 -1
- package/dist/domains/onboarding/onboarding.d.ts +229 -57
- package/dist/domains/onboarding/onboarding.d.ts.map +1 -1
- package/dist/domains/onboarding/onboarding.js +34 -41
- package/dist/domains/onboarding/onboarding.js.map +1 -1
- package/dist/domains/products/products.d.ts +43 -8
- package/dist/domains/products/products.d.ts.map +1 -1
- package/dist/domains/products/products.js +57 -11
- package/dist/domains/products/products.js.map +1 -1
- package/dist/domains/profile/founder-profile.d.ts +68 -104
- package/dist/domains/profile/founder-profile.d.ts.map +1 -1
- package/dist/domains/profile/founder-profile.js +66 -32
- package/dist/domains/profile/founder-profile.js.map +1 -1
- package/dist/domains/profile/my-profile.d.ts +3 -3
- package/dist/domains/reviews/reviews.d.ts +1 -1
- package/dist/domains/reviews/reviews.d.ts.map +1 -1
- package/dist/domains/reviews/reviews.js +2 -2
- package/dist/domains/reviews/reviews.js.map +1 -1
- package/dist/domains/support/support.d.ts +22 -3
- package/dist/domains/support/support.d.ts.map +1 -1
- package/dist/domains/support/support.js +19 -5
- package/dist/domains/support/support.js.map +1 -1
- package/dist/domains/team/team.d.ts.map +1 -1
- package/dist/domains/team/team.js +3 -6
- package/dist/domains/team/team.js.map +1 -1
- package/dist/domains/uploads/uploads.d.ts +7 -22
- package/dist/domains/uploads/uploads.d.ts.map +1 -1
- package/dist/domains/uploads/uploads.js +7 -21
- package/dist/domains/uploads/uploads.js.map +1 -1
- package/dist/env.d.ts +26 -13
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +58 -23
- package/dist/env.js.map +1 -1
- package/dist/index.d.ts +18 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,11 +29,11 @@ Consumed by:
|
|
|
29
29
|
|
|
30
30
|
## Installing it
|
|
31
31
|
|
|
32
|
-
On npm since 0.1.0. Pin the EXACT version, with `-E` so npm writes `0.
|
|
33
|
-
not `^0.
|
|
32
|
+
On npm since 0.1.0. Pin the EXACT version, with `-E` so npm writes `0.18.0` and
|
|
33
|
+
not `^0.18.0` (a caret on `0.x` pins the minor; see Publishing):
|
|
34
34
|
|
|
35
35
|
```sh
|
|
36
|
-
npm install -E founder-schema@0.
|
|
36
|
+
npm install -E founder-schema@0.18.0
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
**Working against a change that is not published yet** is the path TEAM.md
|
|
@@ -94,15 +94,111 @@ SAME schema object, so the two can never disagree about what is allowed.
|
|
|
94
94
|
nothing reads them yet.
|
|
95
95
|
- **Bodies are typed JSON; only queries coerce.** `teamSize: "5"` is refused in a
|
|
96
96
|
body, and `?limit=5` is read as 5.
|
|
97
|
-
- **Shapes are derived, never copied.** One `locationSchema
|
|
98
|
-
`
|
|
99
|
-
places pick, omit or reuse
|
|
97
|
+
- **Shapes are derived, never copied.** One `locationSchema` to READ a place and
|
|
98
|
+
one `indiaLocationSchema` to WRITE one (0.17.0), one `founderLinksSchema`, one
|
|
99
|
+
`ventureSchema`, one `mediaImageSchema`; the other places pick, omit or reuse
|
|
100
|
+
them, and a test checks by object identity.
|
|
100
101
|
|
|
101
102
|
`zod` is a **peer** dependency (`^4.4.0`), on purpose. The consumer owns the Zod
|
|
102
103
|
instance; this package must not bring its own.
|
|
103
104
|
|
|
104
105
|
---
|
|
105
106
|
|
|
107
|
+
## Breaking in 0.18.0: the SSOT audit (2026-10-20)
|
|
108
|
+
|
|
109
|
+
The user, 2026-10-20: "make sure it is the SSOT for data types, validation,
|
|
110
|
+
etc., for front end and back end, implemented thoroughly end to end, no
|
|
111
|
+
patchwork or lazy workaround, and a proper cleanup". Ten read-only reviewers,
|
|
112
|
+
every high finding verified in code. Each rule below lived in two places, or in
|
|
113
|
+
one consumer only, or as a hand-written literal; it is one export now, and both
|
|
114
|
+
consumers use it (`tests/ssot-0-18.test.ts`, one test per rule).
|
|
115
|
+
|
|
116
|
+
**Removed** (no consumer imports any; listed in `public-surface.test.ts`):
|
|
117
|
+
`chatInvitation*`, the 0.4.0 upload-key code (`uploadKey*`, `UPLOAD_KEY_*`,
|
|
118
|
+
`uploadKeyInvalid`; the picker's extensions are `UPLOAD_FILE_EXTENSIONS` now),
|
|
119
|
+
`PRODUCTS_OWNED_MAX`, `REVIEWS_REWARDED_PER_DAY`, the never-sent codes
|
|
120
|
+
`ORDER_EXPIRED` and `COUPON_FOUNDING_EXISTS`, the unused zod wrappers
|
|
121
|
+
`actionSchema`, `purchaseKindSchema`, `notificationPreferenceSchema`,
|
|
122
|
+
`onboardingProgressSchema`, the uploaded-video shape (`feedVideoSchema`: post
|
|
123
|
+
media is images only) and the link preview no server writes (`linkPreview*`,
|
|
124
|
+
`LINK_PREVIEW_*`).
|
|
125
|
+
|
|
126
|
+
**Stricter or changed on the wire:**
|
|
127
|
+
- `PATCH /onboarding` takes the draft in the FORM's shape
|
|
128
|
+
(`onboardingFormSchema.partial().strict()`): typed and bounded, half-answered
|
|
129
|
+
still fine. It took any JSON before.
|
|
130
|
+
- `PATCH /profiles/me` takes only its own fields: `headline`, `about`, `skills`,
|
|
131
|
+
`goals`, `links`. The name and the city are the personal details', the venture
|
|
132
|
+
the business editor's. `goals` keeps at least one (`requiredGoalListSchema`,
|
|
133
|
+
the user), as onboarding's finish does.
|
|
134
|
+
- `PATCH /feed/:id` may clear the text; the server refuses only a post left with
|
|
135
|
+
nothing (`postHasContent`, the create body's rule too).
|
|
136
|
+
- A vote names each option once (`pollVoteDuplicate`). An emptied `?cursor=`
|
|
137
|
+
means none; a cursor is at most `CURSOR_MAX`.
|
|
138
|
+
- A blank ban reason means no reason. The MPIN rule reads `MPIN_LENGTH`.
|
|
139
|
+
- Responses: `grantFailed` is a `GRANT_FAILURE_VALUES`; the dashboard's support
|
|
140
|
+
block adds `waitingTickets` and `openTickets` is status `open` only; product
|
|
141
|
+
links are READ leniently (`productLinksReadSchema`), so an older link cannot
|
|
142
|
+
break a page; ids and usernames are typed where they were strings; excerpts
|
|
143
|
+
are bounded; packed CASL rules are checked (action and subject).
|
|
144
|
+
- New codes for refusals that were a bare `FORBIDDEN`:
|
|
145
|
+
`COMMUNITY_NOT_ON_YOURSELF`, `HIDDEN_BY_STAFF`, `TICKET_MEMBER_ONLY`; body
|
|
146
|
+
messages `bodyTooLarge`, `bodyNotJson`.
|
|
147
|
+
- A community's last admin, alone in it, may leave: that ARCHIVES it (on the
|
|
148
|
+
audit record, as a staff archive is). A plain member last in an adminless
|
|
149
|
+
community leaves it as it was.
|
|
150
|
+
- Every request body types what a caller SENDS: no body field is a
|
|
151
|
+
`z.preprocess` (whose input type is `unknown`), so a web typing a body by
|
|
152
|
+
`z.input<typeof xBodySchema>` cannot send the wrong type and compile.
|
|
153
|
+
`optionalTrimmedString` and `indiaLocationSchema.state` were the two; a test
|
|
154
|
+
walks every exported body and fails on any preprocess. Same runtime rules.
|
|
155
|
+
- A model refusing what the schema accepted answers `500 INTERNAL_ERROR` (our
|
|
156
|
+
mismatch), never `400`; body-parse refusals carry message KEYS.
|
|
157
|
+
- `PATCH /profiles/me` is STRICT: a key that belongs to another editor is a
|
|
158
|
+
400 naming it, never dropped without a word.
|
|
159
|
+
- `grantFailed` adds `GRANT_REFUSED`, any other refusal of a grant: still
|
|
160
|
+
recorded and refunded, never a capture rolled back for a reason not listed.
|
|
161
|
+
- `changesDiscoverCard` ignores points (`FOUNDER_CARD_EDIT_FIELDS`): other
|
|
162
|
+
people's acts move them, and they never lift a member up Discover.
|
|
163
|
+
- New: `keptPaise` (what is kept of a payment: the Overview's revenue) and
|
|
164
|
+
`refundLeftPaise` (what a refund returns: never zeroed by a credit note, which
|
|
165
|
+
a failed refund carries), `CONTRIBUTION_PAIR_WINDOW_DAYS` (the pair cap's own
|
|
166
|
+
window), `RAZORPAY_EVENT_RETENTION_DAYS`, `communityTypeChangeAllowed` (an
|
|
167
|
+
archived community narrows, never opens), `communityViewerSchema.leaveArchives`
|
|
168
|
+
(the server says what leaving does), `envVar.optionalOneOf`;
|
|
169
|
+
`onboardingDraftForStep` answers the form's own type, so a step save needs no
|
|
170
|
+
cast.
|
|
171
|
+
- `founder-schema/env`: a BLANK line never takes a real fallback (a port,
|
|
172
|
+
localhost, `development`, a database name): it is refused at start-up and
|
|
173
|
+
named. Unset still takes it; a blank fallback (`''`, a feature off) still
|
|
174
|
+
takes a blank line. `optionalText({ refuseBlank: true })` is for a setting
|
|
175
|
+
whose absence is a legitimate "off" but whose blank line in a deployment
|
|
176
|
+
would switch a feature off silently (the web's media host).
|
|
177
|
+
- `indiaLocationSchema.state`: missing, blank and unknown all refuse as the
|
|
178
|
+
choice they are (`invalid_value`, as 0.17 did), so a form words "Choose your
|
|
179
|
+
state".
|
|
180
|
+
|
|
181
|
+
**One rule each, moved or named here:** the upgrade price and India's calendar
|
|
182
|
+
months (`upgradePrice`, `addPlanPeriod`, `UPGRADE_PER_*`: the Terms state it
|
|
183
|
+
from these), `rupees`, `INDIA_UTC_OFFSET_MS` / `DAY_MS`, `eachOnce`,
|
|
184
|
+
`missingForListing`, `productIsOnDiscover`, `changesDiscoverCard`,
|
|
185
|
+
`CONTACT_VISIBILITY_AUDIENCE` (the user: members only, all four),
|
|
186
|
+
`canonicalCityName`, the one YouTube rule (posts and products: `/v/`, and the
|
|
187
|
+
music and www.youtu.be hosts), `ventureBodyFrom` / `foundedYearDigits`,
|
|
188
|
+
`QueryArgs<S>` (type a list's arguments by the PARSED shape: a query field's
|
|
189
|
+
input is `unknown`), and the values and limits the backend copied by hand:
|
|
190
|
+
`TICKET_SIDE_VALUES`, `INVOICE_KIND_VALUES`, `COUPON_KIND_VALUES`,
|
|
191
|
+
`REPORT_STATUS_VALUES`, `COMMUNITY_SUGGESTION_DECISION_VALUES`,
|
|
192
|
+
`TICKET_CHANGE_VALUES`, `REFUND_ASK_CATEGORY`, `COUPON_PERCENT_*`,
|
|
193
|
+
`COUPON_LABEL_MIN`, `BILLING_NAME_MIN` / `BILLING_ADDRESS_MIN`, `GSTIN_LENGTH`,
|
|
194
|
+
`INVOICE_NUMBER_MAX`, `RAZORPAY_*_MAX`, `RATING_MIN` / `RATING_MAX`,
|
|
195
|
+
`DASHBOARD_RECENT_DAYS`, `PHONE_INPUT_MAX` / `DATE_INPUT_MAX` /
|
|
196
|
+
`GUIDELINES_VERSION_MAX`. The permission fields type their statuses.
|
|
197
|
+
|
|
198
|
+
**Deploy order: the backend, then the web.** The web reads the new response
|
|
199
|
+
fields; a 0.17 web on a 0.18 backend reads the dashboard and payments as before
|
|
200
|
+
(unknown keys are dropped).
|
|
201
|
+
|
|
106
202
|
## Breaking in 0.17.0: the onboarding photo is saved, and onboarding's single source of truth (2026-10-19)
|
|
107
203
|
|
|
108
204
|
Two pieces of the user's work on onboarding, 2026-10-19.
|
|
@@ -640,8 +736,8 @@ correction of something the backend, the web or the data already disagreed on.
|
|
|
640
736
|
| `discoverFacetsSchema` options | `{ value, label, count }` | `{ value, count }` |
|
|
641
737
|
| `updateUsernameBodySchema` | format only | format, and not reserved |
|
|
642
738
|
|
|
643
|
-
Deprecated,
|
|
644
|
-
`openChatBodySchema`) and `chatInvitationSchema` (describes nothing that
|
|
739
|
+
Deprecated in 0.2.0, kept until 0.18.0 removed them: `chatInvitationBodySchema`
|
|
740
|
+
(use `openChatBodySchema`) and `chatInvitationSchema` (describes nothing that
|
|
645
741
|
exists).
|
|
646
742
|
|
|
647
743
|
---
|
|
@@ -671,25 +767,36 @@ src/
|
|
|
671
767
|
index.ts the public surface. Everything a consumer imports.
|
|
672
768
|
env.ts env field builders, its own entry: `founder-schema/env`
|
|
673
769
|
core/ what every domain builds on (imports no domain)
|
|
674
|
-
limits.ts every length, size and count limit, by name
|
|
770
|
+
limits.ts every length, size and count limit, by name, and India's
|
|
771
|
+
offset and DAY_MS (imports nothing)
|
|
675
772
|
validation.ts message keys, the wire shape of an issue, toValidationIssues
|
|
676
773
|
errors.ts every API error code (API_ERROR_CODE_VALUES)
|
|
677
774
|
common.ts usernames (and the reserved list), the enums, URLs, phone,
|
|
678
|
-
location, coordinates, pagination
|
|
679
|
-
responses.ts success / error / paginated / { id } envelopes
|
|
775
|
+
location, coordinates, pagination, eachOnce, YouTube links
|
|
776
|
+
responses.ts success / error / paginated / { id } envelopes, QueryArgs
|
|
777
|
+
money.ts rupees(): paise as people read them
|
|
680
778
|
access/ who may do what (CASL's words, shared by server and web)
|
|
681
779
|
roles.ts the roles, who may grant which, the active-role window
|
|
682
780
|
permissions.ts actions, subjects, the fields a rule may name, AppAbility
|
|
683
781
|
access.ts GET /me/access, switching role, the MPIN
|
|
684
|
-
|
|
685
|
-
|
|
782
|
+
standing.ts viewer / member (staff count as members), the refusal reasons
|
|
783
|
+
capabilities.ts powers given to selected members (create_communities), who grants them
|
|
784
|
+
domains/<domain>/ one folder per product area: its bodies, params, answers
|
|
785
|
+
and the rules both sides apply
|
|
786
|
+
account/ audit/ billing/ chat/ communities/ contributions/ dashboard/
|
|
787
|
+
discover/ feed/ location/ media/ members/ membership/ moderation/
|
|
788
|
+
notifications/ onboarding/ products/ profile/ reviews/ support/ team/
|
|
789
|
+
uploads/ waitlist/
|
|
686
790
|
tests/
|
|
791
|
+
the domains' own tests, plus:
|
|
687
792
|
ssot.test.ts derived not copied, no body coercion, URLs, uploads, codes
|
|
793
|
+
ssot-0-18.test.ts the 0.18.0 audit: each rule moved here, on the rule
|
|
688
794
|
validation.test.ts keys not sentences, no input leaves, limits applied
|
|
689
795
|
onboarding-steps.test.ts steps, step schemas and the finish body agree
|
|
690
796
|
responses.test.ts the envelopes, pinned
|
|
691
797
|
access.test.ts roles, the grant table, the MPIN, rules across the wire
|
|
692
|
-
public-surface.test.ts export-removal tripwire
|
|
798
|
+
public-surface.test.ts export-removal tripwire, the 0.18.0 removals, and the
|
|
799
|
+
values the database is pinned to
|
|
693
800
|
```
|
|
694
801
|
|
|
695
802
|
A consumer imports from the package root and never from a deep path:
|
|
@@ -727,9 +834,10 @@ cannot see it and the request looks correct in the network tab. Build a write bo
|
|
|
727
834
|
as its own object with no defaults, and have the client send only the keys the
|
|
728
835
|
member actually changed.
|
|
729
836
|
|
|
730
|
-
**A default inside a nested object is the same trap one level down.** A
|
|
731
|
-
|
|
732
|
-
`
|
|
837
|
+
**A default inside a nested object is the same trap one level down.** A write
|
|
838
|
+
body never fills a nested field in: `locationSchema` has no default country
|
|
839
|
+
since it was removed, and a place is WRITTEN through `indiaLocationSchema`,
|
|
840
|
+
whole.
|
|
733
841
|
|
|
734
842
|
**A patch field the member can clear has three states, not two.** Absent means
|
|
735
843
|
do not touch. `null` means clear, and the backend maps it to `$unset`. An empty
|
package/dist/access/access.d.ts
CHANGED
|
@@ -49,7 +49,7 @@ export declare const meAccessResponseSchema: z.ZodObject<{
|
|
|
49
49
|
viewer: "viewer";
|
|
50
50
|
member: "member";
|
|
51
51
|
}>;
|
|
52
|
-
rules: z.ZodArray<z.
|
|
52
|
+
rules: z.ZodArray<z.ZodUnion<readonly [z.ZodTuple<[z.ZodString, z.ZodString], null>, z.ZodTuple<[z.ZodString, z.ZodString, z.ZodUnknown], null>, z.ZodTuple<[z.ZodString, z.ZodString, z.ZodUnknown, z.ZodLiteral<1>], null>, z.ZodTuple<[z.ZodString, z.ZodString, z.ZodUnknown, z.ZodUnion<readonly [z.ZodLiteral<0>, z.ZodLiteral<1>]>, z.ZodString], null>, z.ZodTuple<[z.ZodString, z.ZodString, z.ZodUnknown, z.ZodUnion<readonly [z.ZodLiteral<0>, z.ZodLiteral<1>]>, z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<0>]>, z.ZodString], null>]>>;
|
|
53
53
|
}, z.core.$strip>;
|
|
54
54
|
export type MeAccessResponse = z.infer<typeof meAccessResponseSchema>;
|
|
55
55
|
/** `PUT /me/active-role`. Back to member needs nothing; any staff role needs the MPIN. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/access/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,yCAAyC;AACzC,eAAO,MAAM,iBAAiB,IAAI,CAAC;AACnC,2CAA2C;AAC3C,eAAO,MAAM,YAAY,QAAiB,CAAC;AAE3C,eAAO,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"access.d.ts","sourceRoot":"","sources":["../../src/access/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,yCAAyC;AACzC,eAAO,MAAM,iBAAiB,IAAI,CAAC;AACnC,2CAA2C;AAC3C,eAAO,MAAM,YAAY,QAAiB,CAAC;AAE3C,eAAO,MAAM,UAAU,aAG0B,CAAC;AAElD,2DAA2D;AAC3D,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;iBAiBjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,0FAA0F;AAC1F,eAAO,MAAM,oBAAoB;;;;;;;;;iBAQ7B,CAAC;AACL,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAMlE,oDAAoD;AACpD,eAAO,MAAM,iBAAiB;;;iBAEA,CAAC;AAC/B,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D,sEAAsE;AACtE,eAAO,MAAM,oBAAoB;;;;iBAEH,CAAC;AAC/B,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
|
package/dist/access/access.js
CHANGED
|
@@ -30,7 +30,10 @@ export const MPIN_LENGTH = 6;
|
|
|
30
30
|
export const MPIN_MAX_ATTEMPTS = 5;
|
|
31
31
|
/** How long a locked MPIN stays locked. */
|
|
32
32
|
export const MPIN_LOCK_MS = 15 * 60 * 1000;
|
|
33
|
-
export const mpinSchema = z
|
|
33
|
+
export const mpinSchema = z
|
|
34
|
+
.string()
|
|
35
|
+
.length(MPIN_LENGTH, VALIDATION_MESSAGE.mpinInvalid)
|
|
36
|
+
.regex(/^\d+$/, VALIDATION_MESSAGE.mpinInvalid);
|
|
34
37
|
/** The answer to `GET /me/access`, and to every switch. */
|
|
35
38
|
export const meAccessResponseSchema = z.object({
|
|
36
39
|
/** Every role held, member always among them, highest first. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/access/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC;AAC7B,yCAAyC;AACzC,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AACnC,2CAA2C;AAC3C,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE3C,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/access/access.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAE/C,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,CAAC;AAC7B,yCAAyC;AACzC,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC;AACnC,2CAA2C;AAC3C,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAE3C,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC;KACxB,MAAM,EAAE;KACR,MAAM,CAAC,WAAW,EAAE,kBAAkB,CAAC,WAAW,CAAC;KACnD,KAAK,CAAC,OAAO,EAAE,kBAAkB,CAAC,WAAW,CAAC,CAAC;AAElD,2DAA2D;AAC3D,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,gEAAgE;IAChE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACjC,6EAA6E;IAC7E,UAAU,EAAE,UAAU;IACtB,uFAAuF;IACvF,mBAAmB,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAChD,4FAA4F;IAC5F,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;IACpB;;;;OAIG;IACH,QAAQ,EAAE,cAAc;IACxB,wFAAwF;IACxF,KAAK,EAAE,iBAAiB;CACzB,CAAC,CAAC;AAGH,0FAA0F;AAC1F,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC;KAClC,MAAM,CAAC;IACN,IAAI,EAAE,UAAU;IAChB,IAAI,EAAE,UAAU,CAAC,QAAQ,EAAE;CAC5B,CAAC;KACD,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,WAAW,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE;IACtE,IAAI,EAAE,CAAC,MAAM,CAAC;IACd,KAAK,EAAE,kBAAkB,CAAC,YAAY;CACvC,CAAC,CAAC;AAGL,MAAM,SAAS,GAAG,CAAkD,IAAO,EAAE,EAAE,CAC7E,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,WAAW,CAAC;AACjC,MAAM,QAAQ,GAAG,EAAE,IAAI,EAAE,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,kBAAkB,CAAC,YAAY,EAAE,CAAC;AAEnF,oDAAoD;AACpD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC;KAC/B,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;KACrD,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAG/B,sEAAsE;AACtE,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC;KAClC,MAAM,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;KAC9E,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC","sourcesContent":["/**\n * founder-schema: the access API\n *\n * GET /api/v1/me/access who I am acting as, what I hold, my rules\n * PUT /api/v1/me/active-role switch role (a staff role needs the MPIN)\n * POST /api/v1/me/mpin set the MPIN, once\n * PUT /api/v1/me/mpin change it, with the current one\n *\n * THE MPIN is a 6-digit code that stands between a signed-in member and their\n * staff roles. Signing in proves who you are; the MPIN proves it is still you\n * at the moment you pick up admin powers, on a phone that may have been left\n * unlocked. Set once by its owner; a forgotten one is cleared by a superadmin\n * (an audited action), after which the owner sets a new one. `MPIN_MAX_ATTEMPTS`\n * wrong tries lock it for `MPIN_LOCK_MS`.\n *\n * THE ACTIVE ROLE is per account (the user's ruling, 2026-10-05): switching on\n * one device switches every device. It lasts `ACTIVE_ROLE_TTL_MS`, and the\n * server re-checks it on EVERY request: a role that was revoked, or that\n * expired, is acting as a member again at once, not at the next sign-in.\n *\n * @module founder-schema/access/access\n */\n\nimport { z } from 'zod';\nimport { VALIDATION_MESSAGE } from '../core/validation.js';\nimport { packedRulesSchema } from './permissions.js';\nimport { MEMBER_ROLE, roleSchema } from './roles.js';\nimport { standingSchema } from './standing.js';\n\nexport const MPIN_LENGTH = 6;\n/** Wrong tries before the MPIN locks. */\nexport const MPIN_MAX_ATTEMPTS = 5;\n/** How long a locked MPIN stays locked. */\nexport const MPIN_LOCK_MS = 15 * 60 * 1000;\n\nexport const mpinSchema = z\n .string()\n .length(MPIN_LENGTH, VALIDATION_MESSAGE.mpinInvalid)\n .regex(/^\\d+$/, VALIDATION_MESSAGE.mpinInvalid);\n\n/** The answer to `GET /me/access`, and to every switch. */\nexport const meAccessResponseSchema = z.object({\n /** Every role held, member always among them, highest first. */\n roles: z.array(roleSchema).min(1),\n /** The role being acted as now. Member when no staff role is switched on. */\n activeRole: roleSchema,\n /** When the active staff role lapses back to member. Null while acting as a member. */\n activeRoleExpiresAt: z.iso.datetime().nullable(),\n /** Whether an MPIN is set, so the switcher knows to ask for one or to offer setting one. */\n mpinSet: z.boolean(),\n /**\n * Whether they take part or only look (0.12.0, `access/standing`). The\n * rules already say what follows from it; this says it plainly, for the\n * places that word it (\"Become a member to take part\").\n */\n standing: standingSchema,\n /** The active role's permissions, packed (`packRules`), refusals with their reasons. */\n rules: packedRulesSchema,\n});\nexport type MeAccessResponse = z.infer<typeof meAccessResponseSchema>;\n\n/** `PUT /me/active-role`. Back to member needs nothing; any staff role needs the MPIN. */\nexport const switchRoleBodySchema = z\n .object({\n role: roleSchema,\n mpin: mpinSchema.optional(),\n })\n .refine((body) => body.role === MEMBER_ROLE || body.mpin !== undefined, {\n path: ['mpin'],\n error: VALIDATION_MESSAGE.mpinRequired,\n });\nexport type SwitchRoleBody = z.infer<typeof switchRoleBodySchema>;\n\nconst confirmed = <T extends { mpin: string; confirmMpin: string }>(body: T) =>\n body.mpin === body.confirmMpin;\nconst mismatch = { path: ['confirmMpin'], error: VALIDATION_MESSAGE.mpinMismatch };\n\n/** `POST /me/mpin`: the first MPIN, typed twice. */\nexport const setMpinBodySchema = z\n .object({ mpin: mpinSchema, confirmMpin: mpinSchema })\n .refine(confirmed, mismatch);\nexport type SetMpinBody = z.infer<typeof setMpinBodySchema>;\n\n/** `PUT /me/mpin`: the current MPIN, then the new one typed twice. */\nexport const changeMpinBodySchema = z\n .object({ currentMpin: mpinSchema, mpin: mpinSchema, confirmMpin: mpinSchema })\n .refine(confirmed, mismatch);\nexport type ChangeMpinBody = z.infer<typeof changeMpinBodySchema>;\n"]}
|
|
@@ -15,9 +15,11 @@
|
|
|
15
15
|
*
|
|
16
16
|
* @module founder-schema/access/permissions
|
|
17
17
|
*/
|
|
18
|
-
import type { ForcedSubject, MongoAbility } from '@casl/ability';
|
|
18
|
+
import type { ForcedSubject, MongoAbility, RawRuleOf } from '@casl/ability';
|
|
19
19
|
import { z } from 'zod';
|
|
20
|
-
import type { PurchaseKind } from '../domains/billing/billing.js';
|
|
20
|
+
import type { OrderStatus, PurchaseKind } from '../domains/billing/billing.js';
|
|
21
|
+
import type { CommunitySuggestionStatus } from '../domains/communities/communities.js';
|
|
22
|
+
import type { ReportStatus } from '../domains/feed/feed.js';
|
|
21
23
|
import type { PlatformRole } from './roles.js';
|
|
22
24
|
import type { Standing } from './standing.js';
|
|
23
25
|
/**
|
|
@@ -48,26 +50,7 @@ import type { Standing } from './standing.js';
|
|
|
48
50
|
* Append only: a stored or packed rule names these.
|
|
49
51
|
*/
|
|
50
52
|
export declare const ACTION_VALUES: readonly ["manage", "create", "read", "update", "delete", "moderate", "assign", "suspend", "resolve", "reset", "represent", "react", "vote", "save", "join", "message", "refund"];
|
|
51
|
-
export
|
|
52
|
-
join: "join";
|
|
53
|
-
message: "message";
|
|
54
|
-
manage: "manage";
|
|
55
|
-
create: "create";
|
|
56
|
-
read: "read";
|
|
57
|
-
update: "update";
|
|
58
|
-
delete: "delete";
|
|
59
|
-
moderate: "moderate";
|
|
60
|
-
assign: "assign";
|
|
61
|
-
suspend: "suspend";
|
|
62
|
-
resolve: "resolve";
|
|
63
|
-
reset: "reset";
|
|
64
|
-
represent: "represent";
|
|
65
|
-
react: "react";
|
|
66
|
-
vote: "vote";
|
|
67
|
-
save: "save";
|
|
68
|
-
refund: "refund";
|
|
69
|
-
}>;
|
|
70
|
-
export type Action = z.infer<typeof actionSchema>;
|
|
53
|
+
export type Action = (typeof ACTION_VALUES)[number];
|
|
71
54
|
/**
|
|
72
55
|
* The things.
|
|
73
56
|
*
|
|
@@ -160,7 +143,7 @@ export interface SubjectFields {
|
|
|
160
143
|
};
|
|
161
144
|
Report: {
|
|
162
145
|
reporterId: string;
|
|
163
|
-
status:
|
|
146
|
+
status: ReportStatus;
|
|
164
147
|
};
|
|
165
148
|
SupportTicket: {
|
|
166
149
|
memberId: string;
|
|
@@ -205,7 +188,7 @@ export interface SubjectFields {
|
|
|
205
188
|
/** `suggestedBy` so "your own" is a rule; `status` so only open ones are decided. */
|
|
206
189
|
CommunitySuggestion: {
|
|
207
190
|
suggestedBy: string;
|
|
208
|
-
status:
|
|
191
|
+
status: CommunitySuggestionStatus;
|
|
209
192
|
};
|
|
210
193
|
/** Whose membership (0.12.0), so "never your own" is a rule. */
|
|
211
194
|
Membership: {
|
|
@@ -218,7 +201,7 @@ export interface SubjectFields {
|
|
|
218
201
|
*/
|
|
219
202
|
Payment: {
|
|
220
203
|
userId: string;
|
|
221
|
-
status:
|
|
204
|
+
status: OrderStatus;
|
|
222
205
|
kind: PurchaseKind;
|
|
223
206
|
};
|
|
224
207
|
/** Coupons are the platform's (0.12.0): no condition is ever written on one. */
|
|
@@ -241,11 +224,26 @@ type Same<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false;
|
|
|
241
224
|
type Assert<T extends true> = T;
|
|
242
225
|
export type EverySubjectHasFields = Assert<Same<keyof SubjectFields, Exclude<Subject, 'all'>>>;
|
|
243
226
|
/**
|
|
244
|
-
*
|
|
245
|
-
*
|
|
246
|
-
*
|
|
227
|
+
* One packed rule, in exactly the shapes CASL's `PackRule` takes (a union of
|
|
228
|
+
* tuples, not a rest: `unpackRules` needs one of these), with `conditions` as
|
|
229
|
+
* `unknown`, which `isAppRawRule` and the ability itself then judge.
|
|
247
230
|
*/
|
|
248
|
-
export declare const
|
|
231
|
+
export declare const packedRuleSchema: z.ZodUnion<readonly [z.ZodTuple<[z.ZodString, z.ZodString], null>, z.ZodTuple<[z.ZodString, z.ZodString, z.ZodUnknown], null>, z.ZodTuple<[z.ZodString, z.ZodString, z.ZodUnknown, z.ZodLiteral<1>], null>, z.ZodTuple<[z.ZodString, z.ZodString, z.ZodUnknown, z.ZodUnion<readonly [z.ZodLiteral<0>, z.ZodLiteral<1>]>, z.ZodString], null>, z.ZodTuple<[z.ZodString, z.ZodString, z.ZodUnknown, z.ZodUnion<readonly [z.ZodLiteral<0>, z.ZodLiteral<1>]>, z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<0>]>, z.ZodString], null>]>;
|
|
232
|
+
export declare const packedRulesSchema: z.ZodArray<z.ZodUnion<readonly [z.ZodTuple<[z.ZodString, z.ZodString], null>, z.ZodTuple<[z.ZodString, z.ZodString, z.ZodUnknown], null>, z.ZodTuple<[z.ZodString, z.ZodString, z.ZodUnknown, z.ZodLiteral<1>], null>, z.ZodTuple<[z.ZodString, z.ZodString, z.ZodUnknown, z.ZodUnion<readonly [z.ZodLiteral<0>, z.ZodLiteral<1>]>, z.ZodString], null>, z.ZodTuple<[z.ZodString, z.ZodString, z.ZodUnknown, z.ZodUnion<readonly [z.ZodLiteral<0>, z.ZodLiteral<1>]>, z.ZodUnion<readonly [z.ZodString, z.ZodLiteral<0>]>, z.ZodString], null>]>>;
|
|
249
233
|
export type PackedRules = z.infer<typeof packedRulesSchema>;
|
|
234
|
+
/** One rule as CASL's `unpackRules` gives it back, typed for this app. */
|
|
235
|
+
export type AppRawRule = RawRuleOf<AppAbility>;
|
|
236
|
+
/**
|
|
237
|
+
* Whether an UNPACKED rule names only this app's actions and subjects
|
|
238
|
+
* (0.18.0). Packed rules cross the wire as strings (`packRules` joins several
|
|
239
|
+
* actions or subjects with commas, so the wire shape cannot be an enum); this
|
|
240
|
+
* is the check that turns them back into `AppRawRule`s, with no cast:
|
|
241
|
+
* `rules.every(isAppRawRule)` narrows the list, and a list that fails is a
|
|
242
|
+
* contract mismatch, never an ability built from it.
|
|
243
|
+
*/
|
|
244
|
+
export declare function isAppRawRule(rule: {
|
|
245
|
+
action: string | string[];
|
|
246
|
+
subject?: string | string[];
|
|
247
|
+
}): rule is AppRawRule;
|
|
250
248
|
export {};
|
|
251
249
|
//# sourceMappingURL=permissions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../src/access/permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"permissions.d.ts","sourceRoot":"","sources":["../../src/access/permissions.ts"],"names":[],"mappings":"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"}
|
|
@@ -62,7 +62,6 @@ export const ACTION_VALUES = [
|
|
|
62
62
|
'message',
|
|
63
63
|
'refund',
|
|
64
64
|
];
|
|
65
|
-
export const actionSchema = z.enum(ACTION_VALUES);
|
|
66
65
|
/**
|
|
67
66
|
* The things.
|
|
68
67
|
*
|
|
@@ -111,7 +110,44 @@ export const subjectSchema = z.enum(SUBJECT_VALUES);
|
|
|
111
110
|
/**
|
|
112
111
|
* Rules as they cross the wire: CASL's `packRules` output, an array of
|
|
113
112
|
* compact arrays. The web unpacks them with `unpackRules` before building its
|
|
114
|
-
* ability
|
|
113
|
+
* ability. Each starts with its action(s) and subject(s), comma-joined
|
|
114
|
+
* strings (checked, 0.18.0: an unchecked rule list could silently turn staff
|
|
115
|
+
* routing off); what follows (conditions, inverted, fields, reason) is CASL's
|
|
116
|
+
* own compact form.
|
|
115
117
|
*/
|
|
116
|
-
|
|
118
|
+
const zeroOrOne = z.union([z.literal(0), z.literal(1)]);
|
|
119
|
+
/**
|
|
120
|
+
* One packed rule, in exactly the shapes CASL's `PackRule` takes (a union of
|
|
121
|
+
* tuples, not a rest: `unpackRules` needs one of these), with `conditions` as
|
|
122
|
+
* `unknown`, which `isAppRawRule` and the ability itself then judge.
|
|
123
|
+
*/
|
|
124
|
+
export const packedRuleSchema = z.union([
|
|
125
|
+
z.tuple([z.string().min(1), z.string().min(1)]),
|
|
126
|
+
z.tuple([z.string().min(1), z.string().min(1), z.unknown()]),
|
|
127
|
+
z.tuple([z.string().min(1), z.string().min(1), z.unknown(), z.literal(1)]),
|
|
128
|
+
z.tuple([z.string().min(1), z.string().min(1), z.unknown(), zeroOrOne, z.string()]),
|
|
129
|
+
z.tuple([
|
|
130
|
+
z.string().min(1),
|
|
131
|
+
z.string().min(1),
|
|
132
|
+
z.unknown(),
|
|
133
|
+
zeroOrOne,
|
|
134
|
+
z.union([z.string(), z.literal(0)]),
|
|
135
|
+
z.string(),
|
|
136
|
+
]),
|
|
137
|
+
]);
|
|
138
|
+
export const packedRulesSchema = z.array(packedRuleSchema);
|
|
139
|
+
const ACTION_SET = new Set(ACTION_VALUES);
|
|
140
|
+
const SUBJECT_SET = new Set(SUBJECT_VALUES);
|
|
141
|
+
const everyIn = (value, known) => value !== undefined && (Array.isArray(value) ? value : [value]).every((v) => known.has(v));
|
|
142
|
+
/**
|
|
143
|
+
* Whether an UNPACKED rule names only this app's actions and subjects
|
|
144
|
+
* (0.18.0). Packed rules cross the wire as strings (`packRules` joins several
|
|
145
|
+
* actions or subjects with commas, so the wire shape cannot be an enum); this
|
|
146
|
+
* is the check that turns them back into `AppRawRule`s, with no cast:
|
|
147
|
+
* `rules.every(isAppRawRule)` narrows the list, and a list that fails is a
|
|
148
|
+
* contract mismatch, never an ability built from it.
|
|
149
|
+
*/
|
|
150
|
+
export function isAppRawRule(rule) {
|
|
151
|
+
return everyIn(rule.action, ACTION_SET) && everyIn(rule.subject, SUBJECT_SET);
|
|
152
|
+
}
|
|
117
153
|
//# sourceMappingURL=permissions.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/access/permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,QAAQ;IACR,SAAS;IACT,SAAS;IACT,OAAO;IACP,WAAW;IACX,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,SAAS;IACT,QAAQ;CACA,CAAC;AACX,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AAGlD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,KAAK;IACL,WAAW;IACX,QAAQ;IACR,gBAAgB;IAChB,MAAM;IACN,MAAM;IACN,SAAS;IACT,QAAQ;IACR,eAAe;IACf,YAAY;IACZ,cAAc;IACd,YAAY;IACZ,kBAAkB;IAClB,SAAS;IACT,QAAQ;IACR,WAAW;IACX,qBAAqB;IACrB,YAAY;IACZ,SAAS;IACT,QAAQ;CACA,CAAC;AACX,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;AA6FpD;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC","sourcesContent":["/**\n * founder-schema: what a role may do, in CASL's terms\n *\n * Permissions are ACTION on SUBJECT, optionally narrowed by conditions on the\n * subject's fields (\"update a Post whose authorId is mine\"). The backend writes\n * one small policy per role from these words; the web receives the resulting\n * rules and asks the same questions to decide what to draw. Because both sides\n * spell actions and subjects from this file, a permission check with a typo is\n * a compile error, not a button that silently never shows.\n *\n * The SERVER decides; the web only mirrors. Every rule the web holds came from\n * the server for the role being acted as, and every request is checked again\n * on the server, so a tampered client can show itself a button and nothing\n * more.\n *\n * @module founder-schema/access/permissions\n */\n\nimport type { ForcedSubject, MongoAbility } from '@casl/ability';\nimport { z } from 'zod';\nimport type { PurchaseKind } from '../domains/billing/billing.js';\nimport type { PlatformRole } from './roles.js';\nimport type { Standing } from './standing.js';\n\n/**\n * The verbs.\n *\n * manage every action (CASL's wildcard). Only superadmin holds it on `all`.\n * create, read, update, delete\n * moderate hide, restore or remove someone else's content, act on a report\n * assign grant or revoke (a platform role); take or hand over (a ticket)\n * suspend stop a member signing in to the product, and lift it\n * resolve close a support ticket or a report\n * reset clear a credential its owner forgot (a staff member's MPIN)\n * represent act for a product as one of its team: tag a post with it, reply\n * to its reviews, mark feedback helpful, confirm a tester\n *\n * Taking part (0.12.0, membership): the verbs a viewer is refused and a member\n * holds. Creating content stays `create`; these are the rest.\n *\n * react a reaction on a post, poll or comment; an upvote on a product;\n * marking a review helpful\n * vote vote in a poll\n * save bookmark a post or poll\n * join join a community, ask to, or accept its invitation; accept an\n * invitation to a product's team\n * message start a conversation with a Member\n * refund give a payment's money back (staff)\n *\n * Append only: a stored or packed rule names these.\n */\nexport const ACTION_VALUES = [\n 'manage',\n 'create',\n 'read',\n 'update',\n 'delete',\n 'moderate',\n 'assign',\n 'suspend',\n 'resolve',\n 'reset',\n 'represent',\n 'react',\n 'vote',\n 'save',\n 'join',\n 'message',\n 'refund',\n] as const;\nexport const actionSchema = z.enum(ACTION_VALUES);\nexport type Action = z.infer<typeof actionSchema>;\n\n/**\n * The things.\n *\n * all CASL's wildcard subject\n * Dashboard the staff overview: counts and trends\n * Member a person's account in this product\n * FounderProfile a member's public profile\n * Post, Poll, Comment\n * Report a member's report of a post, poll or comment\n * SupportTicket a member's request for help\n * SavedReply a support agent's reusable answer\n * PlatformRole a staff role, as something granted and revoked\n * AuditEntry the record of a staff action\n * PlatformSettings product-wide configuration\n * Product something a founder builds, with its team\n * Review a member's stars and words on a product\n * Community an official community (0.9.0)\n * CommunitySuggestion a member's suggestion for a new community (0.9.0)\n * Membership a person's platform membership: granted, revoked (0.12.0)\n * Payment a purchase and its invoice, and its refund (0.12.0)\n * Coupon a code that gives a membership or a discount (0.12.0)\n */\nexport const SUBJECT_VALUES = [\n 'all',\n 'Dashboard',\n 'Member',\n 'FounderProfile',\n 'Post',\n 'Poll',\n 'Comment',\n 'Report',\n 'SupportTicket',\n 'SavedReply',\n 'PlatformRole',\n 'AuditEntry',\n 'PlatformSettings',\n 'Product',\n 'Review',\n 'Community',\n 'CommunitySuggestion',\n 'Membership',\n 'Payment',\n 'Coupon',\n] as const;\nexport const subjectSchema = z.enum(SUBJECT_VALUES);\nexport type Subject = z.infer<typeof subjectSchema>;\n\n/**\n * The fields a rule may set CONDITIONS on, per subject: the server's stored\n * names, ids as strings. Declared here so a condition on a field the subject\n * does not have (`{ ownerId: me }` on a Post) is a compile error, on both sides.\n *\n * A web screen does not evaluate these against an API response: a response\n * names things differently (`author.userId`, not `authorId`). Row-level answers\n * come from the server on the item itself (`viewer.canEdit`); the web's own\n * checks are type-level (\"may I moderate Reports at all\").\n */\nexport interface SubjectFields {\n Dashboard: Record<never, never>;\n /**\n * `appRoles` is the staff roles held, so a rule can protect a role (\"never\n * suspend a superadmin\").\n *\n * For `message` (0.12.0), what the asker's relation to them is:\n * `contact` says the asker knows them (a teammate, a private community in\n * common, a conversation already); `standing` is theirs, so nobody starts a\n * conversation with a viewer, who cannot chat; `acceptsStrangers` is their\n * own setting (Premium and Business members may start one, or only people\n * they know). Optional because only the messaging check computes them: a\n * rule on a field that is absent never matches, so a check that forgets them\n * is refused, never let through.\n */\n Member: {\n _id: string;\n appRoles: PlatformRole[];\n contact?: boolean;\n standing?: Standing;\n acceptsStrangers?: boolean;\n };\n FounderProfile: { userId: string };\n Post: { authorId: string };\n Poll: { authorId: string };\n Comment: { authorId: string };\n Report: { reporterId: string; status: string };\n SupportTicket: { memberId: string; assigneeId: string | null };\n SavedReply: { authorId: string };\n /** One role on one person: `memberId` is whose, so \"never your own\" is a rule too. */\n PlatformRole: { role: PlatformRole; memberId: string };\n /** `actorId` is null for an entry a server script wrote. */\n AuditEntry: { actorId: string | null };\n PlatformSettings: Record<never, never>;\n /**\n * The team, as three lists of member ids, so a rule reads \"a product whose\n * managers include me\" (`{ managerIds: me }`). `managerIds` is the owner and\n * the co-founders; `memberIds` is everyone on the team, owner included.\n */\n Product: { ownerId: string; managerIds: string[]; memberIds: string[] };\n Review: { authorId: string };\n /**\n * Its admins and moderators as id lists (0.9.0), so a rule reads \"a\n * community whose admins include me\" (`{ adminIds: me }`). Both short by\n * nature; ordinary members are never listed here (a community has no cap).\n */\n Community: { _id: string; adminIds: string[]; moderatorIds: string[] };\n /** `suggestedBy` so \"your own\" is a rule; `status` so only open ones are decided. */\n CommunitySuggestion: { suggestedBy: string; status: string };\n /** Whose membership (0.12.0), so \"never your own\" is a rule. */\n Membership: { userId: string };\n /**\n * Who pays (0.12.0), what for, and where it stands: a viewer may buy only a\n * membership (a plan needs one), only a paid one is refunded, and never by\n * its own payer.\n */\n Payment: { userId: string; status: string; kind: PurchaseKind };\n /** Coupons are the platform's (0.12.0): no condition is ever written on one. */\n Coupon: Record<never, never>;\n}\n\n/** A document tagged with its subject type, as CASL's `subject('Post', doc)` makes one. */\nexport type SubjectInstance = {\n [K in keyof SubjectFields]: SubjectFields[K] & ForcedSubject<K>;\n}[keyof SubjectFields];\n\n/**\n * The ability both sides build: the backend from its policy, the web from the\n * rules the backend sends. Mongo-style conditions, typed by `SubjectFields`.\n */\nexport type AppAbility = MongoAbility<[Action, Subject | SubjectInstance]>;\n\n/**\n * Compile-time proof that `SubjectFields` names exactly the subjects, no more\n * and no fewer: adding a subject without its fields fails the build here.\n */\ntype Same<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false;\ntype Assert<T extends true> = T;\nexport type EverySubjectHasFields = Assert<Same<keyof SubjectFields, Exclude<Subject, 'all'>>>;\n\n/**\n * Rules as they cross the wire: CASL's `packRules` output, an array of\n * compact arrays. The web unpacks them with `unpackRules` before building its\n * ability; their inner shape is CASL's, so it is not re-described here.\n */\nexport const packedRulesSchema = z.array(z.array(z.unknown()));\nexport type PackedRules = z.infer<typeof packedRulesSchema>;\n"]}
|
|
1
|
+
{"version":3,"file":"permissions.js","sourceRoot":"","sources":["../../src/access/permissions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,QAAQ;IACR,QAAQ;IACR,UAAU;IACV,QAAQ;IACR,SAAS;IACT,SAAS;IACT,OAAO;IACP,WAAW;IACX,OAAO;IACP,MAAM;IACN,MAAM;IACN,MAAM;IACN,SAAS;IACT,QAAQ;CACA,CAAC;AAGX;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,KAAK;IACL,WAAW;IACX,QAAQ;IACR,gBAAgB;IAChB,MAAM;IACN,MAAM;IACN,SAAS;IACT,QAAQ;IACR,eAAe;IACf,YAAY;IACZ,cAAc;IACd,YAAY;IACZ,kBAAkB;IAClB,SAAS;IACT,QAAQ;IACR,WAAW;IACX,qBAAqB;IACrB,YAAY;IACZ,SAAS;IACT,QAAQ;CACA,CAAC;AACX,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;AA6FpD;;;;;;;GAOG;AACH,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC;IACtC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;IAC5D,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IACnF,CAAC,CAAC,KAAK,CAAC;QACN,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACjB,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACjB,CAAC,CAAC,OAAO,EAAE;QACX,SAAS;QACT,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACnC,CAAC,CAAC,MAAM,EAAE;KACX,CAAC;CACH,CAAC,CAAC;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;AAM3D,MAAM,UAAU,GAAwB,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC;AAC/D,MAAM,WAAW,GAAwB,IAAI,GAAG,CAAC,cAAc,CAAC,CAAC;AACjE,MAAM,OAAO,GAAG,CAAC,KAAoC,EAAE,KAA0B,EAAE,EAAE,CACnF,KAAK,KAAK,SAAS,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;AAE7F;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,IAG5B;IACC,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;AAChF,CAAC","sourcesContent":["/**\n * founder-schema: what a role may do, in CASL's terms\n *\n * Permissions are ACTION on SUBJECT, optionally narrowed by conditions on the\n * subject's fields (\"update a Post whose authorId is mine\"). The backend writes\n * one small policy per role from these words; the web receives the resulting\n * rules and asks the same questions to decide what to draw. Because both sides\n * spell actions and subjects from this file, a permission check with a typo is\n * a compile error, not a button that silently never shows.\n *\n * The SERVER decides; the web only mirrors. Every rule the web holds came from\n * the server for the role being acted as, and every request is checked again\n * on the server, so a tampered client can show itself a button and nothing\n * more.\n *\n * @module founder-schema/access/permissions\n */\n\nimport type { ForcedSubject, MongoAbility, RawRuleOf } from '@casl/ability';\nimport { z } from 'zod';\nimport type { OrderStatus, PurchaseKind } from '../domains/billing/billing.js';\nimport type { CommunitySuggestionStatus } from '../domains/communities/communities.js';\nimport type { ReportStatus } from '../domains/feed/feed.js';\nimport type { PlatformRole } from './roles.js';\nimport type { Standing } from './standing.js';\n\n/**\n * The verbs.\n *\n * manage every action (CASL's wildcard). Only superadmin holds it on `all`.\n * create, read, update, delete\n * moderate hide, restore or remove someone else's content, act on a report\n * assign grant or revoke (a platform role); take or hand over (a ticket)\n * suspend stop a member signing in to the product, and lift it\n * resolve close a support ticket or a report\n * reset clear a credential its owner forgot (a staff member's MPIN)\n * represent act for a product as one of its team: tag a post with it, reply\n * to its reviews, mark feedback helpful, confirm a tester\n *\n * Taking part (0.12.0, membership): the verbs a viewer is refused and a member\n * holds. Creating content stays `create`; these are the rest.\n *\n * react a reaction on a post, poll or comment; an upvote on a product;\n * marking a review helpful\n * vote vote in a poll\n * save bookmark a post or poll\n * join join a community, ask to, or accept its invitation; accept an\n * invitation to a product's team\n * message start a conversation with a Member\n * refund give a payment's money back (staff)\n *\n * Append only: a stored or packed rule names these.\n */\nexport const ACTION_VALUES = [\n 'manage',\n 'create',\n 'read',\n 'update',\n 'delete',\n 'moderate',\n 'assign',\n 'suspend',\n 'resolve',\n 'reset',\n 'represent',\n 'react',\n 'vote',\n 'save',\n 'join',\n 'message',\n 'refund',\n] as const;\nexport type Action = (typeof ACTION_VALUES)[number];\n\n/**\n * The things.\n *\n * all CASL's wildcard subject\n * Dashboard the staff overview: counts and trends\n * Member a person's account in this product\n * FounderProfile a member's public profile\n * Post, Poll, Comment\n * Report a member's report of a post, poll or comment\n * SupportTicket a member's request for help\n * SavedReply a support agent's reusable answer\n * PlatformRole a staff role, as something granted and revoked\n * AuditEntry the record of a staff action\n * PlatformSettings product-wide configuration\n * Product something a founder builds, with its team\n * Review a member's stars and words on a product\n * Community an official community (0.9.0)\n * CommunitySuggestion a member's suggestion for a new community (0.9.0)\n * Membership a person's platform membership: granted, revoked (0.12.0)\n * Payment a purchase and its invoice, and its refund (0.12.0)\n * Coupon a code that gives a membership or a discount (0.12.0)\n */\nexport const SUBJECT_VALUES = [\n 'all',\n 'Dashboard',\n 'Member',\n 'FounderProfile',\n 'Post',\n 'Poll',\n 'Comment',\n 'Report',\n 'SupportTicket',\n 'SavedReply',\n 'PlatformRole',\n 'AuditEntry',\n 'PlatformSettings',\n 'Product',\n 'Review',\n 'Community',\n 'CommunitySuggestion',\n 'Membership',\n 'Payment',\n 'Coupon',\n] as const;\nexport const subjectSchema = z.enum(SUBJECT_VALUES);\nexport type Subject = z.infer<typeof subjectSchema>;\n\n/**\n * The fields a rule may set CONDITIONS on, per subject: the server's stored\n * names, ids as strings. Declared here so a condition on a field the subject\n * does not have (`{ ownerId: me }` on a Post) is a compile error, on both sides.\n *\n * A web screen does not evaluate these against an API response: a response\n * names things differently (`author.userId`, not `authorId`). Row-level answers\n * come from the server on the item itself (`viewer.canEdit`); the web's own\n * checks are type-level (\"may I moderate Reports at all\").\n */\nexport interface SubjectFields {\n Dashboard: Record<never, never>;\n /**\n * `appRoles` is the staff roles held, so a rule can protect a role (\"never\n * suspend a superadmin\").\n *\n * For `message` (0.12.0), what the asker's relation to them is:\n * `contact` says the asker knows them (a teammate, a private community in\n * common, a conversation already); `standing` is theirs, so nobody starts a\n * conversation with a viewer, who cannot chat; `acceptsStrangers` is their\n * own setting (Premium and Business members may start one, or only people\n * they know). Optional because only the messaging check computes them: a\n * rule on a field that is absent never matches, so a check that forgets them\n * is refused, never let through.\n */\n Member: {\n _id: string;\n appRoles: PlatformRole[];\n contact?: boolean;\n standing?: Standing;\n acceptsStrangers?: boolean;\n };\n FounderProfile: { userId: string };\n Post: { authorId: string };\n Poll: { authorId: string };\n Comment: { authorId: string };\n Report: { reporterId: string; status: ReportStatus };\n SupportTicket: { memberId: string; assigneeId: string | null };\n SavedReply: { authorId: string };\n /** One role on one person: `memberId` is whose, so \"never your own\" is a rule too. */\n PlatformRole: { role: PlatformRole; memberId: string };\n /** `actorId` is null for an entry a server script wrote. */\n AuditEntry: { actorId: string | null };\n PlatformSettings: Record<never, never>;\n /**\n * The team, as three lists of member ids, so a rule reads \"a product whose\n * managers include me\" (`{ managerIds: me }`). `managerIds` is the owner and\n * the co-founders; `memberIds` is everyone on the team, owner included.\n */\n Product: { ownerId: string; managerIds: string[]; memberIds: string[] };\n Review: { authorId: string };\n /**\n * Its admins and moderators as id lists (0.9.0), so a rule reads \"a\n * community whose admins include me\" (`{ adminIds: me }`). Both short by\n * nature; ordinary members are never listed here (a community has no cap).\n */\n Community: { _id: string; adminIds: string[]; moderatorIds: string[] };\n /** `suggestedBy` so \"your own\" is a rule; `status` so only open ones are decided. */\n CommunitySuggestion: { suggestedBy: string; status: CommunitySuggestionStatus };\n /** Whose membership (0.12.0), so \"never your own\" is a rule. */\n Membership: { userId: string };\n /**\n * Who pays (0.12.0), what for, and where it stands: a viewer may buy only a\n * membership (a plan needs one), only a paid one is refunded, and never by\n * its own payer.\n */\n Payment: { userId: string; status: OrderStatus; kind: PurchaseKind };\n /** Coupons are the platform's (0.12.0): no condition is ever written on one. */\n Coupon: Record<never, never>;\n}\n\n/** A document tagged with its subject type, as CASL's `subject('Post', doc)` makes one. */\nexport type SubjectInstance = {\n [K in keyof SubjectFields]: SubjectFields[K] & ForcedSubject<K>;\n}[keyof SubjectFields];\n\n/**\n * The ability both sides build: the backend from its policy, the web from the\n * rules the backend sends. Mongo-style conditions, typed by `SubjectFields`.\n */\nexport type AppAbility = MongoAbility<[Action, Subject | SubjectInstance]>;\n\n/**\n * Compile-time proof that `SubjectFields` names exactly the subjects, no more\n * and no fewer: adding a subject without its fields fails the build here.\n */\ntype Same<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false;\ntype Assert<T extends true> = T;\nexport type EverySubjectHasFields = Assert<Same<keyof SubjectFields, Exclude<Subject, 'all'>>>;\n\n/**\n * Rules as they cross the wire: CASL's `packRules` output, an array of\n * compact arrays. The web unpacks them with `unpackRules` before building its\n * ability. Each starts with its action(s) and subject(s), comma-joined\n * strings (checked, 0.18.0: an unchecked rule list could silently turn staff\n * routing off); what follows (conditions, inverted, fields, reason) is CASL's\n * own compact form.\n */\nconst zeroOrOne = z.union([z.literal(0), z.literal(1)]);\n/**\n * One packed rule, in exactly the shapes CASL's `PackRule` takes (a union of\n * tuples, not a rest: `unpackRules` needs one of these), with `conditions` as\n * `unknown`, which `isAppRawRule` and the ability itself then judge.\n */\nexport const packedRuleSchema = z.union([\n z.tuple([z.string().min(1), z.string().min(1)]),\n z.tuple([z.string().min(1), z.string().min(1), z.unknown()]),\n z.tuple([z.string().min(1), z.string().min(1), z.unknown(), z.literal(1)]),\n z.tuple([z.string().min(1), z.string().min(1), z.unknown(), zeroOrOne, z.string()]),\n z.tuple([\n z.string().min(1),\n z.string().min(1),\n z.unknown(),\n zeroOrOne,\n z.union([z.string(), z.literal(0)]),\n z.string(),\n ]),\n]);\nexport const packedRulesSchema = z.array(packedRuleSchema);\nexport type PackedRules = z.infer<typeof packedRulesSchema>;\n\n/** One rule as CASL's `unpackRules` gives it back, typed for this app. */\nexport type AppRawRule = RawRuleOf<AppAbility>;\n\nconst ACTION_SET: ReadonlySet<string> = new Set(ACTION_VALUES);\nconst SUBJECT_SET: ReadonlySet<string> = new Set(SUBJECT_VALUES);\nconst everyIn = (value: string | string[] | undefined, known: ReadonlySet<string>) =>\n value !== undefined && (Array.isArray(value) ? value : [value]).every((v) => known.has(v));\n\n/**\n * Whether an UNPACKED rule names only this app's actions and subjects\n * (0.18.0). Packed rules cross the wire as strings (`packRules` joins several\n * actions or subjects with commas, so the wire shape cannot be an enum); this\n * is the check that turns them back into `AppRawRule`s, with no cast:\n * `rules.every(isAppRawRule)` narrows the list, and a list that fails is a\n * contract mismatch, never an ability built from it.\n */\nexport function isAppRawRule(rule: {\n action: string | string[];\n subject?: string | string[];\n}): rule is AppRawRule {\n return everyIn(rule.action, ACTION_SET) && everyIn(rule.subject, SUBJECT_SET);\n}\n"]}
|
package/dist/core/common.d.ts
CHANGED
|
@@ -16,8 +16,14 @@ import { z } from 'zod';
|
|
|
16
16
|
* alone. Jansathi shipped that bug in dating onboarding.
|
|
17
17
|
*/
|
|
18
18
|
export declare const emptyStringToUndefined: (value: unknown) => unknown;
|
|
19
|
-
/**
|
|
20
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Optional trimmed string: a blank one (`''`, spaces) is absent (`undefined`).
|
|
21
|
+
* Trimmed, checked, then blank dropped, never a preprocess (0.18.0: a
|
|
22
|
+
* preprocess types its INPUT as `unknown`, so a caller typing a body by
|
|
23
|
+
* `z.input` could send a number and still compile). `.optional()` OUTSIDE, so
|
|
24
|
+
* the key stays optional in the output type as well as the input.
|
|
25
|
+
*/
|
|
26
|
+
export declare const optionalTrimmedString: (maxLength?: number) => z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
|
|
21
27
|
/**
|
|
22
28
|
* A repeated query param (`?skill=growth&skill=sales`) or a single value,
|
|
23
29
|
* always as an array, at most `max` values.
|
|
@@ -112,12 +118,13 @@ export type ProductListingRequirement = z.infer<typeof productListingRequirement
|
|
|
112
118
|
* is shared as a link and played in the card, never uploaded. The web plays it
|
|
113
119
|
* through youtube-nocookie.com, the one video frame its CSP admits.
|
|
114
120
|
*/
|
|
115
|
-
export declare const YOUTUBE_HOSTS: readonly ["youtube.com", "www.youtube.com", "m.youtube.com", "youtu.be", "youtube-nocookie.com", "www.youtube-nocookie.com"];
|
|
121
|
+
export declare const YOUTUBE_HOSTS: readonly ["youtube.com", "www.youtube.com", "m.youtube.com", "music.youtube.com", "youtu.be", "www.youtu.be", "youtube-nocookie.com", "www.youtube-nocookie.com"];
|
|
116
122
|
/**
|
|
117
123
|
* The 11-character id of a YouTube VIDEO link, or null for anything else (a
|
|
118
124
|
* channel, a playlist, the home page, another site): what a page can embed.
|
|
119
|
-
* Reads `youtu.be/<id>`, `/watch?v=<id>`, and `/shorts/`, `/embed/`, `/live
|
|
120
|
-
* on a `YOUTUBE_HOSTS` host (0.7.0
|
|
125
|
+
* Reads `youtu.be/<id>`, `/watch?v=<id>`, and `/shorts/`, `/embed/`, `/live/`,
|
|
126
|
+
* `/v/<id>` on a `YOUTUBE_HOSTS` host (0.7.0; `/v/` and the feed's hosts
|
|
127
|
+
* 0.18.0). THE rule for a product's video and a post's embed alike.
|
|
121
128
|
*/
|
|
122
129
|
export declare function youtubeVideoId(url: string): string | null;
|
|
123
130
|
/**
|
|
@@ -273,11 +280,16 @@ export declare const goalSchema: z.ZodEnum<{
|
|
|
273
280
|
attend_events: "attend_events";
|
|
274
281
|
}>;
|
|
275
282
|
export type Goal = z.infer<typeof goalSchema>;
|
|
283
|
+
/**
|
|
284
|
+
* True when no value is in the list twice: THE duplicate rule (0.18.0, exported;
|
|
285
|
+
* every list in this package refines with it instead of its own copy).
|
|
286
|
+
*/
|
|
287
|
+
export declare const eachOnce: (items: readonly unknown[]) => boolean;
|
|
276
288
|
/**
|
|
277
289
|
* A member's goals as a list (0.17.0): each goal once, at most every goal.
|
|
278
|
-
* ONE rule for onboarding's finish
|
|
279
|
-
*
|
|
280
|
-
* one, and the profile would show it twice.
|
|
290
|
+
* ONE rule for onboarding's finish and the Me tab's editor, which both add the
|
|
291
|
+
* minimum (`requiredGoalListSchema`, 0.18.0): two copies of one goal met the
|
|
292
|
+
* minimum while meaning one, and the profile would show it twice.
|
|
281
293
|
*/
|
|
282
294
|
export declare const goalListSchema: z.ZodArray<z.ZodEnum<{
|
|
283
295
|
find_cofounder: "find_cofounder";
|
|
@@ -299,6 +311,31 @@ export declare const goalListSchema: z.ZodArray<z.ZodEnum<{
|
|
|
299
311
|
find_service_providers: "find_service_providers";
|
|
300
312
|
attend_events: "attend_events";
|
|
301
313
|
}>>;
|
|
314
|
+
/**
|
|
315
|
+
* A member's goals where they are REQUIRED (0.18.0): at least `GOALS_MIN`.
|
|
316
|
+
* ONE schema for onboarding's finish and the Me editor (the user: "we already
|
|
317
|
+
* have at least one goal required"), so the two can never disagree.
|
|
318
|
+
*/
|
|
319
|
+
export declare const requiredGoalListSchema: z.ZodArray<z.ZodEnum<{
|
|
320
|
+
find_cofounder: "find_cofounder";
|
|
321
|
+
find_investors: "find_investors";
|
|
322
|
+
find_customers: "find_customers";
|
|
323
|
+
hiring: "hiring";
|
|
324
|
+
partnerships: "partnerships";
|
|
325
|
+
mentorship: "mentorship";
|
|
326
|
+
grow_network: "grow_network";
|
|
327
|
+
business_ideas: "business_ideas";
|
|
328
|
+
market_insights: "market_insights";
|
|
329
|
+
learn_startup: "learn_startup";
|
|
330
|
+
start_company: "start_company";
|
|
331
|
+
get_feedback: "get_feedback";
|
|
332
|
+
find_testers: "find_testers";
|
|
333
|
+
mentor_others: "mentor_others";
|
|
334
|
+
invest_in_startups: "invest_in_startups";
|
|
335
|
+
join_startup: "join_startup";
|
|
336
|
+
find_service_providers: "find_service_providers";
|
|
337
|
+
attend_events: "attend_events";
|
|
338
|
+
}>>;
|
|
302
339
|
/** A member's skills (0.17.0): each skill once, at most `SKILLS_MAX`. ONE rule for onboarding and Me. */
|
|
303
340
|
export declare const skillListSchema: z.ZodArray<z.ZodEnum<{
|
|
304
341
|
engineering: "engineering";
|
|
@@ -406,7 +443,7 @@ export declare function normalizePhone(raw: string): string;
|
|
|
406
443
|
*/
|
|
407
444
|
export declare const locationSchema: z.ZodObject<{
|
|
408
445
|
city: z.ZodString;
|
|
409
|
-
state: z.
|
|
446
|
+
state: z.ZodOptional<z.ZodPipe<z.ZodString, z.ZodTransform<string | undefined, string>>>;
|
|
410
447
|
country: z.ZodString;
|
|
411
448
|
}, z.core.$strip>;
|
|
412
449
|
export type FounderLocation = z.infer<typeof locationSchema>;
|
|
@@ -431,7 +468,7 @@ export type CoordinatesQueryInput = z.input<typeof coordinatesQuerySchema>;
|
|
|
431
468
|
*/
|
|
432
469
|
export declare const paginationQuerySchema: z.ZodObject<{
|
|
433
470
|
limit: z.ZodPreprocess<z.ZodDefault<z.ZodCoercedNumber<unknown>>, unknown>;
|
|
434
|
-
cursor: z.ZodOptional<z.ZodString>;
|
|
471
|
+
cursor: z.ZodPreprocess<z.ZodOptional<z.ZodString>, unknown>;
|
|
435
472
|
}, z.core.$strip>;
|
|
436
473
|
export type PaginationQuery = z.infer<typeof paginationQuerySchema>;
|
|
437
474
|
/** What a client may put IN the URL: every field optional, numbers as strings. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/core/common.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/core/common.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAiBxB;;;;;;;GAOG;AACH,eAAO,MAAM,sBAAsB,GAAI,OAAO,OAAO,KAAG,OAGvD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAAI,kBAAe,sFAMtC,CAAC;AAEhB;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,KAAK,MAAM,2DAKlC,CAAC;AAEzC,4EAA4E;AAC5E,eAAO,MAAM,cAAc,wDAGS,CAAC;AACrC,yFAAyF;AACzF,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAI5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,aAAa,UAIxB,CAAC;AACH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEpD;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa,UAAkC,CAAC;AAC7D,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEpD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAIpD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAM1E;AAED,2EAA2E;AAC3E,eAAO,MAAM,cAAc,GAAI,OAAO,SAAS,MAAM,EAAE,EAAE,SAAS,MAAM,aACI,CAAC;AAE7E;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,4CAA6C,CAAC;AAC7F,eAAO,MAAM,+BAA+B;;;;EAA6C,CAAC;AAC1F,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF;;;;GAIG;AACH,eAAO,MAAM,aAAa,mKAWhB,CAAC;AAEX;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgBzD;AAID;;;;;;;;;GASG;AACH,eAAO,MAAM,YAAY,IAAI,CAAC;AAC9B,eAAO,MAAM,YAAY,KAAK,CAAC;AAE/B,eAAO,MAAM,cAAc,aAOqD,CAAC;AACjF,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,kBAAkB,uNAqBrB,CAAC;AAIX,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,aAE/B,CAAC;AAIH;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,qGASvB,CAAC;AACX,eAAO,MAAM,kBAAkB;;;;;;;;;EAA+B,CAAC;AAC/D,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;;GAGG;AACH,eAAO,MAAM,eAAe,2PAsBlB,CAAC;AACX,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;EAA0B,CAAC;AACtD,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD;;;;GAIG;AACH,eAAO,MAAM,YAAY,8LAgBf,CAAC;AACX,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;EAAuB,CAAC;AAChD,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,WAAW,kUAwBd,CAAC;AACX,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;EAAsB,CAAC;AAC9C,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C;;;GAGG;AACH,eAAO,MAAM,QAAQ,GAAI,OAAO,SAAS,OAAO,EAAE,KAAG,OACf,CAAC;AAEvC;;;;;GAKG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;GAGsC,CAAC;AAElE;;;;GAIG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;GAAgC,CAAC;AAEpE,yGAAyG;AACzG,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;GAGsC,CAAC;AAInE;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,2DAA4D,CAAC;AACvF,eAAO,MAAM,YAAY;;;;;EAAwB,CAAC;AAClD,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAIlD;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAI,UAAU,MAAM,KAAG,MAA8C,CAAC;AAE/F;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,wDAKJ,CAAC;AAE3B;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAMvF;AAID,4FAA4F;AAC5F,eAAO,MAAM,YAAY,KAAK,CAAC;AAE/B;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW,aAGgE,CAAC;AACzF,MAAM,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,WAAW,CAAC,CAAC;AAEhD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAMlD;AAID;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,cAAc;;;;iBAIzB,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE7D;;;;;;GAMG;AACH,eAAO,MAAM,sBAAsB;;;iBAGjC,CAAC;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACtE,MAAM,MAAM,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAI3E;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;;;iBAiBhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,kFAAkF;AAClF,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
|