@idealyst/cli 1.2.32 → 1.2.33

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.
Files changed (36) hide show
  1. package/dist/constants.js +5 -1
  2. package/dist/constants.js.map +1 -1
  3. package/dist/generators/core/shared.js +382 -3
  4. package/dist/generators/core/shared.js.map +1 -1
  5. package/dist/generators/extensions/graphql.js +112 -26
  6. package/dist/generators/extensions/graphql.js.map +1 -1
  7. package/dist/generators/extensions/prisma.js +105 -16
  8. package/dist/generators/extensions/prisma.js.map +1 -1
  9. package/dist/generators/extensions/trpc.js +137 -41
  10. package/dist/generators/extensions/trpc.js.map +1 -1
  11. package/dist/templates/core/api/src-graphql/builder.ts +25 -0
  12. package/dist/templates/core/api/src-graphql/schema.ts +217 -0
  13. package/dist/templates/core/api/src-trpc/context.ts +15 -0
  14. package/dist/templates/core/api/src-trpc/index.ts +6 -0
  15. package/dist/templates/core/api/src-trpc/lib/database.ts +4 -0
  16. package/dist/templates/core/api/src-trpc/router/index.ts +187 -0
  17. package/dist/templates/core/api/src-trpc/server.ts +63 -0
  18. package/dist/templates/core/api/src-trpc/trpc.ts +20 -0
  19. package/dist/templates/core/database/schema.prisma +22 -0
  20. package/dist/templates/core/shared/src-components/App.tsx +51 -0
  21. package/dist/templates/core/shared/src-graphql/client.ts +70 -0
  22. package/dist/templates/core/shared/{src/navigation → src-navigation}/AppRouter.tsx +34 -0
  23. package/dist/templates/core/shared/src-screens-graphql/GraphQLDemoScreen.tsx +354 -0
  24. package/dist/templates/core/shared/src-screens-trpc/TRPCDemoScreen.tsx +432 -0
  25. package/dist/templates/core/shared/src-trpc/client.ts +44 -0
  26. package/dist/types/constants.d.ts +9 -5
  27. package/package.json +1 -1
  28. package/dist/templates/core/shared/src/components/App.tsx +0 -13
  29. package/dist/templates/core/shared/src/screens/index.ts +0 -4
  30. /package/dist/templates/core/shared/{src/components → src-components}/index.ts +0 -0
  31. /package/dist/templates/core/shared/{src/layouts → src-layouts}/AppLayout.tsx +0 -0
  32. /package/dist/templates/core/shared/{src/navigation → src-navigation}/index.ts +0 -0
  33. /package/dist/templates/core/shared/{src/screens → src-screens}/ExploreScreen.tsx +0 -0
  34. /package/dist/templates/core/shared/{src/screens → src-screens}/HomeScreen.tsx +0 -0
  35. /package/dist/templates/core/shared/{src/screens → src-screens}/ProfileScreen.tsx +0 -0
  36. /package/dist/templates/core/shared/{src/screens → src-screens}/SettingsScreen.tsx +0 -0
