@akanjs/cli 0.9.60-canary.8 → 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.
Files changed (26) hide show
  1. package/cjs/index.js +16 -47
  2. package/cjs/src/guidelines/scalarConstant/scalarConstant.generate.json +24 -20
  3. package/cjs/src/guidelines/scalarConstant/scalarConstant.instruction.md +284 -326
  4. package/cjs/src/guidelines/scalarDictionary/scalarDictionary.generate.json +32 -32
  5. package/cjs/src/guidelines/scalarDictionary/scalarDictionary.instruction.md +175 -249
  6. package/cjs/src/templates/app/app/[lang]/page.js +19 -36
  7. package/cjs/src/templates/workspaceRoot/package.json.template +5 -5
  8. package/esm/index.js +16 -47
  9. package/esm/src/guidelines/scalarConstant/scalarConstant.generate.json +24 -20
  10. package/esm/src/guidelines/scalarConstant/scalarConstant.instruction.md +284 -326
  11. package/esm/src/guidelines/scalarDictionary/scalarDictionary.generate.json +32 -32
  12. package/esm/src/guidelines/scalarDictionary/scalarDictionary.instruction.md +175 -249
  13. package/esm/src/templates/app/app/[lang]/page.js +19 -36
  14. package/esm/src/templates/workspaceRoot/package.json.template +5 -5
  15. package/package.json +5 -4
  16. package/src/guidelines/scalarConstant/scalarConstant.instruction.md +284 -326
  17. package/src/guidelines/scalarDictionary/scalarDictionary.instruction.md +175 -249
  18. package/src/library/library.command.d.ts +0 -2
  19. package/src/library/library.runner.d.ts +0 -2
  20. package/src/library/library.script.d.ts +0 -2
  21. package/src/scalar/scalar.command.d.ts +1 -1
  22. package/cjs/src/guidelines/fieldDecorator/fieldDecorator.generate.json +0 -135
  23. package/cjs/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +0 -606
  24. package/esm/src/guidelines/fieldDecorator/fieldDecorator.generate.json +0 -135
  25. package/esm/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +0 -606
  26. package/src/guidelines/fieldDecorator/fieldDecorator.instruction.md +0 -606
