@idealyst/cli 1.2.31 → 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 (38) 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 -4
  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/index.ts +1 -1
  21. package/dist/templates/core/shared/src-components/App.tsx +51 -0
  22. package/dist/templates/core/shared/src-graphql/client.ts +70 -0
  23. package/dist/templates/core/shared/{src/navigation → src-navigation}/AppRouter.tsx +34 -0
  24. package/dist/templates/core/shared/src-screens-graphql/GraphQLDemoScreen.tsx +354 -0
  25. package/dist/templates/core/shared/src-screens-trpc/TRPCDemoScreen.tsx +432 -0
  26. package/dist/templates/core/shared/src-trpc/client.ts +44 -0
  27. package/dist/types/constants.d.ts +9 -5
  28. package/package.json +1 -1
  29. package/dist/templates/core/shared/src/components/App.tsx +0 -13
  30. package/dist/templates/core/shared/src/screens/index.ts +0 -4
  31. /package/dist/templates/core/shared/src/{styles.ts → theme.ts} +0 -0
  32. /package/dist/templates/core/shared/{src/components → src-components}/index.ts +0 -0
  33. /package/dist/templates/core/shared/{src/layouts → src-layouts}/AppLayout.tsx +0 -0
  34. /package/dist/templates/core/shared/{src/navigation → src-navigation}/index.ts +0 -0
  35. /package/dist/templates/core/shared/{src/screens → src-screens}/ExploreScreen.tsx +0 -0
  36. /package/dist/templates/core/shared/{src/screens → src-screens}/HomeScreen.tsx +0 -0
  37. /package/dist/templates/core/shared/{src/screens → src-screens}/ProfileScreen.tsx +0 -0
  38. /package/dist/templates/core/shared/{src/screens → src-screens}/SettingsScreen.tsx +0 -0
