gufi-cli 0.1.49 → 0.1.51

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/commands/docs.js +1 -5
  2. package/dist/lib/docs-resolver.d.ts +8 -0
  3. package/dist/lib/docs-resolver.js +27 -0
  4. package/dist/lib/security.js +5 -0
  5. package/dist/mcp.js +56 -43
  6. package/docs/dev-guide/1-01-architecture.md +358 -0
  7. package/docs/dev-guide/1-02-multi-tenant.md +415 -0
  8. package/docs/dev-guide/1-03-column-types.md +594 -0
  9. package/docs/dev-guide/1-04-json-config.md +442 -0
  10. package/docs/dev-guide/1-05-authentication.md +427 -0
  11. package/docs/dev-guide/2-01-api-reference.md +564 -0
  12. package/docs/dev-guide/2-02-automations.md +508 -0
  13. package/docs/dev-guide/2-03-gufi-cli.md +568 -0
  14. package/docs/dev-guide/2-04-realtime.md +401 -0
  15. package/docs/dev-guide/2-05-permissions.md +497 -0
  16. package/docs/dev-guide/2-06-integrations-overview.md +104 -0
  17. package/docs/dev-guide/2-07-stripe.md +173 -0
  18. package/docs/dev-guide/2-08-nayax.md +297 -0
  19. package/docs/dev-guide/2-09-ourvend.md +226 -0
  20. package/docs/dev-guide/2-10-tns.md +177 -0
  21. package/docs/dev-guide/2-11-custom-http.md +268 -0
  22. package/docs/dev-guide/3-01-custom-views.md +555 -0
  23. package/docs/dev-guide/3-02-webhooks-api.md +446 -0
  24. package/docs/mcp/00-overview.md +329 -0
  25. package/docs/mcp/01-architecture.md +226 -0
  26. package/docs/mcp/02-modules.md +285 -0
  27. package/docs/mcp/03-fields.md +357 -0
  28. package/docs/mcp/04-views.md +613 -0
  29. package/docs/mcp/05-automations.md +461 -0
  30. package/docs/mcp/06-api.md +531 -0
  31. package/docs/mcp/07-packages.md +246 -0
  32. package/docs/mcp/08-common-errors.md +284 -0
  33. package/docs/mcp/09-examples.md +453 -0
  34. package/docs/mcp/README.md +71 -0
  35. package/docs/mcp/tool-descriptions.json +64 -0
  36. package/package.json +3 -2
