@kosdev-code/kos-ui-cli 2.1.38 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -6
- package/src/lib/cli.mjs +34 -10
- package/src/lib/generators/component/index.mjs +1230 -27
- package/src/lib/generators/component/index.mjs.map +7 -0
- package/src/lib/generators/model/add-future.mjs +1449 -43
- package/src/lib/generators/model/add-future.mjs.map +7 -0
- package/src/lib/generators/model/companion.mjs +1465 -56
- package/src/lib/generators/model/companion.mjs.map +7 -0
- package/src/lib/generators/model/container.mjs +1234 -45
- package/src/lib/generators/model/container.mjs.map +7 -0
- package/src/lib/generators/model/context.mjs +1102 -35
- package/src/lib/generators/model/context.mjs.map +7 -0
- package/src/lib/generators/model/hook.mjs +1097 -33
- package/src/lib/generators/model/hook.mjs.map +7 -0
- package/src/lib/generators/model/model.mjs +1411 -39
- package/src/lib/generators/model/model.mjs.map +7 -0
- package/src/lib/generators/plugin/index.mjs +1275 -74
- package/src/lib/generators/plugin/index.mjs.map +7 -0
- package/src/lib/generators/project/splash.mjs +691 -21
- package/src/lib/generators/project/splash.mjs.map +7 -0
- package/src/lib/utils/dev-config.mjs +7 -12
- package/src/lib/utils/nx-context.mjs +584 -170
- package/src/lib/utils/nx-context.mjs.map +7 -0
- package/README.md +0 -484
- package/src/index.d.ts +0 -2
- package/src/index.d.ts.map +0 -1
- package/src/index.js +0 -1
- package/src/index.js.map +0 -1
|
@@ -1,10 +1,1425 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
// src/lib/utils/action-factory.mjs
|
|
2
|
+
var actionFactory = (action, metadata2) => {
|
|
3
|
+
return () => {
|
|
4
|
+
const _actions = [{ type: action }];
|
|
5
|
+
if (metadata2.invalidateCache) {
|
|
6
|
+
_actions.push({ type: "clearCache" });
|
|
7
|
+
}
|
|
8
|
+
return _actions;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/lib/utils/nx-context.mjs
|
|
13
|
+
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync } from "fs";
|
|
14
|
+
import path6 from "path";
|
|
15
|
+
import { fileURLToPath } from "url";
|
|
16
|
+
|
|
17
|
+
// src/lib/utils/cache.mjs
|
|
18
|
+
import fs from "fs";
|
|
19
|
+
import path from "path";
|
|
20
|
+
var CACHE_PATH = path.resolve(".nx/cli-cache.json");
|
|
21
|
+
var CACHE_TTL = 600 * 1e3;
|
|
22
|
+
var ARGS = process.argv;
|
|
23
|
+
var DISABLE_CACHE = process.env.DISABLE_CACHE === "true" || process.env.REFRESH === "true";
|
|
24
|
+
var _cache = {};
|
|
25
|
+
var _loaded = false;
|
|
26
|
+
function ensureCacheDir() {
|
|
27
|
+
const dir = path.dirname(CACHE_PATH);
|
|
28
|
+
if (!fs.existsSync(dir))
|
|
29
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
function loadCacheFromDisk() {
|
|
32
|
+
if (_loaded)
|
|
33
|
+
return;
|
|
34
|
+
_loaded = true;
|
|
35
|
+
try {
|
|
36
|
+
if (fs.existsSync(CACHE_PATH)) {
|
|
37
|
+
const data = fs.readFileSync(CACHE_PATH, "utf-8");
|
|
38
|
+
_cache = JSON.parse(data);
|
|
39
|
+
}
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.warn("Failed to load CLI cache:", err);
|
|
42
|
+
_cache = {};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function saveCacheToDisk() {
|
|
46
|
+
try {
|
|
47
|
+
ensureCacheDir();
|
|
48
|
+
fs.writeFileSync(CACHE_PATH, JSON.stringify(_cache, null, 2));
|
|
49
|
+
} catch (err) {
|
|
50
|
+
console.warn("Failed to save CLI cache:", err);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function isFresh(entry, ttl = CACHE_TTL) {
|
|
54
|
+
if (!entry || !entry.timestamp)
|
|
55
|
+
return false;
|
|
56
|
+
return Date.now() - entry.timestamp < ttl;
|
|
57
|
+
}
|
|
58
|
+
function getCached(key, ttl = CACHE_TTL) {
|
|
59
|
+
if (DISABLE_CACHE)
|
|
60
|
+
return null;
|
|
61
|
+
loadCacheFromDisk();
|
|
62
|
+
const entry = _cache[key];
|
|
63
|
+
if (isFresh(entry, ttl))
|
|
64
|
+
return entry.data;
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
function setCached(key, data) {
|
|
68
|
+
loadCacheFromDisk();
|
|
69
|
+
_cache[key] = {
|
|
70
|
+
data,
|
|
71
|
+
timestamp: Date.now()
|
|
72
|
+
};
|
|
73
|
+
saveCacheToDisk();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ../kos-codegen-core/src/lib/codegen-filesystem.ts
|
|
77
|
+
import * as fs2 from "fs";
|
|
78
|
+
import * as path2 from "path";
|
|
79
|
+
var DirectFileSystem = class {
|
|
80
|
+
root;
|
|
81
|
+
constructor(workspaceRoot) {
|
|
82
|
+
this.root = path2.resolve(workspaceRoot);
|
|
83
|
+
}
|
|
84
|
+
read(filePath) {
|
|
85
|
+
const abs = this.resolve(filePath);
|
|
86
|
+
try {
|
|
87
|
+
return fs2.readFileSync(abs, "utf-8");
|
|
88
|
+
} catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
write(filePath, content) {
|
|
93
|
+
const abs = this.resolve(filePath);
|
|
94
|
+
fs2.mkdirSync(path2.dirname(abs), { recursive: true });
|
|
95
|
+
fs2.writeFileSync(abs, content, "utf-8");
|
|
96
|
+
}
|
|
97
|
+
exists(filePath) {
|
|
98
|
+
return fs2.existsSync(this.resolve(filePath));
|
|
99
|
+
}
|
|
100
|
+
delete(filePath) {
|
|
101
|
+
const abs = this.resolve(filePath);
|
|
102
|
+
try {
|
|
103
|
+
fs2.unlinkSync(abs);
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
listFiles(dirPath) {
|
|
108
|
+
const abs = this.resolve(dirPath);
|
|
109
|
+
if (!fs2.existsSync(abs)) {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
return this.walkDir(abs).map((file) => path2.relative(this.root, file));
|
|
113
|
+
}
|
|
114
|
+
resolve(filePath) {
|
|
115
|
+
if (path2.isAbsolute(filePath)) {
|
|
116
|
+
return filePath;
|
|
117
|
+
}
|
|
118
|
+
return path2.join(this.root, filePath);
|
|
119
|
+
}
|
|
120
|
+
walkDir(dir) {
|
|
121
|
+
const results = [];
|
|
122
|
+
const entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
123
|
+
for (const entry of entries) {
|
|
124
|
+
const full = path2.join(dir, entry.name);
|
|
125
|
+
if (entry.isDirectory()) {
|
|
126
|
+
results.push(...this.walkDir(full));
|
|
127
|
+
} else {
|
|
128
|
+
results.push(full);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return results;
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
// ../kos-codegen-core/src/lib/generate-files.ts
|
|
136
|
+
import * as ejs from "ejs";
|
|
137
|
+
|
|
138
|
+
// ../kos-codegen-core/src/lib/logger.ts
|
|
139
|
+
var noopLogger = {
|
|
140
|
+
debug: () => {
|
|
141
|
+
},
|
|
142
|
+
info: () => {
|
|
143
|
+
},
|
|
144
|
+
warn: () => {
|
|
145
|
+
},
|
|
146
|
+
error: () => {
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
var activeLogger = noopLogger;
|
|
150
|
+
function getCodegenLogger() {
|
|
151
|
+
return activeLogger;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// ../kos-codegen-core/src/lib/project-discovery.ts
|
|
155
|
+
import * as fs3 from "fs";
|
|
156
|
+
import * as path3 from "path";
|
|
157
|
+
import fg from "fast-glob";
|
|
158
|
+
function discoverProjects(workspaceRoot) {
|
|
159
|
+
const logger = getCodegenLogger();
|
|
160
|
+
const projects = /* @__PURE__ */ new Map();
|
|
161
|
+
const projectJsonPaths = fg.sync("**/project.json", {
|
|
162
|
+
cwd: workspaceRoot,
|
|
163
|
+
ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"],
|
|
164
|
+
absolute: false
|
|
165
|
+
});
|
|
166
|
+
for (const relPath of projectJsonPaths) {
|
|
167
|
+
const absPath = path3.join(workspaceRoot, relPath);
|
|
168
|
+
try {
|
|
169
|
+
const raw = fs3.readFileSync(absPath, "utf-8");
|
|
170
|
+
const json = JSON.parse(raw);
|
|
171
|
+
const projectRoot = path3.dirname(relPath);
|
|
172
|
+
const name = json.name ?? path3.basename(projectRoot);
|
|
173
|
+
const config = {
|
|
174
|
+
name,
|
|
175
|
+
root: projectRoot,
|
|
176
|
+
sourceRoot: json.sourceRoot ?? path3.join(projectRoot, "src"),
|
|
177
|
+
projectType: json.projectType,
|
|
178
|
+
targets: json.targets,
|
|
179
|
+
tags: json.tags
|
|
180
|
+
};
|
|
181
|
+
projects.set(name, config);
|
|
182
|
+
logger.debug(`Discovered project: ${name} at ${projectRoot}`);
|
|
183
|
+
} catch (err) {
|
|
184
|
+
logger.warn(`Failed to parse ${absPath}: ${err}`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
logger.info(`Discovered ${projects.size} projects`);
|
|
188
|
+
return projects;
|
|
189
|
+
}
|
|
190
|
+
function findProjectByName(workspaceRoot, projectName, projects) {
|
|
191
|
+
const map = projects ?? discoverProjects(workspaceRoot);
|
|
192
|
+
return map.get(projectName);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ../kos-codegen-core/src/lib/format-files.ts
|
|
196
|
+
import prettier from "prettier";
|
|
197
|
+
|
|
198
|
+
// ../kos-codegen-core/src/lib/name-utils.ts
|
|
199
|
+
function dashCase(input) {
|
|
200
|
+
return input.replace(/\s+/g, "-").replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
|
|
201
|
+
}
|
|
202
|
+
function camelCase(input) {
|
|
203
|
+
if (input.length === 0)
|
|
204
|
+
return "";
|
|
205
|
+
const words = input.split(/-|\s+/);
|
|
206
|
+
if (words.length > 0 && words[0].length > 0) {
|
|
207
|
+
words[0] = words[0].charAt(0).toLowerCase() + words[0].slice(1);
|
|
208
|
+
}
|
|
209
|
+
for (let i = 1; i < words.length; i++) {
|
|
210
|
+
words[i] = words[i].charAt(0).toUpperCase() + words[i].slice(1);
|
|
211
|
+
}
|
|
212
|
+
return words.join("");
|
|
213
|
+
}
|
|
214
|
+
function pascalCase(input) {
|
|
215
|
+
if (input.length === 0)
|
|
216
|
+
return "";
|
|
217
|
+
const cc = camelCase(input);
|
|
218
|
+
return cc[0].toUpperCase() + cc.slice(1);
|
|
219
|
+
}
|
|
220
|
+
function properCase(input) {
|
|
221
|
+
const words = input.toLowerCase().replaceAll("-", " ").split(" ").filter(Boolean);
|
|
222
|
+
for (let i = 0; i < words.length; i++) {
|
|
223
|
+
words[i] = words[i][0].toUpperCase() + words[i].slice(1);
|
|
224
|
+
}
|
|
225
|
+
return words.join("");
|
|
226
|
+
}
|
|
227
|
+
function constantCase(input) {
|
|
228
|
+
if (input.length === 0)
|
|
229
|
+
return "";
|
|
230
|
+
return input.toUpperCase().split(/[\s-]+/).filter(Boolean).join("_");
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// ../kos-codegen-core/src/lib/normalize-values.ts
|
|
234
|
+
var normalizeValue = (optionsName, value) => ({
|
|
235
|
+
[`${camelCase(optionsName)}CamelCase`]: camelCase(value),
|
|
236
|
+
[`${camelCase(optionsName)}ConstantCase`]: constantCase(value),
|
|
237
|
+
[`${camelCase(optionsName)}DashCase`]: dashCase(value),
|
|
238
|
+
[`${camelCase(optionsName)}PascalCase`]: pascalCase(value),
|
|
239
|
+
[`${camelCase(optionsName)}ProperCase`]: properCase(value),
|
|
240
|
+
[`${camelCase(optionsName)}LowerCase`]: value.toLowerCase(),
|
|
241
|
+
[`${optionsName}`]: value
|
|
242
|
+
});
|
|
243
|
+
var normalizeAllValues = (options) => {
|
|
244
|
+
let normalizedValues = {};
|
|
245
|
+
for (const key in options) {
|
|
246
|
+
if (Object.prototype.hasOwnProperty.call(options, key)) {
|
|
247
|
+
const element = options[key];
|
|
248
|
+
const newOptions = typeof element !== "string" || element === "" ? { [key]: element } : normalizeValue(key, element);
|
|
249
|
+
normalizedValues = {
|
|
250
|
+
...normalizedValues,
|
|
251
|
+
...newOptions
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return normalizedValues;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
// ../kos-codegen-core/src/lib/kos-config.ts
|
|
259
|
+
import * as path4 from "path";
|
|
260
|
+
function getKosProjectConfiguration(codegenFs, projectName, projects) {
|
|
261
|
+
const project = findProjectByName(codegenFs.root, projectName, projects);
|
|
262
|
+
if (!project)
|
|
263
|
+
return void 0;
|
|
264
|
+
const configPath = path4.join(project.root, ".kos.json");
|
|
265
|
+
if (!codegenFs.exists(configPath)) {
|
|
266
|
+
const defaultConfig = {
|
|
267
|
+
name: `${dashCase(projectName)}-model`,
|
|
268
|
+
type: "kos.model",
|
|
269
|
+
version: "0.1.0",
|
|
270
|
+
models: {},
|
|
271
|
+
generator: { defaults: { model: { folder: "" } } }
|
|
272
|
+
};
|
|
273
|
+
codegenFs.write(configPath, JSON.stringify(defaultConfig, null, 2));
|
|
274
|
+
}
|
|
275
|
+
const content = codegenFs.read(configPath);
|
|
276
|
+
return content ? JSON.parse(content) : void 0;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ../kos-codegen-core/src/lib/generators/update-model-index.ts
|
|
280
|
+
import * as ts from "typescript";
|
|
281
|
+
|
|
282
|
+
// ../kos-codegen-core/src/lib/generators/add-future-to-model/normalize-options.ts
|
|
283
|
+
import * as path5 from "path";
|
|
284
|
+
function normalizeAddFutureOptions(codegenFs, options, projects) {
|
|
285
|
+
const projectConfiguration = findProjectByName(
|
|
286
|
+
codegenFs.root,
|
|
287
|
+
options.modelProject,
|
|
288
|
+
projects
|
|
289
|
+
);
|
|
290
|
+
if (!projectConfiguration) {
|
|
291
|
+
throw new Error(
|
|
292
|
+
`Project not found: ${options.modelProject}. Ensure a project.json exists for this project.`
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
const kosConfig = getKosProjectConfiguration(
|
|
296
|
+
codegenFs,
|
|
297
|
+
options.modelProject,
|
|
298
|
+
projects
|
|
299
|
+
);
|
|
300
|
+
const internal = !!kosConfig?.generator?.internal;
|
|
301
|
+
const normalizedValues = normalizeAllValues({
|
|
302
|
+
modelName: options.modelName
|
|
303
|
+
});
|
|
304
|
+
const nameDashCase = normalizedValues.modelNameDashCase;
|
|
305
|
+
const nameProperCase = normalizedValues.modelNameProperCase;
|
|
306
|
+
const nameCamelCase = normalizedValues.modelNameCamelCase;
|
|
307
|
+
const namePascalCase = normalizedValues.modelNamePascalCase;
|
|
308
|
+
const nameConstantCase = normalizedValues.modelNameConstantCase;
|
|
309
|
+
const nameLowerCase = normalizedValues.modelNameLowerCase;
|
|
310
|
+
const projectRoot = projectConfiguration.root;
|
|
311
|
+
const sourceRoot = projectConfiguration.sourceRoot || path5.join(projectRoot, "src");
|
|
312
|
+
const modelLocation = kosConfig?.generator?.defaults?.model?.folder || "";
|
|
313
|
+
const modelDirectory = path5.join(sourceRoot, modelLocation, nameDashCase);
|
|
314
|
+
const modelFilePath = path5.join(modelDirectory, `${nameDashCase}-model.ts`);
|
|
315
|
+
const servicesDirectory = path5.join(modelDirectory, "services");
|
|
316
|
+
const servicesFilePath = codegenFs.exists(servicesDirectory) ? path5.join(servicesDirectory, `${nameDashCase}-services.ts`) : void 0;
|
|
317
|
+
const registrationFilePath = path5.join(
|
|
318
|
+
modelDirectory,
|
|
319
|
+
`${nameDashCase}-registration.ts`
|
|
320
|
+
);
|
|
321
|
+
return {
|
|
322
|
+
...options,
|
|
323
|
+
nameDashCase,
|
|
324
|
+
nameProperCase,
|
|
325
|
+
nameCamelCase,
|
|
326
|
+
namePascalCase,
|
|
327
|
+
nameConstantCase,
|
|
328
|
+
nameLowerCase,
|
|
329
|
+
projectRoot,
|
|
330
|
+
sourceRoot,
|
|
331
|
+
modelFilePath,
|
|
332
|
+
servicesFilePath,
|
|
333
|
+
registrationFilePath: codegenFs.exists(registrationFilePath) ? registrationFilePath : void 0,
|
|
334
|
+
internal
|
|
335
|
+
};
|
|
336
|
+
}
|
|
6
337
|
|
|
7
|
-
|
|
338
|
+
// ../kos-codegen-core/src/lib/generators/add-future-to-model/model-transformer.ts
|
|
339
|
+
var ModelFileTransformer = class {
|
|
340
|
+
constructor(codegenFs, options) {
|
|
341
|
+
this.codegenFs = codegenFs;
|
|
342
|
+
this.options = options;
|
|
343
|
+
}
|
|
344
|
+
transform() {
|
|
345
|
+
const { modelFilePath } = this.options;
|
|
346
|
+
if (!this.codegenFs.exists(modelFilePath)) {
|
|
347
|
+
throw new Error(`Model file not found: ${modelFilePath}`);
|
|
348
|
+
}
|
|
349
|
+
let content = this.codegenFs.read(modelFilePath);
|
|
350
|
+
content = this.addESLintDisable(content);
|
|
351
|
+
content = this.addImports(content);
|
|
352
|
+
content = this.addServiceImport(content);
|
|
353
|
+
content = this.addInterfaceMerging(content);
|
|
354
|
+
content = this.addDecorator(content);
|
|
355
|
+
content = this.updatePublicType(content);
|
|
356
|
+
content = this.removeLegacySetup(content);
|
|
357
|
+
content = this.addFutureMethod(content);
|
|
358
|
+
if (this.options.futureType === "complete") {
|
|
359
|
+
content = this.addOnFutureUpdateMethod(content);
|
|
360
|
+
}
|
|
361
|
+
this.codegenFs.write(modelFilePath, content);
|
|
362
|
+
}
|
|
363
|
+
addESLintDisable(content) {
|
|
364
|
+
if (content.includes("@typescript-eslint/no-unsafe-declaration-merging")) {
|
|
365
|
+
return content;
|
|
366
|
+
}
|
|
367
|
+
const eslintDisable = "/* eslint-disable @typescript-eslint/no-unsafe-declaration-merging */\n";
|
|
368
|
+
return eslintDisable + content;
|
|
369
|
+
}
|
|
370
|
+
addImports(content) {
|
|
371
|
+
const { internal, futureType } = this.options;
|
|
372
|
+
const isComplete = futureType === "complete";
|
|
373
|
+
const kosModelImportRegex = internal ? /import\s*{\s*([^}]*kosModel[^}]*)\s*}\s*from\s*"\.\.\/\.\.\/\.\.\/core\/core\/decorators"/ : /import\s*{\s*([^}]*kosModel[^}]*)\s*}\s*from\s*"@kosdev-code\/kos-ui-sdk"/;
|
|
374
|
+
const kosModelMatch = content.match(kosModelImportRegex);
|
|
375
|
+
if (kosModelMatch) {
|
|
376
|
+
const existingImportsStr = kosModelMatch[1] || "";
|
|
377
|
+
const newImports = [
|
|
378
|
+
"kosFuture",
|
|
379
|
+
"kosFutureAware",
|
|
380
|
+
isComplete ? "KosFutureAwareFull" : "KosFutureAwareMinimal"
|
|
381
|
+
];
|
|
382
|
+
const existingImports = existingImportsStr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
383
|
+
const importsToRemove = [
|
|
384
|
+
"setupCompleteFutureSupport",
|
|
385
|
+
"setupMinimalFutureSupport"
|
|
386
|
+
];
|
|
387
|
+
const cleanedImports = existingImports.filter(
|
|
388
|
+
(imp) => !importsToRemove.includes(imp)
|
|
389
|
+
);
|
|
390
|
+
const importsToAdd = newImports.filter(
|
|
391
|
+
(imp) => !cleanedImports.includes(imp)
|
|
392
|
+
);
|
|
393
|
+
const allImports = [...cleanedImports, ...importsToAdd];
|
|
394
|
+
const newImportLine = internal ? `import { ${allImports.join(
|
|
395
|
+
", "
|
|
396
|
+
)} } from "../../../core/core/decorators"` : `import { ${allImports.join(", ")} } from "@kosdev-code/kos-ui-sdk"`;
|
|
397
|
+
content = content.replace(kosModelImportRegex, newImportLine);
|
|
398
|
+
}
|
|
399
|
+
const typeImportsBase = internal ? "../../../models/types/future-interfaces" : "@kosdev-code/kos-ui-sdk";
|
|
400
|
+
const futureTypeImports = ["ExternalFutureInterface", "IFutureModel"];
|
|
401
|
+
const typeImportRegex = internal ? /import type {([^}]*)} from "\.\.\/\.\.\/\.\.\/models\/types\/future-interfaces"/ : /import type {([^}]*)} from "@kosdev-code\/kos-ui-sdk"/;
|
|
402
|
+
const typeImportMatch = content.match(typeImportRegex);
|
|
403
|
+
if (typeImportMatch) {
|
|
404
|
+
const existingTypes = typeImportMatch[1] || "";
|
|
405
|
+
const existingTypesList = existingTypes.split(",").map((s) => s.trim()).filter(Boolean);
|
|
406
|
+
const typesToRemove = [
|
|
407
|
+
"FutureAwareContainer",
|
|
408
|
+
"FutureHandlerContainer",
|
|
409
|
+
"FutureStateAccessor",
|
|
410
|
+
"FutureUpdateHandler"
|
|
411
|
+
];
|
|
412
|
+
const cleanedTypes = existingTypesList.filter(
|
|
413
|
+
(type) => !typesToRemove.includes(type)
|
|
414
|
+
);
|
|
415
|
+
const typesToAdd = futureTypeImports.filter(
|
|
416
|
+
(type) => !cleanedTypes.includes(type)
|
|
417
|
+
);
|
|
418
|
+
const allTypes = [...cleanedTypes, ...typesToAdd].join(", ");
|
|
419
|
+
const newTypeImport = `import type { ${allTypes} } from "${typeImportsBase}"`;
|
|
420
|
+
content = content.replace(typeImportRegex, newTypeImport);
|
|
421
|
+
} else {
|
|
422
|
+
const importLines = content.split("\n");
|
|
423
|
+
const lastImportIndex = importLines.findLastIndex(
|
|
424
|
+
(line) => line.trim().startsWith("import")
|
|
425
|
+
);
|
|
426
|
+
if (lastImportIndex >= 0) {
|
|
427
|
+
const newTypeImport = `import type { ${futureTypeImports.join(
|
|
428
|
+
", "
|
|
429
|
+
)} } from "${typeImportsBase}";`;
|
|
430
|
+
importLines.splice(lastImportIndex + 1, 0, newTypeImport);
|
|
431
|
+
content = importLines.join("\n");
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return content;
|
|
435
|
+
}
|
|
436
|
+
addServiceImport(content) {
|
|
437
|
+
const { nameProperCase, updateServices } = this.options;
|
|
438
|
+
if (!updateServices) {
|
|
439
|
+
return content;
|
|
440
|
+
}
|
|
441
|
+
if (content.includes(`${nameProperCase}OperationProgress`)) {
|
|
442
|
+
return content;
|
|
443
|
+
}
|
|
444
|
+
const servicesImportRegex = /import\s*{([^}]*)}\s*from\s*["']\.\/services["'];?/s;
|
|
445
|
+
const servicesMatch = content.match(servicesImportRegex);
|
|
446
|
+
if (servicesMatch) {
|
|
447
|
+
const existingImports = servicesMatch[1];
|
|
448
|
+
const cleanedImports = existingImports.split(",").map((s) => s.trim()).filter(Boolean);
|
|
449
|
+
cleanedImports.push(`${nameProperCase}OperationProgress`);
|
|
450
|
+
const newImport = `import { ${cleanedImports.join(
|
|
451
|
+
", "
|
|
452
|
+
)} } from "./services";`;
|
|
453
|
+
content = content.replace(servicesImportRegex, newImport);
|
|
454
|
+
} else {
|
|
455
|
+
const typesImportRegex = /import\s+type\s+{[^}]*}\s+from\s+["']\.\/types["'];?/;
|
|
456
|
+
const typesMatch = content.match(typesImportRegex);
|
|
457
|
+
if (typesMatch) {
|
|
458
|
+
const newImport = `
|
|
459
|
+
import type { ${nameProperCase}OperationProgress } from "./services";`;
|
|
460
|
+
content = content.replace(typesMatch[0], typesMatch[0] + newImport);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
return content;
|
|
464
|
+
}
|
|
465
|
+
addInterfaceMerging(content) {
|
|
466
|
+
const { nameProperCase, futureType } = this.options;
|
|
467
|
+
const isComplete = futureType === "complete";
|
|
468
|
+
const interfaceType = isComplete ? "KosFutureAwareFull" : "KosFutureAwareMinimal";
|
|
469
|
+
const progressType = this.options.updateServices ? `${nameProperCase}OperationProgress` : "Record<string, unknown>";
|
|
470
|
+
const classRegex = new RegExp(
|
|
471
|
+
`(@kosModel[^\\n]*\\n)([^\\n]*export\\s+class\\s+${nameProperCase}ModelImpl)`,
|
|
472
|
+
"m"
|
|
473
|
+
);
|
|
474
|
+
const classMatch = content.match(classRegex);
|
|
475
|
+
if (classMatch) {
|
|
476
|
+
const interfaceRegex = new RegExp(
|
|
477
|
+
`interface\\s+${nameProperCase}ModelImpl\\s+extends`
|
|
478
|
+
);
|
|
479
|
+
if (!content.match(interfaceRegex)) {
|
|
480
|
+
const interfaceMerging = `
|
|
481
|
+
// Interface merging for Future Container type safety
|
|
482
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
|
483
|
+
export interface ${nameProperCase}ModelImpl extends ${interfaceType}<${progressType}> {}
|
|
484
|
+
|
|
485
|
+
`;
|
|
486
|
+
content = content.replace(
|
|
487
|
+
classMatch[0],
|
|
488
|
+
interfaceMerging + classMatch[0]
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return content;
|
|
493
|
+
}
|
|
494
|
+
addDecorator(content) {
|
|
495
|
+
const { nameProperCase, futureType } = this.options;
|
|
496
|
+
const isComplete = futureType === "complete";
|
|
497
|
+
const classRegex = new RegExp(
|
|
498
|
+
`(@kosModel[^\\n]*\\n)((?:@[^\\n]*\\n)*)([^\\n]*export\\s+class\\s+${nameProperCase}ModelImpl)`,
|
|
499
|
+
"m"
|
|
500
|
+
);
|
|
501
|
+
const classMatch = content.match(classRegex);
|
|
502
|
+
if (classMatch) {
|
|
503
|
+
if (!classMatch[2].includes("@kosFutureAware")) {
|
|
504
|
+
const decoratorOptions = isComplete ? "" : "{ mode: 'minimal' }";
|
|
505
|
+
const futureDecorator = `@kosFutureAware(${decoratorOptions})
|
|
506
|
+
`;
|
|
507
|
+
content = content.replace(
|
|
508
|
+
classMatch[0],
|
|
509
|
+
classMatch[1] + classMatch[2] + futureDecorator + classMatch[3]
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return content;
|
|
514
|
+
}
|
|
515
|
+
updatePublicType(content) {
|
|
516
|
+
const { nameProperCase, updateServices } = this.options;
|
|
517
|
+
const progressType = updateServices ? `${nameProperCase}OperationProgress` : "Record<string, unknown>";
|
|
518
|
+
const typeRegex = new RegExp(
|
|
519
|
+
`export\\s+type\\s+${nameProperCase}Model\\s*=\\s*PublicModelInterface<${nameProperCase}ModelImpl>([^;]*);`,
|
|
520
|
+
"s"
|
|
521
|
+
);
|
|
522
|
+
const typeMatch = content.match(typeRegex);
|
|
523
|
+
if (typeMatch) {
|
|
524
|
+
if (!typeMatch[1].includes("ExternalFutureInterface")) {
|
|
525
|
+
const newType = `export type ${nameProperCase}Model = PublicModelInterface<${nameProperCase}ModelImpl> & ExternalFutureInterface<${progressType}>;`;
|
|
526
|
+
content = content.replace(typeMatch[0], newType);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return content;
|
|
530
|
+
}
|
|
531
|
+
removeLegacySetup(content) {
|
|
532
|
+
const setupRegex = /\s*setup(Complete|Minimal)FutureSupport\(this\);?\s*/g;
|
|
533
|
+
content = content.replace(setupRegex, "");
|
|
534
|
+
const propertyRegex = /\s*(public|private|protected)?\s*(declare\s+)?futureHandler[!?]?:\s*FutureAwareContainer[^;]*;\s*/g;
|
|
535
|
+
content = content.replace(propertyRegex, "");
|
|
536
|
+
const futurePropertyRegex = /\s*(public|private|protected)?\s*(declare\s+)?future\??:\s*IFutureModel[^;]*;\s*/g;
|
|
537
|
+
content = content.replace(futurePropertyRegex, "");
|
|
538
|
+
const implementsRegex = new RegExp(
|
|
539
|
+
`(implements\\s+[^{]*?)\\s*,?\\s*(FutureUpdateHandler|FutureHandlerContainer|FutureStateAccessor)`,
|
|
540
|
+
"g"
|
|
541
|
+
);
|
|
542
|
+
content = content.replace(implementsRegex, "$1");
|
|
543
|
+
content = content.replace(/,\s*,/g, ",");
|
|
544
|
+
content = content.replace(/implements\s*,/g, "implements");
|
|
545
|
+
return content;
|
|
546
|
+
}
|
|
547
|
+
addFutureMethod(content) {
|
|
548
|
+
const { nameProperCase } = this.options;
|
|
549
|
+
if (content.includes("@kosFuture()")) {
|
|
550
|
+
return content;
|
|
551
|
+
}
|
|
552
|
+
const classRegex = new RegExp(
|
|
553
|
+
`class\\s+${nameProperCase}ModelImpl[^{]*{([\\s\\S]*)}\\s*$`,
|
|
554
|
+
"m"
|
|
555
|
+
);
|
|
556
|
+
const classMatch = content.match(classRegex);
|
|
557
|
+
if (classMatch) {
|
|
558
|
+
const methodCode = `
|
|
559
|
+
/**
|
|
560
|
+
* Placeholder method for Future operations
|
|
561
|
+
* Replace this with your actual long-running operation
|
|
562
|
+
*/
|
|
563
|
+
@kosFuture()
|
|
564
|
+
async performLongRunningOperation(): Promise<void> {
|
|
565
|
+
// TODO: Implement your long-running operation here
|
|
566
|
+
// This method should use a service that returns a Future for progress tracking
|
|
567
|
+
|
|
568
|
+
this.logger.debug(\`Starting long-running operation for \${this.id}\`);
|
|
569
|
+
|
|
570
|
+
// Example implementation pattern using services:
|
|
571
|
+
// import { perform${nameProperCase}Operation } from './services';
|
|
572
|
+
//
|
|
573
|
+
// const future = await perform${nameProperCase}Operation();
|
|
574
|
+
// return this.futureHandler.setFuture(future);
|
|
575
|
+
|
|
576
|
+
// Placeholder that doesn't actually do anything
|
|
577
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
578
|
+
|
|
579
|
+
this.logger.debug(\`Completed long-running operation for \${this.id}\`);
|
|
580
|
+
}
|
|
581
|
+
`;
|
|
582
|
+
const classContent = classMatch[1];
|
|
583
|
+
const lastBraceIndex = classContent.lastIndexOf("}");
|
|
584
|
+
if (lastBraceIndex >= 0) {
|
|
585
|
+
const updatedContent = classContent.slice(0, lastBraceIndex) + methodCode + classContent.slice(lastBraceIndex);
|
|
586
|
+
content = content.replace(
|
|
587
|
+
classMatch[0],
|
|
588
|
+
`class ${nameProperCase}ModelImpl${classMatch[0].match(/[^{]*/)?.[0]}{${updatedContent}}`
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return content;
|
|
593
|
+
}
|
|
594
|
+
addOnFutureUpdateMethod(content) {
|
|
595
|
+
const { nameProperCase, updateServices } = this.options;
|
|
596
|
+
const progressType = updateServices ? `${nameProperCase}OperationProgress` : "Record<string, unknown>";
|
|
597
|
+
if (content.includes("onFutureUpdate")) {
|
|
598
|
+
return content;
|
|
599
|
+
}
|
|
600
|
+
const futureMethodRegex = /@kosFuture\(\)[^}]*}/s;
|
|
601
|
+
const futureMethodMatch = content.match(futureMethodRegex);
|
|
602
|
+
if (futureMethodMatch) {
|
|
603
|
+
const methodCode = `
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Optional: Custom Future update handling
|
|
607
|
+
* Called whenever the Future state changes (progress, status, completion, etc.)
|
|
608
|
+
*/
|
|
609
|
+
onFutureUpdate?(update: IFutureModel<${progressType}>): void {
|
|
610
|
+
// Add custom Future update logic here
|
|
611
|
+
// Examples:
|
|
612
|
+
// - Log progress milestones
|
|
613
|
+
// - Update derived state based on progress
|
|
614
|
+
// - Handle specific error conditions
|
|
615
|
+
// - Trigger notifications at certain thresholds
|
|
616
|
+
|
|
617
|
+
this.logger.debug(\`Future update for \${this.id}:\`, {
|
|
618
|
+
progress: update.progress,
|
|
619
|
+
status: update.status,
|
|
620
|
+
endState: update.endState,
|
|
621
|
+
clientData: update.clientData
|
|
622
|
+
});
|
|
623
|
+
}`;
|
|
624
|
+
content = content.replace(
|
|
625
|
+
futureMethodMatch[0],
|
|
626
|
+
futureMethodMatch[0] + methodCode
|
|
627
|
+
);
|
|
628
|
+
}
|
|
629
|
+
return content;
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
// ../kos-codegen-core/src/lib/generators/add-future-to-model/service-transformer.ts
|
|
634
|
+
var ServiceFileTransformer = class {
|
|
635
|
+
constructor(codegenFs, options) {
|
|
636
|
+
this.codegenFs = codegenFs;
|
|
637
|
+
this.options = options;
|
|
638
|
+
}
|
|
639
|
+
transform() {
|
|
640
|
+
const { servicesFilePath } = this.options;
|
|
641
|
+
if (!servicesFilePath || !this.codegenFs.exists(servicesFilePath)) {
|
|
642
|
+
this.createServicesFile();
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
let content = this.codegenFs.read(servicesFilePath);
|
|
646
|
+
content = this.addFutureImports(content);
|
|
647
|
+
content = this.addFutureService(content);
|
|
648
|
+
content = this.addProgressTypes(content);
|
|
649
|
+
this.codegenFs.write(servicesFilePath, content);
|
|
650
|
+
}
|
|
651
|
+
createServicesFile() {
|
|
652
|
+
const { servicesFilePath, nameProperCase, nameDashCase, nameLowerCase } = this.options;
|
|
653
|
+
if (!servicesFilePath) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
const content = `import {
|
|
657
|
+
KosLog,
|
|
658
|
+
type ClientResponse,
|
|
659
|
+
type DeepRequired,
|
|
660
|
+
type ElementType,
|
|
661
|
+
type ServiceResponse,
|
|
662
|
+
type FutureResponse
|
|
663
|
+
} from '@kosdev-code/kos-ui-sdk';
|
|
664
|
+
|
|
665
|
+
import API, { type KosApi, type ApiPath } from '../../../utils/service';
|
|
666
|
+
|
|
667
|
+
const log = KosLog.createLogger({name: "${nameDashCase}-service", group: "Services"});
|
|
668
|
+
|
|
669
|
+
const SERVICE_PATH: ApiPath = "ENTER_SERVICE_PATH"
|
|
670
|
+
export type ${nameProperCase}ClientResponse = ClientResponse<
|
|
671
|
+
KosApi,
|
|
672
|
+
typeof SERVICE_PATH,
|
|
673
|
+
'get'
|
|
674
|
+
>;
|
|
675
|
+
export type ${nameProperCase}Response = DeepRequired<${nameProperCase}ClientResponse>;
|
|
676
|
+
|
|
677
|
+
/**
|
|
678
|
+
* @category Service
|
|
679
|
+
* Retrieves the initial ${nameLowerCase} data.
|
|
680
|
+
*/
|
|
681
|
+
export const get${nameProperCase} = async (): Promise<ServiceResponse<${nameProperCase}Response>> => {
|
|
682
|
+
log.debug('sending GET for ${nameLowerCase}');
|
|
683
|
+
return await API.get(SERVICE_PATH);
|
|
684
|
+
};
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* @category Service - Future Operation
|
|
688
|
+
* Placeholder for a long-running operation that returns a Future for progress tracking
|
|
689
|
+
*
|
|
690
|
+
* Replace this with your actual long-running service operation
|
|
691
|
+
*/
|
|
692
|
+
export const perform${nameProperCase}Operation = async (): Promise<FutureResponse> => {
|
|
693
|
+
// TODO: Implement your long-running service operation here
|
|
694
|
+
// This should return a Future that can be tracked for progress
|
|
695
|
+
|
|
696
|
+
log.debug('starting long-running ${nameLowerCase} operation');
|
|
697
|
+
|
|
698
|
+
// Example pattern:
|
|
699
|
+
// return API.post(OPERATION_SERVICE_PATH, {
|
|
700
|
+
// // operation parameters
|
|
701
|
+
// });
|
|
702
|
+
|
|
703
|
+
// Placeholder - replace with actual implementation
|
|
704
|
+
throw new Error('perform${nameProperCase}Operation not yet implemented');
|
|
705
|
+
};
|
|
706
|
+
|
|
707
|
+
// Additional Future-aware service types (add as needed)
|
|
708
|
+
export type ${nameProperCase}OperationProgress = {
|
|
709
|
+
// Define your progress data structure here
|
|
710
|
+
stage: string;
|
|
711
|
+
percentComplete: number;
|
|
712
|
+
currentItem?: string;
|
|
713
|
+
totalItems?: number;
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
export type ${nameProperCase}OperationResult = {
|
|
717
|
+
// Define your operation result structure here
|
|
718
|
+
success: boolean;
|
|
719
|
+
message?: string;
|
|
720
|
+
data?: any;
|
|
721
|
+
};
|
|
722
|
+
`;
|
|
723
|
+
this.codegenFs.write(servicesFilePath, content);
|
|
724
|
+
}
|
|
725
|
+
addFutureImports(content) {
|
|
726
|
+
if (content.includes("FutureResponse")) {
|
|
727
|
+
return content;
|
|
728
|
+
}
|
|
729
|
+
const importRegex = /import {\s*([^}]*)\s*} from '@kosdev-code\/kos-ui-sdk';/;
|
|
730
|
+
const importMatch = content.match(importRegex);
|
|
731
|
+
if (importMatch) {
|
|
732
|
+
const existingImports = importMatch[1];
|
|
733
|
+
if (existingImports.includes("FutureResponse")) {
|
|
734
|
+
return content;
|
|
735
|
+
}
|
|
736
|
+
const cleanedImports = existingImports.trim().replace(/,\s*$/, "");
|
|
737
|
+
const newImports = cleanedImports ? `${cleanedImports},
|
|
738
|
+
type FutureResponse` : `type FutureResponse`;
|
|
739
|
+
const newImportStatement = `import {
|
|
740
|
+
${newImports}
|
|
741
|
+
} from '@kosdev-code/kos-ui-sdk';`;
|
|
742
|
+
return content.replace(importMatch[0], newImportStatement);
|
|
743
|
+
}
|
|
744
|
+
return content;
|
|
745
|
+
}
|
|
746
|
+
addFutureService(content) {
|
|
747
|
+
const { nameProperCase, nameLowerCase } = this.options;
|
|
748
|
+
if (content.includes(`perform${nameProperCase}Operation`)) {
|
|
749
|
+
return content;
|
|
750
|
+
}
|
|
751
|
+
const futureService = `
|
|
752
|
+
/**
|
|
753
|
+
* @category Service - Future Operation
|
|
754
|
+
* Placeholder for a long-running operation that returns a Future for progress tracking
|
|
755
|
+
*
|
|
756
|
+
* Replace this with your actual long-running service operation
|
|
757
|
+
*/
|
|
758
|
+
export const perform${nameProperCase}Operation = async (): Promise<FutureResponse> => {
|
|
759
|
+
// TODO: Implement your long-running service operation here
|
|
760
|
+
// This should return a Future that can be tracked for progress
|
|
761
|
+
|
|
762
|
+
log.debug('starting long-running ${nameLowerCase} operation');
|
|
763
|
+
|
|
764
|
+
// Example pattern:
|
|
765
|
+
// return API.post(OPERATION_SERVICE_PATH, {
|
|
766
|
+
// // operation parameters
|
|
767
|
+
// });
|
|
768
|
+
|
|
769
|
+
// Placeholder - replace with actual implementation
|
|
770
|
+
throw new Error('perform${nameProperCase}Operation not yet implemented');
|
|
771
|
+
};`;
|
|
772
|
+
return content + "\n" + futureService;
|
|
773
|
+
}
|
|
774
|
+
addProgressTypes(content) {
|
|
775
|
+
const { nameProperCase } = this.options;
|
|
776
|
+
if (content.includes(`${nameProperCase}OperationProgress`)) {
|
|
777
|
+
return content;
|
|
778
|
+
}
|
|
779
|
+
const progressTypes = `
|
|
780
|
+
// Additional Future-aware service types (add as needed)
|
|
781
|
+
export type ${nameProperCase}OperationProgress = {
|
|
782
|
+
// Define your progress data structure here
|
|
783
|
+
stage: string;
|
|
784
|
+
percentComplete: number;
|
|
785
|
+
currentItem?: string;
|
|
786
|
+
totalItems?: number;
|
|
787
|
+
};
|
|
788
|
+
|
|
789
|
+
export type ${nameProperCase}OperationResult = {
|
|
790
|
+
// Define your operation result structure here
|
|
791
|
+
success: boolean;
|
|
792
|
+
message?: string;
|
|
793
|
+
data?: any;
|
|
794
|
+
};`;
|
|
795
|
+
return content + "\n" + progressTypes;
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
|
|
799
|
+
// ../kos-codegen-core/src/lib/generators/add-future-to-model/registration-transformer.ts
|
|
800
|
+
var RegistrationFileTransformer = class {
|
|
801
|
+
constructor(codegenFs, options) {
|
|
802
|
+
this.codegenFs = codegenFs;
|
|
803
|
+
this.options = options;
|
|
804
|
+
}
|
|
805
|
+
transform() {
|
|
806
|
+
const logger = getCodegenLogger();
|
|
807
|
+
const { registrationFilePath } = this.options;
|
|
808
|
+
if (!registrationFilePath || !this.codegenFs.exists(registrationFilePath)) {
|
|
809
|
+
logger.warn(
|
|
810
|
+
"Registration file not found, skipping registration updates"
|
|
811
|
+
);
|
|
812
|
+
return;
|
|
813
|
+
}
|
|
814
|
+
let content = this.codegenFs.read(registrationFilePath);
|
|
815
|
+
content = this.addTypeCast(content);
|
|
816
|
+
content = this.updateDocumentation(content);
|
|
817
|
+
this.codegenFs.write(registrationFilePath, content);
|
|
818
|
+
}
|
|
819
|
+
addTypeCast(content) {
|
|
820
|
+
const { nameProperCase } = this.options;
|
|
821
|
+
const factoryRegex = new RegExp(`class: ${nameProperCase}ModelImpl,`);
|
|
822
|
+
const replacement = `class: ${nameProperCase}ModelImpl as any, // Type cast needed for Future intersection`;
|
|
823
|
+
return content.replace(factoryRegex, replacement);
|
|
824
|
+
}
|
|
825
|
+
updateDocumentation(content) {
|
|
826
|
+
const { nameProperCase, futureType } = this.options;
|
|
827
|
+
const descriptionRegex = new RegExp(
|
|
828
|
+
`(\\* The registration bean includes convenience methods for creating and working with ${nameProperCase}Model instances\\.)`
|
|
829
|
+
);
|
|
830
|
+
const futureDocumentation = `$1
|
|
831
|
+
*
|
|
832
|
+
* ## Future Support
|
|
833
|
+
* This model includes ${futureType} Future support for tracking long-running operations with:
|
|
834
|
+
* - Progress tracking (0-1) with reactive updates
|
|
835
|
+
* - Status messages during operation
|
|
836
|
+
* - Cancellation support with bi-directional AbortController integration
|
|
837
|
+
* - Reactive integration for UI updates${futureType === "complete" ? "\n * - Internal access to Future state for custom logic and computed properties" : ""}`;
|
|
838
|
+
if (content.match(descriptionRegex)) {
|
|
839
|
+
content = content.replace(descriptionRegex, futureDocumentation);
|
|
840
|
+
}
|
|
841
|
+
const factoryExampleRegex = /(\*\s+\}\);?\s*\*\s+```)/;
|
|
842
|
+
const futureExample = `$1
|
|
843
|
+
*
|
|
844
|
+
* // Example: Accessing Future state (when a Future is active)
|
|
845
|
+
* const isRunning = model.futureIsRunning;
|
|
846
|
+
* const progress = model.futureProgress; // 0-1
|
|
847
|
+
* const status = model.futureStatus; // Current status message
|
|
848
|
+
* \`\`\``;
|
|
849
|
+
if (content.match(factoryExampleRegex)) {
|
|
850
|
+
content = content.replace(factoryExampleRegex, futureExample);
|
|
851
|
+
}
|
|
852
|
+
const predicateExampleRegex = /(\*\s+model\.updateAvailability\(false\);?\s*\*\s+\})/;
|
|
853
|
+
const predicateFutureExample = `$1
|
|
854
|
+
*
|
|
855
|
+
* // Future capabilities are also available
|
|
856
|
+
* const isRunning = model.futureIsRunning;
|
|
857
|
+
* const progress = model.futureProgress;
|
|
858
|
+
* }`;
|
|
859
|
+
if (content.match(predicateExampleRegex)) {
|
|
860
|
+
content = content.replace(predicateExampleRegex, predicateFutureExample);
|
|
861
|
+
}
|
|
862
|
+
return content;
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
|
|
866
|
+
// ../kos-codegen-core/src/lib/generators/add-future-to-model/generate-add-future-to-model.ts
|
|
867
|
+
function addFutureToModel(codegenFs, options, projects) {
|
|
868
|
+
const logger = getCodegenLogger();
|
|
869
|
+
const normalized = normalizeAddFutureOptions(codegenFs, options, projects);
|
|
870
|
+
logger.info(
|
|
871
|
+
`Adding ${normalized.futureType} Future support to model: ${normalized.modelName}`
|
|
872
|
+
);
|
|
873
|
+
if (!codegenFs.exists(normalized.modelFilePath)) {
|
|
874
|
+
throw new Error(`Model file not found: ${normalized.modelFilePath}`);
|
|
875
|
+
}
|
|
876
|
+
if (options.dryRun) {
|
|
877
|
+
logger.info("DRY RUN - No files will be modified");
|
|
878
|
+
logger.info(`Would modify model file: ${normalized.modelFilePath}`);
|
|
879
|
+
if (normalized.servicesFilePath) {
|
|
880
|
+
logger.info(
|
|
881
|
+
`Would modify/create services file: ${normalized.servicesFilePath}`
|
|
882
|
+
);
|
|
883
|
+
}
|
|
884
|
+
if (normalized.registrationFilePath) {
|
|
885
|
+
logger.info(
|
|
886
|
+
`Would modify registration file: ${normalized.registrationFilePath}`
|
|
887
|
+
);
|
|
888
|
+
}
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
try {
|
|
892
|
+
logger.info(`Transforming model file: ${normalized.modelFilePath}`);
|
|
893
|
+
const modelTransformer = new ModelFileTransformer(codegenFs, normalized);
|
|
894
|
+
modelTransformer.transform();
|
|
895
|
+
if (normalized.updateServices) {
|
|
896
|
+
logger.info(
|
|
897
|
+
`Transforming services file: ${normalized.servicesFilePath || "creating new"}`
|
|
898
|
+
);
|
|
899
|
+
const serviceTransformer = new ServiceFileTransformer(
|
|
900
|
+
codegenFs,
|
|
901
|
+
normalized
|
|
902
|
+
);
|
|
903
|
+
serviceTransformer.transform();
|
|
904
|
+
}
|
|
905
|
+
if (normalized.registrationFilePath) {
|
|
906
|
+
logger.info(
|
|
907
|
+
`Transforming registration file: ${normalized.registrationFilePath}`
|
|
908
|
+
);
|
|
909
|
+
const registrationTransformer = new RegistrationFileTransformer(
|
|
910
|
+
codegenFs,
|
|
911
|
+
normalized
|
|
912
|
+
);
|
|
913
|
+
registrationTransformer.transform();
|
|
914
|
+
}
|
|
915
|
+
logger.info(
|
|
916
|
+
`Successfully added ${normalized.futureType} Future support to ${normalized.modelName}`
|
|
917
|
+
);
|
|
918
|
+
logger.info("");
|
|
919
|
+
logger.info("Next steps:");
|
|
920
|
+
logger.info(
|
|
921
|
+
"1. Review the generated @kosFuture method and implement your actual operation"
|
|
922
|
+
);
|
|
923
|
+
logger.info(
|
|
924
|
+
"2. Update the service method to return a proper FutureResponse"
|
|
925
|
+
);
|
|
926
|
+
logger.info("3. Define your specific progress and result types");
|
|
927
|
+
if (normalized.futureType === "complete") {
|
|
928
|
+
logger.info(
|
|
929
|
+
"4. Customize the onFutureUpdate method for your specific needs"
|
|
930
|
+
);
|
|
931
|
+
}
|
|
932
|
+
} catch (error) {
|
|
933
|
+
logger.error(`Failed to add Future support: ${error}`);
|
|
934
|
+
throw error;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
// ../kos-codegen-core/src/lib/generators/component/types.ts
|
|
939
|
+
var PLUGIN_TYPES = {
|
|
940
|
+
CUI: "cui",
|
|
941
|
+
UTILITY: "utility",
|
|
942
|
+
TROUBLE_ACTION: "troubleAction",
|
|
943
|
+
SETUP: "setup",
|
|
944
|
+
SETTING: "setting",
|
|
945
|
+
NAV: "nav",
|
|
946
|
+
CONTROL_POUR: "controlPour",
|
|
947
|
+
CUSTOM: "custom"
|
|
948
|
+
};
|
|
949
|
+
var CONTRIBUTION_TYPE_MAP = {
|
|
950
|
+
[PLUGIN_TYPES.SETUP]: "setup",
|
|
951
|
+
[PLUGIN_TYPES.CUI]: "cui",
|
|
952
|
+
[PLUGIN_TYPES.UTILITY]: "utility",
|
|
953
|
+
[PLUGIN_TYPES.SETTING]: "setting",
|
|
954
|
+
[PLUGIN_TYPES.NAV]: "nav",
|
|
955
|
+
[PLUGIN_TYPES.TROUBLE_ACTION]: "trouble-action",
|
|
956
|
+
[PLUGIN_TYPES.CONTROL_POUR]: "control-pour",
|
|
957
|
+
[PLUGIN_TYPES.CUSTOM]: "custom"
|
|
958
|
+
};
|
|
959
|
+
var LOCALIZED_PLUGIN_TYPES = /* @__PURE__ */ new Set([
|
|
960
|
+
PLUGIN_TYPES.CUI,
|
|
961
|
+
PLUGIN_TYPES.UTILITY,
|
|
962
|
+
PLUGIN_TYPES.SETUP,
|
|
963
|
+
PLUGIN_TYPES.SETTING,
|
|
964
|
+
PLUGIN_TYPES.NAV,
|
|
965
|
+
PLUGIN_TYPES.CONTROL_POUR,
|
|
966
|
+
PLUGIN_TYPES.TROUBLE_ACTION,
|
|
967
|
+
PLUGIN_TYPES.CUSTOM
|
|
968
|
+
]);
|
|
969
|
+
|
|
970
|
+
// ../kos-codegen-core/src/lib/generators/component/plugin-handlers/base.ts
|
|
971
|
+
var BasePluginHandler = class {
|
|
972
|
+
getContributionKey() {
|
|
973
|
+
return this.contributionKey;
|
|
974
|
+
}
|
|
975
|
+
requiresLocalization() {
|
|
976
|
+
return this.requiresI18n;
|
|
977
|
+
}
|
|
978
|
+
getTemplatePath() {
|
|
979
|
+
return this.contributionKey;
|
|
980
|
+
}
|
|
981
|
+
/**
|
|
982
|
+
* Helper to create experience configuration
|
|
983
|
+
*/
|
|
984
|
+
createExperience(options, experienceId) {
|
|
985
|
+
const compPath = this.getComponentPath(options);
|
|
986
|
+
return {
|
|
987
|
+
id: experienceId,
|
|
988
|
+
component: options.namePascalCase,
|
|
989
|
+
location: `./src/${compPath}`
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
/**
|
|
993
|
+
* Helper to get component path
|
|
994
|
+
*/
|
|
995
|
+
getComponentPath(options) {
|
|
996
|
+
return `${options.appDirectory}/${this.contributionKey}/${options.nameDashCase}/${options.nameDashCase}.tsx`;
|
|
997
|
+
}
|
|
998
|
+
/**
|
|
999
|
+
* Helper to create config prefix
|
|
1000
|
+
*/
|
|
1001
|
+
getConfigPrefix(options) {
|
|
1002
|
+
return `${options.appProject}.${options.nameCamelCase}`;
|
|
1003
|
+
}
|
|
1004
|
+
};
|
|
1005
|
+
|
|
1006
|
+
// ../kos-codegen-core/src/lib/generators/component/plugin-handlers/control-pour-handler.ts
|
|
1007
|
+
var ControlPourPluginHandler = class extends BasePluginHandler {
|
|
1008
|
+
pluginType = PLUGIN_TYPES.CONTROL_POUR;
|
|
1009
|
+
contributionKey = "control-pour";
|
|
1010
|
+
requiresI18n = true;
|
|
1011
|
+
createConfiguration(options) {
|
|
1012
|
+
const configPrefix = this.getConfigPrefix(options);
|
|
1013
|
+
const experienceId = `${configPrefix}.controlPour.experience`;
|
|
1014
|
+
const contribution = {
|
|
1015
|
+
id: `${configPrefix}.controlPour`,
|
|
1016
|
+
title: `${configPrefix}.controlPour.title`,
|
|
1017
|
+
namespace: options.appProject,
|
|
1018
|
+
experienceId
|
|
1019
|
+
};
|
|
1020
|
+
const experience = this.createExperience(options, experienceId);
|
|
1021
|
+
return {
|
|
1022
|
+
contributions: {
|
|
1023
|
+
controlPour: [contribution]
|
|
1024
|
+
},
|
|
1025
|
+
experiences: {
|
|
1026
|
+
[experienceId]: experience
|
|
1027
|
+
}
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1031
|
+
|
|
1032
|
+
// ../kos-codegen-core/src/lib/generators/component/plugin-handlers/cui-handler.ts
|
|
1033
|
+
var CuiPluginHandler = class extends BasePluginHandler {
|
|
1034
|
+
pluginType = PLUGIN_TYPES.CUI;
|
|
1035
|
+
contributionKey = "cui";
|
|
1036
|
+
requiresI18n = true;
|
|
1037
|
+
createConfiguration(options) {
|
|
1038
|
+
const configPrefix = this.getConfigPrefix(options);
|
|
1039
|
+
const experienceId = `${configPrefix}.cui.experience`;
|
|
1040
|
+
const contribution = {
|
|
1041
|
+
id: configPrefix,
|
|
1042
|
+
title: `${configPrefix}.cui.title`,
|
|
1043
|
+
namespace: options.appProject,
|
|
1044
|
+
experienceId
|
|
1045
|
+
};
|
|
1046
|
+
const experience = this.createExperience(options, experienceId);
|
|
1047
|
+
return {
|
|
1048
|
+
contributions: {
|
|
1049
|
+
cui: [contribution]
|
|
1050
|
+
},
|
|
1051
|
+
experiences: {
|
|
1052
|
+
[experienceId]: experience
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
}
|
|
1056
|
+
};
|
|
1057
|
+
|
|
1058
|
+
// ../kos-codegen-core/src/lib/generators/component/plugin-handlers/custom-handler.ts
|
|
1059
|
+
var CustomPluginHandler = class extends BasePluginHandler {
|
|
1060
|
+
pluginType = PLUGIN_TYPES.CUSTOM;
|
|
1061
|
+
contributionKey = "custom";
|
|
1062
|
+
requiresI18n = true;
|
|
1063
|
+
createConfiguration(options) {
|
|
1064
|
+
const configPrefix = this.getConfigPrefix(options);
|
|
1065
|
+
const experienceId = `${configPrefix}.${options.contributionKey || "custom"}.experience`;
|
|
1066
|
+
const userContributionKey = options.contributionKey || "custom";
|
|
1067
|
+
const contribution = {
|
|
1068
|
+
id: configPrefix,
|
|
1069
|
+
title: `${configPrefix}.${userContributionKey}.title`,
|
|
1070
|
+
namespace: options.appProject,
|
|
1071
|
+
experienceId
|
|
1072
|
+
// TODO: Add additional fields as required by the plugin-explorer specification
|
|
1073
|
+
// Refer to the plugin-explorer documentation for your specific contribution type
|
|
1074
|
+
};
|
|
1075
|
+
const experience = this.createExperience(options, experienceId);
|
|
1076
|
+
return {
|
|
1077
|
+
contributions: {
|
|
1078
|
+
[userContributionKey]: [contribution]
|
|
1079
|
+
},
|
|
1080
|
+
experiences: {
|
|
1081
|
+
[experienceId]: experience
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
getTemplatePath() {
|
|
1086
|
+
return this.contributionKey || "custom";
|
|
1087
|
+
}
|
|
1088
|
+
getComponentPath(options) {
|
|
1089
|
+
const pathKey = options.contributionKey || "custom";
|
|
1090
|
+
return `${options.appDirectory}/${pathKey}/${options.nameDashCase}/${options.nameDashCase}.tsx`;
|
|
1091
|
+
}
|
|
1092
|
+
};
|
|
1093
|
+
|
|
1094
|
+
// ../kos-codegen-core/src/lib/generators/component/plugin-handlers/default-handler.ts
|
|
1095
|
+
var DefaultComponentHandler = class extends BasePluginHandler {
|
|
1096
|
+
pluginType = "component";
|
|
1097
|
+
contributionKey = "components";
|
|
1098
|
+
requiresI18n = false;
|
|
1099
|
+
createConfiguration(options) {
|
|
1100
|
+
const compPath = this.getComponentPath(options);
|
|
1101
|
+
const viewConfig = {
|
|
1102
|
+
id: `${options.appProject}.${options.nameCamelCase}`,
|
|
1103
|
+
title: "ddk.ncui.config.title",
|
|
1104
|
+
namespace: options.appProject,
|
|
1105
|
+
component: options.namePascalCase,
|
|
1106
|
+
location: `./src/${compPath}`
|
|
1107
|
+
};
|
|
1108
|
+
return {
|
|
1109
|
+
contributions: {},
|
|
1110
|
+
experiences: {},
|
|
1111
|
+
views: {
|
|
1112
|
+
[this.getTabViewKey()]: [viewConfig]
|
|
1113
|
+
}
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
getTabViewKey() {
|
|
1117
|
+
return "ddk.ncui.settings.tabView";
|
|
1118
|
+
}
|
|
1119
|
+
getTemplatePath() {
|
|
1120
|
+
return "files";
|
|
1121
|
+
}
|
|
1122
|
+
};
|
|
1123
|
+
|
|
1124
|
+
// ../kos-codegen-core/src/lib/generators/component/plugin-handlers/nav-handler.ts
|
|
1125
|
+
var NavPluginHandler = class extends BasePluginHandler {
|
|
1126
|
+
pluginType = PLUGIN_TYPES.NAV;
|
|
1127
|
+
contributionKey = "nav";
|
|
1128
|
+
requiresI18n = true;
|
|
1129
|
+
createConfiguration(options) {
|
|
1130
|
+
const configPrefix = this.getConfigPrefix(options);
|
|
1131
|
+
const experienceId = `${configPrefix}.nav.experience`;
|
|
1132
|
+
const contribution = {
|
|
1133
|
+
id: `${configPrefix}.nav`,
|
|
1134
|
+
title: `${configPrefix}.nav.title`,
|
|
1135
|
+
namespace: options.appProject,
|
|
1136
|
+
navDescriptor: options.nameLowerCase,
|
|
1137
|
+
experienceId
|
|
1138
|
+
};
|
|
1139
|
+
const experience = this.createExperience(options, experienceId);
|
|
1140
|
+
return {
|
|
1141
|
+
contributions: {
|
|
1142
|
+
navViews: [contribution]
|
|
1143
|
+
},
|
|
1144
|
+
experiences: {
|
|
1145
|
+
[experienceId]: experience
|
|
1146
|
+
}
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
};
|
|
1150
|
+
|
|
1151
|
+
// ../kos-codegen-core/src/lib/generators/component/plugin-handlers/setting-handler.ts
|
|
1152
|
+
var SettingPluginHandler = class extends BasePluginHandler {
|
|
1153
|
+
pluginType = PLUGIN_TYPES.SETTING;
|
|
1154
|
+
contributionKey = "setting";
|
|
1155
|
+
requiresI18n = true;
|
|
1156
|
+
createConfiguration(options) {
|
|
1157
|
+
const configPrefix = this.getConfigPrefix(options);
|
|
1158
|
+
const experienceId = `${configPrefix}.settings.experience`;
|
|
1159
|
+
const contribution = {
|
|
1160
|
+
id: `${configPrefix}.setting`,
|
|
1161
|
+
title: `${configPrefix}.setting.title`,
|
|
1162
|
+
namespace: options.appProject,
|
|
1163
|
+
settingsGroup: options.group || "general",
|
|
1164
|
+
experienceId
|
|
1165
|
+
};
|
|
1166
|
+
const experience = this.createExperience(options, experienceId);
|
|
1167
|
+
return {
|
|
1168
|
+
contributions: {
|
|
1169
|
+
settings: [contribution]
|
|
1170
|
+
},
|
|
1171
|
+
experiences: {
|
|
1172
|
+
[experienceId]: experience
|
|
1173
|
+
}
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
};
|
|
1177
|
+
|
|
1178
|
+
// ../kos-codegen-core/src/lib/generators/component/plugin-handlers/setup-handler.ts
|
|
1179
|
+
var SetupPluginHandler = class extends BasePluginHandler {
|
|
1180
|
+
pluginType = PLUGIN_TYPES.SETUP;
|
|
1181
|
+
contributionKey = "setup";
|
|
1182
|
+
requiresI18n = true;
|
|
1183
|
+
createConfiguration(options) {
|
|
1184
|
+
const configPrefix = this.getConfigPrefix(options);
|
|
1185
|
+
const experienceId = `${configPrefix}.setup.experience`;
|
|
1186
|
+
const contribution = {
|
|
1187
|
+
id: `${configPrefix}.setup`,
|
|
1188
|
+
title: `${configPrefix}.setup.title`,
|
|
1189
|
+
namespace: options.appProject,
|
|
1190
|
+
setupDescriptor: options.nameCamelCase,
|
|
1191
|
+
experienceId
|
|
1192
|
+
};
|
|
1193
|
+
const experience = this.createExperience(options, experienceId);
|
|
1194
|
+
return {
|
|
1195
|
+
contributions: {
|
|
1196
|
+
setupStep: [contribution]
|
|
1197
|
+
},
|
|
1198
|
+
experiences: {
|
|
1199
|
+
[experienceId]: experience
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
1202
|
+
}
|
|
1203
|
+
};
|
|
1204
|
+
|
|
1205
|
+
// ../kos-codegen-core/src/lib/generators/component/plugin-handlers/trouble-action-handler.ts
|
|
1206
|
+
var TroubleActionPluginHandler = class extends BasePluginHandler {
|
|
1207
|
+
pluginType = PLUGIN_TYPES.TROUBLE_ACTION;
|
|
1208
|
+
contributionKey = "trouble-action";
|
|
1209
|
+
requiresI18n = true;
|
|
1210
|
+
createConfiguration(options) {
|
|
1211
|
+
const configPrefix = this.getConfigPrefix(options);
|
|
1212
|
+
const experienceId = `${configPrefix}.troubleAction.experience`;
|
|
1213
|
+
const contribution = {
|
|
1214
|
+
id: `${configPrefix}.troubleAction`,
|
|
1215
|
+
title: `${configPrefix}.troubleAction.title`,
|
|
1216
|
+
namespace: options.appProject,
|
|
1217
|
+
troubleType: options.nameCamelCase,
|
|
1218
|
+
experienceId
|
|
1219
|
+
};
|
|
1220
|
+
const experience = this.createExperience(options, experienceId);
|
|
1221
|
+
return {
|
|
1222
|
+
contributions: {
|
|
1223
|
+
troubleActions: [contribution]
|
|
1224
|
+
},
|
|
1225
|
+
experiences: {
|
|
1226
|
+
[experienceId]: experience
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
1231
|
+
|
|
1232
|
+
// ../kos-codegen-core/src/lib/generators/component/plugin-handlers/utility-handler.ts
|
|
1233
|
+
var UtilityPluginHandler = class extends BasePluginHandler {
|
|
1234
|
+
pluginType = PLUGIN_TYPES.UTILITY;
|
|
1235
|
+
contributionKey = "utility";
|
|
1236
|
+
requiresI18n = true;
|
|
1237
|
+
createConfiguration(options) {
|
|
1238
|
+
const configPrefix = this.getConfigPrefix(options);
|
|
1239
|
+
const experienceId = `${configPrefix}.util.experience`;
|
|
1240
|
+
const contribution = {
|
|
1241
|
+
id: `${configPrefix}.util`,
|
|
1242
|
+
title: `${configPrefix}.utility.title`,
|
|
1243
|
+
namespace: options.appProject,
|
|
1244
|
+
utilDescriptor: options.nameCamelCase,
|
|
1245
|
+
experienceId
|
|
1246
|
+
};
|
|
1247
|
+
const experience = this.createExperience(options, experienceId);
|
|
1248
|
+
return {
|
|
1249
|
+
contributions: {
|
|
1250
|
+
utilities: [contribution]
|
|
1251
|
+
},
|
|
1252
|
+
experiences: {
|
|
1253
|
+
[experienceId]: experience
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
};
|
|
1258
|
+
|
|
1259
|
+
// ../kos-codegen-core/src/lib/generators/component/plugin-handlers/factory.ts
|
|
1260
|
+
var PluginHandlerFactory = class {
|
|
1261
|
+
static handlers = /* @__PURE__ */ new Map([
|
|
1262
|
+
[PLUGIN_TYPES.CUI, CuiPluginHandler],
|
|
1263
|
+
[PLUGIN_TYPES.UTILITY, UtilityPluginHandler],
|
|
1264
|
+
[PLUGIN_TYPES.SETTING, SettingPluginHandler],
|
|
1265
|
+
[PLUGIN_TYPES.SETUP, SetupPluginHandler],
|
|
1266
|
+
[PLUGIN_TYPES.NAV, NavPluginHandler],
|
|
1267
|
+
[PLUGIN_TYPES.CONTROL_POUR, ControlPourPluginHandler],
|
|
1268
|
+
[PLUGIN_TYPES.TROUBLE_ACTION, TroubleActionPluginHandler],
|
|
1269
|
+
[PLUGIN_TYPES.CUSTOM, CustomPluginHandler]
|
|
1270
|
+
]);
|
|
1271
|
+
static createHandler(pluginType) {
|
|
1272
|
+
if (!pluginType) {
|
|
1273
|
+
return new DefaultComponentHandler();
|
|
1274
|
+
}
|
|
1275
|
+
const HandlerClass = this.handlers.get(pluginType);
|
|
1276
|
+
if (!HandlerClass) {
|
|
1277
|
+
console.warn(
|
|
1278
|
+
`No handler found for plugin type: ${pluginType}. Using default handler.`
|
|
1279
|
+
);
|
|
1280
|
+
return new DefaultComponentHandler();
|
|
1281
|
+
}
|
|
1282
|
+
return new HandlerClass();
|
|
1283
|
+
}
|
|
1284
|
+
static isValidPluginType(type) {
|
|
1285
|
+
return this.handlers.has(type);
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
// src/lib/utils/nx-context.mjs
|
|
1290
|
+
var __dirname2 = path6.dirname(fileURLToPath(import.meta.url));
|
|
1291
|
+
function findKosJsonFiles(dir = process.cwd(), files = []) {
|
|
1292
|
+
try {
|
|
1293
|
+
const entries = readdirSync2(dir);
|
|
1294
|
+
for (const entry of entries) {
|
|
1295
|
+
if (entry === "node_modules" || entry === ".git" || entry === ".nx" || entry === "dist" || entry === "coverage" || entry === ".vscode" || entry === ".idea" || entry.startsWith(".") && entry !== ".kos.json") {
|
|
1296
|
+
continue;
|
|
1297
|
+
}
|
|
1298
|
+
const fullPath = path6.join(dir, entry);
|
|
1299
|
+
try {
|
|
1300
|
+
const stat = statSync(fullPath);
|
|
1301
|
+
if (stat.isFile() && entry === ".kos.json") {
|
|
1302
|
+
files.push(fullPath);
|
|
1303
|
+
} else if (stat.isDirectory()) {
|
|
1304
|
+
if (entry !== "tmp" && entry !== "temp" && entry !== "build") {
|
|
1305
|
+
findKosJsonFiles(fullPath, files);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
} catch (error) {
|
|
1309
|
+
continue;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
} catch (error) {
|
|
1313
|
+
}
|
|
1314
|
+
return files;
|
|
1315
|
+
}
|
|
1316
|
+
async function getAllKosProjects() {
|
|
1317
|
+
const cached = getCached("allKosProjects");
|
|
1318
|
+
if (cached)
|
|
1319
|
+
return cached;
|
|
1320
|
+
if (process.env.KOS_CLI_QUIET !== "true") {
|
|
1321
|
+
console.warn(`[kos-cli] Discovering KOS projects by scanning .kos.json files...`);
|
|
1322
|
+
}
|
|
1323
|
+
const projectDirs = ["apps", "libs", "packages"];
|
|
1324
|
+
const kosJsonFiles = [];
|
|
1325
|
+
const workspaceRoot = process.cwd();
|
|
1326
|
+
for (const dir of projectDirs) {
|
|
1327
|
+
const dirPath = path6.join(workspaceRoot, dir);
|
|
1328
|
+
if (existsSync3(dirPath)) {
|
|
1329
|
+
findKosJsonFiles(dirPath, kosJsonFiles);
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
const rootKosJson = path6.join(workspaceRoot, ".kos.json");
|
|
1333
|
+
if (existsSync3(rootKosJson)) {
|
|
1334
|
+
kosJsonFiles.push(rootKosJson);
|
|
1335
|
+
}
|
|
1336
|
+
if (kosJsonFiles.length === 0) {
|
|
1337
|
+
if (process.env.KOS_CLI_QUIET !== "true") {
|
|
1338
|
+
console.warn(`[kos-cli] No .kos.json files found in common directories, performing full workspace scan...`);
|
|
1339
|
+
}
|
|
1340
|
+
findKosJsonFiles(workspaceRoot, kosJsonFiles);
|
|
1341
|
+
}
|
|
1342
|
+
const kosProjects = [];
|
|
1343
|
+
for (const kosJsonPath of kosJsonFiles) {
|
|
1344
|
+
try {
|
|
1345
|
+
const kosConfig = JSON.parse(readFileSync3(kosJsonPath, "utf-8"));
|
|
1346
|
+
const projectDir = path6.dirname(kosJsonPath);
|
|
1347
|
+
let projectName = null;
|
|
1348
|
+
const projectJsonPath = path6.join(projectDir, "project.json");
|
|
1349
|
+
if (existsSync3(projectJsonPath)) {
|
|
1350
|
+
try {
|
|
1351
|
+
const projectJson = JSON.parse(readFileSync3(projectJsonPath, "utf-8"));
|
|
1352
|
+
projectName = projectJson.name;
|
|
1353
|
+
} catch (error) {
|
|
1354
|
+
projectName = path6.basename(projectDir);
|
|
1355
|
+
}
|
|
1356
|
+
} else {
|
|
1357
|
+
projectName = path6.basename(projectDir);
|
|
1358
|
+
}
|
|
1359
|
+
if (kosConfig.type === "root") {
|
|
1360
|
+
continue;
|
|
1361
|
+
}
|
|
1362
|
+
kosProjects.push({
|
|
1363
|
+
name: projectName,
|
|
1364
|
+
path: projectDir,
|
|
1365
|
+
kosJsonPath,
|
|
1366
|
+
config: kosConfig,
|
|
1367
|
+
projectType: kosConfig.generator?.projectType
|
|
1368
|
+
});
|
|
1369
|
+
} catch (error) {
|
|
1370
|
+
console.warn(`[kos-cli] Error reading ${kosJsonPath}: ${error.message}`);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
setCached("allKosProjects", kosProjects);
|
|
1374
|
+
return kosProjects;
|
|
1375
|
+
}
|
|
1376
|
+
async function getAllModels() {
|
|
1377
|
+
const cached = getCached("allModels");
|
|
1378
|
+
if (cached)
|
|
1379
|
+
return cached;
|
|
1380
|
+
if (process.env.KOS_CLI_QUIET !== "true") {
|
|
1381
|
+
console.warn(`[kos-cli] Scanning for models in KOS projects...`);
|
|
1382
|
+
}
|
|
1383
|
+
const allKosProjects = await getAllKosProjects();
|
|
1384
|
+
const models = [];
|
|
1385
|
+
for (const kosProject of allKosProjects) {
|
|
1386
|
+
if (kosProject.config.models) {
|
|
1387
|
+
Object.keys(kosProject.config.models).forEach((model) => {
|
|
1388
|
+
models.push({ model, project: kosProject.name });
|
|
1389
|
+
});
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
models.sort((a, b) => {
|
|
1393
|
+
if (a.project === b.project) {
|
|
1394
|
+
return a.model.localeCompare(b.model);
|
|
1395
|
+
} else {
|
|
1396
|
+
return a.project.localeCompare(b.project);
|
|
1397
|
+
}
|
|
1398
|
+
});
|
|
1399
|
+
setCached("allModels", models);
|
|
1400
|
+
return models;
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
// src/lib/utils/prompts.mjs
|
|
1404
|
+
var DEFAULT_PROMPTS = [
|
|
1405
|
+
{
|
|
1406
|
+
type: "confirm",
|
|
1407
|
+
name: "interactive",
|
|
1408
|
+
message: "Use interactive mode?",
|
|
1409
|
+
default: false,
|
|
1410
|
+
when: false
|
|
1411
|
+
},
|
|
1412
|
+
{
|
|
1413
|
+
type: "confirm",
|
|
1414
|
+
name: "dryRun",
|
|
1415
|
+
message: "Dry run the command?",
|
|
1416
|
+
default: false,
|
|
1417
|
+
when: false
|
|
1418
|
+
}
|
|
1419
|
+
];
|
|
1420
|
+
|
|
1421
|
+
// src/lib/generators/model/add-future.mjs
|
|
1422
|
+
var metadata = {
|
|
8
1423
|
key: "add-future",
|
|
9
1424
|
name: "Add Future Support to Model",
|
|
10
1425
|
invalidateCache: true,
|
|
@@ -15,54 +1430,41 @@ export const metadata = {
|
|
|
15
1430
|
futureType: "futureType",
|
|
16
1431
|
updateServices: "updateServices",
|
|
17
1432
|
dryRun: "dryRun",
|
|
18
|
-
interactive: "interactive"
|
|
19
|
-
}
|
|
1433
|
+
interactive: "interactive"
|
|
1434
|
+
}
|
|
20
1435
|
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// Check if we're in interactive mode by looking at process args
|
|
24
|
-
const isInteractive = process.argv.includes('-i') || process.argv.includes('--interactive');
|
|
25
|
-
|
|
26
|
-
// For interactive mode, use lazy loading. For non-interactive, load immediately.
|
|
1436
|
+
async function add_future_default(plop) {
|
|
1437
|
+
const isInteractive = process.argv.includes("-i") || process.argv.includes("--interactive");
|
|
27
1438
|
let modelChoices;
|
|
28
1439
|
if (isInteractive) {
|
|
29
1440
|
modelChoices = async () => {
|
|
30
1441
|
const allModels = await getAllModels();
|
|
31
1442
|
return allModels.map((m) => ({
|
|
32
1443
|
name: `${m.model} (${m.project})`,
|
|
33
|
-
value: m.model
|
|
1444
|
+
value: m.model
|
|
34
1445
|
}));
|
|
35
1446
|
};
|
|
36
1447
|
} else {
|
|
37
1448
|
const allModels = await getAllModels();
|
|
38
1449
|
modelChoices = allModels.map((m) => ({
|
|
39
1450
|
name: `${m.model} (${m.project})`,
|
|
40
|
-
value: m.model
|
|
1451
|
+
value: m.model
|
|
41
1452
|
}));
|
|
42
1453
|
}
|
|
43
|
-
|
|
44
|
-
|
|
1454
|
+
plop.setActionType("addFutureToModel", async function(answers) {
|
|
1455
|
+
const cwd = process.cwd();
|
|
1456
|
+
const codegenFs = new DirectFileSystem(cwd);
|
|
45
1457
|
const allModels = await getAllModels();
|
|
46
|
-
const modelProject = allModels.find(
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
--updateServices=${!!answers.updateServices} \\
|
|
55
|
-
--no-interactive ${answers.dryRun ? "--dryRun" : ""} --verbose`;
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
await execute(command);
|
|
59
|
-
} catch (error) {
|
|
60
|
-
throw new Error(error);
|
|
61
|
-
}
|
|
62
|
-
|
|
1458
|
+
const modelProject = allModels.find((m) => m.model === answers.modelName)?.project;
|
|
1459
|
+
addFutureToModel(codegenFs, {
|
|
1460
|
+
modelName: answers.modelName,
|
|
1461
|
+
modelProject,
|
|
1462
|
+
futureType: answers.futureType,
|
|
1463
|
+
updateServices: !!answers.updateServices,
|
|
1464
|
+
dryRun: !!answers.dryRun
|
|
1465
|
+
});
|
|
63
1466
|
return `Future support (${answers.futureType}) added to model ${answers.modelName} in ${modelProject}`;
|
|
64
1467
|
});
|
|
65
|
-
|
|
66
1468
|
plop.setGenerator("add-future", {
|
|
67
1469
|
description: "Add Future support to an existing KOS Model",
|
|
68
1470
|
prompts: [
|
|
@@ -71,7 +1473,7 @@ export default async function (plop) {
|
|
|
71
1473
|
type: "list",
|
|
72
1474
|
name: "modelName",
|
|
73
1475
|
message: "Which model do you want to add Future support to?",
|
|
74
|
-
choices: modelChoices
|
|
1476
|
+
choices: modelChoices
|
|
75
1477
|
},
|
|
76
1478
|
{
|
|
77
1479
|
type: "list",
|
|
@@ -79,18 +1481,22 @@ export default async function (plop) {
|
|
|
79
1481
|
message: "What type of Future support?",
|
|
80
1482
|
choices: [
|
|
81
1483
|
{ name: "Minimal (external access only)", value: "minimal" },
|
|
82
|
-
{ name: "Complete (internal + external access)", value: "complete" }
|
|
1484
|
+
{ name: "Complete (internal + external access)", value: "complete" }
|
|
83
1485
|
],
|
|
84
|
-
default: "minimal"
|
|
1486
|
+
default: "minimal"
|
|
85
1487
|
},
|
|
86
1488
|
{
|
|
87
1489
|
type: "confirm",
|
|
88
1490
|
name: "updateServices",
|
|
89
1491
|
message: "Update/create services file with Future operations?",
|
|
90
|
-
default: true
|
|
91
|
-
}
|
|
1492
|
+
default: true
|
|
1493
|
+
}
|
|
92
1494
|
],
|
|
93
|
-
|
|
94
|
-
actions: actionFactory("addFutureToModel", metadata),
|
|
1495
|
+
actions: actionFactory("addFutureToModel", metadata)
|
|
95
1496
|
});
|
|
96
1497
|
}
|
|
1498
|
+
export {
|
|
1499
|
+
add_future_default as default,
|
|
1500
|
+
metadata
|
|
1501
|
+
};
|
|
1502
|
+
//# sourceMappingURL=add-future.mjs.map
|