@akanjs/cli 2.4.0-rc.9 → 2.4.1-rc.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 (69) hide show
  1. package/agent.command-h4afc69n.js +26 -0
  2. package/application.command-4ctkfdan.js +165 -0
  3. package/applicationBuildRunner-esa1kj7v.js +284 -0
  4. package/applicationReleasePackager-brvth6rs.js +245 -0
  5. package/capacitorApp-y0h6cgft.js +58 -0
  6. package/cloud.command-rpztn4fh.js +94 -0
  7. package/commandManifest.json +1 -0
  8. package/context.command-nqbtak4f.js +82 -0
  9. package/dependencyScanner-m4x5maek.js +9 -0
  10. package/guideline.command-ya0dh44f.js +356 -0
  11. package/incrementalBuilder.proc.js +89 -17394
  12. package/index-1xdrsbry.js +1447 -0
  13. package/index-45aj5ry0.js +990 -0
  14. package/index-5vvwc0cz.js +559 -0
  15. package/index-61keag0s.js +40 -0
  16. package/index-6pz1j0zj.js +62 -0
  17. package/index-73pr2cmy.js +534 -0
  18. package/index-76rn3g2c.js +76 -0
  19. package/index-85msc0wg.js +161 -0
  20. package/index-8pkbzj26.js +840 -0
  21. package/index-8rc0bm04.js +514 -0
  22. package/index-9sp6fsc5.js +1884 -0
  23. package/index-a6sbyy0b.js +2769 -0
  24. package/index-b0brjbp3.js +83 -0
  25. package/index-fgc8r6dj.js +33 -0
  26. package/index-h6ca6qg0.js +2777 -0
  27. package/index-hdqztm58.js +758 -0
  28. package/index-hwzpw9c1.js +202 -0
  29. package/index-pmm9e2jf.js +120 -0
  30. package/index-qaq13qk3.js +80 -0
  31. package/index-qhtr07v8.js +1072 -0
  32. package/index-r24hmh0q.js +4 -0
  33. package/index-ss469dec.js +11 -0
  34. package/index-swf4bmbg.js +25 -0
  35. package/index-w7fyqjrw.js +462 -0
  36. package/index-wnp7hwq7.js +193 -0
  37. package/index-wq8jwx8z.js +224 -0
  38. package/index-x53a5nya.js +301 -0
  39. package/index-xmc2w32q.js +4359 -0
  40. package/index-y3hdhy4p.js +229 -0
  41. package/index.js +54 -23340
  42. package/library.command-r15zdqvp.js +33 -0
  43. package/localRegistry.command-5tcahs3f.js +178 -0
  44. package/module.command-qrj3kmyz.js +54 -0
  45. package/package.command-r8sq5kzp.js +43 -0
  46. package/package.json +2 -2
  47. package/page.command-c6xdx0xm.js +24 -0
  48. package/primitive.command-pv9ssmtf.js +62 -0
  49. package/quality.command-es67wvdp.js +809 -0
  50. package/repair.command-677675vw.js +67 -0
  51. package/routeSourceValidator-wbhmbwpj.js +132 -0
  52. package/scalar.command-kabkd6wd.js +35 -0
  53. package/templates/app/page/_layout.tsx +19 -1
  54. package/templates/app/public/fonts/Pretendard-Black.woff2 +0 -0
  55. package/templates/app/public/fonts/Pretendard-Bold.woff2 +0 -0
  56. package/templates/app/public/fonts/Pretendard-ExtraBold.woff2 +0 -0
  57. package/templates/app/public/fonts/Pretendard-ExtraLight.woff2 +0 -0
  58. package/templates/app/public/fonts/Pretendard-Light.woff2 +0 -0
  59. package/templates/app/public/fonts/Pretendard-Medium.woff2 +0 -0
  60. package/templates/app/public/fonts/Pretendard-Regular.woff2 +0 -0
  61. package/templates/app/public/fonts/Pretendard-SemiBold.woff2 +0 -0
  62. package/templates/app/public/fonts/Pretendard-Thin.woff2 +0 -0
  63. package/templates/facetIndex/index.ts +1 -1
  64. package/typeChecker-kravn7ns.js +8 -0
  65. package/typecheck.proc.js +4 -194
  66. package/workflow.command-64r6cw0w.js +108 -0
  67. package/workspace.command-vrws0rgx.js +435 -0
  68. package/README.ko.md +0 -72
  69. package/README.md +0 -85
