ugly-app 0.1.78 → 0.1.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -143
- package/coverage/AppProvider.tsx.html +817 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/clover.xml +62 -0
- package/coverage/coverage-final.json +2 -0
- package/coverage/favicon.png +0 -0
- package/coverage/index.html +116 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +210 -0
- package/dist/cli/dev.js +1 -1
- package/dist/cli/dev.js.map +1 -1
- package/dist/cli/version.d.ts +1 -1
- package/dist/cli/version.js +1 -1
- package/dist/client/components/Button.d.ts +3 -3
- package/dist/client/components/Button.d.ts.map +1 -1
- package/dist/client/components/Button.js +27 -13
- package/dist/client/components/Button.js.map +1 -1
- package/dist/client/components/Card.d.ts +3 -3
- package/dist/client/components/Card.d.ts.map +1 -1
- package/dist/client/components/Card.js +10 -3
- package/dist/client/components/Card.js.map +1 -1
- package/dist/client/components/EnumInput.d.ts +3 -3
- package/dist/client/components/EnumInput.d.ts.map +1 -1
- package/dist/client/components/EnumInput.js +17 -2
- package/dist/client/components/EnumInput.js.map +1 -1
- package/dist/client/components/PageLayout.d.ts +3 -3
- package/dist/client/components/PageLayout.d.ts.map +1 -1
- package/dist/client/components/PageLayout.js +14 -2
- package/dist/client/components/PageLayout.js.map +1 -1
- package/dist/client/components/Panel.d.ts +1 -2
- package/dist/client/components/Panel.d.ts.map +1 -1
- package/dist/client/components/Panel.js +7 -2
- package/dist/client/components/Panel.js.map +1 -1
- package/dist/client/components/PopupPanel.d.ts +3 -3
- package/dist/client/components/PopupPanel.d.ts.map +1 -1
- package/dist/client/components/PopupPanel.js +11 -2
- package/dist/client/components/PopupPanel.js.map +1 -1
- package/dist/client/components/Pressable.d.ts +1 -2
- package/dist/client/components/Pressable.d.ts.map +1 -1
- package/dist/client/components/Pressable.js +8 -2
- package/dist/client/components/Pressable.js.map +1 -1
- package/dist/client/components/ScrollView.js +1 -1
- package/dist/client/components/ScrollView.js.map +1 -1
- package/dist/client/components/SettingGroup.d.ts +3 -3
- package/dist/client/components/SettingGroup.d.ts.map +1 -1
- package/dist/client/components/SettingGroup.js +15 -2
- package/dist/client/components/SettingGroup.js.map +1 -1
- package/dist/client/components/ValidatedTextInput.d.ts.map +1 -1
- package/dist/client/components/ValidatedTextInput.js +0 -1
- package/dist/client/components/ValidatedTextInput.js.map +1 -1
- package/dist/client/createSocket.d.ts.map +1 -1
- package/dist/client/createSocket.js +3 -3
- package/dist/client/createSocket.js.map +1 -1
- package/dist/server/App.d.ts +29 -3
- package/dist/server/App.d.ts.map +1 -1
- package/dist/server/App.js +72 -7
- package/dist/server/App.js.map +1 -1
- package/dist/server/Router.d.ts +1 -1
- package/dist/server/Router.d.ts.map +1 -1
- package/dist/server/Router.js +6 -1
- package/dist/server/Router.js.map +1 -1
- package/dist/server/Socket.d.ts.map +1 -1
- package/dist/server/Socket.js +21 -8
- package/dist/server/Socket.js.map +1 -1
- package/dist/server/StoreHandlers.d.ts +4 -0
- package/dist/server/StoreHandlers.d.ts.map +1 -0
- package/dist/server/StoreHandlers.js +160 -0
- package/dist/server/StoreHandlers.js.map +1 -0
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/shared/Socket.d.ts +33 -3
- package/dist/shared/Socket.d.ts.map +1 -1
- package/eslint.config.js +106 -0
- package/package.json +2 -1
- package/templates/.claude/skills/fix-code/SKILL.md +16 -4
- package/templates/client/pages/AITestPage.tsx +17 -56
- package/templates/client/pages/AuthDemoPage.tsx +13 -33
- package/templates/client/pages/HomePage.tsx +14 -22
- package/templates/client/pages/SearchPage.tsx +7 -13
- package/templates/client/pages/TodoDemoPage.tsx +222 -0
- package/templates/client/pages/UserPage.tsx +7 -13
- package/templates/client/styles.css +0 -3
- package/templates/eslint.config.js +78 -0
- package/templates/package.json +5 -2
- package/templates/postcss.config.js +0 -1
- package/templates/vitest.config.ts +21 -0
- package/templates/tailwind.config.js +0 -6
package/README.md
CHANGED
|
@@ -8,8 +8,7 @@ A full-stack TypeScript framework for building production-ready web applications
|
|
|
8
8
|
- **Client**: React + Vite with typed routing, lazy pages, and popup management
|
|
9
9
|
- **Database**: MongoDB with typed collections, dot-notation updates, indexes, migrations, and live document tracking
|
|
10
10
|
- **Auth**: JWT + HttpOnly cookies, ugly.bot OAuth out of the box, extensible via `AuthProvider`
|
|
11
|
-
- **AI**: Text generation (Together, Claude, OpenAI, Google, Groq, Fireworks
|
|
12
|
-
- **Analytics**: EventCounter (high-throughput counters), EventLog (event capture), A/B experiments
|
|
11
|
+
- **AI**: Text generation (Together, Claude, OpenAI, Google, Groq, Fireworks) + image generation (Together, FAL, Google, Wavespeed) + embeddings + STT/TTS
|
|
13
12
|
- **Storage**: Cloudflare R2 / AWS S3 with presigned uploads
|
|
14
13
|
- **CLI**: `ugly-app` commands for dev, build, deploy, migrations, logs, and auth utilities
|
|
15
14
|
|
|
@@ -32,22 +31,16 @@ Entry point for the server. Creates an Express + WebSocket server with typed RPC
|
|
|
32
31
|
```typescript
|
|
33
32
|
import {
|
|
34
33
|
createApp,
|
|
35
|
-
createImageGen,
|
|
36
|
-
createTextGen,
|
|
37
34
|
createUserHelper,
|
|
38
|
-
eventLogCapture,
|
|
39
|
-
eventLogServerCapture,
|
|
40
35
|
getFeedbackHandlers,
|
|
41
|
-
getExperimentAssignments,
|
|
42
36
|
type AppConfigurator,
|
|
43
37
|
type RequestHandlers,
|
|
44
38
|
} from 'ugly-app';
|
|
45
39
|
import { dbDefaults } from 'ugly-app/shared';
|
|
46
40
|
import { requests } from '../shared/api';
|
|
47
|
-
import type { User } from '../shared/collections';
|
|
48
41
|
import { collections } from '../shared/collections';
|
|
49
|
-
import { experiments } from '../shared/experiments';
|
|
50
42
|
import { pages } from '../shared/pages';
|
|
43
|
+
import type { User } from '../shared/collections';
|
|
51
44
|
|
|
52
45
|
const userHelper = createUserHelper<User>(collections.user);
|
|
53
46
|
const maintainBotUserId = process.env.MAINTAIN_BOT_USER_ID ?? '';
|
|
@@ -56,38 +49,10 @@ const app = createApp(
|
|
|
56
49
|
{ requests },
|
|
57
50
|
{
|
|
58
51
|
...getFeedbackHandlers(maintainBotUserId),
|
|
59
|
-
|
|
60
52
|
getMe: async (userId: string) => {
|
|
61
53
|
const user = await userHelper.get(app.db, userId);
|
|
62
54
|
return { userId, email: user?.email, phone: user?.phone };
|
|
63
55
|
},
|
|
64
|
-
|
|
65
|
-
// Experiment bucketing + session start event
|
|
66
|
-
initSession: async (userId, { sessionId }) => {
|
|
67
|
-
const branches = getExperimentAssignments(userId, sessionId, experiments);
|
|
68
|
-
await eventLogServerCapture('SESSION_START', {}, sessionId, userId, branches);
|
|
69
|
-
return { branches };
|
|
70
|
-
},
|
|
71
|
-
|
|
72
|
-
// Generic event capture with experiment branch tagging
|
|
73
|
-
captureEvent: async (userId, { eventName, sessionId, properties }) => {
|
|
74
|
-
const branches = getExperimentAssignments(userId, sessionId, experiments);
|
|
75
|
-
const { eventId } = await eventLogCapture(
|
|
76
|
-
{ eventName, sessionId, userId, properties: properties ?? {}, experimentBranches: branches },
|
|
77
|
-
userId,
|
|
78
|
-
);
|
|
79
|
-
return { eventId };
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
textGen: async (userId, { model, messages }) => {
|
|
83
|
-
const text = await createTextGen(userId).generate(messages, { model });
|
|
84
|
-
return { text };
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
imageGen: async (userId, { model, prompt }) => {
|
|
88
|
-
const url = await createImageGen(userId).generate(prompt, { model });
|
|
89
|
-
return { url };
|
|
90
|
-
},
|
|
91
56
|
} satisfies RequestHandlers<typeof requests>,
|
|
92
57
|
collections,
|
|
93
58
|
(configurator: AppConfigurator) => {
|
|
@@ -672,16 +637,14 @@ Run `npm run db:init` to create/update indexes.
|
|
|
672
637
|
### Text generation
|
|
673
638
|
|
|
674
639
|
```typescript
|
|
675
|
-
import {
|
|
676
|
-
const textGen =
|
|
640
|
+
import { createTextGenClient } from 'ugly-app';
|
|
641
|
+
const textGen = createTextGenClient();
|
|
677
642
|
|
|
678
643
|
const text = await textGen.generate(messages);
|
|
679
644
|
const json = await textGen.generateJson(schema, messages); // Zod schema, retries on parse failure
|
|
680
645
|
const result = await textGen.generateWithTools(messages, tools); // automatic tool-call loop
|
|
681
646
|
```
|
|
682
647
|
|
|
683
|
-
`createTextGen(userId, defaults?)` creates a client scoped to a user (for rate limiting and billing). Optional `defaults` set provider, model, temperature, etc.
|
|
684
|
-
|
|
685
648
|
| Provider | `provider` value | Default model | JSON | Tools | Vision |
|
|
686
649
|
|----------|-----------------|---------------|------|-------|--------|
|
|
687
650
|
| Together AI | `'together'` | Llama-4-Maverick-17B-128E | yes | yes | yes |
|
|
@@ -697,14 +660,12 @@ Use `provider: 'auto'` (default) to let the system pick based on requirements.
|
|
|
697
660
|
### Image generation
|
|
698
661
|
|
|
699
662
|
```typescript
|
|
700
|
-
import {
|
|
701
|
-
const imageGen =
|
|
663
|
+
import { createImageGenClient } from 'ugly-app';
|
|
664
|
+
const imageGen = createImageGenClient();
|
|
702
665
|
|
|
703
666
|
const url = await imageGen.generate(prompt, { width: 1024, height: 1024 });
|
|
704
667
|
```
|
|
705
668
|
|
|
706
|
-
`createImageGen(userId, defaults?)` works the same way — user-scoped with optional defaults.
|
|
707
|
-
|
|
708
669
|
| Provider | `provider` value |
|
|
709
670
|
|----------|-----------------|
|
|
710
671
|
| Together AI (FLUX schnell) | `'together'` |
|
|
@@ -885,104 +846,6 @@ submitFeedback: authReq({
|
|
|
885
846
|
|
|
886
847
|
The framework enforces rate limits automatically before calling the handler.
|
|
887
848
|
|
|
888
|
-
### EventCounter
|
|
889
|
-
|
|
890
|
-
High-throughput counters aggregated into hourly buckets in MongoDB. Increments are instant (in-memory) and flushed every 60 seconds.
|
|
891
|
-
|
|
892
|
-
```typescript
|
|
893
|
-
import {
|
|
894
|
-
eventCounterIncrement,
|
|
895
|
-
startEventCounterFlush,
|
|
896
|
-
flushCountersToMongo,
|
|
897
|
-
eventCounterGetCounts,
|
|
898
|
-
eventCounterGetTopTypes,
|
|
899
|
-
} from 'ugly-app';
|
|
900
|
-
|
|
901
|
-
// Increment a counter (instant, no DB write)
|
|
902
|
-
eventCounterIncrement('api_getMe');
|
|
903
|
-
|
|
904
|
-
// Start periodic flush — call once at server startup
|
|
905
|
-
startEventCounterFlush();
|
|
906
|
-
|
|
907
|
-
// Manually flush (e.g. on graceful shutdown)
|
|
908
|
-
await flushCountersToMongo();
|
|
909
|
-
|
|
910
|
-
// Query counts (admin only)
|
|
911
|
-
const { counts } = await eventCounterGetCounts(
|
|
912
|
-
{ granularity: 'hours', intervals: 24, category: 'api' },
|
|
913
|
-
isAdmin,
|
|
914
|
-
);
|
|
915
|
-
|
|
916
|
-
// Query top counter types (admin only)
|
|
917
|
-
const { types } = await eventCounterGetTopTypes(
|
|
918
|
-
{ limit: 10, category: 'api' },
|
|
919
|
-
isAdmin,
|
|
920
|
-
);
|
|
921
|
-
```
|
|
922
|
-
|
|
923
|
-
Counter type names use a `category_name` convention (e.g. `api_getMe`, `ai_textGen`). Data is auto-expired after 30 days via MongoDB TTL.
|
|
924
|
-
|
|
925
|
-
### EventLog
|
|
926
|
-
|
|
927
|
-
Structured event capture for analytics. Events are stored in MongoDB with session, user, and experiment branch associations.
|
|
928
|
-
|
|
929
|
-
```typescript
|
|
930
|
-
import { eventLogCapture, eventLogServerCapture } from 'ugly-app';
|
|
931
|
-
|
|
932
|
-
// Capture an event (returns eventId)
|
|
933
|
-
const { eventId } = await eventLogCapture(
|
|
934
|
-
{
|
|
935
|
-
eventName: 'BUTTON_CLICK',
|
|
936
|
-
sessionId,
|
|
937
|
-
userId,
|
|
938
|
-
properties: { page: 'home', target: 'cta' },
|
|
939
|
-
experimentBranches: branches,
|
|
940
|
-
},
|
|
941
|
-
userId,
|
|
942
|
-
);
|
|
943
|
-
|
|
944
|
-
// Server-side shorthand (fire-and-forget, no eventId returned)
|
|
945
|
-
await eventLogServerCapture('SESSION_START', { source: 'web' }, sessionId, userId, branches);
|
|
946
|
-
```
|
|
947
|
-
|
|
948
|
-
Admin query functions: `eventLogGetList`, `eventLogGetCounts`, `eventLogGetTopEvents`, `eventLogGetTopUsers`, `eventLogGetTopSessions`, `eventLogGetUniqueUsersCounts`, `eventLogGetUniqueSessionsCounts`.
|
|
949
|
-
|
|
950
|
-
### Experiments (A/B testing)
|
|
951
|
-
|
|
952
|
-
Define experiments in `shared/experiments.ts` and use deterministic bucketing to assign users to branches.
|
|
953
|
-
|
|
954
|
-
```typescript
|
|
955
|
-
// shared/experiments.ts
|
|
956
|
-
import type { Experiment } from 'ugly-app/shared';
|
|
957
|
-
|
|
958
|
-
export const experiments: Experiment[] = [
|
|
959
|
-
{
|
|
960
|
-
id: 'onboarding-v2',
|
|
961
|
-
name: 'Onboarding V2',
|
|
962
|
-
description: 'Test new onboarding flow',
|
|
963
|
-
branches: [
|
|
964
|
-
{ id: 'control', name: 'Control', weight: 1 },
|
|
965
|
-
{ id: 'variant', name: 'Variant', weight: 1 },
|
|
966
|
-
],
|
|
967
|
-
events: ['ONBOARDING_COMPLETE', 'SIGNUP'],
|
|
968
|
-
active: true,
|
|
969
|
-
},
|
|
970
|
-
];
|
|
971
|
-
```
|
|
972
|
-
|
|
973
|
-
```typescript
|
|
974
|
-
// Server-side assignment
|
|
975
|
-
import { getExperimentAssignments, getExperimentBranch } from 'ugly-app';
|
|
976
|
-
|
|
977
|
-
// Get all branch assignments for a user/session (deterministic hash)
|
|
978
|
-
const branches = getExperimentAssignments(userId, sessionId, experiments);
|
|
979
|
-
|
|
980
|
-
// Get a single experiment branch
|
|
981
|
-
const branch = getExperimentBranch(userId, sessionId, experiment);
|
|
982
|
-
```
|
|
983
|
-
|
|
984
|
-
Shared helpers: `getActiveExperiments`, `getExperimentById`, `getExperimentBranch`, `getExperimentAssignments`. Server admin: `experimentGetMetrics` for per-branch analytics.
|
|
985
|
-
|
|
986
849
|
---
|
|
987
850
|
|
|
988
851
|
## Built-in endpoints
|