@@ -0,0 +1,432 @@
1
+ import React, { useState } from 'react';
2
+ import {
3
+ Screen,
4
+ View,
5
+ Text,
6
+ Card,
7
+ Button,
8
+ Icon,
9
+ Badge,
10
+ ActivityIndicator,
11
+ Alert,
12
+ TextInput,
13
+ Divider,
14
+ } from '@idealyst/components';
15
+ import { trpc } from '../trpc/client';
16
+
17
+ // Set this to true if database/Prisma is available
18
+ // This will be replaced by the CLI generator based on user selection
19
+ const HAS_DATABASE = true;
20
+
21
+ export const TRPCDemoScreen: React.FC = () => {
22
+ const [echoInput, setEchoInput] = useState('');
23
+ const [newItemTitle, setNewItemTitle] = useState('');
24
+
25
+ // ==========================================================================
26
+ // Base tRPC queries (no database required)
27
+ // ==========================================================================
28
+ const healthQuery = trpc.health.useQuery();
29
+ const counterQuery = trpc.counter.get.useQuery();
30
+
31
+ const echoQuery = trpc.echo.useQuery(
32
+ { message: echoInput },
33
+ { enabled: echoInput.length > 0 }
34
+ );
35
+
36
+ const incrementMutation = trpc.counter.increment.useMutation({
37
+ onSuccess: () => counterQuery.refetch(),
38
+ });
39
+
40
+ const decrementMutation = trpc.counter.decrement.useMutation({
41
+ onSuccess: () => counterQuery.refetch(),
42
+ });
43
+
44
+ const resetMutation = trpc.counter.reset.useMutation({
45
+ onSuccess: () => counterQuery.refetch(),
46
+ });
47
+
48
+ // ==========================================================================
49
+ // Database tRPC queries (requires Prisma)
50
+ // ==========================================================================
51
+ const itemsQuery = HAS_DATABASE ? trpc.items.list.useQuery() : null;
52
+ const statsQuery = HAS_DATABASE ? trpc.items.stats.useQuery() : null;
53
+
54
+ const createMutation = trpc.items.create.useMutation({
55
+ onSuccess: () => {
56
+ itemsQuery?.refetch();
57
+ statsQuery?.refetch();
58
+ setNewItemTitle('');
59
+ },
60
+ });
61
+
62
+ const toggleMutation = trpc.items.toggle.useMutation({
63
+ onSuccess: () => {
64
+ itemsQuery?.refetch();
65
+ statsQuery?.refetch();
66
+ },
67
+ });
68
+
69
+ const deleteMutation = trpc.items.delete.useMutation({
70
+ onSuccess: () => {
71
+ itemsQuery?.refetch();
72
+ statsQuery?.refetch();
73
+ },
74
+ });
75
+
76
+ const isLoading = healthQuery.isLoading;
77
+ const hasError = healthQuery.isError;
78
+
79
+ const handleCreateItem = () => {
80
+ if (newItemTitle.trim()) {
81
+ createMutation.mutate({ title: newItemTitle.trim() });
82
+ }
83
+ };
84
+
85
+ return (
86
+ <Screen background="primary" padding="lg" scrollable>
87
+ <View gap="lg">
88
+ {/* Header */}
89
+ <View gap="sm">
90
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
91
+ <Icon name="api" size={28} intent="primary" />
92
+ <Text typography="h3">tRPC Demo</Text>
93
+ </View>
94
+ <Text color="secondary">
95
+ Type-safe API calls with full end-to-end type safety
96
+ </Text>
97
+ </View>
98
+
99
+ {/* Loading State */}
100
+ {isLoading && (
101
+ <Card type="outlined" padding="lg">
102
+ <View style={{ alignItems: 'center', gap: 12 }}>
103
+ <ActivityIndicator size="lg" intent="primary" />
104
+ <Text color="secondary">Connecting to API...</Text>
105
+ </View>
106
+ </Card>
107
+ )}
108
+
109
+ {/* Error State */}
110
+ {hasError && (
111
+ <Alert intent="danger" title="Connection Error">
112
+ Could not connect to the API. Make sure the server is running.
113
+ </Alert>
114
+ )}
115
+
116
+ {/* ================================================================== */}
117
+ {/* SECTION 1: Base Routes (No Database Required) */}
118
+ {/* ================================================================== */}
119
+
120
+ <View gap="sm">
121
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
122
+ <Icon name="lightning-bolt" size={20} intent="warning" />
123
+ <Text typography="h5" weight="semibold">Base API Routes</Text>
124
+ <Badge intent="warning" size="sm">No Database</Badge>
125
+ </View>
126
+ <Text typography="caption" color="secondary">
127
+ These routes work without any database setup
128
+ </Text>
129
+ </View>
130
+
131
+ {/* Health Check */}
132
+ {healthQuery.data && (
133
+ <Card type="elevated" padding="md" gap="sm">
134
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
135
+ <Icon name="heart-pulse" size={20} intent="success" />
136
+ <Text weight="semibold">Health Check</Text>
137
+ <Badge intent="success" size="sm">
138
+ {healthQuery.data.status}
139
+ </Badge>
140
+ </View>
141
+ <Text typography="caption" color="secondary">
142
+ Version: {healthQuery.data.version} | {healthQuery.data.timestamp}
143
+ </Text>
144
+ </Card>
145
+ )}
146
+
147
+ {/* Echo Demo */}
148
+ <Card type="outlined" padding="md" gap="md" style={{ minHeight: 180 }}>
149
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
150
+ <Icon name="message-reply-text" size={20} intent="primary" />
151
+ <Text weight="semibold">Echo Endpoint</Text>
152
+ </View>
153
+
154
+ <TextInput
155
+ placeholder="Type a message to echo..."
156
+ value={echoInput}
157
+ onChangeText={setEchoInput}
158
+ />
159
+
160
+ <View
161
+ background="secondary"
162
+ padding="md"
163
+ radius="md"
164
+ gap="xs"
165
+ style={{ height: 80 }}
166
+ >
167
+ {echoQuery.data ? (
168
+ <>
169
+ <Text typography="caption" color="secondary" numberOfLines={1}>Original: {echoQuery.data.original}</Text>
170
+ <Text typography="caption" color="secondary" numberOfLines={1}>Reversed: {echoQuery.data.reversed}</Text>
171
+ <Text typography="caption" color="secondary">Length: {echoQuery.data.length}</Text>
172
+ </>
173
+ ) : (
174
+ <Text typography="caption" color="secondary" style={{ opacity: 0.5 }}>
175
+ Type a message above to see the echo response...
176
+ </Text>
177
+ )}
178
+ </View>
179
+ </Card>
180
+
181
+ {/* Counter Demo */}
182
+ <Card type="outlined" padding="md" gap="md">
183
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
184
+ <Icon name="counter" size={20} intent="primary" />
185
+ <Text weight="semibold">In-Memory Counter</Text>
186
+ </View>
187
+
188
+ <View style={{ alignItems: 'center', gap: 12 }}>
189
+ <Text typography="h2" color="primary">
190
+ {counterQuery.data?.value ?? 0}
191
+ </Text>
192
+
193
+ <View style={{ flexDirection: 'row', gap: 8 }}>
194
+ <Button
195
+ size="sm"
196
+ intent="danger"
197
+ leftIcon="minus"
198
+ onPress={() => decrementMutation.mutate()}
199
+ disabled={decrementMutation.isPending}
200
+ >
201
+ -1
202
+ </Button>
203
+ <Button
204
+ size="sm"
205
+ intent="neutral"
206
+ leftIcon="refresh"
207
+ onPress={() => resetMutation.mutate()}
208
+ disabled={resetMutation.isPending}
209
+ >
210
+ Reset
211
+ </Button>
212
+ <Button
213
+ size="sm"
214
+ intent="success"
215
+ leftIcon="plus"
216
+ onPress={() => incrementMutation.mutate()}
217
+ disabled={incrementMutation.isPending}
218
+ >
219
+ +1
220
+ </Button>
221
+ </View>
222
+ </View>
223
+
224
+ <Text typography="caption" color="secondary" style={{ textAlign: 'center' }}>
225
+ Server-side state - persists across page refreshes but resets on server restart
226
+ </Text>
227
+ </Card>
228
+
229
+ {/* ================================================================== */}
230
+ {/* SECTION 2: Database Routes (Requires Prisma) */}
231
+ {/* ================================================================== */}
232
+
233
+ {HAS_DATABASE && (
234
+ <>
235
+ <Divider />
236
+
237
+ <View gap="sm">
238
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
239
+ <Icon name="database" size={20} intent="success" />
240
+ <Text typography="h5" weight="semibold">Database Routes</Text>
241
+ <Badge intent="success" size="sm">Prisma</Badge>
242
+ </View>
243
+ <Text typography="caption" color="secondary">
244
+ Full CRUD operations with persistent database storage
245
+ </Text>
246
+ </View>
247
+
248
+ {/* Stats */}
249
+ {statsQuery?.data && (
250
+ <Card type="outlined" padding="md" gap="md">
251
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
252
+ <Icon name="chart-bar" size={20} intent="primary" />
253
+ <Text weight="semibold">Item Statistics</Text>
254
+ </View>
255
+
256
+ <View style={{ flexDirection: 'row', gap: 12 }}>
257
+ <View
258
+ background="secondary"
259
+ padding="md"
260
+ radius="md"
261
+ style={{ flex: 1, alignItems: 'center' }}
262
+ >
263
+ <Text typography="h4">{statsQuery.data.total}</Text>
264
+ <Text typography="caption" color="secondary">
265
+ Total
266
+ </Text>
267
+ </View>
268
+ <View
269
+ background="secondary"
270
+ padding="md"
271
+ radius="md"
272
+ style={{ flex: 1, alignItems: 'center' }}
273
+ >
274
+ <Text typography="h4" color="success">
275
+ {statsQuery.data.completed}
276
+ </Text>
277
+ <Text typography="caption" color="secondary">
278
+ Completed
279
+ </Text>
280
+ </View>
281
+ <View
282
+ background="secondary"
283
+ padding="md"
284
+ radius="md"
285
+ style={{ flex: 1, alignItems: 'center' }}
286
+ >
287
+ <Text typography="h4" color="warning">
288
+ {statsQuery.data.pending}
289
+ </Text>
290
+ <Text typography="caption" color="secondary">
291
+ Pending
292
+ </Text>
293
+ </View>
294
+ </View>
295
+ </Card>
296
+ )}
297
+
298
+ {/* Create Item */}
299
+ <Card type="elevated" padding="md" gap="md">
300
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
301
+ <Icon name="plus-circle" size={20} intent="success" />
302
+ <Text weight="semibold">Create Item</Text>
303
+ </View>
304
+
305
+ <TextInput
306
+ placeholder="Enter item title..."
307
+ value={newItemTitle}
308
+ onChangeText={setNewItemTitle}
309
+ />
310
+
311
+ <Button
312
+ intent="primary"
313
+ leftIcon="plus"
314
+ onPress={handleCreateItem}
315
+ disabled={!newItemTitle.trim() || createMutation.isPending}
316
+ >
317
+ {createMutation.isPending ? 'Creating...' : 'Add Item'}
318
+ </Button>
319
+ </Card>
320
+
321
+ {/* Items List */}
322
+ {itemsQuery?.data && itemsQuery.data.length > 0 && (
323
+ <Card type="outlined" padding="md" gap="md">
324
+ <View style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}>
325
+ <Icon name="format-list-checks" size={20} intent="primary" />
326
+ <Text weight="semibold">Items</Text>
327
+ <Badge intent="primary" size="sm">
328
+ {itemsQuery.data.length}
329
+ </Badge>
330
+ </View>
331
+
332
+ <View gap="sm">
333
+ {itemsQuery.data.map((item: { id: string; title: string; description?: string | null; completed: boolean }) => (
334
+ <View
335
+ key={item.id}
336
+ background="secondary"
337
+ padding="sm"
338
+ radius="sm"
339
+ style={{ flexDirection: 'row', alignItems: 'center', gap: 8 }}
340
+ >
341
+ <Button
342
+ size="sm"
343
+ intent={item.completed ? 'success' : 'neutral'}
344
+ leftIcon={item.completed ? 'check-circle' : 'circle-outline'}
345
+ onPress={() => toggleMutation.mutate({ id: item.id })}
346
+ />
347
+ <View style={{ flex: 1 }}>
348
+ <Text
349
+ weight="medium"
350
+ style={{
351
+ textDecorationLine: item.completed ? 'line-through' : 'none',
352
+ opacity: item.completed ? 0.6 : 1,
353
+ }}
354
+ >
355
+ {item.title}
356
+ </Text>
357
+ {item.description && (
358
+ <Text typography="caption" color="secondary">
359
+ {item.description}
360
+ </Text>
361
+ )}
362
+ </View>
363
+ <Button
364
+ size="sm"
365
+ intent="danger"
366
+ leftIcon="delete"
367
+ onPress={() => deleteMutation.mutate({ id: item.id })}
368
+ />
369
+ </View>
370
+ ))}
371
+ </View>
372
+ </Card>
373
+ )}
374
+
375
+ {/* Empty State */}
376
+ {itemsQuery?.data && itemsQuery.data.length === 0 && (
377
+ <Card type="outlined" padding="lg">
378
+ <View style={{ alignItems: 'center', gap: 8 }}>
379
+ <Icon name="inbox-outline" size={48} textColor="secondary" />
380
+ <Text color="secondary">No items yet. Create one above!</Text>
381
+ </View>
382
+ </Card>
383
+ )}
384
+ </>
385
+ )}
386
+
387
+ {/* Refetch Button */}
388
+ <Button
389
+ intent="primary"
390
+ leftIcon="refresh"
391
+ onPress={() => {
392
+ healthQuery.refetch();
393
+ counterQuery.refetch();
394
+ if (echoInput) echoQuery.refetch();
395
+ if (HAS_DATABASE) {
396
+ itemsQuery?.refetch();
397
+ statsQuery?.refetch();
398
+ }
399
+ }}
400
+ >
401
+ Refetch All Data
402
+ </Button>
403
+
404
+ {/* Code Example */}
405
+ <Card type="outlined" padding="md" gap="sm">
406
+ <Text weight="semibold">tRPC Usage Examples</Text>
407
+ <View background="secondary" padding="md" radius="sm">
408
+ <Text
409
+ typography="caption"
410
+ style={{ fontFamily: 'monospace', lineHeight: 20 }}
411
+ >
412
+ {`// Query with full type inference
413
+ const { data } = trpc.health.useQuery();
414
+ const counter = trpc.counter.get.useQuery();
415
+
416
+ // Mutations
417
+ trpc.counter.increment.useMutation();
418
+ trpc.echo.useQuery({ message: 'hello' });${HAS_DATABASE ? `
419
+
420
+ // Database operations (with Prisma)
421
+ const items = trpc.items.list.useQuery();
422
+ trpc.items.create.useMutation();
423
+ trpc.items.toggle.useMutation();` : ''}`}
424
+ </Text>
425
+ </View>
426
+ </Card>
427
+ </View>
428
+ </Screen>
429
+ );
430
+ };
431
+
432
+ export default TRPCDemoScreen;
@@ -0,0 +1,44 @@
1
+ import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
2
+ import { createTRPCReact } from '@trpc/react-query';
3
+ import type { AppRouter } from '@{{workspaceScope}}/api';
4
+
5
+ // Create the tRPC React hooks with full type safety
6
+ export const trpc: ReturnType<typeof createTRPCReact<AppRouter>> =
7
+ createTRPCReact<AppRouter>();
8
+
9
+ // Configuration for tRPC client
10
+ export interface TRPCClientConfig {
11
+ apiUrl: string;
12
+ headers?: () => Record<string, string>;
13
+ }
14
+
15
+ // Create tRPC client factory
16
+ export function createTRPCClient(
17
+ config: TRPCClientConfig
18
+ ): ReturnType<typeof trpc.createClient> {
19
+ return trpc.createClient({
20
+ links: [
21
+ httpBatchLink({
22
+ url: config.apiUrl,
23
+ headers: config.headers,
24
+ }),
25
+ ],
26
+ });
27
+ }
28
+
29
+ // Create a vanilla client (for use outside of React components)
30
+ export function createVanillaTRPCClient(
31
+ config: TRPCClientConfig
32
+ ): ReturnType<typeof createTRPCProxyClient<AppRouter>> {
33
+ return createTRPCProxyClient<AppRouter>({
34
+ links: [
35
+ httpBatchLink({
36
+ url: config.apiUrl,
37
+ headers: config.headers,
38
+ }),
39
+ ],
40
+ });
41
+ }
42
+
43
+ // Export types
44
+ export type { AppRouter } from '@{{workspaceScope}}/api';
@@ -2,7 +2,7 @@
2
2
  * Constants and default values for the CLI
