@jchat/addon-sdk 1.0.0-alpha.6 → 1.0.0-alpha.7

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/index.d.mts CHANGED
@@ -1,14 +1,178 @@
1
- import { a as MountPoint, i as MOUNT_POINTS, n as AddonPermission, r as AddonPermissionDefinition, t as AddonManifest } from "./contracts.mjs";
1
+ import { i as MountPoint, n as AddonPermissionDefinition, r as MOUNT_POINTS, t as AddonManifest } from "./contracts.mjs";
2
2
  import { ComponentType, SVGProps } from "react";
3
+ import { InfiniteData, MutationFunction, QueryFunction, QueryFunctionContext, QueryKey, UndefinedInitialDataInfiniteOptions, UndefinedInitialDataOptions, UseInfiniteQueryResult, UseMutationOptions, UseMutationResult, UseQueryResult, useQueryClient } from "@tanstack/react-query";
3
4
  import { Elysia } from "elysia";
4
5
  import { Kysely } from "kysely";
5
6
  //#region ../icon/dist/index.d.mts
6
7
  //#region src/types.d.ts
7
8
  type IconComponent = ComponentType<SVGProps<SVGSVGElement>>;
8
- declare const ICON_NAMES: readonly ["ProfileMen", "ProfileWomen", "Stories", "Search", "AdminPanel", "Investigation", "Logout", "Users", "Settings", "RoomList", "AdminShip", "Announcement", "NewsFeed", "Ranks", "TopPoints", "Like", "DisLike", "Love", "Funny", "Comment", "Chat", "Brush", "Image", "Mic", "More", "Palette", "Smiley", "Send", "Private", "Action", "View", "Female", "Male", "LuSend", "Heart", "Trash", "HorizontalOption", "Menu", "UserGroup", "Globe", "Plus", "Loader", "X", "UserRoundPlus", "Footprints", "Calendar", "Volume", "Youtube", "Upload", "Play", "Pause", "Stop", "Ok", "ImageFill", "MicFill", "YoutubeFill", "PlusFill", "Music", "Gift", "Camera", "Envelope", "Key", "Signature", "UserPen", "Edit", "AddressCard", "Star", "StatusOnline", "StatusAway", "StatusBusy", "StatusEating", "StatusGaming", "StatusSinging", "StatusListening", "StatusStay"];
9
+ declare const ICON_NAMES: readonly ["Activity", "ArrowLeft", "ArrowRight", "Ban", "Bell", "BookOpen", "Brush", "Calendar", "Camera", "ChartBar", "Check", "CheckCheck", "CheckCircle2", "ChevronDown", "ChevronLeft", "ChevronRight", "ChevronUp", "CircleAlert", "CircleCheck", "CircleOff", "CirclePlay", "CirclePlus", "Clock", "Coffee", "Contact", "Cpu", "Database", "Dices", "Ellipsis", "Eraser", "ExternalLink", "Eye", "EyeOff", "Flag", "Flame", "FolderPlus", "Footprints", "Gamepad2", "Gift", "Globe", "HardDrive", "Headphones", "Heart", "Image", "Info", "Key", "KeyRound", "Layers", "LayoutDashboard", "LayoutList", "LayoutTemplate", "Link2", "ListOrdered", "Loader", "LoaderCircle", "Lock", "LogOut", "Mail", "Maximize", "Megaphone", "MemoryStick", "Menu", "MessageCircle", "MessageSquare", "Mic", "Minimize", "Minus", "Monitor", "Moon", "MoreHorizontal", "Music", "Music2", "Newspaper", "Palette", "Paperclip", "PartyPopper", "Pause", "PenLine", "Play", "Plus", "Radio", "Reply", "RotateCcw", "RotateCw", "Search", "Send", "Server", "Settings", "Shield", "ShieldAlert", "ShieldCheck", "SkipForward", "SlidersHorizontal", "Smartphone", "Smile", "Sparkles", "Square", "SquarePen", "Star", "Sun", "ThumbsDown", "ThumbsUp", "Timer", "Trash", "Trash2", "Trophy", "Upload", "User", "UserCheck", "UserCog", "UserMinus", "UserPen", "UserPlus", "UserRoundPlus", "Users", "UserX", "Utensils", "Video", "VideoOff", "Volume", "Volume2", "VolumeX", "X", "Youtube", "Zap"];
9
10
  type IconName = (typeof ICON_NAMES)[number];