@@ -1,606 +0,0 @@
1
- # Field Decorator Usage Guide for Akan.js
2
-
3
- ## Purpose of Field Decorators
4
-
5
- Field decorators in Akan.js serve as the foundation for data modeling by:
6
-
7
- - Defining properties in model classes with type safety
8
- - Configuring GraphQL schema generation for API endpoints
9
- - Setting up MongoDB schema properties and validation
10
- - Enabling search indexing and optimization
11
- - Managing relationships between data models
12
- - Controlling data visibility and access patterns
13
-
14
- ## Decorator Types and Their Uses
15
-
16
- | Decorator | Purpose | Database | GraphQL | Default Behavior |
17
- | ------------------ | ------------------------------------- | ------------- | ---------- | ----------------------------------- |
18
- | `@Field.Prop()` | Standard visible property | ✅ Stored | ✅ Exposed | Included in all operations |
19
- | `@Field.Hidden()` | Internal data not exposed in API | ✅ Stored | ❌ Hidden | Excluded from GraphQL schema |
20
- | `@Field.Secret()` | Sensitive data with restricted access | ✅ Stored | ✅ Exposed | Not selected by default in queries |
21
- | `@Field.Resolve()` | Computed property (no DB storage) | ❌ Not stored | ✅ Exposed | Calculated at runtime via resolvers |
22
-
23
- ## Basic Syntax and Structure
24
-
25
- ```typescript
26
- @Field.Prop(() => Type, { ...options })
27
- propertyName: TypeScriptType;
28
- ```
29
-
30
- ### Key Components
31
-
32
- 1. **Decorator Function**: Determines field behavior (`Field.Prop`, `Field.Hidden`, etc.)
33
- 2. **Type Arrow Function**: Specifies GraphQL/MongoDB type using `() => Type` syntax
34
- 3. **Options Object**: Configures field behavior with various options
35
- 4. **TypeScript Type**: Defines the type for static analysis and development
36
-
37
- ## Field.Prop for Standard Properties
38
-
39
- Used for typical model properties that should be stored in the database and exposed in the GraphQL API.
40
-
41
- ```typescript
42
- // String property
43
- @Field.Prop(() => String)
44
- name: string;
45
-
46
- // Number property with validation
47
- @Field.Prop(() => Int, { min: 0, max: 100 })
48
- score: number;
49
-
50
- // Boolean property with default
51
- @Field.Prop(() => Boolean, { default: false })
52
- isActive: boolean;
53
- ```
54
-
55
- ## Field.Hidden for Database-Only Properties
56
-
57
- Used for properties that should be stored in the database but not exposed in the GraphQL API.
58
-
59
- ```typescript
60
- // Internal tracking data
61
- @Field.Hidden(() => String)
62
- internalId: string;
63
-
64
- // Technical metadata
65
- @Field.Hidden(() => JSON, { default: {} })
66
- systemMetadata: Record<string, any>;
67
- ```
68
-
69
- ## Field.Secret for Sensitive Data
70
-
71
- Used for sensitive properties that are stored but not selected by default in queries.
72
-
73
- ```typescript
74
- // Personal information
75
- @Field.Secret(() => String, { nullable: true })
76
- phoneNumber: string | null;
77
-
78
- // Security data
79
- @Field.Secret(() => String)
80
- apiKey: string;
81
- ```
82
-
83
- ## Field.Resolve for Computed Properties
84
-
85
- Used for properties that are calculated at runtime and not stored in the database.
86
-
87
- ```typescript
88
- // Computed property from other fields
89
- @Field.Resolve(() => String)
90
- get fullName(): string {
91
- return `${this.firstName} ${this.lastName}`;
92
- }
93
-
94
- // Calculated numeric value
95
- @Field.Resolve(() => Int)
96
- get age(): number {
97
- return dayjs().diff(this.birthDate, 'year');
98
- }
99
- ```
100
-
101
- ## Field Options Reference
102
-
103
- | Option | Type | Description | Default | Example |
104
- | ------------ | ----------------------------------- | ------------------------------------ | ------- | ---------------------------------- |
105
- | `default` | `any` | Default value | `null` | `{ default: "active" }` |
106
- | `nullable` | `boolean` | Allows null values | `false` | `{ nullable: true }` |
107
- | `enum` | `Enum` | Restricts to enum values | - | `{ enum: StatusEnum }` |
108
- | `immutable` | `boolean` | Prevents modification after creation | `false` | `{ immutable: true }` |
109
- | `min` | `number` | Minimum value (numbers) | - | `{ min: 0 }` |
110
- | `max` | `number` | Maximum value (numbers) | - | `{ max: 100 }` |
111
- | `minlength` | `number` | Minimum length (strings) | - | `{ minlength: 3 }` |
112
- | `maxlength` | `number` | Maximum length (strings) | - | `{ maxlength: 255 }` |
113
- | `type` | `"email"`, `"url"`, `"password"` | Special validation type | - | `{ type: "email" }` |
114
- | `validate` | `function` | Custom validation function | - | `{ validate: isEmail }` |
115
- | `select` | `boolean` | Include in default queries | `true` | `{ select: false }` |
116
- | `ref` | `string` | Reference to another model | - | `{ ref: "User" }` |
117
- | `refPath` | `string` | Dynamic reference path | - | `{ refPath: "modelType" }` |
118
- | `refType` | `"child"`, `"parent"`, `"relation"` | Relationship type | - | `{ refType: "child" }` |
119
- | `of` | `GqlScalar` | Type for Map values | - | `{ of: String }` |
120
- | `text` | `"search"`, `"filter"` | Search indexing behavior | - | `{ text: "search" }` |
121
- | `query` | `QueryOf<any>` | Default MongoDB query | - | `{ query: { active: true } }` |
122
- | `accumulate` | `AccumulatorOperator` | MongoDB aggregation | - | `{ accumulate: { $sum: 1 } }` |
123
- | `example` | `any` | Example value for docs | - | `{ example: "example@email.com" }` |
124
-
125
- ## Working with Primitive Types
126
-
127
- ### String Fields
128
-
129
- ```typescript
130
- // Basic string
131
- @Field.Prop(() => String)
132
- name: string;
133
-
134
- // With length constraints
135
- @Field.Prop(() => String, { minlength: 3, maxlength: 50 })
136
- username: string;
137
-
138
- // With special validation type
139
- @Field.Prop(() => String, { type: "email" })
140
- email: string;
141
- ```
142
-
143
- ### Number Fields
144
-
145
- ```typescript
146
- // Integer
147
- @Field.Prop(() => Int)
148
- count: number;
149
-
150
- // Float with range
151
- @Field.Prop(() => Float, { min: 0, max: 1 })
152
- percentage: number;
153
- ```
154
-
155
- ### Boolean Fields
156
-
157
- ```typescript
158
- // Boolean with default
159
- @Field.Prop(() => Boolean, { default: false })
160
- isPublished: boolean;
161
- ```
162
-
163
- ## Working with Enums
164
-
165
- ```typescript
166
- // Define enum with enumOf
167
- export const Status = enumOf(["active", "inactive", "pending"] as const);
168
- export type Status = enumOf<typeof Status>;
169
-
170
- // Use in field
171
- @Field.Prop(() => String, { enum: Status, default: "active" })
172
- status: Status;
173
- ```
174
-
175
- ## Working with Arrays and Nested Structures
176
-
177
- ### Simple Arrays
178
-
179
- ```typescript
180
- // String array
181
- @Field.Prop(() => [String], { default: [] })
182
- tags: string[];
183
-
184
- // Number array
185
- @Field.Prop(() => [Int], { default: [] })
186
- scores: number[];
187
- ```
188
-
189
- ### Nested Arrays
190
-
191
- ```typescript
192
- // 2D array
193
- @Field.Prop(() => [[Int]], { default: [] })
194
- matrix: number[][];
195
- ```
196
-
197
- ### Arrays of Custom Types
198
-
199
- ```typescript
200
- // Array of references
201
- @Field.Prop(() => [LightComment], { ref: "Comment" })
202
- comments: LightComment[];
203
-
204
- // Array of embedded scalars
205
- @Field.Prop(() => [Address])
206
- addresses: Address[];
207
- ```
208
-
209
- ## Reference Fields and Relationships
210
-
211
- ### Single References
212
-
213
- ```typescript
214
- // Basic reference
215
- @Field.Prop(() => LightUser, { ref: "User" })
216
- owner: LightUser;
217
-
218
- // Optional reference
219
- @Field.Prop(() => LightCategory, { ref: "Category", nullable: true })
220
- category: LightCategory | null;
221
- ```
222
-
223
- ### Dynamic References
224
-
225
- ```typescript
226
- // Reference determined by path
227
- @Field.Prop(() => String, { enum: ModelType })
228
- modelType: ModelType;
229
-
230
- @Field.Prop(() => LightModel, { refPath: "modelType" })
231
- model: LightModel;
232
- ```
233
-
234
- ### Relationship Types
235
-
236
- ```typescript
237
- // Parent-child relationship
238
- @Field.Prop(() => LightProject, { ref: "Project", refType: "parent" })
239
- project: LightProject;
240
-
241
- // Many-to-many relationship
242
- @Field.Prop(() => [LightTag], { ref: "Tag", refType: "relation" })
243
- tags: LightTag[];
244
- ```
245
-
246
- ## Date and Special Type Handling
247
-
248
- ### Date Fields
249
-
250
- ```typescript
251
- // Current date default
252
- @Field.Prop(() => Date, { default: () => dayjs() })
253
- createdAt: Dayjs;
254
-
255
- // Immutable timestamp
256
- @Field.Prop(() => Date, { default: () => dayjs(), immutable: true })
257
- createdAt: Dayjs;
258
- ```
259
-
260
- ### JSON and Map Types
261
-
262
- ```typescript
263
- // JSON object
264
- @Field.Prop(() => JSON, { default: {} })
265
- metadata: Record<string, any>;
266
-
267
- // Map type
268
- @Field.Prop(() => Map, { of: String, default: new Map() })
269
- translations: Map<string, string>;
270
- ```
271
-
272
- ## Validation and Constraints
273
-
274
- ### Built-in Validation
275
-
276
- ```typescript
277
- // Numeric range
278
- @Field.Prop(() => Int, { min: 1, max: 10 })
279
- rating: number;
280
-
281
- // String length
282
- @Field.Prop(() => String, { minlength: 8, maxlength: 100 })
283
- password: string;
284
- ```
285
-
286
- ### Custom Validation
287
-
288
- ```typescript
289
- // Custom validation function
290
- @Field.Prop(() => String, {
291
- validate: (value, model) => {
292
- return /^[A-Z][a-z]+$/.test(value);
293
- }
294
- })
295
- properName: string;
296
- ```
297
-
298
- ## Search Optimization
299
-
300
- ### Full-Text Search
301
-
302
- ```typescript
303
- // Searchable text field
304
- @Field.Prop(() => String, { text: "search" })
305
- description: string;
306
- ```
307
-
308
- ### Filter-Only Fields
309
-
310
- ```typescript
311
- // Filterable but not searchable
312
- @Field.Prop(() => String, { text: "filter" })
313
- category: string;
314
- ```
315
-
316
- ## Default Values and Immutability
317
-
318
- ### Static Defaults
319
-
320
- ```typescript
321
- // Static default value
322
- @Field.Prop(() => String, { default: "Guest" })
323
- role: string;
324
- ```
325
-
326
- ### Dynamic Defaults
327
-
328
- ```typescript
329
- // Function-based default
330
- @Field.Prop(() => Date, { default: () => dayjs() })
331
- createdAt: Dayjs;
332
-
333
- // Complex default value
334
- @Field.Prop(() => JSON, { default: () => ({ version: 1, settings: {} }) })
335
- config: Record<string, any>;
336
- ```
337
-
338
- ### Immutable Fields
339
-
340
- ```typescript
341
- // Cannot be changed after creation
342
- @Field.Prop(() => String, { immutable: true })
343
- code: string;
344
- ```
345
-
346
- ## Integration with Model Types
347
-
348
- ### Input Models
349
-
350
- ```typescript
351
- export class ProductInput {
352
- @Field.Prop(() => String)
353
- name: string;
354
-
355
- @Field.Prop(() => Float, { min: 0 })
356
- price: number;
357
- }
358
- ```
359
-
360
- ### Object Models
361
-
362
- ```typescript
363
- export class ProductObject extends via(ProductInput) {
364
- @Field.Prop(() => Date, { default: dayjs() })
365
- createdAt: Dayjs;
366
- }
367
- ```
368
-
369
- ### Light Models
370
-
371
- ```typescript
372
- export class LightProduct extends via(ProductObject, ["name", "price", "status"] as const) {}
373
- ```
374
-
375
- ### Full Models
376
-
377
- ```typescript
378
- export class Product extends via(ProductObject, LightProduct) {}
379
- ```
380
-
381
- ### Scalar Models
382
-
383
- ```typescript
384
- export class Address {
385
- @Field.Prop(() => String)
386
- street: string;
387
-
388
- @Field.Prop(() => String)
389
- city: string;
390
- }
391
- ```
392
-
393
- ## Best Practices
394
-
395
- ### Type Safety
396
-
397
- Always ensure TypeScript types match GraphQL types:
398
-
399
- ```typescript
400
- // ✅ Correct
401
- @Field.Prop(() => Int)
402
- count: number;
403
-
404
- // ❌ Incorrect (type mismatch)
405
- @Field.Prop(() => String)
406
- count: number;
407
- ```
408
-
409
- ### Nullable Fields
410
-
411
- Always add both the decorator option and TypeScript union type:
412
-
413
- ```typescript
414
- // ✅ Correct
415
- @Field.Prop(() => String, { nullable: true })
416
- middleName: string | null;
417
-
418
- // ❌ Incorrect (missing union type)
419
- @Field.Prop(() => String, { nullable: true })
420
- middleName: string;
421
- ```
422
-
423
- ### Default Values
424
-
425
- Provide sensible defaults for required fields:
426
-
427
- ```typescript
428
- // ✅ Correct
429
- @Field.Prop(() => [String], { default: [] })
430
- tags: string[];
431
-
432
- // ❌ Incorrect (missing default for array)
433
- @Field.Prop(() => [String])
434
- tags: string[];
435
- ```
436
-
437
- ### Sensitive Data
438
-
439
- Use `Field.Secret` for sensitive information:
440
-
441
- ```typescript
442
- // ✅ Correct
443
- @Field.Secret(() => String)
444
- apiKey: string;
445
-
446
- // ❌ Incorrect (exposes sensitive data by default)
447
- @Field.Prop(() => String)
448
- apiKey: string;
449
- ```
450
-
451
- ### Array Type Syntax
452
-
453
- Use bracket notation instead of generics:
454
-
455
- ```typescript
456
- // ✅ Correct
457
- @Field.Prop(() => [String])
458
- tags: string[];
459
-
460
- // ❌ Incorrect (using generics)
461
- @Field.Prop(() => Array<String>)
462
- tags: string[];
463
- ```
464
-
465
- ### Date Handling
466
-
467
- Always use `Dayjs` type for date fields:
468
-
469
- ```typescript
470
- // ✅ Correct
471
- @Field.Prop(() => Date)
472
- createdAt: Dayjs;
473
-
474
- // ❌ Incorrect (using JavaScript Date)
475
- @Field.Prop(() => Date)
476
- createdAt: Date;
477
- ```
478
-
479
- ## Troubleshooting Common Issues
480
-
481
- ### Field Not Appearing in GraphQL
482
-
483
- **Problem**: Field is defined but not showing up in GraphQL schema
484
- **Possible Causes**:
485
-
486
- - Using `@Field.Hidden()` instead of `@Field.Prop()`
487
- - Field has `{ select: false }` option
488
- - GraphQL schema not regenerated after changes
489
-
490
- **Solution**:
491
-
492
- ```typescript
493
- // Change this
494
- @Field.Hidden(() => String)
495
- name: string;
496
-
497
- // To this
498
- @Field.Prop(() => String)
499
- name: string;
500
- ```
501
-
502
- ### Default Value Not Applied
503
-
504
- **Problem**: Default value isn't set when creating new documents
505
- **Possible Causes**:
506
-
507
- - Default value type doesn't match field type
508
- - Default function throws an error
509
- - Middleware overriding the value
510
-
511
- **Solution**:
512
-
513
- ```typescript
514
- // Ensure type consistency
515
- @Field.Prop(() => Int, { default: 0 }) // number default for number field
516
- count: number;
517
- ```
518
-
519
- ### Validation Errors
520
-
521
- **Problem**: Unexpected validation failures
522
- **Possible Causes**:
523
-
524
- - Value outside min/max range
525
- - String length outside minlength/maxlength
526
- - Custom validation function returning false
527
-
528
- **Solution**:
529
-
530
- ```typescript
531
- // Check constraints
532
- @Field.Prop(() => String, {
533
- minlength: 3,
534
- validate: (value) => /^[A-Z]/.test(value) // Must start with uppercase
535
- })
536
- username: string;
537
- ```
538
-
539
- ### Enum Values Not Working
540
-
541
- **Problem**: Enum validation not working properly
542
- **Possible Causes**:
543
-
544
- - Enum not created with `enumOf()`
545
- - Enum values don't match expected format
546
- - Missing `{ enum: YourEnum }` in options
547
-
548
- **Solution**:
549
-
550
- ```typescript
551
- // Proper enum definition
552
- export const Status = enumOf(["active", "inactive"] as const);
553
- export type Status = enumOf<typeof Status>;
554
-
555
- // Correct field definition
556
- @Field.Prop(() => String, { enum: Status, default: "active" })
557
- status: Status;
558
- ```
559
-
560
- ### Search Not Working
561
-
562
- **Problem**: Full-text search not working on fields
563
- **Possible Causes**:
564
-
565
- - Missing `{ text: "search" }` option
566
- - Search daemon not running
567
- - Model not marked for indexing
568
-
569
- **Solution**:
570
-
571
- ```typescript
572
- // Add text search option
573
- @Field.Prop(() => String, { text: "search" })
574
- description: string;
575
- ```
576
-
577
- ### Circular Dependencies
578
-
579
- **Problem**: Circular import errors when referencing models
580
- **Possible Causes**:
581
-
582
- - Models directly importing each other
583
- - Using Full models instead of Light models for references
584
-
585
- **Solution**:
586
-
587
- ```typescript
588
- // Use Light models for references
589
- @Field.Prop(() => LightUser, { ref: "User" }) // Not FullUser
590
- owner: LightUser;
591
- ```
592
-
593
- ## Summary Checklist
594
-
595
- 1. **Choose the right decorator**: `Field.Prop`, `Field.Hidden`, `Field.Secret`, or `Field.Resolve`
596
- 2. **Use correct type syntax**: `() => Type` or `() => [Type]` for arrays
597
- 3. **Match TypeScript types**: Ensure decorator type matches property type
598
- 4. **Handle nullability**: Use `{ nullable: true }` and `Type | null` together
599
- 5. **Provide defaults**: Set appropriate default values, especially for arrays
600
- 6. **Add validation**: Use min/max, minlength/maxlength, or custom validation
601
- 7. **Protect sensitive data**: Use `Field.Secret` for sensitive information
602
- 8. **Optimize for search**: Add `{ text: "search" }` to searchable fields
603
- 9. **Mark immutable fields**: Use `{ immutable: true }` for unchangeable fields
604
- 10. **Use proper references**: Specify `ref`, `refPath`, or `refType` for relationships
605
-
606
- Following these guidelines will ensure your field decorators work correctly across the entire Akan.js framework ecosystem, from GraphQL APIs to MongoDB persistence and search functionality.