@donotdev/cli 0.0.6 → 0.0.8

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 (87) hide show
  1. package/README.md +3 -18
  2. package/dependencies-matrix.json +64 -121
  3. package/dist/bin/commands/build.js +173 -161
  4. package/dist/bin/commands/bump.js +181 -156
  5. package/dist/bin/commands/cacheout.js +188 -171
  6. package/dist/bin/commands/create-app.js +213 -156
  7. package/dist/bin/commands/create-project.js +183 -154
  8. package/dist/bin/commands/deploy.js +491 -477
  9. package/dist/bin/commands/dev.js +176 -160
  10. package/dist/bin/commands/emu.js +181 -165
  11. package/dist/bin/commands/format.js +191 -174
  12. package/dist/bin/commands/lint.js +191 -171
  13. package/dist/bin/commands/preview.js +177 -161
  14. package/dist/bin/commands/sync-secrets.js +172 -158
  15. package/dist/bin/commands/wai.d.ts +11 -0
  16. package/dist/bin/commands/wai.d.ts.map +1 -0
  17. package/dist/bin/commands/wai.js +12 -0
  18. package/dist/bin/commands/wai.js.map +1 -0
  19. package/dist/bin/dndev.js +24 -24
  20. package/dist/bin/donotdev.js +24 -24
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +661 -669
  24. package/dist/index.js.map +1 -1
  25. package/package.json +1 -1
  26. package/templates/app-demo/src/config/app.ts.example +12 -0
  27. package/templates/app-next/src/config/app.ts.example +75 -48
  28. package/templates/app-vite/index.html.example +71 -37
  29. package/templates/app-vite/src/config/app.ts.example +75 -47
  30. package/templates/app-vite/src/pages/FormPageExample.tsx.example +152 -0
  31. package/templates/app-vite/src/pages/HomePage.tsx.example +81 -134
  32. package/templates/app-vite/src/pages/ListPageExample.tsx.example +88 -0
  33. package/templates/functions-firebase/build.mjs.example +8 -1
  34. package/templates/functions-firebase/functions-firebase/build.mjs.example +8 -1
  35. package/templates/functions-firebase/functions-firebase/src/index.ts.example +19 -25
  36. package/templates/functions-firebase/functions.config.js.example +35 -0
  37. package/templates/root-consumer/entities/ExampleEntity.ts.example +223 -0
  38. package/templates/root-consumer/entities/demo.ts.example +576 -0
  39. package/templates/root-consumer/entities/index.ts.example +15 -0
  40. package/templates/root-consumer/eslint.config.js.example +2 -80
  41. package/templates/root-consumer/guides/{AGENT_START_HERE.md.example → dndev/AGENT_START_HERE.md.example} +22 -0
  42. package/templates/root-consumer/guides/dndev/COMPONENTS_CRUD.md.example +231 -0
  43. package/templates/root-consumer/guides/{SETUP_AUTH.md.example → dndev/SETUP_AUTH.md.example} +30 -0
  44. package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +539 -0
  45. package/templates/root-consumer/guides/dndev/SETUP_FUNCTIONS.md.example +116 -0
  46. package/templates/root-consumer/guides/{SETUP_I18N.md.example → dndev/SETUP_I18N.md.example} +46 -0
  47. package/templates/root-consumer/guides/wai-way/WAI_WAY_CLI.md.example +404 -0
  48. package/templates/root-consumer/guides/wai-way/agents/architect.md.example +78 -0
  49. package/templates/root-consumer/guides/wai-way/agents/builder.md.example +87 -0
  50. package/templates/root-consumer/guides/wai-way/agents/extractor.md.example +325 -0
  51. package/templates/root-consumer/guides/wai-way/agents/polisher.md.example +100 -0
  52. package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +281 -0
  53. package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +77 -0
  54. package/templates/root-consumer/guides/wai-way/blueprints/2_entities.md.example +104 -0
  55. package/templates/root-consumer/guides/wai-way/blueprints/3_compose.md.example +124 -0
  56. package/templates/root-consumer/guides/wai-way/blueprints/4_configure.md.example +165 -0
  57. package/templates/root-consumer/guides/wai-way/context_map.json.example +95 -0
  58. package/templates/root-consumer/guides/wai-way/entity_patterns.md.example +840 -0
  59. package/templates/root-consumer/guides/wai-way/page_patterns.md.example +686 -0
  60. package/templates/root-consumer/guides/wai-way/presets_guide.md.example +217 -0
  61. package/templates/root-consumer/guides/wai-way/spec_template.md.example +312 -0
  62. package/templates/functions-firebase/functions-firebase/src/crud/createEntity.ts.example +0 -19
  63. package/templates/functions-firebase/functions-firebase/src/crud/deleteEntity.ts.example +0 -14
  64. package/templates/functions-firebase/functions-firebase/src/crud/getEntity.ts.example +0 -14
  65. package/templates/functions-firebase/functions-firebase/src/crud/index.ts.example +0 -12
  66. package/templates/functions-firebase/functions-firebase/src/crud/listEntities.ts.example +0 -14
  67. package/templates/functions-firebase/functions-firebase/src/crud/updateEntity.ts.example +0 -14
  68. package/templates/root-consumer/guides/COMPONENTS_CRUD.md.example +0 -70
  69. package/templates/root-consumer/guides/SETUP_CRUD.md.example +0 -1244
  70. package/templates/root-consumer/guides/SETUP_FUNCTIONS.md.example +0 -114
  71. /package/templates/root-consumer/guides/{COMPONENTS_ADV.md.example → dndev/COMPONENTS_ADV.md.example} +0 -0
  72. /package/templates/root-consumer/guides/{COMPONENTS_ATOMIC.md.example → dndev/COMPONENTS_ATOMIC.md.example} +0 -0
  73. /package/templates/root-consumer/guides/{COMPONENTS_UI.md.example → dndev/COMPONENTS_UI.md.example} +0 -0
  74. /package/templates/root-consumer/guides/{ENV_SETUP.md.example → dndev/ENV_SETUP.md.example} +0 -0
  75. /package/templates/root-consumer/guides/{INDEX.md.example → dndev/INDEX.md.example} +0 -0
  76. /package/templates/root-consumer/guides/{SETUP_APP_CONFIG.md.example → dndev/SETUP_APP_CONFIG.md.example} +0 -0
  77. /package/templates/root-consumer/guides/{SETUP_BILLING.md.example → dndev/SETUP_BILLING.md.example} +0 -0
  78. /package/templates/root-consumer/guides/{SETUP_LAYOUTS.md.example → dndev/SETUP_LAYOUTS.md.example} +0 -0
  79. /package/templates/root-consumer/guides/{SETUP_OAUTH.md.example → dndev/SETUP_OAUTH.md.example} +0 -0
  80. /package/templates/root-consumer/guides/{SETUP_PAGES.md.example → dndev/SETUP_PAGES.md.example} +0 -0
  81. /package/templates/root-consumer/guides/{SETUP_PWA.md.example → dndev/SETUP_PWA.md.example} +0 -0
  82. /package/templates/root-consumer/guides/{SETUP_THEMES.md.example → dndev/SETUP_THEMES.md.example} +0 -0
  83. /package/templates/root-consumer/guides/{USE_ROUTING.md.example → dndev/USE_ROUTING.md.example} +0 -0
  84. /package/templates/root-consumer/guides/{advanced → dndev/advanced}/APP_CHECK.md.example +0 -0
  85. /package/templates/root-consumer/guides/{advanced → dndev/advanced}/COOKIE_REFERENCE.md.example +0 -0
  86. /package/templates/root-consumer/guides/{advanced → dndev/advanced}/EMULATORS.md.example +0 -0
  87. /package/templates/root-consumer/guides/{advanced → dndev/advanced}/VERSION_CONTROL.md.example +0 -0