10
11
  type IconPack = Record<IconName, IconComponent>;
11
12
  //#endregion
13
+ //#region src/i18n.d.ts
14
+ declare const HOST_LANGUAGES: readonly ["ar", "bg", "hr", "de", "el", "en", "es", "fr", "he", "hi", "kn", "ml", "nl", "pt", "ro", "ru", "ta", "te", "tr", "ur"];
15
+ type HostLanguage = (typeof HOST_LANGUAGES)[number];
16
+ interface AddonLocaleValidationResult {
17
+ valid: boolean;
18
+ errors: string[];
19
+ missingFiles: string[];
20
+ missingKeys: Record<string, string[]>;
21
+ extraKeys: Record<string, string[]>;
22
+ }
23
+ /**
24
+ * Validates that an addon's `locales/` directory contains valid translation files for all 11 host
25
+ * languages with 100% key parity against `en.json`.
26
+ *
27
+ * @param dirPath - Path to the addon's locales directory.
28
+ * @param domainName - Optional label for error messages (defaults to "Addon").
29
+ */
30
+ declare function validateAddonLocales(dirPath: string, domainName?: string): AddonLocaleValidationResult;
31
+ type AddonTranslationDictionary = Record<string, unknown>;
32
+ type AddonTranslationResources = Record<string, AddonTranslationDictionary>;
33
+ type AddonLocaleLoader = (locale: string) => Promise<AddonTranslationDictionary> | AddonTranslationDictionary;
34
+ interface LocaleContextValue {
35
+ locale: string;
36
+ dir: "ltr" | "rtl";
37
+ }
38
+ declare const LocaleContext: import("react").Context<LocaleContextValue>;
39
+ interface TranslatorOptions {
40
+ resources?: AddonTranslationResources;
41
+ defaultLocale?: string;
42
+ fallbackLocale?: string;
43
+ }
44
+ type TranslateFunction<TKeys extends string = string> = (key: TKeys | (string & {}), paramsOrLang?: Record<string, unknown> | string, maybeLang?: string) => string;
45
+ /**
46
+ * Creates a standalone translation function for backend server routes (`src/api.ts`) or utilities.
47
+ *
48
+ * @param resources - Dictionary of translation resources (e.g., `{ en: enJson, ta: taJson }`).
49
+ * @param defaultLocale - Default locale to use if not specified per call (defaults to "en").
50
+ * @param fallbackLocale - Fallback locale if a key is missing in the target language (defaults to
51
+ * "en").
52
+ */
53
+ declare function createTranslator<TResources extends AddonTranslationResources = AddonTranslationResources>(resources?: TResources, defaultLocale?: string, fallbackLocale?: string): TranslateFunction<TResources extends {
54
+ en: infer TEn;
55
+ } ? TEn extends Record<string, unknown> ? Extract<keyof TEn, string> : string : string>;
56
+ /**
57
+ * Hook to access the current active host locale and text direction in an addon component.
58
+ *
59
+ * @example
60
+ * ;```tsx
61
+ * import { useLocale } from "@jchat/addon-sdk"
62
+ *
63
+ * export default function MyAddon() {
64
+ * const { locale, dir, isRtl } = useLocale()
65
+ * return <div dir={dir}>Current language: {locale}</div>
66
+ * }
67
+ * ```
68
+ */
69
+ declare function useLocale(): {
70
+ locale: string;
71
+ dir: "ltr" | "rtl";
72
+ isRtl: boolean;
73
+ };
74
+ /**
75
+ * Hook to access translation function and locale metadata inside addon components. Automatically
76
+ * synchronizes with host language switches and falls back to English. Supports static dictionary
77
+ * resources or dynamic async locale loaders.
78
+ *
79
+ * @example
80
+ * ;```tsx
81
+ * import { useTranslation } from "@jchat/addon-sdk"
82
+ * import locales from "./locales"
83
+ *
84
+ * export default function MyAddon() {
85
+ * const { t, locale, dir } = useTranslation(locales)
86
+ * return (
87
+ * <div dir={dir}>
88
+ * <h1>{t("welcome", { name: "Guest" })}</h1>
89
+ * </div>
90
+ * )
91
+ * }
92
+ * ```
93
+ *
94
+ * @param resourcesOrLoader - Addon-scoped translation dictionaries or async chunk loader.
95
+ * @param fallbackLocale - Fallback language code when a string is missing (default: "en").
96
+ */
97
+ declare function useTranslation<TResources extends AddonTranslationResources | AddonLocaleLoader = AddonTranslationResources>(resourcesOrLoader?: TResources, fallbackLocale?: string): {
98
+ t: TranslateFunction<TResources extends {
99
+ en: infer TEn;
100
+ } ? TEn extends Record<string, unknown> ? Extract<keyof TEn, string> : string : string>;
101
+ locale: string;
102
+ dir: "ltr" | "rtl";
103
+ isRtl: boolean;
104
+ isReady: boolean;
105
+ };
106
+ //#endregion
107
+ //#region src/query.d.ts
108
+ interface UseAddonQueryOptions<TData = unknown, TError = Error> extends Omit<UndefinedInitialDataOptions<TData, TError, TData, QueryKey>, "queryKey" | "queryFn"> {
109
+ /** The API path to fetch, automatically prefixed with `/api/v1/addons/<addonId>` */
110
+ path?: string;
111
+ /** Custom query key elements appended to the automatic path cache key */
112
+ queryKey?: QueryKey;
113
+ /** Custom query function override */
114
+ queryFn?: QueryFunction<TData, QueryKey>;
115
+ }
116
+ interface UseAddonMutationOptions<TData = unknown, TVariables = unknown, TError = Error, TContext = unknown> extends Omit<UseMutationOptions<TData, TError, TVariables, TContext>, "mutationFn"> {
117
+ /** The target API path, or a dynamic function of mutation variables */
118
+ path?: string | ((variables: TVariables) => string);
119
+ /** HTTP method to use (defaults to 'POST') */
120
+ method?: "POST" | "PUT" | "DELETE";
121
+ /** Query keys to automatically invalidate upon successful mutation */
122
+ invalidateQueries?: QueryKey[];
123
+ /** Custom mutation function override */
124
+ mutationFn?: MutationFunction<TData, TVariables>;
125
+ }
126
+ interface UseAddonInfiniteQueryOptions<TData = unknown, TError = Error, TPageParam = unknown> extends Omit<UndefinedInitialDataInfiniteOptions<TData, TError, InfiniteData<TData, TPageParam>, QueryKey, TPageParam>, "queryKey" | "queryFn"> {
127
+ /** The API path to fetch, or a dynamic function that receives pageParam and returns the path */
128
+ path?: string | ((pageParam: TPageParam) => string);
129
+ /** Custom query key elements appended to the automatic cache key */
130
+ queryKey?: QueryKey;
131
+ /** Custom query function override */
132
+ queryFn?: (context: QueryFunctionContext<QueryKey, TPageParam>) => Promise<TData>;
133
+ }
134
+ /**
135
+ * Perform a declarative, cached GET request using the host's shared React Query instance.
136
+ * Automatically injects authentication headers and prefixes addon paths.
137
+ *
138
+ * @example
139
+ * ;```tsx
140
+ * const { data, isLoading } = useAddonQuery<{ polls: Poll[] }>({
141
+ * path: "/polls",
142
+ * queryKey: [siteId]
143
+ * })
144
+ * ```
145
+ */
146
+ declare function useAddonQuery<TData = unknown, TError = Error>(options: UseAddonQueryOptions<TData, TError>): UseQueryResult<TData, TError>;
147
+ /**
148
+ * Perform a paginated or infinite scrolling request using the host's shared React Query instance.
149
+ *
150
+ * @example
151
+ * ;```tsx
152
+ * const { data, fetchNextPage, hasNextPage } = useAddonInfiniteQuery<GifResponse, Error, number>({
153
+ * path: (page) => `/trending?offset=${page * 20}`,
154
+ * initialPageParam: 0,
155
+ * getNextPageParam: (lastPage, allPages) => (lastPage.hasMore ? allPages.length : undefined)
156
+ * })
157
+ * ```
158
+ */
159
+ declare function useAddonInfiniteQuery<TData = unknown, TError = Error, TPageParam = unknown>(options: UseAddonInfiniteQueryOptions<TData, TError, TPageParam>): UseInfiniteQueryResult<InfiniteData<TData, TPageParam>, TError>;
160
+ /**
161
+ * Perform a declarative mutation using the host's shared React Query instance. Automatically
162
+ * executes the request and can invalidate specified query keys upon success.
163
+ *
164
+ * @example
165
+ * ;```tsx
166
+ * const { mutate, isPending } = useAddonMutation<Poll, CreatePollInput>({
167
+ * path: "/polls",
168
+ * method: "POST",
169
+ * invalidateQueries: [["/polls"]],
170
+ * onSuccess: () => sdk.toast("Poll created!", "success")
171
+ * })
172
+ * ```
173
+ */
174
+ declare function useAddonMutation<TData = unknown, TVariables = unknown, TError = Error, TContext = unknown>(options: UseAddonMutationOptions<TData, TVariables, TError, TContext>): UseMutationResult<TData, TError, TVariables, TContext>;
175
+ //#endregion
12
176
  //#region src/index.d.ts