@@ -0,0 +1,217 @@
1
+ /**
2
+ * GraphQL Schema Definition
3
+ *
4
+ * Uses Prisma database for data persistence.
5
+ */
6
+
7
+ import { builder } from './builder.js';
8
+ import { prisma } from '../lib/database.js';
9
+
10
+ // =============================================================================
11
+ // Object Types
12
+ // =============================================================================
13
+
14
+ const ItemType = builder.objectRef<{
15
+ id: string;
16
+ title: string;
17
+ description: string | null;
18
+ completed: boolean;
19
+ createdAt: Date;
20
+ updatedAt: Date;
21
+ }>('Item');
22
+
23
+ builder.objectType(ItemType, {
24
+ description: 'A simple item for demonstrating CRUD operations',
25
+ fields: (t) => ({
26
+ id: t.exposeString('id'),
27
+ title: t.exposeString('title'),
28
+ description: t.exposeString('description', { nullable: true }),
29
+ completed: t.exposeBoolean('completed'),
30
+ createdAt: t.expose('createdAt', { type: 'DateTime' }),
31
+ updatedAt: t.expose('updatedAt', { type: 'DateTime' }),
32
+ }),
33
+ });
34
+
35
+ const ItemStatsType = builder.objectRef<{
36
+ total: number;
37
+ completed: number;
38
+ pending: number;
39
+ }>('ItemStats');
40
+
41
+ builder.objectType(ItemStatsType, {
42
+ description: 'Aggregate item statistics',
43
+ fields: (t) => ({
44
+ total: t.exposeInt('total'),
45
+ completed: t.exposeInt('completed'),
46
+ pending: t.exposeInt('pending'),
47
+ }),
48
+ });
49
+
50
+ // =============================================================================
51
+ // Queries
52
+ // =============================================================================
53
+
54
+ builder.queryFields((t) => ({
55
+ // Get single item by ID
56
+ item: t.field({
57
+ type: ItemType,
58
+ nullable: true,
59
+ args: {
60
+ id: t.arg.string({ required: true }),
61
+ },
62
+ resolve: async (_, args) => {
63
+ return await prisma.item.findUnique({
64
+ where: { id: args.id },
65
+ });
66
+ },
67
+ }),
68
+
69
+ // List all items with optional filtering
70
+ items: t.field({
71
+ type: [ItemType],
72
+ args: {
73
+ completed: t.arg.boolean(),
74
+ search: t.arg.string(),
75
+ },
76
+ resolve: async (_, args) => {
77
+ const where: any = {};
78
+
79
+ if (args.completed !== null && args.completed !== undefined) {
80
+ where.completed = args.completed;
81
+ }
82
+
83
+ if (args.search) {
84
+ where.OR = [
85
+ { title: { contains: args.search } },
86
+ { description: { contains: args.search } },
87
+ ];
88
+ }
89
+
90
+ return await prisma.item.findMany({
91
+ where,
92
+ orderBy: { createdAt: 'desc' },
93
+ });
94
+ },
95
+ }),
96
+
97
+ // Get item stats
98
+ itemStats: t.field({
99
+ type: ItemStatsType,
100
+ resolve: async () => {
101
+ const items = await prisma.item.findMany();
102
+
103
+ return {
104
+ total: items.length,
105
+ completed: items.filter((i) => i.completed).length,
106
+ pending: items.filter((i) => !i.completed).length,
107
+ };
108
+ },
109
+ }),
110
+ }));
111
+
112
+ // =============================================================================
113
+ // Mutations
114
+ // =============================================================================
115
+
116
+ const CreateItemInput = builder.inputType('CreateItemInput', {
117
+ fields: (t) => ({
118
+ title: t.string({ required: true }),
119
+ description: t.string(),
120
+ completed: t.boolean({ defaultValue: false }),
121
+ }),
122
+ });
123
+
124
+ const UpdateItemInput = builder.inputType('UpdateItemInput', {
125
+ fields: (t) => ({
126
+ title: t.string(),
127
+ description: t.string(),
128
+ completed: t.boolean(),
129
+ }),
130
+ });
131
+
132
+ builder.mutationFields((t) => ({
133
+ // Create a new item
134
+ createItem: t.field({
135
+ type: ItemType,
136
+ args: {
137
+ input: t.arg({ type: CreateItemInput, required: true }),
138
+ },
139
+ resolve: async (_, args) => {
140
+ return await prisma.item.create({
141
+ data: {
142
+ title: args.input.title,
143
+ description: args.input.description,
144
+ completed: args.input.completed ?? false,
145
+ },
146
+ });
147
+ },
148
+ }),
149
+
150
+ // Update an existing item
151
+ updateItem: t.field({
152
+ type: ItemType,
153
+ nullable: true,
154
+ args: {
155
+ id: t.arg.string({ required: true }),
156
+ input: t.arg({ type: UpdateItemInput, required: true }),
157
+ },
158
+ resolve: async (_, args) => {
159
+ const data: any = {};
160
+
161
+ if (args.input.title !== null && args.input.title !== undefined) {
162
+ data.title = args.input.title;
163
+ }
164
+ if (args.input.description !== undefined) {
165
+ data.description = args.input.description;
166
+ }
167
+ if (args.input.completed !== null && args.input.completed !== undefined) {
168
+ data.completed = args.input.completed;
169
+ }
170
+
171
+ return await prisma.item.update({
172
+ where: { id: args.id },
173
+ data,
174
+ });
175
+ },
176
+ }),
177
+
178
+ // Delete an item
179
+ deleteItem: t.field({
180
+ type: ItemType,
181
+ nullable: true,
182
+ args: {
183
+ id: t.arg.string({ required: true }),
184
+ },
185
+ resolve: async (_, args) => {
186
+ return await prisma.item.delete({
187
+ where: { id: args.id },
188
+ });
189
+ },
190
+ }),
191
+
192
+ // Toggle item completed status
193
+ toggleItem: t.field({
194
+ type: ItemType,
195
+ nullable: true,
196
+ args: {
197
+ id: t.arg.string({ required: true }),
198
+ },
199
+ resolve: async (_, args) => {
200
+ const item = await prisma.item.findUnique({
201
+ where: { id: args.id },
202
+ });
203
+
204
+ if (!item) {
205
+ return null;
206
+ }
207
+
208
+ return await prisma.item.update({
209
+ where: { id: args.id },
210
+ data: { completed: !item.completed },
211
+ });
212
+ },
213
+ }),
214
+ }));
215
+
216
+ // Build and export the schema
217
+ export const schema = builder.toSchema();
@@ -0,0 +1,15 @@
1
+ import type { CreateExpressContextOptions } from '@trpc/server/adapters/express';
2
+
3
+ export interface Context {
4
+ req: CreateExpressContextOptions['req'];
5
+ res: CreateExpressContextOptions['res'];
6
+ }
7
+
8
+ export const createContext = ({ req, res }: CreateExpressContextOptions): Context => {
9
+ return {
10
+ req,
11
+ res,
12
+ };
13
+ };
14
+
15
+ export type { CreateExpressContextOptions };
@@ -0,0 +1,6 @@
1
+ // Main exports for the API
2
+ export { appRouter } from './router/index.js';
3
+ export type { AppRouter } from './router/index.js';
4
+
5
+ // Export context type for client usage
6
+ export type { Context } from './context.js';
@@ -0,0 +1,4 @@
1
+ import { prisma } from '@{{workspaceScope}}/database';
2
+
3
+ export { prisma };
4
+ export default prisma;
@@ -0,0 +1,187 @@
1
+ import { z } from 'zod';
2
+ import { router, publicProcedure } from '../trpc.js';
3
+ import { prisma } from '../lib/database.js';
4
+ import { ItemCreateSchema, ItemUpdateSchema } from '@{{workspaceScope}}/database';
5
+
6
+ // ==========================================================================
7
+ // Base Routes (no database required)
8
+ // ==========================================================================
9
+
10
+ const baseRoutes = {
11
+ // Hello world procedure
12
+ hello: publicProcedure
13
+ .input(z.object({ name: z.string().optional() }))
14
+ .query(({ input }) => {
15
+ return {
16
+ greeting: `Hello ${input.name || 'World'}!`,
17
+ timestamp: new Date().toISOString(),
18
+ };
19
+ }),
20
+
21
+ // Health check procedure
22
+ health: publicProcedure.query(() => {
23
+ return {
24
+ status: 'OK',
25
+ timestamp: new Date().toISOString(),
26
+ version: '1.0.0',
27
+ };
28
+ }),
29
+
30
+ // Echo procedure - returns what you send
31
+ echo: publicProcedure
32
+ .input(z.object({ message: z.string() }))
33
+ .query(({ input }) => {
34
+ return {
35
+ original: input.message,
36
+ reversed: input.message.split('').reverse().join(''),
37
+ length: input.message.length,
38
+ timestamp: new Date().toISOString(),
39
+ };
40
+ }),
41
+
42
+ // Counter - in-memory state demo
43
+ counter: router({
44
+ // Get current counter value
45
+ get: publicProcedure.query(() => {
46
+ return { value: counterState.value };
47
+ }),
48
+
49
+ // Increment counter
50
+ increment: publicProcedure.mutation(() => {
51
+ counterState.value += 1;
52
+ return { value: counterState.value };
53
+ }),
54
+
55
+ // Decrement counter
56
+ decrement: publicProcedure.mutation(() => {
57
+ counterState.value -= 1;
58
+ return { value: counterState.value };
59
+ }),
60
+
61
+ // Reset counter
62
+ reset: publicProcedure.mutation(() => {
63
+ counterState.value = 0;
64
+ return { value: counterState.value };
65
+ }),
66
+ }),
67
+ };
68
+
69
+ // In-memory counter state
70
+ const counterState = { value: 0 };
71
+
72
+ // ==========================================================================
73
+ // Database Routes (requires Prisma)
74
+ // ==========================================================================
75
+
76
+ const itemsRouter = router({
77
+ // List all items
78
+ list: publicProcedure
79
+ .input(
80
+ z.object({
81
+ completed: z.boolean().optional(),
82
+ search: z.string().optional(),
83
+ }).optional()
84
+ )
85
+ .query(async ({ input }) => {
86
+ const where: any = {};
87
+
88
+ if (input?.completed !== undefined) {
89
+ where.completed = input.completed;
90
+ }
91
+
92
+ if (input?.search) {
93
+ where.OR = [
94
+ { title: { contains: input.search } },
95
+ { description: { contains: input.search } },
96
+ ];
97
+ }
98
+
99
+ return await prisma.item.findMany({
100
+ where,
101
+ orderBy: { createdAt: 'desc' },
102
+ });
103
+ }),
104
+
105
+ // Get single item by ID
106
+ byId: publicProcedure
107
+ .input(z.object({ id: z.string() }))
108
+ .query(async ({ input }) => {
109
+ return await prisma.item.findUnique({
110
+ where: { id: input.id },
111
+ });
112
+ }),
113
+
114
+ // Create item
115
+ create: publicProcedure
116
+ .input(ItemCreateSchema)
117
+ .mutation(async ({ input }) => {
118
+ return await prisma.item.create({
119
+ data: input,
120
+ });
121
+ }),
122
+
123
+ // Update item
124
+ update: publicProcedure
125
+ .input(
126
+ z.object({
127
+ id: z.string(),
128
+ data: ItemUpdateSchema,
129
+ })
130
+ )
131
+ .mutation(async ({ input }) => {
132
+ return await prisma.item.update({
133
+ where: { id: input.id },
134
+ data: input.data,
135
+ });
136
+ }),
137
+
138
+ // Delete item
139
+ delete: publicProcedure
140
+ .input(z.object({ id: z.string() }))
141
+ .mutation(async ({ input }) => {
142
+ return await prisma.item.delete({
143
+ where: { id: input.id },
144
+ });
145
+ }),
146
+
147
+ // Toggle completed status
148
+ toggle: publicProcedure
149
+ .input(z.object({ id: z.string() }))
150
+ .mutation(async ({ input }) => {
151
+ const item = await prisma.item.findUnique({
152
+ where: { id: input.id },
153
+ });
154
+
155
+ if (!item) {
156
+ throw new Error('Item not found');
157
+ }
158
+
159
+ return await prisma.item.update({
160
+ where: { id: input.id },
161
+ data: { completed: !item.completed },
162
+ });
163
+ }),
164
+
165
+ // Get stats
166
+ stats: publicProcedure.query(async () => {
167
+ const items = await prisma.item.findMany();
168
+
169
+ return {
170
+ total: items.length,
171
+ completed: items.filter((i: { completed: boolean }) => i.completed).length,
172
+ pending: items.filter((i: { completed: boolean }) => !i.completed).length,
173
+ };
174
+ }),
175
+ });
176
+
177
+ // ==========================================================================
178
+ // Combined Router
179
+ // ==========================================================================
180
+
181
+ export const appRouter = router({
182
+ ...baseRoutes,
183
+ items: itemsRouter,
184
+ });
185
+
186
+ // Export type definition of API
187
+ export type AppRouter = typeof appRouter;
@@ -0,0 +1,63 @@
1
+ import express from 'express';
2
+ import cors from 'cors';
3
+ import { createExpressMiddleware } from '@trpc/server/adapters/express';
4
+ import { createYoga } from 'graphql-yoga';
5
+ import { appRouter } from './router/index.js';
6
+ import { createContext } from './context.js';
7
+ import { schema } from './graphql/schema.js';
8
+ import dotenv from 'dotenv';
9
+
10
+ // Load environment variables
11
+ dotenv.config();
12
+
13
+ const app = express();
14
+ const PORT = process.env.PORT || 3000;
15
+
16
+ // CORS configuration
17
+ app.use(cors({
18
+ origin: process.env.CORS_ORIGIN || 'http://localhost:5173',
19
+ credentials: true,
20
+ }));
21
+
22
+ // Health check endpoint
23
+ app.get('/health', (req, res) => {
24
+ res.json({ status: 'OK', timestamp: new Date().toISOString() });
25
+ });
26
+
27
+ // tRPC middleware
28
+ app.use(
29
+ '/trpc',
30
+ createExpressMiddleware({
31
+ router: appRouter,
32
+ createContext,
33
+ })
34
+ );
35
+
36
+ // GraphQL Yoga middleware
37
+ const yoga = createYoga({
38
+ schema,
39
+ graphqlEndpoint: '/graphql',
40
+ // Enable GraphiQL playground in development
41
+ graphiql: process.env.NODE_ENV !== 'production',
42
+ });
43
+
44
+ app.use('/graphql', yoga);
45
+
46
+ // Default route
47
+ app.get('/', (req, res) => {
48
+ res.json({
49
+ message: 'Welcome to {{appDisplayName}} API',
50
+ endpoints: {
51
+ health: '/health',
52
+ trpc: '/trpc',
53
+ graphql: '/graphql',
54
+ }
55
+ });
56
+ });
57
+
58
+ app.listen(PORT, () => {
59
+ console.log(`Server running on http://localhost:${PORT}`);
60
+ console.log(`tRPC API available at http://localhost:${PORT}/trpc`);
61
+ console.log(`GraphQL API available at http://localhost:${PORT}/graphql`);
62
+ console.log(`Health check at http://localhost:${PORT}/health`);
63
+ });
@@ -0,0 +1,20 @@
1
+ import { initTRPC } from '@trpc/server';
2
+ import { type Context } from './context.js';
3
+ import { ZodError } from 'zod';
4
+
5
+ const t = initTRPC.context<Context>().create({
6
+ errorFormatter({ shape, error }) {
7
+ return {
8
+ ...shape,
9
+ data: {
10
+ ...shape.data,
11
+ zodError:
12
+ error.cause instanceof ZodError ? error.cause.flatten() : null,
13
+ },
14
+ };
15
+ },
16
+ });
17
+
18
+ // Export reusable router and procedure helpers
19
+ export const router = t.router;
20
+ export const publicProcedure = t.procedure;
@@ -0,0 +1,22 @@
1
+ // Prisma schema for {{appDisplayName}}
2
+ // Learn more: https://pris.ly/d/prisma-schema
3
+
4
+ generator client {
5
+ provider = "prisma-client-js"
6
+ output = "./generated/client"
7
+ }
8
+
9
+ datasource db {
10
+ provider = "sqlite"
11
+ url = env("DATABASE_URL")
12
+ }
13
+
14
+ // Simple Item model for demonstrating CRUD operations
15
+ model Item {
16
+ id String @id @default(cuid())
17
+ title String
18
+ description String?
19
+ completed Boolean @default(false)
20
+ createdAt DateTime @default(now())
21
+ updatedAt DateTime @updatedAt
22
+ }
@@ -1,4 +1,4 @@
1
- import './styles';
1
+ import './theme';
2
2
 