@@ -0,0 +1,840 @@
1
+ # Entity Pattern Catalog
2
+
3
+ > **Don't invent schemas. Copy patterns, customize fields.**
4
+
5
+ ---
6
+
7
+ ## Pattern Index
8
+
9
+ | Pattern | Use For | Key Fields |
10
+ |---------|---------|------------|
11
+ | [UserProfile](#userprofile) | Extended user data | displayName, avatar, bio, preferences |
12
+ | [Post/Article](#postarticle) | Content with author | title, content, author, publishedAt |
13
+ | [Product](#product) | E-commerce items | name, price, images, category, stock |
14
+ | [Order](#order) | Purchases/transactions | userId, items, total, status |
15
+ | [Review](#review) | User feedback | entityId, userId, rating, comment |
16
+ | [Comment](#comment) | Threaded discussions | parentId, content, userId |
17
+ | [Category/Tag](#categorytag) | Classification | name, slug, parent |
18
+ | [Settings](#settings) | User preferences | userId, theme, notifications, locale |
19
+ | [Workspace/Team](#workspaceteam) | Multi-tenant | name, ownerId, members |
20
+ | [Invitation](#invitation) | Invite flows | email, role, expiresAt |
21
+ | [Media](#media) | File library | url, type, size, uploadedBy |
22
+ | [Log/Activity](#logactivity) | Audit trail | action, userId, entityId, timestamp |
23
+
24
+ ---
25
+
26
+ ## UserProfile
27
+
28
+ **Extends auth user with app-specific data.**
29
+
30
+ ```typescript
31
+ export const userProfileEntity = defineEntity({
32
+ name: 'UserProfile',
33
+ collection: 'userProfiles',
34
+ fields: {
35
+ // Link to auth user (same ID)
36
+ userId: {
37
+ name: 'userId',
38
+ label: 'userId',
39
+ type: 'text',
40
+ visibility: 'hidden',
41
+ },
42
+ displayName: {
43
+ name: 'displayName',
44
+ label: 'displayName',
45
+ type: 'text',
46
+ visibility: 'guest',
47
+ validation: { required: true, maxLength: 50 },
48
+ },
49
+ avatar: {
50
+ name: 'avatar',
51
+ label: 'avatar',
52
+ type: 'avatar',
53
+ visibility: 'guest',
54
+ },
55
+ bio: {
56
+ name: 'bio',
57
+ label: 'bio',
58
+ type: 'textarea',
59
+ visibility: 'guest',
60
+ validation: { maxLength: 500 },
61
+ },
62
+ // Private fields
63
+ email: {
64
+ name: 'email',
65
+ label: 'email',
66
+ type: 'email',
67
+ visibility: 'owner',
68
+ editable: 'never',
69
+ },
70
+ role: {
71
+ name: 'role',
72
+ label: 'role',
73
+ type: 'select',
74
+ visibility: 'admin',
75
+ editable: 'admin',
76
+ validation: {
77
+ options: [
78
+ { value: 'user', label: 'User' },
79
+ { value: 'admin', label: 'Admin' },
80
+ ],
81
+ },
82
+ },
83
+ },
84
+ access: {
85
+ create: 'user', // Created on signup
86
+ read: 'guest', // Public profiles
87
+ update: 'owner', // Users edit own profile
88
+ delete: 'admin',
89
+ },
90
+ });
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Post/Article
96
+
97
+ **Content with author, dates, status.**
98
+
99
+ ```typescript
100
+ export const postEntity = defineEntity({
101
+ name: 'Post',
102
+ collection: 'posts',
103
+ fields: {
104
+ title: {
105
+ name: 'title',
106
+ label: 'title',
107
+ type: 'text',
108
+ visibility: 'guest',
109
+ validation: { required: true, maxLength: 200 },
110
+ },
111
+ slug: {
112
+ name: 'slug',
113
+ label: 'slug',
114
+ type: 'text',
115
+ visibility: 'guest',
116
+ validation: { required: true, pattern: /^[a-z0-9-]+$/ },
117
+ },
118
+ excerpt: {
119
+ name: 'excerpt',
120
+ label: 'excerpt',
121
+ type: 'textarea',
122
+ visibility: 'guest',
123
+ validation: { maxLength: 300 },
124
+ },
125
+ content: {
126
+ name: 'content',
127
+ label: 'content',
128
+ type: 'richtext',
129
+ visibility: 'guest',
130
+ validation: { required: true },
131
+ },
132
+ featuredImage: {
133
+ name: 'featuredImage',
134
+ label: 'featuredImage',
135
+ type: 'image',
136
+ visibility: 'guest',
137
+ },
138
+ authorId: {
139
+ name: 'authorId',
140
+ label: 'authorId',
141
+ type: 'text',
142
+ visibility: 'guest',
143
+ editable: 'never',
144
+ },
145
+ category: {
146
+ name: 'category',
147
+ label: 'category',
148
+ type: 'select',
149
+ visibility: 'guest',
150
+ validation: {
151
+ options: [
152
+ { value: 'news', label: 'News' },
153
+ { value: 'tutorial', label: 'Tutorial' },
154
+ { value: 'announcement', label: 'Announcement' },
155
+ ],
156
+ },
157
+ },
158
+ tags: {
159
+ name: 'tags',
160
+ label: 'tags',
161
+ type: 'array',
162
+ visibility: 'guest',
163
+ },
164
+ publishedAt: {
165
+ name: 'publishedAt',
166
+ label: 'publishedAt',
167
+ type: 'timestamp',
168
+ visibility: 'guest',
169
+ },
170
+ },
171
+ access: {
172
+ create: 'admin',
173
+ read: 'guest',
174
+ update: 'admin',
175
+ delete: 'admin',
176
+ },
177
+ });
178
+ ```
179
+
180
+ ---
181
+
182
+ ## Product
183
+
184
+ **E-commerce item with pricing and inventory.**
185
+
186
+ ```typescript
187
+ export const productEntity = defineEntity({
188
+ name: 'Product',
189
+ collection: 'products',
190
+ fields: {
191
+ name: {
192
+ name: 'name',
193
+ label: 'name',
194
+ type: 'text',
195
+ visibility: 'guest',
196
+ validation: { required: true },
197
+ },
198
+ description: {
199
+ name: 'description',
200
+ label: 'description',
201
+ type: 'richtext',
202
+ visibility: 'guest',
203
+ },
204
+ price: {
205
+ name: 'price',
206
+ label: 'price',
207
+ type: 'number',
208
+ visibility: 'guest',
209
+ validation: { required: true, min: 0 },
210
+ },
211
+ compareAtPrice: {
212
+ name: 'compareAtPrice',
213
+ label: 'compareAtPrice',
214
+ type: 'number',
215
+ visibility: 'guest',
216
+ },
217
+ images: {
218
+ name: 'images',
219
+ label: 'images',
220
+ type: 'images',
221
+ visibility: 'guest',
222
+ },
223
+ category: {
224
+ name: 'category',
225
+ label: 'category',
226
+ type: 'combobox',
227
+ visibility: 'guest',
228
+ validation: { required: true },
229
+ },
230
+ sku: {
231
+ name: 'sku',
232
+ label: 'sku',
233
+ type: 'text',
234
+ visibility: 'admin',
235
+ },
236
+ stock: {
237
+ name: 'stock',
238
+ label: 'stock',
239
+ type: 'number',
240
+ visibility: 'admin',
241
+ validation: { min: 0 },
242
+ },
243
+ isActive: {
244
+ name: 'isActive',
245
+ label: 'isActive',
246
+ type: 'switch',
247
+ visibility: 'admin',
248
+ },
249
+ },
250
+ access: {
251
+ create: 'admin',
252
+ read: 'guest',
253
+ update: 'admin',
254
+ delete: 'admin',
255
+ },
256
+ });
257
+ ```
258
+
259
+ ---
260
+
261
+ ## Order
262
+
263
+ **Purchase transaction with line items.**
264
+
265
+ ```typescript
266
+ export const orderEntity = defineEntity({
267
+ name: 'Order',
268
+ collection: 'orders',
269
+ fields: {
270
+ userId: {
271
+ name: 'userId',
272
+ label: 'userId',
273
+ type: 'text',
274
+ visibility: 'admin',
275
+ editable: 'never',
276
+ },
277
+ items: {
278
+ name: 'items',
279
+ label: 'items',
280
+ type: 'text', // Schema override below
281
+ visibility: 'owner',
282
+ validation: {
283
+ schema: v.array(v.object({
284
+ productId: v.string(),
285
+ name: v.string(),
286
+ price: v.number(),
287
+ quantity: v.number(),
288
+ })),
289
+ },
290
+ },
291
+ subtotal: {
292
+ name: 'subtotal',
293
+ label: 'subtotal',
294
+ type: 'number',
295
+ visibility: 'owner',
296
+ editable: 'never',
297
+ },
298
+ tax: {
299
+ name: 'tax',
300
+ label: 'tax',
301
+ type: 'number',
302
+ visibility: 'owner',
303
+ editable: 'never',
304
+ },
305
+ total: {
306
+ name: 'total',
307
+ label: 'total',
308
+ type: 'number',
309
+ visibility: 'owner',
310
+ editable: 'never',
311
+ },
312
+ status: {
313
+ name: 'status',
314
+ label: 'status',
315
+ type: 'select',
316
+ visibility: 'owner',
317
+ editable: 'admin',
318
+ validation: {
319
+ options: [
320
+ { value: 'pending', label: 'Pending' },
321
+ { value: 'paid', label: 'Paid' },
322
+ { value: 'shipped', label: 'Shipped' },
323
+ { value: 'delivered', label: 'Delivered' },
324
+ { value: 'cancelled', label: 'Cancelled' },
325
+ ],
326
+ },
327
+ },
328
+ shippingAddress: {
329
+ name: 'shippingAddress',
330
+ label: 'shippingAddress',
331
+ type: 'address',
332
+ visibility: 'owner',
333
+ },
334
+ },
335
+ access: {
336
+ create: 'user',
337
+ read: 'owner', // Users see own orders
338
+ update: 'admin', // Only admin updates status
339
+ delete: 'super',
340
+ },
341
+ });
342
+ ```
343
+
344
+ ---
345
+
346
+ ## Review
347
+
348
+ **User feedback with rating.**
349
+
350
+ ```typescript
351
+ export const reviewEntity = defineEntity({
352
+ name: 'Review',
353
+ collection: 'reviews',
354
+ fields: {
355
+ productId: {
356
+ name: 'productId',
357
+ label: 'productId',
358
+ type: 'text',
359
+ visibility: 'guest',
360
+ editable: 'never',
361
+ },
362
+ userId: {
363
+ name: 'userId',
364
+ label: 'userId',
365
+ type: 'text',
366
+ visibility: 'guest',
367
+ editable: 'never',
368
+ },
369
+ rating: {
370
+ name: 'rating',
371
+ label: 'rating',
372
+ type: 'number',
373
+ visibility: 'guest',
374
+ validation: { required: true, min: 1, max: 5 },
375
+ },
376
+ title: {
377
+ name: 'title',
378
+ label: 'title',
379
+ type: 'text',
380
+ visibility: 'guest',
381
+ validation: { maxLength: 100 },
382
+ },
383
+ comment: {
384
+ name: 'comment',
385
+ label: 'comment',
386
+ type: 'textarea',
387
+ visibility: 'guest',
388
+ validation: { required: true, maxLength: 1000 },
389
+ },
390
+ isVerifiedPurchase: {
391
+ name: 'isVerifiedPurchase',
392
+ label: 'isVerifiedPurchase',
393
+ type: 'switch',
394
+ visibility: 'guest',
395
+ editable: 'never',
396
+ },
397
+ },
398
+ access: {
399
+ create: 'user',
400
+ read: 'guest',
401
+ update: 'owner',
402
+ delete: 'admin',
403
+ },
404
+ });
405
+ ```
406
+
407
+ ---
408
+
409
+ ## Comment
410
+
411
+ **Threaded comments with parent reference.**
412
+
413
+ ```typescript
414
+ export const commentEntity = defineEntity({
415
+ name: 'Comment',
416
+ collection: 'comments',
417
+ fields: {
418
+ postId: {
419
+ name: 'postId',
420
+ label: 'postId',
421
+ type: 'text',
422
+ visibility: 'guest',
423
+ editable: 'never',
424
+ },
425
+ parentId: {
426
+ name: 'parentId',
427
+ label: 'parentId',
428
+ type: 'text',
429
+ visibility: 'guest',
430
+ editable: 'never',
431
+ },
432
+ userId: {
433
+ name: 'userId',
434
+ label: 'userId',
435
+ type: 'text',
436
+ visibility: 'guest',
437
+ editable: 'never',
438
+ },
439
+ content: {
440
+ name: 'content',
441
+ label: 'content',
442
+ type: 'textarea',
443
+ visibility: 'guest',
444
+ validation: { required: true, maxLength: 2000 },
445
+ },
446
+ },
447
+ access: {
448
+ create: 'user',
449
+ read: 'guest',
450
+ update: 'owner',
451
+ delete: 'admin',
452
+ },
453
+ });
454
+ ```
455
+
456
+ ---
457
+
458
+ ## Category/Tag
459
+
460
+ **Hierarchical classification.**
461
+
462
+ ```typescript
463
+ export const categoryEntity = defineEntity({
464
+ name: 'Category',
465
+ collection: 'categories',
466
+ fields: {
467
+ name: {
468
+ name: 'name',
469
+ label: 'name',
470
+ type: 'text',
471
+ visibility: 'guest',
472
+ validation: { required: true },
473
+ },
474
+ slug: {
475
+ name: 'slug',
476
+ label: 'slug',
477
+ type: 'text',
478
+ visibility: 'guest',
479
+ validation: { required: true },
480
+ },
481
+ parentId: {
482
+ name: 'parentId',
483
+ label: 'parentId',
484
+ type: 'text',
485
+ visibility: 'guest',
486
+ },
487
+ order: {
488
+ name: 'order',
489
+ label: 'order',
490
+ type: 'number',
491
+ visibility: 'admin',
492
+ },
493
+ },
494
+ access: {
495
+ create: 'admin',
496
+ read: 'guest',
497
+ update: 'admin',
498
+ delete: 'admin',
499
+ },
500
+ });
501
+ ```
502
+
503
+ ---
504
+
505
+ ## Settings
506
+
507
+ **User preferences.**
508
+
509
+ ```typescript
510
+ export const settingsEntity = defineEntity({
511
+ name: 'Settings',
512
+ collection: 'settings',
513
+ fields: {
514
+ userId: {
515
+ name: 'userId',
516
+ label: 'userId',
517
+ type: 'text',
518
+ visibility: 'hidden',
519
+ editable: 'never',
520
+ },
521
+ theme: {
522
+ name: 'theme',
523
+ label: 'theme',
524
+ type: 'select',
525
+ visibility: 'owner',
526
+ validation: {
527
+ options: [
528
+ { value: 'light', label: 'Light' },
529
+ { value: 'dark', label: 'Dark' },
530
+ { value: 'system', label: 'System' },
531
+ ],
532
+ },
533
+ },
534
+ locale: {
535
+ name: 'locale',
536
+ label: 'locale',
537
+ type: 'select',
538
+ visibility: 'owner',
539
+ validation: {
540
+ options: [
541
+ { value: 'en', label: 'English' },
542
+ { value: 'fr', label: 'French' },
543
+ ],
544
+ },
545
+ },
546
+ emailNotifications: {
547
+ name: 'emailNotifications',
548
+ label: 'emailNotifications',
549
+ type: 'switch',
550
+ visibility: 'owner',
551
+ },
552
+ pushNotifications: {
553
+ name: 'pushNotifications',
554
+ label: 'pushNotifications',
555
+ type: 'switch',
556
+ visibility: 'owner',
557
+ },
558
+ },
559
+ access: {
560
+ create: 'user',
561
+ read: 'owner',
562
+ update: 'owner',
563
+ delete: 'never',
564
+ },
565
+ });
566
+ ```
567
+
568
+ ---
569
+
570
+ ## Workspace/Team
571
+
572
+ **Multi-tenant organization.**
573
+
574
+ ```typescript
575
+ export const workspaceEntity = defineEntity({
576
+ name: 'Workspace',
577
+ collection: 'workspaces',
578
+ fields: {
579
+ name: {
580
+ name: 'name',
581
+ label: 'name',
582
+ type: 'text',
583
+ visibility: 'user',
584
+ validation: { required: true },
585
+ },
586
+ slug: {
587
+ name: 'slug',
588
+ label: 'slug',
589
+ type: 'text',
590
+ visibility: 'user',
591
+ validation: { required: true },
592
+ },
593
+ ownerId: {
594
+ name: 'ownerId',
595
+ label: 'ownerId',
596
+ type: 'text',
597
+ visibility: 'admin',
598
+ editable: 'never',
599
+ },
600
+ members: {
601
+ name: 'members',
602
+ label: 'members',
603
+ type: 'text',
604
+ visibility: 'user',
605
+ validation: {
606
+ schema: v.array(v.object({
607
+ userId: v.string(),
608
+ role: v.picklist(['owner', 'admin', 'member', 'viewer']),
609
+ joinedAt: v.string(),
610
+ })),
611
+ },
612
+ },
613
+ plan: {
614
+ name: 'plan',
615
+ label: 'plan',
616
+ type: 'select',
617
+ visibility: 'admin',
618
+ validation: {
619
+ options: [
620
+ { value: 'free', label: 'Free' },
621
+ { value: 'pro', label: 'Pro' },
622
+ { value: 'enterprise', label: 'Enterprise' },
623
+ ],
624
+ },
625
+ },
626
+ },
627
+ access: {
628
+ create: 'user',
629
+ read: 'user', // Workspace-level access control
630
+ update: 'admin', // Workspace admin
631
+ delete: 'super',
632
+ },
633
+ });
634
+ ```
635
+
636
+ ---
637
+
638
+ ## Invitation
639
+
640
+ **Invite flow with expiry.**
641
+
642
+ ```typescript
643
+ export const invitationEntity = defineEntity({
644
+ name: 'Invitation',
645
+ collection: 'invitations',
646
+ fields: {
647
+ email: {
648
+ name: 'email',
649
+ label: 'email',
650
+ type: 'email',
651
+ visibility: 'admin',
652
+ validation: { required: true },
653
+ },
654
+ workspaceId: {
655
+ name: 'workspaceId',
656
+ label: 'workspaceId',
657
+ type: 'text',
658
+ visibility: 'admin',
659
+ editable: 'never',
660
+ },
661
+ role: {
662
+ name: 'role',
663
+ label: 'role',
664
+ type: 'select',
665
+ visibility: 'admin',
666
+ validation: {
667
+ options: [
668
+ { value: 'admin', label: 'Admin' },
669
+ { value: 'member', label: 'Member' },
670
+ { value: 'viewer', label: 'Viewer' },
671
+ ],
672
+ },
673
+ },
674
+ invitedBy: {
675
+ name: 'invitedBy',
676
+ label: 'invitedBy',
677
+ type: 'text',
678
+ visibility: 'admin',
679
+ editable: 'never',
680
+ },
681
+ expiresAt: {
682
+ name: 'expiresAt',
683
+ label: 'expiresAt',
684
+ type: 'timestamp',
685
+ visibility: 'admin',
686
+ },
687
+ acceptedAt: {
688
+ name: 'acceptedAt',
689
+ label: 'acceptedAt',
690
+ type: 'timestamp',
691
+ visibility: 'admin',
692
+ editable: 'never',
693
+ },
694
+ },
695
+ access: {
696
+ create: 'admin',
697
+ read: 'admin',
698
+ update: 'never',
699
+ delete: 'admin',
700
+ },
701
+ });
702
+ ```
703
+
704
+ ---
705
+
706
+ ## Media
707
+
708
+ **File/asset library.**
709
+
710
+ ```typescript
711
+ export const mediaEntity = defineEntity({
712
+ name: 'Media',
713
+ collection: 'media',
714
+ fields: {
715
+ name: {
716
+ name: 'name',
717
+ label: 'name',
718
+ type: 'text',
719
+ visibility: 'user',
720
+ },
721
+ url: {
722
+ name: 'url',
723
+ label: 'url',
724
+ type: 'url',
725
+ visibility: 'user',
726
+ editable: 'never',
727
+ },
728
+ type: {
729
+ name: 'type',
730
+ label: 'type',
731
+ type: 'select',
732
+ visibility: 'user',
733
+ editable: 'never',
734
+ validation: {
735
+ options: [
736
+ { value: 'image', label: 'Image' },
737
+ { value: 'video', label: 'Video' },
738
+ { value: 'document', label: 'Document' },
739
+ { value: 'other', label: 'Other' },
740
+ ],
741
+ },
742
+ },
743
+ size: {
744
+ name: 'size',
745
+ label: 'size',
746
+ type: 'number',
747
+ visibility: 'user',
748
+ editable: 'never',
749
+ },
750
+ uploadedBy: {
751
+ name: 'uploadedBy',
752
+ label: 'uploadedBy',
753
+ type: 'text',
754
+ visibility: 'admin',
755
+ editable: 'never',
756
+ },
757
+ },
758
+ access: {
759
+ create: 'user',
760
+ read: 'user',
761
+ update: 'owner',
762
+ delete: 'owner',
763
+ },
764
+ });
765
+ ```
766
+
767
+ ---
768
+
769
+ ## Log/Activity
770
+
771
+ **Audit trail.**
772
+
773
+ ```typescript
774
+ export const activityLogEntity = defineEntity({
775
+ name: 'ActivityLog',
776
+ collection: 'activityLogs',
777
+ fields: {
778
+ action: {
779
+ name: 'action',
780
+ label: 'action',
781
+ type: 'text',
782
+ visibility: 'admin',
783
+ editable: 'never',
784
+ },
785
+ entityType: {
786
+ name: 'entityType',
787
+ label: 'entityType',
788
+ type: 'text',
789
+ visibility: 'admin',
790
+ editable: 'never',
791
+ },
792
+ entityId: {
793
+ name: 'entityId',
794
+ label: 'entityId',
795
+ type: 'text',
796
+ visibility: 'admin',
797
+ editable: 'never',
798
+ },
799
+ userId: {
800
+ name: 'userId',
801
+ label: 'userId',
802
+ type: 'text',
803
+ visibility: 'admin',
804
+ editable: 'never',
805
+ },
806
+ changes: {
807
+ name: 'changes',
808
+ label: 'changes',
809
+ type: 'text',
810
+ visibility: 'admin',
811
+ editable: 'never',
812
+ },
813
+ ip: {
814
+ name: 'ip',
815
+ label: 'ip',
816
+ type: 'text',
817
+ visibility: 'super',
818
+ editable: 'never',
819
+ },
820
+ },
821
+ access: {
822
+ create: 'never', // System-generated only
823
+ read: 'admin',
824
+ update: 'never',
825
+ delete: 'super',
826
+ },
827
+ });
828
+ ```
829
+
830
+ ---
831
+
832
+ ## Usage
833
+
834
+ 1. **Find pattern** that matches your need
835
+ 2. **Copy** the entity definition
836
+ 3. **Customize** field names, labels, options
837
+ 4. **Adjust** access rules for your use case
838
+ 5. **Export** from `entities/index.ts`
839
+
840
+ **Do NOT invent new patterns unless none fit.**