13
177
  type AddonJsonBody = Record<string, unknown> | unknown[] | null;
14
178
  /**
@@ -32,7 +196,7 @@ interface AddonUser {
32
196
  gender: string;
33
197
  }
34
198
  interface AddonRequestUser {
35
- id?: string;
199
+ id: string;
36
200
  permissions?: string[];
37
201
  siteId?: string;
38
202
  }
@@ -41,6 +205,32 @@ interface AddonRequestUser {
41
205
  * schema switching, dialect/connection access, and lifecycle methods.
42
206
  */
43
207
  type AddonDatabase<TDatabase = Record<string, Record<string, unknown>>> = Pick<Kysely<TDatabase>, "deleteFrom" | "fn" | "insertInto" | "selectFrom" | "transaction" | "updateTable">;
208
+ /**
209
+ * Scoped caching and key-value client provided to addon server instances. Keys are automatically
210
+ * namespaced by the host as `addon:<addonId>:<key>`.
211
+ */
212
+ interface AddonCacheClient {
213
+ /** Retrieve a string or JSON-parsed value by key */
214
+ get: <T = string>(key: string) => Promise<T | null>;
215
+ /** Store a value with an optional time-to-live in seconds */
216
+ set: (key: string, value: unknown, ttlSeconds?: number) => Promise<void>;
217
+ /** Delete one or more keys */
218
+ del: (...keys: string[]) => Promise<number>;
219
+ /** Atomically increment a numeric counter and return the new value */
220
+ incr: (key: string) => Promise<number>;
221
+ /** Set a timeout on a key in seconds */
222
+ expire: (key: string, ttlSeconds: number) => Promise<boolean>;
223
+ /** Get the remaining time-to-live of a key in seconds (-2 if key does not exist, -1 if no TTL) */
224
+ ttl: (key: string) => Promise<number>;
225
+ }
226
+ interface AddonServerEmitTarget {
227
+ /** Target a specific site (defaults to the request user's siteId) */
228
+ siteId?: string;
229
+ /** Target a specific chat room */
230
+ roomId?: string;
231
+ /** Target a specific user */
232
+ userId?: string;
233
+ }
44
234
  /**
45
235
  * Context provided to an addon's server-side factory. Parameterize it with the addon's table
46
236
  * interface: `AddonServerContext<MyAddonDatabase>`.
@@ -62,13 +252,44 @@ interface AddonServerContext<TDatabase = Record<string, Record<string, unknown>>
62
252
  * @param user - The user object from the request context
63
253
  * @param permission - The permission to check, e.g., 'message.send' or 'addon.stories.view'
64
254
  */
