@promakeai/cli 0.0.5 → 0.0.6

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 (91) hide show
  1. package/dist/index.js +214 -135
  2. package/dist/registry/about-page.json +1 -1
  3. package/dist/registry/about-section.json +1 -1
  4. package/dist/registry/api.json +55 -0
  5. package/dist/registry/auth.json +70 -0
  6. package/dist/registry/bento-grid-section.json +1 -1
  7. package/dist/registry/blog-list-page.json +1 -1
  8. package/dist/registry/blog-section.json +1 -1
  9. package/dist/registry/cart-drawer.json +1 -1
  10. package/dist/registry/cart-page.json +3 -2
  11. package/dist/registry/category-section.json +1 -1
  12. package/dist/registry/checkout-page.json +3 -2
  13. package/dist/registry/contact-info-grid.json +1 -1
  14. package/dist/registry/contact-page-centered.json +1 -1
  15. package/dist/registry/contact-page-map-overlay.json +1 -1
  16. package/dist/registry/contact-page.json +1 -1
  17. package/dist/registry/cookies-page.json +1 -1
  18. package/dist/registry/cta-section.json +1 -1
  19. package/dist/registry/db.json +129 -0
  20. package/dist/registry/docs/cart-page.md +1 -0
  21. package/dist/registry/docs/checkout-page.md +1 -0
  22. package/dist/registry/docs/forgot-password-page.md +37 -0
  23. package/dist/registry/docs/header-ecommerce.md +1 -0
  24. package/dist/registry/docs/products-page.md +1 -0
  25. package/dist/registry/docs/register-page.md +39 -0
  26. package/dist/registry/ecommerce-core.json +1 -1
  27. package/dist/registry/empty-page.json +1 -1
  28. package/dist/registry/faq-categorized.json +1 -1
  29. package/dist/registry/faq-simple.json +1 -1
  30. package/dist/registry/favorites-blog-block.json +1 -1
  31. package/dist/registry/favorites-ecommerce-block.json +1 -1
  32. package/dist/registry/feature-section.json +1 -1
  33. package/dist/registry/featured-products.json +1 -1
  34. package/dist/registry/footer-detailed.json +1 -1
  35. package/dist/registry/footer-minimal.json +3 -3
  36. package/dist/registry/footer.json +1 -1
  37. package/dist/registry/forgot-password-page.json +49 -0
  38. package/dist/registry/header-ecommerce.json +3 -2
  39. package/dist/registry/header-mega.json +1 -1
  40. package/dist/registry/header-minimal.json +1 -1
  41. package/dist/registry/header-simple.json +1 -1
  42. package/dist/registry/hero-cta.json +1 -1
  43. package/dist/registry/hero-gradient.json +1 -1
  44. package/dist/registry/hero-profile.json +1 -1
  45. package/dist/registry/hero.json +1 -1
  46. package/dist/registry/index.json +3 -0
  47. package/dist/registry/orders-list-block.json +1 -1
  48. package/dist/registry/payment-success-block.json +1 -1
  49. package/dist/registry/post-detail-block.json +1 -1
  50. package/dist/registry/pricing-section.json +1 -1
  51. package/dist/registry/privacy-page.json +1 -1
  52. package/dist/registry/products-page.json +3 -2
  53. package/dist/registry/register-page.json +49 -0
  54. package/dist/registry/related-posts-block.json +1 -1
  55. package/dist/registry/terms-page.json +1 -1
  56. package/dist/registry/testimonials-carousel.json +1 -1
  57. package/dist/registry/testimonials-grid.json +1 -1
  58. package/package.json +1 -1
  59. package/template/src/App.tsx +3 -24
  60. package/template/src/components/Layout.tsx +0 -4
  61. package/template/src/index.css +1 -0
  62. package/template/src/lang/en/index.json +1 -28
  63. package/template/src/lang/tr/index.json +1 -28
  64. package/template/src/pages/Index.tsx +1 -102
  65. package/template/src/components/Footer.tsx +0 -100
  66. package/template/src/components/Header.tsx +0 -79
  67. package/template/src/components/Hero.tsx +0 -69
  68. package/template/src/modules/api/USAGE.md +0 -515
  69. package/template/src/modules/api/customer-client.ts +0 -20
  70. package/template/src/modules/api/get-error-message.ts +0 -18
  71. package/template/src/modules/api/validation/en.json +0 -29
  72. package/template/src/modules/api/validation/tr.json +0 -29
  73. package/template/src/modules/auth/USAGE.md +0 -248
  74. package/template/src/modules/auth/auth-header-menu.tsx +0 -123
  75. package/template/src/modules/auth/auth-store.ts +0 -57
  76. package/template/src/modules/auth/forgot-password-page.tsx +0 -371
  77. package/template/src/modules/auth/login-page.tsx +0 -183
  78. package/template/src/modules/auth/register-page.tsx +0 -252
  79. package/template/src/modules/auth/use-auth.ts +0 -273
  80. package/template/src/modules/db/adapters/IDataAdapter.ts +0 -26
  81. package/template/src/modules/db/adapters/SqliteAdapter.ts +0 -364
  82. package/template/src/modules/db/adapters/index.ts +0 -2
  83. package/template/src/modules/db/config.ts +0 -59
  84. package/template/src/modules/db/core/DataManager.ts +0 -125
  85. package/template/src/modules/db/core/types.ts +0 -101
  86. package/template/src/modules/db/index.ts +0 -42
  87. package/template/src/modules/db/react/QueryProvider.tsx +0 -16
  88. package/template/src/modules/db/react/index.ts +0 -23
  89. package/template/src/modules/db/react/queryClient.ts +0 -64
  90. package/template/src/modules/db/react/useRepository.ts +0 -400
  91. package/template/src/modules/db/utils/parsers.ts +0 -96
