@kosdev-code/kos-codegen-core 0.1.0-next.888 → 0.1.0-next.898

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.mjs CHANGED
@@ -991,7 +991,8 @@ function normalizeOptions(codegenFs, options, projects) {
991
991
  const booleanDefaults = {
992
992
  companion: false,
993
993
  skipRegistration: false,
994
- futureAware: "none"
994
+ futureAware: "none",
995
+ singleton: false
995
996
  };
996
997
  return {
997
998
  ...booleanDefaults,
@@ -1083,6 +1084,15 @@ function readSourceFile(codegenFs, filePath, inspect) {
1083
1084
  project.createSourceFile(filePath, content, { overwrite: true })
1084
1085
  );
1085
1086
  }
1087
+ function decoratorConfigText(sourceFile, cls, decoratorName) {
1088
+ const arg = cls.getDecorator(decoratorName)?.getArguments()[0];
1089
+ if (!arg) return void 0;
1090
+ if (arg.getKindName() === "ObjectLiteralExpression") return arg.getText();
1091
+ const text = arg.getText().trim();
1092
+ if (!/^[A-Za-z_$][\w$]*$/.test(text)) return text;
1093
+ const initializer = sourceFile.getVariableDeclaration(text)?.getInitializer()?.getText();
1094
+ return initializer ? initializer.replace(/\s+as\s+const\s*$/, "") : text;
1095
+ }
1086
1096
  function ensureNamedImport(sourceFile, moduleSpecifier, names) {
1087
1097
  const decls = sourceFile.getImportDeclarations().filter((d) => d.getModuleSpecifierValue() === moduleSpecifier);
1088
1098
  const existing = /* @__PURE__ */ new Set();
@@ -1881,9 +1891,9 @@ function readDeclaredModelType(codegenFs, filePath) {
1881
1891
  try {
1882
1892
  return readSourceFile(codegenFs, filePath, (sf) => {
1883
1893
  const cls = sf.getClasses().find((c) => c.getDecorator("kosModel"));
1884
- const arg = cls?.getDecorator("kosModel")?.getArguments()[0];
1885
- if (!arg) return void 0;
1886
- const inner = arg.getKindName() === "ObjectLiteralExpression" ? modelTypeIdProperty(arg.getText()) : arg.getText();
1894
+ if (!cls) return void 0;
1895
+ const config = decoratorConfigText(sf, cls, "kosModel");
1896
+ const inner = config?.startsWith("{") ? modelTypeIdProperty(config) : config;
1887
1897
  if (!inner) return void 0;
1888
1898
  return resolveToStringLiteral(inner.trim(), sf.getFullText());
1889
1899
  });
@@ -2801,7 +2811,7 @@ function describeModel(codegenFs, options, projects) {
2801
2811
  };
2802
2812
  }
2803
2813
  const classDecorators = cls.getDecorators().map((d) => d.getName());
2804
- const kosModelArg = cls.getDecorator("kosModel") ? firstDecoratorArg(cls.getDecorator("kosModel").getText()) : void 0;
2814
+ const kosModelArg = decoratorConfigText(sf, cls, "kosModel");
2805
2815
  const singleton = /\bsingleton\s*:\s*true\b/.test(kosModelArg ?? "");
2806
2816
  return {
2807
2817
  modelFilePath,
@@ -3232,6 +3242,13 @@ function assertServiceModuleAcceptsTransform(codegenFs, serviceModuleFile, model
3232
3242
  `${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.`
3233
3243
  );
3234
3244
  }
3245
+ const MUTATING_METHODS = /* @__PURE__ */ new Set(["post", "put", "delete", "patch"]);
3246
+ function assertLifecycleSuitsMethod(method, mode, methodName) {
3247
+ if (mode !== "lifecycle" || !MUTATING_METHODS.has(method)) return;
3248
+ throw new Error(
3249
+ `${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.`
3250
+ );
3251
+ }
3235
3252
  function assertServiceModuleAcceptsProvisional(codegenFs, serviceModuleFile, modelProject) {
3236
3253
  const file = `${serviceModuleFile}.ts`;
3237
3254
  if (!codegenFs.exists(file)) return;
@@ -3413,6 +3430,7 @@ function addServiceRequestToModel(codegenFs, options, projects) {
3413
3430
  );
3414
3431
  const method = (options.method || "get").toLowerCase();
3415
3432
  const mode = options.mode ?? (options.lifecycle ? "lifecycle" : "method");
3433
+ assertLifecycleSuitsMethod(method, mode, options.methodName);
3416
3434
  const lifecycle = options.lifecycle || "LOAD";
3417
3435
  const catalogName = `${pascalCase(modelBase)}Endpoints`;
3418
3436
  const mockMode = options.mock ?? "auto";