@@ -0,0 +1,840 @@
1
+ // @bun
2
+ import {
3
+ PageScript
4
+ } from "./index-swf4bmbg.js";
5
+ import {
6
+ pluralizeName
7
+ } from "./index-ss469dec.js";
8
+ import {
9
+ Prompter
10
+ } from "./index-qaq13qk3.js";
11
+ import {
12
+ bilingualDescriptionForField,
13
+ bilingualLabelForField,
14
+ createPassedPrimitiveReport,
15
+ generatedFilesForSync,
16
+ moduleSourcePaths,
17
+ sourceFile
18
+ } from "./index-h6ca6qg0.js";
19
+ import {
20
+ AiSession
21
+ } from "./index-5vvwc0cz.js";
22
+ import {
23
+ runner,
24
+ script
25
+ } from "./index-hdqztm58.js";
26
+ import {
27
+ ModuleExecutor
28
+ } from "./index-a6sbyy0b.js";
29
+ import {
30
+ FileSys
31
+ } from "./index-61keag0s.js";
32
+
33
+ // pkgs/@akanjs/devkit/getRelatedCnsts.ts
34
+ import { readFileSync, realpathSync } from "fs";
35
+ import ora from "ora";
36
+ import * as ts from "typescript";
37
+ var tsTranspiler = new Bun.Transpiler({ loader: "ts" });
38
+ var tsxTranspiler = new Bun.Transpiler({ loader: "tsx" });
39
+ var getTranspiler = (filePath) => filePath.endsWith(".tsx") ? tsxTranspiler : tsTranspiler;
40
+ var scanModuleSpecifiers = (source, filePath, includeExports) => {
41
+ const scannedImports = getTranspiler(filePath).scanImports(source).map((imp) => imp.path).filter(Boolean);
42
+ if (includeExports) {
43
+ const specifiers = new Set(scannedImports);
44
+ const typeOnlyModuleRegex = /\b(?:import|export)\s+type\s+[\s\S]*?\s+from\s*["']([^"']+)["']/g;
45
+ for (const match of source.matchAll(typeOnlyModuleRegex)) {
46
+ const importPath = match[1];
47
+ if (importPath)
48
+ specifiers.add(importPath);
49
+ }
50
+ const exportFromRegex = /\bexport\s+(?:type\s+)?(?:\*|{[\s\S]*?})\s+from\s*["']([^"']+)["']/g;
51
+ for (const match of source.matchAll(exportFromRegex)) {
52
+ const importPath = match[1];
53
+ if (importPath)
54
+ specifiers.add(importPath);
55
+ }
56
+ return specifiers;
57
+ }
58
+ const importSpecifiers = new Set;
59
+ const importDeclarationRegex = /\bimport\s+(?:type\s+)?(?:["']([^"']+)["']|[\s\S]*?\s+from\s*["']([^"']+)["'])/g;
60
+ for (const match of source.matchAll(importDeclarationRegex)) {
61
+ const importPath = match[1] ?? match[2];
62
+ if (importPath && (scannedImports.includes(importPath) || match[0].startsWith("import type"))) {
63
+ importSpecifiers.add(importPath);
64
+ }
65
+ }
66
+ return importSpecifiers;
67
+ };
68
+ var parseTsConfig = (tsConfigPath = "./tsconfig.json") => {
69
+ const configFile = ts.readConfigFile(tsConfigPath, (path) => {
70
+ return ts.sys.readFile(path);
71
+ });
72
+ return ts.parseJsonConfigFileContent(configFile.config, ts.sys, realpathSync(tsConfigPath).replace(/[^/\\]+$/, ""));
73
+ };
74
+ var collectImportedFiles = (constantFilePath, parsedConfig) => {
75
+ const allFilesToAnalyze = new Set([constantFilePath]);
76
+ const analyzedFiles = new Set;
77
+ const spinner = ora("Collecting related files...");
78
+ spinner.start();
79
+ function collectImported(filePath) {
80
+ if (analyzedFiles.has(filePath))
81
+ return;
82
+ analyzedFiles.add(filePath);
83
+ const source = readFileSync(filePath, "utf-8");
84
+ for (const importPath of scanModuleSpecifiers(source, filePath, false)) {
85
+ if (!importPath.startsWith("."))
86
+ continue;
87
+ const resolved = ts.resolveModuleName(importPath, filePath, parsedConfig.options, ts.sys).resolvedModule?.resolvedFileName;
88
+ if (resolved && !allFilesToAnalyze.has(resolved)) {
89
+ allFilesToAnalyze.add(resolved);
90
+ collectImported(resolved);
91
+ }
92
+ }
93
+ }
94
+ collectImported(constantFilePath);
95
+ spinner.succeed(`Found ${allFilesToAnalyze.size} related files.`);
96
+ return {
97
+ allFilesToAnalyze,
98
+ analyzedFiles
99
+ };
100
+ };
101
+ var createTsProgram = (filePaths, options) => {
102
+ const spinner = ora("Creating TypeScript program for all files...");
103
+ spinner.start();
104
+ const program = ts.createProgram(Array.from(filePaths), options);
105
+ const checker = program.getTypeChecker();
106
+ spinner.succeed("TypeScript program created.");
107
+ return {
108
+ program,
109
+ checker
110
+ };
111
+ };
112
+ var createSymbolCache = (checker) => {
113
+ const symbolCache = new Map;
114
+ return (node) => {
115
+ const cacheKey = `${node.getSourceFile().fileName}:${node.pos}:${node.end}`;
116
+ if (!symbolCache.has(cacheKey)) {
117
+ symbolCache.set(cacheKey, checker.getSymbolAtLocation(node));
118
+ }
119
+ return symbolCache.get(cacheKey);
120
+ };
121
+ };
122
+ var analyzeProperties = (filesToAnalyze, program, checker) => {
123
+ const propertyMap = new Map;
124
+ const analyzedFiles = new Set;
125
+ const sourceLineCache = new Map;
126
+ const getCachedSymbol = createSymbolCache(checker);
127
+ const spinner = ora("Analyzing property relationships...");
128
+ spinner.start();
129
+ function analyzeFileProperties(filePath) {
130
+ if (analyzedFiles.has(filePath))
131
+ return;
132
+ analyzedFiles.add(filePath);
133
+ const source = program.getSourceFile(filePath);
134
+ if (!source)
135
+ return;
136
+ if (!sourceLineCache.has(filePath)) {
137
+ sourceLineCache.set(filePath, source.getFullText().split(`
138
+ `));
139
+ }
140
+ const sourceLines = sourceLineCache.get(filePath);
141
+ function visit(node) {
142
+ if (!source)
143
+ return;
144
+ if (ts.isPropertyAccessExpression(node)) {
145
+ const left = node.expression;
146
+ const right = node.name;
147
+ const { line } = ts.getLineAndCharacterOfPosition(source, node.getStart());
148
+ if (ts.isIdentifier(left) && sourceLines && sourceLines.length > line && sourceLines[line] && (sourceLines[line]?.includes(`@Field.Prop(() => ${left.text}.${right.text}`) || sourceLines[line].includes(`base.Filter(${left.text}.${right.text},`))) {
149
+ const symbol = getCachedSymbol(right);
150
+ if (symbol?.declarations && symbol.declarations.length > 0) {
151
+ const key = symbol.declarations[0]?.getSourceFile().fileName.split("/").pop()?.split(".")[0] ?? "";
152
+ const property = propertyMap.get(key);
153
+ const isScalar = symbol.declarations[0]?.getSourceFile().fileName.includes("_") ?? false;
154
+ const symbolFilePath = symbol.declarations[0]?.getSourceFile().fileName.replace(`${ts.sys.getCurrentDirectory()}/`, "");
155
+ if (!symbolFilePath)
156
+ throw new Error(`No symbol file path found for ${left.text}.${right.text}`);
157
+ if (property) {
158
+ propertyMap.set(`${left.text}.${right.text}`, {
159
+ filePath: symbolFilePath,
160
+ isLibModule: true,
161
+ isImport: false,
162
+ libName: left.text,
163
+ source: readFileSync(symbolFilePath, "utf-8"),
164
+ isScalar
165
+ });
166
+ } else {
167
+ propertyMap.set(key, {
168
+ filePath: symbolFilePath,
169
+ isLibModule: true,
170
+ isImport: false,
171
+ libName: left.text,
172
+ isScalar,
173
+ source: readFileSync(symbolFilePath, "utf-8")
174
+ });
175
+ }
176
+ }
177
+ }
178
+ } else if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
179
+ const importPath = node.moduleSpecifier.text;
180
+ if (importPath.startsWith(".")) {
181
+ const resolved = ts.resolveModuleName(importPath, filePath, program.getCompilerOptions(), ts.sys).resolvedModule?.resolvedFileName;
182
+ const moduleName = importPath.split("/").pop()?.split(".")[0] ?? "";
183
+ const property = propertyMap.get(moduleName);
184
+ const isScalar = importPath.includes("_");
185
+ if (moduleName && resolved && (!property || property.filePath !== resolved)) {
186
+ propertyMap.set(moduleName, {
187
+ filePath: resolved,
188
+ isLibModule: false,
189
+ isImport: true,
190
+ isScalar,
191
+ source: readFileSync(resolved, "utf-8")
192
+ });
193
+ }
194
+ }
195
+ }
196
+ ts.forEachChild(node, visit);
197
+ }
198
+ visit(source);
199
+ }
200
+ for (const filePath of filesToAnalyze) {
201
+ analyzeFileProperties(filePath);
202
+ }
203
+ spinner.succeed(`Analysis complete. Found ${propertyMap.size} properties.`);
204
+ return propertyMap;
205
+ };
206
+ var getRelatedCnsts = (constantFilePath) => {
207
+ const parsedConfig = parseTsConfig();
208
+ const { allFilesToAnalyze } = collectImportedFiles(constantFilePath, parsedConfig);
209
+ const { program, checker } = createTsProgram(allFilesToAnalyze, parsedConfig.options);
210
+ const propertyMap = analyzeProperties(allFilesToAnalyze, program, checker);
211
+ return Array.from(propertyMap.entries()).map(([key, value]) => ({ key, ...value }));
212
+ };
213
+
214
+ // pkgs/@akanjs/cli/module/module.script.ts
215
+ import { input } from "@inquirer/prompts";
216
+ import { capitalize as capitalize3, randomPicks } from "akanjs/common";
217
+
218
+ // pkgs/@akanjs/cli/module/module.request.ts
219
+ import { capitalize } from "akanjs/common";
220
+
221
+ // pkgs/@akanjs/cli/module/module.prompt.ts
222
+ var componentDefaultDescription = ({
223
+ modelName,
224
+ ModelName,
225
+ exampleFiles,
226
+ constant,
227
+ properties
228
+ }) => ``;
229
+
230
+ // pkgs/@akanjs/cli/module/module.request.ts
231
+ class ModuleRequest extends Prompter {
232
+ sysType;
233
+ sysName;
234
+ config;
235
+ modelName;
236
+ constructor(options) {
237
+ super();
238
+ this.sysType = options.sysType;
239
+ this.sysName = options.sysName;
240
+ this.config = options.config;
241
+ this.modelName = options.modelName;
242
+ }
243
+ async requestModelConstant({
244
+ modelDesc,
245
+ modelSchemaDesign,
246
+ boilerplate,
247
+ exampleFiles
248
+ }) {
249
+ return this.makeTsFileUpdatePrompt({
250
+ context: `
251
+ 1. <model>.constant.ts \uD30C\uC77C\uC5D0 \uB300\uD55C \uAC1C\uC694
252
+ ${await this.getDocumentation("modelConstant")}
253
+
254
+ 2. <model>.constant.ts \uD30C\uC77C\uC758 Enum \uC791\uC131\uBC95
255
+ ${await this.getDocumentation("enumConstant")}
256
+
257
+ 3. <model>.constant.ts \uD30C\uC77C\uC758 Field \uC791\uC131\uBC95
258
+ ${await this.getDocumentation("fieldRule")}
259
+
260
+ 4. \uD604\uC7AC \uD504\uB85C\uC81D\uD2B8 \uB0B4 \uB2E4\uB978 constant.ts \uD30C\uC77C\uB4E4\uC5D0 \uB300\uD55C \uC608\uC2DC
261
+ ${exampleFiles.map((constant) => `
262
+ Example file: ${constant.filePath}
263
+ \`\`\`
264
+ ${constant.content}
265
+ \`\`\`
266
+ `).join(`
267
+ `)}
268
+ `,
269
+ request: `
270
+ \uC704 \uB0B4\uC6A9\uB4E4\uC744 \uBC14\uD0D5\uC73C\uB85C \uD30C\uC2F1\uD558\uAE30 \uC27D\uAC8C \uC544\uB798\uC5D0 \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uB9DE\uAC8C \uC815\uB9AC\uD574\uC11C \uC918
271
+
272
+ Application name: ${this.sysName}
273
+ Model name: ${this.modelName}
274
+ Model description: ${modelDesc}
275
+ Model schema design: ${modelSchemaDesign}
276
+
277
+ Target filename: ${this.modelName}.constant.ts
278
+ \`\`\`typescript
279
+ // File: lib/${this.modelName}/${this.modelName}.constant.ts
280
+ ${boilerplate}
281
+ \`\`\`
282
+
283
+ \uB9CC\uC57D, ${this.modelName}.constant.ts \uD30C\uC77C \uC678\uC5D0 \uB2E4\uB978 \uC2A4\uCE7C\uB77C \uBAA8\uB378\uC744 \uCD94\uAC00\uB85C \uC791\uC131\uD574\uC57C\uD55C\uB2E4\uBA74, \uB2E4\uC74C\uACFC \uAC19\uC740 \uD615\uC2DD\uC73C\uB85C \uCD94\uAC00\uB85C \uC791\uC131\uD574\uC918
284
+ \`\`\`typescript
285
+ // File: lib/__scalar/otherModel/otherModel.constant.ts
286
+ ...\uD30C\uC77C \uB0B4\uC6A9
287
+ \`\`\`
288
+ `
289
+ });
290
+ }
291
+ }
292
+ var requestDictionary = ({
293
+ sysName,
294
+ modelName,
295
+ constant,
296
+ modelDesc,
297
+ modelSchemaDesign,
298
+ boilerplate,
299
+ exampleFiles
300
+ }) => `
301
+
302
+ You are writing an Akan.js model dictionary file.
303
+
304
+ Model constant:
305
+ \`\`\`typescript
306
+ // File: lib/${modelName}/${modelName}.constant.ts
307
+ ${constant}
308
+ \`\`\`
309
+
310
+ Reference dictionary files:
311
+ ${exampleFiles.map((exampleFile) => `
312
+ \`\`\`typescript
313
+ // File: ${exampleFile.filePath}
314
+ ${exampleFile.content}
315
+ \`\`\`
316
+ `).join(`
317
+ `)}
318
+
319
+ Application name: ${sysName}
320
+ Model name: ${modelName}
321
+ Model description: ${modelDesc}
322
+ Model schema design: ${modelSchemaDesign}
323
+
324
+ Complete the dictionary according to the constant file and boilerplate below.
325
+ Return only file results in parseable code blocks.
326
+
327
+ \`\`\`typescript
328
+ // File: lib/${modelName}/${modelName}.dictionary.ts
329
+ ${boilerplate}
330
+ \`\`\`
331
+ `;
332
+ var requestTemplate = ({
333
+ sysName,
334
+ modelName,
335
+ ModelName,
336
+ boilerplate,
337
+ constant,
338
+ properties,
339
+ exampleFiles
340
+ }) => `
341
+ ${componentDefaultDescription({
342
+ sysName,
343
+ modelName,
344
+ ModelName: ModelName ?? capitalize(modelName),
345
+ exampleFiles,
346
+ constant,
347
+ properties
348
+ })}
349
+ \uC5ED\uD560\uBD80\uC5EC
350
+ - Akan.js \uC0AC\uB0B4 \uD504\uB808\uC784\uC6CC\uD06C \uAE30\uBC18 Typescript \uC2DC\uB2C8\uC5B4 \uD504\uB860\uD2B8\uC5D4\uB4DC \uAC1C\uBC1C\uC790.
351
+
352
+ \uCF54\uB529 \uADDC\uCE59
353
+ - \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
354
+ - \uC544\uC774\uCF58: react-icons \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
355
+ - CSS: tailwind, DaisyUI(card/hero \uAC19\uC740 \uBCF5\uC7A1\uD55C \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 X) \uC0AC\uC6A9
356
+ - Ui Component: @util/ui \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
357
+ - \uC870\uAC74\uBD80 \uD074\uB798\uC2A4: clsx \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
358
+ \uCF54\uB4DC \uC2A4\uD0C0\uC77C
359
+ - \uC0C9\uC0C1: \uD558\uB4DC\uCF54\uB529(bg-red) \uB300\uC2E0 \uD14C\uB9C8 \uC0C9\uC0C1(bg-primary) \uC0AC\uC6A9
360
+ - \uC870\uAC74\uBD80 \uB80C\uB354\uB9C1: field && <div>... \uB300\uC2E0 field ? <div>... : null \uC0AC\uC6A9
361
+ - \uBAA8\uB378 \uC811\uADFC: \uAD6C\uC870\uBD84\uD574\uD560\uB2F9 \uB300\uC2E0 ${modelName}.field \uD615\uC2DD\uC73C\uB85C \uC811\uADFC
362
+ - \uD0C0\uC785 \uC548\uC804: ${modelName}.constant.ts\uC758 \uC2A4\uD0A4\uB9C8 \uCC38\uC870\uD558\uC5EC \uC5D0\uB7EC \uBC29\uC9C0
363
+ \uD544\uB4DC \uC811\uADFC \uC804: \uC2A4\uD0A4\uB9C8\uC758 \uC2E4\uC81C \uD544\uB4DC \uB9AC\uC2A4\uD2B8 \uC791\uC131 \uBC0F \uAC80\uD1A0 \uD544\uC218
364
+
365
+ \uC5C4\uACA9\uD55C \uC8FC\uC758\uC0AC\uD56D
366
+ - UI \uD0B7\uC740 \uBB38\uC11C\uC5D0 \uBA85\uC2DC\uB41C \uCEF4\uD3EC\uB10C\uD2B8\uB9CC \uC0AC\uC6A9
367
+ - \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uC804 \uBB38\uC11C \uD655\uC778 \uBC0F props \uC815\uD655\uD788 \uAC80\uC99D
368
+ - \uBA85\uC2DC\uB41C \uB8F0 \uC678 \uC784\uC758 \uCD94\uC0C1\uD654 \uAE08\uC9C0
369
+ - dayjs \uB77C\uC774\uBE0C\uB7EC\uB9AC\uB294 akanjs/base\uC5D0\uC11C \uB798\uD551\uD558\uC5EC \uC81C\uACF5\uD558\uACE0 \uC788\uC74C.
370
+
371
+ \uC694\uCCAD\uC0AC\uD56D
372
+ - \uC544\uB798 \uC81C\uACF5\uD560 \uAE30\uBCF8 \uD15C\uD50C\uB9BF \uCF54\uB4DC\uC5D0 \uCD94\uAC00\uB85C \uCEF4\uD3EC\uB10C\uD2B8 \uAC1C\uBC1C
373
+ - ${ModelName}.Template.tsx \uCF54\uB4DC \uC791\uC131
374
+ - \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uC740 \uBAA8\uB378 \uC774\uB984\uC740 \uC2A4\uD0A4\uB9C8\uC5D0 \uAE30\uBC18\uD55C \uAE30\uB2A5\uC5D0 \uCD08\uC810\uC744 \uB450\uACE0 \uC791\uC131
375
+ - \uC544\uB798 \uC81C\uACF5\uD560 \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uC788\uB294 General \uCEF4\uD3EC\uB10C\uD2B8 1\uAC1C\uB97C \uC81C\uC678\uD55C \uB514\uC790\uC778 \uCEF4\uD3EC\uB10C\uD2B8 4\uAC1C \uAC1C\uBC1C
376
+ - \uCD94\uC0C1\uD654 \uD574\uC57C\uD558\uB294 \uACBD\uC6B0\uAC00 \uC788\uC744 \uACBD\uC6B0\uC5D4 \uBB38\uC11C\uB97C \uB2E4\uC2DC \uCC38\uACE0\uD558\uACE0 \uC124\uBA85\uB41C \uB0B4\uC5D0\uC11C \uD574\uACB0\uD574\uC57C\uD568.
377
+ - \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uB9DE\uAC8C \uC815\uB9AC\uD574\uC11C \uC81C\uACF5
378
+ -
379
+
380
+ Application name: ${sysName}
381
+ Model name: ${modelName}
382
+ Target filename: ${ModelName}.Template.tsx
383
+ \`\`\`
384
+ ${boilerplate}
385
+ \`\`\`
386
+
387
+
388
+ `;
389
+ var requestView = ({
390
+ sysName,
391
+ modelName,
392
+ ModelName,
393
+ boilerplate,
394
+ constant,
395
+ properties,
396
+ exampleFiles
397
+ }) => `
398
+ ${componentDefaultDescription({
399
+ sysName,
400
+ modelName,
401
+ ModelName: ModelName ?? capitalize(modelName),
402
+ exampleFiles,
403
+ constant,
404
+ properties
405
+ })}
406
+
407
+ \uC5ED\uD560\uBD80\uC5EC
408
+ - Akan.js \uC0AC\uB0B4 \uD504\uB808\uC784\uC6CC\uD06C \uAE30\uBC18 Typescript \uC2DC\uB2C8\uC5B4 \uD504\uB860\uD2B8\uC5D4\uB4DC \uAC1C\uBC1C\uC790.
409
+
410
+ \uCF54\uB529 \uADDC\uCE59
411
+ - \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
412
+ - \uC544\uC774\uCF58: react-icons \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
413
+ - CSS: tailwind, DaisyUI(card/hero \uAC19\uC740 \uBCF5\uC7A1\uD55C \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 X) \uC0AC\uC6A9
414
+ - Ui Component: @util/ui \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
415
+ - \uC870\uAC74\uBD80 \uD074\uB798\uC2A4: clsx \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
416
+ \uCF54\uB4DC \uC2A4\uD0C0\uC77C
417
+ - \uC0C9\uC0C1: \uD558\uB4DC\uCF54\uB529(bg-red) \uB300\uC2E0 \uD14C\uB9C8 \uC0C9\uC0C1(bg-primary) \uC0AC\uC6A9
418
+ - \uC870\uAC74\uBD80 \uB80C\uB354\uB9C1: field && <div>... \uB300\uC2E0 field ? <div>... : null \uC0AC\uC6A9
419
+ - \uBAA8\uB378 \uC811\uADFC: \uAD6C\uC870\uBD84\uD574\uD560\uB2F9 \uB300\uC2E0 ${modelName}.field \uD615\uC2DD\uC73C\uB85C \uC811\uADFC
420
+ - \uD0C0\uC785 \uC548\uC804: ${modelName}.constant.ts\uC758 \uC2A4\uD0A4\uB9C8 \uCC38\uC870\uD558\uC5EC \uC5D0\uB7EC \uBC29\uC9C0
421
+ \uD544\uB4DC \uC811\uADFC \uC804: \uC2A4\uD0A4\uB9C8\uC758 \uC2E4\uC81C \uD544\uB4DC \uB9AC\uC2A4\uD2B8 \uC791\uC131 \uBC0F \uAC80\uD1A0 \uD544\uC218
422
+
423
+ \uC5C4\uACA9\uD55C \uC8FC\uC758\uC0AC\uD56D
424
+ - UI \uD0B7\uC740 \uBB38\uC11C\uC5D0 \uBA85\uC2DC\uB41C \uCEF4\uD3EC\uB10C\uD2B8\uB9CC \uC0AC\uC6A9
425
+ - \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uC804 \uBB38\uC11C \uD655\uC778 \uBC0F props \uC815\uD655\uD788 \uAC80\uC99D
426
+ - \uBA85\uC2DC\uB41C \uB8F0 \uC678 \uC784\uC758 \uCD94\uC0C1\uD654 \uAE08\uC9C0
427
+ - dayjs \uB77C\uC774\uBE0C\uB7EC\uB9AC\uB294 akanjs/base\uC5D0\uC11C \uB798\uD551\uD558\uC5EC \uC81C\uACF5\uD558\uACE0 \uC788\uC74C.
428
+ - util/ui \uC774\uC678\uC5D0 \uBA85\uC2DC\uB418\uC788\uC9C0 \uC54A\uC740 \uCEF4\uD3EC\uB10C\uD2B8 \uC808\uB300 \uC0AC\uC6A9 \uAE08\uC9C0.
429
+
430
+ \uC694\uCCAD\uC0AC\uD56D
431
+ - \uC544\uB798 \uC81C\uACF5\uD560 \uAE30\uBCF8 \uD15C\uD50C\uB9BF \uCF54\uB4DC\uC5D0 \uCD94\uAC00\uB85C \uCEF4\uD3EC\uB10C\uD2B8 \uAC1C\uBC1C
432
+ - ${ModelName}.View.tsx \uCF54\uB4DC \uC791\uC131
433
+ - \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uC5D0 ${ModelName}\uC740 \uC0DD\uB7B5\uD558\uBA70, \uB514\uC790\uC778 \uC911\uC810\uC758 \uC774\uB984\uC73C\uB85C \uC791\uC131
434
+ - \uC544\uB798 \uC81C\uACF5\uD560 \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uC788\uB294 General \uCEF4\uD3EC\uB10C\uD2B8 1\uAC1C\uB97C \uC81C\uC678\uD55C \uB514\uC790\uC778 \uCEF4\uD3EC\uB10C\uD2B8 4\uAC1C \uAC1C\uBC1C
435
+ - \uCD94\uC0C1\uD654 \uD574\uC57C\uD558\uB294 \uACBD\uC6B0\uAC00 \uC788\uC744 \uACBD\uC6B0\uC5D4 \uBB38\uC11C\uB97C \uB2E4\uC2DC \uCC38\uACE0\uD558\uACE0 \uC124\uBA85\uB41C \uB0B4\uC5D0\uC11C \uD574\uACB0\uD574\uC57C\uD568.
436
+ - \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uB9DE\uAC8C \uC815\uB9AC\uD574\uC11C \uC81C\uACF5
437
+ - UI \uD0B7\uC740 \uBB38\uC11C\uC5D0 \uBA85\uC2DC\uB41C \uCEF4\uD3EC\uB10C\uD2B8\uB9CC \uC0AC\uC6A9
438
+
439
+
440
+ Application name: ${sysName}
441
+ Model name: ${modelName}
442
+
443
+ Target filename: ${ModelName}.View.tsx
444
+ \`\`\`
445
+ ${boilerplate}
446
+ \`\`\`
447
+
448
+
449
+ `;
450
+ var requestUnit = ({
451
+ sysName,
452
+ modelName,
453
+ ModelName,
454
+ constant,
455
+ properties,
456
+ boilerplate,
457
+ exampleFiles
458
+ }) => `
459
+ ${componentDefaultDescription({
460
+ sysName,
461
+ modelName,
462
+ ModelName: ModelName ?? capitalize(modelName),
463
+ exampleFiles,
464
+ constant,
465
+ properties
466
+ })}
467
+
468
+ \uC5ED\uD560\uBD80\uC5EC
469
+ - Akan.js \uC0AC\uB0B4 \uD504\uB808\uC784\uC6CC\uD06C \uAE30\uBC18 Typescript \uC2DC\uB2C8\uC5B4 \uD504\uB860\uD2B8\uC5D4\uB4DC \uAC1C\uBC1C\uC790.
470
+
471
+ \uCF54\uB529 \uADDC\uCE59
472
+ - \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
473
+ - \uC544\uC774\uCF58: react-icons \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
474
+ - CSS: tailwind, DaisyUI(card/hero \uAC19\uC740 \uBCF5\uC7A1\uD55C \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 X) \uC0AC\uC6A9
475
+ - Ui Component: @util/ui \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
476
+ - \uC870\uAC74\uBD80 \uD074\uB798\uC2A4: clsx \uB77C\uC774\uBE0C\uB7EC\uB9AC \uC0AC\uC6A9
477
+ \uCF54\uB4DC \uC2A4\uD0C0\uC77C
478
+ - \uC0C9\uC0C1: \uD558\uB4DC\uCF54\uB529(bg-red) \uB300\uC2E0 \uD14C\uB9C8 \uC0C9\uC0C1(bg-primary) \uC0AC\uC6A9
479
+ - \uC870\uAC74\uBD80 \uB80C\uB354\uB9C1: field && <div>... \uB300\uC2E0 field ? <div>... : null \uC0AC\uC6A9
480
+ - \uBAA8\uB378 \uC811\uADFC: \uAD6C\uC870\uBD84\uD574\uD560\uB2F9 \uB300\uC2E0 ${modelName}.field \uD615\uC2DD\uC73C\uB85C \uC811\uADFC
481
+ - \uD0C0\uC785 \uC548\uC804: ${modelName}.constant.ts\uC758 \uC2A4\uD0A4\uB9C8 \uCC38\uC870\uD558\uC5EC \uC5D0\uB7EC \uBC29\uC9C0
482
+ \uD544\uB4DC \uC811\uADFC \uC804: \uC2A4\uD0A4\uB9C8\uC758 \uC2E4\uC81C \uD544\uB4DC \uB9AC\uC2A4\uD2B8 \uC791\uC131 \uBC0F \uAC80\uD1A0 \uD544\uC218
483
+
484
+ \uC5C4\uACA9\uD55C \uC8FC\uC758\uC0AC\uD56D
485
+ - UI \uD0B7\uC740 \uBB38\uC11C\uC5D0 \uBA85\uC2DC\uB41C \uCEF4\uD3EC\uB10C\uD2B8\uB9CC \uC0AC\uC6A9
486
+ - \uCEF4\uD3EC\uB10C\uD2B8 \uC0AC\uC6A9 \uC804 \uBB38\uC11C \uD655\uC778 \uBC0F props \uC815\uD655\uD788 \uAC80\uC99D
487
+ - \uBA85\uC2DC\uB41C \uB8F0 \uC678 \uC784\uC758 \uCD94\uC0C1\uD654 \uAE08\uC9C0
488
+ - dayjs \uB77C\uC774\uBE0C\uB7EC\uB9AC\uB294 akanjs/base\uC5D0\uC11C \uB798\uD551\uD558\uC5EC \uC81C\uACF5\uD558\uACE0 \uC788\uC74C.
489
+
490
+ \uC694\uCCAD\uC0AC\uD56D
491
+ - \uC544\uB798 \uC81C\uACF5\uD560 \uAE30\uBCF8 \uD15C\uD50C\uB9BF \uCF54\uB4DC\uC5D0 \uCD94\uAC00\uB85C \uCEF4\uD3EC\uB10C\uD2B8 \uAC1C\uBC1C
492
+ - ${ModelName}.Unit.tsx \uCF54\uB4DC \uC791\uC131
493
+ - \uCEF4\uD3EC\uB10C\uD2B8 \uC774\uB984\uC5D0 ${ModelName}\uC740 \uC0DD\uB7B5\uD558\uBA70, \uB514\uC790\uC778 \uC911\uC810\uC758 \uC774\uB984\uC73C\uB85C \uC791\uC131
494
+ - \uC608\uC2DC\uD30C\uC77C \uCEF4\uD3EC\uB10C\uD2B8\uC758 \uAE30\uBC18\uD558\uC5EC \uC77C\uBC18\uC801\uC73C\uB85C \uC0AC\uC6A9 \uAC00\uB2A5\uD55C \uCEF4\uD3EC\uB10C\uD2B8 1\uAC1C\uC640 \uB514\uC790\uC778\uC801 \uC694\uC18C\uAC00 \uD3EC\uD568\uB41C \uCEF4\uD3EC\uB10C\uD2B8 3\uAC1C \uAC1C\uBC1C
495
+ - \uCD94\uC0C1\uD654 \uD574\uC57C\uD558\uB294 \uACBD\uC6B0\uAC00 \uC788\uC744 \uACBD\uC6B0\uC5D4 \uBB38\uC11C\uB97C \uB2E4\uC2DC \uCC38\uACE0\uD558\uACE0 \uC124\uBA85\uB41C \uB0B4\uC5D0\uC11C \uD574\uACB0\uD574\uC57C\uD568.
496
+ - \uBCF4\uC77C\uB7EC\uD50C\uB808\uC774\uD2B8\uC5D0 \uB9DE\uAC8C \uC815\uB9AC\uD574\uC11C \uC81C\uACF5
497
+
498
+
499
+ Application name: ${sysName}
500
+ Model name: ${modelName}
501
+
502
+ Target filename: ${ModelName}.Unit.tsx
503
+ \`\`\`
504
+ ${boilerplate}
505
+ \`\`\`
506
+
507
+
508
+ `;
509
+
510
+ // pkgs/@akanjs/cli/module/module.runner.ts
511
+ import { capitalize as capitalize2 } from "akanjs/common";
512
+ var purposeByModule = {
513
+ budget: "Budget represents planned or actual money allocated inside the app.",
514
+ project: "Project represents a project workspace or business initiative managed by the app.",
515
+ task: "Task represents work items that move through the app workflow."
516
+ };
517
+ var moduleAbstractContent = (moduleName) => {
518
+ const title = capitalize2(moduleName);
519
+ const label = bilingualLabelForField(moduleName);
520
+ const purpose = purposeByModule[moduleName] ?? `${title} represents ${label.en.toLowerCase()} records managed by the app.`;
521
+ return `# ${title} Module Abstract
522
+
523
+ ## Purpose
524
+
525
+ ${purpose}
526
+
527
+ ## Domain Rules
528
+
529
+ - Keep ${label.en.toLowerCase()} data consistent with user-facing dictionary labels.
530
+
531
+ ## Data Meaning
532
+
533
+ ${label.en} (${label.ko}) is the primary business concept for this module.
534
+
535
+ ## Workflows
536
+
537
+ No lifecycle workflow yet.
538
+
539
+ ## Agent Notes
540
+
541
+ - Read this abstract before changing the module.
542
+ - Update this file when business invariants, workflows, or public behavior change.
543
+ - Do not update this file for formatting-only, import-only, or style-only changes.
544
+
545
+ ## Related Modules
546
+
547
+ - None yet.
548
+ `;
549
+ };
550
+ var localModuleFilename = (moduleName, pathKey) => moduleSourcePaths(moduleName)[pathKey].replace(`lib/${moduleName}/`, "");
551
+
552
+ class ModuleRunner extends runner("module") {
553
+ async createService(module) {
554
+ const serviceName = module.name.replace(/^_+/, "");
555
+ await module.applyTemplate({
556
+ basePath: `.`,
557
+ template: "service",
558
+ dict: { model: serviceName, sysName: module.sys.name }
559
+ });
560
+ const [abstractContent, dictionaryContent, serviceContent, signalContent, storeContent] = await Promise.all([
561
+ module.readFile(`${serviceName}.abstract.md`),
562
+ module.readFile(`${serviceName}.dictionary.ts`),
563
+ module.readFile(`${serviceName}.service.ts`),
564
+ module.readFile(`${serviceName}.signal.ts`),
565
+ module.readFile(`${serviceName}.store.ts`)
566
+ ]);
567
+ return {
568
+ abstract: { filename: `${serviceName}.abstract.md`, content: abstractContent },
569
+ dictionary: { filename: `${serviceName}.dictionary.ts`, content: dictionaryContent },
570
+ service: { filename: `${serviceName}.service.ts`, content: serviceContent },
571
+ signal: { filename: `${serviceName}.signal.ts`, content: signalContent },
572
+ store: { filename: `${serviceName}.store.ts`, content: storeContent }
573
+ };
574
+ }
575
+ async removeModule(module) {
576
+ await module.sys.removeDir(`lib/${module.name}`);
577
+ }
578
+ async createComponentTemplate(module, type) {
579
+ await module.sys.applyTemplate({
580
+ basePath: `./lib/${module.name}`,
581
+ template: `module/__Model__.${capitalize2(type)}.tsx`,
582
+ dict: { model: module.name, appName: module.sys.name }
583
+ });
584
+ return {
585
+ component: {
586
+ filename: `${capitalize2(module.name)}.${capitalize2(type)}.tsx`,
587
+ content: await module.sys.readFile(`lib/${module.name}/${capitalize2(module.name)}.${capitalize2(type)}.tsx`)
588
+ }
589
+ };
590
+ }
591
+ async createModuleTemplate(module) {
592
+ const names = pluralizeName(module.name);
593
+ const modelLabel = bilingualLabelForField(module.name);
594
+ const modelDescription = bilingualDescriptionForField(module.name);
595
+ const filenames = {
596
+ abstract: localModuleFilename(module.name, "abstract"),
597
+ constant: localModuleFilename(module.name, "constant"),
598
+ dictionary: localModuleFilename(module.name, "dictionary"),
599
+ service: localModuleFilename(module.name, "service"),
600
+ store: localModuleFilename(module.name, "store"),
601
+ signal: localModuleFilename(module.name, "signal"),
602
+ unit: localModuleFilename(module.name, "unit"),
603
+ view: localModuleFilename(module.name, "view"),
604
+ template: localModuleFilename(module.name, "template"),
605
+ zone: localModuleFilename(module.name, "zone"),
606
+ util: localModuleFilename(module.name, "util")
607
+ };
608
+ await module.applyTemplate({
609
+ basePath: `.`,
610
+ template: "module",
611
+ dict: {
612
+ model: module.name,
613
+ models: names,
614
+ sysName: module.sys.name,
615
+ modelLabelEn: modelLabel.en,
616
+ modelLabelKo: modelLabel.ko,
617
+ modelDescEn: modelDescription.en,
618
+ modelDescKo: modelDescription.ko
619
+ }
620
+ });
621
+ await module.writeFile(filenames.abstract, moduleAbstractContent(module.name));
622
+ const [
623
+ abstractContent,
624
+ constantContent,
625
+ dictionaryContent,
626
+ serviceContent,
627
+ storeContent,
628
+ signalContent,
629
+ unitContent,
630
+ viewContent,
631
+ templateContent,
632
+ zoneContent,
633
+ utilContent
634
+ ] = await Promise.all([
635
+ module.readFile(filenames.abstract),
636
+ module.readFile(filenames.constant),
637
+ module.readFile(filenames.dictionary),
638
+ module.readFile(filenames.service),
639
+ module.readFile(filenames.store),
640
+ module.readFile(filenames.signal),
641
+ module.readFile(filenames.unit),
642
+ module.readFile(filenames.view),
643
+ module.readFile(filenames.template),
644
+ module.readFile(filenames.zone),
645
+ module.readFile(filenames.util)
646
+ ]);
647
+ return {
648
+ abstract: { filename: filenames.abstract, content: abstractContent },
649
+ constant: { filename: filenames.constant, content: constantContent },
650
+ dictionary: { filename: filenames.dictionary, content: dictionaryContent },
651
+ service: { filename: filenames.service, content: serviceContent },
652
+ store: { filename: filenames.store, content: storeContent },
653
+ signal: { filename: filenames.signal, content: signalContent },
654
+ unit: { filename: filenames.unit, content: unitContent },
655
+ view: { filename: filenames.view, content: viewContent },
656
+ template: { filename: filenames.template, content: templateContent },
657
+ zone: { filename: filenames.zone, content: zoneContent },
658
+ util: { filename: filenames.util, content: utilContent }
659
+ };
660
+ }
661
+ }
662
+
663
+ // pkgs/@akanjs/cli/module/module.script.ts
664
+ var moduleChangedFiles = (sys2, moduleName, files) => Object.values(files).map((file) => sourceFile(sys2, `lib/${moduleName}/${file.filename}`, "create", "Module source file was created."));
665
+
666
+ class ModuleScript extends script("module", [ModuleRunner, PageScript]) {
667
+ async createModuleTemplate(sys2, name, { page = false } = {}) {
668
+ const mod = ModuleExecutor.from(sys2, name);
669
+ const files = await this.moduleRunner.createModuleTemplate(mod);
670
+ if (page && sys2.type === "app")
671
+ await this.pageScript.createCrudPage(mod, { app: sys2, basePath: null, single: false });
672
+ await sys2.scan();
673
+ return createPassedPrimitiveReport({
674
+ command: "create-module",
675
+ changedFiles: moduleChangedFiles(sys2, name, files),
676
+ generatedFiles: generatedFilesForSync(sys2, "Generated files were refreshed after module creation."),
677
+ target: sys2.name
678
+ });
679
+ }
680
+ async createModule(sys2, name, {
681
+ page = false,
682
+ description,
683
+ schemaDescription
684
+ } = {}) {
685
+ const isContinued = await sys2.exists(`lib/${name}/${name}.constant.ts`);
686
+ const session = new AiSession("createModule", { workspace: sys2.workspace, cacheKey: name, isContinued });
687
+ const moduleConstantExampleFiles = await sys2.workspace.getConstantFiles();
688
+ const executor = ModuleExecutor.from(sys2, name);
689
+ const files = await this.moduleRunner.createModuleTemplate(executor);
690
+ const { constant, dictionary } = files;
691
+ if (page && sys2.type === "app")
692
+ await this.pageScript.createCrudPage(executor, { app: sys2, basePath: null, single: false });
693
+ const modelDesc = description ?? await input({ message: "description of module" });
694
+ const modelSchemaDesign = schemaDescription ?? await input({ message: "schema description of module" });
695
+ const config = await sys2.getConfig();
696
+ const moduleRequest = new ModuleRequest({ sysType: sys2.type, sysName: sys2.name, modelName: name, config });
697
+ const constantRequestPrompt = await moduleRequest.requestModelConstant({
698
+ modelDesc,
699
+ modelSchemaDesign,
700
+ boilerplate: constant.content,
701
+ exampleFiles: randomPicks(moduleConstantExampleFiles, Math.min(10, moduleConstantExampleFiles.length))
702
+ });
703
+ const constantGuide = await ModuleRequest.getGuideJson("modelConstant");
704
+ const constantWrites = await session.writeTypescripts(constantRequestPrompt, sys2, {
705
+ validate: constantGuide.update.rules
706
+ });
707
+ for (const scalarName of this.#getCreatedScalarNames(constantWrites.map(({ filePath }) => filePath))) {
708
+ await sys2.applyTemplate({
709
+ basePath: "./lib/__scalar",
710
+ template: "__scalar",
711
+ dict: { model: scalarName, models: pluralizeName(scalarName), sysName: sys2.name },
712
+ overwrite: false
713
+ });
714
+ }
715
+ const constantContent = await sys2.readFile(`lib/${name}/${name}.constant.ts`);
716
+ const dictionaryGuide = await ModuleRequest.getGuideJson("modelDictionary");
717
+ await session.writeTypescripts(requestDictionary({
718
+ sysName: sys2.name,
719
+ modelName: name,
720
+ constant: constantContent,
721
+ modelDesc,
722
+ modelSchemaDesign,
723
+ boilerplate: dictionary.content,
724
+ exampleFiles: randomPicks(moduleConstantExampleFiles, Math.min(10, moduleConstantExampleFiles.length))
725
+ }), sys2, { validate: dictionaryGuide.update.rules });
726
+ await sys2.scan();
727
+ sys2.log(`Module ${name} created in ${sys2.type}s/${sys2.name}/lib/${name}`);
728
+ return createPassedPrimitiveReport({
729
+ command: "create-module",
730
+ changedFiles: [
731
+ ...moduleChangedFiles(sys2, name, files),
732
+ ...constantWrites.map((write) => sourceFile(sys2, write.filePath, "modify", "AI-assisted module constant source was updated."))
733
+ ],
734
+ generatedFiles: generatedFilesForSync(sys2, "Generated files were refreshed after module creation."),
735
+ target: sys2.name
736
+ });
737
+ }
738
+ #getCreatedScalarNames(filePaths) {
739
+ return [
740
+ ...new Set(filePaths.map((filePath) => /^lib\/__scalar\/([^/]+)\/[^/]+\.constant\.ts$/.exec(filePath)?.[1]).filter((scalarName) => !!scalarName))
741
+ ];
742
+ }
743
+ async removeModule(mod) {
744
+ await this.moduleRunner.removeModule(mod);
745
+ }
746
+ async createService(sys2, name) {
747
+ const service = ModuleExecutor.from(sys2, `_${name}`);
748
+ const files = await this.moduleRunner.createService(service);
749
+ await sys2.scan();
750
+ return createPassedPrimitiveReport({
751
+ command: "create-service",
752
+ changedFiles: moduleChangedFiles(sys2, `_${name}`, files),
753
+ generatedFiles: generatedFilesForSync(sys2, "Generated files were refreshed after service creation."),
754
+ target: sys2.name
755
+ });
756
+ }
757
+ async createTest(workspace, name) {}
758
+ async createTemplate(mod) {
759
+ const { component: template } = await this.moduleRunner.createComponentTemplate(mod, "template");
760
+ const paths = moduleSourcePaths(mod.name);
761
+ const templateExampleFiles = (await mod.sys.getTemplatesSourceCode()).filter((f) => !f.filePath.includes(paths.template));
762
+ const Name = capitalize3(mod.name);
763
+ const relatedCnsts = getRelatedCnsts(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
764
+ const constant = await FileSys.readText(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
765
+ const session = new AiSession("createTemplate", { workspace: mod.sys.workspace, cacheKey: mod.name });
766
+ const promptRst = requestTemplate({
767
+ sysName: mod.sys.name,
768
+ modelName: mod.name,
769
+ ModelName: Name,
770
+ constant,
771
+ boilerplate: template.content,
772
+ properties: relatedCnsts.map((r) => ({ key: r.key, source: r.source })),
773
+ exampleFiles: randomPicks(templateExampleFiles, Math.min(20, templateExampleFiles.length))
774
+ });
775
+ const content = await session.editTypescript(promptRst);
776
+ //! 파일을 {name}.View.tsx에 저장.
777
+ await mod.writeFile(`${Name}.Template.tsx`, content);
778
+ return createPassedPrimitiveReport({
779
+ command: "create-template",
780
+ changedFiles: [
781
+ sourceFile(mod.sys, `lib/${mod.name}/${Name}.Template.tsx`, "modify", "Template UI source was created.")
782
+ ],
783
+ target: mod.sys.name
784
+ });
785
+ }
786
+ async createUnit(mod) {
787
+ const { component: unit } = await this.moduleRunner.createComponentTemplate(mod, "unit");
788
+ const paths = moduleSourcePaths(mod.name);
789
+ const Name = capitalize3(mod.name);
790
+ const unitExampleFiles = (await mod.sys.getUnitsSourceCode()).filter((f) => !f.filePath.includes(paths.unit));
791
+ const relatedCnsts = getRelatedCnsts(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
792
+ const constant = await FileSys.readText(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
793
+ const session = new AiSession("createUnit", { workspace: mod.sys.workspace, cacheKey: mod.name });
794
+ const promptRst = requestUnit({
795
+ sysName: mod.sys.name,
796
+ modelName: mod.name,
797
+ ModelName: Name,
798
+ constant,
799
+ properties: relatedCnsts.map((r) => ({ key: r.key, source: r.source })),
800
+ exampleFiles: randomPicks(unitExampleFiles, Math.min(10, unitExampleFiles.length)),
801
+ boilerplate: unit.content
802
+ });
803
+ const content = await session.editTypescript(promptRst);
804
+ //! 파일을 {name}.Unit.tsx에 저장.
805
+ await mod.writeFile(`${Name}.Unit.tsx`, content);
806
+ return createPassedPrimitiveReport({
807
+ command: "create-unit",
808
+ changedFiles: [sourceFile(mod.sys, `lib/${mod.name}/${Name}.Unit.tsx`, "modify", "Unit UI source was created.")],
809
+ target: mod.sys.name
810
+ });
811
+ }
812
+ async createView(mod) {
813
+ const { component: view } = await this.moduleRunner.createComponentTemplate(mod, "view");
814
+ const paths = moduleSourcePaths(mod.name);
815
+ const viewExampleFiles = (await mod.sys.getViewsSourceCode()).filter((f) => !f.filePath.includes(paths.view));
816
+ const Name = capitalize3(mod.name);
817
+ const relatedCnsts = getRelatedCnsts(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
818
+ const constant = await FileSys.readText(`${mod.sys.cwdPath}/lib/${mod.name}/${mod.name}.constant.ts`);
819
+ const session = new AiSession("createView", { workspace: mod.sys.workspace, cacheKey: mod.name });
820
+ const promptRst = requestView({
821
+ sysName: mod.sys.name,
822
+ modelName: mod.name,
823
+ ModelName: Name,
824
+ constant,
825
+ boilerplate: view.content,
826
+ properties: relatedCnsts.map((r) => ({ key: r.key, source: r.source })),
827
+ exampleFiles: randomPicks(viewExampleFiles, Math.min(20, viewExampleFiles.length))
828
+ });
829
+ const content = await session.editTypescript(promptRst);
830
+ //! 파일을 {name}.View.tsx에 저장.
831
+ await mod.writeFile(`${Name}.View.tsx`, content);
832
+ return createPassedPrimitiveReport({
833
+ command: "create-view",
834
+ changedFiles: [sourceFile(mod.sys, `lib/${mod.name}/${Name}.View.tsx`, "modify", "View UI source was created.")],
835
+ target: mod.sys.name
836
+ });
837
+ }
838
+ }
839
+
840
+ export { ModuleScript };