@@ -1,23 +0,0 @@
1
- // Provider
2
- export { DBQueryProvider } from "./QueryProvider";
3
-
4
- // Query client and utilities
5
- export { queryClient, queryKeys, cacheUtils } from "./queryClient";
6
-
7
- // Generic repository hooks
8
- export {
9
- useRepositoryQuery,
10
- useRepositoryQueryOne,
11
- useRepositoryQueryById,
12
- useRepositoryPagination,
13
- useRepositoryInfiniteQuery,
14
- useRepositoryCreate,
15
- useRepositoryUpdate,
16
- useRepositoryDelete,
17
- // Raw SQL hooks
18
- useRawQuery,
19
- useRawQueryOne,
20
- } from "./useRepository";
21
-
22
- // Types
23
- export type { RepositoryQueryOptions } from "./useRepository";
@@ -1,64 +0,0 @@
1
- import { QueryClient } from "@tanstack/react-query";
2
-
3
- /**
4
- * React Query handles ALL caching, refetching, and invalidation
5
- * No custom cache needed!
6
- */
7
- export const queryClient = new QueryClient({
8
- defaultOptions: {
9
- queries: {
10
- staleTime: 30 * 1000, // 30 seconds fresh
11
- gcTime: 5 * 60 * 1000, // 5 minutes in cache (was cacheTime)
12
- retry: 1,
13
- refetchOnWindowFocus: false, // Don't auto-refetch on window focus
14
- refetchOnReconnect: false, // Don't refetch on reconnect
15
- refetchOnMount: false, // Don't refetch on component mount (prevent loops)
16
- },
17
- mutations: {
18
- retry: 0,
19
- },
20
- },
21
- });
22
-
23
- /**
24
- * Query key factory - for cache management
25
- * React Query uses these keys to cache and invalidate queries
26
- */
27
- export const queryKeys = {
28
- all: (table: string) => [table] as const,
29
- lists: (table: string) => [table, "list"] as const,
30
- list: (table: string, options?: any) => [table, "list", options] as const,
31
- details: (table: string) => [table, "detail"] as const,
32
- detail: (table: string, id: number | string) =>
33
- [table, "detail", id] as const,
34
- paginated: (table: string, page: number, limit: number, options?: any) =>
35
- [table, "paginated", page, limit, options] as const,
36
- infinite: (table: string, limit: number, options?: any) =>
37
- [table, "infinite", limit, options] as const,
38
- count: (table: string, options?: any) => [table, "count", options] as const,
39
- };
40
-
41
- /**
42
- * Manual cache utilities (rarely needed)
43
- */
44
- export const cacheUtils = {
45
- // Invalidate all queries for a table
46
- invalidateTable: (table: string) => {
47
- return queryClient.invalidateQueries({ queryKey: queryKeys.all(table) });
48
- },
49
-
50
- // Clear all cache
51
- clearAll: () => {
52
- return queryClient.clear();
53
- },
54
-
55
- // Get cached data
56
- getCachedData: <T>(queryKey: any[]) => {
57
- return queryClient.getQueryData<T>(queryKey);
58
- },
59
-
60
- // Set cached data manually
61
- setCachedData: <T>(queryKey: any[], data: T) => {
62
- return queryClient.setQueryData<T>(queryKey, data);
63
- },
64
- };
@@ -1,400 +0,0 @@
1
- import {
2
- useQuery,
3
- useMutation,
4
- useQueryClient,
5
- useInfiniteQuery,
6
- type UseQueryOptions,
7
- type UseMutationOptions,
8
- type UseInfiniteQueryOptions,
9
- } from "@tanstack/react-query";
10
- import { DataManager } from "../core/DataManager";
11
- import { getAdapter } from "../config";
12
- import { queryKeys } from "./queryClient";
13
- import type { QueryOptions } from "../core/types";
14
-
15
- // Singleton manager
16
- let managerInstance: DataManager | null = null;
17
- function getManager() {
18
- if (!managerInstance) {
19
- managerInstance = DataManager.getInstance(getAdapter());
20
- }
21
- return managerInstance;
22
- }
23
-
24
- // ==========================================
25
- // QUERY HOOKS
26
- // ==========================================
27
-
28
- // Omit 'select' from QueryOptions to avoid conflict with React Query's select
29
- export interface RepositoryQueryOptions<T> extends Omit<
30
- QueryOptions,
31
- "select"
32
- > {
33
- // SQL SELECT fields (renamed to avoid conflict)
34
- selectFields?: string[];
35
-
36
- // React Query options
37
- enabled?: boolean;
38
- staleTime?: number;
39
- gcTime?: number;
40
- refetchOnWindowFocus?: boolean;
41
- refetchInterval?: number | false;
42
- select?: (data: T[]) => any; // React Query data transformation
43
- }
44
-
45
- /**
46
- * Generic query hook - React Query handles all caching
47
- * @example
48
- * // Simple query
49
- * const { data: posts, isLoading } = useRepositoryQuery('posts', {
50
- * where: { published: 1 },
51
- * orderBy: [{ field: 'created_at', direction: 'DESC' }],
52
- * staleTime: 60000
53
- * });
54
- *
55
- * // With JOIN
56
- * const { data: posts } = useRepositoryQuery('posts', {
57
- * selectFields: ['posts.*', 'c.name as category_name'],
58
- * joins: [{
59
- * type: 'INNER',
60
- * table: 'post_categories',
61
- * alias: 'pc',
62
- * on: { leftField: 'posts.id', rightField: 'pc.post_id' }
63
- * }],
64
- * distinct: true
65
- * });
66
- *
67
- * // With complex WHERE
68
- * const { data: products } = useRepositoryQuery('products', {
69
- * whereAdvanced: {
70
- * type: 'AND',
71
- * conditions: [
72
- * { field: 'published', operator: '=', value: 1 },
73
- * { type: 'OR', conditions: [
74
- * { field: 'name', operator: 'LIKE', value: '%phone%' },
75
- * { field: 'description', operator: 'LIKE', value: '%phone%' }
76
- * ]}
77
- * ]
78
- * }
79
- * });
80
- */
81
- export function useRepositoryQuery<T = any>(
82
- table: string,
83
- options: RepositoryQueryOptions<T> = {},
84
- queryOptions?: Omit<UseQueryOptions<T[], Error>, "queryKey" | "queryFn">,
85
- ) {
86
- const manager = getManager();
87
- const {
88
- // QueryOptions fields
89
- where,
90
- limit,
91
- offset,
92
- orderBy,
93
- include,
94
- // New complex query fields
95
- selectFields,
96
- distinct,
97
- joins,
98
- whereAdvanced,
99
- groupBy,
100
- having,
101
- // React Query options
102
- select,
103
- enabled,
104
- staleTime,
105
- gcTime,
106
- refetchOnWindowFocus,
107
- refetchInterval,
108
- } = options;
109
-
110
- const queryOpts: QueryOptions = {
111
- where,
112
- limit,
113
- offset,
114
- orderBy,
115
- include,
116
- select: selectFields,
117
- distinct,
118
- joins,
119
- whereAdvanced,
120
- groupBy,
121
- having,
122
- };
123
-
124
- return useQuery<T[], Error>({
125
- queryKey: queryKeys.list(table, queryOpts),
126
- queryFn: () => manager.query<T>(table, queryOpts),
127
- select,
128
- enabled,
129
- staleTime,
130
- gcTime,
131
- refetchOnWindowFocus,
132
- refetchInterval,
133
- ...queryOptions,
134
- });
135
- }
136
-
137
- /**
138
- * Query single record
139
- * @example
140
- * const { data: post } = useRepositoryQueryOne('posts', {
141
- * where: { slug: 'my-post' }
142
- * });
143
- */
144
- export function useRepositoryQueryOne<T = any>(
145
- table: string,
146
- options: RepositoryQueryOptions<T> = {},
147
- ) {
148
- const result = useRepositoryQuery<T>(table, { ...options, limit: 1 });
149
-
150
- return {
151
- ...result,
152
- data: result.data?.[0] || null,
153
- };
154
- }
155
-
156
- /**
157
- * Query by ID - React Query caches by ID automatically
158
- * @example
159
- * const { data: post, isLoading } = useRepositoryQueryById('posts', postId);
160
- */
161
- export function useRepositoryQueryById<T = any>(
162
- table: string,
163
- id: number | string | null | undefined,
164
- options: Omit<UseQueryOptions<T | null, Error>, "queryKey" | "queryFn"> = {},
165
- ) {
166
- const manager = getManager();
167
-
168
- return useQuery<T | null, Error>({
169
- queryKey: queryKeys.detail(table, id as any),
170
- queryFn: () => manager.queryById<T>(table, id as any),
171
- enabled: options.enabled !== false && id != null,
172
- ...options,
173
- });
174
- }
175
-
176
- /**
177
- * Paginated query - React Query caches each page
178
- * @example
179
- * const { data, totalPages, hasMore } = useRepositoryPagination('products', page, 20);
180
- */
181
- export function useRepositoryPagination<T = any>(
182
- table: string,
183
- page: number = 1,
184
- limit: number = 10,
185
- options: QueryOptions = {},
186
- ) {
187
- const manager = getManager();
188
-
189
- return useQuery({
190
- queryKey: queryKeys.paginated(table, page, limit, options),
191
- queryFn: () => manager.paginate<T>(table, page, limit, options),
192
- });
193
- }
194
-
195
- /**
196
- * Infinite query for infinite scroll / load more
197
- * @example
198
- * const { data, fetchNextPage, hasNextPage, isFetchingNextPage } =
199
- * useRepositoryInfiniteQuery('posts', 20, {
200
- * where: { published: 1 },
201
- * orderBy: [{ field: 'created_at', direction: 'DESC' }]
202
- * });
203
- *
204
- * // data.pages = [page1Data, page2Data, page3Data, ...]
205
- * const allPosts = data?.pages.flatMap(page => page.data) ?? [];
206
- */
207
- export function useRepositoryInfiniteQuery<T = any>(
208
- table: string,
209
- pageSize: number = 20,
210
- options: QueryOptions = {},
211
- queryOptions?: Omit<
212
- UseInfiniteQueryOptions<
213
- { data: T[]; page: number; totalPages: number; hasMore: boolean },
214
- Error
215
- >,
216
- "queryKey" | "queryFn" | "getNextPageParam" | "initialPageParam"
217
- >,
218
- ) {
219
- const manager = getManager();
220
-
221
- return useInfiniteQuery({
222
- queryKey: queryKeys.infinite(table, pageSize, options),
223
- queryFn: ({ pageParam }) =>
224
- manager.paginate<T>(table, pageParam as number, pageSize, options),
225
- initialPageParam: 1,
226
- getNextPageParam: (lastPage) => {
227
- if (!lastPage.hasMore) return undefined;
228
- return lastPage.page + 1;
229
- },
230
- ...queryOptions,
231
- });
232
- }
233
-
234
- // ==========================================
235
- // MUTATION HOOKS (Auto-invalidation via React Query)
236
- // ==========================================
237
-
238
- /**
239
- * Create mutation - React Query handles cache invalidation
240
- * @example
241
- * const { mutate: createPost } = useRepositoryCreate('posts', {
242
- * onSuccess: () => toast.success('Created!')
243
- * });
244
- */
245
- export function useRepositoryCreate<T = any>(
246
- table: string,
247
- options: Omit<UseMutationOptions<T, Error, Partial<T>>, "mutationFn"> & {
248
- invalidate?: string[];
249
- } = {},
250
- ) {
251
- const manager = getManager();
252
- const queryClient = useQueryClient();
253
- const { invalidate = [table], ...mutationOptions } = options;
254
-
255
- return useMutation<T, Error, Partial<T>>({
256
- mutationFn: (data) => manager.create<T>(table, data),
257
- onSuccess: () => {
258
- // React Query automatically invalidates and refetches
259
- invalidate.forEach((t) => {
260
- queryClient.invalidateQueries({ queryKey: queryKeys.all(t) });
261
- });
262
- },
263
- ...mutationOptions,
264
- });
265
- }
266
-
267
- /**
268
- * Update mutation - Optimistic update via React Query
269
- * @example
270
- * const { mutate: updatePost } = useRepositoryUpdate('posts', {
271
- * onSuccess: () => toast.success('Updated!')
272
- * });
273
- * updatePost({ id: 1, data: { title: 'New Title' } });
274
- */
275
- export function useRepositoryUpdate<T = any>(
276
- table: string,
277
- options: Omit<
278
- UseMutationOptions<T, Error, { id: number | string; data: Partial<T> }>,
279
- "mutationFn"
280
- > = {},
281
- ) {
282
- const manager = getManager();
283
- const queryClient = useQueryClient();
284
-
285
- return useMutation<T, Error, { id: number | string; data: Partial<T> }>({
286
- mutationFn: ({ id, data }) => manager.update<T>(table, id, data),
287
- onSuccess: (data, variables) => {
288
- // Invalidate list queries
289
- queryClient.invalidateQueries({ queryKey: queryKeys.all(table) });
290
-
291
- // Update detail cache optimistically
292
- queryClient.setQueryData(queryKeys.detail(table, variables.id), data);
293
- },
294
- ...options,
295
- });
296
- }
297
-
298
- /**
299
- * Delete mutation
300
- * @example
301
- * const { mutate: deletePost } = useRepositoryDelete('posts', {
302
- * onSuccess: () => toast.success('Deleted!')
303
- * });
304
- * deletePost(postId);
305
- */
306
- export function useRepositoryDelete(
307
- table: string,
308
- options: Omit<
309
- UseMutationOptions<boolean, Error, number | string>,
310
- "mutationFn"
311
- > = {},
312
- ) {
313
- const manager = getManager();
314
- const queryClient = useQueryClient();
315
-
316
- return useMutation<boolean, Error, number | string>({
317
- mutationFn: (id) => manager.delete(table, id),
318
- onSuccess: (_data, id) => {
319
- // Invalidate and remove from cache
320
- queryClient.invalidateQueries({ queryKey: queryKeys.all(table) });
321
- queryClient.removeQueries({ queryKey: queryKeys.detail(table, id) });
322
- },
323
- ...options,
324
- });
325
- }
326
-
327
- // ==========================================
328
- // RAW SQL QUERY HOOKS
329
- // ==========================================
330
-
331
- /**
332
- * Raw SQL query hook - for complex queries that can't be expressed with QueryOptions
333
- * @example
334
- * // Complex JOIN query
335
- * const { data: posts } = useRawQuery<Post>(
336
- * ['posts-with-categories', categorySlug],
337
- * `SELECT DISTINCT p.*, c.name as category_name
338
- * FROM posts p
339
- * JOIN post_categories pc ON p.id = pc.post_id
340
- * JOIN blog_categories c ON pc.category_id = c.id
341
- * WHERE c.slug = ? AND p.published = 1
342
- * ORDER BY p.published_at DESC`,
343
- * [categorySlug]
344
- * );
345
- *
346
- * // Aggregation query
347
- * const { data: stats } = useRawQuery<{total: number, avg: number}>(
348
- * ['product-stats'],
349
- * `SELECT COUNT(*) as total, AVG(price) as avg FROM products WHERE published = 1`
350
- * );
351
- */
352
- export function useRawQuery<T = any>(
353
- queryKey: any[],
354
- sql: string,
355
- params?: any[],
356
- options?: Omit<UseQueryOptions<T[], Error>, "queryKey" | "queryFn">,
357
- ) {
358
- const manager = getManager();
359
-
360
- return useQuery<T[], Error>({
361
- queryKey: ["raw", ...queryKey],
362
- queryFn: () => manager.raw<T>(sql, params),
363
- ...options,
364
- });
365
- }
366
-
367
- /**
368
- * Raw SQL query hook for single result - aggregations, single lookups
369
- * @example
370
- * // Get price range
371
- * const { data: priceRange } = useRawQueryOne<{min: number, max: number}>(
372
- * ['price-range'],
373
- * `SELECT MIN(price) as min, MAX(price) as max FROM products WHERE published = 1`
374
- * );
375
- *
376
- * // Get single post with category
377
- * const { data: post } = useRawQueryOne<Post>(
378
- * ['post-detail', slug],
379
- * `SELECT p.*, c.name as category_name
380
- * FROM posts p
381
- * LEFT JOIN post_categories pc ON p.id = pc.post_id
382
- * LEFT JOIN blog_categories c ON pc.category_id = c.id
383
- * WHERE p.slug = ?`,
384
- * [slug]
385
- * );
386
- */
387
- export function useRawQueryOne<T = any>(
388
- queryKey: any[],
389
- sql: string,
390
- params?: any[],
391
- options?: Omit<UseQueryOptions<T | null, Error>, "queryKey" | "queryFn">,
392
- ) {
393
- const manager = getManager();
394
-
395
- return useQuery<T | null, Error>({
396
- queryKey: ["raw", ...queryKey],
397
- queryFn: () => manager.rawOne<T>(sql, params),
398
- ...options,
399
- });
400
- }
@@ -1,96 +0,0 @@
1
- /**
2
- * Database field parsers - Client-side utilities
3
- * NO automatic parsing - client decides what to parse and when
4
- */
5
-
6
- /**
7
- * Parse comma-separated string to array
8
- * @example "tag1,tag2,tag3" -> ["tag1", "tag2", "tag3"]
9
- */
10
- export const parseCommaSeparatedString = (value: string): string[] => {
11
- if (!value || typeof value !== "string") return [];
12
- return value
13
- .split(",")
14
- .map((item) => item.trim())
15
- .filter(Boolean);
16
- };
17
-
18
- /**
19
- * Parse JSON string to array
20
- * @example '["img1.jpg","img2.jpg"]' -> ["img1.jpg", "img2.jpg"]
21
- */
22
- export const parseJSONStringToArray = (value: string): string[] => {
23
- if (!value || typeof value !== "string") return [];
24
- try {
25
- const parsed = JSON.parse(value);
26
- return Array.isArray(parsed) ? parsed : [];
27
- } catch (e) {
28
- console.warn("Failed to parse JSON array:", value);
29
- return [];
30
- }
31
- };
32
-
33
- /**
34
- * Smart array parser - tries JSON first, falls back to comma-separated
35
- * @example '["a","b"]' -> ["a", "b"] OR "a,b" -> ["a", "b"]
36
- */
37
- export const parseStringToArray = (value: any): string[] => {
38
- if (!value) return [];
39
- if (Array.isArray(value)) return value;
40
- if (typeof value !== "string") return [];
41
-
42
- // Try JSON first
43
- if (value.trim().startsWith("[")) {
44
- const jsonResult = parseJSONStringToArray(value);
45
- if (jsonResult.length > 0) return jsonResult;
46
- }
47
-
48
- // Fall back to comma-separated
49
- return parseCommaSeparatedString(value);
50
- };
51
-
52
- /**
53
- * Parse JSON string to object
54
- * @example '{"key":"value"}' -> {key: "value"}
55
- */
56
- export const parseJSONString = <T = any>(
57
- value: any,
58
- defaultValue: T | null = null,
59
- ): T | null => {
60
- if (!value) return defaultValue;
61
- if (typeof value === "object") return value; // Already parsed
62
- if (typeof value !== "string") return defaultValue;
63
-
64
- try {
65
- return JSON.parse(value);
66
- } catch (e) {
67
- console.warn("Failed to parse JSON:", value);
68
- return defaultValue;
69
- }
70
- };
71
-
72
- /**
73
- * Parse SQLite boolean (0/1) to JavaScript boolean
74
- * @example 1 -> true, 0 -> false
75
- */
76
- export const parseSQLiteBoolean = (value: any): boolean => {
77
- if (typeof value === "boolean") return value;
78
- if (typeof value === "number") return value !== 0;
79
- if (typeof value === "string") {
80
- const lower = value.toLowerCase();
81
- return lower === "true" || lower === "1" || lower === "yes";
82
- }
83
- return Boolean(value);
84
- };
85
-
86
- /**
87
- * Parse number safely with default fallback
88
- * @example "123" -> 123, "invalid" -> 0 (or provided default)
89
- */
90
- export const parseNumberSafe = (
91
- value: any,
92
- defaultValue: number = 0,
93
- ): number => {
94
- const num = Number(value);
95
- return isNaN(num) ? defaultValue : num;
96
- };