@akanjs/cli 2.3.9-rc.0 → 2.3.9-rc.2

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 (32) hide show
  1. package/incrementalBuilder.proc.js +907 -18
  2. package/index.js +3928 -1486
  3. package/package.json +2 -2
  4. package/templates/appSample/common/formatters.ts +1 -1
  5. package/templates/appSample/common/validators.ts +1 -1
  6. package/templates/appSample/lib/_noti/noti.dictionary.ts +1 -1
  7. package/templates/appSample/lib/_noti/noti.service.ts +1 -1
  8. package/templates/appSample/lib/_noti/noti.signal.ts +1 -1
  9. package/templates/appSample/lib/_noti/noti.store.ts +1 -1
  10. package/templates/appSample/lib/task/task.abstract.ts +1 -1
  11. package/templates/appSample/lib/task/task.constant.ts +1 -1
  12. package/templates/appSample/lib/task/task.dictionary.ts +1 -1
  13. package/templates/appSample/lib/task/task.document.ts +3 -3
  14. package/templates/appSample/lib/task/task.service.ts +2 -2
  15. package/templates/appSample/lib/task/task.signal.spec.ts +70 -0
  16. package/templates/appSample/lib/task/task.signal.test.ts +20 -0
  17. package/templates/appSample/lib/task/task.signal.ts +2 -2
  18. package/templates/appSample/lib/task/task.store.ts +2 -2
  19. package/templates/appSample/srvkit/AuthGuard.ts +1 -1
  20. package/templates/appSample/srvkit/SessionInternalArg.ts +1 -1
  21. package/templates/appSample/ui/GlobalLoading.tsx +1 -1
  22. package/templates/appSample/ui/QuantityControl.tsx +1 -1
  23. package/templates/appSample/webkit/useDebounce.ts +1 -1
  24. package/templates/lib/useClient.ts +1 -4
  25. package/templates/module/__Model__.Unit.tsx +1 -4
  26. package/templates/workspaceRoot/.cursor/rules/{akan-scan-conventions.mdc.template → akan-sync-conventions.mdc.template} +3 -3
  27. package/templates/workspaceRoot/.cursor/rules/akan.mdc.template +5 -0
  28. package/templates/workspaceRoot/AGENTS.md.template +77 -58
  29. package/templates/workspaceRoot/docs/AI-DEVELOPMENT.md.template +51 -0
  30. package/templates/workspaceRoot/docs/GENERATED.md.template +54 -0
  31. package/templates/workspaceRoot/package.json.template +26 -0
  32. package/templates/workspaceRoot/Untitled +0 -1
@@ -2809,7 +2809,7 @@ class Executor {
2809
2809
  this.logger.verbose(`Remove directory ${readPath}`);
2810
2810
  return this;
2811
2811
  }
2812
- async writeFile(filePath, content, { overwrite = true } = {}) {
2812
+ async writeFile(filePath, content, { overwrite = true, silent = false } = {}) {
2813
2813
  const writePath = this.getPath(filePath);
2814
2814
  const dir = path7.dirname(writePath);
2815
2815
  if (!await FileSys.dirExists(dir))
@@ -2827,7 +2827,8 @@ class Executor {
2827
2827
  }
2828
2828
  } else {
2829
2829
  await FileSys.writeText(writePath, contentStr);
2830
- this.logger.rawLog(chalk4.green(`File Create: ${filePath}`));
2830
+ if (!silent)
2831
+ this.logger.rawLog(chalk4.green(`File Create: ${filePath}`));
2831
2832
  }
2832
2833
  return { filePath: writePath, content: contentStr };
2833
2834
  }