65
- hasPermission: (user: AddonRequestUser | null | undefined, permission: AddonPermission) => boolean;
255
+ hasPermission: (user: AddonRequestUser | null | undefined, permission: string) => boolean;
66
256
  /**
67
257
  * A Kysely database instance scoped entirely to your addon's tables. You cannot access core Jchat
68
258
  * tables (Users, Rooms, etc.) through this instance.
69
259
  */
70
260
  db: AddonDatabase<TDatabase>;
261
+ /** A secure, namespaced key-value cache client scoped to your addon (`addon:<addonId>:<key>`). */
262
+ cache: AddonCacheClient;
263
+ /**
264
+ * Broadcast a real-time event to connected clients across the site or room. Clients listen via
265
+ * `sdk.events.on('addon:<addonId>:<event>', callback)` or `sdk.events.on(event, callback)`.
266
+ */
267
+ emit: (event: string, payload: unknown, target?: AddonServerEmitTarget) => Promise<void>;
71
268
  }
269
+ /**
270
+ * Elysia app instance provided to addon server endpoints with Jchat macro metadata (e.g.
271
+ * requirePermission) typed.
272
+ */
273
+ type AddonApp = Elysia<"", {
274
+ decorator: {};
275
+ store: {};
276
+ derive: {};
277
+ resolve: {
278
+ user: AddonRequestUser;
279
+ };
280
+ }, {
281
+ typebox: {};
282
+ error: {};
283
+ }, {
284
+ schema: {};
285
+ standaloneSchema: {};
286
+ macro: {
287
+ requirePermission?: string | string[];
288
+ };
289
+ macroFn: {};
290
+ parser: {};
291
+ response: {};
292
+ }>;
72
293
  /**
73
294
  * The entry point for your addon's server bundle. You should default-export a function matching
74
295
  * this signature.
@@ -83,7 +304,7 @@ interface AddonServerContext<TDatabase = Record<string, Record<string, unknown>>
83
304
  * export default factory
84
305
  * ```
85
306
  */
