go-duck-cli 1.4.8 → 1.4.12

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.
@@ -178,7 +178,8 @@ export const generatePostmanCollection = async (config, entities, outputDir, ope
178
178
  if (f.type === 'Integer' || f.type === 'Long') return `${f.name}: 100`;
179
179
  if (f.type === 'Float' || f.type === 'BigDecimal') return `${f.name}: 99.99`;
180
180
  if (f.type === 'Boolean') return `${f.name}: true`;
181
- if (f.type === 'LocalDate') return `${f.name}: "2024-01-01"`;
181
+ if (f.type === 'Time') return `${f.name}: "0000-01-01T12:00:00Z"`;
182
+ if (f.type === 'LocalDate') return `${f.name}: "2024-01-01T00:00:00Z"`;
182
183
  if (f.type === 'Instant') return `${f.name}: "2024-01-01T12:00:00Z"`;
183
184
  if (f.type === 'JSON' || f.type === 'JSONB') return `${f.name}: "{\\"attribute\\": \\"example_value\\"}"`;
184
185
  return `${f.name}: "test"`;
@@ -361,9 +362,10 @@ export const generatePostmanCollection = async (config, entities, outputDir, ope
361
362
  for (const f of fields) {
362
363
  if (f.type === 'String' || f.type === 'Text') obj[f.name] = "Sample " + f.name;
363
364
  else if (f.type === 'Integer' || f.type === 'Long') obj[f.name] = 100;
364
- else if (f.type === 'Float' || f.type === 'BigDecimal') obj[f.name] = 99.99;
365
+ else if (f.type === 'Float' || f.type === 'Double' || f.type === 'BigDecimal') obj[f.name] = 99.99;
365
366
  else if (f.type === 'Boolean') obj[f.name] = true;
366
- else if (f.type === 'LocalDate') obj[f.name] = "2024-01-01";
367
+ else if (f.type === 'Time') obj[f.name] = "0000-01-01T12:00:00Z";
368
+ else if (f.type === 'LocalDate') obj[f.name] = "2024-01-01T00:00:00Z";
367
369
  else if (f.type === 'Instant' || f.type === 'DateTime' || f.type === 'Datetime') obj[f.name] = "2024-01-01T12:00:00Z";
368
370
  else if (f.type === 'JSON' || f.type === 'JSONB') obj[f.name] = {"attribute": "example_value"};
369
371
  else if (f.isEnum) obj[f.name] = "ACTIVE"; // Safe fallback for enums
@@ -322,11 +322,13 @@ const getSwaggerFieldSchema = (type) => {
322
322
  const schemas = {
323
323
  'String': { type: 'string' },
324
324
  'Integer': { type: 'integer', format: 'int32' },
325
- 'Float': { type: 'number', format: 'double' },
325
+ 'Float': { type: 'number', format: 'float' },
326
+ 'Double': { type: 'number', format: 'double' },
326
327
  'Boolean': { type: 'boolean' },
328
+ 'Time': { type: 'string', format: 'date-time' },
327
329
  'Long': { type: 'integer', format: 'int64' },
328
330
  'BigDecimal': { type: 'number', format: 'double' },
329
- 'LocalDate': { type: 'string', format: 'date' },
331
+ 'LocalDate': { type: 'string', format: 'date-time' },
330
332
  'Instant': { type: 'string', format: 'date-time' },
331
333
  'JSON': { type: 'object' },
332
334
  'JSONB': { type: 'object' },
@@ -203,8 +203,18 @@ import (
203
203
  "time"
204
204
  "github.com/shirou/gopsutil/v3/cpu"
205
205
  "github.com/shirou/gopsutil/v3/process"
206
+ "github.com/shirou/gopsutil/v3/mem"
206
207
  "os"
207
208
  "fmt"
209
+ "strconv"
210
+ "strings"
211
+ "sync"
212
+ )
213
+
214
+ var (
215
+ cgroupCPULock sync.Mutex
216
+ lastCgroupTime time.Time
217
+ lastCgroupUsageNs uint64
208
218
  )
209
219
 
210
220
  type SystemMetrics struct {
@@ -212,9 +222,13 @@ type SystemMetrics struct {
212
222
  StartTime string \`json:"start_time"\`
213
223
  ProcessCPUUsage float64 \`json:"process_cpu_usage"\`
214
224
  SystemCPUUsage float64 \`json:"system_cpu_usage"\`
225
+ PodCPULimitPct float64 \`json:"pod_cpu_limit_pct"\`
226
+ IsPodCPULimited bool \`json:"is_pod_cpu_limited"\`
227
+ CPUThreadsUsage []float64 \`json:"cpu_threads_usage"\`
215
228
  SystemCPUCount int \`json:"system_cpu_count"\`
216
229
  ProcessFilesOpen int32 \`json:"process_files_open"\`
217
230
 
231
+ TotalMemMB uint64 \`json:"total_mem_mb"\`
218
232
  HeapAllocMB uint64 \`json:"heap_alloc_mb"\`
219
233
  HeapSysMB uint64 \`json:"heap_sys_mb"\`
220
234
  NumGC uint32 \`json:"num_gc"\`
@@ -222,11 +236,88 @@ type SystemMetrics struct {
222
236
  Goroutines int \`json:"goroutines"\`
223
237
  }
224
238
 
239
+ func getContainerMemoryLimit() uint64 {
240
+ // Check cgroup v2
241
+ if data, err := os.ReadFile("/sys/fs/cgroup/memory.max"); err == nil {
242
+ val := strings.TrimSpace(string(data))
243
+ if val != "max" && val != "" {
244
+ if limit, err := strconv.ParseUint(val, 10, 64); err == nil {
245
+ return limit
246
+ }
247
+ }
248
+ }
249
+ // Check cgroup v1
250
+ if data, err := os.ReadFile("/sys/fs/cgroup/memory/memory.limit_in_bytes"); err == nil {
251
+ val := strings.TrimSpace(string(data))
252
+ if limit, err := strconv.ParseUint(val, 10, 64); err == nil {
253
+ // Some systems report an absurdly high number (e.g. 9223372036854771712) for "no limit"
254
+ if limit < 9000000000000000000 {
255
+ return limit
256
+ }
257
+ }
258
+ }
259
+ return 0
260
+ }
261
+
262
+ func getCgroupCPUUsageNs() (uint64, error) {
263
+ // try v2
264
+ if data, err := os.ReadFile("/sys/fs/cgroup/cpu.stat"); err == nil {
265
+ lines := strings.Split(string(data), "\n")
266
+ for _, line := range lines {
267
+ if strings.HasPrefix(line, "usage_usec") {
268
+ parts := strings.Fields(line)
269
+ if len(parts) == 2 {
270
+ if usec, err := strconv.ParseUint(parts[1], 10, 64); err == nil {
271
+ return usec * 1000, nil // to nanoseconds
272
+ }
273
+ }
274
+ }
275
+ }
276
+ }
277
+ // try v1
278
+ if data, err := os.ReadFile("/sys/fs/cgroup/cpuacct/cpuacct.usage"); err == nil {
279
+ val := strings.TrimSpace(string(data))
280
+ if ns, err := strconv.ParseUint(val, 10, 64); err == nil {
281
+ return ns, nil
282
+ }
283
+ }
284
+ return 0, fmt.Errorf("not found")
285
+ }
286
+
287
+ func getCgroupCPULimitCores() float64 {
288
+ // check v2
289
+ if data, err := os.ReadFile("/sys/fs/cgroup/cpu.max"); err == nil {
290
+ parts := strings.Fields(strings.TrimSpace(string(data)))
291
+ if len(parts) == 2 && parts[0] != "max" {
292
+ max, err1 := strconv.ParseFloat(parts[0], 64)
293
+ period, err2 := strconv.ParseFloat(parts[1], 64)
294
+ if err1 == nil && err2 == nil && period > 0 {
295
+ return max / period
296
+ }
297
+ }
298
+ }
299
+ // check v1
300
+ if quotaData, err := os.ReadFile("/sys/fs/cgroup/cpu/cpu.cfs_quota_us"); err == nil {
301
+ quotaStr := strings.TrimSpace(string(quotaData))
302
+ if quotaStr != "-1" {
303
+ if quota, err := strconv.ParseFloat(quotaStr, 64); err == nil {
304
+ if periodData, err := os.ReadFile("/sys/fs/cgroup/cpu/cpu.cfs_period_us"); err == nil {
305
+ if period, err := strconv.ParseFloat(strings.TrimSpace(string(periodData)), 64); err == nil && period > 0 {
306
+ return quota / period
307
+ }
308
+ }
309
+ }
310
+ }
311
+ }
312
+ return 0
313
+ }
314
+
225
315
  func CollectSystemMetrics() SystemMetrics {
226
316
  var m runtime.MemStats
227
317
  runtime.ReadMemStats(&m)
228
318
 
229
319
  cpuPercent, _ := cpu.Percent(0, false)
320
+ threadsPercent, _ := cpu.Percent(0, true)
230
321
  sysCPU := 0.0
231
322
  if len(cpuPercent) > 0 {
232
323
  sysCPU = cpuPercent[0]
@@ -241,23 +332,59 @@ func CollectSystemMetrics() SystemMetrics {
241
332
  openFiles = int32(len(files))
242
333
  }
243
334
 
335
+ vMem, _ := mem.VirtualMemory()
336
+ totalMemMB := uint64(0)
337
+
338
+ // 1. Try Kubernetes / Docker Cgroup Limits first
339
+ cgroupLimitBytes := getContainerMemoryLimit()
340
+ if cgroupLimitBytes > 0 {
341
+ totalMemMB = cgroupLimitBytes / 1024 / 1024
342
+ } else if vMem != nil {
343
+ // 2. Fallback to underlying physical host memory
344
+ totalMemMB = vMem.Total / 1024 / 1024
345
+ }
346
+
244
347
  appMetrics := GetGlobalMetrics()
245
348
  uptime := time.Since(appMetrics.StartTime)
246
349
 
247
- // Convert uptime duration to readable string like "10 days 4 hours..."
248
- days := int(uptime.Hours()) / 24
249
- hours := int(uptime.Hours()) % 24
250
- mins := int(uptime.Minutes()) % 60
251
- secs := int(uptime.Seconds()) % 60
252
- uptimeStr := fmt.Sprintf("%d days %d hours %d minutes %d seconds", days, hours, mins, secs)
350
+ uptimeStr := fmt.Sprintf("%d days, %d hours, %d minutes",
351
+ int(uptime.Hours())/24, int(uptime.Hours())%24, int(uptime.Minutes())%60)
352
+
353
+ podCPULimitPct := 0.0
354
+ isPodCPULimited := false
355
+
356
+ limitCores := getCgroupCPULimitCores()
357
+ if limitCores > 0 {
358
+ isPodCPULimited = true
359
+ cgroupCPULock.Lock()
360
+ usageNs, err := getCgroupCPUUsageNs()
361
+ now := time.Now()
362
+ if err == nil {
363
+ if !lastCgroupTime.IsZero() && usageNs > lastCgroupUsageNs {
364
+ timeDelta := float64(now.Sub(lastCgroupTime).Nanoseconds())
365
+ usageDelta := float64(usageNs - lastCgroupUsageNs)
366
+ if timeDelta > 0 {
367
+ coresUsed := usageDelta / timeDelta
368
+ podCPULimitPct = (coresUsed / limitCores) * 100.0
369
+ }
370
+ }
371
+ lastCgroupUsageNs = usageNs
372
+ lastCgroupTime = now
373
+ }
374
+ cgroupCPULock.Unlock()
375
+ }
253
376
 
254
377
  return SystemMetrics{
255
378
  Uptime: uptimeStr,
256
379
  StartTime: appMetrics.StartTime.Format(time.RFC1123),
257
380
  ProcessCPUUsage: procCPU,
258
381
  SystemCPUUsage: sysCPU,
382
+ PodCPULimitPct: podCPULimitPct,
383
+ IsPodCPULimited: isPodCPULimited,
384
+ CPUThreadsUsage: threadsPercent,
259
385
  SystemCPUCount: runtime.NumCPU(),
260
386
  ProcessFilesOpen: openFiles,
387
+ TotalMemMB: totalMemMB,
261
388
  HeapAllocMB: m.HeapAlloc / 1024 / 1024,
262
389
  HeapSysMB: m.HeapSys / 1024 / 1024,
263
390
  NumGC: m.NumGC,
package/go.mod ADDED
@@ -0,0 +1,15 @@
1
+ module testmod
2
+
3
+ go 1.26.1
4
+
5
+ require (
6
+ filippo.io/edwards25519 v1.1.0 // indirect
7
+ github.com/go-sql-driver/mysql v1.8.1 // indirect
8
+ github.com/google/uuid v1.6.0 // indirect
9
+ github.com/jinzhu/inflection v1.0.0 // indirect
10
+ github.com/jinzhu/now v1.1.5 // indirect
11
+ golang.org/x/text v0.20.0 // indirect
12
+ gorm.io/datatypes v1.2.7 // indirect
13
+ gorm.io/driver/mysql v1.5.6 // indirect
14
+ gorm.io/gorm v1.30.0 // indirect
15
+ )
package/go.sum ADDED
@@ -0,0 +1,20 @@
1
+ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
2
+ filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
3
+ github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
4
+ github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
5
+ github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
6
+ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
7
+ github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
8
+ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
9
+ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
10
+ github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
11
+ github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
12
+ golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
13
+ golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
14
+ gorm.io/datatypes v1.2.7 h1:ww9GAhF1aGXZY3EB3cJPJ7//JiuQo7DlQA7NNlVaTdk=
15
+ gorm.io/datatypes v1.2.7/go.mod h1:M2iO+6S3hhi4nAyYe444Pcb0dcIiOMJ7QHaUXxyiNZY=
16
+ gorm.io/driver/mysql v1.5.6 h1:Ld4mkIickM+EliaQZQx3uOJDJHtrd70MxAUqWqlx3Y8=
17
+ gorm.io/driver/mysql v1.5.6/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM=
18
+ gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
19
+ gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs=
20
+ gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE=
package/index.js CHANGED
@@ -216,6 +216,11 @@ Handlebars.registerHelper('isJson', (type) => {
216
216
  return t === 'json' || t === 'jsonb';
217
217
  });
218
218
 
219
+ Handlebars.registerHelper('hasDecimal', (fields) => {
220
+ if (!fields || !Array.isArray(fields)) return false;
221
+ return fields.some(f => f.type === 'BigDecimal');
222
+ });
223
+
219
224
  Handlebars.registerHelper('toLowerCase', (str) => {
220
225
  if (typeof str !== 'string') return '';
221
226
  return str.toLowerCase();
@@ -235,11 +240,13 @@ Handlebars.registerHelper('toGoType', (type, options) => {
235
240
  'text': 'string',
236
241
  'integer': 'int',
237
242
  'int': 'int',
238
- 'float': 'float64',
243
+ 'float': 'float32',
244
+ 'double': 'float64',
239
245
  'boolean': 'bool',
240
246
  'bool': 'bool',
247
+ 'time': 'time.Time',
241
248
  'long': 'int64',
242
- 'bigdecimal': 'float64',
249
+ 'bigdecimal': 'decimal.Decimal',
243
250
  'localdate': 'time.Time',
244
251
  'instant': 'time.Time',
245
252
  'datetime': 'time.Time',
@@ -662,7 +669,8 @@ program
662
669
  // 8. Generate Swagger Docs
663
670
  await generateSwaggerDocs(config, entities, absoluteOutputDir, openEntities);
664
671
  await generatePostmanCollection(config, entities, absoluteOutputDir, openEntities);
665
- console.log(chalk.green('✅ Swagger API and Postman Collection generated!'));
672
+ await generateMQTTTopicsJSON(config, entities, absoluteOutputDir);
673
+ console.log(chalk.green('✅ Swagger API, Postman Collection, and MQTT Dictionary generated!'));
666
674
 
667
675
  // 8.5 Generate Web Docs App
668
676
  await generateDocumentation(config, entities, absoluteOutputDir, enums, openEntities);
@@ -835,9 +843,10 @@ require (
835
843
  // Sync WebSocket
836
844
  await generateWebSocketCode(config, entities, absoluteOutputDir);
837
845
 
838
- // Sync Swagger Docs
846
+ // Sync Swagger Docs & MQTT Dictionary
839
847
  await generateSwaggerDocs(config, entities, absoluteOutputDir, openEntities);
840
848
  await generatePostmanCollection(config, entities, absoluteOutputDir, openEntities);
849
+ await generateMQTTTopicsJSON(config, entities, absoluteOutputDir);
841
850
 
842
851
  // Sync Web Docs App
843
852
  await generateDocumentation(config, entities, absoluteOutputDir, enums, openEntities);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "go-duck-cli",
3
- "version": "1.4.8",
3
+ "version": "1.4.12",
4
4
  "description": "The Ultimate Evolutionary Go Microservice Scaffolder.",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/parser/gdl.js CHANGED
@@ -8,7 +8,7 @@ import path from 'path';
8
8
  * fieldName Type [required] [unique] [text]
9
9
  *
10
10
  * Supported types:
11
- * String, Text, Integer, Long, Float, BigDecimal, Boolean, LocalDate, Instant, JSON, JSONB
11
+ * String, Text, Integer, Long, Float, Double, BigDecimal, Boolean, Time, LocalDate, Instant, JSON, JSONB
12
12
  *
13
13
  * Type overrides in GDL:
14
14
  * - `Text` maps to TEXT in DB and string in Go
@@ -266,9 +266,11 @@ export const toGoType = (type, enums = []) => {
266
266
  Text: 'string',
267
267
  Integer: 'int',
268
268
  Long: 'int64',
269
- Float: 'float64',
270
- BigDecimal: 'float64',
269
+ Float: 'float32',
270
+ Double: 'float64',
271
+ BigDecimal: 'decimal.Decimal',
271
272
  Boolean: 'bool',
273
+ Time: 'time.Time',
272
274
  LocalDate: 'time.Time',
273
275
  Instant: 'time.Time',
274
276
  JSON: 'datatypes.JSON',
@@ -291,9 +293,11 @@ export const toLiquibaseType = (field, enums = []) => {
291
293
  String: 'VARCHAR(255)',
292
294
  Integer: 'INT',
293
295
  Long: 'BIGINT',
294
- Float: 'DECIMAL',
295
- BigDecimal: 'DECIMAL',
296
+ Float: 'REAL',
297
+ Double: 'DOUBLE PRECISION',
298
+ BigDecimal: 'NUMERIC(19, 4)',
296
299
  Boolean: 'BOOLEAN',
300
+ Time: 'TIME',
297
301
  LocalDate: 'DATE',
298
302
  Instant: 'TIMESTAMP',
299
303
  JSON: 'JSON',
@@ -41,6 +41,39 @@ metadata JSONB
41
41
  </div>
42
42
  </div>
43
43
 
44
+ <!-- Comprehensive Example -->
45
+ <div class="bg-slate-900 p-10 rounded-[3rem] border border-slate-800 mb-20 shadow-xl shadow-slate-900/50">
46
+ <h2 class="text-2xl font-black text-white mb-6 m-0 flex items-center">
47
+ <svg class="w-6 h-6 mr-3 text-emerald-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4" /></svg>
48
+ Comprehensive Financial Entity Example
49
+ </h2>
50
+ <p class="text-slate-400 mb-6">This real-world example demonstrates the use of high-precision numeric types like <code class="text-emerald-400">Double</code> and <code class="text-emerald-400">BigDecimal</code> alongside standard types, modifiers, and entity annotations.</p>
51
+
52
+ <div class="bg-slate-950 p-6 rounded-2xl border border-slate-800 shadow-inner overflow-x-auto">
53
+ <pre class="bg-transparent p-0 text-emerald-400 font-mono text-sm leading-relaxed whitespace-pre font-bold m-0">
54
+ @Audited
55
+ @Searchable
56
+ entity Transaction {
57
+ uuid id required unique // Maps to UUID
58
+ string(50) transactionId required unique // Maps to VARCHAR(50)
59
+ string currency required // Maps to VARCHAR(255)
60
+ text notes // Maps to TEXT
61
+ int attemptCount // Maps to INTEGER
62
+ long userId required // Maps to BIGINT
63
+ float fee // Maps to REAL and float32
64
+ double exchangeRate // Maps to DOUBLE PRECISION and float64
65
+ bigdecimal amount required // Maps to NUMERIC(19,4) and decimal.Decimal
66
+ bool isInternational // Maps to BOOLEAN
67
+ time cutoffTime // Maps to TIME and time.Time
68
+ datetime processedAt // Maps to TIMESTAMP and time.Time
69
+ instant createdAt // Maps to TIMESTAMP and time.Time
70
+ jsonb metadata // Maps to JSONB
71
+ json rawPayload // Maps to JSON
72
+ }
73
+ </pre>
74
+ </div>
75
+ </div>
76
+
44
77
  <!-- ELITE TYPE REFERENCE -->
45
78
  <section class="mb-20">
46
79
  <h2 class="text-3xl font-black text-slate-900 mb-8 tracking-tight underline decoration-emerald-500 underline-offset-8">Global Type System Reference</h2>
@@ -74,6 +107,21 @@ metadata JSONB
74
107
  <td class="px-6 py-4 font-mono text-slate-400">int64</td>
75
108
  <td class="px-6 py-4 font-mono text-slate-400">BIGINT</td>
76
109
  </tr>
110
+ <tr class="hover:bg-emerald-50/30 transition-colors">
111
+ <td class="px-6 py-4 font-mono font-bold text-emerald-700">float</td>
112
+ <td class="px-6 py-4 font-mono text-slate-400">float32</td>
113
+ <td class="px-6 py-4 font-mono text-slate-400">REAL</td>
114
+ </tr>
115
+ <tr class="hover:bg-emerald-50/30 transition-colors">
116
+ <td class="px-6 py-4 font-mono font-bold text-emerald-700">double</td>
117
+ <td class="px-6 py-4 font-mono text-slate-400">float64</td>
118
+ <td class="px-6 py-4 font-mono text-slate-400">DOUBLE PRECISION</td>
119
+ </tr>
120
+ <tr class="hover:bg-emerald-50/30 transition-colors">
121
+ <td class="px-6 py-4 font-mono font-bold text-emerald-700">bigdecimal</td>
122
+ <td class="px-6 py-4 font-mono text-slate-400">decimal.Decimal</td>
123
+ <td class="px-6 py-4 font-mono text-slate-400">NUMERIC(19, 4)</td>
124
+ </tr>
77
125
  <tr class="hover:bg-emerald-50/30 transition-colors">
78
126
  <td class="px-6 py-4 font-mono font-bold text-emerald-700">bool / boolean</td>
79
127
  <td class="px-6 py-4 font-mono text-slate-400">bool</td>
@@ -89,6 +137,11 @@ metadata JSONB
89
137
  <td class="px-6 py-4 font-mono text-slate-400">time.Time</td>
90
138
  <td class="px-6 py-4 font-mono text-slate-400">TIMESTAMP</td>
91
139
  </tr>
140
+ <tr class="hover:bg-emerald-50/30 transition-colors">
141
+ <td class="px-6 py-4 font-mono font-bold text-emerald-700">time</td>
142
+ <td class="px-6 py-4 font-mono text-slate-400">time.Time</td>
143
+ <td class="px-6 py-4 font-mono text-slate-400">TIME</td>
144
+ </tr>
92
145
  <tr class="hover:bg-emerald-50/30 transition-colors">
93
146
  <td class="px-6 py-4 font-mono font-bold text-emerald-700">uuid</td>
94
147
  <td class="px-6 py-4 font-mono text-slate-400">uuid.UUID</td>
@@ -5,6 +5,9 @@ import (
5
5
  {{#if (hasJson fields)}}
6
6
  "gorm.io/datatypes"
7
7
  {{/if}}
8
+ {{#if (hasDecimal fields)}}
9
+ "github.com/shopspring/decimal"
10
+ {{/if}}
8
11
  )
9
12
 
10
13
  type {{name}} struct {