@shadowob/shared 0.3.4 → 0.4.1

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.
@@ -0,0 +1,362 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/utils/index.ts
21
+ var utils_exports = {};
22
+ __export(utils_exports, {
23
+ CAT_AVATAR_COUNT: () => CAT_AVATAR_COUNT,
24
+ formatDate: () => formatDate,
25
+ generateInviteCode: () => generateInviteCode,
26
+ generateRandomCatConfig: () => generateRandomCatConfig,
27
+ getAllCatAvatars: () => getAllCatAvatars,
28
+ getCatAvatar: () => getCatAvatar,
29
+ getCatAvatarByUserId: () => getCatAvatarByUserId,
30
+ getCatSvgString: () => getCatSvgString,
31
+ isValidEmail: () => isValidEmail,
32
+ renderCatSvg: () => renderCatSvg,
33
+ slugify: () => slugify
34
+ });
35
+ module.exports = __toCommonJS(utils_exports);
36
+ var import_nanoid = require("nanoid");
37
+
38
+ // src/utils/avatar-generator.ts
39
+ var COLORS = {
40
+ bg: [
41
+ "transparent",
42
+ "#1e1f22",
43
+ "#313338",
44
+ "#5865F2",
45
+ "#23a559",
46
+ "#da373c",
47
+ "#f472b6",
48
+ "#3b82f6",
49
+ "#fbbf24",
50
+ "#a855f7",
51
+ "#1abc9c",
52
+ "#f39c12",
53
+ "#e74c3c"
54
+ ],
55
+ body: [
56
+ "#2d2d30",
57
+ "#e8842c",
58
+ "#e8e8e8",
59
+ "#7a7a80",
60
+ "#d4a574",
61
+ "#6b8094",
62
+ "#f472b6",
63
+ "#c8d6e5",
64
+ "#3e2723",
65
+ "#bdc3c7",
66
+ "#ffb8b8"
67
+ ],
68
+ eyes: [
69
+ "#f8e71c",
70
+ "#00f3ff",
71
+ "#4ade80",
72
+ "#60a5fa",
73
+ "#a855f7",
74
+ "#fbbf24",
75
+ "#f87171",
76
+ "#ffc0cb",
77
+ "#1dd1a1",
78
+ "#e056fd"
79
+ ],
80
+ pattern: ["#1a1a1c", "#ffffff", "#5a4a46", "#3d3d40", "#9a9aa0", "#d1ccc0", "#2d3436"]
81
+ };
82
+ function getRandomElement(arr) {
83
+ return arr[Math.floor(Math.random() * arr.length)];
84
+ }
85
+ function generateRandomCatConfig() {
86
+ return {
87
+ bg: getRandomElement(COLORS.bg),
88
+ bgPattern: getRandomElement(["none", "dots", "stripes", "grid", "stars"]),
89
+ body: getRandomElement(COLORS.body),
90
+ pattern: getRandomElement([
91
+ "none",
92
+ "tabby",
93
+ "tuxedo",
94
+ "siamese",
95
+ "calico",
96
+ "bicolor"
97
+ ]),
98
+ patternColor: getRandomElement(COLORS.pattern),
99
+ eyeColor: getRandomElement(COLORS.eyes),
100
+ expression: getRandomElement([
101
+ "smile",
102
+ "open",
103
+ "flat",
104
+ "sad",
105
+ "surprised",
106
+ "kawaii",
107
+ "winking",
108
+ "smirk"
109
+ ]),
110
+ decoration: getRandomElement([
111
+ "none",
112
+ "glasses",
113
+ "blush",
114
+ "scar",
115
+ "flower",
116
+ "fish",
117
+ "headband"
118
+ ])
119
+ };
120
+ }
121
+ function renderCatSvg(config) {
122
+ const { bg, bgPattern, body, pattern, patternColor, eyeColor, expression, decoration } = config;
123
+ const stroke = "#1a1a1c";
124
+ let svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">`;
125
+ if (bg && bg !== "transparent") {
126
+ svg += `<rect width="100" height="100" fill="${bg}" rx="20" />`;
127
+ const pColor = `rgba(255,255,255,0.15)`;
128
+ const cleanBg = bg.replace("#", "");
129
+ if (bgPattern === "dots") {
130
+ svg += `<pattern id="p-${cleanBg}-dots" x="0" y="0" width="10" height="10" patternUnits="userSpaceOnUse"><circle cx="2" cy="2" r="2" fill="${pColor}"/></pattern><rect width="100" height="100" fill="url(#p-${cleanBg}-dots)" rx="20" />`;
131
+ } else if (bgPattern === "stripes") {
132
+ svg += `<pattern id="p-${cleanBg}-str" x="0" y="0" width="12" height="12" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="12" stroke="${pColor}" stroke-width="4"/></pattern><rect width="100" height="100" fill="url(#p-${cleanBg}-str)" rx="20" />`;
133
+ } else if (bgPattern === "grid") {
134
+ svg += `<pattern id="p-${cleanBg}-grid" width="16" height="16" patternUnits="userSpaceOnUse"><path d="M 16 0 L 0 0 0 16" fill="none" stroke="${pColor}" stroke-width="1"/></pattern><rect width="100" height="100" fill="url(#p-${cleanBg}-grid)" rx="20" />`;
135
+ } else if (bgPattern === "stars") {
136
+ svg += `<pattern id="p-${cleanBg}-star" width="20" height="20" patternUnits="userSpaceOnUse"><path d="M10,2 L12,8 L18,8 L13,12 L15,18 L10,14 L5,18 L7,12 L2,8 L8,8 Z" fill="${pColor}" transform="scale(0.5) translate(5,5)"/></pattern><rect width="100" height="100" fill="url(#p-${cleanBg}-star)" rx="20" />`;
137
+ }
138
+ }
139
+ svg += `<ellipse cx="50" cy="85" rx="30" ry="6" fill="rgba(0,0,0,0.2)"/>`;
140
+ svg += `<path d="M22,45 C15,22 28,18 34,22 C38,25 40,38 40,38" fill="${body}" stroke="${stroke}" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>`;
141
+ svg += `<path d="M78,45 C85,22 72,18 66,22 C62,25 60,38 60,38" fill="${body}" stroke="${stroke}" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>`;
142
+ svg += `<path d="M26,38 C22,26 29,24 33,26 C35,28 36,34 36,34" fill="#ffb8b8" opacity="0.8"/>`;
143
+ svg += `<path d="M74,38 C78,26 71,24 67,26 C65,28 64,34 64,34" fill="#ffb8b8" opacity="0.8"/>`;
144
+ svg += `<ellipse cx="50" cy="58" rx="38" ry="30" fill="${body}" stroke="${stroke}" stroke-width="2.5"/>`;
145
+ if (pattern === "tabby") {
146
+ svg += `<path d="M50,30 L50,40 M42,32 L46,39 M58,32 L54,39" stroke="${patternColor}" stroke-width="3" stroke-linecap="round" opacity="0.7"/>`;
147
+ svg += `<path d="M18,55 L26,57 M20,62 L27,62" stroke="${patternColor}" stroke-width="2.5" stroke-linecap="round" opacity="0.7"/>`;
148
+ svg += `<path d="M82,55 L74,57 M80,62 L73,62" stroke="${patternColor}" stroke-width="2.5" stroke-linecap="round" opacity="0.7"/>`;
149
+ } else if (pattern === "tuxedo") {
150
+ svg += `<path d="M50,56 C30,68 28,88 50,88 C72,88 70,68 50,56" fill="${patternColor}" opacity="0.95"/>`;
151
+ svg += `<ellipse cx="50" cy="65" rx="16" ry="12" fill="${patternColor}" opacity="0.95"/>`;
152
+ } else if (pattern === "siamese") {
153
+ svg += `<ellipse cx="50" cy="62" rx="20" ry="16" fill="${patternColor}" opacity="0.6"/>`;
154
+ } else if (pattern === "calico") {
155
+ svg += `<path d="M25,40 Q35,30 45,45 Q35,55 25,40" fill="${patternColor}" opacity="0.8"/>`;
156
+ svg += `<path d="M75,45 Q65,60 55,50 Q65,35 75,45" fill="#e8842c" opacity="0.8"/>`;
157
+ } else if (pattern === "bicolor") {
158
+ svg += `<path d="M12,58 Q30,30 50,40 Q60,70 50,88 Q12,88 12,58 Z" fill="${patternColor}" opacity="0.8"/>`;
159
+ }
160
+ if (decoration === "blush") {
161
+ svg += `<ellipse cx="28" cy="62" rx="5" ry="3" fill="#ff7675" opacity="0.7"/>`;
162
+ svg += `<ellipse cx="72" cy="62" rx="5" ry="3" fill="#ff7675" opacity="0.7"/>`;
163
+ }
164
+ if (decoration === "scar") {
165
+ svg += `<path d="M28,42 L40,52 M30,48 L35,43 M34,51 L39,46 M32,53 L37,48" stroke="#d63031" stroke-width="1.5" stroke-linecap="round"/>`;
166
+ }
167
+ const drawEye = (cx, cy, lookDir = 0) => {
168
+ if (expression === "kawaii") {
169
+ return `<path d="M${cx - 8},${cy} Q${cx},${cy - 8} ${cx + 8},${cy} Q${cx},${cy - 3} ${cx - 8},${cy}" fill="${eyeColor}" stroke="${stroke}" stroke-width="1.5"/><circle cx="${cx + lookDir}" cy="${cy - 2}" r="3" fill="white"/><circle cx="${cx - 3 + lookDir}" cy="${cy}" r="1" fill="white"/>`;
170
+ } else if (expression === "winking" && cx > 50) {
171
+ return `<path d="M${cx - 7},${cy + 2} Q${cx},${cy - 4} ${cx + 7},${cy + 2}" fill="none" stroke="${stroke}" stroke-width="2.5" stroke-linecap="round"/>`;
172
+ } else if (expression === "surprised") {
173
+ return `<circle cx="${cx}" cy="${cy}" r="8" fill="white" stroke="${stroke}" stroke-width="1.5"/><circle cx="${cx}" cy="${cy}" r="3" fill="${eyeColor}"/>`;
174
+ } else {
175
+ return `<circle cx="${cx}" cy="${cy}" r="7.5" fill="${eyeColor}" stroke="${stroke}" stroke-width="1.5"/><circle cx="${cx - 2 + lookDir}" cy="${cy - 2}" r="2.5" fill="white"/><circle cx="${cx + 2 + lookDir}" cy="${cy + 1}" r="1" fill="white"/>`;
176
+ }
177
+ };
178
+ if (expression === "smirk") {
179
+ svg += `<path d="M26,50 L42,50" stroke="${stroke}" stroke-width="2.5" stroke-linecap="round"/>`;
180
+ svg += drawEye(66, 52, 2);
181
+ } else {
182
+ svg += drawEye(34, 52, 0);
183
+ svg += drawEye(66, 52, 0);
184
+ }
185
+ if (decoration === "glasses") {
186
+ svg += `<circle cx="34" cy="52" r="11" fill="rgba(255,255,255,0.2)" stroke="#2d3436" stroke-width="2.5"/>`;
187
+ svg += `<circle cx="66" cy="52" r="11" fill="rgba(255,255,255,0.2)" stroke="#2d3436" stroke-width="2.5"/>`;
188
+ svg += `<path d="M45,50 Q50,48 55,50" fill="none" stroke="#2d3436" stroke-width="2.5" stroke-linecap="round"/>`;
189
+ }
190
+ svg += `<path d="M47,62 L53,62 L50,65 Z" fill="#ff9ff3" stroke="${stroke}" stroke-width="1" stroke-linejoin="round"/>`;
191
+ if (expression === "smile" || expression === "kawaii" || expression === "winking") {
192
+ svg += `<path d="M42,67 Q46,72 50,67 M50,67 Q54,72 58,67" fill="none" stroke="${stroke}" stroke-width="2" stroke-linecap="round"/>`;
193
+ } else if (expression === "open") {
194
+ svg += `<path d="M46,67 Q50,75 54,67 Z" fill="#ff7675" stroke="${stroke}" stroke-width="1.5" stroke-linejoin="round"/>`;
195
+ } else if (expression === "flat") {
196
+ svg += `<path d="M47,68 L53,68" stroke="${stroke}" stroke-width="2" stroke-linecap="round"/>`;
197
+ } else if (expression === "sad") {
198
+ svg += `<path d="M43,70 Q50,64 57,70" fill="none" stroke="${stroke}" stroke-width="2" stroke-linecap="round"/>`;
199
+ } else if (expression === "surprised") {
200
+ svg += `<circle cx="50" cy="70" r="3" fill="#1a1a1c"/>`;
201
+ } else if (expression === "smirk") {
202
+ svg += `<path d="M46,67 Q52,69 56,64" fill="none" stroke="${stroke}" stroke-width="2" stroke-linecap="round"/>`;
203
+ }
204
+ svg += `<path d="M28,62 L15,60 M28,65 L14,66" stroke="${stroke}" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>`;
205
+ svg += `<path d="M72,62 L85,60 M72,65 L86,66" stroke="${stroke}" stroke-width="1.5" stroke-linecap="round" opacity="0.6"/>`;
206
+ if (decoration === "headband") {
207
+ svg += `<path d="M16,35 Q50,20 84,35" fill="none" stroke="#e17055" stroke-width="6" stroke-linecap="round"/>`;
208
+ svg += `<path d="M50,15 L60,25 L50,28 L40,25 Z" fill="#fab1a0" stroke="#e17055" stroke-width="2"/>`;
209
+ } else if (decoration === "flower") {
210
+ svg += `<path d="M75,30 Q80,20 85,30 Q95,25 85,35 Q90,45 80,40 Q70,45 75,35 Q65,25 75,30 Z" fill="#ffeaa7" stroke="#fdcb6e" stroke-width="1.5"/>`;
211
+ svg += `<circle cx="80" cy="33" r="3" fill="#d63031"/>`;
212
+ } else if (decoration === "fish") {
213
+ svg += `<path d="M40,82 Q50,75 60,82 L65,78 L65,86 L60,82 Q50,89 40,82 Z" fill="#81ecec" stroke="${stroke}" stroke-width="1.5"/>`;
214
+ svg += `<circle cx="45" cy="81" r="1" fill="${stroke}"/>`;
215
+ }
216
+ svg += `</svg>`;
217
+ return `data:image/svg+xml,${encodeURIComponent(svg.replace(/\n\s*/g, ""))}`;
218
+ }
219
+
220
+ // src/utils/pixel-cats.ts
221
+ var variants = [
222
+ // 0: Shadow — Black cat (logo style)
223
+ {
224
+ body: "#2d2d30",
225
+ stroke: "#1a1a1c",
226
+ earInner: "#3d3d40",
227
+ eyeL: "#f8e71c",
228
+ eyeR: "#00f3ff",
229
+ nose: "#3a2a26"
230
+ },
231
+ // 1: Mikan — Orange tabby
232
+ {
233
+ body: "#e8842c",
234
+ stroke: "#1a1a1c",
235
+ earInner: "#f5a623",
236
+ eyeL: "#4ade80",
237
+ eyeR: "#4ade80",
238
+ nose: "#d46b1a"
239
+ },
240
+ // 2: Yuki — White cat
241
+ {
242
+ body: "#e8e8e8",
243
+ stroke: "#a0a0a0",
244
+ earInner: "#ffc0cb",
245
+ eyeL: "#60a5fa",
246
+ eyeR: "#60a5fa",
247
+ nose: "#f5a0b0"
248
+ },
249
+ // 3: Haiiro — Gray cat
250
+ {
251
+ body: "#7a7a80",
252
+ stroke: "#4a4a50",
253
+ earInner: "#9a9aa0",
254
+ eyeL: "#fbbf24",
255
+ eyeR: "#fbbf24",
256
+ nose: "#5a4a46"
257
+ },
258
+ // 4: Tuxedo — Black & white accents
259
+ {
260
+ body: "#2d2d30",
261
+ stroke: "#1a1a1c",
262
+ earInner: "#e0e0e0",
263
+ eyeL: "#22c55e",
264
+ eyeR: "#22c55e",
265
+ nose: "#3a2a26"
266
+ },
267
+ // 5: Mocha — Cream/beige
268
+ {
269
+ body: "#d4a574",
270
+ stroke: "#8b6914",
271
+ earInner: "#e8c9a0",
272
+ eyeL: "#d97706",
273
+ eyeR: "#d97706",
274
+ nose: "#a0705a"
275
+ },
276
+ // 6: Blue — Russian blue
277
+ {
278
+ body: "#6b8094",
279
+ stroke: "#3d5060",
280
+ earInner: "#8ba0b4",
281
+ eyeL: "#22c55e",
282
+ eyeR: "#22c55e",
283
+ nose: "#5a6a76"
284
+ },
285
+ // 7: Sakura — Fantasy pink
286
+ {
287
+ body: "#f472b6",
288
+ stroke: "#be185d",
289
+ earInner: "#f9a8d4",
290
+ eyeL: "#a855f7",
291
+ eyeR: "#c084fc",
292
+ nose: "#d44a8c"
293
+ }
294
+ ];
295
+ function makeCatSvg(c) {
296
+ return [
297
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">',
298
+ `<path d="M22,45 Q15,22 28,22 Q34,22 40,38" fill="${c.body}" stroke="${c.stroke}" stroke-width="2.5" stroke-linecap="round"/>`,
299
+ `<path d="M78,45 Q85,22 72,22 Q66,22 60,38" fill="${c.body}" stroke="${c.stroke}" stroke-width="2.5" stroke-linecap="round"/>`,
300
+ `<path d="M26,38 Q22,26 29,26 Q33,26 36,34" fill="${c.earInner}" opacity="0.5"/>`,
301
+ `<path d="M74,38 Q78,26 71,26 Q67,26 64,34" fill="${c.earInner}" opacity="0.5"/>`,
302
+ `<ellipse cx="50" cy="58" rx="36" ry="28" fill="${c.body}" stroke="${c.stroke}" stroke-width="2.5"/>`,
303
+ `<circle cx="35" cy="52" r="7" fill="${c.eyeL}" stroke="${c.stroke}" stroke-width="1.5"/>`,
304
+ `<circle cx="33" cy="49" r="2.5" fill="white"/>`,
305
+ `<circle cx="65" cy="52" r="7" fill="${c.eyeR}" stroke="${c.stroke}" stroke-width="1.5"/>`,
306
+ `<circle cx="63" cy="49" r="2.5" fill="white"/>`,
307
+ `<ellipse cx="50" cy="62" rx="3.5" ry="2.2" fill="${c.nose}"/>`,
308
+ `<path d="M42,67 Q46,72 50,67" fill="none" stroke="${c.stroke}" stroke-width="2" stroke-linecap="round"/>`,
309
+ `<path d="M50,67 Q54,72 58,67" fill="none" stroke="${c.stroke}" stroke-width="2" stroke-linecap="round"/>`,
310
+ "</svg>"
311
+ ].join("");
312
+ }
313
+ var catDataUris = variants.map((v) => {
314
+ const svg = makeCatSvg(v);
315
+ return `data:image/svg+xml,${encodeURIComponent(svg)}`;
316
+ });
317
+ function getCatAvatar(index) {
318
+ return catDataUris[index % catDataUris.length];
319
+ }
320
+ function getCatAvatarByUserId(userId) {
321
+ let hash = 0;
322
+ for (let i = 0; i < userId.length; i++) {
323
+ hash = (hash << 5) - hash + userId.charCodeAt(i) | 0;
324
+ }
325
+ return getCatAvatar(Math.abs(hash) % catDataUris.length);
326
+ }
327
+ function getAllCatAvatars() {
328
+ const names = ["\u5F71\u5B50", "\u871C\u67D1", "\u5C0F\u96EA", "\u7070\u7070", "\u71D5\u5C3E\u670D", "\u6469\u5361", "\u84DD\u84DD", "\u5C0F\u6A31"];
329
+ return catDataUris.map((uri, i) => ({ index: i, name: names[i], dataUri: uri }));
330
+ }
331
+ function getCatSvgString(index) {
332
+ return makeCatSvg(variants[index % variants.length]);
333
+ }
334
+ var CAT_AVATAR_COUNT = variants.length;
335
+
336
+ // src/utils/index.ts
337
+ var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
338
+ var generateInviteCode = (0, import_nanoid.customAlphabet)(alphabet, 8);
339
+ function formatDate(date) {
340
+ const d = typeof date === "string" ? new Date(date) : date;
341
+ return d.toISOString();
342
+ }
343
+ function isValidEmail(email) {
344
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
345
+ }
346
+ function slugify(text) {
347
+ return text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
348
+ }
349
+ // Annotate the CommonJS export names for ESM import in node:
350
+ 0 && (module.exports = {
351
+ CAT_AVATAR_COUNT,
352
+ formatDate,
353
+ generateInviteCode,
354
+ generateRandomCatConfig,
355
+ getAllCatAvatars,
356
+ getCatAvatar,
357
+ getCatAvatarByUserId,
358
+ getCatSvgString,
359
+ isValidEmail,
360
+ renderCatSvg,
361
+ slugify
362
+ });
@@ -0,0 +1,41 @@
1
+ type CatPattern = 'none' | 'tabby' | 'tuxedo' | 'siamese' | 'calico' | 'bicolor';
2
+ type CatExpression = 'smile' | 'open' | 'flat' | 'sad' | 'surprised' | 'kawaii' | 'winking' | 'smirk';
3
+ type CatDecoration = 'none' | 'glasses' | 'blush' | 'scar' | 'flower' | 'fish' | 'headband';
4
+ type BgPattern = 'none' | 'dots' | 'stripes' | 'grid' | 'stars';
5
+ interface CatConfig {
6
+ bg: string;
7
+ bgPattern: BgPattern;
8
+ body: string;
9
+ pattern: CatPattern;
10
+ patternColor: string;
11
+ eyeColor: string;
12
+ expression: CatExpression;
13
+ decoration: CatDecoration;
14
+ }
15
+ declare function generateRandomCatConfig(): CatConfig;
16
+ declare function renderCatSvg(config: CatConfig): string;
17
+
18
+ /**
19
+ * Pixel Art Cat Avatar System
20
+ * 8 unique cat variants with distinct color schemes inspired by the Shadow logo
21
+ */
22
+ /** Get avatar data URI by index (0-7) */
23
+ declare function getCatAvatar(index: number): string;
24
+ /** Get deterministic avatar by user ID string */
25
+ declare function getCatAvatarByUserId(userId: string): string;
26
+ /** Get all cat avatars for selection UI */
27
+ declare function getAllCatAvatars(): {
28
+ index: number;
29
+ name: string;
30
+ dataUri: string;
31
+ }[];
32
+ /** Get the raw SVG string for a cat variant (useful for generating PNG files) */
33
+ declare function getCatSvgString(index: number): string;
34
+ declare const CAT_AVATAR_COUNT: number;
35
+
36
+ declare const generateInviteCode: (size?: number) => string;
37
+ declare function formatDate(date: string | Date): string;
38
+ declare function isValidEmail(email: string): boolean;
39
+ declare function slugify(text: string): string;
40
+
41
+ export { type BgPattern, CAT_AVATAR_COUNT, type CatConfig, type CatDecoration, type CatExpression, type CatPattern, formatDate, generateInviteCode, generateRandomCatConfig, getAllCatAvatars, getCatAvatar, getCatAvatarByUserId, getCatSvgString, isValidEmail, renderCatSvg, slugify };
@@ -0,0 +1,41 @@
1
+ type CatPattern = 'none' | 'tabby' | 'tuxedo' | 'siamese' | 'calico' | 'bicolor';
2
+ type CatExpression = 'smile' | 'open' | 'flat' | 'sad' | 'surprised' | 'kawaii' | 'winking' | 'smirk';
3
+ type CatDecoration = 'none' | 'glasses' | 'blush' | 'scar' | 'flower' | 'fish' | 'headband';
4
+ type BgPattern = 'none' | 'dots' | 'stripes' | 'grid' | 'stars';
5
+ interface CatConfig {
6
+ bg: string;
7
+ bgPattern: BgPattern;
8
+ body: string;
9
+ pattern: CatPattern;
10
+ patternColor: string;
11
+ eyeColor: string;
12
+ expression: CatExpression;
13
+ decoration: CatDecoration;
14
+ }
15
+ declare function generateRandomCatConfig(): CatConfig;
16
+ declare function renderCatSvg(config: CatConfig): string;
17
+
18
+ /**
19
+ * Pixel Art Cat Avatar System
20
+ * 8 unique cat variants with distinct color schemes inspired by the Shadow logo
21
+ */
22
+ /** Get avatar data URI by index (0-7) */
23
+ declare function getCatAvatar(index: number): string;
24
+ /** Get deterministic avatar by user ID string */
25
+ declare function getCatAvatarByUserId(userId: string): string;
26
+ /** Get all cat avatars for selection UI */
27
+ declare function getAllCatAvatars(): {
28
+ index: number;
29
+ name: string;
30
+ dataUri: string;
31
+ }[];
32
+ /** Get the raw SVG string for a cat variant (useful for generating PNG files) */
33
+ declare function getCatSvgString(index: number): string;
34
+ declare const CAT_AVATAR_COUNT: number;
35
+
36
+ declare const generateInviteCode: (size?: number) => string;
37
+ declare function formatDate(date: string | Date): string;
38
+ declare function isValidEmail(email: string): boolean;
39
+ declare function slugify(text: string): string;
40
+
41
+ export { type BgPattern, CAT_AVATAR_COUNT, type CatConfig, type CatDecoration, type CatExpression, type CatPattern, formatDate, generateInviteCode, generateRandomCatConfig, getAllCatAvatars, getCatAvatar, getCatAvatarByUserId, getCatSvgString, isValidEmail, renderCatSvg, slugify };
@@ -0,0 +1,26 @@
1
+ import {
2
+ CAT_AVATAR_COUNT,
3
+ formatDate,
4
+ generateInviteCode,
5
+ generateRandomCatConfig,
6
+ getAllCatAvatars,
7
+ getCatAvatar,
8
+ getCatAvatarByUserId,
9
+ getCatSvgString,
10
+ isValidEmail,
11
+ renderCatSvg,
12
+ slugify
13
+ } from "../chunk-PXKHJSTK.js";
14
+ export {
15
+ CAT_AVATAR_COUNT,
16
+ formatDate,
17
+ generateInviteCode,
18
+ generateRandomCatConfig,
19
+ getAllCatAvatars,
20
+ getCatAvatar,
21
+ getCatAvatarByUserId,
22
+ getCatSvgString,
23
+ isValidEmail,
24
+ renderCatSvg,
25
+ slugify
26
+ };
package/package.json CHANGED
@@ -1,19 +1,57 @@
1
1
  {
2
2
  "name": "@shadowob/shared",
3
- "version": "0.3.4",
3
+ "version": "0.4.1",
4
4
  "type": "module",
5
- "main": "./src/index.ts",
6
- "types": "./src/index.ts",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "require": "./dist/index.cjs",
8
+ "types": "./dist/index.d.ts",
7
9
  "exports": {
8
- ".": "./src/index.ts",
9
- "./types": "./src/types/index.ts",
10
- "./constants": "./src/constants/index.ts",
11
- "./utils": "./src/utils/index.ts"
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "development": "./src/index.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "./types": {
18
+ "types": "./dist/types/index.d.ts",
19
+ "development": "./src/types/index.ts",
20
+ "import": "./dist/types/index.js",
21
+ "require": "./dist/types/index.cjs",
22
+ "default": "./dist/types/index.js"
23
+ },
24
+ "./constants": {
25
+ "types": "./dist/constants/index.d.ts",
26
+ "development": "./src/constants/index.ts",
27
+ "import": "./dist/constants/index.js",
28
+ "require": "./dist/constants/index.cjs",
29
+ "default": "./dist/constants/index.js"
30
+ },
31
+ "./utils": {
32
+ "types": "./dist/utils/index.d.ts",
33
+ "development": "./src/utils/index.ts",
34
+ "import": "./dist/utils/index.js",
35
+ "require": "./dist/utils/index.cjs",
36
+ "default": "./dist/utils/index.js"
37
+ }
12
38
  },