3
3
  // Export the unified App component
4
4
  export { App } from './components';
@@ -0,0 +1,51 @@
1
+ import React, { useState } from 'react';
2
+ import { NavigatorProvider } from '@idealyst/navigation';
3
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
4
+ import { AppRouter } from '../navigation/AppRouter';
5
+ import { trpc, createTRPCClient } from '../trpc/client';
6
+ import { ApolloProvider, createApolloClient } from '../graphql/client';
7
+
8
+ // API configuration
9
+ const API_URL = 'http://localhost:3002';
10
+
11
+ /**
12
+ * Main App component for the {{appDisplayName}}
13
+ * Sets up navigation with tRPC and Apollo providers
14
+ */
15
+ export const App: React.FC = () => {
16
+ // Create React Query client
17
+ const [queryClient] = useState(() => new QueryClient({
18
+ defaultOptions: {
19
+ queries: {
20
+ retry: 1,
21
+ staleTime: 5000,
22
+ },
23
+ },
24
+ }));
25
+
26
+ // Create tRPC client
27
+ const [trpcClient] = useState(() =>
28
+ createTRPCClient({
29
+ apiUrl: `${API_URL}/trpc`,
30
+ })
31
+ );
32
+
33
+ // Create Apollo client
34
+ const [apolloClient] = useState(() =>
35
+ createApolloClient({
36
+ graphqlUrl: `${API_URL}/graphql`,
37
+ })
38
+ );
39
+
40
+ return (
41
+ <trpc.Provider client={trpcClient} queryClient={queryClient}>
42
+ <QueryClientProvider client={queryClient}>
43
+ <ApolloProvider client={apolloClient}>
44
+ <NavigatorProvider route={AppRouter} />
45
+ </ApolloProvider>
46
+ </QueryClientProvider>
47
+ </trpc.Provider>
48
+ );
49
+ };
50
+
51
+ export default App;
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Apollo Client Configuration
3
+ *
4
+ * Provides a configured Apollo Client for GraphQL queries and mutations.
5
+ */
6
+
7
+ import { ApolloClient, InMemoryCache, HttpLink, from } from '@apollo/client';
8
+ import { onError } from '@apollo/client/link/error';
9
+
10
+ export interface ApolloClientConfig {
11
+ graphqlUrl?: string;
12
+ headers?: () => Record<string, string>;
13
+ }
14
+
15
+ // Error handling link
16
+ const createErrorLink = () =>
17
+ onError(({ graphQLErrors, networkError }) => {
18
+ if (graphQLErrors) {
19
+ graphQLErrors.forEach(({ message, locations, path }) => {
20
+ console.error(
21
+ `[GraphQL error]: Message: ${message}, Location: ${JSON.stringify(locations)}, Path: ${path}`
22
+ );
23
+ });
24
+ }
25
+ if (networkError) {
26
+ console.error(`[Network error]: ${networkError}`);
27
+ }
28
+ });
29
+
30
+ /**
31
+ * Creates a configured Apollo Client
32
+ */
33
+ export function createApolloClient(config: ApolloClientConfig = {}) {
34
+ const { graphqlUrl = 'http://localhost:3000/graphql', headers } = config;
35
+
36
+ // HTTP link to GraphQL endpoint
37
+ const httpLink = new HttpLink({
38
+ uri: graphqlUrl,
39
+ credentials: 'include',
40
+ headers: headers?.(),
41
+ });
42
+
43
+ // Create the Apollo Client
44
+ return new ApolloClient({
45
+ link: from([createErrorLink(), httpLink]),
46
+ cache: new InMemoryCache({
47
+ typePolicies: {
48
+ Item: {
49
+ keyFields: ['id'],
50
+ },
51
+ },
52
+ }),
53
+ defaultOptions: {
54
+ watchQuery: {
55
+ fetchPolicy: 'cache-and-network',
56
+ },
57
+ },
58
+ });
59
+ }
60
+
61
+ // Re-export Apollo hooks and types for convenience
62
+ export {
63
+ ApolloProvider,
64
+ useQuery,
65
+ useMutation,
66
+ useLazyQuery,
67
+ gql,
68
+ } from '@apollo/client';
69
+
70
+ export type { ApolloError, QueryResult, MutationResult } from '@apollo/client';