founder-schema 0.7.0 → 0.9.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 +39 -0
- package/dist/access/capabilities.d.ts +34 -0
- package/dist/access/capabilities.d.ts.map +1 -0
- package/dist/access/capabilities.js +35 -0
- package/dist/access/capabilities.js.map +1 -0
- package/dist/access/permissions.d.ts +20 -1
- package/dist/access/permissions.d.ts.map +1 -1
- package/dist/access/permissions.js +4 -0
- package/dist/access/permissions.js.map +1 -1
- package/dist/core/errors.d.ts +39 -1
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +63 -0
- package/dist/core/errors.js.map +1 -1
- package/dist/core/limits.d.ts +12 -0
- package/dist/core/limits.d.ts.map +1 -1
- package/dist/core/limits.js +13 -0
- package/dist/core/limits.js.map +1 -1
- package/dist/core/responses.d.ts +53 -0
- package/dist/core/responses.d.ts.map +1 -1
- package/dist/core/responses.js +7 -0
- package/dist/core/responses.js.map +1 -1
- package/dist/core/validation.d.ts +6 -0
- package/dist/core/validation.d.ts.map +1 -1
- package/dist/core/validation.js +6 -0
- package/dist/core/validation.js.map +1 -1
- package/dist/domains/audit/audit.d.ts +47 -3
- package/dist/domains/audit/audit.d.ts.map +1 -1
- package/dist/domains/audit/audit.js +40 -4
- package/dist/domains/audit/audit.js.map +1 -1
- package/dist/domains/communities/communities.d.ts +1537 -0
- package/dist/domains/communities/communities.d.ts.map +1 -0
- package/dist/domains/communities/communities.js +464 -0
- package/dist/domains/communities/communities.js.map +1 -0
- package/dist/domains/feed/feed.d.ts +996 -45
- package/dist/domains/feed/feed.d.ts.map +1 -1
- package/dist/domains/feed/feed.js +197 -10
- package/dist/domains/feed/feed.js.map +1 -1
- package/dist/domains/media/media.d.ts +7 -1
- package/dist/domains/media/media.d.ts.map +1 -1
- package/dist/domains/media/media.js +5 -0
- package/dist/domains/media/media.js.map +1 -1
- package/dist/domains/members/members.d.ts +6 -0
- package/dist/domains/members/members.d.ts.map +1 -1
- package/dist/domains/members/members.js +5 -0
- package/dist/domains/members/members.js.map +1 -1
- package/dist/domains/moderation/moderation.d.ts +6 -4
- package/dist/domains/moderation/moderation.d.ts.map +1 -1
- package/dist/domains/moderation/moderation.js +9 -2
- package/dist/domains/moderation/moderation.js.map +1 -1
- package/dist/domains/notifications/notifications.d.ts +133 -4
- package/dist/domains/notifications/notifications.d.ts.map +1 -1
- package/dist/domains/notifications/notifications.js +80 -3
- package/dist/domains/notifications/notifications.js.map +1 -1
- package/dist/domains/team/team.d.ts +22 -0
- package/dist/domains/team/team.d.ts.map +1 -1
- package/dist/domains/team/team.js +22 -0
- package/dist/domains/team/team.js.map +1 -1
- package/dist/domains/uploads/uploads.d.ts +1 -0
- package/dist/domains/uploads/uploads.d.ts.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/core/limits.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/core/limits.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema — Every length, size and count limit, by name\n *\n * ONE PLACE FOR EVERY NUMBER a client and the server must agree on. Before this,\n * the limits lived inline (`.max(80)`, `.max(1000)`) where nothing else could\n * read them, so the web client re-typed each one for its counters and the\n * backend re-typed them as Mongoose `maxlength`. Three copies of one number is\n * how the image alt text came to be 280 here and 500 in the backend.\n *\n * The rule from 0.2.0: a schema uses one of these constants, a consumer imports\n * the same constant, and nobody writes the digits again.\n *\n * Imports nothing, so any module here can use it without a cycle. Limits that\n * already had a name in their own module (POST_MAX_BODY_CHARS, POLL_*,\n * USERNAME_*, INTERESTS_*, MIN_AGE_YEARS...) stay where they are and keep their\n * names; this file holds the ones that had none.\n *\n * ⚠️ Lowering a limit is a breaking change for stored data: a value that was\n * valid when written fails the next edit. Raise freely; lower deliberately.\n *\n * @module founder-schema/core/limits\n */\n\n// ─── Profile ─────────────────────────────────────────────────────────────────\n\nexport const DISPLAY_NAME_MAX = 80;\nexport const HEADLINE_MAX = 120;\nexport const ABOUT_MAX = 1000;\n/** Skills a member may list. Discover filters on them. */\nexport const SKILLS_MAX = 8;\n/** Goals a member must pick at onboarding. The list itself caps the maximum. */\nexport const GOALS_MIN = 1;\n\n// ─── Location ────────────────────────────────────────────────────────────────\n\nexport const CITY_MAX = 80;\nexport const STATE_MAX = 80;\nexport const COUNTRY_MAX = 80;\n\n// ─── Venture ─────────────────────────────────────────────────────────────────\n\nexport const VENTURE_NAME_MAX = 120;\nexport const VENTURE_TAGLINE_MAX = 160;\n/** Team size including the founder. */\nexport const TEAM_SIZE_MIN = 1;\nexport const TEAM_SIZE_MAX = 100_000;\n/** Earliest founding year accepted. The ceiling is \"this year\", read per parse. */\nexport const FOUNDED_YEAR_MIN = 1900;\n\n// ─── Links ───────────────────────────────────────────────────────────────────\n\n/**\n * Longest URL a member may TYPE. Applied by `httpUrlSchema` itself, so every\n * member URL carries it: profile links, a venture website.\n *\n * ⚠ NOT applied to URLs the server reads from elsewhere (a link preview's\n * page or image), which use `safeUrlSchema`: a 314-character CDN image URL is\n * ordinary, and one of them would otherwise fail a whole feed page.\n */\nexport const LINK_URL_MAX = 300;\n\n// ─── Feed and moderation ─────────────────────────────────────────────────────\n\n/** Author-supplied image description. The backend's stored copy uses this too. */\nexport const MEDIA_ALT_MAX = 280;\nexport const LINK_PREVIEW_TITLE_MAX = 200;\nexport const LINK_PREVIEW_DESCRIPTION_MAX = 500;\n/** The free-text note on a report. */\nexport const REPORT_NOTE_MAX = 1000;\n/** A moderator's note on a decision (why it was hidden, removed or dismissed). */\nexport const MODERATION_NOTE_MAX = 500;\n/** What a post, poll or comment is shown as in a staff list: its first words. */\nexport const MODERATION_EXCERPT_MAX = 160;\n\n// ─── Staff ───────────────────────────────────────────────────────────────────\n\n/** Why a member was suspended. Shown to staff, and on record in the audit log. */\nexport const SUSPENSION_REASON_MIN = 3;\nexport const SUSPENSION_REASON_MAX = 500;\n/** The staff member directory's search box (a name, a handle or an email). */\nexport const MEMBER_SEARCH_MAX = 120;\n\n// ─── Support ─────────────────────────────────────────────────────────────────\n\nexport const TICKET_SUBJECT_MIN = 3;\nexport const TICKET_SUBJECT_MAX = 120;\n/** One message in a ticket, the member's or an agent's. */\nexport const TICKET_MESSAGE_MAX = 4000;\n/**\n * Messages one ticket may hold. A conversation that long is a new problem, so\n * the member is asked to open a new ticket rather than the thread growing\n * without bound.\n */\nexport const TICKET_MESSAGES_MAX = 200;\nexport const SAVED_REPLY_TITLE_MAX = 80;\nexport const SAVED_REPLY_BODY_MAX = 4000;\n/** Saved replies kept in all: a team's library, not an archive. */\nexport const SAVED_REPLIES_MAX = 200;\n\n// ─── Uploads ─────────────────────────────────────────────────────────────────\n\n/**\n * Largest file accepted for upload: 10 MB.\n *\n * Enforced three times. The upload-slot body refuses a bigger `contentLength`;\n * the size is SIGNED INTO the upload URL, so storage refuses a body of any other\n * size; and the server checks the stored object again before processing it.\n * The client checks first only to fail fast: it shrinks a photo well below this\n * before uploading (see `MEDIA_SIZES`), so a member rarely meets the limit.\n */\nexport const UPLOAD_MAX_BYTES = 10 * 1024 * 1024;\n\n/**\n * Largest image, in pixels, the server will decode: 50 megapixels, one frame.\n *\n * Bytes do not bound memory. A 50,000 x 50,000 PNG of one colour compresses to\n * a few kilobytes and decodes to ten gigabytes. The server reads the dimensions\n * first and refuses anything above this before decoding (`UPLOAD_TOO_MANY_PIXELS`).\n * 50 MP admits every phone camera's full-resolution photo up to 48 MP.\n */\nexport const MEDIA_MAX_INPUT_PIXELS = 50_000_000;\n\n/**\n * Hours an upload may wait to be attached to a post or set as an avatar. After\n * that the server deletes it: the composer was abandoned, or the tab closed.\n */\nexport const MEDIA_UNATTACHED_TTL_HOURS = 24;\n\n/**\n * Uploads one member may have waiting (requested or processed, not yet\n * attached) at once. A full post is `POST_MAX_MEDIA` (10); this leaves room for\n * a retry or two without letting one account fill storage.\n */\nexport const MEDIA_PENDING_MAX = 30;\n\n/** Longest placeholder string: a base64 ThumbHash is about 30 characters. */\nexport const MEDIA_PLACEHOLDER_MAX = 100;\n\n// ─── Lists and search ────────────────────────────────────────────────────────\n\n/** Values one Discover multi-select filter may carry (`?skill=a&skill=b`). */\nexport const DISCOVER_FILTER_VALUES_MAX = 10;\n/** Discover's free-text inputs: the name search, city and country. */\nexport const DISCOVER_TEXT_MAX = 80;\n/** Page size bounds for every cursor-paged list. */\nexport const PAGE_SIZE_MAX = 50;\nexport const PAGE_SIZE_DEFAULT = 20;\n\n// ─── Chat ────────────────────────────────────────────────────────────────────\n\n/**\n * Members one participants lookup may name. Bounded so the lookup cannot be\n * used to page through the directory.\n */\nexport const CHAT_PARTICIPANTS_MAX = 100;\n\n// ─── Products ────────────────────────────────────────────────────────────────\n\nexport const PRODUCT_NAME_MAX = 60;\n/** One line on a Discover card: short enough to never wrap twice on a phone. */\nexport const PRODUCT_TAGLINE_MAX = 80;\nexport const PRODUCT_DESCRIPTION_MAX = 3000;\n/** Screenshots on one product page. The first is its cover on Discover. */\nexport const PRODUCT_SCREENSHOTS_MAX = 8;\nexport const PRODUCT_CAPTION_MAX = 140;\n/**\n * Products one founder may OWN at once (archived ones count; deleting frees a\n * place). Being on someone else's team is not counted: the user ruled there is\n * no limit on a product's team, and so none on how many teams one joins.\n */\nexport const PRODUCTS_OWNED_MAX = 5;\n/** Team members a product page shows; the rest are one tap away (`/team`). */\nexport const PRODUCT_TEAM_SHOWN = 12;\n/**\n * Days a post's ask keeps its product on the \"asking for testers\" (or feedback,\n * launch, ideas) shelf. Each new post with that ask restarts the clock.\n */\nexport const PRODUCT_ASK_OPEN_DAYS = 14;\n\n// ─── Reviews ─────────────────────────────────────────────────────────────────\n\n/**\n * A review says WHY. A star with no reason does not help the founder, which is\n * the whole point of reviewing here, so the text is required and has a floor.\n */\nexport const REVIEW_TEXT_MIN = 30;\nexport const REVIEW_TEXT_MAX = 2000;\nexport const REVIEW_REPLY_MAX = 1000;\n/**\n * Reviews before a product shows an average (and enters \"Top rated\"). One or two\n * ratings say more about who wrote them than about the product.\n */\nexport const REVIEW_AVERAGE_MIN_COUNT = 3;\n\n// ─── Contribution ────────────────────────────────────────────────────────────\n\n/** Points one review can earn its writer through other members' Helpful votes. */\nexport const REVIEW_HELPFUL_POINTS_MAX = 10;\n/**\n * @deprecated 0.7.0: writing a review earns no points (the score counts only\n * what OTHER members recognise), so nothing reads this. Kept so no export is\n * removed.\n */\nexport const REVIEWS_REWARDED_PER_DAY = 3;\n/**\n * Points one member can earn from ONE other member over any 30 days, all kinds\n * together, so two founders cannot farm each other by trading Helpful marks.\n */\nexport const CONTRIBUTION_PAIR_POINTS_MAX = 30;\n/** Window of the \"this month\" score and the top-helpers list. */\nexport const CONTRIBUTION_RECENT_DAYS = 30;\n/** Rows on the top-helpers list. */\nexport const CONTRIBUTION_LEADERBOARD_SIZE = 20;\n\n// ─── Notifications ───────────────────────────────────────────────────────────\n\n/** Days a notification is kept. Neither Jansathi nor GymMonk ever deletes one. */\nexport const NOTIFICATION_RETENTION_DAYS = 90;\n/** The first words of a comment or review a notification shows. */\nexport const NOTIFICATION_EXCERPT_MAX = 140;\n"]}
|
|
1
|
+
{"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/core/limits.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema — Every length, size and count limit, by name\n *\n * ONE PLACE FOR EVERY NUMBER a client and the server must agree on. Before this,\n * the limits lived inline (`.max(80)`, `.max(1000)`) where nothing else could\n * read them, so the web client re-typed each one for its counters and the\n * backend re-typed them as Mongoose `maxlength`. Three copies of one number is\n * how the image alt text came to be 280 here and 500 in the backend.\n *\n * The rule from 0.2.0: a schema uses one of these constants, a consumer imports\n * the same constant, and nobody writes the digits again.\n *\n * Imports nothing, so any module here can use it without a cycle. Limits that\n * already had a name in their own module (POST_MAX_BODY_CHARS, POLL_*,\n * USERNAME_*, INTERESTS_*, MIN_AGE_YEARS...) stay where they are and keep their\n * names; this file holds the ones that had none.\n *\n * ⚠️ Lowering a limit is a breaking change for stored data: a value that was\n * valid when written fails the next edit. Raise freely; lower deliberately.\n *\n * @module founder-schema/core/limits\n */\n\n// ─── Profile ─────────────────────────────────────────────────────────────────\n\nexport const DISPLAY_NAME_MAX = 80;\nexport const HEADLINE_MAX = 120;\nexport const ABOUT_MAX = 1000;\n/** Skills a member may list. Discover filters on them. */\nexport const SKILLS_MAX = 8;\n/** Goals a member must pick at onboarding. The list itself caps the maximum. */\nexport const GOALS_MIN = 1;\n\n// ─── Location ────────────────────────────────────────────────────────────────\n\nexport const CITY_MAX = 80;\nexport const STATE_MAX = 80;\nexport const COUNTRY_MAX = 80;\n\n// ─── Venture ─────────────────────────────────────────────────────────────────\n\nexport const VENTURE_NAME_MAX = 120;\nexport const VENTURE_TAGLINE_MAX = 160;\n/** Team size including the founder. */\nexport const TEAM_SIZE_MIN = 1;\nexport const TEAM_SIZE_MAX = 100_000;\n/** Earliest founding year accepted. The ceiling is \"this year\", read per parse. */\nexport const FOUNDED_YEAR_MIN = 1900;\n\n// ─── Links ───────────────────────────────────────────────────────────────────\n\n/**\n * Longest URL a member may TYPE. Applied by `httpUrlSchema` itself, so every\n * member URL carries it: profile links, a venture website.\n *\n * ⚠ NOT applied to URLs the server reads from elsewhere (a link preview's\n * page or image), which use `safeUrlSchema`: a 314-character CDN image URL is\n * ordinary, and one of them would otherwise fail a whole feed page.\n */\nexport const LINK_URL_MAX = 300;\n\n// ─── Feed and moderation ─────────────────────────────────────────────────────\n\n/** Author-supplied image description. The backend's stored copy uses this too. */\nexport const MEDIA_ALT_MAX = 280;\nexport const LINK_PREVIEW_TITLE_MAX = 200;\nexport const LINK_PREVIEW_DESCRIPTION_MAX = 500;\n/** The free-text note on a report. */\nexport const REPORT_NOTE_MAX = 1000;\n/** A moderator's note on a decision (why it was hidden, removed or dismissed). */\nexport const MODERATION_NOTE_MAX = 500;\n/** What a post, poll or comment is shown as in a staff list: its first words. */\nexport const MODERATION_EXCERPT_MAX = 160;\n\n// ─── Staff ───────────────────────────────────────────────────────────────────\n\n/** Why a member was suspended. Shown to staff, and on record in the audit log. */\nexport const SUSPENSION_REASON_MIN = 3;\nexport const SUSPENSION_REASON_MAX = 500;\n/** The staff member directory's search box (a name, a handle or an email). */\nexport const MEMBER_SEARCH_MAX = 120;\n\n// ─── Support ─────────────────────────────────────────────────────────────────\n\nexport const TICKET_SUBJECT_MIN = 3;\nexport const TICKET_SUBJECT_MAX = 120;\n/** One message in a ticket, the member's or an agent's. */\nexport const TICKET_MESSAGE_MAX = 4000;\n/**\n * Messages one ticket may hold. A conversation that long is a new problem, so\n * the member is asked to open a new ticket rather than the thread growing\n * without bound.\n */\nexport const TICKET_MESSAGES_MAX = 200;\nexport const SAVED_REPLY_TITLE_MAX = 80;\nexport const SAVED_REPLY_BODY_MAX = 4000;\n/** Saved replies kept in all: a team's library, not an archive. */\nexport const SAVED_REPLIES_MAX = 200;\n\n// ─── Uploads ─────────────────────────────────────────────────────────────────\n\n/**\n * Largest file accepted for upload: 10 MB.\n *\n * Enforced three times. The upload-slot body refuses a bigger `contentLength`;\n * the size is SIGNED INTO the upload URL, so storage refuses a body of any other\n * size; and the server checks the stored object again before processing it.\n * The client checks first only to fail fast: it shrinks a photo well below this\n * before uploading (see `MEDIA_SIZES`), so a member rarely meets the limit.\n */\nexport const UPLOAD_MAX_BYTES = 10 * 1024 * 1024;\n\n/**\n * Largest image, in pixels, the server will decode: 50 megapixels, one frame.\n *\n * Bytes do not bound memory. A 50,000 x 50,000 PNG of one colour compresses to\n * a few kilobytes and decodes to ten gigabytes. The server reads the dimensions\n * first and refuses anything above this before decoding (`UPLOAD_TOO_MANY_PIXELS`).\n * 50 MP admits every phone camera's full-resolution photo up to 48 MP.\n */\nexport const MEDIA_MAX_INPUT_PIXELS = 50_000_000;\n\n/**\n * Hours an upload may wait to be attached to a post or set as an avatar. After\n * that the server deletes it: the composer was abandoned, or the tab closed.\n */\nexport const MEDIA_UNATTACHED_TTL_HOURS = 24;\n\n/**\n * Uploads one member may have waiting (requested or processed, not yet\n * attached) at once. A full post is `POST_MAX_MEDIA` (10); this leaves room for\n * a retry or two without letting one account fill storage.\n */\nexport const MEDIA_PENDING_MAX = 30;\n\n/** Longest placeholder string: a base64 ThumbHash is about 30 characters. */\nexport const MEDIA_PLACEHOLDER_MAX = 100;\n\n// ─── Lists and search ────────────────────────────────────────────────────────\n\n/** Values one Discover multi-select filter may carry (`?skill=a&skill=b`). */\nexport const DISCOVER_FILTER_VALUES_MAX = 10;\n/** Discover's free-text inputs: the name search, city and country. */\nexport const DISCOVER_TEXT_MAX = 80;\n/** Page size bounds for every cursor-paged list. */\nexport const PAGE_SIZE_MAX = 50;\nexport const PAGE_SIZE_DEFAULT = 20;\n\n// ─── Chat ────────────────────────────────────────────────────────────────────\n\n/**\n * Members one participants lookup may name. Bounded so the lookup cannot be\n * used to page through the directory.\n */\nexport const CHAT_PARTICIPANTS_MAX = 100;\n\n// ─── Products ────────────────────────────────────────────────────────────────\n\nexport const PRODUCT_NAME_MAX = 60;\n/** One line on a Discover card: short enough to never wrap twice on a phone. */\nexport const PRODUCT_TAGLINE_MAX = 80;\nexport const PRODUCT_DESCRIPTION_MAX = 3000;\n/** Screenshots on one product page. The first is its cover on Discover. */\nexport const PRODUCT_SCREENSHOTS_MAX = 8;\nexport const PRODUCT_CAPTION_MAX = 140;\n/**\n * Products one founder may OWN at once (archived ones count; deleting frees a\n * place). Being on someone else's team is not counted: the user ruled there is\n * no limit on a product's team, and so none on how many teams one joins.\n */\nexport const PRODUCTS_OWNED_MAX = 5;\n/** Team members a product page shows; the rest are one tap away (`/team`). */\nexport const PRODUCT_TEAM_SHOWN = 12;\n/**\n * Days a post's ask keeps its product on the \"asking for testers\" (or feedback,\n * launch, ideas) shelf. Each new post with that ask restarts the clock.\n */\nexport const PRODUCT_ASK_OPEN_DAYS = 14;\n\n// ─── Communities (0.9.0) ─────────────────────────────────────────────────────\n\nexport const COMMUNITY_NAME_MIN = 3;\nexport const COMMUNITY_NAME_MAX = 60;\nexport const COMMUNITY_DESCRIPTION_MAX = 500;\n/** Rules on one community, each one line. */\nexport const COMMUNITY_RULES_MAX = 10;\nexport const COMMUNITY_RULE_MAX = 200;\n/** What a suggester says the community is for, beyond its description. */\nexport const COMMUNITY_SUGGESTION_WHY_MAX = 500;\n/** A declined suggestion says why: long enough to be a reason, not a novel. */\nexport const COMMUNITY_DECLINE_REASON_MIN = 3;\nexport const COMMUNITY_DECLINE_REASON_MAX = 500;\nexport const COMMUNITY_BAN_REASON_MAX = 500;\n\n// ─── Reviews ─────────────────────────────────────────────────────────────────\n\n/**\n * A review says WHY. A star with no reason does not help the founder, which is\n * the whole point of reviewing here, so the text is required and has a floor.\n */\nexport const REVIEW_TEXT_MIN = 30;\nexport const REVIEW_TEXT_MAX = 2000;\nexport const REVIEW_REPLY_MAX = 1000;\n/**\n * Reviews before a product shows an average (and enters \"Top rated\"). One or two\n * ratings say more about who wrote them than about the product.\n */\nexport const REVIEW_AVERAGE_MIN_COUNT = 3;\n\n// ─── Contribution ────────────────────────────────────────────────────────────\n\n/** Points one review can earn its writer through other members' Helpful votes. */\nexport const REVIEW_HELPFUL_POINTS_MAX = 10;\n/**\n * @deprecated 0.7.0: writing a review earns no points (the score counts only\n * what OTHER members recognise), so nothing reads this. Kept so no export is\n * removed.\n */\nexport const REVIEWS_REWARDED_PER_DAY = 3;\n/**\n * Points one member can earn from ONE other member over any 30 days, all kinds\n * together, so two founders cannot farm each other by trading Helpful marks.\n */\nexport const CONTRIBUTION_PAIR_POINTS_MAX = 30;\n/** Window of the \"this month\" score and the top-helpers list. */\nexport const CONTRIBUTION_RECENT_DAYS = 30;\n/** Rows on the top-helpers list. */\nexport const CONTRIBUTION_LEADERBOARD_SIZE = 20;\n\n// ─── Notifications ───────────────────────────────────────────────────────────\n\n/** Days a notification is kept. Neither Jansathi nor GymMonk ever deletes one. */\nexport const NOTIFICATION_RETENTION_DAYS = 90;\n/** The first words of a comment or review a notification shows. */\nexport const NOTIFICATION_EXCERPT_MAX = 140;\n"]}
|
package/dist/core/responses.d.ts
CHANGED
|
@@ -40,6 +40,11 @@ export declare const apiErrorDetailsSchema: z.ZodObject<{
|
|
|
40
40
|
tagline: "tagline";
|
|
41
41
|
screenshot: "screenshot";
|
|
42
42
|
}>>>;
|
|
43
|
+
nextAt: z.ZodOptional<z.ZodISODateTime>;
|
|
44
|
+
communities: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
45
|
+
id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
46
|
+
name: z.ZodString;
|
|
47
|
+
}, z.core.$strip>>>;
|
|
43
48
|
}, z.core.$strip>;
|
|
44
49
|
export type ApiErrorDetails = z.infer<typeof apiErrorDetailsSchema>;
|
|
45
50
|
/**
|
|
@@ -78,6 +83,9 @@ export declare const apiErrorSchema: z.ZodObject<{
|
|
|
78
83
|
REPLY_TOO_DEEP: "REPLY_TOO_DEEP";
|
|
79
84
|
CANNOT_REPORT_OWN: "CANNOT_REPORT_OWN";
|
|
80
85
|
FEEDBACK_NOT_MARKABLE: "FEEDBACK_NOT_MARKABLE";
|
|
86
|
+
CANNOT_REPOST_OWN: "CANNOT_REPOST_OWN";
|
|
87
|
+
CANNOT_REPOST_A_REPOST: "CANNOT_REPOST_A_REPOST";
|
|
88
|
+
ALREADY_REPOSTED: "ALREADY_REPOSTED";
|
|
81
89
|
PRODUCT_LIMIT_REACHED: "PRODUCT_LIMIT_REACHED";
|
|
82
90
|
PRODUCT_NOT_LISTABLE: "PRODUCT_NOT_LISTABLE";
|
|
83
91
|
PRODUCT_ARCHIVED: "PRODUCT_ARCHIVED";
|
|
@@ -88,6 +96,20 @@ export declare const apiErrorSchema: z.ZodObject<{
|
|
|
88
96
|
PRODUCT_OWNER_MUST_TRANSFER: "PRODUCT_OWNER_MUST_TRANSFER";
|
|
89
97
|
PRODUCT_OWN_TEAM: "PRODUCT_OWN_TEAM";
|
|
90
98
|
TESTER_NOT_ELIGIBLE: "TESTER_NOT_ELIGIBLE";
|
|
99
|
+
COMMUNITY_JOIN_REQUIRED: "COMMUNITY_JOIN_REQUIRED";
|
|
100
|
+
COMMUNITY_NAME_TAKEN: "COMMUNITY_NAME_TAKEN";
|
|
101
|
+
COMMUNITY_ALREADY_MEMBER: "COMMUNITY_ALREADY_MEMBER";
|
|
102
|
+
COMMUNITY_MEMBER_NOT_INVITABLE: "COMMUNITY_MEMBER_NOT_INVITABLE";
|
|
103
|
+
COMMUNITY_MEMBER_BANNED: "COMMUNITY_MEMBER_BANNED";
|
|
104
|
+
COMMUNITY_INVITE_NOT_NEEDED: "COMMUNITY_INVITE_NOT_NEEDED";
|
|
105
|
+
COMMUNITY_LAST_ADMIN: "COMMUNITY_LAST_ADMIN";
|
|
106
|
+
COMMUNITY_ARCHIVED: "COMMUNITY_ARCHIVED";
|
|
107
|
+
COMMUNITY_EXPOSURE_UNCONFIRMED: "COMMUNITY_EXPOSURE_UNCONFIRMED";
|
|
108
|
+
COMMUNITY_PINS_FULL: "COMMUNITY_PINS_FULL";
|
|
109
|
+
COMMUNITY_ANNOUNCE_TOO_SOON: "COMMUNITY_ANNOUNCE_TOO_SOON";
|
|
110
|
+
COMMUNITY_CONTENT_NOT_REPOSTABLE: "COMMUNITY_CONTENT_NOT_REPOSTABLE";
|
|
111
|
+
COMMUNITY_SUGGESTIONS_FULL: "COMMUNITY_SUGGESTIONS_FULL";
|
|
112
|
+
COMMUNITY_SUGGESTION_DECIDED: "COMMUNITY_SUGGESTION_DECIDED";
|
|
91
113
|
REVIEW_EXISTS: "REVIEW_EXISTS";
|
|
92
114
|
REVIEW_OWN_VOTE: "REVIEW_OWN_VOTE";
|
|
93
115
|
MODERATION_ACTION_INVALID: "MODERATION_ACTION_INVALID";
|
|
@@ -100,6 +122,8 @@ export declare const apiErrorSchema: z.ZodObject<{
|
|
|
100
122
|
MPIN_LOCKED: "MPIN_LOCKED";
|
|
101
123
|
CANNOT_CHANGE_OWN_ROLES: "CANNOT_CHANGE_OWN_ROLES";
|
|
102
124
|
ROLE_NOT_GRANTABLE: "ROLE_NOT_GRANTABLE";
|
|
125
|
+
CANNOT_CHANGE_OWN_CAPABILITIES: "CANNOT_CHANGE_OWN_CAPABILITIES";
|
|
126
|
+
CAPABILITY_NOT_GRANTABLE: "CAPABILITY_NOT_GRANTABLE";
|
|
103
127
|
TICKET_FULL: "TICKET_FULL";
|
|
104
128
|
SAVED_REPLIES_FULL: "SAVED_REPLIES_FULL";
|
|
105
129
|
}>;
|
|
@@ -126,6 +150,11 @@ export declare const apiErrorSchema: z.ZodObject<{
|
|
|
126
150
|
tagline: "tagline";
|
|
127
151
|
screenshot: "screenshot";
|
|
128
152
|
}>>>;
|
|
153
|
+
nextAt: z.ZodOptional<z.ZodISODateTime>;
|
|
154
|
+
communities: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
155
|
+
id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
156
|
+
name: z.ZodString;
|
|
157
|
+
}, z.core.$strip>>>;
|
|
129
158
|
}, z.core.$strip>>;
|
|
130
159
|
}, z.core.$strip>;
|
|
131
160
|
export type ApiError = z.infer<typeof apiErrorSchema>;
|
|
@@ -169,6 +198,9 @@ export declare const errorResponseSchema: z.ZodObject<{
|
|
|
169
198
|
REPLY_TOO_DEEP: "REPLY_TOO_DEEP";
|
|
170
199
|
CANNOT_REPORT_OWN: "CANNOT_REPORT_OWN";
|
|
171
200
|
FEEDBACK_NOT_MARKABLE: "FEEDBACK_NOT_MARKABLE";
|
|
201
|
+
CANNOT_REPOST_OWN: "CANNOT_REPOST_OWN";
|
|
202
|
+
CANNOT_REPOST_A_REPOST: "CANNOT_REPOST_A_REPOST";
|
|
203
|
+
ALREADY_REPOSTED: "ALREADY_REPOSTED";
|
|
172
204
|
PRODUCT_LIMIT_REACHED: "PRODUCT_LIMIT_REACHED";
|
|
173
205
|
PRODUCT_NOT_LISTABLE: "PRODUCT_NOT_LISTABLE";
|
|
174
206
|
PRODUCT_ARCHIVED: "PRODUCT_ARCHIVED";
|
|
@@ -179,6 +211,20 @@ export declare const errorResponseSchema: z.ZodObject<{
|
|
|
179
211
|
PRODUCT_OWNER_MUST_TRANSFER: "PRODUCT_OWNER_MUST_TRANSFER";
|
|
180
212
|
PRODUCT_OWN_TEAM: "PRODUCT_OWN_TEAM";
|
|
181
213
|
TESTER_NOT_ELIGIBLE: "TESTER_NOT_ELIGIBLE";
|
|
214
|
+
COMMUNITY_JOIN_REQUIRED: "COMMUNITY_JOIN_REQUIRED";
|
|
215
|
+
COMMUNITY_NAME_TAKEN: "COMMUNITY_NAME_TAKEN";
|
|
216
|
+
COMMUNITY_ALREADY_MEMBER: "COMMUNITY_ALREADY_MEMBER";
|
|
217
|
+
COMMUNITY_MEMBER_NOT_INVITABLE: "COMMUNITY_MEMBER_NOT_INVITABLE";
|
|
218
|
+
COMMUNITY_MEMBER_BANNED: "COMMUNITY_MEMBER_BANNED";
|
|
219
|
+
COMMUNITY_INVITE_NOT_NEEDED: "COMMUNITY_INVITE_NOT_NEEDED";
|
|
220
|
+
COMMUNITY_LAST_ADMIN: "COMMUNITY_LAST_ADMIN";
|
|
221
|
+
COMMUNITY_ARCHIVED: "COMMUNITY_ARCHIVED";
|
|
222
|
+
COMMUNITY_EXPOSURE_UNCONFIRMED: "COMMUNITY_EXPOSURE_UNCONFIRMED";
|
|
223
|
+
COMMUNITY_PINS_FULL: "COMMUNITY_PINS_FULL";
|
|
224
|
+
COMMUNITY_ANNOUNCE_TOO_SOON: "COMMUNITY_ANNOUNCE_TOO_SOON";
|
|
225
|
+
COMMUNITY_CONTENT_NOT_REPOSTABLE: "COMMUNITY_CONTENT_NOT_REPOSTABLE";
|
|
226
|
+
COMMUNITY_SUGGESTIONS_FULL: "COMMUNITY_SUGGESTIONS_FULL";
|
|
227
|
+
COMMUNITY_SUGGESTION_DECIDED: "COMMUNITY_SUGGESTION_DECIDED";
|
|
182
228
|
REVIEW_EXISTS: "REVIEW_EXISTS";
|
|
183
229
|
REVIEW_OWN_VOTE: "REVIEW_OWN_VOTE";
|
|
184
230
|
MODERATION_ACTION_INVALID: "MODERATION_ACTION_INVALID";
|
|
@@ -191,6 +237,8 @@ export declare const errorResponseSchema: z.ZodObject<{
|
|
|
191
237
|
MPIN_LOCKED: "MPIN_LOCKED";
|
|
192
238
|
CANNOT_CHANGE_OWN_ROLES: "CANNOT_CHANGE_OWN_ROLES";
|
|
193
239
|
ROLE_NOT_GRANTABLE: "ROLE_NOT_GRANTABLE";
|
|
240
|
+
CANNOT_CHANGE_OWN_CAPABILITIES: "CANNOT_CHANGE_OWN_CAPABILITIES";
|
|
241
|
+
CAPABILITY_NOT_GRANTABLE: "CAPABILITY_NOT_GRANTABLE";
|
|
194
242
|
TICKET_FULL: "TICKET_FULL";
|
|
195
243
|
SAVED_REPLIES_FULL: "SAVED_REPLIES_FULL";
|
|
196
244
|
}>;
|
|
@@ -217,6 +265,11 @@ export declare const errorResponseSchema: z.ZodObject<{
|
|
|
217
265
|
tagline: "tagline";
|
|
218
266
|
screenshot: "screenshot";
|
|
219
267
|
}>>>;
|
|
268
|
+
nextAt: z.ZodOptional<z.ZodISODateTime>;
|
|
269
|
+
communities: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
270
|
+
id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
271
|
+
name: z.ZodString;
|
|
272
|
+
}, z.core.$strip>>>;
|
|
220
273
|
}, z.core.$strip>>;
|
|
221
274
|
}, z.core.$strip>;
|
|
222
275
|
}, z.core.$strip>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"responses.d.ts","sourceRoot":"","sources":["../../src/core/responses.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB
|
|
1
|
+
{"version":3,"file":"responses.d.ts","sourceRoot":"","sources":["../../src/core/responses.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAKxB;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAehC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAIzB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD,mDAAmD;AACnD,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC;;;;iBAK9D,CAAC;AAEL,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAG9B,CAAC;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEhE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,MAAM,CAAC;;;iBAIxD,CAAC;AAEL;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;iBAAmC,CAAC;AACjE,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC"}
|
package/dist/core/responses.js
CHANGED
|
@@ -29,6 +29,13 @@ export const apiErrorDetailsSchema = z.object({
|
|
|
29
29
|
lockedUntil: z.iso.datetime().optional(),
|
|
30
30
|
/** `PRODUCT_NOT_LISTABLE`: what the product lacks to be (or stay) listed. */
|
|
31
31
|
missing: z.array(productListingRequirementSchema).optional(),
|
|
32
|
+
/** `COMMUNITY_ANNOUNCE_TOO_SOON` (0.9.0): when notifying every member is allowed again. */
|
|
33
|
+
nextAt: z.iso.datetime().optional(),
|
|
34
|
+
/**
|
|
35
|
+
* `COMMUNITY_LAST_ADMIN` on an account deletion (0.9.0): the communities the
|
|
36
|
+
* member is the only admin of, to hand over first.
|
|
37
|
+
*/
|
|
38
|
+
communities: z.array(z.object({ id: objectIdSchema, name: z.string() })).optional(),
|
|
32
39
|
});
|
|
33
40
|
/**
|
|
34
41
|
* `code` is one of `API_ERROR_CODE_VALUES`, and it is what a client acts on.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"responses.js","sourceRoot":"","sources":["../../src/core/responses.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,+BAA+B,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAExD;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,QAAQ,EAAE;IACjD,gEAAgE;IAChE,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAChD,uDAAuD;IACvD,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACxC,6EAA6E;IAC7E,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC,QAAQ,EAAE;
|
|
1
|
+
{"version":3,"file":"responses.js","sourceRoot":"","sources":["../../src/core/responses.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,cAAc,EAAE,+BAA+B,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAExD;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC,QAAQ,EAAE;IACjD,gEAAgE;IAChE,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAChD,uDAAuD;IACvD,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACxC,6EAA6E;IAC7E,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC,QAAQ,EAAE;IAC5D,2FAA2F;IAC3F,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IACnC;;;OAGG;IACH,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE;CACpF,CAAC,CAAC;AAGH;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,IAAI,EAAE,kBAAkB;IACxB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,OAAO,EAAE,qBAAqB,CAAC,QAAQ,EAAE;CAC1C,CAAC,CAAC;AAGH,mDAAmD;AACnD,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAsB,IAAO,EAAE,EAAE,CACpE,CAAC,CAAC,MAAM,CAAC;IACP,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;IACxB,IAAI;IACJ,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAC;AAEL,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;IACzB,KAAK,EAAE,cAAc;CACtB,CAAC,CAAC;AAGH;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAsB,IAAO,EAAE,EAAE,CAC9D,CAAC,CAAC,MAAM,CAAC;IACP,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;IACpB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAClC,CAAC,CAAC;AAEL;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC,CAAC","sourcesContent":["/**\n * founder-schema — API response envelope\n *\n * Every Founder Circle endpoint answers in this shape. The clients read the\n * failure reason at `error.message`, so a handler must never return a bare\n * string body.\n *\n * @module founder-schema/core/responses\n */\n\nimport { z } from 'zod';\nimport { objectIdSchema, productListingRequirementSchema } from './common.js';\nimport { apiErrorCodeSchema } from './errors.js';\nimport { validationIssueSchema } from './validation.js';\n\n/**\n * What an error carries beyond its code, each field named by the code that\n * sends it. Declared here, not loosely typed, because a client parses errors\n * with this schema and Zod drops a key the schema does not name.\n *\n * `issues` is EVERY problem of a 400, each with its path, built on the server\n * by `toValidationIssues`. Until 0.2.0 this was typed as a record of strings\n * that nothing ever sent, while the real 400s carried only the first issue's\n * message, so a client could not tell which field to mark.\n */\nexport const apiErrorDetailsSchema = z.object({\n issues: z.array(validationIssueSchema).optional(),\n /** `MPIN_INCORRECT`: wrong tries left before the MPIN locks. */\n attemptsLeft: z.number().int().min(0).optional(),\n /** `MPIN_LOCKED`: when the MPIN can be tried again. */\n lockedUntil: z.iso.datetime().optional(),\n /** `PRODUCT_NOT_LISTABLE`: what the product lacks to be (or stay) listed. */\n missing: z.array(productListingRequirementSchema).optional(),\n /** `COMMUNITY_ANNOUNCE_TOO_SOON` (0.9.0): when notifying every member is allowed again. */\n nextAt: z.iso.datetime().optional(),\n /**\n * `COMMUNITY_LAST_ADMIN` on an account deletion (0.9.0): the communities the\n * member is the only admin of, to hand over first.\n */\n communities: z.array(z.object({ id: objectIdSchema, name: z.string() })).optional(),\n});\nexport type ApiErrorDetails = z.infer<typeof apiErrorDetailsSchema>;\n\n/**\n * `code` is one of `API_ERROR_CODE_VALUES`, and it is what a client acts on.\n * `message` is English, for logs and for a client that predates the code.\n */\nexport const apiErrorSchema = z.object({\n code: apiErrorCodeSchema,\n message: z.string(),\n details: apiErrorDetailsSchema.optional(),\n});\nexport type ApiError = z.infer<typeof apiErrorSchema>;\n\n/** Success envelope. `T` is the payload schema. */\nexport const successResponseSchema = <T extends z.ZodType>(data: T) =>\n z.object({\n success: z.literal(true),\n data,\n message: z.string().optional(),\n });\n\nexport const errorResponseSchema = z.object({\n success: z.literal(false),\n error: apiErrorSchema,\n});\nexport type ErrorResponse = z.infer<typeof errorResponseSchema>;\n\n/**\n * Cursor-paginated list envelope.\n *\n * `nextCursor` is REQUIRED and is `null` at the end of the list. Omitting the\n * key is not the same thing and does not validate: a handler must decide\n * whether there is another page and say so, because \"the key is missing\" and\n * \"there are no more results\" are indistinguishable to a client that has to\n * choose between showing a Load more button and not showing one.\n *\n * An earlier version of this comment said absence and null were equivalent.\n * They never were; the schema has always required the key. Corrected\n * 2026-09-21, and pinned by a test.\n */\nexport const paginatedSchema = <T extends z.ZodType>(item: T) =>\n z.object({\n items: z.array(item),\n nextCursor: z.string().nullable(),\n });\n\n/**\n * What a create, an edit or a delete answers: the id of the thing it touched.\n * A client that needs the whole item reads it, so a write never ships a second\n * projection of it that could drift from the read path's.\n */\nexport const idResponseSchema = z.object({ id: objectIdSchema });\nexport type IdResponse = z.infer<typeof idResponseSchema>;\n"]}
|
|
@@ -93,6 +93,12 @@ export declare const VALIDATION_MESSAGE: {
|
|
|
93
93
|
readonly screenshotsDuplicate: "screenshotsDuplicate";
|
|
94
94
|
/** The same platform twice in one list. */
|
|
95
95
|
readonly platformsDuplicate: "platformsDuplicate";
|
|
96
|
+
/** The same topic twice in one list (0.9.0). */
|
|
97
|
+
readonly topicsDuplicate: "topicsDuplicate";
|
|
98
|
+
/** A post or poll in a community naming a product or an ask (0.9.0): community content carries neither. */
|
|
99
|
+
readonly communityWithProduct: "communityWithProduct";
|
|
100
|
+
/** The same capability twice in one list (0.9.0). */
|
|
101
|
+
readonly capabilitiesDuplicate: "capabilitiesDuplicate";
|
|
96
102
|
};
|
|
97
103
|
export type ValidationMessageKey = (typeof VALIDATION_MESSAGE)[keyof typeof VALIDATION_MESSAGE];
|
|
98
104
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/core/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB;IAC7B,6BAA6B;;IAE7B,6DAA6D;;IAE7D,2CAA2C;;IAE3C,+BAA+B;;IAE/B,uDAAuD;;IAEvD,6BAA6B;;IAE7B,8DAA8D;;IAE9D,uCAAuC;;IAEvC,wDAAwD;;IAExD,0DAA0D;;IAE1D,0CAA0C;;IAE1C,4CAA4C;;IAE5C,uEAAuE;;IAEvE,uFAAuF;;IAEvF,sEAAsE;;IAEtE,yEAAyE;;IAEzE,8CAA8C;;IAE9C,4CAA4C;;IAE5C,kDAAkD;;IAElD,uCAAuC;;IAEvC,yEAAyE;;IAEzE,uDAAuD;;IAEvD,wDAAwD;;IAExD,+CAA+C;;IAE/C,gFAAgF;;IAEhF,gDAAgD;;IAEhD,2CAA2C;;
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/core/validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB;IAC7B,6BAA6B;;IAE7B,6DAA6D;;IAE7D,2CAA2C;;IAE3C,+BAA+B;;IAE/B,uDAAuD;;IAEvD,6BAA6B;;IAE7B,8DAA8D;;IAE9D,uCAAuC;;IAEvC,wDAAwD;;IAExD,0DAA0D;;IAE1D,0CAA0C;;IAE1C,4CAA4C;;IAE5C,uEAAuE;;IAEvE,uFAAuF;;IAEvF,sEAAsE;;IAEtE,yEAAyE;;IAEzE,8CAA8C;;IAE9C,4CAA4C;;IAE5C,kDAAkD;;IAElD,uCAAuC;;IAEvC,yEAAyE;;IAEzE,uDAAuD;;IAEvD,wDAAwD;;IAExD,+CAA+C;;IAE/C,gFAAgF;;IAEhF,gDAAgD;;IAEhD,2CAA2C;;IAE3C,gDAAgD;;IAEhD,2GAA2G;;IAE3G,qDAAqD;;CAE7C,CAAC;AACX,MAAM,MAAM,oBAAoB,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,OAAO,kBAAkB,CAAC,CAAC;AAEhG;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,sCAAuC,CAAC;AACxE,eAAO,MAAM,iBAAiB;;;;EAA8B,CAAC;AAC7D,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;iBAkBhC,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAEpE,6EAA6E;AAC7E,UAAU,YAAY;IACpB,IAAI,EAAE,SAAS,WAAW,EAAE,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAKD;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE;IAAE,MAAM,EAAE,SAAS,YAAY,EAAE,CAAA;CAAE,EAC1C,QAAQ,CAAC,EAAE,WAAW,GACrB,eAAe,EAAE,CAkBnB"}
|
package/dist/core/validation.js
CHANGED
|
@@ -93,6 +93,12 @@ export const VALIDATION_MESSAGE = {
|
|
|
93
93
|
screenshotsDuplicate: 'screenshotsDuplicate',
|
|
94
94
|
/** The same platform twice in one list. */
|
|
95
95
|
platformsDuplicate: 'platformsDuplicate',
|
|
96
|
+
/** The same topic twice in one list (0.9.0). */
|
|
97
|
+
topicsDuplicate: 'topicsDuplicate',
|
|
98
|
+
/** A post or poll in a community naming a product or an ask (0.9.0): community content carries neither. */
|
|
99
|
+
communityWithProduct: 'communityWithProduct',
|
|
100
|
+
/** The same capability twice in one list (0.9.0). */
|
|
101
|
+
capabilitiesDuplicate: 'capabilitiesDuplicate',
|
|
96
102
|
};
|
|
97
103
|
/**
|
|
98
104
|
* Which part of the request an issue is about.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/core/validation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema — How a validation failure is described, on both sides\n *\n * TWO RULES, and they are what let the web client show any error, its own or\n * the server's, on the right field in the member's language:\n *\n * 1. A CUSTOM MESSAGE IS A KEY, NEVER A SENTENCE. Where a schema needs its own\n * message (a refine, a regex), the message is one of `VALIDATION_MESSAGE`,\n * such as `phoneInvalid`. The client turns the key into text in English or\n * Hindi. Before 0.2.0 these were English sentences, so a Hindi screen showed\n * an English error, and a client that wanted to translate one had to match\n * on the wording.\n *\n * Plain bounds carry NO custom message. `.min(1)` and `.max(80)` produce\n * Zod's own `too_small` / `too_big` issues, which already say which bound\n * and what it was (`minimum`, `maximum`, `origin`), and the client words\n * them from those. A hand-written \"at most 80 characters\" would only\n * restate the number in one language.\n *\n * 2. THE SERVER SENDS EVERY ISSUE, IN THIS SHAPE. A 400 answers with\n * `error.details.issues`, built by `toValidationIssues`, so the client can\n * put each one under its field. Until 0.2.0 the backend sent only the\n * first issue's message, with no path at all.\n *\n * ⚠️ `toValidationIssues` COPIES A WHITELIST OF FIELDS, it never spreads an\n * issue. Zod can carry the rejected `input` on an issue, and these bodies hold a\n * date of birth and a phone number. Echoing them into an error response sends\n * them back to the client, into logs and into any proxy in between. A test\n * proves `input` never survives.\n *\n * @module founder-schema/core/validation\n */\n\nimport { z } from 'zod';\n\n/**\n * Every custom message a schema in this package can produce. Each is a\n * translation key the clients define in every language they ship.\n *\n * ⚠️ Append only, and add the translation in the same change. Renaming one\n * shows the raw key to every member until each client ships the new name.\n */\nexport const VALIDATION_MESSAGE = {\n /** Not a 24-hex Mongo id. */\n invalidId: 'invalidId',\n /** Handle: wrong characters, or a dot in the wrong place. */\n usernameInvalid: 'usernameInvalid',\n /** Not a 10-digit Indian mobile number. */\n phoneInvalid: 'phoneInvalid',\n /** Not a `YYYY-MM-DD` date. */\n dateInvalid: 'dateInvalid',\n /** A date that does not exist, such as 30 February. */\n dobInvalid: 'dobInvalid',\n /** Under the minimum age. */\n dobUnderAge: 'dobUnderAge',\n /** Over the sanity ceiling: almost always a mistyped year. */\n dobTooOld: 'dobTooOld',\n /** A founding year after this year. */\n foundedYearFuture: 'foundedYearFuture',\n /** An upload key that is not one this product mints. */\n uploadKeyInvalid: 'uploadKeyInvalid',\n /** The same uploaded image attached twice to one post. */\n mediaDuplicate: 'mediaDuplicate',\n /** A post with neither text nor media. */\n postEmpty: 'postEmpty',\n /** Two poll options with the same label. */\n pollOptionsDuplicate: 'pollOptionsDuplicate',\n /** An empty string sent to clear a patch field; clearing is `null`. */\n clearWithNull: 'clearWithNull',\n /** Not a plain http(s) URL on a real domain (`javascript:`, `localhost`, an IP...). */\n urlInvalid: 'urlInvalid',\n /** A handle this product keeps for itself (`admin`, `support`...). */\n usernameReserved: 'usernameReserved',\n /** Agreed to a version of the guidelines that is not the current one. */\n guidelinesOutdated: 'guidelinesOutdated',\n /** An MPIN that is not exactly six digits. */\n mpinInvalid: 'mpinInvalid',\n /** The MPIN and its confirmation differ. */\n mpinMismatch: 'mpinMismatch',\n /** Switching to a staff role without the MPIN. */\n mpinRequired: 'mpinRequired',\n /** The same role twice in one list. */\n rolesDuplicate: 'rolesDuplicate',\n /** An ask (\"testers wanted\") on a post or poll that names no product. */\n askWithoutProduct: 'askWithoutProduct',\n /** An App Store link that is not on apps.apple.com. */\n appStoreLinkHost: 'appStoreLinkHost',\n /** A Play Store link that is not on play.google.com. */\n playStoreLinkHost: 'playStoreLinkHost',\n /** A GitHub link that is not on github.com. */\n githubLinkHost: 'githubLinkHost',\n /** A demo video that is not a YouTube link. Video is shared, never uploaded. */\n videoLinkHost: 'videoLinkHost',\n /** The same screenshot twice in one gallery. */\n screenshotsDuplicate: 'screenshotsDuplicate',\n /** The same platform twice in one list. */\n platformsDuplicate: 'platformsDuplicate',\n} as const;\nexport type ValidationMessageKey = (typeof VALIDATION_MESSAGE)[keyof typeof VALIDATION_MESSAGE];\n\n/**\n * Which part of the request an issue is about.\n *\n * ONE PATH CONVENTION: `path` is ALWAYS relative to this part, so a body field\n * reads `['venture', 'name']` with `location: 'body'`, never\n * `['body', 'venture', 'name']`. A form maps body issues straight onto its\n * fields without stripping a prefix, and a client that sees `query` or\n * `params` knows the problem is the URL, not anything the member typed.\n */\nexport const REQUEST_PART_VALUES = ['body', 'query', 'params'] as const;\nexport const requestPartSchema = z.enum(REQUEST_PART_VALUES);\nexport type RequestPart = z.infer<typeof requestPartSchema>;\n\n/**\n * One problem with one field, as it crosses the wire.\n *\n * `code` is Zod's issue code (`too_small`, `too_big`, `invalid_type`,\n * `invalid_format`, `custom`...). `message` is either one of\n * `VALIDATION_MESSAGE` or, for a plain bound, Zod's own English text, which the\n * client ignores in favour of `code` plus `minimum` / `maximum`.\n */\nexport const validationIssueSchema = z.object({\n /** Set by the server on every issue it sends; absent on a client's own parse. */\n location: requestPartSchema.optional(),\n /**\n * Where the problem is, RELATIVE TO `location`: `['venture', 'name']`,\n * `['options', 2, 'label']`, `['contentId']`.\n */\n path: z.array(z.union([z.string(), z.number()])),\n code: z.string(),\n message: z.string(),\n /** `too_small`: the bound that was missed. */\n minimum: z.number().optional(),\n /** `too_big`: the bound that was passed. */\n maximum: z.number().optional(),\n /** What was measured: `string`, `array`, `number`... */\n origin: z.string().optional(),\n /** `invalid_format`: which format, e.g. `url`, `regex`, `email`. */\n format: z.string().optional(),\n});\nexport type ValidationIssue = z.infer<typeof validationIssueSchema>;\n\n/** The loosest shape this needs from a Zod issue, so it works across 4.x. */\ninterface ZodIssueLike {\n path: readonly PropertyKey[];\n code: string;\n message: string;\n minimum?: unknown;\n maximum?: unknown;\n origin?: unknown;\n format?: unknown;\n}\n\nconst toNumber = (v: unknown): number | undefined =>\n typeof v === 'number' ? v : typeof v === 'bigint' ? Number(v) : undefined;\n\n/**\n * A Zod error as the wire shape. Use this on the server for every 400, and on\n * the client to treat its own errors and the server's the same way.\n *\n * `location` is stamped on every issue when given; the server always gives it.\n *\n * Copies a WHITELIST of fields (see the module note). A path segment that is a\n * symbol, which JSON cannot carry, becomes its description.\n */\nexport function toValidationIssues(\n error: { issues: readonly ZodIssueLike[] },\n location?: RequestPart,\n): ValidationIssue[] {\n return error.issues.map((issue) => {\n const out: ValidationIssue = {\n ...(location ? { location } : {}),\n path: issue.path.map((p) =>\n typeof p === 'number' ? p : typeof p === 'string' ? p : String(p.description ?? ''),\n ),\n code: issue.code,\n message: issue.message,\n };\n const minimum = toNumber(issue.minimum);\n const maximum = toNumber(issue.maximum);\n if (minimum !== undefined) out.minimum = minimum;\n if (maximum !== undefined) out.maximum = maximum;\n if (typeof issue.origin === 'string') out.origin = issue.origin;\n if (typeof issue.format === 'string') out.format = issue.format;\n return out;\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/core/validation.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * founder-schema — How a validation failure is described, on both sides\n *\n * TWO RULES, and they are what let the web client show any error, its own or\n * the server's, on the right field in the member's language:\n *\n * 1. A CUSTOM MESSAGE IS A KEY, NEVER A SENTENCE. Where a schema needs its own\n * message (a refine, a regex), the message is one of `VALIDATION_MESSAGE`,\n * such as `phoneInvalid`. The client turns the key into text in English or\n * Hindi. Before 0.2.0 these were English sentences, so a Hindi screen showed\n * an English error, and a client that wanted to translate one had to match\n * on the wording.\n *\n * Plain bounds carry NO custom message. `.min(1)` and `.max(80)` produce\n * Zod's own `too_small` / `too_big` issues, which already say which bound\n * and what it was (`minimum`, `maximum`, `origin`), and the client words\n * them from those. A hand-written \"at most 80 characters\" would only\n * restate the number in one language.\n *\n * 2. THE SERVER SENDS EVERY ISSUE, IN THIS SHAPE. A 400 answers with\n * `error.details.issues`, built by `toValidationIssues`, so the client can\n * put each one under its field. Until 0.2.0 the backend sent only the\n * first issue's message, with no path at all.\n *\n * ⚠️ `toValidationIssues` COPIES A WHITELIST OF FIELDS, it never spreads an\n * issue. Zod can carry the rejected `input` on an issue, and these bodies hold a\n * date of birth and a phone number. Echoing them into an error response sends\n * them back to the client, into logs and into any proxy in between. A test\n * proves `input` never survives.\n *\n * @module founder-schema/core/validation\n */\n\nimport { z } from 'zod';\n\n/**\n * Every custom message a schema in this package can produce. Each is a\n * translation key the clients define in every language they ship.\n *\n * ⚠️ Append only, and add the translation in the same change. Renaming one\n * shows the raw key to every member until each client ships the new name.\n */\nexport const VALIDATION_MESSAGE = {\n /** Not a 24-hex Mongo id. */\n invalidId: 'invalidId',\n /** Handle: wrong characters, or a dot in the wrong place. */\n usernameInvalid: 'usernameInvalid',\n /** Not a 10-digit Indian mobile number. */\n phoneInvalid: 'phoneInvalid',\n /** Not a `YYYY-MM-DD` date. */\n dateInvalid: 'dateInvalid',\n /** A date that does not exist, such as 30 February. */\n dobInvalid: 'dobInvalid',\n /** Under the minimum age. */\n dobUnderAge: 'dobUnderAge',\n /** Over the sanity ceiling: almost always a mistyped year. */\n dobTooOld: 'dobTooOld',\n /** A founding year after this year. */\n foundedYearFuture: 'foundedYearFuture',\n /** An upload key that is not one this product mints. */\n uploadKeyInvalid: 'uploadKeyInvalid',\n /** The same uploaded image attached twice to one post. */\n mediaDuplicate: 'mediaDuplicate',\n /** A post with neither text nor media. */\n postEmpty: 'postEmpty',\n /** Two poll options with the same label. */\n pollOptionsDuplicate: 'pollOptionsDuplicate',\n /** An empty string sent to clear a patch field; clearing is `null`. */\n clearWithNull: 'clearWithNull',\n /** Not a plain http(s) URL on a real domain (`javascript:`, `localhost`, an IP...). */\n urlInvalid: 'urlInvalid',\n /** A handle this product keeps for itself (`admin`, `support`...). */\n usernameReserved: 'usernameReserved',\n /** Agreed to a version of the guidelines that is not the current one. */\n guidelinesOutdated: 'guidelinesOutdated',\n /** An MPIN that is not exactly six digits. */\n mpinInvalid: 'mpinInvalid',\n /** The MPIN and its confirmation differ. */\n mpinMismatch: 'mpinMismatch',\n /** Switching to a staff role without the MPIN. */\n mpinRequired: 'mpinRequired',\n /** The same role twice in one list. */\n rolesDuplicate: 'rolesDuplicate',\n /** An ask (\"testers wanted\") on a post or poll that names no product. */\n askWithoutProduct: 'askWithoutProduct',\n /** An App Store link that is not on apps.apple.com. */\n appStoreLinkHost: 'appStoreLinkHost',\n /** A Play Store link that is not on play.google.com. */\n playStoreLinkHost: 'playStoreLinkHost',\n /** A GitHub link that is not on github.com. */\n githubLinkHost: 'githubLinkHost',\n /** A demo video that is not a YouTube link. Video is shared, never uploaded. */\n videoLinkHost: 'videoLinkHost',\n /** The same screenshot twice in one gallery. */\n screenshotsDuplicate: 'screenshotsDuplicate',\n /** The same platform twice in one list. */\n platformsDuplicate: 'platformsDuplicate',\n /** The same topic twice in one list (0.9.0). */\n topicsDuplicate: 'topicsDuplicate',\n /** A post or poll in a community naming a product or an ask (0.9.0): community content carries neither. */\n communityWithProduct: 'communityWithProduct',\n /** The same capability twice in one list (0.9.0). */\n capabilitiesDuplicate: 'capabilitiesDuplicate',\n} as const;\nexport type ValidationMessageKey = (typeof VALIDATION_MESSAGE)[keyof typeof VALIDATION_MESSAGE];\n\n/**\n * Which part of the request an issue is about.\n *\n * ONE PATH CONVENTION: `path` is ALWAYS relative to this part, so a body field\n * reads `['venture', 'name']` with `location: 'body'`, never\n * `['body', 'venture', 'name']`. A form maps body issues straight onto its\n * fields without stripping a prefix, and a client that sees `query` or\n * `params` knows the problem is the URL, not anything the member typed.\n */\nexport const REQUEST_PART_VALUES = ['body', 'query', 'params'] as const;\nexport const requestPartSchema = z.enum(REQUEST_PART_VALUES);\nexport type RequestPart = z.infer<typeof requestPartSchema>;\n\n/**\n * One problem with one field, as it crosses the wire.\n *\n * `code` is Zod's issue code (`too_small`, `too_big`, `invalid_type`,\n * `invalid_format`, `custom`...). `message` is either one of\n * `VALIDATION_MESSAGE` or, for a plain bound, Zod's own English text, which the\n * client ignores in favour of `code` plus `minimum` / `maximum`.\n */\nexport const validationIssueSchema = z.object({\n /** Set by the server on every issue it sends; absent on a client's own parse. */\n location: requestPartSchema.optional(),\n /**\n * Where the problem is, RELATIVE TO `location`: `['venture', 'name']`,\n * `['options', 2, 'label']`, `['contentId']`.\n */\n path: z.array(z.union([z.string(), z.number()])),\n code: z.string(),\n message: z.string(),\n /** `too_small`: the bound that was missed. */\n minimum: z.number().optional(),\n /** `too_big`: the bound that was passed. */\n maximum: z.number().optional(),\n /** What was measured: `string`, `array`, `number`... */\n origin: z.string().optional(),\n /** `invalid_format`: which format, e.g. `url`, `regex`, `email`. */\n format: z.string().optional(),\n});\nexport type ValidationIssue = z.infer<typeof validationIssueSchema>;\n\n/** The loosest shape this needs from a Zod issue, so it works across 4.x. */\ninterface ZodIssueLike {\n path: readonly PropertyKey[];\n code: string;\n message: string;\n minimum?: unknown;\n maximum?: unknown;\n origin?: unknown;\n format?: unknown;\n}\n\nconst toNumber = (v: unknown): number | undefined =>\n typeof v === 'number' ? v : typeof v === 'bigint' ? Number(v) : undefined;\n\n/**\n * A Zod error as the wire shape. Use this on the server for every 400, and on\n * the client to treat its own errors and the server's the same way.\n *\n * `location` is stamped on every issue when given; the server always gives it.\n *\n * Copies a WHITELIST of fields (see the module note). A path segment that is a\n * symbol, which JSON cannot carry, becomes its description.\n */\nexport function toValidationIssues(\n error: { issues: readonly ZodIssueLike[] },\n location?: RequestPart,\n): ValidationIssue[] {\n return error.issues.map((issue) => {\n const out: ValidationIssue = {\n ...(location ? { location } : {}),\n path: issue.path.map((p) =>\n typeof p === 'number' ? p : typeof p === 'string' ? p : String(p.description ?? ''),\n ),\n code: issue.code,\n message: issue.message,\n };\n const minimum = toNumber(issue.minimum);\n const maximum = toNumber(issue.maximum);\n if (minimum !== undefined) out.minimum = minimum;\n if (maximum !== undefined) out.maximum = maximum;\n if (typeof issue.origin === 'string') out.origin = issue.origin;\n if (typeof issue.format === 'string') out.format = issue.format;\n return out;\n });\n}\n"]}
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* @module founder-schema/domains/audit
|
|
23
23
|
*/
|
|
24
24
|
import { z } from 'zod';
|
|
25
|
-
export declare const AUDIT_ACTION_VALUES: readonly ["role.granted", "role.revoked", "mpin.cleared", "content.removed", "content.hidden", "content.restored", "report.dismissed", "member.suspended", "member.unsuspended", "ticket.claimed", "ticket.resolved"];
|
|
25
|
+
export declare const AUDIT_ACTION_VALUES: readonly ["role.granted", "role.revoked", "mpin.cleared", "content.removed", "content.hidden", "content.restored", "report.dismissed", "member.suspended", "member.unsuspended", "ticket.claimed", "ticket.resolved", "capability.granted", "capability.revoked", "community.created", "community.visibility_changed", "community.archived", "community.restored", "community.deleted", "community_suggestion.declined", "community.orphaned"];
|
|
26
26
|
export declare const auditActionSchema: z.ZodEnum<{
|
|
27
27
|
"role.granted": "role.granted";
|
|
28
28
|
"role.revoked": "role.revoked";
|
|
@@ -35,11 +35,21 @@ export declare const auditActionSchema: z.ZodEnum<{
|
|
|
35
35
|
"member.unsuspended": "member.unsuspended";
|
|
36
36
|
"ticket.claimed": "ticket.claimed";
|
|
37
37
|
"ticket.resolved": "ticket.resolved";
|
|
38
|
+
"capability.granted": "capability.granted";
|
|
39
|
+
"capability.revoked": "capability.revoked";
|
|
40
|
+
"community.created": "community.created";
|
|
41
|
+
"community.visibility_changed": "community.visibility_changed";
|
|
42
|
+
"community.archived": "community.archived";
|
|
43
|
+
"community.restored": "community.restored";
|
|
44
|
+
"community.deleted": "community.deleted";
|
|
45
|
+
"community_suggestion.declined": "community_suggestion.declined";
|
|
46
|
+
"community.orphaned": "community.orphaned";
|
|
38
47
|
}>;
|
|
39
48
|
export type AuditAction = z.infer<typeof auditActionSchema>;
|
|
40
49
|
/**
|
|
41
|
-
* Who did it. A person, acting as one of their roles;
|
|
42
|
-
* is the only way a superadmin is ever made.
|
|
50
|
+
* Who did it. A person, acting as one of their roles; a server script, which
|
|
51
|
+
* is the only way a superadmin is ever made; or (0.9.0) a member acting in a
|
|
52
|
+
* community they run, who is not staff.
|
|
43
53
|
*/
|
|
44
54
|
export declare const auditActorSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
45
55
|
kind: z.ZodLiteral<"staff">;
|
|
@@ -55,6 +65,10 @@ export declare const auditActorSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
55
65
|
}, z.core.$strip>, z.ZodObject<{
|
|
56
66
|
kind: z.ZodLiteral<"script">;
|
|
57
67
|
name: z.ZodString;
|
|
68
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
69
|
+
kind: z.ZodLiteral<"member">;
|
|
70
|
+
id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
71
|
+
displayName: z.ZodString;
|
|
58
72
|
}, z.core.$strip>], "kind">;
|
|
59
73
|
export type AuditActor = z.infer<typeof auditActorSchema>;
|
|
60
74
|
export declare const auditEntrySchema: z.ZodObject<{
|
|
@@ -73,6 +87,10 @@ export declare const auditEntrySchema: z.ZodObject<{
|
|
|
73
87
|
}, z.core.$strip>, z.ZodObject<{
|
|
74
88
|
kind: z.ZodLiteral<"script">;
|
|
75
89
|
name: z.ZodString;
|
|
90
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
91
|
+
kind: z.ZodLiteral<"member">;
|
|
92
|
+
id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
93
|
+
displayName: z.ZodString;
|
|
76
94
|
}, z.core.$strip>], "kind">;
|
|
77
95
|
action: z.ZodEnum<{
|
|
78
96
|
"role.granted": "role.granted";
|
|
@@ -86,6 +104,15 @@ export declare const auditEntrySchema: z.ZodObject<{
|
|
|
86
104
|
"member.unsuspended": "member.unsuspended";
|
|
87
105
|
"ticket.claimed": "ticket.claimed";
|
|
88
106
|
"ticket.resolved": "ticket.resolved";
|
|
107
|
+
"capability.granted": "capability.granted";
|
|
108
|
+
"capability.revoked": "capability.revoked";
|
|
109
|
+
"community.created": "community.created";
|
|
110
|
+
"community.visibility_changed": "community.visibility_changed";
|
|
111
|
+
"community.archived": "community.archived";
|
|
112
|
+
"community.restored": "community.restored";
|
|
113
|
+
"community.deleted": "community.deleted";
|
|
114
|
+
"community_suggestion.declined": "community_suggestion.declined";
|
|
115
|
+
"community.orphaned": "community.orphaned";
|
|
89
116
|
}>;
|
|
90
117
|
target: z.ZodObject<{
|
|
91
118
|
subject: z.ZodEnum<{
|
|
@@ -104,6 +131,8 @@ export declare const auditEntrySchema: z.ZodObject<{
|
|
|
104
131
|
PlatformSettings: "PlatformSettings";
|
|
105
132
|
Product: "Product";
|
|
106
133
|
Review: "Review";
|
|
134
|
+
Community: "Community";
|
|
135
|
+
CommunitySuggestion: "CommunitySuggestion";
|
|
107
136
|
}>;
|
|
108
137
|
id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
109
138
|
label: z.ZodNullable<z.ZodString>;
|
|
@@ -129,6 +158,10 @@ export declare const auditPageSchema: z.ZodObject<{
|
|
|
129
158
|
}, z.core.$strip>, z.ZodObject<{
|
|
130
159
|
kind: z.ZodLiteral<"script">;
|
|
131
160
|
name: z.ZodString;
|
|
161
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
162
|
+
kind: z.ZodLiteral<"member">;
|
|
163
|
+
id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
164
|
+
displayName: z.ZodString;
|
|
132
165
|
}, z.core.$strip>], "kind">;
|
|
133
166
|
action: z.ZodEnum<{
|
|
134
167
|
"role.granted": "role.granted";
|
|
@@ -142,6 +175,15 @@ export declare const auditPageSchema: z.ZodObject<{
|
|
|
142
175
|
"member.unsuspended": "member.unsuspended";
|
|
143
176
|
"ticket.claimed": "ticket.claimed";
|
|
144
177
|
"ticket.resolved": "ticket.resolved";
|
|
178
|
+
"capability.granted": "capability.granted";
|
|
179
|
+
"capability.revoked": "capability.revoked";
|
|
180
|
+
"community.created": "community.created";
|
|
181
|
+
"community.visibility_changed": "community.visibility_changed";
|
|
182
|
+
"community.archived": "community.archived";
|
|
183
|
+
"community.restored": "community.restored";
|
|
184
|
+
"community.deleted": "community.deleted";
|
|
185
|
+
"community_suggestion.declined": "community_suggestion.declined";
|
|
186
|
+
"community.orphaned": "community.orphaned";
|
|
145
187
|
}>;
|
|
146
188
|
target: z.ZodObject<{
|
|
147
189
|
subject: z.ZodEnum<{
|
|
@@ -160,6 +202,8 @@ export declare const auditPageSchema: z.ZodObject<{
|
|
|
160
202
|
PlatformSettings: "PlatformSettings";
|
|
161
203
|
Product: "Product";
|
|
162
204
|
Review: "Review";
|
|
205
|
+
Community: "Community";
|
|
206
|
+
CommunitySuggestion: "CommunitySuggestion";
|
|
163
207
|
}>;
|
|
164
208
|
id: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
165
209
|
label: z.ZodNullable<z.ZodString>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../../src/domains/audit/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,mBAAmB,
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../../src/domains/audit/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAMxB,eAAO,MAAM,mBAAmB,gbAkDtB,CAAC;AACX,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;EAA8B,CAAC;AAC7D,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAE5D;;;;GAIG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;2BAyB3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAS3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAoC,CAAC;AACjE,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC"}
|
|
@@ -37,10 +37,12 @@ export const AUDIT_ACTION_VALUES = [
|
|
|
37
37
|
'content.removed',
|
|
38
38
|
/**
|
|
39
39
|
* Staff hid a post, poll, comment, product or review: out of every feed and
|
|
40
|
-
* list, restorable. `detail.note`.
|
|
40
|
+
* list, restorable. `detail.note`. Since 0.9.0 also a community's
|
|
41
|
+
* moderators hiding a post, poll or comment in it (actor kind `member`, or
|
|
42
|
+
* staff), with `detail.communityId`.
|
|
41
43
|
*/
|
|
42
44
|
'content.hidden',
|
|
43
|
-
/**
|
|
45
|
+
/** Hidden content (a product or review too) put back, by staff or (0.9.0) a community's moderators. */
|
|
44
46
|
'content.restored',
|
|
45
47
|
/** Staff looked at what was reported and left it up. `detail.reports`: how many closed. */
|
|
46
48
|
'report.dismissed',
|
|
@@ -52,11 +54,34 @@ export const AUDIT_ACTION_VALUES = [
|
|
|
52
54
|
'ticket.claimed',
|
|
53
55
|
/** A support agent closed a ticket as resolved. */
|
|
54
56
|
'ticket.resolved',
|
|
57
|
+
/** A capability was granted (0.9.0). `detail.capability`. */
|
|
58
|
+
'capability.granted',
|
|
59
|
+
/** A capability was taken away (0.9.0). `detail.capability`. */
|
|
60
|
+
'capability.revoked',
|
|
61
|
+
/** A community was created (0.9.0). `detail.visibility`; `detail.suggestionId` when from a suggestion. */
|
|
62
|
+
'community.created',
|
|
63
|
+
/** A community's type changed (0.9.0). `detail.from`, `detail.to`, `detail.exposed`. */
|
|
64
|
+
'community.visibility_changed',
|
|
65
|
+
/** Staff archived a community (0.9.0). */
|
|
66
|
+
'community.archived',
|
|
67
|
+
/** Staff restored an archived community (0.9.0). */
|
|
68
|
+
'community.restored',
|
|
69
|
+
/** Staff deleted a community (0.9.0). */
|
|
70
|
+
'community.deleted',
|
|
71
|
+
/** Staff declined a community suggestion (0.9.0). `detail.reason`. Approving is `community.created`. */
|
|
72
|
+
'community_suggestion.declined',
|
|
73
|
+
/**
|
|
74
|
+
* A community was left with no admin (0.9.0): its last admin's account was
|
|
75
|
+
* deleted where that cannot be refused. Written by the deletion (actor
|
|
76
|
+
* kind `script`); managers then make a member admin.
|
|
77
|
+
*/
|
|
78
|
+
'community.orphaned',
|
|
55
79
|
];
|
|
56
80
|
export const auditActionSchema = z.enum(AUDIT_ACTION_VALUES);
|
|
57
81
|
/**
|
|
58
|
-
* Who did it. A person, acting as one of their roles;
|
|
59
|
-
* is the only way a superadmin is ever made.
|
|
82
|
+
* Who did it. A person, acting as one of their roles; a server script, which
|
|
83
|
+
* is the only way a superadmin is ever made; or (0.9.0) a member acting in a
|
|
84
|
+
* community they run, who is not staff.
|
|
60
85
|
*/
|
|
61
86
|
export const auditActorSchema = z.discriminatedUnion('kind', [
|
|
62
87
|
z.object({
|
|
@@ -72,6 +97,17 @@ export const auditActorSchema = z.discriminatedUnion('kind', [
|
|
|
72
97
|
/** Which script, e.g. `grant-superadmin`. */
|
|
73
98
|
name: z.string(),
|
|
74
99
|
}),
|
|
100
|
+
/**
|
|
101
|
+
* A member who is not acting as staff (0.9.0): a community's admin changing
|
|
102
|
+
* its type, say. Its own kind, so nobody reading the log mistakes a
|
|
103
|
+
* community admin for staff.
|
|
104
|
+
*/
|
|
105
|
+
z.object({
|
|
106
|
+
kind: z.literal('member'),
|
|
107
|
+
id: objectIdSchema,
|
|
108
|
+
/** Their name when they did it. */
|
|
109
|
+
displayName: z.string(),
|
|
110
|
+
}),
|
|
75
111
|
]);
|
|
76
112
|
export const auditEntrySchema = z.object({
|
|
77
113
|
id: objectIdSchema,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../../src/domains/audit/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE1D,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,kDAAkD;IAClD,cAAc;IACd,qDAAqD;IACrD,cAAc;IACd,uEAAuE;IACvE,cAAc;IACd,gGAAgG;IAChG,iBAAiB;IACjB
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../../src/domains/audit/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE1D,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,kDAAkD;IAClD,cAAc;IACd,qDAAqD;IACrD,cAAc;IACd,uEAAuE;IACvE,cAAc;IACd,gGAAgG;IAChG,iBAAiB;IACjB;;;;;OAKG;IACH,gBAAgB;IAChB,uGAAuG;IACvG,kBAAkB;IAClB,2FAA2F;IAC3F,kBAAkB;IAClB,gEAAgE;IAChE,kBAAkB;IAClB,+BAA+B;IAC/B,oBAAoB;IACpB,qCAAqC;IACrC,gBAAgB;IAChB,mDAAmD;IACnD,iBAAiB;IACjB,6DAA6D;IAC7D,oBAAoB;IACpB,gEAAgE;IAChE,oBAAoB;IACpB,0GAA0G;IAC1G,mBAAmB;IACnB,wFAAwF;IACxF,8BAA8B;IAC9B,0CAA0C;IAC1C,oBAAoB;IACpB,oDAAoD;IACpD,oBAAoB;IACpB,yCAAyC;IACzC,mBAAmB;IACnB,wGAAwG;IACxG,+BAA+B;IAC/B;;;;OAIG;IACH,oBAAoB;CACZ,CAAC;AACX,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;AAG7D;;;;GAIG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE;IAC3D,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;QACxB,EAAE,EAAE,cAAc;QAClB,qFAAqF;QACrF,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;QACvB,oCAAoC;QACpC,QAAQ,EAAE,UAAU;KACrB,CAAC;IACF,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;QACzB,6CAA6C;QAC7C,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;KACjB,CAAC;IACF;;;;OAIG;IACH,CAAC,CAAC,MAAM,CAAC;QACP,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;QACzB,EAAE,EAAE,cAAc;QAClB,mCAAmC;QACnC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;KACxB,CAAC;CACH,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,MAAM,CAAC;IACvC,EAAE,EAAE,cAAc;IAClB,KAAK,EAAE,gBAAgB;IACvB,MAAM,EAAE,iBAAiB;IACzB,6EAA6E;IAC7E,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,aAAa,EAAE,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC;IAC9F,mFAAmF;IACnF,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACtF,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE;CACrB,CAAC,CAAC;AAGH,MAAM,CAAC,MAAM,eAAe,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAC","sourcesContent":["/**\n * founder-schema: the audit log\n *\n * GET /api/v1/audit staff actions, newest first\n *\n * Every action a staff member takes on a MEMBER or on what a member wrote\n * (roles, MPIN resets, suspensions, moderation decisions, taking and closing\n * tickets) is written here, at the moment it happens, by the server: who did\n * it, AS WHICH ROLE, what they did, to what, and anything that explains it.\n * The support team's own library (saved replies) is not a member's, so its\n * edits are not entries. Entries are never edited or\n * deleted. It is how a dispute (\"who removed my post, and why\") gets an\n * answer, and how a misused staff account is noticed.\n *\n * WHO READS WHAT is the reader's permissions, applied to the query: a\n * superadmin reads every entry; any other staff role reads the entries it\n * wrote itself (its History).\n *\n * The action list grows with the staff features; it never shrinks, because an\n * old entry must still parse.\n *\n * @module founder-schema/domains/audit\n */\n\nimport { z } from 'zod';\nimport { subjectSchema } from '../../access/permissions.js';\nimport { roleSchema } from '../../access/roles.js';\nimport { objectIdSchema } from '../../core/common.js';\nimport { paginatedSchema } from '../../core/responses.js';\n\nexport const AUDIT_ACTION_VALUES = [\n /** A platform role was granted. `detail.role`. */\n 'role.granted',\n /** A platform role was taken away. `detail.role`. */\n 'role.revoked',\n /** Someone's forgotten MPIN was cleared, so they can set a new one. */\n 'mpin.cleared',\n /** Staff removed someone else's post, poll, comment or review (`target.subject` says which). */\n 'content.removed',\n /**\n * Staff hid a post, poll, comment, product or review: out of every feed and\n * list, restorable. `detail.note`. Since 0.9.0 also a community's\n * moderators hiding a post, poll or comment in it (actor kind `member`, or\n * staff), with `detail.communityId`.\n */\n 'content.hidden',\n /** Hidden content (a product or review too) put back, by staff or (0.9.0) a community's moderators. */\n 'content.restored',\n /** Staff looked at what was reported and left it up. `detail.reports`: how many closed. */\n 'report.dismissed',\n /** A member was suspended from the product. `detail.reason`. */\n 'member.suspended',\n /** A suspension was lifted. */\n 'member.unsuspended',\n /** A support agent took a ticket. */\n 'ticket.claimed',\n /** A support agent closed a ticket as resolved. */\n 'ticket.resolved',\n /** A capability was granted (0.9.0). `detail.capability`. */\n 'capability.granted',\n /** A capability was taken away (0.9.0). `detail.capability`. */\n 'capability.revoked',\n /** A community was created (0.9.0). `detail.visibility`; `detail.suggestionId` when from a suggestion. */\n 'community.created',\n /** A community's type changed (0.9.0). `detail.from`, `detail.to`, `detail.exposed`. */\n 'community.visibility_changed',\n /** Staff archived a community (0.9.0). */\n 'community.archived',\n /** Staff restored an archived community (0.9.0). */\n 'community.restored',\n /** Staff deleted a community (0.9.0). */\n 'community.deleted',\n /** Staff declined a community suggestion (0.9.0). `detail.reason`. Approving is `community.created`. */\n 'community_suggestion.declined',\n /**\n * A community was left with no admin (0.9.0): its last admin's account was\n * deleted where that cannot be refused. Written by the deletion (actor\n * kind `script`); managers then make a member admin.\n */\n 'community.orphaned',\n] as const;\nexport const auditActionSchema = z.enum(AUDIT_ACTION_VALUES);\nexport type AuditAction = z.infer<typeof auditActionSchema>;\n\n/**\n * Who did it. A person, acting as one of their roles; a server script, which\n * is the only way a superadmin is ever made; or (0.9.0) a member acting in a\n * community they run, who is not staff.\n */\nexport const auditActorSchema = z.discriminatedUnion('kind', [\n z.object({\n kind: z.literal('staff'),\n id: objectIdSchema,\n /** Their name when they did it. An entry is a record, so it is not renamed later. */\n displayName: z.string(),\n /** The role they were acting as. */\n actingAs: roleSchema,\n }),\n z.object({\n kind: z.literal('script'),\n /** Which script, e.g. `grant-superadmin`. */\n name: z.string(),\n }),\n /**\n * A member who is not acting as staff (0.9.0): a community's admin changing\n * its type, say. Its own kind, so nobody reading the log mistakes a\n * community admin for staff.\n */\n z.object({\n kind: z.literal('member'),\n id: objectIdSchema,\n /** Their name when they did it. */\n displayName: z.string(),\n }),\n]);\nexport type AuditActor = z.infer<typeof auditActorSchema>;\n\nexport const auditEntrySchema = z.object({\n id: objectIdSchema,\n actor: auditActorSchema,\n action: auditActionSchema,\n /** What it was done to. `label` is its name at the time, when it has one. */\n target: z.object({ subject: subjectSchema, id: objectIdSchema, label: z.string().nullable() }),\n /** The specifics, by action (the role granted, a reason given). Never a secret. */\n detail: z.record(z.string(), z.union([z.string(), z.number(), z.boolean(), z.null()])),\n at: z.iso.datetime(),\n});\nexport type AuditEntry = z.infer<typeof auditEntrySchema>;\n\nexport const auditPageSchema = paginatedSchema(auditEntrySchema);\nexport type AuditPage = z.infer<typeof auditPageSchema>;\n"]}
|