39
+ "files": [
40
+ "dist"
41
+ ],
13
42
  "dependencies": {
14
43
  "nanoid": "^5.1.7"
15
44
  },
45
+ "devDependencies": {
46
+ "tsup": "^8.5.0",
47
+ "typescript": "^5.9.3"
48
+ },
49
+ "publishConfig": {
50
+ "access": "public"
51
+ },
16
52
  "scripts": {
53
+ "build": "tsup",
54
+ "dev": "tsup --watch",
17
55
  "test": "vitest run",
18
56
  "test:watch": "vitest"
19
57
  }
@@ -1,69 +0,0 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { CLIENT_EVENTS, SERVER_EVENTS } from '../src/constants/events'
3
- import { LIMITS } from '../src/constants/limits'
4
-
5
- describe('CLIENT_EVENTS', () => {
6
- it('should have all expected client events', () => {
7
- expect(CLIENT_EVENTS.CHANNEL_JOIN).toBe('channel:join')
8
- expect(CLIENT_EVENTS.CHANNEL_LEAVE).toBe('channel:leave')
9
- expect(CLIENT_EVENTS.MESSAGE_SEND).toBe('message:send')
10
- expect(CLIENT_EVENTS.MESSAGE_TYPING).toBe('message:typing')
11
- expect(CLIENT_EVENTS.PRESENCE_UPDATE).toBe('presence:update')
12
- })
13
-
14
- it('should be readonly', () => {
15
- const events = CLIENT_EVENTS
16
- expect(Object.keys(events)).toHaveLength(5)
17
- })
18
- })
19
-
20
- describe('SERVER_EVENTS', () => {
21
- it('should have all expected server events', () => {
22
- expect(SERVER_EVENTS.MESSAGE_NEW).toBe('message:new')
23
- expect(SERVER_EVENTS.MESSAGE_UPDATE).toBe('message:update')
24
- expect(SERVER_EVENTS.MESSAGE_DELETE).toBe('message:delete')
25
- expect(SERVER_EVENTS.MEMBER_TYPING).toBe('member:typing')
26
- expect(SERVER_EVENTS.MEMBER_JOIN).toBe('member:join')
27
- expect(SERVER_EVENTS.MEMBER_LEAVE).toBe('member:leave')
28
- expect(SERVER_EVENTS.PRESENCE_CHANGE).toBe('presence:change')
29
- expect(SERVER_EVENTS.REACTION_ADD).toBe('reaction:add')
30
- expect(SERVER_EVENTS.REACTION_REMOVE).toBe('reaction:remove')
31
- expect(SERVER_EVENTS.NOTIFICATION_NEW).toBe('notification:new')
32
- expect(SERVER_EVENTS.DM_MESSAGE_NEW).toBe('dm:message:new')
33
- })
34
-
35
- it('should have 11 server events', () => {
36
- expect(Object.keys(SERVER_EVENTS)).toHaveLength(11)
37
- })
38
- })
39
-
40
- describe('LIMITS', () => {
41
- it('should define message limits', () => {
42
- expect(LIMITS.MESSAGE_CONTENT_MAX).toBe(4000)
43
- expect(LIMITS.MESSAGES_PER_PAGE).toBe(50)
44
- })
45
-
46
- it('should define username limits', () => {
47
- expect(LIMITS.USERNAME_MIN).toBe(3)
48
- expect(LIMITS.USERNAME_MAX).toBe(32)
49
- })
50
-
51
- it('should define file upload limit', () => {
52
- expect(LIMITS.FILE_UPLOAD_MAX_SIZE).toBe(10 * 1024 * 1024)
53
- })
54
-
55
- it('should define server/channel limits', () => {
56
- expect(LIMITS.SERVER_NAME_MAX).toBe(100)
57
- expect(LIMITS.CHANNEL_NAME_MAX).toBe(100)
58
- expect(LIMITS.SERVERS_PER_USER_MAX).toBe(100)
59
- expect(LIMITS.CHANNELS_PER_SERVER_MAX).toBe(200)
60
- })
61
-
62
- it('should define invite code length', () => {
63
- expect(LIMITS.INVITE_CODE_LENGTH).toBe(8)
64
- })
65
-
66
- it('should define password min length', () => {
67
- expect(LIMITS.PASSWORD_MIN).toBe(8)
68
- })
69
- })