@@ -3491,14 +3492,15 @@ class AppExecutor extends SysExecutor {
3491
3492
  getCommandEnv(env = {}) {
3492
3493
  const basePort = 8282;
3493
3494
  const portOffset = WorkspaceExecutor.getBaseDevEnv().portOffset;
3494
- const PORT = basePort ? (basePort + portOffset).toString() : undefined;
3495
+ const PORT = (basePort + portOffset).toString();
3495
3496
  const AKAN_PUBLIC_SERVER_PORT = portOffset ? (8282 + portOffset).toString() : undefined;
3496
3497
  return {
3497
3498
  ...process.env,
3498
3499
  AKAN_PUBLIC_APP_NAME: this.name,
3499
3500
  AKAN_WORKSPACE_ROOT: this.workspace.workspaceRoot,
3500
3501
  NODE_NO_WARNINGS: "1",
3501
- ...PORT ? { PORT, AKAN_PUBLIC_CLIENT_PORT: PORT } : {},
3502
+ PORT,
3503
+ AKAN_PUBLIC_CLIENT_PORT: PORT,
3502
3504
  ...AKAN_PUBLIC_SERVER_PORT ? { AKAN_PUBLIC_SERVER_PORT } : {},
3503
3505
  ...env
3504
3506
  };
@@ -4777,17 +4779,91 @@ class AkanAppHost {
4777
4779
  import { readdir } from "fs/promises";
4778
4780
  import path10 from "path";
4779
4781
  import { capitalize as capitalize2 } from "akanjs/common";
4782
+ var resourceList = [
4783
+ { uri: "akan://docs/framework", name: "Akan framework guide", mimeType: "text/markdown" },
4784
+ { uri: "akan://guidelines/framework", name: "Framework guideline", mimeType: "text/markdown" },
4785
+ { uri: "akan://guidelines/modelSignal", name: "Model signal guideline", mimeType: "text/markdown" },
4786
+ { uri: "akan://workspace/summary", name: "Workspace summary", mimeType: "application/json" },
4787
+ { uri: "akan://workspace/apps", name: "Workspace apps", mimeType: "application/json" },
4788
+ { uri: "akan://workspace/modules", name: "Workspace modules", mimeType: "application/json" }
4789
+ ];
4790
+ var cursorMcpConfigPath = ".cursor/mcp.json";
4791
+ var cursorWorkspaceFolder = "$" + "{workspaceFolder}";
4792
+ var createAkanCursorMcpServer = (mode = "readonly") => ({
4793
+ type: "stdio",
4794
+ command: "bash",
4795
+ args: ["-lc", `cd "${cursorWorkspaceFolder}" && akan mcp --mode ${mode}`]
4796
+ });
4797
+ var akanCursorMcpServer = createAkanCursorMcpServer();
4798
+ var renderDoctorText = (result) => {
4799
+ const lines = [`Akan doctor status: ${result.status}`];
4800
+ if (result.diagnostics.length === 0) {
4801
+ lines.push("", "No Akan workspace diagnostics found.");
4802
+ } else {
4803
+ lines.push("", ...result.diagnostics.map((diagnostic) => [
4804
+ `[${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`,
4805
+ diagnostic.path ? ` ${diagnostic.path}` : ""
4806
+ ].filter(Boolean).join(`
4807
+ `)));
4808
+ }
4809
+ lines.push("", "Generated file freshness:", result.generatedFilesFreshness.message, `Refresh: ${result.generatedFilesFreshness.refreshCommand}`, "", "Repair actions:", ...result.repairActions.length ? result.repairActions.map((action) => `- ${action.command}`) : ["- none"], "", "Validation commands:", ...result.validationCommands.map((command) => `- ${command}`));
4810
+ return `${lines.join(`
4811
+ `)}
4812
+ `;
4813
+ };
4780
4814
  var generatedFiles = [
4781
- "cnst.ts",
4782
- "db.ts",
4783
- "dict.ts",
4784
- "option.ts",
4785
- "sig.ts",
4786
- "srv.ts",
4787
- "st.ts",
4788
- "useClient.ts",
4789
- "useServer.ts"
4815
+ "apps/*/client.ts",
4816
+ "apps/*/server.ts",
4817
+ "*/lib/cnst.ts",
4818
+ "*/lib/db.ts",
4819
+ "*/lib/dict.ts",
4820
+ "*/lib/option.ts",
4821
+ "*/lib/sig.ts",
4822
+ "*/lib/srv.ts",
4823
+ "*/lib/st.ts",
4824
+ "*/lib/useClient.ts",
4825
+ "*/lib/useServer.ts",
4826
+ "*/lib/**/index.ts",
4827
+ "*/ui/index.ts",
4828
+ "*/webkit/index.ts",
4829
+ "*/srvkit/index.ts",
4830
+ "*/common/index.ts"
4831
+ ];
4832
+ var validationCommands = [
4833
+ "akan sync <app-or-lib>",
4834
+ "akan lint <app-or-lib-or-pkg>",
4835
+ "akan typecheck <app-name>",
4836
+ "akan test <app-or-lib-or-pkg>",
4837
+ "akan build <app-name>",
4838
+ "akan doctor --strict --format json"
4790
4839
  ];
4840
+ var generatedFilesFreshness = {
4841
+ status: "unknown",
4842
+ message: "Run sync before validation so generated Akan files match the current source conventions.",
4843
+ refreshCommand: "akan sync <app-or-lib>",
4844
+ verifyingCommands: ["akan lint <app-or-lib-or-pkg>", "akan build <app-name>"]
4845
+ };
4846
+ var repairAction = (kind, command, reason, safeToRun) => ({
4847
+ kind,
4848
+ command,
4849
+ reason,
4850
+ safeToRun
4851
+ });
4852
+ var moduleShapeFiles = (module) => {
4853
+ if (module.kind === "service") {
4854
+ return [`${module.name}.dictionary.ts`, `${module.name}.service.ts`, `${module.name}.signal.ts`];
4855
+ }
4856
+ if (module.kind === "scalar")
4857
+ return [`${module.name}.constant.ts`, `${module.name}.dictionary.ts`];
4858
+ return [
4859
+ `${module.name}.constant.ts`,
4860
+ `${module.name}.dictionary.ts`,
4861
+ `${module.name}.service.ts`,
4862
+ `${module.name}.store.ts`,
4863
+ `${module.name}.signal.ts`
4864
+ ];
4865
+ };
4866
+ var constantFieldNames = (content) => [...content.matchAll(/\b([A-Za-z_$][\w$]*)\s*:\s*field\(/g)].map((match) => match[1]).filter(Boolean);
4791
4867
  var appRootAllowFiles = new Set([
4792
4868
  "akan.app.json",
4793
4869
  "akan.config.ts",
@@ -4945,39 +5021,95 @@ class AkanContextAnalyzer {
4945
5021
  libs,
4946
5022
  pkgs,
4947
5023
  generatedFiles,
4948
- validationCommands: ["akan lint <app-or-lib-or-pkg>", "akan build <app-name>", "akan start <app-name>"]
5024
+ validationCommands
4949
5025
  };
4950
5026
  }
4951
5027
  static async doctor(workspace, { strict = false } = {}) {
4952
5028
  const context = await AkanContextAnalyzer.analyze(workspace);
4953
5029
  const diagnostics = [];
5030
+ const repairActions = [
5031
+ repairAction("generated", "akan repair generated --app <app-or-lib>", "Refresh generated Akan files.", true),
5032
+ repairAction("format", "akan repair format --target <app-or-lib-or-pkg>", "Run the formatter/linter repair path.", true)
5033
+ ];
4954
5034
  for (const app of context.apps) {
4955
5035
  const appPath = path10.join(workspace.workspaceRoot, app.path);
4956
5036
  for (const entry of await safeReadDir(appPath)) {
4957
5037
  const allowed = entry.isDirectory() ? appRootAllowDirs.has(entry.name) : appRootAllowFiles.has(entry.name);
4958
5038
  if (!allowed) {
5039
+ const action = repairAction("module-shape", `akan repair module-shape --app ${app.name}`, "Review app root shape and remove or move the unknown entry.", false);
4959
5040
  diagnostics.push({
4960
- severity: "warning",
5041
+ severity: "error",
4961
5042
  code: "app-root-unknown-entry",
4962
5043
  path: `${app.path}/${entry.name}`,
4963
- message: `Unexpected ${entry.isDirectory() ? "folder" : "file"} in app root: ${app.path}/${entry.name}`
5044
+ message: `Unexpected ${entry.isDirectory() ? "folder" : "file"} in app root: ${app.path}/${entry.name}`,
5045
+ repairActions: [action]
4964
5046
  });
5047
+ repairActions.push(action);
4965
5048
  }
4966
5049
  }
4967
5050
  }
4968
5051
  for (const sys2 of [...context.apps, ...context.libs]) {
4969
5052
  for (const module of sys2.modules) {
4970
5053
  if (!module.abstract.exists) {
5054
+ const action = repairAction("module-shape", `akan repair module-shape --app ${sys2.name} --module ${module.name}`, "Create the missing module abstract or inspect required source files.", false);
4971
5055
  diagnostics.push({
4972
5056
  severity: strict ? "error" : "warning",
4973
5057
  code: "module-abstract-missing",
4974
5058
  path: module.abstract.path,
4975
- message: `${capitalize2(module.kind)} module ${sys2.name}:${module.name} should include ${module.abstract.path}`
5059
+ message: `${capitalize2(module.kind)} module ${sys2.name}:${module.name} should include ${module.abstract.path}`,
5060
+ repairActions: [action]
5061
+ });
5062
+ repairActions.push(action);
5063
+ }
5064
+ const missingFiles = moduleShapeFiles(module).filter((filename) => !module.files.includes(filename));
5065
+ if (missingFiles.length) {
5066
+ const action = repairAction("module-shape", `akan repair module-shape --app ${sys2.name} --module ${module.name}`, "Review missing required module source files.", false);
5067
+ diagnostics.push({
5068
+ severity: "error",
5069
+ code: "module-shape-invalid",
5070
+ path: module.path,
5071
+ message: `${capitalize2(module.kind)} module ${sys2.name}:${module.name} is missing required files: ${missingFiles.join(", ")}`,
5072
+ repairActions: [action]
4976
5073
  });
5074
+ repairActions.push(action);
5075
+ }
5076
+ if (module.kind !== "service" && module.files.includes(`${module.name}.dictionary.ts`)) {
5077
+ const constantPath = path10.join(workspace.workspaceRoot, module.path, `${module.name}.constant.ts`);
5078
+ const dictionaryPath = path10.join(workspace.workspaceRoot, module.path, `${module.name}.dictionary.ts`);
5079
+ const [constantContent, dictionaryContent] = await Promise.all([
5080
+ safeReadText(constantPath),
5081
+ safeReadText(dictionaryPath)
5082
+ ]);
5083
+ if (constantContent && dictionaryContent) {
5084
+ for (const fieldName of constantFieldNames(constantContent)) {
5085
+ if (new RegExp(`\\b${fieldName}\\s*:`).test(dictionaryContent))
5086
+ continue;
5087
+ const action = repairAction("dictionary", `akan repair dictionary --app ${sys2.name} --module ${module.name}`, "Add missing dictionary labels for source constant fields.", false);
5088
+ diagnostics.push({
5089
+ severity: "warning",
5090
+ code: "dictionary-label-missing",
5091
+ path: `${module.path}/${module.name}.dictionary.ts`,
5092
+ message: `Dictionary labels for ${sys2.name}:${module.name}.${fieldName} were not found.`,
5093
+ repairActions: [action]
5094
+ });
5095
+ repairActions.push(action);
5096
+ }
5097
+ }
4977
5098
  }
4978
5099
  }
4979
5100
  }
4980
- return { schemaVersion: 1, strict, diagnostics };
5101
+ return {
5102
+ schemaVersion: 1,
5103
+ repoName: context.repoName,
5104
+ root: context.root,
5105
+ strict,
5106
+ status: diagnostics.some((diagnostic) => diagnostic.severity === "error") ? "failed" : "passed",
5107
+ diagnostics,
5108
+ generatedFiles: context.generatedFiles,
5109
+ generatedFilesFreshness,
5110
+ validationCommands: context.validationCommands,
5111
+ repairActions
5112
+ };
4981
5113
  }
4982
5114
  static findModules(context, moduleName) {
4983
5115
  const modules = [...context.apps, ...context.libs].flatMap((sys2) => sys2.modules);
@@ -11115,6 +11247,8 @@ var getInternalArgumentValue = async (argMeta, value, workspace) => {
11115
11247
  } else if (sysType === "app") {
11116
11248
  if (value && appNames.includes(value))
11117
11249
  return AppExecutor.from(workspace, value);
11250
+ if (!value && appNames.length === 1 && appNames[0])
11251
+ return AppExecutor.from(workspace, appNames[0]);
11118
11252
  const appName = await select3({ message: `Select the ${sysType} name`, choices: appNames });
11119
11253
  return AppExecutor.from(workspace, appName);
11120
11254
  } else if (sysType === "lib") {
@@ -11690,6 +11824,761 @@ import { Box as Box2, Newline, Text as Text2, useInput as useInput2 } from "ink"
11690
11824
  import { useEffect as useEffect3, useState as useState3 } from "react";
11691
11825
  import { jsxDEV as jsxDEV2, Fragment as Fragment2 } from "react/jsx-dev-runtime";
11692
11826
  "use client";
11827
+ // pkgs/@akanjs/devkit/workflow/index.ts
11828
+ var jsonText = (value, { trailingNewline = true } = {}) => `${JSON.stringify(value, null, 2)}${trailingNewline ? `
11829
+ ` : ""}`;
11830
+ var surfaceModes = new Set(["infer", "include", "skip"]);
11831
+ var parseStringList = (value) => {
11832
+ if (Array.isArray(value)) {
11833
+ const values = value.filter((item) => typeof item === "string" && item.trim().length > 0);
11834
+ return values.length === value.length ? values : null;
11835
+ }
11836
+ if (typeof value !== "string")
11837
+ return null;
11838
+ return value.split(",").map((item) => item.trim()).filter(Boolean);
11839
+ };
11840
+ var normalizeInputValue = (name, spec, value) => {
11841
+ if (spec.type === "string")
11842
+ return typeof value === "string" && value.length > 0 ? value : null;
11843
+ if (spec.type === "string-list") {
11844
+ const values = parseStringList(value);
11845
+ return values && values.length > 0 ? values : null;
11846
+ }
11847
+ if (typeof value === "string" && surfaceModes.has(value))
11848
+ return value;
11849
+ throw new Error(`Unsupported workflow input value for ${name}`);
11850
+ };
11851
+ var listWorkflowSpecs = (specs) => [...specs].sort((a, b) => a.name.localeCompare(b.name));
11852
+ var getWorkflowSpec = (specs, name) => specs.find((spec) => spec.name === name) ?? null;
11853
+ var compactWorkflowInputs = (inputs) => Object.fromEntries(Object.entries(inputs).filter(([, value]) => value !== null && value !== ""));
11854
+ var createWorkflowPlan = (spec, rawInputs) => {
11855
+ const inputs = {};
11856
+ const diagnostics = [];
11857
+ for (const [name, inputSpec] of Object.entries(spec.inputs)) {
11858
+ const rawValue = rawInputs[name];
11859
+ if (rawValue === undefined || rawValue === null || rawValue === "") {
11860
+ if (inputSpec.required) {
11861
+ diagnostics.push({
11862
+ severity: "error",
11863
+ code: "workflow-input-missing",
11864
+ input: name,
11865
+ message: `Workflow ${spec.name} requires input "${name}".`
11866
+ });
11867
+ }
11868
+ continue;
11869
+ }
11870
+ const value = normalizeInputValue(name, inputSpec, rawValue);
11871
+ if (value === null) {
11872
+ diagnostics.push({
11873
+ severity: "error",
11874
+ code: "workflow-input-invalid",
11875
+ input: name,
11876
+ message: `Workflow ${spec.name} input "${name}" must be ${inputSpec.type}.`
11877
+ });
11878
+ continue;
11879
+ }
11880
+ if (inputSpec.allowedValues && typeof value === "string" && !inputSpec.allowedValues.includes(value)) {
11881
+ diagnostics.push({
11882
+ severity: "error",
11883
+ code: "workflow-input-not-allowed",
11884
+ input: name,
11885
+ message: `Workflow ${spec.name} input "${name}" must be one of: ${inputSpec.allowedValues.join(", ")}.`
11886
+ });
11887
+ continue;
11888
+ }
11889
+ inputs[name] = value;
11890
+ }
11891
+ const fieldType = inputs.type;
11892
+ if (spec.name === "add-field" && typeof fieldType === "string" && (fieldType.toLowerCase() === "number" || fieldType.toLowerCase() === "numeric")) {
11893
+ diagnostics.push({
11894
+ severity: "error",
11895
+ code: "primitive-field-type-unsupported",
11896
+ input: "type",
11897
+ message: `Field type "${fieldType}" is ambiguous in Akan. Use Int for integer fields or Float for decimal fields.`
11898
+ });
11899
+ }
11900
+ return {
11901
+ schemaVersion: 1,
11902
+ workflow: spec.name,
11903
+ mode: "plan",
11904
+ inputs,
11905
+ optionalSurfaces: spec.optionalSurfaces ?? {},
11906
+ steps: spec.steps,
11907
+ predictedChanges: spec.predictedChanges,
11908
+ validation: spec.validation,
11909
+ diagnostics,
11910
+ requiresApproval: true
11911
+ };
11912
+ };
11913
+ var renderWorkflowList = (specs) => [
11914
+ "# Akan Workflows",
11915
+ "",
11916
+ ...specs.flatMap((spec) => [`- \`${spec.name}\`: ${spec.description}`, ` - When: ${spec.whenToUse}`]),
11917
+ ""
11918
+ ].join(`
11919
+ `);
11920
+ var renderWorkflowExplain = (spec) => [
11921
+ `# Workflow: ${spec.name}`,
11922
+ "",
11923
+ spec.description,
11924
+ "",
11925
+ "## When To Use",
11926
+ spec.whenToUse,
11927
+ "",
11928
+ "## Inputs",
11929
+ ...Object.entries(spec.inputs).map(([name, input4]) => `- \`${name}\`${input4.required ? " (required)" : ""}: ${input4.description}${input4.allowedValues ? ` Allowed: ${input4.allowedValues.join(", ")}.` : ""}`),
11930
+ "",
11931
+ "## Optional Surfaces",
11932
+ ...Object.entries(spec.optionalSurfaces ?? {}).map(([name, mode]) => `- \`${name}\`: ${mode}`),
11933
+ "",
11934
+ "## Steps",
11935
+ ...spec.steps.map((step, index) => `${index + 1}. \`${step.id}\` (${step.tool}): ${step.description}`),
11936
+ "",
11937
+ "## Validation",
11938
+ ...spec.validation.map((validation) => `- \`${validation.command}\`: ${validation.reason}`),
11939
+ ""
11940
+ ].join(`
11941
+ `);
11942
+ var renderWorkflowPlan = (plan) => [
11943
+ `# Workflow Plan: ${plan.workflow}`,
11944
+ "",
11945
+ `- Mode: ${plan.mode}`,
11946
+ `- Requires approval: ${plan.requiresApproval}`,
11947
+ "",
11948
+ "## Inputs",
11949
+ ...Object.entries(plan.inputs).map(([name, value]) => `- \`${name}\`: ${Array.isArray(value) ? value.join(", ") : value}`),
11950
+ "",
11951
+ "## Optional Surfaces",
11952
+ ...Object.entries(plan.optionalSurfaces).map(([name, mode]) => `- \`${name}\`: ${mode}`),
11953
+ "",
11954
+ "## Steps",
11955
+ ...plan.steps.map((step, index) => `${index + 1}. \`${step.id}\` (${step.tool}): ${step.description}`),
11956
+ "",
11957
+ "## Predicted Changes",
11958
+ ...plan.predictedChanges.map((change) => `- \`${change.action}\` ${change.target}: ${change.reason}`),
11959
+ "",
11960
+ "## Validation",
11961
+ ...plan.validation.map((validation) => `- \`${validation.command}\`: ${validation.reason}`),
11962
+ "",
11963
+ "## Diagnostics",
11964
+ ...plan.diagnostics.length ? plan.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
11965
+ ""
11966
+ ].join(`
11967
+ `);
11968
+ var workflowStatus = (diagnostics) => diagnostics.some((diagnostic) => diagnostic.severity === "error") ? "failed" : "passed";
11969
+ var commandStatus = (commands) => commands.some((command3) => command3.status === "failed") ? "failed" : "passed";
11970
+ var uniqueBy = (values, key) => {
11971
+ const seen = new Set;
11972
+ return values.filter((value) => {
11973
+ const itemKey = key(value);
11974
+ if (seen.has(itemKey))
11975
+ return false;
11976
+ seen.add(itemKey);
11977
+ return true;
11978
+ });
11979
+ };
11980
+ var createWorkflowApplyReport = ({
11981
+ workflow,
11982
+ mode,
11983
+ changedFiles = [],
11984
+ generatedFiles: generatedFiles2 = [],
11985
+ appliedCommands = [],
11986
+ recommendedValidationCommands,
11987
+ commands = [],
11988
+ diagnostics = [],
11989
+ nextActions = [],
11990
+ plan
11991
+ }) => {
11992
+ const validationCommands2 = recommendedValidationCommands ?? commands;
11993
+ return {
11994
+ schemaVersion: 1,
11995
+ workflow,
11996
+ mode,
11997
+ status: workflowStatus(diagnostics),
11998
+ changedFiles: uniqueBy(changedFiles, (file) => `${file.action}:${file.path}:${file.reason}`),
11999
+ generatedFiles: uniqueBy(generatedFiles2, (file) => `${file.action}:${file.path}:${file.reason}`),
12000
+ appliedCommands: uniqueBy(appliedCommands, (command3) => command3.command),
12001
+ recommendedValidationCommands: uniqueBy(validationCommands2, (command3) => command3.command),
12002
+ commands: uniqueBy(validationCommands2, (command3) => command3.command),
12003
+ diagnostics,
12004
+ nextActions: uniqueBy(nextActions, (action) => action.command),
12005
+ plan
12006
+ };
12007
+ };
12008
+ var resolveWorkflowCommand = (command3, plan) => {
12009
+ const target = typeof plan.inputs.app === "string" ? plan.inputs.app : "<app-or-lib>";
12010
+ return command3.replaceAll("<app-or-lib-or-pkg>", target).replaceAll("<app-or-lib>", target).replaceAll("<app-name>", target);
12011
+ };
12012
+ var workflowCommandsForPlan = (plan) => plan.validation.map((validation) => ({
12013
+ command: resolveWorkflowCommand(validation.command, plan),
12014
+ reason: validation.reason,
12015
+ kind: validation.kind
12016
+ }));
12017
+ var workflowRunsDir = ".akan/workflows/runs";
12018
+ var createWorkflowRunId = (prefix = "run") => `${prefix}-${new Date().toISOString().replace(/[-:.TZ]/g, "").slice(0, 14)}-${Math.random().toString(36).slice(2, 8)}`;
12019
+ var getRunId = (artifact) => {
12020
+ if ("runId" in artifact)
12021
+ return artifact.runId;
12022
+ return createWorkflowRunId("mode" in artifact ? artifact.mode : artifact.kind);
12023
+ };
12024
+ var workflowRunArtifactPath = (runId) => `${workflowRunsDir}/${runId}.json`;
12025
+ var writeWorkflowRunArtifact = async (workspace, artifact) => {
12026
+ const runId = getRunId(artifact);
12027
+ const artifactPath = workflowRunArtifactPath(runId);
12028
+ await workspace.writeFile(artifactPath, jsonText(artifact), { silent: true });
12029
+ return { runId, path: artifactPath };
12030
+ };
12031
+ var readWorkflowRunArtifact = async (workspace, runId) => {
12032
+ const artifactPath = workflowRunArtifactPath(runId);
12033
+ if (!await workspace.exists(artifactPath))
12034
+ throw new Error(`Workflow run artifact does not exist: ${artifactPath}`);
12035
+ return await workspace.readJson(artifactPath);
12036
+ };
12037
+ var createWorkflowValidationRunReport = async ({
12038
+ runId = createWorkflowRunId("validation"),
12039
+ workflow,
12040
+ source,
12041
+ plan,
12042
+ commands,
12043
+ execute,
12044
+ diagnostics = [],
12045
+ repairActions = []
12046
+ }) => {
12047
+ const results = [];
12048
+ for (const command3 of commands) {
12049
+ results.push(await execute(command3));
12050
+ }
12051
+ const commandDiagnostics = results.flatMap((result) => result.status === "failed" ? [
12052
+ {
12053
+ severity: "error",
12054
+ code: "workflow-validation-command-failed",
12055
+ message: `Validation command failed: ${result.command}`,
12056
+ command: result.command,
12057
+ kind: result.kind,
12058
+ failureScope: result.failureScope
12059
+ }
12060
+ ] : []);
12061
+ return {
12062
+ schemaVersion: 1,
12063
+ runId,
12064
+ workflow,
12065
+ mode: "validate",
12066
+ source,
12067
+ status: workflowStatus([...diagnostics, ...commandDiagnostics]) === "failed" ? "failed" : commandStatus(results),
12068
+ commands: results,
12069
+ diagnostics: [...diagnostics, ...commandDiagnostics],
12070
+ repairActions: uniqueBy(repairActions, (action) => action.command),
12071
+ nextActions: results.filter((result) => result.status === "failed").map((result) => ({ command: result.command, reason: "Re-run this validation command after repair." })),
12072
+ plan
12073
+ };
12074
+ };
12075
+ var createDryRunWorkflowApplyReport = (plan) => {
12076
+ const changedFiles = plan.predictedChanges.flatMap((change) => {
12077
+ if (change.action !== "create" && change.action !== "modify")
12078
+ return [];
12079
+ return [
12080
+ {
12081
+ path: change.target,
12082
+ action: change.action,
12083
+ reason: change.reason
12084
+ }
12085
+ ];
12086
+ });
12087
+ const generatedFiles2 = plan.predictedChanges.flatMap((change) => {
12088
+ if (change.action !== "sync")
12089
+ return [];
12090
+ return [
12091
+ {
12092
+ path: change.target,
12093
+ action: "sync",
12094
+ reason: change.reason
12095
+ }
12096
+ ];
12097
+ });
12098
+ const diagnostics = [...plan.diagnostics];
12099
+ return createWorkflowApplyReport({
12100
+ workflow: plan.workflow,
12101
+ mode: "dry-run",
12102
+ changedFiles,
12103
+ generatedFiles: generatedFiles2,
12104
+ commands: workflowCommandsForPlan(plan),
12105
+ diagnostics,
12106
+ nextActions: workflowCommandsForPlan(plan),
12107
+ plan
12108
+ });
12109
+ };
12110
+ var workflowStepKey = (workflow, stepId) => `${workflow}:${stepId}`;
12111
+ var primitiveReportToWorkflowStepResult = (report) => ({
12112
+ changedFiles: report.changedFiles,
12113
+ generatedFiles: report.generatedFiles,
12114
+ commands: report.validationCommands,
12115
+ diagnostics: report.diagnostics,
12116
+ nextActions: report.nextActions
12117
+ });
12118
+ var workflowStringInput = (value) => typeof value === "string" ? value : null;
12119
+ var workflowStringListInput = (value) => Array.isArray(value) ? value.join(",") : null;
12120
+ var resolveWorkflowSys = async (workspace, target) => {
12121
+ if (!target)
12122
+ return null;
12123
+ const [apps, libs] = await workspace.getSyss();
12124
+ if (apps.includes(target))
12125
+ return AppExecutor.from(workspace, target);
12126
+ if (libs.includes(target))
12127
+ return LibExecutor.from(workspace, target);
12128
+ return null;
12129
+ };
12130
+ var targetMissing = (input4 = "app") => ({
12131
+ severity: "error",
12132
+ code: "workflow-target-missing",
12133
+ input: input4,
12134
+ message: "Workflow target app or library was not found."
12135
+ });
12136
+ var inputMissing = (input4) => ({
12137
+ severity: "error",
12138
+ code: "workflow-input-missing",
12139
+ input: input4,
12140
+ message: `Workflow input "${input4}" is required for apply.`
12141
+ });
12142
+ var unsupportedInput = (input4, message) => ({
12143
+ severity: "error",
12144
+ code: "workflow-input-unsupported",
12145
+ input: input4,
12146
+ message
12147
+ });
12148
+ var createWorkflowStepRegistry = ({
12149
+ workspace,
12150
+ createModule,
12151
+ createScalar,
12152
+ createUi,
12153
+ addField,
12154
+ addEnumField
12155
+ }) => {
12156
+ const inspect = async () => {
12157
+ return;
12158
+ };
12159
+ const commandOnly = async () => {
12160
+ return;
12161
+ };
12162
+ return {
12163
+ inspectSystem: inspect,
12164
+ inspectModule: inspect,
12165
+ syncTarget: commandOnly,
12166
+ lintTarget: commandOnly,
12167
+ [workflowStepKey("create-module", "create-module")]: async (_step, plan) => {
12168
+ const app = workflowStringInput(plan.inputs.app);
12169
+ const module = workflowStringInput(plan.inputs.module);
12170
+ const sys3 = await resolveWorkflowSys(workspace, app);
12171
+ if (!sys3 || !module)
12172
+ return { diagnostics: [!sys3 ? targetMissing() : inputMissing("module")] };
12173
+ return primitiveReportToWorkflowStepResult(await createModule(sys3, module));
12174
+ },
12175
+ [workflowStepKey("create-scalar", "create-scalar")]: async (_step, plan) => {
12176
+ const app = workflowStringInput(plan.inputs.app);
12177
+ const scalar = workflowStringInput(plan.inputs.scalar);
12178
+ const sys3 = await resolveWorkflowSys(workspace, app);
12179
+ if (!sys3 || !scalar)
12180
+ return { diagnostics: [!sys3 ? targetMissing() : inputMissing("scalar")] };
12181
+ return primitiveReportToWorkflowStepResult(await createScalar(sys3, scalar));
12182
+ },
12183
+ [workflowStepKey("create-ui", "create-ui")]: async (_step, plan) => {
12184
+ const surface = workflowStringInput(plan.inputs.surface);
12185
+ if (surface !== "view" && surface !== "unit" && surface !== "template") {
12186
+ return {
12187
+ diagnostics: [
12188
+ unsupportedInput("surface", "Workflow apply currently supports create-ui surfaces: view, unit, template.")
12189
+ ]
12190
+ };
12191
+ }
12192
+ return primitiveReportToWorkflowStepResult(await createUi({
12193
+ app: workflowStringInput(plan.inputs.app),
12194
+ module: workflowStringInput(plan.inputs.module),
12195
+ surface
12196
+ }));
12197
+ },
12198
+ [workflowStepKey("add-field", "update-constant")]: async (_step, plan) => {
12199
+ if (workflowStringInput(plan.inputs.type)?.toLowerCase() === "enum") {
12200
+ return primitiveReportToWorkflowStepResult(await addEnumField({
12201
+ app: workflowStringInput(plan.inputs.app),
12202
+ module: workflowStringInput(plan.inputs.module),
12203
+ field: workflowStringInput(plan.inputs.field),
12204
+ values: workflowStringListInput(plan.inputs.values),
12205
+ defaultValue: workflowStringInput(plan.inputs.default)
12206
+ }));
12207
+ }
12208
+ return primitiveReportToWorkflowStepResult(await addField({
12209
+ app: workflowStringInput(plan.inputs.app),
12210
+ module: workflowStringInput(plan.inputs.module),
12211
+ field: workflowStringInput(plan.inputs.field),
12212
+ type: workflowStringInput(plan.inputs.type),
12213
+ defaultValue: workflowStringInput(plan.inputs.default)
12214
+ }));
12215
+ },
12216
+ [workflowStepKey("add-field", "update-dictionary")]: inspect,
12217
+ [workflowStepKey("add-field", "update-ui-surfaces")]: inspect,
12218
+ [workflowStepKey("add-enum-field", "update-constant")]: async (_step, plan) => primitiveReportToWorkflowStepResult(await addEnumField({
12219
+ app: workflowStringInput(plan.inputs.app),
12220
+ module: workflowStringInput(plan.inputs.module),
12221
+ field: workflowStringInput(plan.inputs.field),
12222
+ values: workflowStringListInput(plan.inputs.values),
12223
+ defaultValue: workflowStringInput(plan.inputs.default)
12224
+ })),
12225
+ [workflowStepKey("add-enum-field", "update-dictionary")]: inspect,
12226
+ [workflowStepKey("add-enum-field", "update-option")]: inspect
12227
+ };
12228
+ };
12229
+ var renderWorkflowApplyReport = (report) => [
12230
+ `# Workflow Apply: ${report.workflow}`,
12231
+ "",
12232
+ `- Mode: ${report.mode}`,
12233
+ `- Status: ${report.status}`,
12234
+ "",
12235
+ "## Changed Files",
12236
+ ...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
12237
+ "",
12238
+ "## Generated Files",
12239
+ ...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
12240
+ "",
12241
+ "## Applied Commands",
12242
+ ...report.appliedCommands.length ? report.appliedCommands.map((command3) => `- \`${command3.command}\`: ${command3.reason}`) : ["- none"],
12243
+ "",
12244
+ "## Recommended Validation Commands",
12245
+ ...report.recommendedValidationCommands.length ? report.recommendedValidationCommands.map((command3) => `- \`${command3.command}\`: ${command3.reason}`) : ["- none"],
12246
+ "",
12247
+ "## Diagnostics",
12248
+ ...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
12249
+ "",
12250
+ "## Next Actions",
12251
+ ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
12252
+ ""
12253
+ ].join(`
12254
+ `);
12255
+ var renderWorkflowApply = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowApplyReport(report);
12256
+ var renderWorkflowValidationRunReport = (report) => [
12257
+ `# Workflow Validation: ${report.workflow}`,
12258
+ "",
12259
+ `- Run: ${report.runId}`,
12260
+ `- Status: ${report.status}`,
12261
+ "",
12262
+ "## Commands",
12263
+ ...report.commands.length ? report.commands.map((command3) => {
12264
+ const scope = command3.failureScope ? ` (${command3.failureScope})` : "";
12265
+ return `- [${command3.status}] \`${command3.command}\`${scope}: ${command3.reason}`;
12266
+ }) : ["- none"],
12267
+ "",
12268
+ "## Diagnostics",
12269
+ ...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
12270
+ "",
12271
+ "## Repair Actions",
12272
+ ...report.repairActions.length ? report.repairActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
12273
+ "",
12274
+ "## Next Actions",
12275
+ ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
12276
+ ""
12277
+ ].join(`
12278
+ `);
12279
+ var renderWorkflowValidation = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowValidationRunReport(report);
12280
+ var renderWorkflowRunArtifact = (artifact, format = "markdown") => {
12281
+ if ("kind" in artifact)
12282
+ return renderRepairReport(artifact, format);
12283
+ if ("mode" in artifact && artifact.mode === "validate")
12284
+ return renderWorkflowValidation(artifact, format);
12285
+ if ("mode" in artifact && (artifact.mode === "apply" || artifact.mode === "dry-run")) {
12286
+ return renderWorkflowApply(artifact, format);
12287
+ }
12288
+ return jsonText(artifact);
12289
+ };
12290
+ var createRepairReport = ({
12291
+ command: command3,
12292
+ kind,
12293
+ target = null,
12294
+ diagnostics = [],
12295
+ repairActions = [],
12296
+ nextActions = [],
12297
+ commands = []
12298
+ }) => ({
12299
+ schemaVersion: 1,
12300
+ command: command3,
12301
+ kind,
12302
+ target,
12303
+ status: workflowStatus(diagnostics) === "failed" || commandStatus(commands) === "failed" ? "failed" : "passed",
12304
+ diagnostics,
12305
+ repairActions: uniqueBy(repairActions, (action) => action.command),
12306
+ nextActions: uniqueBy(nextActions, (action) => action.command),
12307
+ commands
12308
+ });
12309
+ var renderRepairReportMarkdown = (report) => [
12310
+ `# Akan Repair: ${report.kind}`,
12311
+ "",
12312
+ `- Status: ${report.status}`,
12313
+ `- Target: ${report.target ?? "none"}`,
12314
+ "",
12315
+ "## Commands",
12316
+ ...report.commands.length ? report.commands.map((command3) => `- [${command3.status}] \`${command3.command}\`: ${command3.reason}`) : ["- none"],
12317
+ "",
12318
+ "## Diagnostics",
12319
+ ...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
12320
+ "",
12321
+ "## Next Actions",
12322
+ ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
12323
+ ""
12324
+ ].join(`
12325
+ `);
12326
+ var renderRepairReport = (report, format = "markdown") => format === "json" ? jsonText(report) : renderRepairReportMarkdown(report);
12327
+
12328
+ class WorkflowExecutor {
12329
+ registry;
12330
+ constructor(registry) {
12331
+ this.registry = registry;
12332
+ }
12333
+ async apply(plan) {
12334
+ const changedFiles = [];
12335
+ const generatedFiles2 = [];
12336
+ const recommendedValidationCommands = [];
12337
+ const diagnostics = [...plan.diagnostics];
12338
+ const nextActions = [];
12339
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
12340
+ return createWorkflowApplyReport({
12341
+ workflow: plan.workflow,
12342
+ mode: "apply",
12343
+ changedFiles,
12344
+ generatedFiles: generatedFiles2,
12345
+ recommendedValidationCommands,
12346
+ diagnostics,
12347
+ nextActions,
12348
+ plan
12349
+ });
12350
+ }
12351
+ recommendedValidationCommands.push(...workflowCommandsForPlan(plan));
12352
+ nextActions.push(...workflowCommandsForPlan(plan));
12353
+ for (const step of plan.steps) {
12354
+ const runner2 = this.registry[workflowStepKey(plan.workflow, step.id)] ?? this.registry[step.tool] ?? this.registry[step.id];
12355
+ if (!runner2) {
12356
+ diagnostics.push({
12357
+ severity: "error",
12358
+ code: "workflow-step-unsupported",
12359
+ message: `Workflow ${plan.workflow} step "${step.id}" is not supported by workflow apply yet.`
12360
+ });
12361
+ nextActions.push({
12362
+ command: `akan workflow explain ${plan.workflow}`,
12363
+ reason: "Review the unsupported workflow step before retrying apply."
12364
+ });
12365
+ break;
12366
+ }
12367
+ const result = await runner2(step, plan);
12368
+ if (!result)
12369
+ continue;
12370
+ changedFiles.push(...result.changedFiles ?? []);
12371
+ generatedFiles2.push(...result.generatedFiles ?? []);
12372
+ recommendedValidationCommands.push(...result.commands ?? []);
12373
+ diagnostics.push(...result.diagnostics ?? []);
12374
+ nextActions.push(...result.nextActions ?? []);
12375
+ if (diagnostics.some((diagnostic) => diagnostic.severity === "error"))
12376
+ break;
12377
+ }
12378
+ return createWorkflowApplyReport({
12379
+ workflow: plan.workflow,
12380
+ mode: "apply",
12381
+ changedFiles,
12382
+ generatedFiles: generatedFiles2,
12383
+ recommendedValidationCommands,
12384
+ diagnostics,
12385
+ nextActions,
12386
+ plan
12387
+ });
12388
+ }
12389
+ }
12390
+ var createPrimitiveWriteReport = ({
12391
+ command: command3,
12392
+ status,
12393
+ changedFiles = [],
12394
+ generatedFiles: generatedFiles2 = [],
12395
+ validationCommands: validationCommands2 = [],
12396
+ diagnostics = [],
12397
+ nextActions = []
12398
+ }) => ({
12399
+ schemaVersion: 1,
12400
+ command: command3,
12401
+ status: status ?? (diagnostics.some((diagnostic) => diagnostic.severity === "error") ? "failed" : "passed"),
12402
+ changedFiles,
12403
+ generatedFiles: generatedFiles2,
12404
+ validationCommands: validationCommands2,
12405
+ diagnostics,
12406
+ nextActions
12407
+ });
12408
+ var renderPrimitiveWriteReport = (report) => [
12409
+ `# Primitive Write: ${report.command}`,
12410
+ "",
12411
+ `- Status: ${report.status}`,
12412
+ "",
12413
+ "## Changed Files",
12414
+ ...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
12415
+ "",
12416
+ "## Generated Files",
12417
+ ...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
12418
+ "",
12419
+ "## Validation Commands",
12420
+ ...report.validationCommands.length ? report.validationCommands.map((validation) => `- \`${validation.command}\`: ${validation.reason}`) : ["- none"],
12421
+ "",
12422
+ "## Diagnostics",
12423
+ ...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
12424
+ "",
12425
+ "## Next Actions",
12426
+ ...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
12427
+ ""
12428
+ ].join(`
12429
+ `);
12430
+ var renderPrimitiveReport = (report, format = "markdown") => format === "json" ? jsonText(report) : renderPrimitiveWriteReport(report);
12431
+ var getSysRoot = (sys3) => `${sys3.type}s/${sys3.name}`;
12432
+ var sourceFile = (sys3, path40, action, reason) => ({
12433
+ path: `${getSysRoot(sys3)}/${path40}`,
12434
+ action,
12435
+ reason
12436
+ });
12437
+ var generatedFilesForSync = (sys3, reason = "Generated files may change after sync.") => [
12438
+ { path: `${getSysRoot(sys3)}/lib/cnst.ts`, action: "sync", reason },
12439
+ { path: `${getSysRoot(sys3)}/lib/dict.ts`, action: "sync", reason },
12440
+ { path: `${getSysRoot(sys3)}/lib/option.ts`, action: "sync", reason },
12441
+ { path: `${getSysRoot(sys3)}/lib/index.ts`, action: "sync", reason }
12442
+ ];
12443
+ var validationCommandsForTarget = (target) => [
12444
+ { command: `akan sync ${target}`, reason: "Refresh generated Akan files from source conventions." },
12445
+ { command: `akan lint ${target}`, reason: "Validate formatting, imports, and static lint rules." }
12446
+ ];
12447
+ var nextActionsForTarget = (target) => [
12448
+ { command: `akan sync ${target}`, reason: "Refresh generated Akan files after source changes." },
12449
+ { command: `akan lint ${target}`, reason: "Validate the target after generated files are refreshed." }
12450
+ ];
12451
+ var createPassedPrimitiveReport = ({
12452
+ command: command3,
12453
+ changedFiles,
12454
+ generatedFiles: generatedFiles2,
12455
+ target,
12456
+ nextActions
12457
+ }) => createPrimitiveWriteReport({
12458
+ command: command3,
12459
+ changedFiles,
12460
+ generatedFiles: generatedFiles2 ?? [],
12461
+ validationCommands: validationCommandsForTarget(target),
12462
+ diagnostics: [],
12463
+ nextActions: nextActions ?? nextActionsForTarget(target)
12464
+ });
12465
+ var scalarChangedFiles = (sys3, scalarName, files) => Object.values(files).map((file) => sourceFile(sys3, `lib/__scalar/${scalarName}/${file.filename}`, "create", "Scalar source file was created."));
12466
+ var titleize = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
12467
+ var lowerlize = (value) => `${value.slice(0, 1).toLowerCase()}${value.slice(1)}`;
12468
+ var compactDiagnostics = (diagnostics) => diagnostics.filter((diagnostic) => !!diagnostic);
12469
+ var normalizeFieldType = (typeName) => {
12470
+ const normalizedTypes = {
12471
+ string: "String",
12472
+ boolean: "Boolean",
12473
+ date: "Date",
12474
+ int: "Int",
12475
+ integer: "Int",
12476
+ float: "Float",
12477
+ double: "Float",
12478
+ decimal: "Float"
12479
+ };
12480
+ return normalizedTypes[typeName.toLowerCase()] ?? typeName;
12481
+ };
12482
+ var ensureBaseTypeImport = (content, typeName) => {
12483
+ if (typeName !== "Int" && typeName !== "Float")
12484
+ return content;
12485
+ if (new RegExp(`import \\{[^}]*\\b${typeName}\\b[^}]*\\} from "akanjs/base";`).test(content))
12486
+ return content;
12487
+ const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
12488
+ if (baseImport) {
12489
+ const names = baseImport[1].split(",").map((name) => name.trim()).filter(Boolean);
12490
+ return content.replace(baseImport[0], `import { ${[...names, typeName].sort().join(", ")} } from "akanjs/base";`);
12491
+ }
12492
+ return `import { ${typeName} } from "akanjs/base";
12493
+ ${content}`;
12494
+ };
12495
+ var fieldExpression = (typeName, defaultValue) => {
12496
+ const typeExpression = normalizeFieldType(typeName);
12497
+ const defaultOption = defaultValue ? `, { default: ${JSON.stringify(defaultValue)} }` : "";
12498
+ return `field(${typeExpression}${defaultOption})`;
12499
+ };
12500
+ var insertIntoObject = (content, className, line) => {
12501
+ const classIndex = content.indexOf(`export class ${className} extends via`);
12502
+ if (classIndex < 0)
12503
+ return null;
12504
+ const objectEndIndex = content.indexOf("}))", classIndex);
12505
+ if (objectEndIndex < 0)
12506
+ return null;
12507
+ const prefix = content.slice(0, objectEndIndex);
12508
+ const suffix = content.slice(objectEndIndex);
12509
+ const insertion = prefix.endsWith(`
12510
+ `) ? ` ${line}
12511
+ ` : `
12512
+ ${line}
12513
+ `;
12514
+ return `${prefix}${insertion}${suffix}`;
12515
+ };
12516
+ var ensureEnumImport = (content) => {
12517
+ if (content.includes("enumOf"))
12518
+ return content;
12519
+ const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
12520
+ if (baseImport) {
12521
+ const names = baseImport[1]?.split(",").map((name) => name.trim()) ?? [];
12522
+ return content.replace(baseImport[0], `import { ${[...names, "enumOf"].sort().join(", ")} } from "akanjs/base";`);
12523
+ }
12524
+ return `import { enumOf } from "akanjs/base";
12525
+ ${content}`;
12526
+ };
12527
+ var insertEnumClass = (content, enumClassName, enumName, values) => {
12528
+ if (content.includes(`export class ${enumClassName} extends enumOf`))
12529
+ return content;
12530
+ const enumClass = `export class ${enumClassName} extends enumOf("${enumName}", [
12531
+ ${values.map((value) => ` ${JSON.stringify(value)},`).join(`
12532
+ `)}
12533
+ ] as const) {}
12534
+
12535
+ `;
12536
+ const firstClassIndex = content.indexOf("export class ");
12537
+ if (firstClassIndex < 0)
12538
+ return `${content}
12539
+ ${enumClass}`;
12540
+ return `${content.slice(0, firstClassIndex)}${enumClass}${content.slice(firstClassIndex)}`;
12541
+ };
12542
+ var insertDictionaryModelField = (content, moduleClassName, fieldName) => {
12543
+ if (new RegExp(`\\b${fieldName}\\s*:`).test(content))
12544
+ return content;
12545
+ const label = titleize(fieldName);
12546
+ const modelIndex = content.indexOf(`.model<${moduleClassName}>((t) => ({`);
12547
+ if (modelIndex < 0)
12548
+ return null;
12549
+ const objectEndIndex = content.indexOf(" }))", modelIndex);
12550
+ if (objectEndIndex < 0)
12551
+ return null;
12552
+ return `${content.slice(0, objectEndIndex)} ${fieldName}: t([${JSON.stringify(label)}, ${JSON.stringify(label)}]).desc([${JSON.stringify(label)}, ${JSON.stringify(label)}]),
12553
+ ${content.slice(objectEndIndex)}`;
12554
+ };
12555
+ var ensureConstantTypeImport = (content, constantPath, typeName) => {
12556
+ if (new RegExp(`import type \\{[^}]*\\b${typeName}\\b[^}]*\\} from "${constantPath}";`).test(content))
12557
+ return content;
12558
+ const importPattern = new RegExp(`import type \\{ ([^}]+) \\} from "${constantPath}";`);
12559
+ const existingImport = content.match(importPattern);
12560
+ if (existingImport !== null) {
12561
+ const names = existingImport[1]?.split(",").map((name) => name.trim()) ?? [];
12562
+ return content.replace(existingImport[0], `import type { ${[...names, typeName].sort().join(", ")} } from "${constantPath}";`);
12563
+ }
12564
+ return `import type { ${typeName} } from "${constantPath}";
12565
+ ${content}`;
12566
+ };
12567
+ var insertDictionaryEnum = (content, enumClassName, enumName, values) => {
12568
+ if (content.includes(`.enum<${enumClassName}>("${enumName}"`))
12569
+ return content;
12570
+ const enumBlock = ` .enum<${enumClassName}>("${enumName}", (t) => ({
12571
+ ${values.map((value) => ` ${value}: t([${JSON.stringify(titleize(value))}, ${JSON.stringify(titleize(value))}]),`).join(`
12572
+ `)}
12573
+ }))
12574
+ `;
12575
+ const chainEndIndex = content.lastIndexOf(";");
12576
+ const insertBeforeIndex = [content.indexOf(".error("), content.indexOf(".translate("), chainEndIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
12577
+ if (insertBeforeIndex === undefined)
12578
+ return null;
12579
+ return `${content.slice(0, insertBeforeIndex)}${enumBlock}${content.slice(insertBeforeIndex)}`;
12580
+ };
12581
+ var parseValues = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
11693
12582
  // pkgs/@akanjs/devkit/incrementalBuilder/incrementalBuilder.proc.ts
11694
12583
  import { Logger as Logger11 } from "akanjs/common";
11695
12584
  class IncrementalBuilder {