3
3
  */
4
4
  export declare const VERSION = "0.1.0";
5
- export declare const IDEALYST_VERSION = "1.2.32";
5
+ export declare const IDEALYST_VERSION = "1.2.33";
6
6
  export declare const REACT_NATIVE_VERSION = "0.83.0";
7
7
  export declare const REACT_VERSION = "19.1.0";
8
8
  export declare const DEFAULT_TIMEOUT = 300000;
@@ -18,9 +18,9 @@ export declare const TEMPLATE_EXTENSIONS: readonly [".ts", ".tsx", ".js", ".jsx"
18
18
  export declare const IGNORE_PATTERNS: readonly ["node_modules", ".git", "dist", "build", ".cache", ".DS_Store", "Thumbs.db", "*.log", "*.tmp", "*.bak", "~*"];
19
19
  export declare const DEPENDENCIES: {
20
20
  readonly core: {
21
- readonly '@idealyst/components': "^1.2.32";
22
- readonly '@idealyst/theme': "^1.2.32";
23
- readonly '@idealyst/navigation': "^1.2.32";
21
+ readonly '@idealyst/components': "^1.2.33";
22
+ readonly '@idealyst/theme': "^1.2.33";
23
+ readonly '@idealyst/navigation': "^1.2.33";
24
24
  };
25
25
  readonly web: {
26
26
  readonly '@mdi/js': "^7.4.47";
@@ -60,12 +60,16 @@ export declare const DEPENDENCIES: {
60
60
  };
61
61
  readonly graphql: {
62
62
  readonly '@apollo/client': "^3.11.0";
63
+ readonly '@tanstack/react-query': "^5.62.0";
63
64
  readonly graphql: "^16.9.0";
64
65
  };
65
66
  readonly graphqlServer: {
66
67
  readonly '@pothos/core': "^4.3.0";
67
68
  readonly 'graphql-yoga': "^5.10.0";
68
69
  };
70
+ readonly graphqlServerPrisma: {
71
+ readonly '@pothos/plugin-prisma': "^4.3.0";
72
+ };
69
73
  readonly prisma: {
70
74
  readonly '@prisma/client': "^5.19.0";
71
75
  };
@@ -73,7 +77,7 @@ export declare const DEPENDENCIES: {
73
77
  readonly prisma: "^5.19.0";
74
78
  };
75
79
  readonly tooling: {
76
- readonly '@idealyst/tooling': "^1.2.32";
80
+ readonly '@idealyst/tooling': "^1.2.33";
77
81
  };
78
82
  readonly api: {
79
83
  readonly express: "^4.21.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@idealyst/cli",
3
- "version": "1.2.32",
3
+ "version": "1.2.33",
4
4
  "description": "CLI tool for generating Idealyst Framework projects",
5
5
  "readme": "README.md",
6
6
  "main": "dist/index.js",
@@ -1,13 +0,0 @@
1
- import React from 'react';
2
- import { NavigatorProvider } from '@idealyst/navigation';
3
- import { AppRouter } from '../navigation/AppRouter';
4
-
5
- /**
6
- * Main App component for the {{appDisplayName}}
7
- * Sets up navigation with the AppRouter
8
- */
9
- export const App: React.FC = () => {
10
- return <NavigatorProvider route={AppRouter} />;
11
- };
12
-
13
- export default App;
@@ -1,4 +0,0 @@
1
- export { HomeScreen } from './HomeScreen';
2
- export { ExploreScreen } from './ExploreScreen';
3
- export { ProfileScreen } from './ProfileScreen';
4
- export { SettingsScreen } from './SettingsScreen';