@kosdev-code/kos-codegen-core 3.0.21 → 3.0.23

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/index.js CHANGED
@@ -1013,7 +1013,8 @@ function normalizeOptions(codegenFs, options, projects) {
1013
1013
  const booleanDefaults = {
1014
1014
  companion: false,
1015
1015
  skipRegistration: false,
1016
- futureAware: "none"
1016
+ futureAware: "none",
1017
+ singleton: false
1017
1018
  };
1018
1019
  return {
1019
1020
  ...booleanDefaults,
@@ -1105,6 +1106,15 @@ function readSourceFile(codegenFs, filePath, inspect) {
1105
1106
  project.createSourceFile(filePath, content, { overwrite: true })
1106
1107
  );
1107
1108
  }
1109
+ function decoratorConfigText(sourceFile, cls, decoratorName) {
1110
+ const arg = cls.getDecorator(decoratorName)?.getArguments()[0];
1111
+ if (!arg) return void 0;
1112
+ if (arg.getKindName() === "ObjectLiteralExpression") return arg.getText();
1113
+ const text = arg.getText().trim();
1114
+ if (!/^[A-Za-z_$][\w$]*$/.test(text)) return text;
1115
+ const initializer = sourceFile.getVariableDeclaration(text)?.getInitializer()?.getText();
1116
+ return initializer ? initializer.replace(/\s+as\s+const\s*$/, "") : text;
1117
+ }
1108
1118
  function ensureNamedImport(sourceFile, moduleSpecifier, names) {
1109
1119
  const decls = sourceFile.getImportDeclarations().filter((d) => d.getModuleSpecifierValue() === moduleSpecifier);
1110
1120
  const existing = /* @__PURE__ */ new Set();
@@ -1903,9 +1913,9 @@ function readDeclaredModelType(codegenFs, filePath) {
1903
1913
  try {
1904
1914
  return readSourceFile(codegenFs, filePath, (sf) => {
1905
1915
  const cls = sf.getClasses().find((c) => c.getDecorator("kosModel"));
1906
- const arg = cls?.getDecorator("kosModel")?.getArguments()[0];
1907
- if (!arg) return void 0;
1908
- const inner = arg.getKindName() === "ObjectLiteralExpression" ? modelTypeIdProperty(arg.getText()) : arg.getText();
1916
+ if (!cls) return void 0;
1917
+ const config = decoratorConfigText(sf, cls, "kosModel");
1918
+ const inner = config?.startsWith("{") ? modelTypeIdProperty(config) : config;
1909
1919
  if (!inner) return void 0;
1910
1920
  return resolveToStringLiteral(inner.trim(), sf.getFullText());
1911
1921
  });
@@ -2823,7 +2833,7 @@ function describeModel(codegenFs, options, projects) {
2823
2833
  };
2824
2834
  }
2825
2835
  const classDecorators = cls.getDecorators().map((d) => d.getName());
2826
- const kosModelArg = cls.getDecorator("kosModel") ? firstDecoratorArg(cls.getDecorator("kosModel").getText()) : void 0;
2836
+ const kosModelArg = decoratorConfigText(sf, cls, "kosModel");
2827
2837
  const singleton = /\bsingleton\s*:\s*true\b/.test(kosModelArg ?? "");
2828
2838
  return {
2829
2839
  modelFilePath,
@@ -3254,6 +3264,13 @@ function assertServiceModuleAcceptsTransform(codegenFs, serviceModuleFile, model
3254
3264
  `${file} predates the transform-aware service layer (EndpointCtx takes ${typeParams} type parameter). Run \`kosui api:generate --project ${modelProject} --helpers-only\` to refresh the helper layer in place (no spec fetch, openapi.d.ts untouched), then add the service request.`
3255
3265
  );
3256
3266
  }
3267
+ const MUTATING_METHODS = /* @__PURE__ */ new Set(["post", "put", "delete", "patch"]);
3268
+ function assertLifecycleSuitsMethod(method, mode, methodName) {
3269
+ if (mode !== "lifecycle" || !MUTATING_METHODS.has(method)) return;
3270
+ throw new Error(
3271
+ `${methodName} is a ${method.toUpperCase()} with a lifecycle, so it would run every time the model reaches that phase rather than when something calls it — a mutation on every load. Drop \`lifecycle\` to get the method-driven form (the default for ${method.toUpperCase()}), or pass \`mode: "method"\`. If the phase really is the trigger, the model calls the method from its own lifecycle hook, where the call is visible.`
3272
+ );
3273
+ }
3257
3274
  function assertServiceModuleAcceptsProvisional(codegenFs, serviceModuleFile, modelProject) {
3258
3275
  const file = `${serviceModuleFile}.ts`;
3259
3276
  if (!codegenFs.exists(file)) return;
@@ -3435,6 +3452,7 @@ function addServiceRequestToModel(codegenFs, options, projects) {
3435
3452
  );
3436
3453
  const method = (options.method || "get").toLowerCase();
3437
3454
  const mode = options.mode ?? (options.lifecycle ? "lifecycle" : "method");
3455
+ assertLifecycleSuitsMethod(method, mode, options.methodName);
3438
3456
  const lifecycle = options.lifecycle || "LOAD";
3439
3457
  const catalogName = `${pascalCase(modelBase)}Endpoints`;
3440
3458
  const mockMode = options.mock ?? "auto";