86
- type AddonServerFactory<TDatabase = Record<string, Record<string, unknown>>> = (ctx: AddonServerContext<TDatabase>, app: Elysia) => Elysia;
307
+ type AddonServerFactory<TDatabase = Record<string, Record<string, unknown>>> = (ctx: AddonServerContext<TDatabase>, app: AddonApp) => Elysia;
87
308
  /** Props passed to your addon's root React component by the Jchat host. */
88
309
  interface AddonProps {
89
310
  /** The mount point where this addon instance is rendered (e.g., 'chat-sidebar') */
@@ -100,6 +321,10 @@ interface AddonProps {
100
321
  roomId?: string;
101
322
  /** The ID of the active Jchat site */
102
323
  siteId?: string;
324
+ /** The active user locale (e.g. 'en', 'ta', 'ar') */
325
+ locale?: string;
326
+ /** Text direction for the current locale */
327
+ dir?: "ltr" | "rtl";
103
328
  }
104
329
  interface AddonApi {
105
330
  get: <TResponse = unknown>(path: string) => Promise<TResponse>;
@@ -107,6 +332,13 @@ interface AddonApi {
107
332
  put: <TResponse = unknown>(path: string, body?: AddonJsonBody) => Promise<TResponse>;
108
333
  delete: <TResponse = unknown>(path: string) => Promise<TResponse>;
109
334
  }
335
+ /** First-class addon settings management interface provided by the host. */
336
+ interface AddonSettingsApi {
337
+ /** Fetch the current site's saved configuration for this addon */
338
+ get: <T = Record<string, unknown>>() => Promise<T | null>;
339
+ /** Save updated configuration for this addon for the current site */
340
+ save: <T = Record<string, unknown>>(settings: T) => Promise<void>;
341
+ }
110
342
  type AddonEventListener = (data: unknown) => void;
111
343
  /** A font option provided by the JChat host. */
112
344
  interface AddonFont {
@@ -128,9 +360,15 @@ interface AddonFont {
128
360
  interface AddonSDK {
129
361
  /**
130
362
  * Make authenticated API calls to your addon's backend or core Jchat endpoints. All calls
131
- * automatically include the current user's sessionId and site validation headers.
363
+ * automatically include the current user's sessionId and site validation headers. Relative paths
364
+ * are automatically prefixed with `/api/v1/addons/<addonId>`.
132
365
  */
133
366
  api: AddonApi;
367
+ /**
368
+ * First-class settings API to load and save addon configuration without boilerplate path
369
+ * construction.
370
+ */
371
+ settings: AddonSettingsApi;
134
372
  /**
135
373
  * Display a temporary notification toast in the host UI.
136
374
  *
@@ -190,6 +428,10 @@ interface AddonSDK {
190
428
  * Use this to build a font picker or apply a matching font family in your addon UI.
191
429
  */
192
430
  supportedFonts?: AddonFont[];
431
+ /** The active user locale (e.g. 'en', 'ta', 'ar') */
432
+ locale: string;
433
+ /** Text direction for the active locale ('ltr' or 'rtl') */
434
+ dir: "ltr" | "rtl";
193
435
  }
194
436
  /**
195
437
  * Seed data for your addon's tables, provided inside the addon's isolated schema.
@@ -269,4 +511,4 @@ declare function useIcons(): {
269
511
  */
270
512
  declare function getAddonSdkVersion(): string;
271
513
  //#endregion
272
- export { AddonApi, AddonDatabase, AddonEventListener, AddonFont, AddonJsonBody, type AddonManifest, type AddonPermission, type AddonPermissionDefinition, AddonProps, AddonRequestUser, AddonSDK, AddonSeedDocument, AddonServerContext, AddonServerFactory, AddonUser, MOUNT_POINTS, type MountPoint, defineAddonManifest, defineAddonServer, getAddonSdkVersion, useAddonSDK, useIcons };
514
+ export { AddonApi, AddonApp, AddonCacheClient, AddonDatabase, AddonEventListener, AddonFont, AddonJsonBody, AddonLocaleLoader, AddonLocaleValidationResult, type AddonManifest, type AddonPermissionDefinition, AddonProps, AddonRequestUser, AddonSDK, AddonSeedDocument, AddonServerContext, AddonServerEmitTarget, AddonServerFactory, AddonSettingsApi, AddonTranslationDictionary, AddonTranslationResources, AddonUser, HOST_LANGUAGES, HostLanguage, ICON_NAMES, type IconComponent, type IconName, type IconPack, type InfiniteData, LocaleContext, LocaleContextValue, MOUNT_POINTS, type MountPoint, type QueryKey, TranslateFunction, TranslatorOptions, UseAddonInfiniteQueryOptions, UseAddonMutationOptions, UseAddonQueryOptions, createTranslator, defineAddonManifest, defineAddonServer, getAddonSdkVersion, useAddonInfiniteQuery, useAddonMutation, useAddonQuery, useAddonSDK, useIcons, useLocale, useQueryClient, useTranslation, validateAddonLocales };