metabinaries 1.0.0

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,303 @@
1
+ export const configTemplates = {
2
+ '.gitattributes': `# Auto detect text files and perform LF normalization
3
+ * text=auto
4
+
5
+ # Force Unix line endings for shell scripts
6
+ *.sh text eol=lf
7
+ .husky/* text eol=lf
8
+
9
+ # Force Windows line endings for batch files
10
+ *.bat text eol=crlf
11
+
12
+ # Binary files
13
+ *.png binary
14
+ *.jpg binary
15
+ *.ico binary
16
+ *.gif binary
17
+ *.woff binary
18
+ *.woff2 binary
19
+ `,
20
+
21
+ '.gitignore': `# dependencies
22
+ /node_modules
23
+ /.pnp
24
+ .pnp.js
25
+
26
+ # testing
27
+ /coverage
28
+
29
+ # next.js
30
+ /.next/
31
+ /out/
32
+
33
+ # production
34
+ /build
35
+
36
+ # debug
37
+ npm-debug.log*
38
+ yarn-debug.log*
39
+ yarn-error.log*
40
+
41
+ # local env files
42
+ .env
43
+ .env.local
44
+ .env.development.local
45
+ .env.test.local
46
+ .env.production.local
47
+
48
+ # vercel
49
+ .vercel
50
+
51
+ # typescript
52
+ *.tsbuildinfo
53
+ next-env.d.ts`,
54
+
55
+ '.env': `NEXT_PUBLIC_API_URL=https://api.example.com
56
+ NEXT_PUBLIC_APP_URL=https://app.example.com
57
+ NEXT_PUBLIC_SITE_URL=https://example.com
58
+ NEXT_PUBLIC_DEFAULT_LOCALE=en`,
59
+
60
+ '.env.local': `NEXT_PUBLIC_API_URL=http://localhost:3000/api
61
+ NEXT_PUBLIC_APP_URL=http://localhost:3000
62
+ NEXT_PUBLIC_SITE_URL=http://localhost:3000
63
+ NEXT_PUBLIC_DEFAULT_LOCALE=en`,
64
+
65
+ '.env.example': `NEXT_PUBLIC_API_URL=
66
+ NEXT_PUBLIC_APP_URL=
67
+ NEXT_PUBLIC_SITE_URL=
68
+ NEXT_PUBLIC_DEFAULT_LOCALE=en`,
69
+
70
+ 'api.yml': projectName => `openapi: 3.0.0
71
+ info:
72
+ title: ${projectName} API
73
+ version: 1.0.0
74
+ paths: {}`,
75
+
76
+ '.versionrc.json': `{
77
+ "types": [
78
+ {"type": "feat", "section": "Features"},
79
+ {"type": "fix", "section": "Bug Fixes"}
80
+ ]
81
+ }`,
82
+
83
+ 'components.json': `{
84
+ "$schema": "https://ui.shadcn.com/schema.json",
85
+ "style": "default",
86
+ "rsc": true,
87
+ "tsx": true,
88
+ "tailwind": {
89
+ "config": "tailwind.config.ts",
90
+ "css": "app/[locale]/globals.css",
91
+ "baseColor": "slate",
92
+ "cssVariables": true,
93
+ "prefix": ""
94
+ },
95
+ "aliases": {
96
+ "components": "@/components",
97
+ "utils": "@/lib/utils"
98
+ }
99
+ }`,
100
+
101
+ 'eslint.config.mjs': `export default [];`,
102
+
103
+ 'next.config.ts': `import type { NextConfig } from "next";
104
+ import createNextIntlPlugin from "next-intl/plugin";
105
+
106
+ const withNextIntl = createNextIntlPlugin();
107
+
108
+ const nextConfig: NextConfig = {
109
+
110
+ async headers() {
111
+ return [
112
+ {
113
+ source: "/api/(.*)",
114
+ headers: [
115
+ { key: "Access-Control-Allow-Credentials", value: "true" },
116
+ { key: "Access-Control-Allow-Origin", value: "*" }, // Recommended: Change to specific domain in production
117
+ {
118
+ key: "Access-Control-Allow-Methods",
119
+ value: "GET, POST, PUT, DELETE, OPTIONS, PATCH",
120
+ },
121
+ {
122
+ key: "Access-Control-Allow-Headers",
123
+ value: "Content-Type, Authorization, x-user-id, Accept",
124
+ },
125
+ ],
126
+ },
127
+ {
128
+ source: "/(.*)",
129
+ headers: [
130
+ { key: "X-Frame-Options", value: "DENY" },
131
+ { key: "X-Content-Type-Options", value: "nosniff" },
132
+ { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
133
+ { key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=()" },
134
+ { key: "Strict-Transport-Security", value: "max-age=31536000; includeSubDomains; preload" },
135
+ { key: "X-XSS-Protection", value: "1; mode=block" },
136
+ ],
137
+ },
138
+ ];
139
+ },
140
+
141
+ allowedDevOrigins: [
142
+ "http://localhost:3000",
143
+ ...(process.env.NEXT_PUBLIC_API_URL
144
+ ? [process.env.NEXT_PUBLIC_API_URL]
145
+ : []),
146
+ "https://api-dev.smartbinaries.com/",
147
+ ],
148
+
149
+ images: {
150
+ remotePatterns: [
151
+ {
152
+ protocol: "https",
153
+ hostname: "**",
154
+ port: "",
155
+ pathname: "/**",
156
+ },
157
+ {
158
+ protocol: "http",
159
+ hostname: "**",
160
+ port: "",
161
+ pathname: "/**",
162
+ },
163
+ ],
164
+ unoptimized: true,
165
+ formats: ["image/avif", "image/webp"],
166
+ },
167
+ };
168
+
169
+ export default withNextIntl(nextConfig);`,
170
+
171
+ 'postcss.config.mjs': `const config = {
172
+ plugins: {
173
+ "@tailwindcss/postcss": {},
174
+ },
175
+ };
176
+
177
+ export default config;`,
178
+
179
+ 'tailwind.config.ts': `import type { Config } from "tailwindcss";
180
+
181
+ const config: Config = {
182
+ darkMode: "class",
183
+ content: [
184
+ "./pages/**/*.{js,ts,jsx,tsx,mdx}",
185
+ "./components/**/*.{js,ts,jsx,tsx,mdx}",
186
+ "./app/**/*.{js,ts,jsx,tsx,mdx}",
187
+ ],
188
+ theme: {
189
+ extend: {
190
+ colors: {
191
+ background: 'hsl(var(--background))',
192
+ foreground: 'hsl(var(--foreground))',
193
+ card: {
194
+ DEFAULT: 'hsl(var(--card))',
195
+ foreground: 'hsl(var(--card-foreground))'
196
+ },
197
+ popover: {
198
+ DEFAULT: 'hsl(var(--popover))',
199
+ foreground: 'hsl(var(--popover-foreground))'
200
+ },
201
+ primary: {
202
+ DEFAULT: 'hsl(var(--primary))',
203
+ foreground: 'hsl(var(--primary-foreground))'
204
+ },
205
+ secondary: {
206
+ DEFAULT: 'hsl(var(--secondary))',
207
+ foreground: 'hsl(var(--secondary-foreground))'
208
+ },
209
+ muted: {
210
+ DEFAULT: 'hsl(var(--muted))',
211
+ foreground: 'hsl(var(--muted-foreground))'
212
+ },
213
+ accent: {
214
+ DEFAULT: 'hsl(var(--accent))',
215
+ foreground: 'hsl(var(--accent-foreground))'
216
+ },
217
+ destructive: {
218
+ DEFAULT: 'hsl(var(--destructive))',
219
+ foreground: 'hsl(var(--destructive-foreground))'
220
+ },
221
+ border: 'hsl(var(--border))',
222
+ input: 'hsl(var(--input))',
223
+ ring: 'hsl(var(--ring))',
224
+ chart: {
225
+ '1': 'hsl(var(--chart-1))',
226
+ '2': 'hsl(var(--chart-2))',
227
+ '3': 'hsl(var(--chart-3))',
228
+ '4': 'hsl(var(--chart-4))',
229
+ '5': 'hsl(var(--chart-5))'
230
+ }
231
+ },
232
+ borderRadius: {
233
+ lg: 'var(--radius)',
234
+ md: 'calc(var(--radius) - 2px)',
235
+ sm: 'calc(var(--radius) - 4px)'
236
+ }
237
+ }
238
+ },
239
+ plugins: [require("tailwindcss-animate")],
240
+ };
241
+ export default config;`,
242
+
243
+ 'tsconfig.json': `{
244
+ "compilerOptions": {
245
+ "target": "ES2017",
246
+ "lib": ["dom", "dom.iterable", "esnext"],
247
+ "allowJs": true,
248
+ "skipLibCheck": true,
249
+ "strict": true,
250
+ "noEmit": true,
251
+ "esModuleInterop": true,
252
+ "module": "esnext",
253
+ "moduleResolution": "bundler",
254
+ "resolveJsonModule": true,
255
+ "isolatedModules": true,
256
+ "jsx": "react-jsx",
257
+ "incremental": true,
258
+ "plugins": [
259
+ {
260
+ "name": "next"
261
+ }
262
+ ],
263
+ "paths": {
264
+ "@/*": ["./*"]
265
+ }
266
+ },
267
+ "include": [
268
+ "next-env.d.ts",
269
+ "**/*.ts",
270
+ "**/*.tsx",
271
+ ".next/types/**/*.ts",
272
+ ".next/dev/types/**/*.ts",
273
+ "**/*.mts",
274
+ "app/[locale]/(auth)/layout.tsx"
275
+ ],
276
+ "exclude": ["node_modules"]
277
+ }`,
278
+ 'i18n.ts': `import { getRequestConfig } from 'next-intl/server';
279
+ import { notFound } from 'next/navigation';
280
+ import { routing } from './i18n/routing';
281
+
282
+ export default getRequestConfig(async ({ locale }) => {
283
+ // Validate that the incoming \`locale\` parameter is valid
284
+ if (!locale || !routing.locales.includes(locale as any)) notFound();
285
+
286
+ return {
287
+ locale,
288
+ messages: (await import(\`./messages/\${locale}.json\`)).default,
289
+ };
290
+ });`,
291
+ 'messages/en.json': `{
292
+ "HomePage": {
293
+ "title": "Welcome to MetaBinaries",
294
+ "description": "Your CRM-style workspace is ready."
295
+ }
296
+ }`,
297
+ 'messages/ar.json': `{
298
+ "HomePage": {
299
+ "title": "مرحباً بك في ميتابيناريز",
300
+ "description": "بيئة عمل CRM الخاصة بك جاهزة."
301
+ }
302
+ }`,
303
+ };
@@ -0,0 +1,328 @@
1
+ export const coreTemplates = {
2
+ 'lib/utils.ts': `import { clsx, type ClassValue } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
4
+
5
+ export function cn(...inputs: ClassValue[]) {
6
+ return twMerge(clsx(inputs));
7
+ }`,
8
+
9
+ 'hooks/use-mobile.tsx': `import * as React from 'react';
10
+
11
+ const MOBILE_BREAKPOINT = 768;
12
+
13
+ export function useIsMobile() {
14
+ const [isMobile, setIsMobile] = React.useState<boolean | undefined>(
15
+ undefined
16
+ );
17
+
18
+ React.useEffect(() => {
19
+ const mql = window.matchMedia(\`(max-width: \${MOBILE_BREAKPOINT - 1}px)\`);
20
+ const onChange = () => {
21
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
22
+ };
23
+ mql.addEventListener('change', onChange);
24
+ setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
25
+ return () => mql.removeEventListener('change', onChange);
26
+ }, []);
27
+
28
+ return !!isMobile;
29
+ }`,
30
+
31
+ 'hooks/use-mobile.ts': `export function useIsMobile() { return false; }`,
32
+
33
+ 'lib/rate-limit.ts': `import { NextRequest, NextResponse } from 'next/server';
34
+
35
+ // Rate limiting configuration
36
+ const RATE_LIMIT_CONFIG = {
37
+ windowMs: 15 * 60 * 1000, // 15 minutes
38
+ maxRequests: 5, // Maximum 5 requests per window
39
+ message: 'Too many contact form submissions, please try again later.',
40
+ };
41
+
42
+ // Store for rate limiting (in production, use Redis or similar)
43
+ const rateLimitStore = new Map<string, { count: number; resetTime: number }>();
44
+
45
+ export function rateLimitMiddleware(request: NextRequest) {
46
+ const ip =
47
+ request.headers.get('x-forwarded-for') ||
48
+ request.headers.get('x-real-ip') ||
49
+ request.headers.get('cf-connecting-ip') ||
50
+ 'unknown';
51
+ const now = Date.now();
52
+ const windowMs = RATE_LIMIT_CONFIG.windowMs;
53
+
54
+ // Clean up expired entries
55
+ for (const [key, value] of rateLimitStore.entries()) {
56
+ if (now > value.resetTime) {
57
+ rateLimitStore.delete(key);
58
+ }
59
+ }
60
+
61
+ const current = rateLimitStore.get(ip);
62
+
63
+ if (!current) {
64
+ // First request from this IP
65
+ rateLimitStore.set(ip, {
66
+ count: 1,
67
+ resetTime: now + windowMs,
68
+ });
69
+ return null; // No rate limit exceeded
70
+ }
71
+
72
+ if (now > current.resetTime) {
73
+ // Window has expired, reset
74
+ rateLimitStore.set(ip, {
75
+ count: 1,
76
+ resetTime: now + windowMs,
77
+ });
78
+ return null; // No rate limit exceeded
79
+ }
80
+
81
+ if (current.count >= RATE_LIMIT_CONFIG.maxRequests) {
82
+ // Rate limit exceeded
83
+ return NextResponse.json(
84
+ {
85
+ success: false,
86
+ error: RATE_LIMIT_CONFIG.message,
87
+ retryAfter: Math.ceil((current.resetTime - now) / 1000),
88
+ },
89
+ { status: 429 }
90
+ );
91
+ }
92
+
93
+ // Increment count
94
+ current.count++;
95
+ rateLimitStore.set(ip, current);
96
+
97
+ return null; // No rate limit exceeded
98
+ }
99
+
100
+ // Alternative implementation using a more sophisticated approach
101
+ export class RateLimiter {
102
+ private store = new Map<string, { count: number; resetTime: number }>();
103
+
104
+ constructor(
105
+ private windowMs: number = 15 * 60 * 1000, // 15 minutes
106
+ private maxRequests: number = 5
107
+ ) {}
108
+
109
+ isAllowed(identifier: string): { allowed: boolean; retryAfter?: number } {
110
+ const now = Date.now();
111
+
112
+ // Clean up expired entries
113
+ this.cleanup();
114
+
115
+ const current = this.store.get(identifier);
116
+
117
+ if (!current) {
118
+ this.store.set(identifier, {
119
+ count: 1,
120
+ resetTime: now + this.windowMs,
121
+ });
122
+ return { allowed: true };
123
+ }
124
+
125
+ if (now > current.resetTime) {
126
+ // Window has expired, reset
127
+ this.store.set(identifier, {
128
+ count: 1,
129
+ resetTime: now + this.windowMs,
130
+ });
131
+ return { allowed: true };
132
+ }
133
+
134
+ if (current.count >= this.maxRequests) {
135
+ return {
136
+ allowed: false,
137
+ retryAfter: Math.ceil((current.resetTime - now) / 1000),
138
+ };
139
+ }
140
+
141
+ // Increment count
142
+ current.count++;
143
+ this.store.set(identifier, current);
144
+
145
+ return { allowed: true };
146
+ }
147
+
148
+ private cleanup() {
149
+ const now = Date.now();
150
+ for (const [key, value] of this.store.entries()) {
151
+ if (now > value.resetTime) {
152
+ this.store.delete(key);
153
+ }
154
+ }
155
+ }
156
+
157
+ // Get remaining requests for an identifier
158
+ getRemainingRequests(identifier: string): number {
159
+ const current = this.store.get(identifier);
160
+ if (!current) return this.maxRequests;
161
+
162
+ const now = Date.now();
163
+ if (now > current.resetTime) return this.maxRequests;
164
+
165
+ return Math.max(0, this.maxRequests - current.count);
166
+ }
167
+
168
+ // Get reset time for an identifier
169
+ getResetTime(identifier: string): number | null {
170
+ const current = this.store.get(identifier);
171
+ if (!current) return null;
172
+
173
+ const now = Date.now();
174
+ if (now > current.resetTime) return null;
175
+
176
+ return current.resetTime;
177
+ }
178
+ }
179
+
180
+ // Create a global rate limiter instance
181
+ export const contactFormRateLimiter = new RateLimiter(
182
+ 15 * 60 * 1000, // 15 minutes
183
+ 5 // 5 requests per window
184
+ );`,
185
+
186
+ 'lib/features/NAME/NAME.api.ts': `export const nameApi = {};`,
187
+ 'lib/features/NAME/NAME.service.ts': `export const nameService = {};`,
188
+ 'lib/features/NAME/NAME.slice.ts': `import { createSlice } from '@reduxjs/toolkit';
189
+ export const nameSlice = createSlice({ name: 'name', initialState: {}, reducers: {} });
190
+ export default nameSlice.reducer;`,
191
+ 'lib/features/NAME/NAME.types.ts': `export interface NameType {}`,
192
+ 'lib/features/NAME/NAME.utils.ts': `export const nameUtils = {};`,
193
+ 'lib/features/NAME/useNAME.ts': `export const useName = () => ({});`,
194
+
195
+ 'lib/shared/store.ts': `import { configureStore } from '@reduxjs/toolkit';
196
+ export const store = configureStore({ reducer: {} });`,
197
+
198
+ 'lib/shared/axios.ts': `import axios from 'axios';
199
+
200
+ const instance = axios.create({
201
+ baseURL: process.env.NEXT_PUBLIC_API_URL || '/api',
202
+ headers: {
203
+ 'Content-Type': 'application/json',
204
+ },
205
+ timeout: 10000, // Security: Add timeout to prevent hanging requests
206
+ });
207
+
208
+ // Request Interceptor: Add Authorization header
209
+ instance.interceptors.request.use(
210
+ (config) => {
211
+ // Security: Ensure token is retrieved safely
212
+ const token = typeof window !== 'undefined' ? localStorage.getItem('token') : null;
213
+ if (token) {
214
+ config.headers.Authorization = \`Bearer \${token}\`;
215
+ }
216
+ return config;
217
+ },
218
+ (error) => Promise.reject(error)
219
+ );
220
+
221
+ // Response Interceptor: Handle global errors (e.g., 401)
222
+ instance.interceptors.response.use(
223
+ (response) => response,
224
+ (error) => {
225
+ if (error.response?.status === 401) {
226
+ // Security: Clear sensible data on unauthorized access
227
+ if (typeof window !== 'undefined') {
228
+ localStorage.removeItem('token');
229
+ const locale = window.location.pathname.split('/')[1] || 'en';
230
+ window.location.href = \`/\${locale}/login\`;
231
+ }
232
+ }
233
+
234
+ // Security: Sanitize error responses before logging/showing to users
235
+ const sanitizedError = {
236
+ message: error.response?.data?.message || error.message || 'An unexpected error occurred',
237
+ status: error.response?.status,
238
+ };
239
+
240
+ return Promise.reject(sanitizedError);
241
+ }
242
+ );
243
+
244
+ export default instance;`,
245
+
246
+ 'lib/shared/utils/validation/common-rules.ts': `import { z } from 'zod';
247
+ export const commonRules = { email: z.string().email() };`,
248
+
249
+ 'lib/shared/utils/validation/NAME.validation.ts': `import { z } from 'zod';
250
+ export const nameValidation = z.object({});`,
251
+
252
+ 'i18n/routing.ts': `import { defineRouting } from 'next-intl/routing';
253
+
254
+ export const routing = defineRouting({
255
+ // A list of all locales that are supported
256
+ locales: ['en', 'ar'],
257
+
258
+ // Used when no locale matches
259
+ defaultLocale:
260
+ (process.env.NEXT_PUBLIC_DEFAULT_LOCALE as 'en' | 'ar') || 'en',
261
+ localePrefix: 'always',
262
+ });`,
263
+
264
+ 'i18n/navigation.ts': `import { createNavigation } from 'next-intl/navigation';
265
+ import { routing } from './routing';
266
+
267
+ // Lightweight wrappers around Next.js' navigation
268
+ // APIs that consider the routing configuration
269
+ export const { Link, redirect, usePathname, useRouter, getPathname } =
270
+ createNavigation(routing);`,
271
+
272
+ 'i18n/request.ts': `import { getRequestConfig } from 'next-intl/server';
273
+ import { hasLocale } from 'next-intl';
274
+ import { routing } from './routing';
275
+
276
+ export default getRequestConfig(async ({ requestLocale }) => {
277
+ // Typically corresponds to the \`[locale]\` segment
278
+ const requested = await requestLocale;
279
+ let locale = hasLocale(routing.locales, requested)
280
+ ? requested
281
+ : routing.defaultLocale;
282
+
283
+ // Safety check: ensure locale is never undefined or empty
284
+ if (!locale || !hasLocale(routing.locales, locale)) {
285
+ locale = 'en'; // Fallback to 'en' as the ultimate default
286
+ }
287
+
288
+ return {
289
+ locale,
290
+ messages: (await import(\`@/messages/\${locale}.json\`)).default,
291
+ };
292
+ });`,
293
+
294
+ 'middleware.ts': `import createMiddleware from 'next-intl/middleware';
295
+ import { routing } from './i18n/routing';
296
+
297
+ export default createMiddleware(routing);
298
+
299
+ export const config = {
300
+ matcher: ['/', '/(ar|en)/:path*']
301
+ };`,
302
+
303
+ 'lib/features/notifications/NotificationContext.tsx': `'use client';
304
+
305
+ import React, { createContext, useContext, useState, ReactNode } from 'react';
306
+
307
+ interface NotificationContextType {
308
+ // Add notification state and methods here
309
+ }
310
+
311
+ const NotificationContext = createContext<NotificationContextType | undefined>(undefined);
312
+
313
+ export function NotificationProvider({ children }: { children: ReactNode }) {
314
+ return (
315
+ <NotificationContext.Provider value={{}}>
316
+ {children}
317
+ </NotificationContext.Provider>
318
+ );
319
+ }
320
+
321
+ export function useNotifications() {
322
+ const context = useContext(NotificationContext);
323
+ if (context === undefined) {
324
+ throw new Error('useNotifications must be used within a NotificationProvider');
325
+ }
326
+ return context;
327
+ }`,
328
+ };
@@ -0,0 +1,21 @@
1
+ export const folderStructure = [
2
+ '.husky',
3
+ '.husky/_',
4
+ 'app/[locale]/(auth)/forgot-password',
5
+ 'app/[locale]/(auth)/login',
6
+ 'app/[locale]/(auth)/sign-up',
7
+ 'app/[locale]/(auth)/_components',
8
+ 'app/[locale]/(workspace)/(home)/_components',
9
+ 'app/[locale]/(workspace)/_Components',
10
+ 'components/layout',
11
+ 'components/shared',
12
+ 'components/ui',
13
+ 'docs',
14
+ 'hooks',
15
+ 'i18n',
16
+ 'lib/features/NAME',
17
+ 'lib/shared/utils/validation',
18
+ 'messages',
19
+ 'public/images',
20
+ 'public/icons',
21
+ ];