mycontext-cli 4.2.7 → 4.2.10

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 (98) hide show
  1. package/dist/commands/agent.d.ts.map +1 -1
  2. package/dist/commands/agent.js +10 -1
  3. package/dist/commands/agent.js.map +1 -1
  4. package/dist/commands/generate.d.ts.map +1 -1
  5. package/dist/commands/generate.js +19 -60
  6. package/dist/commands/generate.js.map +1 -1
  7. package/dist/commands/init-interactive.d.ts +20 -0
  8. package/dist/commands/init-interactive.d.ts.map +1 -1
  9. package/dist/commands/init-interactive.js +168 -5
  10. package/dist/commands/init-interactive.js.map +1 -1
  11. package/dist/config/shadcn-catalog.json +93 -0
  12. package/dist/core/brain/BrainClient.d.ts +1 -1
  13. package/dist/core/brain/BrainClient.d.ts.map +1 -1
  14. package/dist/core/brain/BrainClient.js +5 -5
  15. package/dist/core/brain/BrainClient.js.map +1 -1
  16. package/dist/doctor/DoctorEngine.d.ts.map +1 -1
  17. package/dist/doctor/DoctorEngine.js +21 -11
  18. package/dist/doctor/DoctorEngine.js.map +1 -1
  19. package/dist/doctor/rules/dead-code-rules.d.ts.map +1 -1
  20. package/dist/doctor/rules/dead-code-rules.js +33 -0
  21. package/dist/doctor/rules/dead-code-rules.js.map +1 -1
  22. package/dist/doctor/rules/instantdb-rules.d.ts.map +1 -1
  23. package/dist/doctor/rules/instantdb-rules.js +278 -69
  24. package/dist/doctor/rules/instantdb-rules.js.map +1 -1
  25. package/dist/doctor/rules/nextjs-rules.d.ts.map +1 -1
  26. package/dist/doctor/rules/nextjs-rules.js +53 -3
  27. package/dist/doctor/rules/nextjs-rules.js.map +1 -1
  28. package/dist/package.json +4 -2
  29. package/dist/services/ComponentInferenceEngine.d.ts +66 -0
  30. package/dist/services/ComponentInferenceEngine.d.ts.map +1 -0
  31. package/dist/services/ComponentInferenceEngine.js +302 -0
  32. package/dist/services/ComponentInferenceEngine.js.map +1 -0
  33. package/dist/services/ComponentRegistry.d.ts +61 -0
  34. package/dist/services/ComponentRegistry.d.ts.map +1 -0
  35. package/dist/services/ComponentRegistry.js +128 -0
  36. package/dist/services/ComponentRegistry.js.map +1 -0
  37. package/dist/services/InferenceEngine.js +1 -1
  38. package/dist/services/ProjectScanner.d.ts.map +1 -1
  39. package/dist/services/ProjectScanner.js +18 -1
  40. package/dist/services/ProjectScanner.js.map +1 -1
  41. package/dist/services/ScaffoldEngine.d.ts +87 -0
  42. package/dist/services/ScaffoldEngine.d.ts.map +1 -0
  43. package/dist/services/ScaffoldEngine.js +409 -0
  44. package/dist/services/ScaffoldEngine.js.map +1 -0
  45. package/dist/services/ScaffoldPreview.d.ts +62 -0
  46. package/dist/services/ScaffoldPreview.d.ts.map +1 -0
  47. package/dist/services/ScaffoldPreview.js +292 -0
  48. package/dist/services/ScaffoldPreview.js.map +1 -0
  49. package/dist/services/TemplateEngine.d.ts +136 -0
  50. package/dist/services/TemplateEngine.d.ts.map +1 -0
  51. package/dist/services/TemplateEngine.js +483 -0
  52. package/dist/services/TemplateEngine.js.map +1 -0
  53. package/dist/services/TemplateHelpers.d.ts +9 -0
  54. package/dist/services/TemplateHelpers.d.ts.map +1 -0
  55. package/dist/services/TemplateHelpers.js +212 -0
  56. package/dist/services/TemplateHelpers.js.map +1 -0
  57. package/dist/templates/actions/auth-actions.ts.hbs +140 -0
  58. package/dist/templates/actions/crud-actions.ts.hbs +113 -0
  59. package/dist/templates/components/auth/login-form.tsx.hbs +67 -0
  60. package/dist/templates/components/auth/login-skeleton.tsx.hbs +24 -0
  61. package/dist/templates/components/auth/register-form.tsx.hbs +116 -0
  62. package/dist/templates/components/crud/entity-card.tsx.hbs +71 -0
  63. package/dist/templates/components/crud/entity-form.tsx.hbs +158 -0
  64. package/dist/templates/components/crud/entity-skeleton.tsx.hbs +90 -0
  65. package/dist/templates/components/crud/entity-table.tsx.hbs +129 -0
  66. package/dist/templates/components/ui/button.tsx.hbs +53 -0
  67. package/dist/templates/components/ui/card.tsx.hbs +68 -0
  68. package/dist/templates/components/ui/input.tsx.hbs +33 -0
  69. package/dist/templates/components/ui/label.tsx.hbs +20 -0
  70. package/dist/templates/components/ui/skeleton.tsx.hbs +15 -0
  71. package/dist/templates/components/ui/theme-provider.tsx.hbs +66 -0
  72. package/dist/templates/components/ui/theme-toggle.tsx.hbs +30 -0
  73. package/dist/templates/config/app.css.hbs +150 -0
  74. package/dist/templates/layouts/dashboard-layout.tsx.hbs +69 -0
  75. package/dist/templates/layouts/error.tsx.hbs +51 -0
  76. package/dist/templates/layouts/loading.tsx.hbs +22 -0
  77. package/dist/templates/layouts/not-found.tsx.hbs +24 -0
  78. package/dist/templates/layouts/root-layout.tsx.hbs +40 -0
  79. package/dist/templates/lib/instant.ts.hbs +19 -0
  80. package/dist/templates/lib/utils.ts.hbs +24 -0
  81. package/dist/templates/pages/auth/login-page.tsx.hbs +30 -0
  82. package/dist/templates/pages/auth/register-page.tsx.hbs +38 -0
  83. package/dist/templates/pages/crud/create-page.tsx.hbs +42 -0
  84. package/dist/templates/pages/crud/detail-page.tsx.hbs +90 -0
  85. package/dist/templates/pages/crud/list-page.tsx.hbs +60 -0
  86. package/dist/templates/pages/crud/loading.tsx.hbs +13 -0
  87. package/dist/templates/pages/landing-page.tsx.hbs +111 -0
  88. package/dist/types/asl.d.ts +1 -1
  89. package/dist/types/asl.d.ts.map +1 -1
  90. package/dist/types/living-context.d.ts +1 -1
  91. package/dist/types/living-context.d.ts.map +1 -1
  92. package/dist/utils/FileGenerator.js +3 -3
  93. package/dist/utils/FileGenerator.js.map +1 -1
  94. package/dist/utils/generateTypesFromSchema.d.ts +47 -0
  95. package/dist/utils/generateTypesFromSchema.d.ts.map +1 -0
  96. package/dist/utils/generateTypesFromSchema.js +298 -0
  97. package/dist/utils/generateTypesFromSchema.js.map +1 -0
  98. package/package.json +4 -2
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.TemplateHelpers = void 0;
40
+ const handlebars_1 = __importDefault(require("handlebars"));
41
+ const fs = __importStar(require("fs-extra"));
42
+ class TemplateHelpers {
43
+ constructor() {
44
+ this.handlebars = handlebars_1.default.create();
45
+ this.registerHelpers();
46
+ }
47
+ registerHelpers() {
48
+ // String transformation helpers
49
+ this.handlebars.registerHelper('lowercase', (str) => {
50
+ if (!str)
51
+ return '';
52
+ return str.toLowerCase();
53
+ });
54
+ this.handlebars.registerHelper('uppercase', (str) => {
55
+ if (!str)
56
+ return '';
57
+ return str.toUpperCase();
58
+ });
59
+ this.handlebars.registerHelper('capitalize', (str) => {
60
+ if (!str)
61
+ return '';
62
+ return str.charAt(0).toUpperCase() + str.slice(1);
63
+ });
64
+ this.handlebars.registerHelper('camelCase', (str) => {
65
+ if (!str)
66
+ return '';
67
+ return str.replace(/-([a-z])/g, (g) => (g[1] ? g[1].toUpperCase() : ''));
68
+ });
69
+ this.handlebars.registerHelper('pascalCase', (str) => {
70
+ if (!str)
71
+ return '';
72
+ const camel = str.replace(/-([a-z])/g, (g) => (g[1] ? g[1].toUpperCase() : ''));
73
+ return camel.charAt(0).toUpperCase() + camel.slice(1);
74
+ });
75
+ // Type mapping helpers for Zod
76
+ this.handlebars.registerHelper('zodType', (aslType) => {
77
+ const typeMap = {
78
+ string: 'string()',
79
+ text: 'string()',
80
+ number: 'number()',
81
+ boolean: 'boolean()',
82
+ date: 'date()',
83
+ json: 'any()',
84
+ ref: 'string()',
85
+ email: 'string().email()',
86
+ };
87
+ return typeMap[aslType] || 'string()';
88
+ });
89
+ // Type mapping helpers for TypeScript
90
+ this.handlebars.registerHelper('tsType', (aslType) => {
91
+ const typeMap = {
92
+ string: 'string',
93
+ text: 'string',
94
+ number: 'number',
95
+ boolean: 'boolean',
96
+ date: 'Date',
97
+ json: 'any',
98
+ ref: 'string',
99
+ email: 'string',
100
+ };
101
+ return typeMap[aslType] || 'string';
102
+ });
103
+ // Comparison helpers
104
+ this.handlebars.registerHelper('eq', (a, b) => {
105
+ return a === b;
106
+ });
107
+ this.handlebars.registerHelper('neq', (a, b) => {
108
+ return a !== b;
109
+ });
110
+ this.handlebars.registerHelper('gt', (a, b) => {
111
+ return a > b;
112
+ });
113
+ this.handlebars.registerHelper('lt', (a, b) => {
114
+ return a < b;
115
+ });
116
+ // Logical helpers
117
+ this.handlebars.registerHelper('and', (...args) => {
118
+ // Remove the Handlebars options object (last argument)
119
+ const values = args.slice(0, -1);
120
+ return values.every(Boolean);
121
+ });
122
+ this.handlebars.registerHelper('or', (...args) => {
123
+ // Remove the Handlebars options object (last argument)
124
+ const values = args.slice(0, -1);
125
+ return values.some(Boolean);
126
+ });
127
+ this.handlebars.registerHelper('not', (value) => {
128
+ return !value;
129
+ });
130
+ // Array/iteration helpers
131
+ this.handlebars.registerHelper('join', (arr, separator = ', ') => {
132
+ if (!Array.isArray(arr))
133
+ return '';
134
+ return arr.join(separator);
135
+ });
136
+ this.handlebars.registerHelper('length', (arr) => {
137
+ if (!Array.isArray(arr))
138
+ return 0;
139
+ return arr.length;
140
+ });
141
+ // Index helpers for iterations
142
+ this.handlebars.registerHelper('isFirst', function (index) {
143
+ return index === 0;
144
+ });
145
+ this.handlebars.registerHelper('isLast', function (index, array) {
146
+ return index === array.length - 1;
147
+ });
148
+ // JSON helpers
149
+ this.handlebars.registerHelper('json', (context) => {
150
+ return JSON.stringify(context, null, 2);
151
+ });
152
+ // Conditional helpers for field types
153
+ this.handlebars.registerHelper('isTextField', (type) => {
154
+ return type === 'string' || type === 'text' || type === 'email';
155
+ });
156
+ this.handlebars.registerHelper('isNumberField', (type) => {
157
+ return type === 'number';
158
+ });
159
+ this.handlebars.registerHelper('isBooleanField', (type) => {
160
+ return type === 'boolean';
161
+ });
162
+ this.handlebars.registerHelper('isDateField', (type) => {
163
+ return type === 'date';
164
+ });
165
+ // Pluralization helper
166
+ this.handlebars.registerHelper('pluralize', (str) => {
167
+ if (!str)
168
+ return '';
169
+ // Simple pluralization logic
170
+ if (str.endsWith('y')) {
171
+ return str.slice(0, -1) + 'ies';
172
+ }
173
+ if (str.endsWith('s') || str.endsWith('sh') || str.endsWith('ch')) {
174
+ return str + 'es';
175
+ }
176
+ return str + 's';
177
+ });
178
+ // Singularize helper
179
+ this.handlebars.registerHelper('singularize', (str) => {
180
+ if (!str)
181
+ return '';
182
+ // Simple singularization logic
183
+ if (str.endsWith('ies')) {
184
+ return str.slice(0, -3) + 'y';
185
+ }
186
+ if (str.endsWith('es')) {
187
+ return str.slice(0, -2);
188
+ }
189
+ if (str.endsWith('s')) {
190
+ return str.slice(0, -1);
191
+ }
192
+ return str;
193
+ });
194
+ }
195
+ async render(templatePath, data) {
196
+ const templateContent = await fs.readFile(templatePath, 'utf-8');
197
+ const template = this.handlebars.compile(templateContent);
198
+ return template(data);
199
+ }
200
+ async renderFromString(templateString, data) {
201
+ const template = this.handlebars.compile(templateString);
202
+ return template(data);
203
+ }
204
+ // Helper method to get file extension from template path
205
+ getOutputExtension(templatePath) {
206
+ // Extract extension from .tsx.hbs or .ts.hbs or .css.hbs
207
+ const match = templatePath.match(/\.([^.]+)\.hbs$/);
208
+ return match ? `.${match[1]}` : '';
209
+ }
210
+ }
211
+ exports.TemplateHelpers = TemplateHelpers;
212
+ //# sourceMappingURL=TemplateHelpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TemplateHelpers.js","sourceRoot":"","sources":["../../src/services/TemplateHelpers.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,4DAAmC;AACnC,6CAA8B;AAG9B,MAAa,eAAe;IAG1B;QACE,IAAI,CAAC,UAAU,GAAG,oBAAU,CAAC,MAAM,EAAE,CAAA;QACrC,IAAI,CAAC,eAAe,EAAE,CAAA;IACxB,CAAC;IAEO,eAAe;QACrB,gCAAgC;QAChC,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,GAAW,EAAE,EAAE;YAC1D,IAAI,CAAC,GAAG;gBAAE,OAAO,EAAE,CAAA;YACnB,OAAO,GAAG,CAAC,WAAW,EAAE,CAAA;QAC1B,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,GAAW,EAAE,EAAE;YAC1D,IAAI,CAAC,GAAG;gBAAE,OAAO,EAAE,CAAA;YACnB,OAAO,GAAG,CAAC,WAAW,EAAE,CAAA;QAC1B,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,YAAY,EAAE,CAAC,GAAW,EAAE,EAAE;YAC3D,IAAI,CAAC,GAAG;gBAAE,OAAO,EAAE,CAAA;YACnB,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QACnD,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,GAAW,EAAE,EAAE;YAC1D,IAAI,CAAC,GAAG;gBAAE,OAAO,EAAE,CAAA;YACnB,OAAO,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QAClF,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,YAAY,EAAE,CAAC,GAAW,EAAE,EAAE;YAC3D,IAAI,CAAC,GAAG;gBAAE,OAAO,EAAE,CAAA;YACnB,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;YACvF,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAA;QACvD,CAAC,CAAC,CAAA;QAEF,+BAA+B;QAC/B,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,OAAe,EAAE,EAAE;YAC5D,MAAM,OAAO,GAA2B;gBACtC,MAAM,EAAE,UAAU;gBAClB,IAAI,EAAE,UAAU;gBAChB,MAAM,EAAE,UAAU;gBAClB,OAAO,EAAE,WAAW;gBACpB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,OAAO;gBACb,GAAG,EAAE,UAAU;gBACf,KAAK,EAAE,kBAAkB;aAC1B,CAAA;YACD,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,UAAU,CAAA;QACvC,CAAC,CAAC,CAAA;QAEF,sCAAsC;QACtC,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,QAAQ,EAAE,CAAC,OAAe,EAAE,EAAE;YAC3D,MAAM,OAAO,GAA2B;gBACtC,MAAM,EAAE,QAAQ;gBAChB,IAAI,EAAE,QAAQ;gBACd,MAAM,EAAE,QAAQ;gBAChB,OAAO,EAAE,SAAS;gBAClB,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,KAAK;gBACX,GAAG,EAAE,QAAQ;gBACb,KAAK,EAAE,QAAQ;aAChB,CAAA;YACD,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAA;QACrC,CAAC,CAAC,CAAA;QAEF,qBAAqB;QACrB,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAM,EAAE,CAAM,EAAE,EAAE;YACtD,OAAO,CAAC,KAAK,CAAC,CAAA;QAChB,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC,CAAM,EAAE,CAAM,EAAE,EAAE;YACvD,OAAO,CAAC,KAAK,CAAC,CAAA;QAChB,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE;YAC5D,OAAO,CAAC,GAAG,CAAC,CAAA;QACd,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,CAAS,EAAE,CAAS,EAAE,EAAE;YAC5D,OAAO,CAAC,GAAG,CAAC,CAAA;QACd,CAAC,CAAC,CAAA;QAEF,kBAAkB;QAClB,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC,GAAG,IAAW,EAAE,EAAE;YACvD,uDAAuD;YACvD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;YAChC,OAAO,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;QAC9B,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,GAAG,IAAW,EAAE,EAAE;YACtD,uDAAuD;YACvD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;YAChC,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;QAC7B,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC,KAAU,EAAE,EAAE;YACnD,OAAO,CAAC,KAAK,CAAA;QACf,CAAC,CAAC,CAAA;QAEF,0BAA0B;QAC1B,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,GAAU,EAAE,YAAoB,IAAI,EAAE,EAAE;YAC9E,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;gBAAE,OAAO,EAAE,CAAA;YAClC,OAAO,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QAC5B,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,QAAQ,EAAE,CAAC,GAAU,EAAE,EAAE;YACtD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;gBAAE,OAAO,CAAC,CAAA;YACjC,OAAO,GAAG,CAAC,MAAM,CAAA;QACnB,CAAC,CAAC,CAAA;QAEF,+BAA+B;QAC/B,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,SAAS,EAAE,UAAqB,KAAa;YAC1E,OAAO,KAAK,KAAK,CAAC,CAAA;QACpB,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,QAAQ,EAAE,UAAqB,KAAa,EAAE,KAAY;YACvF,OAAO,KAAK,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,CAAA;QACnC,CAAC,CAAC,CAAA;QAEF,eAAe;QACf,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,OAAY,EAAE,EAAE;YACtD,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;QACzC,CAAC,CAAC,CAAA;QAEF,sCAAsC;QACtC,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC,IAAY,EAAE,EAAE;YAC7D,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,MAAM,IAAI,IAAI,KAAK,OAAO,CAAA;QACjE,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,eAAe,EAAE,CAAC,IAAY,EAAE,EAAE;YAC/D,OAAO,IAAI,KAAK,QAAQ,CAAA;QAC1B,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,gBAAgB,EAAE,CAAC,IAAY,EAAE,EAAE;YAChE,OAAO,IAAI,KAAK,SAAS,CAAA;QAC3B,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC,IAAY,EAAE,EAAE;YAC7D,OAAO,IAAI,KAAK,MAAM,CAAA;QACxB,CAAC,CAAC,CAAA;QAEF,uBAAuB;QACvB,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,GAAW,EAAE,EAAE;YAC1D,IAAI,CAAC,GAAG;gBAAE,OAAO,EAAE,CAAA;YACnB,6BAA6B;YAC7B,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtB,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAA;YACjC,CAAC;YACD,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClE,OAAO,GAAG,GAAG,IAAI,CAAA;YACnB,CAAC;YACD,OAAO,GAAG,GAAG,GAAG,CAAA;QAClB,CAAC,CAAC,CAAA;QAEF,qBAAqB;QACrB,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC,GAAW,EAAE,EAAE;YAC5D,IAAI,CAAC,GAAG;gBAAE,OAAO,EAAE,CAAA;YACnB,+BAA+B;YAC/B,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBACxB,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,GAAG,CAAA;YAC/B,CAAC;YACD,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBACvB,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;YACzB,CAAC;YACD,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBACtB,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;YACzB,CAAC;YACD,OAAO,GAAG,CAAA;QACZ,CAAC,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,YAAoB,EAAE,IAAS;QAC1C,MAAM,eAAe,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;QAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,eAAe,CAAC,CAAA;QACzD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAA;IACvB,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,cAAsB,EAAE,IAAS;QACtD,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,cAAc,CAAC,CAAA;QACxD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAA;IACvB,CAAC;IAED,yDAAyD;IACzD,kBAAkB,CAAC,YAAoB;QACrC,yDAAyD;QACzD,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAA;QACnD,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;IACpC,CAAC;CACF;AA7LD,0CA6LC"}
@@ -0,0 +1,140 @@
1
+ 'use server'
2
+
3
+ import { revalidatePath } from 'next/cache'
4
+ import { cookies } from 'next/headers'
5
+ import { db } from '@/lib/instant'
6
+ import { z } from 'zod'
7
+
8
+ const loginSchema = z.object({
9
+ email: z.string().email('Invalid email address'),
10
+ password: z.string().min(8, 'Password must be at least 8 characters'),
11
+ })
12
+
13
+ const registerSchema = z.object({
14
+ name: z.string().min(1, 'Name is required'),
15
+ email: z.string().email('Invalid email address'),
16
+ password: z.string().min(8, 'Password must be at least 8 characters'),
17
+ })
18
+
19
+ export async function login(formData: FormData) {
20
+ try {
21
+ const validated = loginSchema.parse({
22
+ email: formData.get('email'),
23
+ password: formData.get('password'),
24
+ })
25
+
26
+ // Authenticate with InstantDB
27
+ const { user, error } = await db.auth.signInWithPassword({
28
+ email: validated.email,
29
+ password: validated.password,
30
+ })
31
+
32
+ if (error) {
33
+ return { error: error.message || 'Invalid credentials' }
34
+ }
35
+
36
+ // Store session in cookie
37
+ const cookieStore = await cookies()
38
+ cookieStore.set('session', user.id, {
39
+ httpOnly: true,
40
+ secure: process.env.NODE_ENV === 'production',
41
+ sameSite: 'lax',
42
+ maxAge: 60 * 60 * 24 * 7, // 7 days
43
+ })
44
+
45
+ revalidatePath('/')
46
+ return { success: true }
47
+ } catch (error) {
48
+ if (error instanceof z.ZodError) {
49
+ return { error: error.errors[0].message }
50
+ }
51
+ return { error: 'An unexpected error occurred' }
52
+ }
53
+ }
54
+
55
+ export async function register(formData: FormData) {
56
+ try {
57
+ const validated = registerSchema.parse({
58
+ name: formData.get('name'),
59
+ email: formData.get('email'),
60
+ password: formData.get('password'),
61
+ })
62
+
63
+ // Create account with InstantDB
64
+ const { user, error } = await db.auth.signUp({
65
+ email: validated.email,
66
+ password: validated.password,
67
+ })
68
+
69
+ if (error) {
70
+ return { error: error.message || 'Failed to create account' }
71
+ }
72
+
73
+ // Update user profile with name
74
+ await db.transact([
75
+ db.tx.users[user.id].update({
76
+ name: validated.name,
77
+ }),
78
+ ])
79
+
80
+ // Store session in cookie
81
+ const cookieStore = await cookies()
82
+ cookieStore.set('session', user.id, {
83
+ httpOnly: true,
84
+ secure: process.env.NODE_ENV === 'production',
85
+ sameSite: 'lax',
86
+ maxAge: 60 * 60 * 24 * 7, // 7 days
87
+ })
88
+
89
+ revalidatePath('/')
90
+ return { success: true }
91
+ } catch (error) {
92
+ if (error instanceof z.ZodError) {
93
+ return { error: error.errors[0].message }
94
+ }
95
+ return { error: 'An unexpected error occurred' }
96
+ }
97
+ }
98
+
99
+ export async function logout() {
100
+ try {
101
+ await db.auth.signOut()
102
+
103
+ const cookieStore = await cookies()
104
+ cookieStore.delete('session')
105
+
106
+ revalidatePath('/')
107
+ return { success: true }
108
+ } catch (error) {
109
+ return { error: 'Failed to log out' }
110
+ }
111
+ }
112
+
113
+ export async function getCurrentUser() {
114
+ try {
115
+ const cookieStore = await cookies()
116
+ const sessionCookie = cookieStore.get('session')
117
+
118
+ if (!sessionCookie) {
119
+ return { user: null }
120
+ }
121
+
122
+ const { data, error } = await db.query({
123
+ users: {
124
+ $: {
125
+ where: {
126
+ id: sessionCookie.value,
127
+ },
128
+ },
129
+ },
130
+ })
131
+
132
+ if (error || !data.users?.[0]) {
133
+ return { user: null }
134
+ }
135
+
136
+ return { user: data.users[0] }
137
+ } catch (error) {
138
+ return { user: null }
139
+ }
140
+ }
@@ -0,0 +1,113 @@
1
+ 'use server'
2
+
3
+ import { revalidatePath } from 'next/cache'
4
+ import { db } from '@/lib/instant'
5
+ import { z } from 'zod'
6
+
7
+ const {{entityLower}}Schema = z.object({
8
+ {{#each fields}}
9
+ {{name}}: z.{{zodType type}}(){{#if required}}.min(1, '{{capitalize name}} is required'){{/if}},
10
+ {{/each}}
11
+ })
12
+
13
+ export async function create{{entityName}}(formData: FormData) {
14
+ try {
15
+ const validated = {{entityLower}}Schema.parse({
16
+ {{#each fields}}
17
+ {{name}}: formData.get('{{name}}'),
18
+ {{/each}}
19
+ })
20
+
21
+ const id = db.id()
22
+
23
+ await db.transact([
24
+ db.tx.{{entityName}}[id].update(validated)
25
+ ])
26
+
27
+ revalidatePath('/{{entityLower}}s')
28
+ revalidatePath(`/{{entityLower}}s/${id}`)
29
+
30
+ return { success: true, id }
31
+ } catch (error) {
32
+ if (error instanceof z.ZodError) {
33
+ return { success: false, error: error.errors[0].message }
34
+ }
35
+ return { success: false, error: 'Failed to create {{entityLower}}' }
36
+ }
37
+ }
38
+
39
+ export async function update{{entityName}}(id: string, formData: FormData) {
40
+ try {
41
+ const validated = {{entityLower}}Schema.parse({
42
+ {{#each fields}}
43
+ {{name}}: formData.get('{{name}}'),
44
+ {{/each}}
45
+ })
46
+
47
+ await db.transact([
48
+ db.tx.{{entityName}}[id].update(validated)
49
+ ])
50
+
51
+ revalidatePath('/{{entityLower}}s')
52
+ revalidatePath(`/{{entityLower}}s/${id}`)
53
+
54
+ return { success: true }
55
+ } catch (error) {
56
+ if (error instanceof z.ZodError) {
57
+ return { success: false, error: error.errors[0].message }
58
+ }
59
+ return { success: false, error: 'Failed to update {{entityLower}}' }
60
+ }
61
+ }
62
+
63
+ export async function delete{{entityName}}(id: string) {
64
+ try {
65
+ await db.transact([
66
+ db.tx.{{entityName}}[id].delete()
67
+ ])
68
+
69
+ revalidatePath('/{{entityLower}}s')
70
+
71
+ return { success: true }
72
+ } catch (error) {
73
+ return { success: false, error: 'Failed to delete {{entityLower}}' }
74
+ }
75
+ }
76
+
77
+ export async function get{{entityName}}(id: string) {
78
+ try {
79
+ const { data, error } = await db.query({
80
+ {{entityLower}}s: {
81
+ $: {
82
+ where: {
83
+ id,
84
+ },
85
+ },
86
+ },
87
+ })
88
+
89
+ if (error || !data.{{entityLower}}s?.[0]) {
90
+ return { {{entityLower}}: null, error: 'Not found' }
91
+ }
92
+
93
+ return { {{entityLower}}: data.{{entityLower}}s[0] }
94
+ } catch (error) {
95
+ return { {{entityLower}}: null, error: 'Failed to fetch {{entityLower}}' }
96
+ }
97
+ }
98
+
99
+ export async function list{{entityName}}s() {
100
+ try {
101
+ const { data, error } = await db.query({
102
+ {{entityLower}}s: {},
103
+ })
104
+
105
+ if (error) {
106
+ return { {{entityLower}}s: [], error: error.message }
107
+ }
108
+
109
+ return { {{entityLower}}s: data.{{entityLower}}s || [] }
110
+ } catch (error) {
111
+ return { {{entityLower}}s: [], error: 'Failed to fetch {{entityLower}}s' }
112
+ }
113
+ }
@@ -0,0 +1,67 @@
1
+ 'use client'
2
+
3
+ import { useState, useTransition } from 'react'
4
+ import { useRouter } from 'next/navigation'
5
+ import { Button } from '@/components/ui/button'
6
+ import { Input } from '@/components/ui/input'
7
+ import { Label } from '@/components/ui/label'
8
+ import { login } from '@/app/actions/auth'
9
+
10
+ export function LoginForm() {
11
+ const router = useRouter()
12
+ const [isPending, startTransition] = useTransition()
13
+ const [error, setError] = useState<string | null>(null)
14
+
15
+ const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
16
+ e.preventDefault()
17
+ setError(null)
18
+
19
+ const formData = new FormData(e.currentTarget)
20
+
21
+ startTransition(async () => {
22
+ const result = await login(formData)
23
+ if (result.error) {
24
+ setError(result.error)
25
+ } else {
26
+ router.push('/dashboard')
27
+ }
28
+ })
29
+ }
30
+
31
+ return (
32
+ <form onSubmit={handleSubmit} className="space-y-4">
33
+ <div className="space-y-2">
34
+ <Label htmlFor="email">Email</Label>
35
+ <Input
36
+ id="email"
37
+ name="email"
38
+ type="email"
39
+ placeholder="you@example.com"
40
+ required
41
+ disabled={isPending}
42
+ />
43
+ </div>
44
+
45
+ <div className="space-y-2">
46
+ <Label htmlFor="password">Password</Label>
47
+ <Input
48
+ id="password"
49
+ name="password"
50
+ type="password"
51
+ required
52
+ disabled={isPending}
53
+ />
54
+ </div>
55
+
56
+ {error && (
57
+ <p className="text-sm text-destructive" role="alert">
58
+ {error}
59
+ </p>
60
+ )}
61
+
62
+ <Button type="submit" className="w-full" disabled={isPending}>
63
+ {isPending ? 'Signing in...' : 'Sign In'}
64
+ </Button>
65
+ </form>
66
+ )
67
+ }
@@ -0,0 +1,24 @@
1
+ import { Skeleton } from '@/components/ui/skeleton'
2
+ import { Card, CardContent, CardHeader } from '@/components/ui/card'
3
+
4
+ export function LoginSkeleton() {
5
+ return (
6
+ <Card>
7
+ <CardHeader>
8
+ <Skeleton className="h-8 w-3/4" />
9
+ <Skeleton className="h-4 w-full" />
10
+ </CardHeader>
11
+ <CardContent className="space-y-4">
12
+ <div className="space-y-2">
13
+ <Skeleton className="h-4 w-16" />
14
+ <Skeleton className="h-10 w-full" />
15
+ </div>
16
+ <div className="space-y-2">
17
+ <Skeleton className="h-4 w-20" />
18
+ <Skeleton className="h-10 w-full" />
19
+ </div>
20
+ <Skeleton className="h-10 w-full" />
21
+ </CardContent>
22
+ </Card>
23
+ )
24
+ }
@@ -0,0 +1,116 @@
1
+ 'use client'
2
+
3
+ import { useState, useTransition } from 'react'
4
+ import { useRouter } from 'next/navigation'
5
+ import { Button } from '@/components/ui/button'
6
+ import { Input } from '@/components/ui/input'
7
+ import { Label } from '@/components/ui/label'
8
+ import { register } from '@/app/actions/auth'
9
+
10
+ export function RegisterForm() {
11
+ const router = useRouter()
12
+ const [isPending, startTransition] = useTransition()
13
+ const [error, setError] = useState<string | null>(null)
14
+
15
+ const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
16
+ e.preventDefault()
17
+ setError(null)
18
+
19
+ const formData = new FormData(e.currentTarget)
20
+
21
+ // Client-side password confirmation validation
22
+ const password = formData.get('password') as string
23
+ const confirmPassword = formData.get('confirmPassword') as string
24
+
25
+ if (password !== confirmPassword) {
26
+ setError('Passwords do not match')
27
+ return
28
+ }
29
+
30
+ startTransition(async () => {
31
+ const result = await register(formData)
32
+ if (result.error) {
33
+ setError(result.error)
34
+ } else {
35
+ router.push('/dashboard')
36
+ }
37
+ })
38
+ }
39
+
40
+ return (
41
+ <form onSubmit={handleSubmit} className="space-y-4">
42
+ <div className="space-y-2">
43
+ <Label htmlFor="name" required>
44
+ Name
45
+ </Label>
46
+ <Input
47
+ id="name"
48
+ name="name"
49
+ type="text"
50
+ placeholder="John Doe"
51
+ required
52
+ disabled={isPending}
53
+ autoComplete="name"
54
+ />
55
+ </div>
56
+
57
+ <div className="space-y-2">
58
+ <Label htmlFor="email" required>
59
+ Email
60
+ </Label>
61
+ <Input
62
+ id="email"
63
+ name="email"
64
+ type="email"
65
+ placeholder="you@example.com"
66
+ required
67
+ disabled={isPending}
68
+ autoComplete="email"
69
+ />
70
+ </div>
71
+
72
+ <div className="space-y-2">
73
+ <Label htmlFor="password" required>
74
+ Password
75
+ </Label>
76
+ <Input
77
+ id="password"
78
+ name="password"
79
+ type="password"
80
+ required
81
+ disabled={isPending}
82
+ autoComplete="new-password"
83
+ minLength={8}
84
+ />
85
+ <p className="text-xs text-muted-foreground">
86
+ Must be at least 8 characters
87
+ </p>
88
+ </div>
89
+
90
+ <div className="space-y-2">
91
+ <Label htmlFor="confirmPassword" required>
92
+ Confirm Password
93
+ </Label>
94
+ <Input
95
+ id="confirmPassword"
96
+ name="confirmPassword"
97
+ type="password"
98
+ required
99
+ disabled={isPending}
100
+ autoComplete="new-password"
101
+ minLength={8}
102
+ />
103
+ </div>
104
+
105
+ {error && (
106
+ <p className="text-sm text-destructive" role="alert">
107
+ {error}
108
+ </p>
109
+ )}
110
+
111
+ <Button type="submit" className="w-full" disabled={isPending}>
112
+ {isPending ? 'Creating account...' : 'Create Account'}
113
+ </Button>
114
+ </form>
115
+ )
116
+ }