@@ -0,0 +1,555 @@
1
+ ---
2
+ id: custom-views
3
+ title: "Custom Views Development"
4
+ description: "Build custom views for the Marketplace"
5
+ icon: Store
6
+ category: dev
7
+ part: 3
8
+ ---
9
+
10
+ # Custom Views Development
11
+
12
+ Build custom views for the Marketplace
13
+
14
+ ## Overview
15
+
16
+ Custom views are React components that extend Gufi's functionality. They can:
17
+
18
+ - Display data in unique ways (dashboards, charts, maps)
19
+ - Provide specialized workflows
20
+ - Integrate with external services
21
+ - Offer industry-specific features
22
+
23
+ ## View Structure
24
+
25
+ ### Standard Directory Layout
26
+
27
+ ```
28
+ my_view/
29
+ ├── index.tsx # Main export + featureConfig
30
+ ├── types.ts # TypeScript types
31
+ ├── core/
32
+ │ ├── dataSources.ts # Data sources (REQUIRED)
33
+ │ ├── automations.ts # Automations declaration (if using automations)
34
+ │ └── index.tsx # featureConfig
35
+ ├── metadata/
36
+ │ ├── seedData.ts # Demo data for preview
37
+ │ ├── inputs.ts # User-configurable fields
38
+ │ └── migrations.ts # SQL migrations
39
+ ├── components/ # UI components
40
+ │ ├── Header.tsx
41
+ │ ├── Dashboard.tsx
42
+ │ └── ...
43
+ └── plugin/ # Optional: ViewPlugin for native
44
+ └── MyViewPlugin.tsx
45
+ ```
46
+
47
+ ### Main Entry (index.tsx)
48
+
49
+ ```typescript
50
+ import React from 'react';
51
+ import type { FullGufiContext } from '@/sdk';
52
+ import { featureConfig } from './core/dataProvider';
53
+ import { MyDashboard } from './components/Dashboard';
54
+
55
+ interface Props {
56
+ id: string;
57
+ gufi: FullGufiContext;
58
+ }
59
+
60
+ export default function MyView({ id, gufi }: Props) {
61
+ return <MyDashboard id={id} gufi={gufi} />;
62
+ }
63
+
64
+ export { featureConfig };
65
+ ```
66
+
67
+ ## Feature Configuration
68
+
69
+ ### Basic Config
70
+
71
+ ```typescript
72
+ // core/dataProvider.ts
73
+ import type { FeatureConfig } from '@/sdk';
74
+
75
+ export const featureConfig: FeatureConfig = {
76
+ id: 'my-analytics-view',
77
+ name: 'My Analytics',
78
+ description: 'Analytics dashboard for sales data',
79
+ version: '1.0.0',
80
+ author: 'Your Name',
81
+ icon: 'BarChart3',
82
+
83
+ // Data requirements
84
+ dataSources: [
85
+ {
86
+ key: 'ordersTable',
87
+ name: 'Orders',
88
+ description: 'Table containing orders',
89
+ required: true
90
+ },
91
+ {
92
+ key: 'productsTable',
93
+ name: 'Products',
94
+ description: 'Products catalog',
95
+ required: false
96
+ }
97
+ ],
98
+
99
+ // User inputs (shown in settings)
100
+ inputs: [
101
+ {
102
+ key: 'defaultPeriod',
103
+ type: 'select',
104
+ label: 'Default Period',
105
+ options: ['week', 'month', 'quarter', 'year'],
106
+ default: 'month'
107
+ }
108
+ ]
109
+ };
110
+ ```
111
+
112
+ ### Data Sources
113
+
114
+ Define what tables your view needs:
115
+
116
+ ```typescript
117
+ dataSources: [
118
+ {
119
+ key: 'ordersTable', // Reference key
120
+ name: 'Orders Table', // Display name
121
+ description: 'Sales orders', // Help text
122
+ required: true, // Must be mapped
123
+ columns: [ // Optional: required columns
124
+ { name: 'total', type: 'currency' },
125
+ { name: 'status', type: 'select' }
126
+ ]
127
+ }
128
+ ]
129
+ ```
130
+
131
+ ### User Inputs
132
+
133
+ Configuration options shown to users:
134
+
135
+ ```typescript
136
+ inputs: [
137
+ {
138
+ key: 'theme',
139
+ type: 'select',
140
+ label: 'Color Theme',
141
+ options: ['purple', 'blue', 'green'],
142
+ default: 'purple'
143
+ },
144
+ {
145
+ key: 'showTrends',
146
+ type: 'checkbox',
147
+ label: 'Show Trend Lines',
148
+ default: true
149
+ },
150
+ {
151
+ key: 'apiEndpoint',
152
+ type: 'text',
153
+ label: 'External API URL',
154
+ placeholder: 'https://api.example.com'
155
+ }
156
+ ]
157
+ ```
158
+
159
+ ## The gufi Context
160
+
161
+ ### What's Available
162
+
163
+ Every view receives the `gufi` context:
164
+
165
+ ```typescript
166
+ interface FullGufiContext {
167
+ // Data Provider
168
+ dataProvider: {
169
+ getList: (params) => Promise<{ data, total }>;
170
+ getOne: (params) => Promise<{ data }>;
171
+ create: (params) => Promise<{ data }>;
172
+ update: (params) => Promise<{ data }>;
173
+ delete: (params) => Promise<void>;
174
+ };
175
+
176
+ // Automations (con acceso a api.stripe.*, api.nayax.*, etc.)
177
+ automation: (name: string, input?: object) => Promise<any>;
178
+
179
+ // WebSocket
180
+ websocket: {
181
+ subscribeToTable: (tableId, callback) => () => void;
182
+ broadcastToView: (event, data) => void;
183
+ isConnected: () => boolean;
184
+ };
185
+
186
+ // Context Info
187
+ context: {
188
+ schema: CompanySchema;
189
+ user: { id, name, email };
190
+ activeCompany: { id, name };
191
+ lang: 'es' | 'en';
192
+ viewSpec: Record<string, string>; // Table mappings
193
+ seedData: SeedDataConfig;
194
+ isPreview: boolean;
195
+ isDev: boolean;
196
+ };
197
+
198
+ // Utilities
199
+ utils: {
200
+ // Translations
201
+ tUI: (key) => string;
202
+ getLang: () => 'es' | 'en';
203
+
204
+ // Notifications
205
+ toastSuccess: (msg) => void;
206
+ toastError: (msg) => void;
207
+ toastInfo: (msg) => void;
208
+
209
+ // Formatting
210
+ formatNumber: (n) => string;
211
+ formatCurrency: (n) => string;
212
+ formatDate: (d) => string;
213
+ formatDateTime: (d) => string;
214
+
215
+ // Data helpers
216
+ groupBy: (arr, key) => Record;
217
+ sumBy: (arr, key) => number;
218
+ sortBy: (arr, key, order) => array;
219
+
220
+ // Other
221
+ resolveTableName: (schema, ref) => string;
222
+ navigate: (path) => void;
223
+ cn: (...classes) => string;
224
+ };
225
+
226
+ // Device Info
227
+ device: {
228
+ isHandheld: boolean;
229
+ isDesktop: boolean;
230
+ isMobile: boolean;
231
+ };
232
+ }
233
+ ```
234
+
235
+ ### Using the Context
236
+
237
+ ```typescript
238
+ export default function MyView({ id, gufi }: Props) {
239
+ const { dataProvider, utils, context, device } = gufi;
240
+ const { tUI, toastSuccess, formatCurrency } = utils;
241
+
242
+ // Get table ID from viewSpec mapping
243
+ const ordersTable = context.viewSpec?.ordersTable;
244
+
245
+ // Load data
246
+ const { data: orders } = useViewData({
247
+ gufi,
248
+ tableKey: 'ordersTable'
249
+ });
250
+
251
+ // Responsive layout
252
+ if (device.isHandheld) {
253
+ return <MobileLayout orders={orders} />;
254
+ }
255
+
256
+ return <DesktopLayout orders={orders} />;
257
+ }
258
+ ```
259
+
260
+ ## Seed Data (Preview Mode)
261
+
262
+ ### Defining Demo Data
263
+
264
+ ```typescript
265
+ // metadata/seedData.ts
266
+ import type { SeedDataConfig } from '@/sdk';
267
+
268
+ export const seedData: SeedDataConfig = {
269
+ ordersTable: [
270
+ {
271
+ id: '@ref:ordersTable.0',
272
+ customer_name: 'Acme Corp',
273
+ total: { currency: 'EUR', amount: 1500 },
274
+ status: 'completed',
275
+ created_at: '@today'
276
+ },
277
+ {
278
+ id: '@ref:ordersTable.1',
279
+ customer_name: 'Beta Inc',
280
+ total: { currency: 'EUR', amount: 800 },
281
+ status: 'pending',
282
+ created_at: '@today'
283
+ }
284
+ ],
285
+ productsTable: [
286
+ {
287
+ id: '@ref:productsTable.0',
288
+ name: 'Widget Pro',
289
+ price: { currency: 'EUR', amount: 99.99 },
290
+ stock: 150
291
+ }
292
+ ]
293
+ };
294
+ ```
295
+
296
+ ### Special Tokens
297
+
298
+ | Token | Value |
299
+ |---|---|
300
+ | `@ref:tableName.index` | Auto-generated ID |
301
+ | `@today` | Current date |
302
+ | `@currentUser` | Current user ID |
303
+ | `@now` | Current timestamp |
304
+
305
+ ### Using Seed Data
306
+
307
+ The SDK automatically uses seed data in preview mode:
308
+
309
+ ```typescript
310
+ const { data, loading, usingSeedData } = useViewData({
311
+ gufi,
312
+ tableKey: 'ordersTable'
313
+ });
314
+
315
+ // usingSeedData is true when in preview
316
+ if (usingSeedData) {
317
+ console.log('Showing demo data');
318
+ }
319
+ ```
320
+
321
+ ## Automations
322
+
323
+ ### Declaring Automations
324
+
325
+ If your view needs to call automations (for Stripe payments, email sending, etc.), you must declare them in `core/automations.ts`:
326
+
327
+ ```typescript
328
+ // core/automations.ts
329
+ export const automations = [
330
+ { name: 'stripe_checkout', description: 'Create Stripe payment session' },
331
+ { name: 'send_confirmation', description: 'Send confirmation email' },
332
+ ] as const;
333
+ ```
334
+
335
+ **Important:**
336
+ - Automations must be declared BEFORE they can be called from the view
337
+ - On `gufi view:push`, declared automations are registered in `__automation_meta__` with `trigger_event='CUSTOM'`
338
+ - Only declared automations can be executed via `gufi.automation()`
339
+
340
+ ### Calling Automations
341
+
342
+ ```typescript
343
+ const handlePayment = async () => {
344
+ try {
345
+ // Only works if 'stripe_checkout' is declared in core/automations.ts
346
+ const result = await gufi.automation('stripe_checkout', {
347
+ amount: 1999,
348
+ customer_email: 'user@example.com',
349
+ });
350
+
351
+ if (result.url) {
352
+ window.location.href = result.url;
353
+ }
354
+ } catch (err) {
355
+ gufi.utils.toastError('Payment failed');
356
+ }
357
+ };
358
+ ```
359
+
360
+ ### Workflow
361
+
362
+ 1. **Create script:** `gufi automation:create stripe_checkout script.js -c 116`
363
+ 2. **Declare in view:** Add to `core/automations.ts`
364
+ 3. **Push view:** `gufi view:push` → Registers in `__automation_meta__`
365
+ 4. **Use in view:** `gufi.automation('stripe_checkout', input)`
366
+
367
+ ## Migrations
368
+
369
+ ### Version Updates
370
+
371
+ When your view schema changes:
372
+
373
+ ```typescript
374
+ // metadata/migrations.ts
375
+ export const migrations = [
376
+ {
377
+ version: '1.1.0',
378
+ description: 'Add status column',
379
+ up: `
380
+ ALTER TABLE {tableName}
381
+ ADD COLUMN IF NOT EXISTS status VARCHAR(50) DEFAULT 'active';
382
+ `,
383
+ down: `
384
+ ALTER TABLE {tableName}
385
+ DROP COLUMN IF EXISTS status;
386
+ `
387
+ },
388
+ {
389
+ version: '1.2.0',
390
+ description: 'Add priority field',
391
+ up: `
392
+ ALTER TABLE {tableName}
393
+ ADD COLUMN IF NOT EXISTS priority INTEGER DEFAULT 0;
394
+ `,
395
+ down: `
396
+ ALTER TABLE {tableName}
397
+ DROP COLUMN IF EXISTS priority;
398
+ `
399
+ }
400
+ ];
401
+ ```
402
+
403
+ ### Placeholders
404
+
405
+ | Placeholder | Value |
406
+ |---|---|
407
+ | `{tableName}` | Physical table name |
408
+ | `{entityId}` | Entity ID |
409
+ | `{companyId}` | Company ID |
410
+ | `{schemaName}` | Company schema |
411
+
412
+ ## Best Practices
413
+
414
+ ### Data Loading
415
+
416
+ ```typescript
417
+ // Good: Use SDK hooks
418
+ const { data, loading, error, refetch } = useViewData({
419
+ gufi,
420
+ tableKey: 'ordersTable',
421
+ filters: { status: 'active' }
422
+ });
423
+
424
+ // Bad: Direct store access
425
+ import { useCompanySchemaStore } from '@/stores'; // Don't do this
426
+ ```
427
+
428
+ ### Error Handling
429
+
430
+ ```typescript
431
+ if (error) {
432
+ return (
433
+ <div className="p-4 text-red-600">
434
+ <AlertCircle className="w-5 h-5" />
435
+ <span>{utils.tUI('common.error')}: {error.message}</span>
436
+ </div>
437
+ );
438
+ }
439
+ ```
440
+
441
+ ### Loading States
442
+
443
+ ```typescript
444
+ if (loading) {
445
+ return (
446
+ <div className="animate-pulse">
447
+ <div className="h-8 bg-zinc-200 rounded" />
448
+ <div className="h-32 bg-zinc-100 rounded mt-4" />
449
+ </div>
450
+ );
451
+ }
452
+ ```
453
+
454
+ ### Responsive Design
455
+
456
+ ```typescript
457
+ const { device } = gufi;
458
+
459
+ return (
460
+ <div className={device.isHandheld ? 'p-2' : 'p-6'}>
461
+ {device.isDesktop && <Sidebar />}
462
+ <MainContent />
463
+ </div>
464
+ );
465
+ ```
466
+
467
+ ## Development Workflow
468
+
469
+ ### Local Development
470
+
471
+ ```bash
472
+ # 1. Pull view for editing
473
+ gufi pull 55
474
+ cd view_55/
475
+
476
+ # 2. Start watch mode
477
+ gufi watch
478
+
479
+ # 3. Make changes - auto syncs
480
+
481
+ # 4. View logs
482
+ gufi logs
483
+
484
+ # 5. Test in LivePreview
485
+ # Open view in Gufi web interface
486
+ ```
487
+
488
+ ### Publishing
489
+
490
+ ```bash
491
+ # 1. Add to package
492
+ gufi package:add-view 14 55
493
+
494
+ # 2. Check changes
495
+ gufi package:check 14
496
+
497
+ # 3. Sync version
498
+ gufi package:sync 14
499
+
500
+ # 4. Publish
501
+ gufi package:publish 14
502
+ ```
503
+
504
+ ## Common Patterns
505
+
506
+ ### Dashboard with KPIs
507
+
508
+ ```typescript
509
+ import { ViewLayout, ViewKPIGrid, ViewKPI, ViewCard } from '@/shared/views';
510
+
511
+ export function Dashboard({ gufi }: Props) {
512
+ const { data } = useViewData({ gufi, tableKey: 'ordersTable' });
513
+
514
+ const totalRevenue = gufi.utils.sumBy(data, 'total.amount');
515
+ const orderCount = data.length;
516
+
517
+ return (
518
+ <ViewLayout>
519
+ <ViewKPIGrid>
520
+ <ViewKPI
521
+ title="Total Revenue"
522
+ value={gufi.utils.formatCurrency(totalRevenue)}
523
+ trend="+12%"
524
+ trendUp
525
+ />
526
+ <ViewKPI
527
+ title="Orders"
528
+ value={orderCount}
529
+ />
530
+ </ViewKPIGrid>
531
+
532
+ <ViewCard title="Recent Orders">
533
+ <OrdersTable orders={data} />
534
+ </ViewCard>
535
+ </ViewLayout>
536
+ );
537
+ }
538
+ ```
539
+
540
+ ### Real-Time Updates
541
+
542
+ ```typescript
543
+ useEffect(() => {
544
+ const tableId = context.viewSpec?.ordersTable;
545
+ if (!tableId) return;
546
+
547
+ const unsubscribe = gufi.websocket.subscribeToTable(tableId, (event) => {
548
+ if (event.type === 'INSERT' || event.type === 'UPDATE') {
549
+ refetch();
550
+ }
551
+ });
552
+
553
+ return () => unsubscribe();
554
+ }, [context.viewSpec?.ordersTable]);
555
+ ```