gymmonk-schema 0.18.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chat.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/messages.d.ts +13 -0
- package/dist/messages.d.ts.map +1 -1
- package/dist/messages.js +7 -0
- package/dist/messages.js.map +1 -1
- package/dist/social.js.map +1 -1
- package/package.json +1 -1
package/dist/chat.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../src/chat.ts"],"names":[],"mappings":"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","sourcesContent":["/**\r\n * gymmonk-schema — Chat\r\n * =====================\r\n * THE wire contract between `gymmonk-web-client` and `gymmonk-chat-server`, and\r\n * the only place either is allowed to name a socket event.\r\n *\r\n * It lives here rather than in the chat server because three codebases need it\r\n * and none of them owns the other two: the client emits, the chat server\r\n * handles, and gymmonk-backend mints the tokens that let the two meet. An event\r\n * name that differs by one character between any pair of them produces a\r\n * message nobody receives and no error anywhere, which is exactly the class of\r\n * bug a shared package exists to make impossible.\r\n *\r\n * @module gymmonk-schema/chat\r\n */\r\n\r\nimport { z } from 'zod';\r\nimport { isoDateTimeSchema, objectIdSchema } from './common.js';\r\n\r\n// ─── Socket events ───────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Every socket event name.\r\n *\r\n * String literals rather than an enum, so both sides put the identical bytes on\r\n * the wire without either importing the other's runtime.\r\n */\r\nexport const CHAT_SOCKET_EVENTS = {\r\n /** Server → client, on connect. The socket is up and awaiting auth. */\r\n HELLO: 'hello',\r\n /** Client → server. `{ token }`. Must arrive inside the auth window. */\r\n AUTH: 'auth',\r\n /** Server → client. `{ userId }` on success. */\r\n AUTH_OK: 'auth:ok',\r\n /** Server → client. `{ code, message }` for any failure, auth or otherwise. */\r\n ERROR: 'error',\r\n\r\n /** Client → server. Open a conversation and start receiving it live. */\r\n CONVERSATION_JOIN: 'conversation:join',\r\n /** Client → server. Leave it (navigated away). */\r\n CONVERSATION_LEAVE: 'conversation:leave',\r\n\r\n /** Client → server. `{ conversationId, clientMessageId, text }`. */\r\n MESSAGE_SEND: 'message:send',\r\n /** Server → everyone in the room, and the recipients' own rooms. */\r\n MESSAGE_NEW: 'message:new',\r\n /** Server → sender only. Their send, acknowledged and stored. */\r\n MESSAGE_SENT: 'message:sent',\r\n /** Client → server. Mark everything up to now as read. */\r\n MESSAGE_READ: 'message:read',\r\n /** Server → the room. `{ conversationId, userId, readAt }`. */\r\n MESSAGE_READ_BY: 'message:read-by',\r\n\r\n /** Client → server. `{ conversationId, typing }`. */\r\n TYPING: 'typing',\r\n /** Server → the other participants. `{ conversationId, userId, typing }`. */\r\n TYPING_UPDATE: 'typing:update',\r\n\r\n /** Server → the room. Who currently has this conversation open. */\r\n PRESENCE_UPDATE: 'presence:update',\r\n} as const;\r\n\r\nexport type ChatSocketEvent = (typeof CHAT_SOCKET_EVENTS)[keyof typeof CHAT_SOCKET_EVENTS];\r\n\r\n// ─── Error codes ─────────────────────────────────────────────────────────────\r\n\r\n/** Stable codes on the `error` event. The client branches on these; the message is for logs. */\r\nexport const CHAT_ERROR = {\r\n AUTH_TIMEOUT: 'AUTH_TIMEOUT',\r\n AUTH_FAILED: 'AUTH_FAILED',\r\n NOT_AUTHENTICATED: 'NOT_AUTHENTICATED',\r\n NOT_A_PARTICIPANT: 'NOT_A_PARTICIPANT',\r\n CONVERSATION_NOT_FOUND: 'CONVERSATION_NOT_FOUND',\r\n INVALID_PAYLOAD: 'INVALID_PAYLOAD',\r\n RATE_LIMITED: 'RATE_LIMITED',\r\n SERVER_ERROR: 'SERVER_ERROR',\r\n} as const;\r\n\r\nexport type ChatErrorCode = (typeof CHAT_ERROR)[keyof typeof CHAT_ERROR];\r\n\r\n// ─── Limits ──────────────────────────────────────────────────────────────────\r\n\r\n/** Longest message body accepted. Matches the column cap in the model. */\r\nexport const MAX_MESSAGE_LENGTH = 4000;\r\n\r\n/** Messages per page of history. */\r\nexport const MESSAGE_PAGE_SIZE = 50;\r\n\r\n/**\r\n * Messages one socket may send per minute.\r\n *\r\n * Not an anti-abuse boundary on its own: a member can only ever write to a\r\n * conversation their own backend authorised. This is what stops a broken client\r\n * looping a send from filling a collection.\r\n */\r\nexport const MESSAGE_RATE_PER_MINUTE = 60;\r\n\r\n/** How long an unauthenticated socket may hold a connection, in ms. */\r\nexport const CHAT_AUTH_TIMEOUT_MS = 10_000;\r\n\r\n/**\r\n * How long a typing indicator survives without a refresh, in ms.\r\n *\r\n * Someone who closes the app mid-sentence never sends `typing: false`, so the\r\n * indicator has to expire on its own or it stays on screen forever. The sender\r\n * re-emits well inside this window while they are still typing.\r\n */\r\nexport const TYPING_TIMEOUT_MS = 4_000;\r\n\r\n/** How often the sender re-emits `typing: true` while typing, in ms. */\r\nexport const TYPING_THROTTLE_MS = 2_000;\r\n\r\n// ─── Wire shapes ─────────────────────────────────────────────────────────────\r\n\r\n/** A participant, denormalised onto the conversation so a list renders without joins. */\r\nexport const chatParticipantSchema = z.object({\r\n userId: objectIdSchema,\r\n displayName: z.string(),\r\n avatarUrl: z.string().nullable(),\r\n});\r\nexport type ChatParticipant = z.infer<typeof chatParticipantSchema>;\r\n\r\n/** A message as every client sees it. */\r\nexport const chatMessageSchema = z.object({\r\n id: objectIdSchema,\r\n conversationId: objectIdSchema,\r\n senderId: objectIdSchema,\r\n text: z.string(),\r\n /** Echoed back so the sender can reconcile its optimistic row. */\r\n clientMessageId: z.string(),\r\n sentAt: isoDateTimeSchema,\r\n readAt: isoDateTimeSchema.nullable(),\r\n /** Deleted for everyone. `text` is then empty. */\r\n deleted: z.boolean(),\r\n});\r\nexport type ChatMessage = z.infer<typeof chatMessageSchema>;\r\n\r\n/** The one-line preview a conversation row shows. */\r\nexport const chatLastMessageSchema = z.object({\r\n text: z.string(),\r\n senderId: objectIdSchema,\r\n sentAt: isoDateTimeSchema,\r\n});\r\nexport type ChatLastMessage = z.infer<typeof chatLastMessageSchema>;\r\n\r\n/**\r\n * A conversation as the list screen sees it.\r\n *\r\n * `unreadCount` is the CALLER's, not a global figure: the same row means\r\n * different things to the two people in it.\r\n */\r\nexport const chatConversationSchema = z.object({\r\n id: objectIdSchema,\r\n participants: z.array(chatParticipantSchema),\r\n lastMessage: chatLastMessageSchema.nullable(),\r\n unreadCount: z.number().int().nonnegative(),\r\n createdAt: isoDateTimeSchema,\r\n /** Last activity. The inbox sorts on this, newest first. */\r\n updatedAt: isoDateTimeSchema,\r\n /**\r\n * Closed to new messages. True once one participant blocks the other.\r\n *\r\n * History still reads: freezing stops the next message, and deleting what was\r\n * already said is a different and much larger decision. The client uses this\r\n * to disable its composer rather than letting a send fail at the server.\r\n *\r\n * RECOVERED from the published 0.17.0 `.d.ts`, not from its sourcemap — that\r\n * map's `sourcesContent` predates this field and dropped it silently.\r\n */\r\n frozen: z.boolean(),\r\n});\r\nexport type ChatConversation = z.infer<typeof chatConversationSchema>;\r\n\r\n// ─── Event payloads ──────────────────────────────────────────────────────────\r\n\r\nexport interface ChatAuthPayload {\r\n token: string;\r\n}\r\n\r\nexport interface ChatConversationRoomPayload {\r\n conversationId: string;\r\n}\r\n\r\nexport interface ChatMessageSendPayload {\r\n conversationId: string;\r\n /** Client-generated idempotency key. A retry with the same value is not a second message. */\r\n clientMessageId: string;\r\n text: string;\r\n}\r\n\r\nexport interface ChatMessageReadPayload {\r\n conversationId: string;\r\n}\r\n\r\nexport interface ChatTypingPayload {\r\n conversationId: string;\r\n typing: boolean;\r\n}\r\n\r\n// ─── Typed event maps ────────────────────────────────────────────────────────\r\n// Socket.io takes these as generics on both ends, so an event emitted with the\r\n// wrong payload is a compile error rather than a message nobody receives.\r\n\r\nexport interface ChatServerToClientEvents {\r\n [CHAT_SOCKET_EVENTS.HELLO]: (payload: { message: string }) => void;\r\n [CHAT_SOCKET_EVENTS.AUTH_OK]: (payload: { userId: string }) => void;\r\n [CHAT_SOCKET_EVENTS.ERROR]: (payload: { code: string; message: string }) => void;\r\n [CHAT_SOCKET_EVENTS.MESSAGE_NEW]: (message: ChatMessage) => void;\r\n [CHAT_SOCKET_EVENTS.MESSAGE_SENT]: (payload: {\r\n clientMessageId: string;\r\n message: ChatMessage;\r\n }) => void;\r\n [CHAT_SOCKET_EVENTS.MESSAGE_READ_BY]: (payload: {\r\n conversationId: string;\r\n userId: string;\r\n readAt: string;\r\n }) => void;\r\n [CHAT_SOCKET_EVENTS.TYPING_UPDATE]: (payload: {\r\n conversationId: string;\r\n userId: string;\r\n typing: boolean;\r\n }) => void;\r\n [CHAT_SOCKET_EVENTS.PRESENCE_UPDATE]: (payload: {\r\n conversationId: string;\r\n userIds: string[];\r\n }) => void;\r\n}\r\n\r\nexport interface ChatClientToServerEvents {\r\n [CHAT_SOCKET_EVENTS.AUTH]: (payload: ChatAuthPayload) => void;\r\n [CHAT_SOCKET_EVENTS.CONVERSATION_JOIN]: (payload: ChatConversationRoomPayload) => void;\r\n [CHAT_SOCKET_EVENTS.CONVERSATION_LEAVE]: (payload: ChatConversationRoomPayload) => void;\r\n [CHAT_SOCKET_EVENTS.MESSAGE_SEND]: (payload: ChatMessageSendPayload) => void;\r\n [CHAT_SOCKET_EVENTS.MESSAGE_READ]: (payload: ChatMessageReadPayload) => void;\r\n [CHAT_SOCKET_EVENTS.TYPING]: (payload: ChatTypingPayload) => void;\r\n}\r\n\r\n// ─── Backend bridge ──────────────────────────────────────────────────────────\r\n\r\n/** `GET /api/v1/chat/config` — where chat lives, whether it is on, and who the caller is. */\r\nexport const chatConfigSchema = z.object({\r\n enabled: z.boolean(),\r\n url: z.string().nullable(),\r\n /**\r\n * The caller's id AS CHAT-SERVER WILL SEE IT.\r\n *\r\n * Sourced from the same place that mints the token rather than from the\r\n * client session, because the two must agree or \"is this message mine\" is\r\n * wrong for somebody.\r\n */\r\n userId: objectIdSchema,\r\n});\r\nexport type ChatConfig = z.infer<typeof chatConfigSchema>;\r\n\r\n/** `POST /api/v1/chat/auth-token` */\r\nexport const chatAuthTokenSchema = z.object({\r\n token: z.string(),\r\n expiresAt: isoDateTimeSchema,\r\n /** Seconds, so a client can schedule a refresh without parsing a date. */\r\n expiresIn: z.number().int().positive(),\r\n});\r\nexport type ChatAuthToken = z.infer<typeof chatAuthTokenSchema>;\r\n\r\n/** `POST /api/v1/chat/invitation` — permission to open a conversation with someone. */\r\nexport const chatInvitationBodySchema = z.object({\r\n userId: objectIdSchema,\r\n});\r\nexport type ChatInvitationBody = z.infer<typeof chatInvitationBodySchema>;\r\n\r\nexport const chatInvitationSchema = z.object({\r\n invitationToken: z.string(),\r\n expiresAt: isoDateTimeSchema,\r\n});\r\nexport type ChatInvitation = z.infer<typeof chatInvitationSchema>;\r\n\r\n/** `POST /api/v1/chat/notify` — chat-server telling the backend to notify people. */\r\nexport const chatNotifyBodySchema = z.object({\r\n recipientIds: z.array(objectIdSchema).min(1),\r\n conversationId: objectIdSchema,\r\n senderId: objectIdSchema,\r\n senderName: z.string(),\r\n text: z.string(),\r\n});\r\nexport type ChatNotifyBody = z.infer<typeof chatNotifyBodySchema>;\r\n\r\n/** JWT `aud` on every chat token. chat-server accepts exactly this. */\r\nexport const CHAT_TOKEN_AUDIENCE = 'gymmonk-chat-server';\r\n\r\n/** `purpose` claim marking an invitation. Auth tokens carry no purpose. */\r\nexport const CHAT_INVITATION_PURPOSE = 'invitation';\r\n"]}
|
|
1
|
+
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../src/chat.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gymmonk-schema — Chat\n * =====================\n * THE wire contract between `gymmonk-web-client` and `gymmonk-chat-server`, and\n * the only place either is allowed to name a socket event.\n *\n * It lives here rather than in the chat server because three codebases need it\n * and none of them owns the other two: the client emits, the chat server\n * handles, and gymmonk-backend mints the tokens that let the two meet. An event\n * name that differs by one character between any pair of them produces a\n * message nobody receives and no error anywhere, which is exactly the class of\n * bug a shared package exists to make impossible.\n *\n * @module gymmonk-schema/chat\n */\n\nimport { z } from 'zod';\nimport { isoDateTimeSchema, objectIdSchema } from './common.js';\n\n// ─── Socket events ───────────────────────────────────────────────────────────\n\n/**\n * Every socket event name.\n *\n * String literals rather than an enum, so both sides put the identical bytes on\n * the wire without either importing the other's runtime.\n */\nexport const CHAT_SOCKET_EVENTS = {\n /** Server → client, on connect. The socket is up and awaiting auth. */\n HELLO: 'hello',\n /** Client → server. `{ token }`. Must arrive inside the auth window. */\n AUTH: 'auth',\n /** Server → client. `{ userId }` on success. */\n AUTH_OK: 'auth:ok',\n /** Server → client. `{ code, message }` for any failure, auth or otherwise. */\n ERROR: 'error',\n\n /** Client → server. Open a conversation and start receiving it live. */\n CONVERSATION_JOIN: 'conversation:join',\n /** Client → server. Leave it (navigated away). */\n CONVERSATION_LEAVE: 'conversation:leave',\n\n /** Client → server. `{ conversationId, clientMessageId, text }`. */\n MESSAGE_SEND: 'message:send',\n /** Server → everyone in the room, and the recipients' own rooms. */\n MESSAGE_NEW: 'message:new',\n /** Server → sender only. Their send, acknowledged and stored. */\n MESSAGE_SENT: 'message:sent',\n /** Client → server. Mark everything up to now as read. */\n MESSAGE_READ: 'message:read',\n /** Server → the room. `{ conversationId, userId, readAt }`. */\n MESSAGE_READ_BY: 'message:read-by',\n\n /** Client → server. `{ conversationId, typing }`. */\n TYPING: 'typing',\n /** Server → the other participants. `{ conversationId, userId, typing }`. */\n TYPING_UPDATE: 'typing:update',\n\n /** Server → the room. Who currently has this conversation open. */\n PRESENCE_UPDATE: 'presence:update',\n} as const;\n\nexport type ChatSocketEvent = (typeof CHAT_SOCKET_EVENTS)[keyof typeof CHAT_SOCKET_EVENTS];\n\n// ─── Error codes ─────────────────────────────────────────────────────────────\n\n/** Stable codes on the `error` event. The client branches on these; the message is for logs. */\nexport const CHAT_ERROR = {\n AUTH_TIMEOUT: 'AUTH_TIMEOUT',\n AUTH_FAILED: 'AUTH_FAILED',\n NOT_AUTHENTICATED: 'NOT_AUTHENTICATED',\n NOT_A_PARTICIPANT: 'NOT_A_PARTICIPANT',\n CONVERSATION_NOT_FOUND: 'CONVERSATION_NOT_FOUND',\n INVALID_PAYLOAD: 'INVALID_PAYLOAD',\n RATE_LIMITED: 'RATE_LIMITED',\n SERVER_ERROR: 'SERVER_ERROR',\n} as const;\n\nexport type ChatErrorCode = (typeof CHAT_ERROR)[keyof typeof CHAT_ERROR];\n\n// ─── Limits ──────────────────────────────────────────────────────────────────\n\n/** Longest message body accepted. Matches the column cap in the model. */\nexport const MAX_MESSAGE_LENGTH = 4000;\n\n/** Messages per page of history. */\nexport const MESSAGE_PAGE_SIZE = 50;\n\n/**\n * Messages one socket may send per minute.\n *\n * Not an anti-abuse boundary on its own: a member can only ever write to a\n * conversation their own backend authorised. This is what stops a broken client\n * looping a send from filling a collection.\n */\nexport const MESSAGE_RATE_PER_MINUTE = 60;\n\n/** How long an unauthenticated socket may hold a connection, in ms. */\nexport const CHAT_AUTH_TIMEOUT_MS = 10_000;\n\n/**\n * How long a typing indicator survives without a refresh, in ms.\n *\n * Someone who closes the app mid-sentence never sends `typing: false`, so the\n * indicator has to expire on its own or it stays on screen forever. The sender\n * re-emits well inside this window while they are still typing.\n */\nexport const TYPING_TIMEOUT_MS = 4_000;\n\n/** How often the sender re-emits `typing: true` while typing, in ms. */\nexport const TYPING_THROTTLE_MS = 2_000;\n\n// ─── Wire shapes ─────────────────────────────────────────────────────────────\n\n/** A participant, denormalised onto the conversation so a list renders without joins. */\nexport const chatParticipantSchema = z.object({\n userId: objectIdSchema,\n displayName: z.string(),\n avatarUrl: z.string().nullable(),\n});\nexport type ChatParticipant = z.infer<typeof chatParticipantSchema>;\n\n/** A message as every client sees it. */\nexport const chatMessageSchema = z.object({\n id: objectIdSchema,\n conversationId: objectIdSchema,\n senderId: objectIdSchema,\n text: z.string(),\n /** Echoed back so the sender can reconcile its optimistic row. */\n clientMessageId: z.string(),\n sentAt: isoDateTimeSchema,\n readAt: isoDateTimeSchema.nullable(),\n /** Deleted for everyone. `text` is then empty. */\n deleted: z.boolean(),\n});\nexport type ChatMessage = z.infer<typeof chatMessageSchema>;\n\n/** The one-line preview a conversation row shows. */\nexport const chatLastMessageSchema = z.object({\n text: z.string(),\n senderId: objectIdSchema,\n sentAt: isoDateTimeSchema,\n});\nexport type ChatLastMessage = z.infer<typeof chatLastMessageSchema>;\n\n/**\n * A conversation as the list screen sees it.\n *\n * `unreadCount` is the CALLER's, not a global figure: the same row means\n * different things to the two people in it.\n */\nexport const chatConversationSchema = z.object({\n id: objectIdSchema,\n participants: z.array(chatParticipantSchema),\n lastMessage: chatLastMessageSchema.nullable(),\n unreadCount: z.number().int().nonnegative(),\n createdAt: isoDateTimeSchema,\n /** Last activity. The inbox sorts on this, newest first. */\n updatedAt: isoDateTimeSchema,\n /**\n * Closed to new messages. True once one participant blocks the other.\n *\n * History still reads: freezing stops the next message, and deleting what was\n * already said is a different and much larger decision. The client uses this\n * to disable its composer rather than letting a send fail at the server.\n *\n * RECOVERED from the published 0.17.0 `.d.ts`, not from its sourcemap — that\n * map's `sourcesContent` predates this field and dropped it silently.\n */\n frozen: z.boolean(),\n});\nexport type ChatConversation = z.infer<typeof chatConversationSchema>;\n\n// ─── Event payloads ──────────────────────────────────────────────────────────\n\nexport interface ChatAuthPayload {\n token: string;\n}\n\nexport interface ChatConversationRoomPayload {\n conversationId: string;\n}\n\nexport interface ChatMessageSendPayload {\n conversationId: string;\n /** Client-generated idempotency key. A retry with the same value is not a second message. */\n clientMessageId: string;\n text: string;\n}\n\nexport interface ChatMessageReadPayload {\n conversationId: string;\n}\n\nexport interface ChatTypingPayload {\n conversationId: string;\n typing: boolean;\n}\n\n// ─── Typed event maps ────────────────────────────────────────────────────────\n// Socket.io takes these as generics on both ends, so an event emitted with the\n// wrong payload is a compile error rather than a message nobody receives.\n\nexport interface ChatServerToClientEvents {\n [CHAT_SOCKET_EVENTS.HELLO]: (payload: { message: string }) => void;\n [CHAT_SOCKET_EVENTS.AUTH_OK]: (payload: { userId: string }) => void;\n [CHAT_SOCKET_EVENTS.ERROR]: (payload: { code: string; message: string }) => void;\n [CHAT_SOCKET_EVENTS.MESSAGE_NEW]: (message: ChatMessage) => void;\n [CHAT_SOCKET_EVENTS.MESSAGE_SENT]: (payload: {\n clientMessageId: string;\n message: ChatMessage;\n }) => void;\n [CHAT_SOCKET_EVENTS.MESSAGE_READ_BY]: (payload: {\n conversationId: string;\n userId: string;\n readAt: string;\n }) => void;\n [CHAT_SOCKET_EVENTS.TYPING_UPDATE]: (payload: {\n conversationId: string;\n userId: string;\n typing: boolean;\n }) => void;\n [CHAT_SOCKET_EVENTS.PRESENCE_UPDATE]: (payload: {\n conversationId: string;\n userIds: string[];\n }) => void;\n}\n\nexport interface ChatClientToServerEvents {\n [CHAT_SOCKET_EVENTS.AUTH]: (payload: ChatAuthPayload) => void;\n [CHAT_SOCKET_EVENTS.CONVERSATION_JOIN]: (payload: ChatConversationRoomPayload) => void;\n [CHAT_SOCKET_EVENTS.CONVERSATION_LEAVE]: (payload: ChatConversationRoomPayload) => void;\n [CHAT_SOCKET_EVENTS.MESSAGE_SEND]: (payload: ChatMessageSendPayload) => void;\n [CHAT_SOCKET_EVENTS.MESSAGE_READ]: (payload: ChatMessageReadPayload) => void;\n [CHAT_SOCKET_EVENTS.TYPING]: (payload: ChatTypingPayload) => void;\n}\n\n// ─── Backend bridge ──────────────────────────────────────────────────────────\n\n/** `GET /api/v1/chat/config` — where chat lives, whether it is on, and who the caller is. */\nexport const chatConfigSchema = z.object({\n enabled: z.boolean(),\n url: z.string().nullable(),\n /**\n * The caller's id AS CHAT-SERVER WILL SEE IT.\n *\n * Sourced from the same place that mints the token rather than from the\n * client session, because the two must agree or \"is this message mine\" is\n * wrong for somebody.\n */\n userId: objectIdSchema,\n});\nexport type ChatConfig = z.infer<typeof chatConfigSchema>;\n\n/** `POST /api/v1/chat/auth-token` */\nexport const chatAuthTokenSchema = z.object({\n token: z.string(),\n expiresAt: isoDateTimeSchema,\n /** Seconds, so a client can schedule a refresh without parsing a date. */\n expiresIn: z.number().int().positive(),\n});\nexport type ChatAuthToken = z.infer<typeof chatAuthTokenSchema>;\n\n/** `POST /api/v1/chat/invitation` — permission to open a conversation with someone. */\nexport const chatInvitationBodySchema = z.object({\n userId: objectIdSchema,\n});\nexport type ChatInvitationBody = z.infer<typeof chatInvitationBodySchema>;\n\nexport const chatInvitationSchema = z.object({\n invitationToken: z.string(),\n expiresAt: isoDateTimeSchema,\n});\nexport type ChatInvitation = z.infer<typeof chatInvitationSchema>;\n\n/** `POST /api/v1/chat/notify` — chat-server telling the backend to notify people. */\nexport const chatNotifyBodySchema = z.object({\n recipientIds: z.array(objectIdSchema).min(1),\n conversationId: objectIdSchema,\n senderId: objectIdSchema,\n senderName: z.string(),\n text: z.string(),\n});\nexport type ChatNotifyBody = z.infer<typeof chatNotifyBodySchema>;\n\n/** JWT `aud` on every chat token. chat-server accepts exactly this. */\nexport const CHAT_TOKEN_AUDIENCE = 'gymmonk-chat-server';\n\n/** `purpose` claim marking an invitation. Auth tokens carry no purpose. */\nexport const CHAT_INVITATION_PURPOSE = 'invitation';\n"]}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,gFAAgF;AAChF,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,6EAA6E;AAC7E,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,gFAAgF;AAChF,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,gFAAgF;AAChF,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,gFAAgF;AAChF,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,gFAAgF;AAChF,cAAc,WAAW,CAAC;AAC1B,gFAAgF;AAChF,cAAc,uBAAuB,CAAC;AACtC,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,gFAAgF;AAChF,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,gFAAgF;AAChF,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,gFAAgF;AAChF,cAAc,eAAe,CAAC;AAC9B,gFAAgF;AAChF,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,gFAAgF;AAChF,cAAc,sBAAsB,CAAC;AACrC,gFAAgF;AAChF,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,gFAAgF;AAChF,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC","sourcesContent":["/**\
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,gFAAgF;AAChF,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,6EAA6E;AAC7E,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC;AAC5B,gFAAgF;AAChF,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,gFAAgF;AAChF,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,gFAAgF;AAChF,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,gFAAgF;AAChF,cAAc,WAAW,CAAC;AAC1B,gFAAgF;AAChF,cAAc,uBAAuB,CAAC;AACtC,cAAc,WAAW,CAAC;AAC1B,cAAc,eAAe,CAAC;AAC9B,gFAAgF;AAChF,cAAc,aAAa,CAAC;AAC5B,cAAc,qBAAqB,CAAC;AACpC,cAAc,iBAAiB,CAAC;AAChC,gFAAgF;AAChF,cAAc,sBAAsB,CAAC;AACrC,cAAc,yBAAyB,CAAC;AACxC,gFAAgF;AAChF,cAAc,eAAe,CAAC;AAC9B,gFAAgF;AAChF,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,gFAAgF;AAChF,cAAc,sBAAsB,CAAC;AACrC,gFAAgF;AAChF,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,gFAAgF;AAChF,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,WAAW,CAAC;AAC1B,cAAc,mBAAmB,CAAC","sourcesContent":["/**\n * gymmonk-schema — Public API\n * ===========================\n * Shared Zod schemas, enums and domain types for GymMonk (fitness SaaS).\n * Single source of truth (SSOT) for data shapes, TypeScript types and\n * validation, consumed by both gymmonk-backend and gymmonk-web-client.\n *\n * Every domain module is re-exported through this barrel. Import names are\n * globally unique across modules, so `export *` composes without collisions.\n */\n\n// ─── Owner desk-entry FORM validation (client-side working shapes) ───────────\nexport * from './add-person-form.js';\nexport * from './announcement.js';\n// ─── Location master data (the address picker's lookup API) ───────────────\nexport * from './area.js';\nexport * from './center.js';\n// ─── Check-in QR (poster payload shared by owner, member and backend) ────────\nexport * from './chat.js';\nexport * from './check-in.js';\n// ─── Foundation ──────────────────────────────────────────────────────────────\nexport * from './common.js';\nexport * from './equipment.js';\n// ─── Training ────────────────────────────────────────────────────────────────\nexport * from './exercise.js';\nexport * from './feedback.js';\n// ─── Uploads ─────────────────────────────────────────────────────────────────\nexport * from './file.js';\n// ─── \"List your gym\" — a member registers the business they run ──────────────\nexport * from './gym-registration.js';\nexport * from './home.js';\nexport * from './insights.js';\n// ─── People ──────────────────────────────────────────────────────────────────\nexport * from './member.js';\nexport * from './member-profile.js';\nexport * from './membership.js';\n// ─── Membership ──────────────────────────────────────────────────────────────\nexport * from './membership-plan.js';\nexport * from './membership-request.js';\n// ─── API result messages + error codes ───────────────────────────────────────\nexport * from './messages.js';\n// ─── Engagement & analytics ──────────────────────────────────────────────────\nexport * from './notification.js';\nexport * from './onboarding.js';\n// ─── Onboarding wizard FORM validation (client-side working shapes) ──────────\nexport * from './onboarding-form.js';\n// ─── Gym structure & onboarding ──────────────────────────────────────────────\nexport * from './organisation.js';\nexport * from './owner-profile.js';\n// ─── Platform console (people + gyms directories, role keys) ─────────────────\nexport * from './platform.js';\nexport * from './pricing.js';\nexport * from './session.js';\nexport * from './shared.js';\nexport * from './social.js';\nexport * from './staff.js';\nexport * from './subscription.js';\nexport * from './user.js';\nexport * from './workout-plan.js';\n"]}
|
package/dist/messages.d.ts
CHANGED
|
@@ -100,9 +100,22 @@ export declare const SUCCESS_MESSAGE: {
|
|
|
100
100
|
readonly workoutPlanCreated: {
|
|
101
101
|
readonly title: "Workout plan created";
|
|
102
102
|
};
|
|
103
|
+
readonly workoutPlanUpdated: {
|
|
104
|
+
readonly title: "Workout plan saved";
|
|
105
|
+
};
|
|
106
|
+
readonly workoutPlanDeleted: {
|
|
107
|
+
readonly title: "Workout plan deleted";
|
|
108
|
+
readonly description: "Members on this plan no longer have one.";
|
|
109
|
+
};
|
|
103
110
|
readonly workoutCreated: {
|
|
104
111
|
readonly title: "Workout created";
|
|
105
112
|
};
|
|
113
|
+
readonly workoutUpdated: {
|
|
114
|
+
readonly title: "Workout saved";
|
|
115
|
+
};
|
|
116
|
+
readonly workoutDeleted: {
|
|
117
|
+
readonly title: "Workout removed";
|
|
118
|
+
};
|
|
106
119
|
readonly workoutPlanAssigned: {
|
|
107
120
|
readonly title: "Workout plan assigned";
|
|
108
121
|
};
|
package/dist/messages.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH;;;;GAIG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;CAwBb,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC;AAIrE,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAID;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;;;;;;IAU1B,sEAAsE
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH;;;;GAIG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;CAwBb,CAAC;AAEX,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAC;AAIrE,wEAAwE;AACxE,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAID;;;GAGG;AACH,eAAO,MAAM,eAAe;;;;;;;;;IAU1B,sEAAsE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgItB,CAAC;AAEnD,MAAM,MAAM,iBAAiB,GAAG,MAAM,OAAO,eAAe,CAAC;AAE7D,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB;iCACV,MAAM,KAAG,aAAa;gCAIvB,MAAM,KAAG,aAAa;sCAIhB,MAAM,KAAG,aAAa;uCAIrB,MAAM,KAAG,aAAa;uCAGtB,MAAM,KAAG,aAAa;uCAItB,MAAM,KAAG,aAAa;CAIxC,CAAC;AAIX;;;;GAIG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;IAiBxB,4EAA4E;;IAE5E,+EAA+E;;IAE/E,gFAAgF;;IAGhF,iDAAiD;;;IAKjD,kDAAkD;;IAElD;;;;OAIG;;IAEH;;;;OAIG;;IAEH,qEAAqE;;IAErE,2EAA2E;;IAE3E,wEAAwE;;;;;;;;;;;IAexE,2EAA2E;;;;;;;;;;;;;;IAkB3E;;;OAGG;;;;IAMH,wEAAwE;;IAExE,uEAAuE;;IAEvE,yEAAyE;;IAEzE,8EAA8E;;IAE9E,0DAA0D;;;IAK1D,4EAA4E;;IAG5E,6DAA6D;;IAE7D,kDAAkD;;IAElD,+DAA+D;;IAE/D,sEAAsE;;;IAItE,kEAAkE;;;;IAIlE,uEAAuE;;IAEvE,8EAA8E;;;;;;;;;;;;;;;;;CAyBrC,CAAC;AAE5C,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,aAAa,CAAC;AAEzD,iDAAiD;AACjD,eAAO,MAAM,iBAAiB;8BACX,MAAM,KAAG,MAAM;IAChC,2DAA2D;sCAClC,MAAM,KAAG,MAAM;CAEhC,CAAC"}
|
package/dist/messages.js
CHANGED
|
@@ -81,7 +81,14 @@ export const SUCCESS_MESSAGE = {
|
|
|
81
81
|
subscriptionUpdated: { title: 'Subscription updated' },
|
|
82
82
|
// Training
|
|
83
83
|
workoutPlanCreated: { title: 'Workout plan created' },
|
|
84
|
+
workoutPlanUpdated: { title: 'Workout plan saved' },
|
|
85
|
+
workoutPlanDeleted: {
|
|
86
|
+
title: 'Workout plan deleted',
|
|
87
|
+
description: 'Members on this plan no longer have one.',
|
|
88
|
+
},
|
|
84
89
|
workoutCreated: { title: 'Workout created' },
|
|
90
|
+
workoutUpdated: { title: 'Workout saved' },
|
|
91
|
+
workoutDeleted: { title: 'Workout removed' },
|
|
85
92
|
workoutPlanAssigned: { title: 'Workout plan assigned' },
|
|
86
93
|
// Roster (owner console)
|
|
87
94
|
attendanceMarked: { title: 'Attendance saved' },
|
package/dist/messages.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,gFAAgF;AAEhF;;;;GAIG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,UAAU;IACV,cAAc,EAAE,gBAAgB;IAChC,gBAAgB,EAAE,kBAAkB;IACpC,SAAS,EAAE,WAAW;IAEtB,OAAO;IACP,aAAa,EAAE,eAAe;IAC9B,aAAa,EAAE,eAAe;IAC9B,YAAY,EAAE,cAAc;IAC5B,SAAS,EAAE,WAAW;IACtB,mBAAmB,EAAE,qBAAqB;IAE1C,OAAO;IACP,cAAc,EAAE,gBAAgB;IAChC,mBAAmB,EAAE,qBAAqB;IAC1C,YAAY,EAAE,cAAc;IAE5B,gBAAgB;IAChB,YAAY,EAAE,cAAc;IAC5B,mBAAmB,EAAE,qBAAqB;IAE1C,oCAAoC;IACpC,mBAAmB,EAAE,qBAAqB;CAClC,CAAC;AAYX,gFAAgF;AAEhF;;;GAGG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,aAAa;IACb,wBAAwB,EAAE;QACxB,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,qBAAqB;KACnC;IACD,uBAAuB,EAAE;QACvB,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EAAE,qBAAqB;KACnC;IACD,sEAAsE;IACtE,mBAAmB,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE;IAEvC,qBAAqB;IACrB,cAAc,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE;IAC5C,mBAAmB,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE;IAEtD,gBAAgB;IAChB,WAAW,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE;IACtC,aAAa,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE;IAC1C,qBAAqB,EAAE,EAAE,KAAK,EAAE,yBAAyB,EAAE;IAC3D,qBAAqB,EAAE,EAAE,KAAK,EAAE,yBAAyB,EAAE;IAC3D,cAAc,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE;IAE9C,2BAA2B;IAC3B,iBAAiB,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE;IAClD,iBAAiB,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE;IAClD,mBAAmB,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE;IACtD,wBAAwB,EAAE;QACxB,KAAK,EAAE,oBAAoB;QAC3B,WAAW,EAAE,kCAAkC;KAChD;IAED,0BAA0B;IAC1B,mBAAmB,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE;IAEtD,WAAW;IACX,kBAAkB,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE;IACrD,cAAc,EAAE,EAAE,KAAK,EAAE,iBAAiB,EAAE;IAC5C,mBAAmB,EAAE,EAAE,KAAK,EAAE,uBAAuB,EAAE;IAEvD,yBAAyB;IACzB,gBAAgB,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE;IAC/C,oBAAoB,EAAE;QACpB,KAAK,EAAE,wBAAwB;QAC/B,WAAW,EAAE,8BAA8B;KAC5C;IACD,kBAAkB,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE;IACnD,aAAa,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE;IAC1C,kBAAkB,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE;IAC7C,sBAAsB,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE;IACtD,yBAAyB,EAAE,EAAE,KAAK,EAAE,sBAAsB,EAAE;IAC5D,gBAAgB,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE;IAE/C,WAAW;IACX,SAAS,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,2BAA2B,EAAE;IAC5E,UAAU,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE;IACnE,gBAAgB,EAAE;QAChB,KAAK,EAAE,kBAAkB;QACzB,WAAW,EAAE,sCAAsC;KACpD;IAED,2BAA2B;IAC3B,qBAAqB,EAAE;QACrB,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,iCAAiC;KAC/C;IACD,yBAAyB,EAAE;QACzB,KAAK,EAAE,iBAAiB;QACxB,WAAW,EAAE,oCAAoC;KAClD;IACD,yBAAyB,EAAE;QACzB,KAAK,EAAE,kBAAkB;QACzB,WAAW,EAAE,2BAA2B;KACzC;IAED,qCAAqC;IACrC,aAAa,EAAE;QACb,KAAK,EAAE,eAAe;QACtB,WAAW,EAAE,2CAA2C;KACzD;IACD,WAAW,EAAE;QACX,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,uCAAuC;KACrD;IACD,WAAW,EAAE;QACX,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,0BAA0B;KACxC;IAED,aAAa;IACb,iBAAiB,EAAE;QACjB,KAAK,EAAE,eAAe;QACtB,WAAW,EAAE,mCAAmC;KACjD;IACD,kBAAkB,EAAE;QAClB,KAAK,EAAE,qBAAqB;QAC5B,WAAW,EAAE,6BAA6B;KAC3C;IACD,gBAAgB,EAAE,EAAE,KAAK,EAAE,mBAAmB,EAAE;IAChD,qBAAqB,EAAE,EAAE,KAAK,EAAE,wBAAwB,EAAE;IAC1D,sBAAsB,EAAE,EAAE,KAAK,EAAE,aAAa,EAAE;IAEhD,6BAA6B;IAC7B,OAAO,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE;IAC7B,SAAS,EAAE,EAAE,KAAK,EAAE,0BAA0B,EAAE;IAChD,iBAAiB,EAAE;QACjB,KAAK,EAAE,gCAAgC;QACvC,WAAW,EAAE,mBAAmB;KACjC;IAED,+EAA+E;IAC/E,oBAAoB,EAAE,EAAE,KAAK,EAAE,wBAAwB,EAAE;IACzD,qBAAqB,EAAE;QACrB,KAAK,EAAE,mBAAmB;QAC1B,WAAW,EAAE,6BAA6B;KAC3C;IACD,sBAAsB,EAAE,EAAE,KAAK,EAAE,oBAAoB,EAAE;IACvD,mBAAmB,EAAE;QACnB,KAAK,EAAE,iBAAiB;QACxB,WAAW,EAAE,oDAAoD;KAClE;IACD,oBAAoB,EAAE,EAAE,KAAK,EAAE,kBAAkB,EAAE;IACnD,kBAAkB,EAAE;QAClB,KAAK,EAAE,aAAa;QACpB,WAAW,EAAE,oDAAoD;KAClE;IACD,mBAAmB,EAAE;QACnB,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,kEAAkE;KAChF;CAC+C,CAAC;AAInD,0DAA0D;AAC1D,MAAM,CAAC,MAAM,mBAAmB,GAAG;IACjC,WAAW,EAAE,CAAC,IAAY,EAAiB,EAAE,CAAC,CAAC;QAC7C,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,yBAAyB,IAAI,GAAG;KAC9C,CAAC;IACF,UAAU,EAAE,CAAC,IAAY,EAAiB,EAAE,CAAC,CAAC;QAC5C,KAAK,EAAE,aAAa;QACpB,WAAW,EAAE,yBAAyB,IAAI,GAAG;KAC9C,CAAC;IACF,aAAa,EAAE,CAAC,OAAe,EAAiB,EAAE,CAAC,CAAC;QAClD,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,6BAA6B,OAAO,GAAG;KACrD,CAAC;IACF,YAAY,EAAE,CAAC,SAAiB,EAAiB,EAAE,CAAC,CAAC;QACnD,KAAK,EAAE,eAAe,SAAS,EAAE;KAClC,CAAC;IACF,cAAc,EAAE,CAAC,OAAe,EAAiB,EAAE,CAAC,CAAC;QACnD,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,GAAG,OAAO,0BAA0B;KAClD,CAAC;IACF,cAAc,EAAE,CAAC,OAAe,EAAiB,EAAE,CAAC,CAAC;QACnD,KAAK,EAAE,cAAc;QACrB,WAAW,EAAE,gBAAgB,OAAO,qBAAqB;KAC1D,CAAC;CACM,CAAC;AAEX,gFAAgF;AAEhF;;;;GAIG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,sBAAsB;IACtB,OAAO,EAAE,yCAAyC;IAClD,OAAO,EAAE,qDAAqD;IAC9D,OAAO,EAAE,uCAAuC;IAChD,cAAc,EAAE,6CAA6C;IAE7D,yBAAyB;IACzB,WAAW,EAAE,sCAAsC;IAEnD,aAAa;IACb,YAAY,EAAE,2BAA2B;IACzC,qBAAqB,EAAE,iDAAiD;IACxE,sBAAsB,EAAE,mDAAmD;IAE3E,8EAA8E;IAC9E,qBAAqB,EAAE,mDAAmD;IAC1E,4EAA4E;IAC5E,oBAAoB,EAAE,oDAAoD;IAC1E,+EAA+E;IAC/E,2BAA2B,EAAE,0DAA0D;IACvF,gFAAgF;IAChF,mBAAmB,EACjB,oFAAoF;IACtF,iDAAiD;IACjD,uBAAuB,EAAE,yCAAyC;IAClE,oBAAoB,EAAE,8BAA8B;IAEpD,+EAA+E;IAC/E,kDAAkD;IAClD,oBAAoB,EAAE,kCAAkC;IACxD;;;;OAIG;IACH,uBAAuB,EAAE,yCAAyC;IAClE;;;;OAIG;IACH,sBAAsB,EAAE,iDAAiD;IACzE,qEAAqE;IACrE,4BAA4B,EAAE,0CAA0C;IACxE,2EAA2E;IAC3E,0BAA0B,EAAE,wCAAwC;IACpE,wEAAwE;IACxE,wBAAwB,EAAE,gDAAgD;IAE1E,8BAA8B;IAC9B,qBAAqB,EAAE,uCAAuC;IAC9D,aAAa,EAAE,iEAAiE;IAChF,mBAAmB,EAAE,0DAA0D;IAC/E,qBAAqB,EAAE,4CAA4C;IACnE,gBAAgB,EAAE,0DAA0D;IAC5E,mBAAmB,EAAE,6DAA6D;IAClF,wBAAwB,EAAE,iEAAiE;IAE3F,aAAa;IACb,eAAe,EAAE,uCAAuC;IACxD,sBAAsB,EAAE,sCAAsC;IAC9D,2EAA2E;IAC3E,sBAAsB,EACpB,iFAAiF;IACnF,eAAe,EAAE,yEAAyE;IAE1F,WAAW;IACX,mBAAmB,EAAE,yBAAyB;IAC9C,yBAAyB;IACzB,oBAAoB,EAAE,mDAAmD;IACzE,kBAAkB,EAAE,0DAA0D;IAC9E,kBAAkB,EAAE,iDAAiD;IACrE,sBAAsB,EAAE,+CAA+C;IACvE,iBAAiB,EAAE,oDAAoD;IACvE,kBAAkB,EAAE,qDAAqD;IACzE,oBAAoB,EAAE,kDAAkD;IACxE,qBAAqB,EAAE,4BAA4B;IACnD,kBAAkB,EAAE,uBAAuB;IAC3C,eAAe,EAAE,6CAA6C;IAC9D;;;OAGG;IACH,gBAAgB,EAAE,+CAA+C;IAEjE,WAAW;IACX,aAAa,EAAE,2CAA2C;IAC1D,cAAc,EAAE,4CAA4C;IAC5D,wEAAwE;IACxE,eAAe,EAAE,gEAAgE;IACjF,uEAAuE;IACvE,UAAU,EAAE,qCAAqC;IACjD,yEAAyE;IACzE,SAAS,EAAE,iEAAiE;IAC5E,8EAA8E;IAC9E,qBAAqB,EAAE,8CAA8C;IACrE,0DAA0D;IAC1D,kBAAkB,EAAE,mDAAmD;IACvE,qBAAqB,EAAE,gDAAgD;IAEvE,8EAA8E;IAC9E,4EAA4E;IAC5E,qBAAqB,EACnB,iFAAiF;IACnF,6DAA6D;IAC7D,mBAAmB,EAAE,0EAA0E;IAC/F,kDAAkD;IAClD,wBAAwB,EAAE,mDAAmD;IAC7E,+DAA+D;IAC/D,8BAA8B,EAAE,uCAAuC;IACvE,sEAAsE;IACtE,oCAAoC,EAClC,6EAA6E;IAC/E,yBAAyB,EAAE,qCAAqC;IAChE,kEAAkE;IAClE,+BAA+B,EAAE,4CAA4C;IAC7E,uBAAuB,EAAE,gDAAgD;IACzE,cAAc,EAAE,oDAAoD;IACpE,uEAAuE;IACvE,WAAW,EAAE,sDAAsD;IACnE,8EAA8E;IAC9E,sBAAsB,EAAE,sCAAsC;IAC9D,kBAAkB,EAAE,6DAA6D;IAEjF,uBAAuB;IACvB,YAAY,EAAE,sEAAsE;IACpF,iBAAiB,EAAE,sEAAsE;IACzF,cAAc,EAAE,6CAA6C;IAC7D,YAAY,EAAE,kEAAkE;IAEhF,aAAa;IACb,sBAAsB,EAAE,yCAAyC;IACjE,sBAAsB,EAAE,oCAAoC;IAE5D,6BAA6B;IAC7B,aAAa,EAAE,gBAAgB;IAC/B,gBAAgB,EAAE,6BAA6B;IAC/C,aAAa,EAAE,mCAAmC;IAClD,YAAY,EAAE,gCAAgC;IAC9C,mBAAmB,EAAE,0CAA0C;IAC/D,eAAe,EAAE,qCAAqC;IAEtD,UAAU;IACV,iBAAiB,EAAE,gDAAgD;IACnE,kBAAkB,EAAE,iDAAiD;CAC5B,CAAC;AAI5C,iDAAiD;AACjD,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,SAAS,EAAE,CAAC,GAAW,EAAU,EAAE,CAAC,qBAAqB,GAAG,SAAS;IACrE,2DAA2D;IAC3D,aAAa,EAAE,CAAC,OAAe,EAAU,EAAE,CACzC,mCAAmC,OAAO,UAAU,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG;CACvE,CAAC","sourcesContent":["/**\n * gymmonk-schema — API result messages\n * ====================================\n * Single source of truth for every user-facing SUCCESS and ERROR message the\n * product shows, plus the API error codes both sides key off.\n *\n * Why it lives here: the schema package is already the SSOT for data shapes,\n * types and validation. Result copy is part of that contract — the backend\n * raises a message, the client shows it, and neither should invent its own\n * wording. Both repos import from here, so copy changes land in one place and\n * can never drift between server and client.\n *\n * Usage:\n * backend → `throw new BadRequestError(ERROR_MESSAGE.centerAddressRequired)`\n * frontend → `showSuccess(...toast(SUCCESS_MESSAGE.profileUpdated))`\n *\n * @module gymmonk-schema/messages\n */\n\n// ─── API error codes ─────────────────────────────────────────────────────────\n\n/**\n * Machine-readable error codes returned in the API envelope\n * (`{ success: false, error: { code, message } }`). The client can branch on a\n * code; the message is what a human reads.\n */\nexport const ERROR_CODE = {\n // General\n INTERNAL_ERROR: 'INTERNAL_ERROR',\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n NOT_FOUND: 'NOT_FOUND',\n\n // Auth\n INVALID_TOKEN: 'INVALID_TOKEN',\n TOKEN_EXPIRED: 'TOKEN_EXPIRED',\n UNAUTHORIZED: 'UNAUTHORIZED',\n FORBIDDEN: 'FORBIDDEN',\n SESSION_INVALIDATED: 'SESSION_INVALIDATED',\n\n // User\n USER_NOT_FOUND: 'USER_NOT_FOUND',\n USER_ALREADY_EXISTS: 'USER_ALREADY_EXISTS',\n INVALID_ROLE: 'INVALID_ROLE',\n\n // Rate limiting\n RATE_LIMITED: 'RATE_LIMITED',\n RATE_LIMIT_EXCEEDED: 'RATE_LIMIT_EXCEEDED',\n\n // Upstream / dependency unavailable\n SERVICE_UNAVAILABLE: 'SERVICE_UNAVAILABLE',\n} as const;\n\nexport type ErrorCode = (typeof ERROR_CODE)[keyof typeof ERROR_CODE];\n\n// ─── Message shape ───────────────────────────────────────────────────────────\n\n/** A result message: a headline and an optional quieter second line. */\nexport interface ResultMessage {\n title: string;\n description?: string;\n}\n\n// ─── Success messages ────────────────────────────────────────────────────────\n\n/**\n * Confirmation shown after a write succeeds, keyed by action. Interpolating\n * entries are functions so the caller supplies the value without re-wording.\n */\nexport const SUCCESS_MESSAGE = {\n // Onboarding\n memberOnboardingComplete: {\n title: \"You're all set\",\n description: 'Welcome to GymMonk.',\n },\n ownerOnboardingComplete: {\n title: 'Your gym is ready',\n description: 'Welcome to GymMonk.',\n },\n /** Each wizard step is persisted on its own, so each one confirms. */\n onboardingStepSaved: { title: 'Saved' },\n\n // Identity & profile\n profileUpdated: { title: 'Profile updated' },\n organisationUpdated: { title: 'Organisation updated' },\n\n // Gym structure\n centerAdded: { title: 'Center added' },\n centerUpdated: { title: 'Center updated' },\n membershipPlanCreated: { title: 'Membership plan created' },\n membershipPlanUpdated: { title: 'Membership plan updated' },\n equipmentSaved: { title: 'Equipment updated' },\n\n // Membership (member-side)\n membershipUpdated: { title: 'Membership updated' },\n membershipRenewed: { title: 'Membership renewed' },\n membershipCancelled: { title: 'Membership cancelled' },\n membershipPauseRequested: {\n title: 'Pause request sent',\n description: 'Your gym will confirm the pause.',\n },\n\n // Owner SaaS subscription\n subscriptionUpdated: { title: 'Subscription updated' },\n\n // Training\n workoutPlanCreated: { title: 'Workout plan created' },\n workoutCreated: { title: 'Workout created' },\n workoutPlanAssigned: { title: 'Workout plan assigned' },\n\n // Roster (owner console)\n attendanceMarked: { title: 'Attendance saved' },\n memberMarkedInactive: {\n title: 'Member marked inactive',\n description: 'They can no longer check in.',\n },\n memberMarkedActive: { title: 'Member reactivated' },\n memberDeleted: { title: 'Member removed' },\n memberPlanAssigned: { title: 'Plan updated' },\n memberMembershipPaused: { title: 'Membership paused' },\n memberMembershipCancelled: { title: 'Membership cancelled' },\n paymentCollected: { title: 'Payment recorded' },\n\n // Sessions\n checkedIn: { title: 'Checked in', description: 'Your session has started.' },\n checkedOut: { title: 'Checked out', description: 'Session saved.' },\n checkInQrRotated: {\n title: 'New QR generated',\n description: 'Print it and replace the old poster.',\n },\n\n // Membership join requests\n membershipRequestSent: {\n title: 'Request sent',\n description: 'The gym will review it shortly.',\n },\n membershipRequestApproved: {\n title: 'Member approved',\n description: 'They can now check in at your gym.',\n },\n membershipRequestRejected: {\n title: 'Request declined',\n description: 'The member has been told.',\n },\n\n // Gym registration + platform review\n gymRegistered: {\n title: 'Gym submitted',\n description: 'Our team will review it and let you know.',\n },\n gymApproved: {\n title: 'Gym approved',\n description: 'It is now listed for members to find.',\n },\n gymRejected: {\n title: 'Gym rejected',\n description: 'The owner has been told.',\n },\n\n // Engagement\n feedbackSubmitted: {\n title: 'Feedback sent',\n description: 'Thank you for helping us improve.',\n },\n complaintSubmitted: {\n title: 'Complaint submitted',\n description: 'Your gym will look into it.',\n },\n announcementSent: { title: 'Announcement sent' },\n announcementScheduled: { title: 'Announcement scheduled' },\n announcementDraftSaved: { title: 'Draft saved' },\n\n // Security (role-switch PIN)\n mpinSet: { title: 'PIN set' },\n mpinReset: { title: 'Your PIN has been reset.' },\n mpinResetLinkSent: {\n title: \"We've emailed you a reset link\",\n description: 'Check your inbox.',\n },\n\n // ── Platform console (superadmin / platform manager) ────────────────────────\n platformRolesUpdated: { title: 'Platform roles updated' },\n platformUserSuspended: {\n title: 'Account suspended',\n description: 'They can no longer sign in.',\n },\n platformUserReinstated: { title: 'Account reinstated' },\n platformUserDeleted: {\n title: 'Account deleted',\n description: 'It is hidden everywhere but can still be restored.',\n },\n platformUserRestored: { title: 'Account restored' },\n platformGymDeleted: {\n title: 'Gym deleted',\n description: 'It is hidden everywhere but can still be restored.',\n },\n platformGymRestored: {\n title: 'Gym restored',\n description: 'It is back in the directory and its owner has the console again.',\n },\n} as const satisfies Record<string, ResultMessage>;\n\nexport type SuccessMessageKey = keyof typeof SUCCESS_MESSAGE;\n\n/** Success messages that name the thing they acted on. */\nexport const SUCCESS_MESSAGE_FOR = {\n memberAdded: (name: string): ResultMessage => ({\n title: 'Member added',\n description: `Share the invite with ${name}.`,\n }),\n staffAdded: (name: string): ResultMessage => ({\n title: 'Staff added',\n description: `Share the invite with ${name}.`,\n }),\n inviteClaimed: (gymName: string): ResultMessage => ({\n title: \"You're all set\",\n description: `Your account is linked to ${gymName}.`,\n }),\n roleSwitched: (roleLabel: string): ResultMessage => ({\n title: `Switched to ${roleLabel}`,\n }),\n gymApprovedFor: (gymName: string): ResultMessage => ({\n title: 'Gym approved',\n description: `${gymName} is now live on GymMonk.`,\n }),\n gymRejectedFor: (gymName: string): ResultMessage => ({\n title: 'Gym rejected',\n description: `The owner of ${gymName} has been told why.`,\n }),\n} as const;\n\n// ─── Error messages ──────────────────────────────────────────────────────────\n\n/**\n * Failure copy. Transport entries describe a request that never reached the\n * API; the rest are domain rules the backend enforces and the client can also\n * check up-front — same wording either way.\n */\nexport const ERROR_MESSAGE = {\n // Transport / generic\n generic: 'Something went wrong. Please try again.',\n network: 'Network error. Check your connection and try again.',\n timeout: 'That took too long. Please try again.',\n sessionExpired: 'Your session expired. Please sign in again.',\n\n // Client-side validation\n formInvalid: 'Please check the highlighted fields.',\n\n // Onboarding\n roleRequired: 'Choose a role to continue',\n ownerOnboardingFailed: 'We could not create your gym. Please try again.',\n memberOnboardingFailed: 'We could not save your details. Please try again.',\n\n // ── Gym registration (\"list your gym\") ─────────────────────────────────────\n gymRegistrationFailed: 'We could not register your gym. Please try again.',\n /** One organisation per account — registering a second is not supported. */\n gymAlreadyRegistered: 'You have already registered a gym on this account.',\n /** Registering a gym is something a MEMBER does, so the profile must exist. */\n gymRegistrationNeedsProfile: 'Finish setting up your profile before registering a gym.',\n /** Every owner action other than editing their own details, before approval. */\n gymAwaitingApproval:\n 'Your gym is waiting for approval. You can manage it once our team has reviewed it.',\n /** The reviewer turned the registration down. */\n gymRegistrationRejected: 'Your gym registration was not approved.',\n organisationNotFound: 'That gym could not be found.',\n\n // ── Platform console ────────────────────────────────────────────────────────\n /** The account is gone, or was already erased. */\n platformUserNotFound: 'That account could not be found.',\n /**\n * An admin acting on their OWN row. Suspending or deleting yourself takes away\n * the access needed to undo it, so the console refuses rather than letting one\n * mis-tap lock the platform's last superadmin out of it.\n */\n platformCannotActOnSelf: 'You cannot do that to your own account.',\n /**\n * Removing the last superadmin. `manage GymApproval` is held by nobody else,\n * so the gym review queue would become unreachable with no way back in short\n * of a database edit.\n */\n platformLastSuperadmin: 'The platform must keep at least one superadmin.',\n /** The typed confirmation did not match the record being deleted. */\n platformConfirmationMismatch: 'That did not match. Nothing was changed.',\n /** Two reviewers acting on the same registration; the second one loses. */\n organisationAlreadyDecided: 'Someone has already reviewed this gym.',\n /** A step failed to persist — the user stays put so nothing is lost. */\n onboardingStepSaveFailed: 'We could not save this step. Please try again.',\n\n // Gym structure prerequisites\n centerRequiredForPlan: 'Pick the center this plan belongs to.',\n planTierTaken: 'This center already has a plan on that tier. Pick another tier.',\n planVariantRequired: 'Add at least one pricing variant before saving the plan.',\n centerAddressRequired: 'Add the center address before creating it.',\n noCenterForPlans: 'No gym center found. Add a center before creating plans.',\n noCenterForWorkouts: 'No gym center found. Add a center before creating workouts.',\n noCenterForAnnouncements: 'No gym center found. Add a center before sending announcements.',\n\n // Membership\n planUnavailable: 'That plan is not available right now.',\n membershipPlanRequired: 'Choose a membership plan to continue',\n /** The member was created, but the plan the owner picked did not stick. */\n initialPlanNotAssigned:\n 'Member added, but the plan could not be assigned. Assign it from their profile.',\n noPlansToAssign: 'No membership plans yet. Create one to assign it while adding a member.',\n\n // Training\n workoutPlanRequired: 'Choose a plan to assign',\n // Roster (owner console)\n attendanceMarkFailed: 'Could not save that attendance. Please try again.',\n memberStatusFailed: \"Could not change that member's status. Please try again.\",\n memberDeleteFailed: 'Could not remove that member. Please try again.',\n memberPlanAssignFailed: 'Could not update that plan. Please try again.',\n memberPauseFailed: 'Could not pause that membership. Please try again.',\n memberCancelFailed: 'Could not cancel that membership. Please try again.',\n paymentCollectFailed: 'Could not record that payment. Please try again.',\n paymentAmountRequired: 'Enter the amount collected',\n pauseDatesRequired: 'Pick both pause dates',\n pauseDatesOrder: 'The end date has to be after the start date',\n /**\n * Attendance, workout plans and payments are all keyed on the central-auth\n * user, which an invited record does not have until the person claims it.\n */\n memberNotClaimed: 'This member has not claimed their invite yet.',\n\n // Sessions\n checkInFailed: 'Could not check you in. Please try again.',\n checkOutFailed: 'Could not check you out. Please try again.',\n /** The camera decoded a QR, but it is not a gymmonk check-in poster. */\n qrNotRecognised: 'That is not a gym check-in QR. Point at the poster at the gym.',\n /** A valid gymmonk QR, but for a gym the member does not belong to. */\n qrWrongGym: 'That QR belongs to a different gym.',\n /** The scanned code matches no center — a retired or mistyped poster. */\n qrUnknown: 'This QR is no longer valid. Ask the gym for the current poster.',\n /** Checking out with a poster from somewhere other than where you started. */\n qrWrongGymForCheckOut: 'Scan the QR at the gym where you checked in.',\n /** Manual entry only accepts a full, well-formed code. */\n checkInCodeInvalid: 'Enter the 16-character code printed under the QR.',\n checkInQrRotateFailed: 'Could not generate a new QR. Please try again.',\n\n // ── Membership join requests ───────────────────────────────────────────────\n /** Scanned a valid poster, but the gym has not accepted this member yet. */\n membershipNotApproved:\n 'Your gym has not accepted your join request yet. You can check in once they do.',\n /** Accepted, but with no live plan — check-in needs both. */\n membershipNotActive: 'Your membership plan is not active. Renew it to start checking in again.',\n /** A second request while one is already open. */\n membershipRequestPending: 'You already have a request waiting with this gym.',\n /** Requesting a gym they are already an accepted member of. */\n membershipRequestAlreadyMember: 'You are already a member of this gym.',\n /** v1 memberships are single-center — leave the current gym first. */\n membershipRequestAlreadyAtAnotherGym:\n 'You already belong to a gym. Cancel that membership before joining another.',\n membershipRequestNotFound: 'That join request no longer exists.',\n /** Two staff acting on the same request; the second one loses. */\n membershipRequestAlreadyDecided: 'Someone has already answered this request.',\n membershipRequestFailed: 'Could not send your request. Please try again.',\n gymCodeUnknown: 'No gym found with that id. Check it with your gym.',\n /** Actions that only make sense once a gym has taken the member on. */\n noGymJoined: 'Join a gym first. You can find one from your Me tab.',\n /** Searching by name needs somewhere to look — see `gymSearchQuerySchema`. */\n gymSearchStateRequired: 'Pick a state to search gyms by name.',\n gymSearchNoResults: 'No gyms matched. Try a different area, or enter the gym id.',\n\n // Camera (QR scanning)\n cameraDenied: 'Camera access is blocked. Allow it in your browser settings to scan.',\n cameraUnavailable: 'No camera found on this device. Enter the code printed under the QR.',\n cameraInsecure: 'Scanning needs a secure (https) connection.',\n cameraFailed: 'Could not start the camera. Enter the code printed under the QR.',\n\n // Engagement\n feedbackRatingRequired: 'Choose a rating to submit your feedback',\n complaintIssueRequired: 'Choose the issue you are reporting',\n\n // Security (role-switch PIN)\n mpinIncorrect: 'Incorrect PIN.',\n mpinVerifyFailed: \"Couldn't verify. Try again.\",\n mpinSetFailed: \"Couldn't set your PIN. Try again.\",\n mpinMismatch: \"Those didn't match. Try again.\",\n mpinResetLinkFailed: \"Couldn't send the reset link. Try again.\",\n mpinResetFailed: \"Couldn't reset your PIN. Try again.\",\n\n // Uploads\n imageUploadFailed: 'Could not upload that image. Please try again.',\n imageProcessFailed: 'Could not process that image. Please try again.',\n} as const satisfies Record<string, string>;\n\nexport type ErrorMessageKey = keyof typeof ERROR_MESSAGE;\n\n/** Error messages that name a limit or value. */\nexport const ERROR_MESSAGE_FOR = {\n maxPhotos: (max: number): string => `You can add up to ${max} photos`,\n /** Role-switch PIN lockout, in whole minutes remaining. */\n mpinLockedOut: (minutes: number): string =>\n `Too many attempts. Try again in ${minutes} minute${minutes > 1 ? 's' : ''}.`,\n} as const;\n"]}
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../src/messages.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gymmonk-schema — API result messages\n * ====================================\n * Single source of truth for every user-facing SUCCESS and ERROR message the\n * product shows, plus the API error codes both sides key off.\n *\n * Why it lives here: the schema package is already the SSOT for data shapes,\n * types and validation. Result copy is part of that contract — the backend\n * raises a message, the client shows it, and neither should invent its own\n * wording. Both repos import from here, so copy changes land in one place and\n * can never drift between server and client.\n *\n * Usage:\n * backend → `throw new BadRequestError(ERROR_MESSAGE.centerAddressRequired)`\n * frontend → `showSuccess(...toast(SUCCESS_MESSAGE.profileUpdated))`\n *\n * @module gymmonk-schema/messages\n */\n\n// ─── API error codes ─────────────────────────────────────────────────────────\n\n/**\n * Machine-readable error codes returned in the API envelope\n * (`{ success: false, error: { code, message } }`). The client can branch on a\n * code; the message is what a human reads.\n */\nexport const ERROR_CODE = {\n // General\n INTERNAL_ERROR: 'INTERNAL_ERROR',\n VALIDATION_ERROR: 'VALIDATION_ERROR',\n NOT_FOUND: 'NOT_FOUND',\n\n // Auth\n INVALID_TOKEN: 'INVALID_TOKEN',\n TOKEN_EXPIRED: 'TOKEN_EXPIRED',\n UNAUTHORIZED: 'UNAUTHORIZED',\n FORBIDDEN: 'FORBIDDEN',\n SESSION_INVALIDATED: 'SESSION_INVALIDATED',\n\n // User\n USER_NOT_FOUND: 'USER_NOT_FOUND',\n USER_ALREADY_EXISTS: 'USER_ALREADY_EXISTS',\n INVALID_ROLE: 'INVALID_ROLE',\n\n // Rate limiting\n RATE_LIMITED: 'RATE_LIMITED',\n RATE_LIMIT_EXCEEDED: 'RATE_LIMIT_EXCEEDED',\n\n // Upstream / dependency unavailable\n SERVICE_UNAVAILABLE: 'SERVICE_UNAVAILABLE',\n} as const;\n\nexport type ErrorCode = (typeof ERROR_CODE)[keyof typeof ERROR_CODE];\n\n// ─── Message shape ───────────────────────────────────────────────────────────\n\n/** A result message: a headline and an optional quieter second line. */\nexport interface ResultMessage {\n title: string;\n description?: string;\n}\n\n// ─── Success messages ────────────────────────────────────────────────────────\n\n/**\n * Confirmation shown after a write succeeds, keyed by action. Interpolating\n * entries are functions so the caller supplies the value without re-wording.\n */\nexport const SUCCESS_MESSAGE = {\n // Onboarding\n memberOnboardingComplete: {\n title: \"You're all set\",\n description: 'Welcome to GymMonk.',\n },\n ownerOnboardingComplete: {\n title: 'Your gym is ready',\n description: 'Welcome to GymMonk.',\n },\n /** Each wizard step is persisted on its own, so each one confirms. */\n onboardingStepSaved: { title: 'Saved' },\n\n // Identity & profile\n profileUpdated: { title: 'Profile updated' },\n organisationUpdated: { title: 'Organisation updated' },\n\n // Gym structure\n centerAdded: { title: 'Center added' },\n centerUpdated: { title: 'Center updated' },\n membershipPlanCreated: { title: 'Membership plan created' },\n membershipPlanUpdated: { title: 'Membership plan updated' },\n equipmentSaved: { title: 'Equipment updated' },\n\n // Membership (member-side)\n membershipUpdated: { title: 'Membership updated' },\n membershipRenewed: { title: 'Membership renewed' },\n membershipCancelled: { title: 'Membership cancelled' },\n membershipPauseRequested: {\n title: 'Pause request sent',\n description: 'Your gym will confirm the pause.',\n },\n\n // Owner SaaS subscription\n subscriptionUpdated: { title: 'Subscription updated' },\n\n // Training\n workoutPlanCreated: { title: 'Workout plan created' },\n workoutPlanUpdated: { title: 'Workout plan saved' },\n workoutPlanDeleted: {\n title: 'Workout plan deleted',\n description: 'Members on this plan no longer have one.',\n },\n workoutCreated: { title: 'Workout created' },\n workoutUpdated: { title: 'Workout saved' },\n workoutDeleted: { title: 'Workout removed' },\n workoutPlanAssigned: { title: 'Workout plan assigned' },\n\n // Roster (owner console)\n attendanceMarked: { title: 'Attendance saved' },\n memberMarkedInactive: {\n title: 'Member marked inactive',\n description: 'They can no longer check in.',\n },\n memberMarkedActive: { title: 'Member reactivated' },\n memberDeleted: { title: 'Member removed' },\n memberPlanAssigned: { title: 'Plan updated' },\n memberMembershipPaused: { title: 'Membership paused' },\n memberMembershipCancelled: { title: 'Membership cancelled' },\n paymentCollected: { title: 'Payment recorded' },\n\n // Sessions\n checkedIn: { title: 'Checked in', description: 'Your session has started.' },\n checkedOut: { title: 'Checked out', description: 'Session saved.' },\n checkInQrRotated: {\n title: 'New QR generated',\n description: 'Print it and replace the old poster.',\n },\n\n // Membership join requests\n membershipRequestSent: {\n title: 'Request sent',\n description: 'The gym will review it shortly.',\n },\n membershipRequestApproved: {\n title: 'Member approved',\n description: 'They can now check in at your gym.',\n },\n membershipRequestRejected: {\n title: 'Request declined',\n description: 'The member has been told.',\n },\n\n // Gym registration + platform review\n gymRegistered: {\n title: 'Gym submitted',\n description: 'Our team will review it and let you know.',\n },\n gymApproved: {\n title: 'Gym approved',\n description: 'It is now listed for members to find.',\n },\n gymRejected: {\n title: 'Gym rejected',\n description: 'The owner has been told.',\n },\n\n // Engagement\n feedbackSubmitted: {\n title: 'Feedback sent',\n description: 'Thank you for helping us improve.',\n },\n complaintSubmitted: {\n title: 'Complaint submitted',\n description: 'Your gym will look into it.',\n },\n announcementSent: { title: 'Announcement sent' },\n announcementScheduled: { title: 'Announcement scheduled' },\n announcementDraftSaved: { title: 'Draft saved' },\n\n // Security (role-switch PIN)\n mpinSet: { title: 'PIN set' },\n mpinReset: { title: 'Your PIN has been reset.' },\n mpinResetLinkSent: {\n title: \"We've emailed you a reset link\",\n description: 'Check your inbox.',\n },\n\n // ── Platform console (superadmin / platform manager) ────────────────────────\n platformRolesUpdated: { title: 'Platform roles updated' },\n platformUserSuspended: {\n title: 'Account suspended',\n description: 'They can no longer sign in.',\n },\n platformUserReinstated: { title: 'Account reinstated' },\n platformUserDeleted: {\n title: 'Account deleted',\n description: 'It is hidden everywhere but can still be restored.',\n },\n platformUserRestored: { title: 'Account restored' },\n platformGymDeleted: {\n title: 'Gym deleted',\n description: 'It is hidden everywhere but can still be restored.',\n },\n platformGymRestored: {\n title: 'Gym restored',\n description: 'It is back in the directory and its owner has the console again.',\n },\n} as const satisfies Record<string, ResultMessage>;\n\nexport type SuccessMessageKey = keyof typeof SUCCESS_MESSAGE;\n\n/** Success messages that name the thing they acted on. */\nexport const SUCCESS_MESSAGE_FOR = {\n memberAdded: (name: string): ResultMessage => ({\n title: 'Member added',\n description: `Share the invite with ${name}.`,\n }),\n staffAdded: (name: string): ResultMessage => ({\n title: 'Staff added',\n description: `Share the invite with ${name}.`,\n }),\n inviteClaimed: (gymName: string): ResultMessage => ({\n title: \"You're all set\",\n description: `Your account is linked to ${gymName}.`,\n }),\n roleSwitched: (roleLabel: string): ResultMessage => ({\n title: `Switched to ${roleLabel}`,\n }),\n gymApprovedFor: (gymName: string): ResultMessage => ({\n title: 'Gym approved',\n description: `${gymName} is now live on GymMonk.`,\n }),\n gymRejectedFor: (gymName: string): ResultMessage => ({\n title: 'Gym rejected',\n description: `The owner of ${gymName} has been told why.`,\n }),\n} as const;\n\n// ─── Error messages ──────────────────────────────────────────────────────────\n\n/**\n * Failure copy. Transport entries describe a request that never reached the\n * API; the rest are domain rules the backend enforces and the client can also\n * check up-front — same wording either way.\n */\nexport const ERROR_MESSAGE = {\n // Transport / generic\n generic: 'Something went wrong. Please try again.',\n network: 'Network error. Check your connection and try again.',\n timeout: 'That took too long. Please try again.',\n sessionExpired: 'Your session expired. Please sign in again.',\n\n // Client-side validation\n formInvalid: 'Please check the highlighted fields.',\n\n // Onboarding\n roleRequired: 'Choose a role to continue',\n ownerOnboardingFailed: 'We could not create your gym. Please try again.',\n memberOnboardingFailed: 'We could not save your details. Please try again.',\n\n // ── Gym registration (\"list your gym\") ─────────────────────────────────────\n gymRegistrationFailed: 'We could not register your gym. Please try again.',\n /** One organisation per account — registering a second is not supported. */\n gymAlreadyRegistered: 'You have already registered a gym on this account.',\n /** Registering a gym is something a MEMBER does, so the profile must exist. */\n gymRegistrationNeedsProfile: 'Finish setting up your profile before registering a gym.',\n /** Every owner action other than editing their own details, before approval. */\n gymAwaitingApproval:\n 'Your gym is waiting for approval. You can manage it once our team has reviewed it.',\n /** The reviewer turned the registration down. */\n gymRegistrationRejected: 'Your gym registration was not approved.',\n organisationNotFound: 'That gym could not be found.',\n\n // ── Platform console ────────────────────────────────────────────────────────\n /** The account is gone, or was already erased. */\n platformUserNotFound: 'That account could not be found.',\n /**\n * An admin acting on their OWN row. Suspending or deleting yourself takes away\n * the access needed to undo it, so the console refuses rather than letting one\n * mis-tap lock the platform's last superadmin out of it.\n */\n platformCannotActOnSelf: 'You cannot do that to your own account.',\n /**\n * Removing the last superadmin. `manage GymApproval` is held by nobody else,\n * so the gym review queue would become unreachable with no way back in short\n * of a database edit.\n */\n platformLastSuperadmin: 'The platform must keep at least one superadmin.',\n /** The typed confirmation did not match the record being deleted. */\n platformConfirmationMismatch: 'That did not match. Nothing was changed.',\n /** Two reviewers acting on the same registration; the second one loses. */\n organisationAlreadyDecided: 'Someone has already reviewed this gym.',\n /** A step failed to persist — the user stays put so nothing is lost. */\n onboardingStepSaveFailed: 'We could not save this step. Please try again.',\n\n // Gym structure prerequisites\n centerRequiredForPlan: 'Pick the center this plan belongs to.',\n planTierTaken: 'This center already has a plan on that tier. Pick another tier.',\n planVariantRequired: 'Add at least one pricing variant before saving the plan.',\n centerAddressRequired: 'Add the center address before creating it.',\n noCenterForPlans: 'No gym center found. Add a center before creating plans.',\n noCenterForWorkouts: 'No gym center found. Add a center before creating workouts.',\n noCenterForAnnouncements: 'No gym center found. Add a center before sending announcements.',\n\n // Membership\n planUnavailable: 'That plan is not available right now.',\n membershipPlanRequired: 'Choose a membership plan to continue',\n /** The member was created, but the plan the owner picked did not stick. */\n initialPlanNotAssigned:\n 'Member added, but the plan could not be assigned. Assign it from their profile.',\n noPlansToAssign: 'No membership plans yet. Create one to assign it while adding a member.',\n\n // Training\n workoutPlanRequired: 'Choose a plan to assign',\n // Roster (owner console)\n attendanceMarkFailed: 'Could not save that attendance. Please try again.',\n memberStatusFailed: \"Could not change that member's status. Please try again.\",\n memberDeleteFailed: 'Could not remove that member. Please try again.',\n memberPlanAssignFailed: 'Could not update that plan. Please try again.',\n memberPauseFailed: 'Could not pause that membership. Please try again.',\n memberCancelFailed: 'Could not cancel that membership. Please try again.',\n paymentCollectFailed: 'Could not record that payment. Please try again.',\n paymentAmountRequired: 'Enter the amount collected',\n pauseDatesRequired: 'Pick both pause dates',\n pauseDatesOrder: 'The end date has to be after the start date',\n /**\n * Attendance, workout plans and payments are all keyed on the central-auth\n * user, which an invited record does not have until the person claims it.\n */\n memberNotClaimed: 'This member has not claimed their invite yet.',\n\n // Sessions\n checkInFailed: 'Could not check you in. Please try again.',\n checkOutFailed: 'Could not check you out. Please try again.',\n /** The camera decoded a QR, but it is not a gymmonk check-in poster. */\n qrNotRecognised: 'That is not a gym check-in QR. Point at the poster at the gym.',\n /** A valid gymmonk QR, but for a gym the member does not belong to. */\n qrWrongGym: 'That QR belongs to a different gym.',\n /** The scanned code matches no center — a retired or mistyped poster. */\n qrUnknown: 'This QR is no longer valid. Ask the gym for the current poster.',\n /** Checking out with a poster from somewhere other than where you started. */\n qrWrongGymForCheckOut: 'Scan the QR at the gym where you checked in.',\n /** Manual entry only accepts a full, well-formed code. */\n checkInCodeInvalid: 'Enter the 16-character code printed under the QR.',\n checkInQrRotateFailed: 'Could not generate a new QR. Please try again.',\n\n // ── Membership join requests ───────────────────────────────────────────────\n /** Scanned a valid poster, but the gym has not accepted this member yet. */\n membershipNotApproved:\n 'Your gym has not accepted your join request yet. You can check in once they do.',\n /** Accepted, but with no live plan — check-in needs both. */\n membershipNotActive: 'Your membership plan is not active. Renew it to start checking in again.',\n /** A second request while one is already open. */\n membershipRequestPending: 'You already have a request waiting with this gym.',\n /** Requesting a gym they are already an accepted member of. */\n membershipRequestAlreadyMember: 'You are already a member of this gym.',\n /** v1 memberships are single-center — leave the current gym first. */\n membershipRequestAlreadyAtAnotherGym:\n 'You already belong to a gym. Cancel that membership before joining another.',\n membershipRequestNotFound: 'That join request no longer exists.',\n /** Two staff acting on the same request; the second one loses. */\n membershipRequestAlreadyDecided: 'Someone has already answered this request.',\n membershipRequestFailed: 'Could not send your request. Please try again.',\n gymCodeUnknown: 'No gym found with that id. Check it with your gym.',\n /** Actions that only make sense once a gym has taken the member on. */\n noGymJoined: 'Join a gym first. You can find one from your Me tab.',\n /** Searching by name needs somewhere to look — see `gymSearchQuerySchema`. */\n gymSearchStateRequired: 'Pick a state to search gyms by name.',\n gymSearchNoResults: 'No gyms matched. Try a different area, or enter the gym id.',\n\n // Camera (QR scanning)\n cameraDenied: 'Camera access is blocked. Allow it in your browser settings to scan.',\n cameraUnavailable: 'No camera found on this device. Enter the code printed under the QR.',\n cameraInsecure: 'Scanning needs a secure (https) connection.',\n cameraFailed: 'Could not start the camera. Enter the code printed under the QR.',\n\n // Engagement\n feedbackRatingRequired: 'Choose a rating to submit your feedback',\n complaintIssueRequired: 'Choose the issue you are reporting',\n\n // Security (role-switch PIN)\n mpinIncorrect: 'Incorrect PIN.',\n mpinVerifyFailed: \"Couldn't verify. Try again.\",\n mpinSetFailed: \"Couldn't set your PIN. Try again.\",\n mpinMismatch: \"Those didn't match. Try again.\",\n mpinResetLinkFailed: \"Couldn't send the reset link. Try again.\",\n mpinResetFailed: \"Couldn't reset your PIN. Try again.\",\n\n // Uploads\n imageUploadFailed: 'Could not upload that image. Please try again.',\n imageProcessFailed: 'Could not process that image. Please try again.',\n} as const satisfies Record<string, string>;\n\nexport type ErrorMessageKey = keyof typeof ERROR_MESSAGE;\n\n/** Error messages that name a limit or value. */\nexport const ERROR_MESSAGE_FOR = {\n maxPhotos: (max: number): string => `You can add up to ${max} photos`,\n /** Role-switch PIN lockout, in whole minutes remaining. */\n mpinLockedOut: (minutes: number): string =>\n `Too many attempts. Try again in ${minutes} minute${minutes > 1 ? 's' : ''}.`,\n} as const;\n"]}
|
package/dist/social.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"social.js","sourceRoot":"","sources":["../src/social.ts"],"names":[],"mappings":"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","sourcesContent":["/**\r\n * gymmonk-schema — Social graph (follow)\r\n * ======================================\r\n * The follow relationship and the people list it feeds.\r\n *\r\n * A follow is UNILATERAL: one row, one direction, no acceptance step. A mutual\r\n * follow is two independent rows. That is what makes \"do I follow them\" and\r\n * \"do they follow me\" separately answerable, and therefore what makes\r\n * \"Follow back\" a state the UI can offer at all.\r\n *\r\n * @module gymmonk-schema/social\r\n */\r\n\r\nimport { z } from 'zod';\r\nimport { isoDateTimeSchema, objectIdSchema } from './common.js';\r\n\r\n// ─── Viewer state ────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * How the viewer stands with one other person.\r\n *\r\n * Both directions are carried, and both timestamps with them. `followedYouAt`\r\n * is what the Followers list shows as \"· 2w\", and `followsViewer` is the only\r\n * thing that distinguishes a \"Follow\" button from a \"Follow back\" button.\r\n */\r\nexport const viewerSocialStateSchema = z.object({\r\n /** The viewer follows them. */\r\n following: z.boolean(),\r\n /** They follow the viewer. */\r\n followsViewer: z.boolean(),\r\n /** When the viewer started following them. Null when they do not. */\r\n followedAt: isoDateTimeSchema.nullable(),\r\n /** When they started following the viewer. Null when they do not. */\r\n followedYouAt: isoDateTimeSchema.nullable(),\r\n});\r\nexport type ViewerSocialState = z.infer<typeof viewerSocialStateSchema>;\r\n\r\n/** Nobody follows anybody. The shape a missing lookup falls back to. */\r\nexport const EMPTY_VIEWER_SOCIAL_STATE: ViewerSocialState = {\r\n following: false,\r\n followsViewer: false,\r\n followedAt: null,\r\n followedYouAt: null,\r\n};\r\n\r\n// ─── Mutuals ─────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * \"Followed by Priya, Rahul and 3 more\" — the people the viewer and this person\r\n * both follow.\r\n *\r\n * `count` is the TOTAL; `avatars` and `sampleNames` carry only the handful\r\n * actually rendered. Sending every mutual so the client can count them would\r\n * put an unbounded array on a list row for one line of secondary text.\r\n */\r\nexport const mutualsSchema = z.object({\r\n count: z.number().int().nonnegative(),\r\n /** Up to three, for the stacked faces. An entry is null when that person has no photo. */\r\n avatars: z.array(z.string().nullable()),\r\n /** Up to three names, for the sentence. */\r\n sampleNames: z.array(z.string()),\r\n});\r\nexport type Mutuals = z.infer<typeof mutualsSchema>;\r\n\r\n/** How many faces and names a mutuals line carries. */\r\nexport const MUTUALS_SAMPLE_SIZE = 3;\r\n\r\n// ─── People list ─────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Why this person is in the viewer's list.\r\n *\r\n * `gym` at the viewer's gym, not followed\r\n * `following` followed by the viewer, and NOT at their gym any more\r\n * `both` at the gym AND followed\r\n *\r\n * The distinction is load-bearing rather than cosmetic. Messaging is gated on\r\n * sharing a gym, so a `following` row must not offer a Message button the\r\n * backend will refuse. jansathi has no equivalent because its People tab is the\r\n * whole community and there is no second axis to be on the wrong side of.\r\n */\r\nexport const buddyRelationSchema = z.enum(['gym', 'following', 'both']);\r\nexport type BuddyRelation = z.infer<typeof buddyRelationSchema>;\r\n\r\n/** Which slice of the list the caller asked for. */\r\nexport const buddyFilterSchema = z.enum(['all', 'gym', 'following', 'followers']);\r\nexport type BuddyFilter = z.infer<typeof buddyFilterSchema>;\r\n\r\nexport const BUDDY_FILTER_VALUES = ['all', 'gym', 'following', 'followers'] as const;\r\n\r\n/**\r\n * One person as a peer is allowed to see them.\r\n *\r\n * Deliberately NOT derived from the owner's member projection. That one carries\r\n * phone numbers, addresses, emergency contacts, dues and attendance, and the\r\n * only reliable way to guarantee none of it reaches a peer is for this to be a\r\n * separate shape rather than the same one with fields removed.\r\n */\r\nexport const buddySchema = z.object({\r\n /** The USER id. This is who you follow and message, not a profile row. */\r\n userId: objectIdSchema,\r\n name: z.string(),\r\n avatarUrl: z.string().nullable(),\r\n /** \"Member since Mar 2025\", or null for someone with no membership history here. */\r\n memberSince: z.string().nullable(),\r\n /**\r\n * When this person was last seen in the app. Null if never.\r\n *\r\n * Carried so the list can offer a \"Recently active\" sort. It is deliberately\r\n * a raw instant rather than a rendered \"2h ago\": the client is the only side\r\n * that knows what time it is where the reader is sitting, and a string\r\n * formatted on the server goes stale the moment it is cached.\r\n */\r\n lastActiveAt: isoDateTimeSchema.nullable(),\r\n relation: buddyRelationSchema,\r\n viewer: viewerSocialStateSchema,\r\n mutuals: mutualsSchema.nullable(),\r\n});\r\nexport type Buddy = z.infer<typeof buddySchema>;\r\n\r\n/** `GET /api/v1/members/at-my-gym?filter=` */\r\nexport const buddyListQuerySchema = z.object({\r\n filter: buddyFilterSchema.default('all'),\r\n});\r\nexport type BuddyListQuery = z.infer<typeof buddyListQuerySchema>;\r\n\r\n// ─── Toggle ──────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * The state AFTER a toggle.\r\n *\r\n * A toggle rather than separate follow and unfollow endpoints: a button acting\r\n * on stale state can send the wrong one, and then the server has to decide\r\n * whether a double unfollow is an error. A toggle has one meaning whatever the\r\n * client believed, and this response says what is now true.\r\n */\r\nexport const toggleFollowResultSchema = z.object({\r\n targetUserId: objectIdSchema,\r\n following: z.boolean(),\r\n followerCount: z.number().int().nonnegative(),\r\n});\r\nexport type ToggleFollowResult = z.infer<typeof toggleFollowResultSchema>;\r\n\r\n/** `GET /api/v1/users/:id/social` — one person's relationship plus their totals. */\r\nexport const socialSnapshotSchema = z.object({\r\n /**\r\n * The person themselves, in the SAME shape as a people-list row.\r\n *\r\n * The profile screen and the row show the same facts and offer the same\r\n * actions, so two shapes would be two places to add a field and one of them\r\n * being forgotten. `viewer` lives inside it.\r\n *\r\n * RECOVERED from the published 0.17.0 `.d.ts`, not from its sourcemap — that\r\n * map's `sourcesContent` predates this field and dropped it silently, which\r\n * would have broken the peer-profile screen on the next publish.\r\n */\r\n person: buddySchema,\r\n followerCount: z.number().int().nonnegative(),\r\n followingCount: z.number().int().nonnegative(),\r\n});\r\nexport type SocialSnapshot = z.infer<typeof socialSnapshotSchema>;\r\n\r\n// ─── Blocking ────────────────────────────────────────────────────────────────\r\n//\r\n// RECOVERED from the published 0.17.0 build, not from its sourcemap: that map's\r\n// `sourcesContent` predates these two and would have dropped them silently.\r\n\r\n/** One row of the viewer's block list. */\r\nexport const blockedUserSchema = z.object({\r\n userId: objectIdSchema,\r\n name: z.string(),\r\n avatarUrl: z.string().nullable(),\r\n blockedAt: isoDateTimeSchema,\r\n});\r\nexport type BlockedUser = z.infer<typeof blockedUserSchema>;\r\n\r\n/** `POST /api/v1/users/:id/block` — the state AFTER the toggle. */\r\nexport const toggleBlockResultSchema = z.object({\r\n targetUserId: objectIdSchema,\r\n blocked: z.boolean(),\r\n});\r\nexport type ToggleBlockResult = z.infer<typeof toggleBlockResultSchema>;\r\n"]}
|
|
1
|
+
{"version":3,"file":"social.js","sourceRoot":"","sources":["../src/social.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * gymmonk-schema — Social graph (follow)\n * ======================================\n * The follow relationship and the people list it feeds.\n *\n * A follow is UNILATERAL: one row, one direction, no acceptance step. A mutual\n * follow is two independent rows. That is what makes \"do I follow them\" and\n * \"do they follow me\" separately answerable, and therefore what makes\n * \"Follow back\" a state the UI can offer at all.\n *\n * @module gymmonk-schema/social\n */\n\nimport { z } from 'zod';\nimport { isoDateTimeSchema, objectIdSchema } from './common.js';\n\n// ─── Viewer state ────────────────────────────────────────────────────────────\n\n/**\n * How the viewer stands with one other person.\n *\n * Both directions are carried, and both timestamps with them. `followedYouAt`\n * is what the Followers list shows as \"· 2w\", and `followsViewer` is the only\n * thing that distinguishes a \"Follow\" button from a \"Follow back\" button.\n */\nexport const viewerSocialStateSchema = z.object({\n /** The viewer follows them. */\n following: z.boolean(),\n /** They follow the viewer. */\n followsViewer: z.boolean(),\n /** When the viewer started following them. Null when they do not. */\n followedAt: isoDateTimeSchema.nullable(),\n /** When they started following the viewer. Null when they do not. */\n followedYouAt: isoDateTimeSchema.nullable(),\n});\nexport type ViewerSocialState = z.infer<typeof viewerSocialStateSchema>;\n\n/** Nobody follows anybody. The shape a missing lookup falls back to. */\nexport const EMPTY_VIEWER_SOCIAL_STATE: ViewerSocialState = {\n following: false,\n followsViewer: false,\n followedAt: null,\n followedYouAt: null,\n};\n\n// ─── Mutuals ─────────────────────────────────────────────────────────────────\n\n/**\n * \"Followed by Priya, Rahul and 3 more\" — the people the viewer and this person\n * both follow.\n *\n * `count` is the TOTAL; `avatars` and `sampleNames` carry only the handful\n * actually rendered. Sending every mutual so the client can count them would\n * put an unbounded array on a list row for one line of secondary text.\n */\nexport const mutualsSchema = z.object({\n count: z.number().int().nonnegative(),\n /** Up to three, for the stacked faces. An entry is null when that person has no photo. */\n avatars: z.array(z.string().nullable()),\n /** Up to three names, for the sentence. */\n sampleNames: z.array(z.string()),\n});\nexport type Mutuals = z.infer<typeof mutualsSchema>;\n\n/** How many faces and names a mutuals line carries. */\nexport const MUTUALS_SAMPLE_SIZE = 3;\n\n// ─── People list ─────────────────────────────────────────────────────────────\n\n/**\n * Why this person is in the viewer's list.\n *\n * `gym` at the viewer's gym, not followed\n * `following` followed by the viewer, and NOT at their gym any more\n * `both` at the gym AND followed\n *\n * The distinction is load-bearing rather than cosmetic. Messaging is gated on\n * sharing a gym, so a `following` row must not offer a Message button the\n * backend will refuse. jansathi has no equivalent because its People tab is the\n * whole community and there is no second axis to be on the wrong side of.\n */\nexport const buddyRelationSchema = z.enum(['gym', 'following', 'both']);\nexport type BuddyRelation = z.infer<typeof buddyRelationSchema>;\n\n/** Which slice of the list the caller asked for. */\nexport const buddyFilterSchema = z.enum(['all', 'gym', 'following', 'followers']);\nexport type BuddyFilter = z.infer<typeof buddyFilterSchema>;\n\nexport const BUDDY_FILTER_VALUES = ['all', 'gym', 'following', 'followers'] as const;\n\n/**\n * One person as a peer is allowed to see them.\n *\n * Deliberately NOT derived from the owner's member projection. That one carries\n * phone numbers, addresses, emergency contacts, dues and attendance, and the\n * only reliable way to guarantee none of it reaches a peer is for this to be a\n * separate shape rather than the same one with fields removed.\n */\nexport const buddySchema = z.object({\n /** The USER id. This is who you follow and message, not a profile row. */\n userId: objectIdSchema,\n name: z.string(),\n avatarUrl: z.string().nullable(),\n /** \"Member since Mar 2025\", or null for someone with no membership history here. */\n memberSince: z.string().nullable(),\n /**\n * When this person was last seen in the app. Null if never.\n *\n * Carried so the list can offer a \"Recently active\" sort. It is deliberately\n * a raw instant rather than a rendered \"2h ago\": the client is the only side\n * that knows what time it is where the reader is sitting, and a string\n * formatted on the server goes stale the moment it is cached.\n */\n lastActiveAt: isoDateTimeSchema.nullable(),\n relation: buddyRelationSchema,\n viewer: viewerSocialStateSchema,\n mutuals: mutualsSchema.nullable(),\n});\nexport type Buddy = z.infer<typeof buddySchema>;\n\n/** `GET /api/v1/members/at-my-gym?filter=` */\nexport const buddyListQuerySchema = z.object({\n filter: buddyFilterSchema.default('all'),\n});\nexport type BuddyListQuery = z.infer<typeof buddyListQuerySchema>;\n\n// ─── Toggle ──────────────────────────────────────────────────────────────────\n\n/**\n * The state AFTER a toggle.\n *\n * A toggle rather than separate follow and unfollow endpoints: a button acting\n * on stale state can send the wrong one, and then the server has to decide\n * whether a double unfollow is an error. A toggle has one meaning whatever the\n * client believed, and this response says what is now true.\n */\nexport const toggleFollowResultSchema = z.object({\n targetUserId: objectIdSchema,\n following: z.boolean(),\n followerCount: z.number().int().nonnegative(),\n});\nexport type ToggleFollowResult = z.infer<typeof toggleFollowResultSchema>;\n\n/** `GET /api/v1/users/:id/social` — one person's relationship plus their totals. */\nexport const socialSnapshotSchema = z.object({\n /**\n * The person themselves, in the SAME shape as a people-list row.\n *\n * The profile screen and the row show the same facts and offer the same\n * actions, so two shapes would be two places to add a field and one of them\n * being forgotten. `viewer` lives inside it.\n *\n * RECOVERED from the published 0.17.0 `.d.ts`, not from its sourcemap — that\n * map's `sourcesContent` predates this field and dropped it silently, which\n * would have broken the peer-profile screen on the next publish.\n */\n person: buddySchema,\n followerCount: z.number().int().nonnegative(),\n followingCount: z.number().int().nonnegative(),\n});\nexport type SocialSnapshot = z.infer<typeof socialSnapshotSchema>;\n\n// ─── Blocking ────────────────────────────────────────────────────────────────\n//\n// RECOVERED from the published 0.17.0 build, not from its sourcemap: that map's\n// `sourcesContent` predates these two and would have dropped them silently.\n\n/** One row of the viewer's block list. */\nexport const blockedUserSchema = z.object({\n userId: objectIdSchema,\n name: z.string(),\n avatarUrl: z.string().nullable(),\n blockedAt: isoDateTimeSchema,\n});\nexport type BlockedUser = z.infer<typeof blockedUserSchema>;\n\n/** `POST /api/v1/users/:id/block` — the state AFTER the toggle. */\nexport const toggleBlockResultSchema = z.object({\n targetUserId: objectIdSchema,\n blocked: z.boolean(),\n});\nexport type ToggleBlockResult = z.infer<typeof toggleBlockResultSchema>;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gymmonk-schema",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "Shared Zod schemas, enums and domain types for GymMonk (fitness SaaS) — single source of truth (SSOT) consumed by gymmonk-backend and gymmonk-web-client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|