@akanjs/cli 2.3.9-rc.3 → 2.3.9-rc.5
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/incrementalBuilder.proc.js +1265 -724
- package/index.js +1558 -761
- package/package.json +2 -2
- package/templates/module/__model__.constant.ts +1 -2
- package/templates/module/__model__.dictionary.ts +10 -4
|
@@ -3396,28 +3396,28 @@ class SysExecutor extends Executor {
|
|
|
3396
3396
|
return scalarModules;
|
|
3397
3397
|
}
|
|
3398
3398
|
async getViewComponents() {
|
|
3399
|
-
const viewComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.View.tsx`).exists());
|
|
3399
|
+
const viewComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.View.tsx`).exists());
|
|
3400
3400
|
return viewComponents;
|
|
3401
3401
|
}
|
|
3402
3402
|
async getUnitComponents() {
|
|
3403
|
-
const unitComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.Unit.tsx`).exists());
|
|
3403
|
+
const unitComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.Unit.tsx`).exists());
|
|
3404
3404
|
return unitComponents;
|
|
3405
3405
|
}
|
|
3406
3406
|
async getTemplateComponents() {
|
|
3407
|
-
const templateComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${name}.Template.tsx`).exists());
|
|
3407
|
+
const templateComponents = (await this.readdir("lib")).filter((name) => !name.startsWith("_") && !name.startsWith("__") && !name.endsWith(".ts")).filter((name) => Bun.file(`${this.cwdPath}/lib/${name}/${capitalize(name)}.Template.tsx`).exists());
|
|
3408
3408
|
return templateComponents;
|
|
3409
3409
|
}
|
|
3410
3410
|
async getViewsSourceCode() {
|
|
3411
3411
|
const viewComponents = await this.getViewComponents();
|
|
3412
|
-
return Promise.all(viewComponents.map((viewComponent) => this.getLocalFile(`lib/${viewComponent}/${viewComponent}.View.tsx`)));
|
|
3412
|
+
return Promise.all(viewComponents.map((viewComponent) => this.getLocalFile(`lib/${viewComponent}/${capitalize(viewComponent)}.View.tsx`)));
|
|
3413
3413
|
}
|
|
3414
3414
|
async getUnitsSourceCode() {
|
|
3415
3415
|
const unitComponents = await this.getUnitComponents();
|
|
3416
|
-
return Promise.all(unitComponents.map((unitComponent) => this.getLocalFile(`lib/${unitComponent}/${unitComponent}.Unit.tsx`)));
|
|
3416
|
+
return Promise.all(unitComponents.map((unitComponent) => this.getLocalFile(`lib/${unitComponent}/${capitalize(unitComponent)}.Unit.tsx`)));
|
|
3417
3417
|
}
|
|
3418
3418
|
async getTemplatesSourceCode() {
|
|
3419
3419
|
const templateComponents = await this.getTemplateComponents();
|
|
3420
|
-
return Promise.all(templateComponents.map((templateComponent) => this.getLocalFile(`lib/${templateComponent}/${templateComponent}.Template.tsx`)));
|
|
3420
|
+
return Promise.all(templateComponents.map((templateComponent) => this.getLocalFile(`lib/${templateComponent}/${capitalize(templateComponent)}.Template.tsx`)));
|
|
3421
3421
|
}
|
|
3422
3422
|
async getScalarConstantFiles() {
|
|
3423
3423
|
const scalarModules = await this.getScalarModules();
|
|
@@ -4778,238 +4778,11 @@ class AkanAppHost {
|
|
|
4778
4778
|
// pkgs/@akanjs/devkit/akanContext.ts
|
|
4779
4779
|
import { readdir } from "fs/promises";
|
|
4780
4780
|
import path10 from "path";
|
|
4781
|
-
import { capitalize as
|
|
4781
|
+
import { capitalize as capitalize4 } from "akanjs/common";
|
|
4782
4782
|
|
|
4783
|
-
// pkgs/@akanjs/devkit/workflow/
|
|
4784
|
-
import { capitalize as capitalize2 } from "akanjs/common";
|
|
4783
|
+
// pkgs/@akanjs/devkit/workflow/utils.ts
|
|
4785
4784
|
var jsonText = (value, { trailingNewline = true } = {}) => `${JSON.stringify(value, null, 2)}${trailingNewline ? `
|
|
4786
4785
|
` : ""}`;
|
|
4787
|
-
var surfaceModes = new Set(["infer", "include", "skip"]);
|
|
4788
|
-
var parseStringList = (value) => {
|
|
4789
|
-
if (Array.isArray(value)) {
|
|
4790
|
-
const values = value.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
4791
|
-
return values.length === value.length ? values : null;
|
|
4792
|
-
}
|
|
4793
|
-
if (typeof value !== "string")
|
|
4794
|
-
return null;
|
|
4795
|
-
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
4796
|
-
};
|
|
4797
|
-
var normalizeInputValue = (name, spec, value) => {
|
|
4798
|
-
if (spec.type === "string")
|
|
4799
|
-
return typeof value === "string" && value.length > 0 ? value : null;
|
|
4800
|
-
if (spec.type === "string-list") {
|
|
4801
|
-
const values = parseStringList(value);
|
|
4802
|
-
return values && values.length > 0 ? values : null;
|
|
4803
|
-
}
|
|
4804
|
-
if (typeof value === "string" && surfaceModes.has(value))
|
|
4805
|
-
return value;
|
|
4806
|
-
throw new Error(`Unsupported workflow input value for ${name}`);
|
|
4807
|
-
};
|
|
4808
|
-
var listWorkflowSpecs = (specs) => [...specs].sort((a, b) => a.name.localeCompare(b.name));
|
|
4809
|
-
var getWorkflowSpec = (specs, name) => specs.find((spec) => spec.name === name) ?? null;
|
|
4810
|
-
var compactWorkflowInputs = (inputs) => Object.fromEntries(Object.entries(inputs).filter(([, value]) => value !== null && value !== ""));
|
|
4811
|
-
var addFieldComponentForType = (typeName) => {
|
|
4812
|
-
const normalizedType = normalizeFieldType(typeName);
|
|
4813
|
-
if (normalizedType === "Boolean")
|
|
4814
|
-
return "Field.ToggleSelect";
|
|
4815
|
-
if (normalizedType === "Date")
|
|
4816
|
-
return "Field.Date";
|
|
4817
|
-
if (normalizedType === "Int" || normalizedType === "Float")
|
|
4818
|
-
return "Field.ToggleSelect or Field.Text";
|
|
4819
|
-
if (typeName.toLowerCase() === "enum")
|
|
4820
|
-
return "Field.ToggleSelect";
|
|
4821
|
-
return "Field.Text";
|
|
4822
|
-
};
|
|
4823
|
-
var createAddFieldRecommendations = (inputs) => {
|
|
4824
|
-
const app = typeof inputs.app === "string" ? inputs.app : "<app-or-lib>";
|
|
4825
|
-
const module = typeof inputs.module === "string" ? inputs.module : "<module>";
|
|
4826
|
-
const field = typeof inputs.field === "string" ? inputs.field : "<field>";
|
|
4827
|
-
const typeName = typeof inputs.type === "string" ? inputs.type : null;
|
|
4828
|
-
if (!typeName)
|
|
4829
|
-
return [];
|
|
4830
|
-
const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
|
|
4831
|
-
const constantPath = `*/lib/${module}/${module}.constant.ts`;
|
|
4832
|
-
const dictionaryPath = `*/lib/${module}/${module}.dictionary.ts`;
|
|
4833
|
-
const templatePath = `*/lib/${module}/${capitalize2(module)}.Template.tsx`;
|
|
4834
|
-
return [
|
|
4835
|
-
...normalizedType === "Int" || normalizedType === "Float" ? [
|
|
4836
|
-
{
|
|
4837
|
-
code: "add-field-import",
|
|
4838
|
-
kind: "import",
|
|
4839
|
-
target: constantPath,
|
|
4840
|
-
confidence: "high",
|
|
4841
|
-
message: `Import ${normalizedType} from "akanjs/base" before writing field(${normalizedType}).`
|
|
4842
|
-
}
|
|
4843
|
-
] : [],
|
|
4844
|
-
{
|
|
4845
|
-
code: "add-field-placement-constant",
|
|
4846
|
-
kind: "placement",
|
|
4847
|
-
target: constantPath,
|
|
4848
|
-
confidence: "high",
|
|
4849
|
-
message: `Insert ${field}: field(${normalizedType}) in ${capitalize2(module)}Input.`
|
|
4850
|
-
},
|
|
4851
|
-
{
|
|
4852
|
-
code: "add-field-placement-dictionary",
|
|
4853
|
-
kind: "placement",
|
|
4854
|
-
target: dictionaryPath,
|
|
4855
|
-
confidence: "high",
|
|
4856
|
-
message: `Add dictionary labels for ${module}.${field}.`
|
|
4857
|
-
},
|
|
4858
|
-
{
|
|
4859
|
-
code: "add-field-component",
|
|
4860
|
-
kind: "ui-component",
|
|
4861
|
-
target: templatePath,
|
|
4862
|
-
confidence: normalizedType === "Int" || normalizedType === "Float" ? "medium" : "high",
|
|
4863
|
-
message: `Recommended UI component for ${field} (${normalizedType}): ${addFieldComponentForType(typeName)}.`
|
|
4864
|
-
},
|
|
4865
|
-
{
|
|
4866
|
-
code: "add-field-ui-manual-review",
|
|
4867
|
-
kind: "manual-action",
|
|
4868
|
-
target: templatePath,
|
|
4869
|
-
action: `Review ${app}:${module} Template/Unit/View/Store surfaces and add ${field} only where the existing pattern is clear.`,
|
|
4870
|
-
confidence: "medium",
|
|
4871
|
-
message: "UI surface edits are planned as manual-review unless a safe existing field-list pattern is detected."
|
|
4872
|
-
}
|
|
4873
|
-
];
|
|
4874
|
-
};
|
|
4875
|
-
var createWorkflowPlanRecommendations = (spec, inputs) => [
|
|
4876
|
-
{
|
|
4877
|
-
code: "workflow-apply-first",
|
|
4878
|
-
kind: "auto-apply",
|
|
4879
|
-
confidence: "high",
|
|
4880
|
-
action: "Call apply_workflow with the MCP planPath before editing source files directly.",
|
|
4881
|
-
message: `Apply the ${spec.name} plan through apply_workflow when MCP returns planPath or next.tool=apply_workflow.`
|
|
4882
|
-
},
|
|
4883
|
-
{
|
|
4884
|
-
code: "workflow-validate-apply-report",
|
|
4885
|
-
kind: "validation",
|
|
4886
|
-
confidence: "high",
|
|
4887
|
-
action: "After apply_workflow, call run_validation with validationTarget when present; otherwise use applyReportPath.",
|
|
4888
|
-
message: "Validate the apply report artifact so diagnostics and recommendations follow the actual apply result."
|
|
4889
|
-
},
|
|
4890
|
-
...spec.name === "add-field" ? createAddFieldRecommendations(inputs) : []
|
|
4891
|
-
];
|
|
4892
|
-
var createWorkflowPlan = (spec, rawInputs) => {
|
|
4893
|
-
const inputs = {};
|
|
4894
|
-
const diagnostics = [];
|
|
4895
|
-
for (const [name, inputSpec] of Object.entries(spec.inputs)) {
|
|
4896
|
-
const rawValue = rawInputs[name];
|
|
4897
|
-
if (rawValue === undefined || rawValue === null || rawValue === "") {
|
|
4898
|
-
if (inputSpec.required) {
|
|
4899
|
-
diagnostics.push({
|
|
4900
|
-
severity: "error",
|
|
4901
|
-
code: "workflow-input-missing",
|
|
4902
|
-
input: name,
|
|
4903
|
-
message: `Workflow ${spec.name} requires input "${name}".`
|
|
4904
|
-
});
|
|
4905
|
-
}
|
|
4906
|
-
continue;
|
|
4907
|
-
}
|
|
4908
|
-
const value = normalizeInputValue(name, inputSpec, rawValue);
|
|
4909
|
-
if (value === null) {
|
|
4910
|
-
diagnostics.push({
|
|
4911
|
-
severity: "error",
|
|
4912
|
-
code: "workflow-input-invalid",
|
|
4913
|
-
input: name,
|
|
4914
|
-
message: `Workflow ${spec.name} input "${name}" must be ${inputSpec.type}.`
|
|
4915
|
-
});
|
|
4916
|
-
continue;
|
|
4917
|
-
}
|
|
4918
|
-
if (inputSpec.allowedValues && typeof value === "string" && !inputSpec.allowedValues.includes(value)) {
|
|
4919
|
-
diagnostics.push({
|
|
4920
|
-
severity: "error",
|
|
4921
|
-
code: "workflow-input-not-allowed",
|
|
4922
|
-
input: name,
|
|
4923
|
-
message: `Workflow ${spec.name} input "${name}" must be one of: ${inputSpec.allowedValues.join(", ")}.`
|
|
4924
|
-
});
|
|
4925
|
-
continue;
|
|
4926
|
-
}
|
|
4927
|
-
inputs[name] = value;
|
|
4928
|
-
}
|
|
4929
|
-
const fieldType = inputs.type;
|
|
4930
|
-
if (spec.name === "add-field" && typeof fieldType === "string" && (fieldType.toLowerCase() === "number" || fieldType.toLowerCase() === "numeric")) {
|
|
4931
|
-
diagnostics.push({
|
|
4932
|
-
severity: "error",
|
|
4933
|
-
code: "primitive-field-type-unsupported",
|
|
4934
|
-
input: "type",
|
|
4935
|
-
message: `Field type "${fieldType}" is ambiguous in Akan. Use Int for integer fields or Float for decimal fields.`
|
|
4936
|
-
});
|
|
4937
|
-
}
|
|
4938
|
-
return {
|
|
4939
|
-
schemaVersion: 1,
|
|
4940
|
-
workflow: spec.name,
|
|
4941
|
-
mode: "plan",
|
|
4942
|
-
inputs,
|
|
4943
|
-
optionalSurfaces: spec.optionalSurfaces ?? {},
|
|
4944
|
-
steps: spec.steps,
|
|
4945
|
-
predictedChanges: spec.predictedChanges,
|
|
4946
|
-
validation: spec.validation,
|
|
4947
|
-
diagnostics,
|
|
4948
|
-
recommendations: createWorkflowPlanRecommendations(spec, inputs),
|
|
4949
|
-
requiresApproval: true
|
|
4950
|
-
};
|
|
4951
|
-
};
|
|
4952
|
-
var renderWorkflowList = (specs) => [
|
|
4953
|
-
"# Akan Workflows",
|
|
4954
|
-
"",
|
|
4955
|
-
...specs.flatMap((spec) => [`- \`${spec.name}\`: ${spec.description}`, ` - When: ${spec.whenToUse}`]),
|
|
4956
|
-
""
|
|
4957
|
-
].join(`
|
|
4958
|
-
`);
|
|
4959
|
-
var renderWorkflowExplain = (spec) => [
|
|
4960
|
-
`# Workflow: ${spec.name}`,
|
|
4961
|
-
"",
|
|
4962
|
-
spec.description,
|
|
4963
|
-
"",
|
|
4964
|
-
"## When To Use",
|
|
4965
|
-
spec.whenToUse,
|
|
4966
|
-
"",
|
|
4967
|
-
"## Inputs",
|
|
4968
|
-
...Object.entries(spec.inputs).map(([name, input2]) => `- \`${name}\`${input2.required ? " (required)" : ""}: ${input2.description}${input2.allowedValues ? ` Allowed: ${input2.allowedValues.join(", ")}.` : ""}`),
|
|
4969
|
-
"",
|
|
4970
|
-
"## Optional Surfaces",
|
|
4971
|
-
...Object.entries(spec.optionalSurfaces ?? {}).map(([name, mode]) => `- \`${name}\`: ${mode}`),
|
|
4972
|
-
"",
|
|
4973
|
-
"## Steps",
|
|
4974
|
-
...spec.steps.map((step, index) => `${index + 1}. \`${step.id}\` (${step.tool}): ${step.description}`),
|
|
4975
|
-
"",
|
|
4976
|
-
"## Validation",
|
|
4977
|
-
...spec.validation.map((validation) => `- \`${validation.command}\`: ${validation.reason}`),
|
|
4978
|
-
""
|
|
4979
|
-
].join(`
|
|
4980
|
-
`);
|
|
4981
|
-
var renderWorkflowPlan = (plan) => [
|
|
4982
|
-
`# Workflow Plan: ${plan.workflow}`,
|
|
4983
|
-
"",
|
|
4984
|
-
`- Mode: ${plan.mode}`,
|
|
4985
|
-
`- Requires approval: ${plan.requiresApproval}`,
|
|
4986
|
-
"",
|
|
4987
|
-
"## Inputs",
|
|
4988
|
-
...Object.entries(plan.inputs).map(([name, value]) => `- \`${name}\`: ${Array.isArray(value) ? value.join(", ") : value}`),
|
|
4989
|
-
"",
|
|
4990
|
-
"## Optional Surfaces",
|
|
4991
|
-
...Object.entries(plan.optionalSurfaces).map(([name, mode]) => `- \`${name}\`: ${mode}`),
|
|
4992
|
-
"",
|
|
4993
|
-
"## Steps",
|
|
4994
|
-
...plan.steps.map((step, index) => `${index + 1}. \`${step.id}\` (${step.tool}): ${step.description}`),
|
|
4995
|
-
"",
|
|
4996
|
-
"## Predicted Changes",
|
|
4997
|
-
...plan.predictedChanges.map((change) => {
|
|
4998
|
-
const scope = change.applyScope ? ` (${change.applyScope})` : "";
|
|
4999
|
-
return `- \`${change.action}\`${scope} ${change.target}: ${change.reason}`;
|
|
5000
|
-
}),
|
|
5001
|
-
"",
|
|
5002
|
-
"## Validation",
|
|
5003
|
-
...plan.validation.map((validation) => `- \`${validation.command}\`: ${validation.reason}`),
|
|
5004
|
-
"",
|
|
5005
|
-
"## Diagnostics",
|
|
5006
|
-
...plan.diagnostics.length ? plan.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
5007
|
-
"",
|
|
5008
|
-
"## Recommendations",
|
|
5009
|
-
...plan.recommendations.length ? plan.recommendations.map((recommendation) => `- [${recommendation.kind}] ${recommendation.message}`) : ["- none"],
|
|
5010
|
-
""
|
|
5011
|
-
].join(`
|
|
5012
|
-
`);
|
|
5013
4786
|
var workflowStatus = (diagnostics) => diagnostics.some((diagnostic) => diagnostic.severity === "error") ? "failed" : "passed";
|
|
5014
4787
|
var commandStatus = (commands) => commands.some((command) => command.status === "failed") ? "failed" : "passed";
|
|
5015
4788
|
var uniqueBy = (values, key) => {
|
|
@@ -5022,6 +4795,9 @@ var uniqueBy = (values, key) => {
|
|
|
5022
4795
|
return true;
|
|
5023
4796
|
});
|
|
5024
4797
|
};
|
|
4798
|
+
var compactDiagnostics = (diagnostics) => diagnostics.filter((diagnostic) => !!diagnostic);
|
|
4799
|
+
|
|
4800
|
+
// pkgs/@akanjs/devkit/workflow/artifacts.ts
|
|
5025
4801
|
var createWorkflowApplyReport = ({
|
|
5026
4802
|
workflow,
|
|
5027
4803
|
mode,
|
|
@@ -5104,6 +4880,66 @@ var readWorkflowRunArtifact = async (workspace, runId) => {
|
|
|
5104
4880
|
throw new Error(`Workflow run artifact does not exist: ${artifactPath}`);
|
|
5105
4881
|
return await workspace.readJson(artifactPath);
|
|
5106
4882
|
};
|
|
4883
|
+
var failedCommandScopes = (commands) => commands.filter((command) => command.status === "failed").map((command) => command.failureScope ?? "unknown");
|
|
4884
|
+
var errorDiagnosticScopes = (diagnostics) => diagnostics.filter((diagnostic) => diagnostic.severity === "error").map((diagnostic) => diagnostic.failureScope ?? (diagnostic.scope === "baseline" ? "workspace-config" : diagnostic.scope === "workflow" ? "source-change" : "unknown"));
|
|
4885
|
+
var hasScopeFailure = (scopes, scope, diagnostics = []) => scopes.includes(scope) || diagnostics.some((diagnostic) => diagnostic.severity === "error" && diagnostic.failureScope === scope);
|
|
4886
|
+
var statusForScope = (commands, diagnostics, scopes, expectedScope) => {
|
|
4887
|
+
const hasCommands = commands.length > 0 || diagnostics.length > 0;
|
|
4888
|
+
if (!hasCommands)
|
|
4889
|
+
return "unknown";
|
|
4890
|
+
return hasScopeFailure(scopes, expectedScope, diagnostics) ? "failed" : "passed";
|
|
4891
|
+
};
|
|
4892
|
+
var statusForValidationKind = (commands, kind) => {
|
|
4893
|
+
const matching = commands.filter((command) => command.kind === kind);
|
|
4894
|
+
if (matching.length === 0)
|
|
4895
|
+
return "unknown";
|
|
4896
|
+
return matching.some((command) => command.status === "failed") ? "failed" : "passed";
|
|
4897
|
+
};
|
|
4898
|
+
var createKnownBlockers = (commands, diagnostics) => {
|
|
4899
|
+
const commandBlockers = commands.filter((command) => command.status === "failed" && (command.failureScope === "workspace-config" || command.failureScope === "environment")).map((command) => ({
|
|
4900
|
+
code: `workflow-validation-${command.failureScope}`,
|
|
4901
|
+
message: `${command.failureScope === "environment" ? "Environment" : "Workspace configuration"} blocker: ${command.command}`,
|
|
4902
|
+
failureScope: command.failureScope ?? "unknown",
|
|
4903
|
+
command: command.command,
|
|
4904
|
+
kind: command.kind,
|
|
4905
|
+
count: 1
|
|
4906
|
+
}));
|
|
4907
|
+
const diagnosticBlockers = diagnostics.filter((diagnostic) => diagnostic.severity === "error" && (diagnostic.failureScope === "workspace-config" || diagnostic.failureScope === "environment" || diagnostic.scope === "baseline")).map((diagnostic) => ({
|
|
4908
|
+
code: diagnostic.code,
|
|
4909
|
+
message: diagnostic.message,
|
|
4910
|
+
failureScope: diagnostic.failureScope ?? "workspace-config",
|
|
4911
|
+
command: diagnostic.command,
|
|
4912
|
+
kind: diagnostic.kind,
|
|
4913
|
+
count: 1
|
|
4914
|
+
}));
|
|
4915
|
+
const grouped = new Map;
|
|
4916
|
+
for (const blocker of [...commandBlockers, ...diagnosticBlockers]) {
|
|
4917
|
+
const key = `${blocker.failureScope}:${blocker.code}:${blocker.command ?? ""}:${blocker.message}`;
|
|
4918
|
+
const existing = grouped.get(key);
|
|
4919
|
+
if (existing)
|
|
4920
|
+
existing.count += blocker.count;
|
|
4921
|
+
else
|
|
4922
|
+
grouped.set(key, blocker);
|
|
4923
|
+
}
|
|
4924
|
+
return [...grouped.values()];
|
|
4925
|
+
};
|
|
4926
|
+
var createValidationStatuses = (commands, diagnostics) => {
|
|
4927
|
+
const scopes = [...failedCommandScopes(commands), ...errorDiagnosticScopes(diagnostics)];
|
|
4928
|
+
const sourceStatus = statusForScope(commands, diagnostics, scopes, "source-change");
|
|
4929
|
+
const workspaceStatus = hasScopeFailure(scopes, "workspace-config", diagnostics) || hasScopeFailure(scopes, "environment", diagnostics) ? "failed" : commands.length || diagnostics.length ? "passed" : "unknown";
|
|
4930
|
+
const overallStatus = hasScopeFailure(scopes, "source-change", diagnostics) ? "failed" : hasScopeFailure(scopes, "workspace-config", diagnostics) ? "blocked-by-workspace-config" : hasScopeFailure(scopes, "environment", diagnostics) ? "blocked-by-environment" : workflowStatus(diagnostics) === "failed" || commandStatus(commands) === "failed" ? "failed" : "passed";
|
|
4931
|
+
return {
|
|
4932
|
+
sourceStatus,
|
|
4933
|
+
workspaceStatus,
|
|
4934
|
+
overallStatus,
|
|
4935
|
+
summary: {
|
|
4936
|
+
sourceChange: sourceStatus,
|
|
4937
|
+
generatedSync: statusForValidationKind(commands, "sync"),
|
|
4938
|
+
workspaceConfig: statusForScope(commands, diagnostics, scopes, "workspace-config"),
|
|
4939
|
+
environment: statusForScope(commands, diagnostics, scopes, "environment")
|
|
4940
|
+
}
|
|
4941
|
+
};
|
|
4942
|
+
};
|
|
5107
4943
|
var createWorkflowValidationRunReport = async ({
|
|
5108
4944
|
runId = createWorkflowRunId("validation"),
|
|
5109
4945
|
workflow,
|
|
@@ -5130,15 +4966,20 @@ var createWorkflowValidationRunReport = async ({
|
|
|
5130
4966
|
failureScope: result.failureScope
|
|
5131
4967
|
}
|
|
5132
4968
|
] : []);
|
|
4969
|
+
const reportDiagnostics = [...diagnostics, ...commandDiagnostics];
|
|
4970
|
+
const scopedDiagnostics = [...reportDiagnostics, ...baselineDiagnostics, ...workflowDiagnostics];
|
|
4971
|
+
const statuses = createValidationStatuses(results, scopedDiagnostics);
|
|
5133
4972
|
return {
|
|
5134
4973
|
schemaVersion: 1,
|
|
5135
4974
|
runId,
|
|
5136
4975
|
workflow,
|
|
5137
4976
|
mode: "validate",
|
|
5138
4977
|
source,
|
|
5139
|
-
status:
|
|
4978
|
+
status: statuses.overallStatus === "passed" ? "passed" : "failed",
|
|
4979
|
+
...statuses,
|
|
4980
|
+
knownBlockers: createKnownBlockers(results, scopedDiagnostics),
|
|
5140
4981
|
commands: results,
|
|
5141
|
-
diagnostics:
|
|
4982
|
+
diagnostics: reportDiagnostics,
|
|
5142
4983
|
baselineDiagnostics,
|
|
5143
4984
|
workflowDiagnostics,
|
|
5144
4985
|
repairActions: uniqueBy(repairActions, (action) => action.command),
|
|
@@ -5182,27 +5023,514 @@ var createDryRunWorkflowApplyReport = (plan) => {
|
|
|
5182
5023
|
plan
|
|
5183
5024
|
});
|
|
5184
5025
|
};
|
|
5185
|
-
var
|
|
5186
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5026
|
+
var createRepairReport = ({
|
|
5027
|
+
command,
|
|
5028
|
+
kind,
|
|
5029
|
+
target = null,
|
|
5030
|
+
diagnostics = [],
|
|
5031
|
+
repairActions = [],
|
|
5032
|
+
nextActions = [],
|
|
5033
|
+
commands = [],
|
|
5034
|
+
generatedFiles = [],
|
|
5035
|
+
syncedAt
|
|
5036
|
+
}) => ({
|
|
5037
|
+
schemaVersion: 1,
|
|
5038
|
+
command,
|
|
5039
|
+
kind,
|
|
5040
|
+
target,
|
|
5041
|
+
status: workflowStatus(diagnostics) === "failed" || commandStatus(commands) === "failed" ? "failed" : "passed",
|
|
5042
|
+
diagnostics,
|
|
5043
|
+
repairActions: uniqueBy(repairActions, (action) => action.command),
|
|
5044
|
+
nextActions: uniqueBy(nextActions, (action) => action.command),
|
|
5045
|
+
commands,
|
|
5046
|
+
generatedFiles,
|
|
5047
|
+
...syncedAt ? { syncedAt } : {}
|
|
5193
5048
|
});
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5049
|
+
// pkgs/@akanjs/devkit/workflow/executor.ts
|
|
5050
|
+
import { capitalize as capitalize2 } from "akanjs/common";
|
|
5051
|
+
|
|
5052
|
+
// pkgs/@akanjs/devkit/workflow/primitive.ts
|
|
5053
|
+
var createPrimitiveWriteReport = ({
|
|
5054
|
+
command,
|
|
5055
|
+
status,
|
|
5056
|
+
changedFiles = [],
|
|
5057
|
+
generatedFiles = [],
|
|
5058
|
+
validationCommands = [],
|
|
5059
|
+
diagnostics = [],
|
|
5060
|
+
nextActions = []
|
|
5061
|
+
}) => ({
|
|
5062
|
+
schemaVersion: 1,
|
|
5063
|
+
command,
|
|
5064
|
+
status: status ?? (diagnostics.some((diagnostic) => diagnostic.severity === "error") ? "failed" : "passed"),
|
|
5065
|
+
changedFiles,
|
|
5066
|
+
generatedFiles,
|
|
5067
|
+
validationCommands,
|
|
5068
|
+
diagnostics,
|
|
5069
|
+
nextActions
|
|
5070
|
+
});
|
|
5071
|
+
var renderPrimitiveWriteReport = (report) => [
|
|
5072
|
+
`# Primitive Write: ${report.command}`,
|
|
5073
|
+
"",
|
|
5074
|
+
`- Status: ${report.status}`,
|
|
5075
|
+
"",
|
|
5076
|
+
"## Changed Files",
|
|
5077
|
+
...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
|
|
5078
|
+
"",
|
|
5079
|
+
"## Generated Files",
|
|
5080
|
+
...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
|
|
5081
|
+
"",
|
|
5082
|
+
"## Validation Commands",
|
|
5083
|
+
...report.validationCommands.length ? report.validationCommands.map((validation) => `- \`${validation.command}\`: ${validation.reason}`) : ["- none"],
|
|
5084
|
+
"",
|
|
5085
|
+
"## Diagnostics",
|
|
5086
|
+
...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
5087
|
+
"",
|
|
5088
|
+
"## Next Actions",
|
|
5089
|
+
...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
5090
|
+
""
|
|
5091
|
+
].join(`
|
|
5092
|
+
`);
|
|
5093
|
+
var renderPrimitiveReport = (report, format = "markdown") => format === "json" ? jsonText(report) : renderPrimitiveWriteReport(report);
|
|
5094
|
+
|
|
5095
|
+
// pkgs/@akanjs/devkit/workflow/source.ts
|
|
5096
|
+
var getSysRoot = (sys2) => `${sys2.type}s/${sys2.name}`;
|
|
5097
|
+
var sourceFile = (sys2, path10, action, reason) => ({
|
|
5098
|
+
path: `${getSysRoot(sys2)}/${path10}`,
|
|
5099
|
+
action,
|
|
5100
|
+
reason
|
|
5101
|
+
});
|
|
5102
|
+
var moduleComponentName = (moduleName) => `${moduleName.slice(0, 1).toUpperCase()}${moduleName.slice(1)}`;
|
|
5103
|
+
var moduleSourcePaths = (moduleName) => {
|
|
5104
|
+
const componentName = moduleComponentName(moduleName);
|
|
5105
|
+
return {
|
|
5106
|
+
abstract: `lib/${moduleName}/${moduleName}.abstract.md`,
|
|
5107
|
+
constant: `lib/${moduleName}/${moduleName}.constant.ts`,
|
|
5108
|
+
dictionary: `lib/${moduleName}/${moduleName}.dictionary.ts`,
|
|
5109
|
+
service: `lib/${moduleName}/${moduleName}.service.ts`,
|
|
5110
|
+
signal: `lib/${moduleName}/${moduleName}.signal.ts`,
|
|
5111
|
+
store: `lib/${moduleName}/${moduleName}.store.ts`,
|
|
5112
|
+
template: `lib/${moduleName}/${componentName}.Template.tsx`,
|
|
5113
|
+
unit: `lib/${moduleName}/${componentName}.Unit.tsx`,
|
|
5114
|
+
util: `lib/${moduleName}/${componentName}.Util.tsx`,
|
|
5115
|
+
view: `lib/${moduleName}/${componentName}.View.tsx`,
|
|
5116
|
+
zone: `lib/${moduleName}/${componentName}.Zone.tsx`
|
|
5117
|
+
};
|
|
5118
|
+
};
|
|
5119
|
+
var generatedFilesForSync = (sys2, reason = "Generated files may change after sync.") => generatedFilePathsForTarget(getSysRoot(sys2), reason);
|
|
5120
|
+
var validationCommandsForTarget = (target) => [
|
|
5121
|
+
{ command: `akan sync ${target}`, reason: "Refresh generated Akan files from source conventions." },
|
|
5122
|
+
{ command: `akan lint ${target}`, reason: "Validate formatting, imports, and static lint rules." }
|
|
5123
|
+
];
|
|
5124
|
+
var nextActionsForTarget = (target) => [
|
|
5125
|
+
{ command: `akan sync ${target}`, reason: "Refresh generated Akan files after source changes." },
|
|
5126
|
+
{ command: `akan lint ${target}`, reason: "Validate the target after generated files are refreshed." }
|
|
5127
|
+
];
|
|
5128
|
+
var createPassedPrimitiveReport = ({
|
|
5129
|
+
command,
|
|
5130
|
+
changedFiles,
|
|
5131
|
+
generatedFiles,
|
|
5132
|
+
target,
|
|
5133
|
+
nextActions
|
|
5134
|
+
}) => createPrimitiveWriteReport({
|
|
5135
|
+
command,
|
|
5136
|
+
changedFiles,
|
|
5137
|
+
generatedFiles: generatedFiles ?? [],
|
|
5138
|
+
validationCommands: validationCommandsForTarget(target),
|
|
5139
|
+
diagnostics: [],
|
|
5140
|
+
nextActions: nextActions ?? nextActionsForTarget(target)
|
|
5141
|
+
});
|
|
5142
|
+
var scalarChangedFiles = (sys2, scalarName, files) => Object.values(files).map((file) => sourceFile(sys2, `lib/__scalar/${scalarName}/${file.filename}`, "create", "Scalar source file was created."));
|
|
5143
|
+
var titleize = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
5144
|
+
var lowerlize = (value) => `${value.slice(0, 1).toLowerCase()}${value.slice(1)}`;
|
|
5145
|
+
var koLabels = {
|
|
5146
|
+
amount: "\uAE08\uC561",
|
|
5147
|
+
budget: "\uC608\uC0B0",
|
|
5148
|
+
category: "\uCE74\uD14C\uACE0\uB9AC",
|
|
5149
|
+
content: "\uB0B4\uC6A9",
|
|
5150
|
+
count: "\uAC1C\uC218",
|
|
5151
|
+
createdAt: "\uC0DD\uC131\uC77C",
|
|
5152
|
+
date: "\uB0A0\uC9DC",
|
|
5153
|
+
description: "\uC124\uBA85",
|
|
5154
|
+
due: "\uB9C8\uAC10\uC77C",
|
|
5155
|
+
dueAt: "\uB9C8\uAC10\uC77C",
|
|
5156
|
+
email: "\uC774\uBA54\uC77C",
|
|
5157
|
+
enabled: "\uD65C\uC131\uD654",
|
|
5158
|
+
endAt: "\uC885\uB8CC\uC77C",
|
|
5159
|
+
id: "ID",
|
|
5160
|
+
name: "\uC774\uB984",
|
|
5161
|
+
owner: "\uB2F4\uB2F9\uC790",
|
|
5162
|
+
priority: "\uC6B0\uC120\uC21C\uC704",
|
|
5163
|
+
project: "\uD504\uB85C\uC81D\uD2B8",
|
|
5164
|
+
rating: "\uD3C9\uC810",
|
|
5165
|
+
startAt: "\uC2DC\uC791\uC77C",
|
|
5166
|
+
status: "\uC0C1\uD0DC",
|
|
5167
|
+
title: "\uC81C\uBAA9",
|
|
5168
|
+
updatedAt: "\uC218\uC815\uC77C"
|
|
5169
|
+
};
|
|
5170
|
+
var splitFieldWords = (fieldName) => fieldName.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").split(/\s+/).map((word) => word.trim()).filter(Boolean);
|
|
5171
|
+
var koLabelForField = (fieldName) => {
|
|
5172
|
+
if (koLabels[fieldName])
|
|
5173
|
+
return koLabels[fieldName];
|
|
5174
|
+
const words = splitFieldWords(fieldName);
|
|
5175
|
+
const translated = words.map((word) => koLabels[word] ?? koLabels[lowerlize(word)] ?? null);
|
|
5176
|
+
return translated.every(Boolean) ? translated.join(" ") : null;
|
|
5177
|
+
};
|
|
5178
|
+
var bilingualLabelForField = (fieldName) => {
|
|
5179
|
+
const en = titleize(fieldName);
|
|
5180
|
+
return { en, ko: koLabelForField(fieldName) ?? en };
|
|
5181
|
+
};
|
|
5182
|
+
var bilingualDescriptionForField = (fieldName) => {
|
|
5183
|
+
const label = bilingualLabelForField(fieldName);
|
|
5184
|
+
return {
|
|
5185
|
+
en: `Enter ${label.en.toLowerCase()}.`,
|
|
5186
|
+
ko: `${label.ko} \uAC12\uC744 \uC785\uB825\uD569\uB2C8\uB2E4.`
|
|
5187
|
+
};
|
|
5188
|
+
};
|
|
5189
|
+
var normalizeFieldType = (typeName) => {
|
|
5190
|
+
const normalizedTypes = {
|
|
5191
|
+
string: "String",
|
|
5192
|
+
boolean: "Boolean",
|
|
5193
|
+
date: "Date",
|
|
5194
|
+
int: "Int",
|
|
5195
|
+
integer: "Int",
|
|
5196
|
+
float: "Float",
|
|
5197
|
+
double: "Float",
|
|
5198
|
+
decimal: "Float"
|
|
5199
|
+
};
|
|
5200
|
+
return normalizedTypes[typeName.toLowerCase()] ?? typeName;
|
|
5201
|
+
};
|
|
5202
|
+
var ensureBaseTypeImport = (content, typeName) => {
|
|
5203
|
+
if (typeName !== "Int" && typeName !== "Float")
|
|
5204
|
+
return content;
|
|
5205
|
+
if (new RegExp(`import \\{[^}]*\\b${typeName}\\b[^}]*\\} from "akanjs/base";`).test(content))
|
|
5206
|
+
return content;
|
|
5207
|
+
const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
|
|
5208
|
+
if (baseImport) {
|
|
5209
|
+
const names = baseImport[1].split(",").map((name) => name.trim()).filter(Boolean);
|
|
5210
|
+
return content.replace(baseImport[0], `import { ${[...names, typeName].sort().join(", ")} } from "akanjs/base";`);
|
|
5211
|
+
}
|
|
5212
|
+
return `import { ${typeName} } from "akanjs/base";
|
|
5213
|
+
${content}`;
|
|
5214
|
+
};
|
|
5215
|
+
var numericDefault = (typeName, rawDefault) => {
|
|
5216
|
+
if (typeof rawDefault === "number") {
|
|
5217
|
+
if (!Number.isFinite(rawDefault))
|
|
5218
|
+
return null;
|
|
5219
|
+
if (typeName === "Int" && !Number.isInteger(rawDefault))
|
|
5220
|
+
return null;
|
|
5221
|
+
return String(rawDefault);
|
|
5222
|
+
}
|
|
5223
|
+
if (typeof rawDefault !== "string")
|
|
5224
|
+
return null;
|
|
5225
|
+
const trimmed = rawDefault.trim();
|
|
5226
|
+
if (!trimmed || !Number.isFinite(Number(trimmed)))
|
|
5227
|
+
return null;
|
|
5228
|
+
if (typeName === "Int" && !/^-?\d+$/.test(trimmed))
|
|
5229
|
+
return null;
|
|
5230
|
+
return trimmed;
|
|
5231
|
+
};
|
|
5232
|
+
var booleanDefault = (rawDefault) => {
|
|
5233
|
+
if (typeof rawDefault === "boolean")
|
|
5234
|
+
return String(rawDefault);
|
|
5235
|
+
if (typeof rawDefault !== "string")
|
|
5236
|
+
return null;
|
|
5237
|
+
const lowered = rawDefault.trim().toLowerCase();
|
|
5238
|
+
return lowered === "true" || lowered === "false" ? lowered : null;
|
|
5239
|
+
};
|
|
5240
|
+
var dateDefault = (rawDefault) => {
|
|
5241
|
+
if (typeof rawDefault === "number" && Number.isFinite(rawDefault))
|
|
5242
|
+
return `new Date(${rawDefault})`;
|
|
5243
|
+
if (typeof rawDefault !== "string")
|
|
5244
|
+
return null;
|
|
5245
|
+
const trimmed = rawDefault.trim();
|
|
5246
|
+
if (!trimmed)
|
|
5247
|
+
return null;
|
|
5248
|
+
if (trimmed === "now")
|
|
5249
|
+
return "new Date()";
|
|
5250
|
+
if (!Number.isNaN(Date.parse(trimmed)))
|
|
5251
|
+
return `new Date(${JSON.stringify(trimmed)})`;
|
|
5252
|
+
return null;
|
|
5253
|
+
};
|
|
5254
|
+
var coerceFieldDefault = (typeName, defaultValue, options = {}) => {
|
|
5255
|
+
const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
|
|
5256
|
+
if (defaultValue === undefined || defaultValue === null || defaultValue === "") {
|
|
5257
|
+
return { expression: null, normalized: false, normalizedType };
|
|
5258
|
+
}
|
|
5259
|
+
if (normalizedType === "Int" || normalizedType === "Float") {
|
|
5260
|
+
const expression = numericDefault(normalizedType, defaultValue);
|
|
5261
|
+
if (expression !== null)
|
|
5262
|
+
return { expression, normalized: typeof defaultValue === "string", normalizedType };
|
|
5263
|
+
return {
|
|
5264
|
+
expression: null,
|
|
5265
|
+
normalized: false,
|
|
5266
|
+
normalizedType,
|
|
5267
|
+
diagnostic: {
|
|
5268
|
+
severity: "error",
|
|
5269
|
+
code: "primitive-default-value-invalid",
|
|
5270
|
+
input: "default",
|
|
5271
|
+
failureScope: "source-change",
|
|
5272
|
+
message: `Default value for ${normalizedType} must be a numeric literal. Received: ${JSON.stringify(defaultValue)}.`
|
|
5273
|
+
}
|
|
5274
|
+
};
|
|
5275
|
+
}
|
|
5276
|
+
if (normalizedType === "Boolean") {
|
|
5277
|
+
const expression = booleanDefault(defaultValue);
|
|
5278
|
+
if (expression !== null)
|
|
5279
|
+
return { expression, normalized: typeof defaultValue === "string", normalizedType };
|
|
5280
|
+
return {
|
|
5281
|
+
expression: null,
|
|
5282
|
+
normalized: false,
|
|
5283
|
+
normalizedType,
|
|
5284
|
+
diagnostic: {
|
|
5285
|
+
severity: "error",
|
|
5286
|
+
code: "primitive-default-value-invalid",
|
|
5287
|
+
input: "default",
|
|
5288
|
+
failureScope: "source-change",
|
|
5289
|
+
message: `Default value for Boolean must be true or false. Received: ${JSON.stringify(defaultValue)}.`
|
|
5290
|
+
}
|
|
5291
|
+
};
|
|
5292
|
+
}
|
|
5293
|
+
if (normalizedType === "Date") {
|
|
5294
|
+
const expression = dateDefault(defaultValue);
|
|
5295
|
+
if (expression !== null)
|
|
5296
|
+
return { expression, normalized: true, normalizedType };
|
|
5297
|
+
return {
|
|
5298
|
+
expression: null,
|
|
5299
|
+
normalized: false,
|
|
5300
|
+
normalizedType,
|
|
5301
|
+
diagnostic: {
|
|
5302
|
+
severity: "error",
|
|
5303
|
+
code: "primitive-default-value-invalid",
|
|
5304
|
+
input: "default",
|
|
5305
|
+
failureScope: "source-change",
|
|
5306
|
+
message: `Default value for Date must be "now", a timestamp, or a parseable date string. Received: ${JSON.stringify(defaultValue)}.`
|
|
5307
|
+
}
|
|
5308
|
+
};
|
|
5309
|
+
}
|
|
5310
|
+
if (normalizedType === "enum") {
|
|
5311
|
+
if (typeof defaultValue === "string" && options.enumValues?.includes(defaultValue)) {
|
|
5312
|
+
return { expression: JSON.stringify(defaultValue), normalized: false, normalizedType };
|
|
5313
|
+
}
|
|
5314
|
+
return {
|
|
5315
|
+
expression: null,
|
|
5316
|
+
normalized: false,
|
|
5317
|
+
normalizedType,
|
|
5318
|
+
diagnostic: {
|
|
5319
|
+
severity: "error",
|
|
5320
|
+
code: "primitive-default-value-invalid",
|
|
5321
|
+
input: "default",
|
|
5322
|
+
failureScope: "source-change",
|
|
5323
|
+
message: `Default value for enum must be one of: ${(options.enumValues ?? []).join(", ")}. Received: ${JSON.stringify(defaultValue)}.`
|
|
5324
|
+
}
|
|
5325
|
+
};
|
|
5326
|
+
}
|
|
5327
|
+
return {
|
|
5328
|
+
expression: JSON.stringify(String(defaultValue)),
|
|
5329
|
+
normalized: typeof defaultValue !== "string",
|
|
5330
|
+
normalizedType
|
|
5331
|
+
};
|
|
5332
|
+
};
|
|
5333
|
+
var fieldExpression = (typeName, defaultValue, options = {}) => {
|
|
5334
|
+
const typeExpression = normalizeFieldType(typeName);
|
|
5335
|
+
const defaultExpression = coerceFieldDefault(options.enumValues ? "enum" : typeExpression, defaultValue, options).expression;
|
|
5336
|
+
const defaultOption = defaultExpression ? `, { default: ${defaultExpression} }` : "";
|
|
5337
|
+
return `field(${typeExpression}${defaultOption})`;
|
|
5338
|
+
};
|
|
5339
|
+
var insertIntoObject = (content, className, line) => {
|
|
5340
|
+
const classIndex = content.indexOf(`export class ${className} extends via`);
|
|
5341
|
+
if (classIndex < 0)
|
|
5342
|
+
return null;
|
|
5343
|
+
const objectEndIndex = content.indexOf("}))", classIndex);
|
|
5344
|
+
if (objectEndIndex < 0)
|
|
5345
|
+
return null;
|
|
5346
|
+
const prefix = content.slice(0, objectEndIndex);
|
|
5347
|
+
const suffix = content.slice(objectEndIndex);
|
|
5348
|
+
const insertion = prefix.endsWith(`
|
|
5349
|
+
`) ? ` ${line}
|
|
5350
|
+
` : `
|
|
5351
|
+
${line}
|
|
5352
|
+
`;
|
|
5353
|
+
return `${prefix}${insertion}${suffix}`;
|
|
5354
|
+
};
|
|
5355
|
+
var insertLightProjectionField = (content, moduleClassName, fieldName) => {
|
|
5356
|
+
const classIndex = content.indexOf(`export class Light${moduleClassName} extends via`);
|
|
5357
|
+
if (classIndex < 0)
|
|
5358
|
+
return null;
|
|
5359
|
+
const arrayMatch = /\[([\s\S]*?)\]\s+as const/.exec(content.slice(classIndex));
|
|
5360
|
+
if (!arrayMatch || arrayMatch.index === undefined)
|
|
5361
|
+
return null;
|
|
5362
|
+
const arrayStart = classIndex + arrayMatch.index;
|
|
5363
|
+
const arrayEnd = arrayStart + arrayMatch[0].length;
|
|
5364
|
+
const fields = [...arrayMatch[1].matchAll(/"([^"]+)"/g)].map((match) => match[1]).filter(Boolean);
|
|
5365
|
+
if (fields.includes(fieldName))
|
|
5366
|
+
return content;
|
|
5367
|
+
const nextFields = [...fields, fieldName];
|
|
5368
|
+
const nextArray = nextFields.length === 0 ? "[] as const" : `[
|
|
5369
|
+
${nextFields.map((field) => ` ${JSON.stringify(field)},`).join(`
|
|
5370
|
+
`)}
|
|
5371
|
+
] as const`;
|
|
5372
|
+
return `${content.slice(0, arrayStart)}${nextArray}${content.slice(arrayEnd)}`;
|
|
5373
|
+
};
|
|
5374
|
+
var insertTemplateField = ({
|
|
5375
|
+
content,
|
|
5376
|
+
moduleName,
|
|
5377
|
+
moduleClassName,
|
|
5378
|
+
fieldName,
|
|
5379
|
+
component
|
|
5380
|
+
}) => {
|
|
5381
|
+
if (content.includes(`l("${moduleName}.${fieldName}")`) || content.includes(`.${fieldName}`))
|
|
5382
|
+
return content;
|
|
5383
|
+
const layoutEndIndex = content.indexOf(" </Layout.Template>");
|
|
5384
|
+
if (layoutEndIndex < 0)
|
|
5385
|
+
return null;
|
|
5386
|
+
const formName = `${moduleName}Form`;
|
|
5387
|
+
if (!content.includes(`const ${formName} = st.use.${moduleName}Form();`))
|
|
5388
|
+
return null;
|
|
5389
|
+
const fieldSetter = `st.do.set${moduleComponentName(fieldName)}On${moduleClassName}`;
|
|
5390
|
+
const fieldBlock = ` <${component}
|
|
5391
|
+
label={l("${moduleName}.${fieldName}")}
|
|
5392
|
+
desc={l("${moduleName}.${fieldName}.desc")}
|
|
5393
|
+
value={${formName}.${fieldName}}
|
|
5394
|
+
onChange={${fieldSetter}}
|
|
5395
|
+
/>
|
|
5396
|
+
`;
|
|
5397
|
+
return `${content.slice(0, layoutEndIndex)}${fieldBlock}${content.slice(layoutEndIndex)}`;
|
|
5398
|
+
};
|
|
5399
|
+
var ensureEnumImport = (content) => {
|
|
5400
|
+
if (content.includes("enumOf"))
|
|
5401
|
+
return content;
|
|
5402
|
+
const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
|
|
5403
|
+
if (baseImport) {
|
|
5404
|
+
const names = baseImport[1]?.split(",").map((name) => name.trim()) ?? [];
|
|
5405
|
+
return content.replace(baseImport[0], `import { ${[...names, "enumOf"].sort().join(", ")} } from "akanjs/base";`);
|
|
5406
|
+
}
|
|
5407
|
+
return `import { enumOf } from "akanjs/base";
|
|
5408
|
+
${content}`;
|
|
5409
|
+
};
|
|
5410
|
+
var insertEnumClass = (content, enumClassName, enumName, values) => {
|
|
5411
|
+
if (content.includes(`export class ${enumClassName} extends enumOf`))
|
|
5412
|
+
return content;
|
|
5413
|
+
const enumClass = `export class ${enumClassName} extends enumOf("${enumName}", [
|
|
5414
|
+
${values.map((value) => ` ${JSON.stringify(value)},`).join(`
|
|
5415
|
+
`)}
|
|
5416
|
+
] as const) {}
|
|
5417
|
+
|
|
5418
|
+
`;
|
|
5419
|
+
const firstClassIndex = content.indexOf("export class ");
|
|
5420
|
+
if (firstClassIndex < 0)
|
|
5421
|
+
return `${content}
|
|
5422
|
+
${enumClass}`;
|
|
5423
|
+
return `${content.slice(0, firstClassIndex)}${enumClass}${content.slice(firstClassIndex)}`;
|
|
5424
|
+
};
|
|
5425
|
+
var insertDictionaryModelField = (content, moduleClassName, fieldName) => {
|
|
5426
|
+
if (new RegExp(`\\b${fieldName}\\s*:`).test(content))
|
|
5427
|
+
return content;
|
|
5428
|
+
const label = bilingualLabelForField(fieldName);
|
|
5429
|
+
const desc = bilingualDescriptionForField(fieldName);
|
|
5430
|
+
const modelIndex = content.indexOf(`.model<${moduleClassName}>((t) => ({`);
|
|
5431
|
+
if (modelIndex < 0)
|
|
5432
|
+
return null;
|
|
5433
|
+
const objectEndIndex = content.indexOf(" }))", modelIndex);
|
|
5434
|
+
if (objectEndIndex < 0)
|
|
5435
|
+
return null;
|
|
5436
|
+
return `${content.slice(0, objectEndIndex)} ${fieldName}: t([${JSON.stringify(label.en)}, ${JSON.stringify(label.ko)}]).desc([${JSON.stringify(desc.en)}, ${JSON.stringify(desc.ko)}]),
|
|
5437
|
+
${content.slice(objectEndIndex)}`;
|
|
5438
|
+
};
|
|
5439
|
+
var ensureConstantTypeImport = (content, constantPath, typeName) => {
|
|
5440
|
+
if (new RegExp(`import type \\{[^}]*\\b${typeName}\\b[^}]*\\} from "${constantPath}";`).test(content))
|
|
5441
|
+
return content;
|
|
5442
|
+
const importPattern = new RegExp(`import type \\{ ([^}]+) \\} from "${constantPath}";`);
|
|
5443
|
+
const existingImport = content.match(importPattern);
|
|
5444
|
+
if (existingImport !== null) {
|
|
5445
|
+
const names = existingImport[1]?.split(",").map((name) => name.trim()) ?? [];
|
|
5446
|
+
return content.replace(existingImport[0], `import type { ${[...names, typeName].sort().join(", ")} } from "${constantPath}";`);
|
|
5447
|
+
}
|
|
5448
|
+
return `import type { ${typeName} } from "${constantPath}";
|
|
5449
|
+
${content}`;
|
|
5450
|
+
};
|
|
5451
|
+
var insertDictionaryEnum = (content, enumClassName, enumName, values) => {
|
|
5452
|
+
if (content.includes(`.enum<${enumClassName}>("${enumName}"`))
|
|
5453
|
+
return content;
|
|
5454
|
+
const enumBlock = ` .enum<${enumClassName}>("${enumName}", (t) => ({
|
|
5455
|
+
${values.map((value) => ` ${value}: t([${JSON.stringify(titleize(value))}, ${JSON.stringify(titleize(value))}]),`).join(`
|
|
5456
|
+
`)}
|
|
5457
|
+
}))
|
|
5458
|
+
`;
|
|
5459
|
+
const chainEndIndex = content.lastIndexOf(";");
|
|
5460
|
+
const insertBeforeIndex = [content.indexOf(".error("), content.indexOf(".translate("), chainEndIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
|
|
5461
|
+
if (insertBeforeIndex === undefined)
|
|
5462
|
+
return null;
|
|
5463
|
+
return `${content.slice(0, insertBeforeIndex)}${enumBlock}${content.slice(insertBeforeIndex)}`;
|
|
5464
|
+
};
|
|
5465
|
+
var parseValues = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
|
|
5466
|
+
|
|
5467
|
+
// pkgs/@akanjs/devkit/workflow/uiPolicy.ts
|
|
5468
|
+
var addFieldUiPolicyForType = (typeName) => {
|
|
5469
|
+
const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
|
|
5470
|
+
if (normalizedType === "Int" || normalizedType === "Float") {
|
|
5471
|
+
return {
|
|
5472
|
+
normalizedType,
|
|
5473
|
+
component: "Field.Number",
|
|
5474
|
+
confidence: "high",
|
|
5475
|
+
autoTemplateSupported: true
|
|
5476
|
+
};
|
|
5477
|
+
}
|
|
5478
|
+
if (normalizedType === "Date") {
|
|
5479
|
+
return {
|
|
5480
|
+
normalizedType,
|
|
5481
|
+
component: "Field.Date",
|
|
5482
|
+
confidence: "high",
|
|
5483
|
+
autoTemplateSupported: true
|
|
5484
|
+
};
|
|
5485
|
+
}
|
|
5486
|
+
if (normalizedType === "Boolean" || normalizedType === "enum") {
|
|
5487
|
+
return {
|
|
5488
|
+
normalizedType,
|
|
5489
|
+
component: "Field.ToggleSelect",
|
|
5490
|
+
confidence: "medium",
|
|
5491
|
+
autoTemplateSupported: false
|
|
5492
|
+
};
|
|
5493
|
+
}
|
|
5494
|
+
if (normalizedType === "String") {
|
|
5495
|
+
return {
|
|
5496
|
+
normalizedType,
|
|
5497
|
+
component: "Field.Text",
|
|
5498
|
+
confidence: "high",
|
|
5499
|
+
autoTemplateSupported: true
|
|
5500
|
+
};
|
|
5501
|
+
}
|
|
5502
|
+
return {
|
|
5503
|
+
normalizedType,
|
|
5504
|
+
component: "Field.Text",
|
|
5505
|
+
confidence: "low",
|
|
5506
|
+
autoTemplateSupported: false
|
|
5507
|
+
};
|
|
5508
|
+
};
|
|
5509
|
+
|
|
5510
|
+
// pkgs/@akanjs/devkit/workflow/executor.ts
|
|
5511
|
+
var workflowStepKey = (workflow, stepId) => `${workflow}:${stepId}`;
|
|
5512
|
+
var primitiveReportToWorkflowStepResult = (report) => ({
|
|
5513
|
+
changedFiles: report.changedFiles,
|
|
5514
|
+
generatedFiles: report.generatedFiles,
|
|
5515
|
+
commands: report.validationCommands,
|
|
5516
|
+
diagnostics: report.diagnostics,
|
|
5517
|
+
recommendations: [],
|
|
5518
|
+
nextActions: report.nextActions
|
|
5519
|
+
});
|
|
5520
|
+
var workflowStringInput = (value) => typeof value === "string" ? value : null;
|
|
5521
|
+
var workflowStringListInput = (value) => Array.isArray(value) ? value.join(",") : null;
|
|
5522
|
+
var workflowStringArrayInput = (value) => Array.isArray(value) ? value : null;
|
|
5523
|
+
var workflowBooleanInput = (value) => typeof value === "boolean" ? value : null;
|
|
5524
|
+
var resolveWorkflowSys = async (workspace, target) => {
|
|
5525
|
+
if (!target)
|
|
5526
|
+
return null;
|
|
5527
|
+
const [apps, libs] = await workspace.getSyss();
|
|
5528
|
+
if (apps.includes(target))
|
|
5529
|
+
return AppExecutor.from(workspace, target);
|
|
5530
|
+
if (libs.includes(target))
|
|
5531
|
+
return LibExecutor.from(workspace, target);
|
|
5532
|
+
return null;
|
|
5533
|
+
};
|
|
5206
5534
|
var targetMissing = (input2 = "app") => ({
|
|
5207
5535
|
severity: "error",
|
|
5208
5536
|
code: "workflow-target-missing",
|
|
@@ -5222,19 +5550,24 @@ var unsupportedInput = (input2, message) => ({
|
|
|
5222
5550
|
message
|
|
5223
5551
|
});
|
|
5224
5552
|
var addFieldUiSurfaceInspection = (plan) => {
|
|
5553
|
+
const app = workflowStringInput(plan.inputs.app);
|
|
5225
5554
|
const module = workflowStringInput(plan.inputs.module) ?? "<module>";
|
|
5226
5555
|
const field = workflowStringInput(plan.inputs.field) ?? "<field>";
|
|
5556
|
+
const typeName = workflowStringInput(plan.inputs.type);
|
|
5557
|
+
const policy = addFieldUiPolicyForType(typeName ?? "String");
|
|
5558
|
+
const surfaces = workflowStringArrayInput(plan.inputs.surfaces);
|
|
5559
|
+
const templateRequested = surfaces?.includes("template") ?? false;
|
|
5227
5560
|
const moduleClassName = capitalize2(module);
|
|
5228
|
-
const target =
|
|
5561
|
+
const target = `${app ? `apps/${app}` : "*"}/${moduleSourcePaths(module).template}`;
|
|
5229
5562
|
return {
|
|
5230
5563
|
recommendations: [
|
|
5231
5564
|
{
|
|
5232
5565
|
code: "add-field-ui-surface-review",
|
|
5233
5566
|
kind: "manual-action",
|
|
5234
5567
|
target,
|
|
5235
|
-
action: `
|
|
5568
|
+
action: templateRequested ? `Template was requested for ${field}. If no Template file changed, auto-edit was skipped because the file was missing, the generated ${module}Form/Layout.Template pattern was not found, or ${policy.component} needs option binding. Candidate position: inside Layout.Template near the existing Field components.` : `Template was not selected, so UI files are intentionally left unchanged. Candidate positions if you expose it later: Layout.Template field list for editing, Light${moduleClassName} projection for list/card data, and Unit/View card sections for display.`,
|
|
5236
5569
|
confidence: "medium",
|
|
5237
|
-
message: `Review
|
|
5570
|
+
message: `Review UI surfaces for ${module}.${field}; recommended component is ${policy.component}, with manual reasons and candidate positions in action.`
|
|
5238
5571
|
}
|
|
5239
5572
|
],
|
|
5240
5573
|
nextActions: [
|
|
@@ -5272,95 +5605,579 @@ var createWorkflowStepRegistry = ({
|
|
|
5272
5605
|
return { diagnostics: [!sys2 ? targetMissing() : inputMissing("module")] };
|
|
5273
5606
|
return primitiveReportToWorkflowStepResult(await createModule(sys2, module));
|
|
5274
5607
|
},
|
|
5275
|
-
[workflowStepKey("create-scalar", "create-scalar")]: async (_step, plan) => {
|
|
5276
|
-
const app = workflowStringInput(plan.inputs.app);
|
|
5277
|
-
const scalar = workflowStringInput(plan.inputs.scalar);
|
|
5278
|
-
const sys2 = await resolveWorkflowSys(workspace, app);
|
|
5279
|
-
if (!sys2 || !scalar)
|
|
5280
|
-
return { diagnostics: [!sys2 ? targetMissing() : inputMissing("scalar")] };
|
|
5281
|
-
return primitiveReportToWorkflowStepResult(await createScalar(sys2, scalar));
|
|
5608
|
+
[workflowStepKey("create-scalar", "create-scalar")]: async (_step, plan) => {
|
|
5609
|
+
const app = workflowStringInput(plan.inputs.app);
|
|
5610
|
+
const scalar = workflowStringInput(plan.inputs.scalar);
|
|
5611
|
+
const sys2 = await resolveWorkflowSys(workspace, app);
|
|
5612
|
+
if (!sys2 || !scalar)
|
|
5613
|
+
return { diagnostics: [!sys2 ? targetMissing() : inputMissing("scalar")] };
|
|
5614
|
+
return primitiveReportToWorkflowStepResult(await createScalar(sys2, scalar));
|
|
5615
|
+
},
|
|
5616
|
+
[workflowStepKey("create-ui", "create-ui")]: async (_step, plan) => {
|
|
5617
|
+
const surface = workflowStringInput(plan.inputs.surface);
|
|
5618
|
+
if (surface !== "view" && surface !== "unit" && surface !== "template") {
|
|
5619
|
+
return {
|
|
5620
|
+
diagnostics: [
|
|
5621
|
+
unsupportedInput("surface", "Workflow apply currently supports create-ui surfaces: view, unit, template.")
|
|
5622
|
+
]
|
|
5623
|
+
};
|
|
5624
|
+
}
|
|
5625
|
+
return primitiveReportToWorkflowStepResult(await createUi({
|
|
5626
|
+
app: workflowStringInput(plan.inputs.app),
|
|
5627
|
+
module: workflowStringInput(plan.inputs.module),
|
|
5628
|
+
surface
|
|
5629
|
+
}));
|
|
5630
|
+
},
|
|
5631
|
+
[workflowStepKey("add-field", "update-constant")]: async (_step, plan) => {
|
|
5632
|
+
if (workflowStringInput(plan.inputs.type)?.toLowerCase() === "enum") {
|
|
5633
|
+
return primitiveReportToWorkflowStepResult(await addEnumField({
|
|
5634
|
+
app: workflowStringInput(plan.inputs.app),
|
|
5635
|
+
module: workflowStringInput(plan.inputs.module),
|
|
5636
|
+
field: workflowStringInput(plan.inputs.field),
|
|
5637
|
+
values: workflowStringListInput(plan.inputs.values),
|
|
5638
|
+
defaultValue: workflowStringInput(plan.inputs.default),
|
|
5639
|
+
surfaces: workflowStringArrayInput(plan.inputs.surfaces),
|
|
5640
|
+
includeInLight: workflowBooleanInput(plan.inputs.includeInLight)
|
|
5641
|
+
}));
|
|
5642
|
+
}
|
|
5643
|
+
return primitiveReportToWorkflowStepResult(await addField({
|
|
5644
|
+
app: workflowStringInput(plan.inputs.app),
|
|
5645
|
+
module: workflowStringInput(plan.inputs.module),
|
|
5646
|
+
field: workflowStringInput(plan.inputs.field),
|
|
5647
|
+
type: workflowStringInput(plan.inputs.type),
|
|
5648
|
+
defaultValue: workflowStringInput(plan.inputs.default),
|
|
5649
|
+
surfaces: workflowStringArrayInput(plan.inputs.surfaces),
|
|
5650
|
+
includeInLight: workflowBooleanInput(plan.inputs.includeInLight)
|
|
5651
|
+
}));
|
|
5652
|
+
},
|
|
5653
|
+
[workflowStepKey("add-field", "update-dictionary")]: inspect,
|
|
5654
|
+
[workflowStepKey("add-field", "update-ui-surfaces")]: async (_step, plan) => addFieldUiSurfaceInspection(plan),
|
|
5655
|
+
[workflowStepKey("add-enum-field", "update-constant")]: async (_step, plan) => primitiveReportToWorkflowStepResult(await addEnumField({
|
|
5656
|
+
app: workflowStringInput(plan.inputs.app),
|
|
5657
|
+
module: workflowStringInput(plan.inputs.module),
|
|
5658
|
+
field: workflowStringInput(plan.inputs.field),
|
|
5659
|
+
values: workflowStringListInput(plan.inputs.values),
|
|
5660
|
+
defaultValue: workflowStringInput(plan.inputs.default)
|
|
5661
|
+
})),
|
|
5662
|
+
[workflowStepKey("add-enum-field", "update-dictionary")]: inspect,
|
|
5663
|
+
[workflowStepKey("add-enum-field", "update-option")]: inspect
|
|
5664
|
+
};
|
|
5665
|
+
};
|
|
5666
|
+
class WorkflowExecutor {
|
|
5667
|
+
registry;
|
|
5668
|
+
constructor(registry) {
|
|
5669
|
+
this.registry = registry;
|
|
5670
|
+
}
|
|
5671
|
+
async apply(plan) {
|
|
5672
|
+
const changedFiles = [];
|
|
5673
|
+
const generatedFiles = [];
|
|
5674
|
+
const recommendedValidationCommands = [];
|
|
5675
|
+
const diagnostics = [...plan.diagnostics];
|
|
5676
|
+
const recommendations = [...plan.recommendations];
|
|
5677
|
+
const nextActions = [];
|
|
5678
|
+
if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
5679
|
+
return createWorkflowApplyReport({
|
|
5680
|
+
workflow: plan.workflow,
|
|
5681
|
+
mode: "apply",
|
|
5682
|
+
changedFiles,
|
|
5683
|
+
generatedFiles,
|
|
5684
|
+
recommendedValidationCommands,
|
|
5685
|
+
diagnostics,
|
|
5686
|
+
recommendations,
|
|
5687
|
+
nextActions,
|
|
5688
|
+
plan
|
|
5689
|
+
});
|
|
5690
|
+
}
|
|
5691
|
+
recommendedValidationCommands.push(...workflowCommandsForPlan(plan));
|
|
5692
|
+
nextActions.push(...workflowCommandsForPlan(plan));
|
|
5693
|
+
for (const step of plan.steps) {
|
|
5694
|
+
const runner = this.registry[workflowStepKey(plan.workflow, step.id)] ?? this.registry[step.tool] ?? this.registry[step.id];
|
|
5695
|
+
if (!runner) {
|
|
5696
|
+
diagnostics.push({
|
|
5697
|
+
severity: "error",
|
|
5698
|
+
code: "workflow-step-unsupported",
|
|
5699
|
+
message: `Workflow ${plan.workflow} step "${step.id}" is not supported by workflow apply yet.`
|
|
5700
|
+
});
|
|
5701
|
+
nextActions.push({
|
|
5702
|
+
command: `akan workflow explain ${plan.workflow}`,
|
|
5703
|
+
reason: "Review the unsupported workflow step before retrying apply."
|
|
5704
|
+
});
|
|
5705
|
+
break;
|
|
5706
|
+
}
|
|
5707
|
+
const result = await runner(step, plan);
|
|
5708
|
+
if (!result)
|
|
5709
|
+
continue;
|
|
5710
|
+
changedFiles.push(...result.changedFiles ?? []);
|
|
5711
|
+
generatedFiles.push(...result.generatedFiles ?? []);
|
|
5712
|
+
recommendedValidationCommands.push(...result.commands ?? []);
|
|
5713
|
+
diagnostics.push(...result.diagnostics ?? []);
|
|
5714
|
+
recommendations.push(...result.recommendations ?? []);
|
|
5715
|
+
nextActions.push(...result.nextActions ?? []);
|
|
5716
|
+
if (diagnostics.some((diagnostic) => diagnostic.severity === "error"))
|
|
5717
|
+
break;
|
|
5718
|
+
}
|
|
5719
|
+
return createWorkflowApplyReport({
|
|
5720
|
+
workflow: plan.workflow,
|
|
5721
|
+
mode: "apply",
|
|
5722
|
+
changedFiles,
|
|
5723
|
+
generatedFiles,
|
|
5724
|
+
recommendedValidationCommands,
|
|
5725
|
+
diagnostics,
|
|
5726
|
+
recommendations,
|
|
5727
|
+
nextActions,
|
|
5728
|
+
plan
|
|
5729
|
+
});
|
|
5730
|
+
}
|
|
5731
|
+
}
|
|
5732
|
+
// pkgs/@akanjs/devkit/workflow/plan.ts
|
|
5733
|
+
import { capitalize as capitalize3 } from "akanjs/common";
|
|
5734
|
+
var surfaceModes = new Set(["infer", "include", "skip"]);
|
|
5735
|
+
var parseStringList = (value) => {
|
|
5736
|
+
if (Array.isArray(value)) {
|
|
5737
|
+
const values = value.filter((item) => typeof item === "string" && item.trim().length > 0);
|
|
5738
|
+
return values.length === value.length ? values : null;
|
|
5739
|
+
}
|
|
5740
|
+
if (typeof value !== "string")
|
|
5741
|
+
return null;
|
|
5742
|
+
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
5743
|
+
};
|
|
5744
|
+
var normalizeInputValue = (name, spec, value) => {
|
|
5745
|
+
if (spec.type === "string")
|
|
5746
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
5747
|
+
if (spec.type === "string-list") {
|
|
5748
|
+
const values = parseStringList(value);
|
|
5749
|
+
return values && values.length > 0 ? values : null;
|
|
5750
|
+
}
|
|
5751
|
+
if (spec.type === "boolean") {
|
|
5752
|
+
if (typeof value === "boolean")
|
|
5753
|
+
return value;
|
|
5754
|
+
if (typeof value !== "string")
|
|
5755
|
+
return null;
|
|
5756
|
+
const lowered = value.trim().toLowerCase();
|
|
5757
|
+
if (lowered === "true")
|
|
5758
|
+
return true;
|
|
5759
|
+
if (lowered === "false")
|
|
5760
|
+
return false;
|
|
5761
|
+
return null;
|
|
5762
|
+
}
|
|
5763
|
+
if (typeof value === "string" && surfaceModes.has(value))
|
|
5764
|
+
return value;
|
|
5765
|
+
throw new Error(`Unsupported workflow input value for ${name}`);
|
|
5766
|
+
};
|
|
5767
|
+
var listWorkflowSpecs = (specs) => [...specs].sort((a, b) => a.name.localeCompare(b.name));
|
|
5768
|
+
var getWorkflowSpec = (specs, name) => specs.find((spec) => spec.name === name) ?? null;
|
|
5769
|
+
var compactWorkflowInputs = (inputs) => Object.fromEntries(Object.entries(inputs).filter(([, value]) => value !== null && value !== ""));
|
|
5770
|
+
var addFieldTargetRoot = (app) => app ? `apps/${app}` : "*";
|
|
5771
|
+
var addFieldPaths = (inputs) => {
|
|
5772
|
+
const app = typeof inputs.app === "string" ? inputs.app : null;
|
|
5773
|
+
const module = typeof inputs.module === "string" ? inputs.module : "<module>";
|
|
5774
|
+
const paths = moduleSourcePaths(module);
|
|
5775
|
+
const root = addFieldTargetRoot(app);
|
|
5776
|
+
return {
|
|
5777
|
+
constant: `${root}/${paths.constant}`,
|
|
5778
|
+
dictionary: `${root}/${paths.dictionary}`,
|
|
5779
|
+
template: `${root}/${paths.template}`,
|
|
5780
|
+
unit: `${root}/${paths.unit}`,
|
|
5781
|
+
view: `${root}/${paths.view}`
|
|
5782
|
+
};
|
|
5783
|
+
};
|
|
5784
|
+
var selectedAddFieldSurfaces = (inputs) => Array.isArray(inputs.surfaces) ? new Set(inputs.surfaces) : null;
|
|
5785
|
+
var addFieldDefaultCoercion = (inputs) => {
|
|
5786
|
+
const typeName = typeof inputs.type === "string" ? inputs.type : null;
|
|
5787
|
+
const defaultValue = typeof inputs.default === "string" ? inputs.default : null;
|
|
5788
|
+
if (!typeName || defaultValue === null)
|
|
5789
|
+
return null;
|
|
5790
|
+
if (typeName.toLowerCase() === "number" || typeName.toLowerCase() === "numeric")
|
|
5791
|
+
return null;
|
|
5792
|
+
const enumValues = Array.isArray(inputs.values) ? inputs.values : null;
|
|
5793
|
+
return coerceFieldDefault(typeName.toLowerCase() === "enum" ? "enum" : typeName, defaultValue, { enumValues });
|
|
5794
|
+
};
|
|
5795
|
+
var createAddFieldDefaultRecommendations = (inputs) => {
|
|
5796
|
+
const field = typeof inputs.field === "string" ? inputs.field : "<field>";
|
|
5797
|
+
const coercion = addFieldDefaultCoercion(inputs);
|
|
5798
|
+
if (!coercion?.normalized || !coercion.expression)
|
|
5799
|
+
return [];
|
|
5800
|
+
return [
|
|
5801
|
+
{
|
|
5802
|
+
code: "add-field-default-normalized",
|
|
5803
|
+
kind: "manual-action",
|
|
5804
|
+
confidence: "high",
|
|
5805
|
+
message: `Default for ${field} will be normalized to ${coercion.normalizedType} literal ${coercion.expression}.`,
|
|
5806
|
+
action: "Review the normalized default in the apply report if this value should stay unset."
|
|
5807
|
+
}
|
|
5808
|
+
];
|
|
5809
|
+
};
|
|
5810
|
+
var createAddFieldRecommendations = (inputs) => {
|
|
5811
|
+
const app = typeof inputs.app === "string" ? inputs.app : "<app-or-lib>";
|
|
5812
|
+
const module = typeof inputs.module === "string" ? inputs.module : "<module>";
|
|
5813
|
+
const field = typeof inputs.field === "string" ? inputs.field : "<field>";
|
|
5814
|
+
const typeName = typeof inputs.type === "string" ? inputs.type : null;
|
|
5815
|
+
if (!typeName)
|
|
5816
|
+
return [];
|
|
5817
|
+
const policy = addFieldUiPolicyForType(typeName);
|
|
5818
|
+
const normalizedType = policy.normalizedType;
|
|
5819
|
+
const paths = addFieldPaths(inputs);
|
|
5820
|
+
const surfaces = selectedAddFieldSurfaces(inputs);
|
|
5821
|
+
const templateRequested = surfaces?.has("template") ?? false;
|
|
5822
|
+
const includeInLight = inputs.includeInLight === true;
|
|
5823
|
+
return [
|
|
5824
|
+
...normalizedType === "Int" || normalizedType === "Float" ? [
|
|
5825
|
+
{
|
|
5826
|
+
code: "add-field-import",
|
|
5827
|
+
kind: "import",
|
|
5828
|
+
target: paths.constant,
|
|
5829
|
+
confidence: "high",
|
|
5830
|
+
message: `Import ${normalizedType} from "akanjs/base" before writing field(${normalizedType}).`
|
|
5831
|
+
}
|
|
5832
|
+
] : [],
|
|
5833
|
+
{
|
|
5834
|
+
code: "add-field-placement-constant",
|
|
5835
|
+
kind: "placement",
|
|
5836
|
+
target: paths.constant,
|
|
5837
|
+
confidence: "high",
|
|
5838
|
+
message: `Insert ${field}: field(${normalizedType}) in ${capitalize3(module)}Input.`
|
|
5839
|
+
},
|
|
5840
|
+
{
|
|
5841
|
+
code: "add-field-placement-dictionary",
|
|
5842
|
+
kind: "placement",
|
|
5843
|
+
target: paths.dictionary,
|
|
5844
|
+
confidence: "high",
|
|
5845
|
+
message: `Add dictionary labels for ${module}.${field}.`
|
|
5846
|
+
},
|
|
5847
|
+
{
|
|
5848
|
+
code: "add-field-component",
|
|
5849
|
+
kind: "ui-component",
|
|
5850
|
+
target: paths.template,
|
|
5851
|
+
confidence: policy.confidence,
|
|
5852
|
+
action: templateRequested ? `apply_workflow will try to add ${policy.component} to Template when the existing Template has a safe generated field-list pattern.` : `Recommended component is ${policy.component}. Pass surfaces=["template"] when this field should be rendered in the Template form.`,
|
|
5853
|
+
message: `Recommended UI component for ${field} (${normalizedType}): ${policy.component}.`
|
|
5854
|
+
},
|
|
5855
|
+
...!surfaces ? [
|
|
5856
|
+
{
|
|
5857
|
+
code: "add-field-template-surface-choice",
|
|
5858
|
+
kind: "manual-action",
|
|
5859
|
+
target: paths.template,
|
|
5860
|
+
action: `Choose whether to expose ${field} in Template by passing surfaces=["template"].`,
|
|
5861
|
+
confidence: "medium",
|
|
5862
|
+
message: `Template exposure for ${module}.${field} is not selected yet.`
|
|
5863
|
+
}
|
|
5864
|
+
] : [],
|
|
5865
|
+
...!includeInLight ? [
|
|
5866
|
+
{
|
|
5867
|
+
code: "add-field-light-projection-choice",
|
|
5868
|
+
kind: "manual-action",
|
|
5869
|
+
target: paths.constant,
|
|
5870
|
+
action: `Pass includeInLight=true when ${field} should appear in Light${capitalize3(module)} list/card projections.`,
|
|
5871
|
+
confidence: "medium",
|
|
5872
|
+
message: `Light projection exposure for ${module}.${field} is not selected yet.`
|
|
5873
|
+
}
|
|
5874
|
+
] : [],
|
|
5875
|
+
...includeInLight ? [
|
|
5876
|
+
{
|
|
5877
|
+
code: "add-field-light-projection",
|
|
5878
|
+
kind: "placement",
|
|
5879
|
+
target: paths.constant,
|
|
5880
|
+
confidence: "high",
|
|
5881
|
+
message: `Add ${field} to Light${capitalize3(module)} projection fields.`
|
|
5882
|
+
}
|
|
5883
|
+
] : [],
|
|
5884
|
+
...surfaces && !templateRequested ? [
|
|
5885
|
+
{
|
|
5886
|
+
code: "add-field-ui-surface-skip",
|
|
5887
|
+
kind: "manual-action",
|
|
5888
|
+
target: paths.template,
|
|
5889
|
+
confidence: "medium",
|
|
5890
|
+
message: `Template is not included in surfaces, so ${module}.${field} will not be auto-rendered there.`
|
|
5891
|
+
}
|
|
5892
|
+
] : [],
|
|
5893
|
+
{
|
|
5894
|
+
code: "add-field-ui-manual-review",
|
|
5895
|
+
kind: "manual-action",
|
|
5896
|
+
target: paths.template,
|
|
5897
|
+
action: `Review ${app}:${module} UI after apply. Template auto-edit requires a generated ${module}Form hook and Layout.Template field list; Unit/View are not auto-edited because list/card placement depends on local layout. Candidate positions: Layout.Template before the closing tag, Light${capitalize3(module)} projection array for list data, and Unit/View card sections for display.`,
|
|
5898
|
+
confidence: "medium",
|
|
5899
|
+
message: "UI manual review includes the reason auto-edit may be skipped and candidate insertion positions."
|
|
5900
|
+
},
|
|
5901
|
+
...createAddFieldDefaultRecommendations(inputs)
|
|
5902
|
+
];
|
|
5903
|
+
};
|
|
5904
|
+
var createWorkflowPlanPredictedChanges = (spec, inputs) => {
|
|
5905
|
+
if (spec.name !== "add-field")
|
|
5906
|
+
return spec.predictedChanges;
|
|
5907
|
+
const paths = addFieldPaths(inputs);
|
|
5908
|
+
const surfaces = selectedAddFieldSurfaces(inputs);
|
|
5909
|
+
const includeInLight = inputs.includeInLight === true;
|
|
5910
|
+
return [
|
|
5911
|
+
{
|
|
5912
|
+
target: paths.constant,
|
|
5913
|
+
action: "modify",
|
|
5914
|
+
applyScope: "auto",
|
|
5915
|
+
reason: includeInLight ? "Field shape and Light projection are added." : "Field shape is added."
|
|
5916
|
+
},
|
|
5917
|
+
{
|
|
5918
|
+
target: paths.dictionary,
|
|
5919
|
+
action: "modify",
|
|
5920
|
+
applyScope: "auto",
|
|
5921
|
+
reason: "Field label is added."
|
|
5282
5922
|
},
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
]
|
|
5290
|
-
};
|
|
5923
|
+
...!surfaces || surfaces.has("template") ? [
|
|
5924
|
+
{
|
|
5925
|
+
target: paths.template,
|
|
5926
|
+
action: "modify",
|
|
5927
|
+
applyScope: surfaces?.has("template") ? "auto" : "manual-review",
|
|
5928
|
+
reason: surfaces?.has("template") ? "Template form is selected for safe-pattern auto insertion." : "Form surface may include the field."
|
|
5291
5929
|
}
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5930
|
+
] : [],
|
|
5931
|
+
{
|
|
5932
|
+
target: `${addFieldTargetRoot(typeof inputs.app === "string" ? inputs.app : null)}/lib/cnst.ts`,
|
|
5933
|
+
action: "sync",
|
|
5934
|
+
applyScope: "generated-sync",
|
|
5935
|
+
reason: "Generated constants may change after sync."
|
|
5297
5936
|
},
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5937
|
+
{
|
|
5938
|
+
target: `${addFieldTargetRoot(typeof inputs.app === "string" ? inputs.app : null)}/lib/dict.ts`,
|
|
5939
|
+
action: "sync",
|
|
5940
|
+
applyScope: "generated-sync",
|
|
5941
|
+
reason: "Generated dictionary barrel may change after sync."
|
|
5942
|
+
}
|
|
5943
|
+
];
|
|
5944
|
+
};
|
|
5945
|
+
var createWorkflowPlanOptionalSurfaces = (spec, inputs) => {
|
|
5946
|
+
const optionalSurfaces = spec.optionalSurfaces ?? {};
|
|
5947
|
+
const surfaces = selectedAddFieldSurfaces(inputs);
|
|
5948
|
+
if (spec.name !== "add-field" || !surfaces)
|
|
5949
|
+
return optionalSurfaces;
|
|
5950
|
+
return Object.fromEntries(Object.entries(optionalSurfaces).map(([surface, mode]) => [surface, surfaces.has(surface) ? "include" : mode]));
|
|
5951
|
+
};
|
|
5952
|
+
var createWorkflowPlanRecommendations = (spec, inputs) => [
|
|
5953
|
+
{
|
|
5954
|
+
code: "workflow-apply-first",
|
|
5955
|
+
kind: "auto-apply",
|
|
5956
|
+
confidence: "high",
|
|
5957
|
+
action: "Call apply_workflow with the MCP planPath before editing source files directly.",
|
|
5958
|
+
message: `Apply the ${spec.name} plan through apply_workflow when MCP returns planPath or next.tool=apply_workflow.`
|
|
5959
|
+
},
|
|
5960
|
+
{
|
|
5961
|
+
code: "workflow-validate-apply-report",
|
|
5962
|
+
kind: "validation",
|
|
5963
|
+
confidence: "high",
|
|
5964
|
+
action: "After apply_workflow, call run_validation with validationTarget when present; otherwise use applyReportPath.",
|
|
5965
|
+
message: "Validate the apply report artifact so diagnostics and recommendations follow the actual apply result."
|
|
5966
|
+
},
|
|
5967
|
+
...spec.name === "add-field" ? createAddFieldRecommendations(inputs) : []
|
|
5968
|
+
];
|
|
5969
|
+
var createWorkflowPlan = (spec, rawInputs) => {
|
|
5970
|
+
const inputs = {};
|
|
5971
|
+
const diagnostics = [];
|
|
5972
|
+
for (const [name, inputSpec] of Object.entries(spec.inputs)) {
|
|
5973
|
+
const rawValue = rawInputs[name];
|
|
5974
|
+
if (rawValue === undefined || rawValue === null || rawValue === "") {
|
|
5975
|
+
if (inputSpec.required) {
|
|
5976
|
+
diagnostics.push({
|
|
5977
|
+
severity: "error",
|
|
5978
|
+
code: "workflow-input-missing",
|
|
5979
|
+
input: name,
|
|
5980
|
+
message: `Workflow ${spec.name} requires input "${name}".`
|
|
5981
|
+
});
|
|
5307
5982
|
}
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5983
|
+
continue;
|
|
5984
|
+
}
|
|
5985
|
+
const value = normalizeInputValue(name, inputSpec, rawValue);
|
|
5986
|
+
if (value === null) {
|
|
5987
|
+
diagnostics.push({
|
|
5988
|
+
severity: "error",
|
|
5989
|
+
code: "workflow-input-invalid",
|
|
5990
|
+
input: name,
|
|
5991
|
+
message: `Workflow ${spec.name} input "${name}" must be ${inputSpec.type}.`
|
|
5992
|
+
});
|
|
5993
|
+
continue;
|
|
5994
|
+
}
|
|
5995
|
+
if (inputSpec.allowedValues && typeof value === "string" && !inputSpec.allowedValues.includes(value)) {
|
|
5996
|
+
diagnostics.push({
|
|
5997
|
+
severity: "error",
|
|
5998
|
+
code: "workflow-input-not-allowed",
|
|
5999
|
+
input: name,
|
|
6000
|
+
message: `Workflow ${spec.name} input "${name}" must be one of: ${inputSpec.allowedValues.join(", ")}.`
|
|
6001
|
+
});
|
|
6002
|
+
continue;
|
|
6003
|
+
}
|
|
6004
|
+
inputs[name] = value;
|
|
6005
|
+
}
|
|
6006
|
+
const fieldType = inputs.type;
|
|
6007
|
+
if (spec.name === "add-field" && typeof fieldType === "string" && (fieldType.toLowerCase() === "number" || fieldType.toLowerCase() === "numeric")) {
|
|
6008
|
+
diagnostics.push({
|
|
6009
|
+
severity: "error",
|
|
6010
|
+
code: "primitive-field-type-unsupported",
|
|
6011
|
+
input: "type",
|
|
6012
|
+
message: `Field type "${fieldType}" is ambiguous in Akan. Use Int for integer fields or Float for decimal fields.`
|
|
6013
|
+
});
|
|
6014
|
+
}
|
|
6015
|
+
if (spec.name === "add-field") {
|
|
6016
|
+
const defaultCoercion = addFieldDefaultCoercion(inputs);
|
|
6017
|
+
if (defaultCoercion?.diagnostic) {
|
|
6018
|
+
diagnostics.push({
|
|
6019
|
+
...defaultCoercion.diagnostic,
|
|
6020
|
+
code: "workflow-default-value-invalid",
|
|
6021
|
+
message: `Plan input default is not valid for ${defaultCoercion.normalizedType}: ${defaultCoercion.diagnostic.message}`
|
|
6022
|
+
});
|
|
6023
|
+
} else if (defaultCoercion?.normalized && defaultCoercion.expression) {
|
|
6024
|
+
diagnostics.push({
|
|
6025
|
+
severity: "warning",
|
|
6026
|
+
code: "workflow-default-value-normalized",
|
|
6027
|
+
input: "default",
|
|
6028
|
+
failureScope: "source-change",
|
|
6029
|
+
message: `Plan input default will be written as ${defaultCoercion.normalizedType} literal ${defaultCoercion.expression}.`
|
|
6030
|
+
});
|
|
6031
|
+
}
|
|
6032
|
+
}
|
|
6033
|
+
return {
|
|
6034
|
+
schemaVersion: 1,
|
|
6035
|
+
workflow: spec.name,
|
|
6036
|
+
mode: "plan",
|
|
6037
|
+
inputs,
|
|
6038
|
+
optionalSurfaces: createWorkflowPlanOptionalSurfaces(spec, inputs),
|
|
6039
|
+
steps: spec.steps,
|
|
6040
|
+
predictedChanges: createWorkflowPlanPredictedChanges(spec, inputs),
|
|
6041
|
+
validation: spec.validation,
|
|
6042
|
+
diagnostics,
|
|
6043
|
+
recommendations: createWorkflowPlanRecommendations(spec, inputs),
|
|
6044
|
+
requiresApproval: true
|
|
5327
6045
|
};
|
|
5328
6046
|
};
|
|
5329
|
-
|
|
5330
|
-
|
|
6047
|
+
// pkgs/@akanjs/devkit/workflow/render.ts
|
|
6048
|
+
var renderWorkflowList = (specs) => [
|
|
6049
|
+
"# Akan Workflows",
|
|
5331
6050
|
"",
|
|
5332
|
-
`-
|
|
5333
|
-
|
|
6051
|
+
...specs.flatMap((spec) => [`- \`${spec.name}\`: ${spec.description}`, ` - When: ${spec.whenToUse}`]),
|
|
6052
|
+
""
|
|
6053
|
+
].join(`
|
|
6054
|
+
`);
|
|
6055
|
+
var renderWorkflowExplain = (spec) => [
|
|
6056
|
+
`# Workflow: ${spec.name}`,
|
|
5334
6057
|
"",
|
|
5335
|
-
|
|
5336
|
-
...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
|
|
6058
|
+
spec.description,
|
|
5337
6059
|
"",
|
|
5338
|
-
"##
|
|
5339
|
-
|
|
6060
|
+
"## When To Use",
|
|
6061
|
+
spec.whenToUse,
|
|
6062
|
+
"",
|
|
6063
|
+
"## Inputs",
|
|
6064
|
+
...Object.entries(spec.inputs).map(([name, input2]) => `- \`${name}\`${input2.required ? " (required)" : ""}: ${input2.description}${input2.allowedValues ? ` Allowed: ${input2.allowedValues.join(", ")}.` : ""}`),
|
|
6065
|
+
"",
|
|
6066
|
+
"## Optional Surfaces",
|
|
6067
|
+
...Object.entries(spec.optionalSurfaces ?? {}).map(([name, mode]) => `- \`${name}\`: ${mode}`),
|
|
6068
|
+
"",
|
|
6069
|
+
"## Steps",
|
|
6070
|
+
...spec.steps.map((step, index) => `${index + 1}. \`${step.id}\` (${step.tool}): ${step.description}`),
|
|
6071
|
+
"",
|
|
6072
|
+
"## Validation",
|
|
6073
|
+
...spec.validation.map((validation) => `- \`${validation.command}\`: ${validation.reason}`),
|
|
6074
|
+
""
|
|
6075
|
+
].join(`
|
|
6076
|
+
`);
|
|
6077
|
+
var renderWorkflowPlan = (plan) => [
|
|
6078
|
+
`# Workflow Plan: ${plan.workflow}`,
|
|
6079
|
+
"",
|
|
6080
|
+
`- Mode: ${plan.mode}`,
|
|
6081
|
+
`- Requires approval: ${plan.requiresApproval}`,
|
|
6082
|
+
"",
|
|
6083
|
+
"## Inputs",
|
|
6084
|
+
...Object.entries(plan.inputs).map(([name, value]) => `- \`${name}\`: ${Array.isArray(value) ? value.join(", ") : value}`),
|
|
6085
|
+
"",
|
|
6086
|
+
"## Optional Surfaces",
|
|
6087
|
+
...Object.entries(plan.optionalSurfaces).map(([name, mode]) => `- \`${name}\`: ${mode}`),
|
|
6088
|
+
"",
|
|
6089
|
+
"## Steps",
|
|
6090
|
+
...plan.steps.map((step, index) => `${index + 1}. \`${step.id}\` (${step.tool}): ${step.description}`),
|
|
5340
6091
|
"",
|
|
5341
|
-
"##
|
|
5342
|
-
...
|
|
6092
|
+
"## Predicted Changes",
|
|
6093
|
+
...plan.predictedChanges.map((change) => {
|
|
6094
|
+
const scope = change.applyScope ? ` (${change.applyScope})` : "";
|
|
6095
|
+
return `- \`${change.action}\`${scope} ${change.target}: ${change.reason}`;
|
|
6096
|
+
}),
|
|
5343
6097
|
"",
|
|
5344
|
-
"##
|
|
5345
|
-
...
|
|
6098
|
+
"## Validation",
|
|
6099
|
+
...plan.validation.map((validation) => `- \`${validation.command}\`: ${validation.reason}`),
|
|
5346
6100
|
"",
|
|
5347
6101
|
"## Diagnostics",
|
|
5348
|
-
...
|
|
6102
|
+
...plan.diagnostics.length ? plan.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
5349
6103
|
"",
|
|
5350
6104
|
"## Recommendations",
|
|
5351
|
-
...
|
|
5352
|
-
"",
|
|
5353
|
-
"## Next Actions",
|
|
5354
|
-
...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
6105
|
+
...plan.recommendations.length ? plan.recommendations.map((recommendation) => `- [${recommendation.kind}] ${recommendation.message}`) : ["- none"],
|
|
5355
6106
|
""
|
|
5356
6107
|
].join(`
|
|
5357
6108
|
`);
|
|
6109
|
+
var renderRecommendation = (recommendation) => {
|
|
6110
|
+
const target = recommendation.target ? ` ${recommendation.target}` : "";
|
|
6111
|
+
const action = recommendation.action ? ` Action: ${recommendation.action}` : "";
|
|
6112
|
+
return `- [${recommendation.kind}]${target} ${recommendation.message}${action}`;
|
|
6113
|
+
};
|
|
6114
|
+
var renderDiagnostic = (diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`;
|
|
6115
|
+
var renderWorkflowApplyReport = (report) => {
|
|
6116
|
+
const manualReviewItems = [
|
|
6117
|
+
...report.diagnostics.filter((diagnostic) => diagnostic.severity === "warning").map(renderDiagnostic),
|
|
6118
|
+
...report.recommendations.filter((recommendation) => recommendation.kind === "manual-action").map(renderRecommendation)
|
|
6119
|
+
];
|
|
6120
|
+
const validationBlockers = report.diagnostics.filter((diagnostic) => diagnostic.severity === "error" && (diagnostic.failureScope === "workspace-config" || diagnostic.failureScope === "environment")).map(renderDiagnostic);
|
|
6121
|
+
return [
|
|
6122
|
+
`# Workflow Apply: ${report.workflow}`,
|
|
6123
|
+
"",
|
|
6124
|
+
`- Mode: ${report.mode}`,
|
|
6125
|
+
`- Status: ${report.status}`,
|
|
6126
|
+
...report.validationTarget ? [`- Validation target: ${report.validationTarget}`] : [],
|
|
6127
|
+
"",
|
|
6128
|
+
"## Automatically Modified",
|
|
6129
|
+
...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
|
|
6130
|
+
"",
|
|
6131
|
+
"## Generated Sync",
|
|
6132
|
+
...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
|
|
6133
|
+
"",
|
|
6134
|
+
"## Applied Commands",
|
|
6135
|
+
...report.appliedCommands.length ? report.appliedCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
|
|
6136
|
+
"",
|
|
6137
|
+
"## Recommended Validation Commands",
|
|
6138
|
+
...report.recommendedValidationCommands.length ? report.recommendedValidationCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
|
|
6139
|
+
"",
|
|
6140
|
+
"## User Review Required",
|
|
6141
|
+
...manualReviewItems.length ? manualReviewItems : ["- none"],
|
|
6142
|
+
"",
|
|
6143
|
+
"## Validation Blockers",
|
|
6144
|
+
...validationBlockers.length ? validationBlockers : report.recommendedValidationCommands.length ? ["- none detected during apply; run validation with the validation target for command-level blockers."] : ["- none"],
|
|
6145
|
+
"",
|
|
6146
|
+
"## Diagnostics",
|
|
6147
|
+
...report.diagnostics.length ? report.diagnostics.map(renderDiagnostic) : ["- none"],
|
|
6148
|
+
"",
|
|
6149
|
+
"## Recommendations",
|
|
6150
|
+
...report.recommendations.length ? report.recommendations.map(renderRecommendation) : ["- none"],
|
|
6151
|
+
"",
|
|
6152
|
+
"## Next Actions",
|
|
6153
|
+
...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
6154
|
+
""
|
|
6155
|
+
].join(`
|
|
6156
|
+
`);
|
|
6157
|
+
};
|
|
5358
6158
|
var renderWorkflowApply = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowApplyReport(report);
|
|
5359
6159
|
var renderWorkflowValidationRunReport = (report) => [
|
|
5360
6160
|
`# Workflow Validation: ${report.workflow}`,
|
|
5361
6161
|
"",
|
|
5362
6162
|
`- Run: ${report.runId}`,
|
|
5363
6163
|
`- Status: ${report.status}`,
|
|
6164
|
+
`- Source status: ${report.sourceStatus}`,
|
|
6165
|
+
`- Workspace status: ${report.workspaceStatus}`,
|
|
6166
|
+
`- Overall status: ${report.overallStatus}`,
|
|
6167
|
+
"",
|
|
6168
|
+
"## Status Summary",
|
|
6169
|
+
`- Required source-change validation: ${report.summary.sourceChange}`,
|
|
6170
|
+
`- Generated sync validation: ${report.summary.generatedSync}`,
|
|
6171
|
+
`- Workspace configuration validation: ${report.summary.workspaceConfig}`,
|
|
6172
|
+
`- Environment validation: ${report.summary.environment}`,
|
|
6173
|
+
"",
|
|
6174
|
+
"## Known Blockers",
|
|
6175
|
+
...report.knownBlockers.length ? report.knownBlockers.map((blocker) => {
|
|
6176
|
+
const command = blocker.command ? ` \`${blocker.command}\`` : "";
|
|
6177
|
+
const count = blocker.count > 1 ? ` (${blocker.count}x)` : "";
|
|
6178
|
+
const known = blocker.known ? " known" : "";
|
|
6179
|
+
return `- [${blocker.failureScope}${known}]${command}${count}: ${blocker.message}`;
|
|
6180
|
+
}) : ["- none"],
|
|
5364
6181
|
"",
|
|
5365
6182
|
"## Commands",
|
|
5366
6183
|
...report.commands.length ? report.commands.map((command) => {
|
|
@@ -5371,164 +6188,23 @@ var renderWorkflowValidationRunReport = (report) => [
|
|
|
5371
6188
|
"## Diagnostics",
|
|
5372
6189
|
...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
5373
6190
|
"",
|
|
5374
|
-
"## Repair Actions",
|
|
5375
|
-
...report.repairActions.length ? report.repairActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
5376
|
-
"",
|
|
5377
|
-
"## Next Actions",
|
|
5378
|
-
...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
5379
|
-
""
|
|
5380
|
-
].join(`
|
|
5381
|
-
`);
|
|
5382
|
-
var renderWorkflowValidation = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowValidationRunReport(report);
|
|
5383
|
-
var renderWorkflowRunArtifact = (artifact, format = "markdown") => {
|
|
5384
|
-
if ("kind" in artifact)
|
|
5385
|
-
return renderRepairReport(artifact, format);
|
|
5386
|
-
if ("mode" in artifact && artifact.mode === "validate")
|
|
5387
|
-
return renderWorkflowValidation(artifact, format);
|
|
5388
|
-
if ("mode" in artifact && (artifact.mode === "apply" || artifact.mode === "dry-run")) {
|
|
5389
|
-
return renderWorkflowApply(artifact, format);
|
|
5390
|
-
}
|
|
5391
|
-
return jsonText(artifact);
|
|
5392
|
-
};
|
|
5393
|
-
var createRepairReport = ({
|
|
5394
|
-
command,
|
|
5395
|
-
kind,
|
|
5396
|
-
target = null,
|
|
5397
|
-
diagnostics = [],
|
|
5398
|
-
repairActions = [],
|
|
5399
|
-
nextActions = [],
|
|
5400
|
-
commands = [],
|
|
5401
|
-
generatedFiles = [],
|
|
5402
|
-
syncedAt
|
|
5403
|
-
}) => ({
|
|
5404
|
-
schemaVersion: 1,
|
|
5405
|
-
command,
|
|
5406
|
-
kind,
|
|
5407
|
-
target,
|
|
5408
|
-
status: workflowStatus(diagnostics) === "failed" || commandStatus(commands) === "failed" ? "failed" : "passed",
|
|
5409
|
-
diagnostics,
|
|
5410
|
-
repairActions: uniqueBy(repairActions, (action) => action.command),
|
|
5411
|
-
nextActions: uniqueBy(nextActions, (action) => action.command),
|
|
5412
|
-
commands,
|
|
5413
|
-
generatedFiles,
|
|
5414
|
-
...syncedAt ? { syncedAt } : {}
|
|
5415
|
-
});
|
|
5416
|
-
var renderRepairReportMarkdown = (report) => [
|
|
5417
|
-
`# Akan Repair: ${report.kind}`,
|
|
5418
|
-
"",
|
|
5419
|
-
`- Status: ${report.status}`,
|
|
5420
|
-
`- Target: ${report.target ?? "none"}`,
|
|
5421
|
-
"",
|
|
5422
|
-
"## Commands",
|
|
5423
|
-
...report.commands.length ? report.commands.map((command) => `- [${command.status}] \`${command.command}\`: ${command.reason}`) : ["- none"],
|
|
5424
|
-
"",
|
|
5425
|
-
"## Diagnostics",
|
|
5426
|
-
...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
5427
|
-
"",
|
|
6191
|
+
"## Repair Actions",
|
|
6192
|
+
...report.repairActions.length ? report.repairActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
6193
|
+
"",
|
|
5428
6194
|
"## Next Actions",
|
|
5429
6195
|
...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
5430
6196
|
""
|
|
5431
6197
|
].join(`
|
|
5432
6198
|
`);
|
|
5433
|
-
var
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
registry;
|
|
5437
|
-
constructor(registry) {
|
|
5438
|
-
this.registry = registry;
|
|
5439
|
-
}
|
|
5440
|
-
async apply(plan) {
|
|
5441
|
-
const changedFiles = [];
|
|
5442
|
-
const generatedFiles = [];
|
|
5443
|
-
const recommendedValidationCommands = [];
|
|
5444
|
-
const diagnostics = [...plan.diagnostics];
|
|
5445
|
-
const recommendations = [...plan.recommendations];
|
|
5446
|
-
const nextActions = [];
|
|
5447
|
-
if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
5448
|
-
return createWorkflowApplyReport({
|
|
5449
|
-
workflow: plan.workflow,
|
|
5450
|
-
mode: "apply",
|
|
5451
|
-
changedFiles,
|
|
5452
|
-
generatedFiles,
|
|
5453
|
-
recommendedValidationCommands,
|
|
5454
|
-
diagnostics,
|
|
5455
|
-
recommendations,
|
|
5456
|
-
nextActions,
|
|
5457
|
-
plan
|
|
5458
|
-
});
|
|
5459
|
-
}
|
|
5460
|
-
recommendedValidationCommands.push(...workflowCommandsForPlan(plan));
|
|
5461
|
-
nextActions.push(...workflowCommandsForPlan(plan));
|
|
5462
|
-
for (const step of plan.steps) {
|
|
5463
|
-
const runner = this.registry[workflowStepKey(plan.workflow, step.id)] ?? this.registry[step.tool] ?? this.registry[step.id];
|
|
5464
|
-
if (!runner) {
|
|
5465
|
-
diagnostics.push({
|
|
5466
|
-
severity: "error",
|
|
5467
|
-
code: "workflow-step-unsupported",
|
|
5468
|
-
message: `Workflow ${plan.workflow} step "${step.id}" is not supported by workflow apply yet.`
|
|
5469
|
-
});
|
|
5470
|
-
nextActions.push({
|
|
5471
|
-
command: `akan workflow explain ${plan.workflow}`,
|
|
5472
|
-
reason: "Review the unsupported workflow step before retrying apply."
|
|
5473
|
-
});
|
|
5474
|
-
break;
|
|
5475
|
-
}
|
|
5476
|
-
const result = await runner(step, plan);
|
|
5477
|
-
if (!result)
|
|
5478
|
-
continue;
|
|
5479
|
-
changedFiles.push(...result.changedFiles ?? []);
|
|
5480
|
-
generatedFiles.push(...result.generatedFiles ?? []);
|
|
5481
|
-
recommendedValidationCommands.push(...result.commands ?? []);
|
|
5482
|
-
diagnostics.push(...result.diagnostics ?? []);
|
|
5483
|
-
recommendations.push(...result.recommendations ?? []);
|
|
5484
|
-
nextActions.push(...result.nextActions ?? []);
|
|
5485
|
-
if (diagnostics.some((diagnostic) => diagnostic.severity === "error"))
|
|
5486
|
-
break;
|
|
5487
|
-
}
|
|
5488
|
-
return createWorkflowApplyReport({
|
|
5489
|
-
workflow: plan.workflow,
|
|
5490
|
-
mode: "apply",
|
|
5491
|
-
changedFiles,
|
|
5492
|
-
generatedFiles,
|
|
5493
|
-
recommendedValidationCommands,
|
|
5494
|
-
diagnostics,
|
|
5495
|
-
recommendations,
|
|
5496
|
-
nextActions,
|
|
5497
|
-
plan
|
|
5498
|
-
});
|
|
5499
|
-
}
|
|
5500
|
-
}
|
|
5501
|
-
var createPrimitiveWriteReport = ({
|
|
5502
|
-
command,
|
|
5503
|
-
status,
|
|
5504
|
-
changedFiles = [],
|
|
5505
|
-
generatedFiles = [],
|
|
5506
|
-
validationCommands = [],
|
|
5507
|
-
diagnostics = [],
|
|
5508
|
-
nextActions = []
|
|
5509
|
-
}) => ({
|
|
5510
|
-
schemaVersion: 1,
|
|
5511
|
-
command,
|
|
5512
|
-
status: status ?? (diagnostics.some((diagnostic) => diagnostic.severity === "error") ? "failed" : "passed"),
|
|
5513
|
-
changedFiles,
|
|
5514
|
-
generatedFiles,
|
|
5515
|
-
validationCommands,
|
|
5516
|
-
diagnostics,
|
|
5517
|
-
nextActions
|
|
5518
|
-
});
|
|
5519
|
-
var renderPrimitiveWriteReport = (report) => [
|
|
5520
|
-
`# Primitive Write: ${report.command}`,
|
|
6199
|
+
var renderWorkflowValidation = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowValidationRunReport(report);
|
|
6200
|
+
var renderRepairReportMarkdown = (report) => [
|
|
6201
|
+
`# Akan Repair: ${report.kind}`,
|
|
5521
6202
|
"",
|
|
5522
6203
|
`- Status: ${report.status}`,
|
|
6204
|
+
`- Target: ${report.target ?? "none"}`,
|
|
5523
6205
|
"",
|
|
5524
|
-
"##
|
|
5525
|
-
...report.
|
|
5526
|
-
"",
|
|
5527
|
-
"## Generated Files",
|
|
5528
|
-
...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
|
|
5529
|
-
"",
|
|
5530
|
-
"## Validation Commands",
|
|
5531
|
-
...report.validationCommands.length ? report.validationCommands.map((validation) => `- \`${validation.command}\`: ${validation.reason}`) : ["- none"],
|
|
6206
|
+
"## Commands",
|
|
6207
|
+
...report.commands.length ? report.commands.map((command) => `- [${command.status}] \`${command.command}\`: ${command.reason}`) : ["- none"],
|
|
5532
6208
|
"",
|
|
5533
6209
|
"## Diagnostics",
|
|
5534
6210
|
...report.diagnostics.length ? report.diagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
@@ -5538,154 +6214,17 @@ var renderPrimitiveWriteReport = (report) => [
|
|
|
5538
6214
|
""
|
|
5539
6215
|
].join(`
|
|
5540
6216
|
`);
|
|
5541
|
-
var
|
|
5542
|
-
var
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
var validationCommandsForTarget = (target) => [
|
|
5550
|
-
{ command: `akan sync ${target}`, reason: "Refresh generated Akan files from source conventions." },
|
|
5551
|
-
{ command: `akan lint ${target}`, reason: "Validate formatting, imports, and static lint rules." }
|
|
5552
|
-
];
|
|
5553
|
-
var nextActionsForTarget = (target) => [
|
|
5554
|
-
{ command: `akan sync ${target}`, reason: "Refresh generated Akan files after source changes." },
|
|
5555
|
-
{ command: `akan lint ${target}`, reason: "Validate the target after generated files are refreshed." }
|
|
5556
|
-
];
|
|
5557
|
-
var createPassedPrimitiveReport = ({
|
|
5558
|
-
command,
|
|
5559
|
-
changedFiles,
|
|
5560
|
-
generatedFiles,
|
|
5561
|
-
target,
|
|
5562
|
-
nextActions
|
|
5563
|
-
}) => createPrimitiveWriteReport({
|
|
5564
|
-
command,
|
|
5565
|
-
changedFiles,
|
|
5566
|
-
generatedFiles: generatedFiles ?? [],
|
|
5567
|
-
validationCommands: validationCommandsForTarget(target),
|
|
5568
|
-
diagnostics: [],
|
|
5569
|
-
nextActions: nextActions ?? nextActionsForTarget(target)
|
|
5570
|
-
});
|
|
5571
|
-
var scalarChangedFiles = (sys2, scalarName, files) => Object.values(files).map((file) => sourceFile(sys2, `lib/__scalar/${scalarName}/${file.filename}`, "create", "Scalar source file was created."));
|
|
5572
|
-
var titleize = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
5573
|
-
var lowerlize = (value) => `${value.slice(0, 1).toLowerCase()}${value.slice(1)}`;
|
|
5574
|
-
var compactDiagnostics = (diagnostics) => diagnostics.filter((diagnostic) => !!diagnostic);
|
|
5575
|
-
var normalizeFieldType = (typeName) => {
|
|
5576
|
-
const normalizedTypes = {
|
|
5577
|
-
string: "String",
|
|
5578
|
-
boolean: "Boolean",
|
|
5579
|
-
date: "Date",
|
|
5580
|
-
int: "Int",
|
|
5581
|
-
integer: "Int",
|
|
5582
|
-
float: "Float",
|
|
5583
|
-
double: "Float",
|
|
5584
|
-
decimal: "Float"
|
|
5585
|
-
};
|
|
5586
|
-
return normalizedTypes[typeName.toLowerCase()] ?? typeName;
|
|
5587
|
-
};
|
|
5588
|
-
var ensureBaseTypeImport = (content, typeName) => {
|
|
5589
|
-
if (typeName !== "Int" && typeName !== "Float")
|
|
5590
|
-
return content;
|
|
5591
|
-
if (new RegExp(`import \\{[^}]*\\b${typeName}\\b[^}]*\\} from "akanjs/base";`).test(content))
|
|
5592
|
-
return content;
|
|
5593
|
-
const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
|
|
5594
|
-
if (baseImport) {
|
|
5595
|
-
const names = baseImport[1].split(",").map((name) => name.trim()).filter(Boolean);
|
|
5596
|
-
return content.replace(baseImport[0], `import { ${[...names, typeName].sort().join(", ")} } from "akanjs/base";`);
|
|
5597
|
-
}
|
|
5598
|
-
return `import { ${typeName} } from "akanjs/base";
|
|
5599
|
-
${content}`;
|
|
5600
|
-
};
|
|
5601
|
-
var fieldExpression = (typeName, defaultValue) => {
|
|
5602
|
-
const typeExpression = normalizeFieldType(typeName);
|
|
5603
|
-
const defaultOption = defaultValue ? `, { default: ${JSON.stringify(defaultValue)} }` : "";
|
|
5604
|
-
return `field(${typeExpression}${defaultOption})`;
|
|
5605
|
-
};
|
|
5606
|
-
var insertIntoObject = (content, className, line) => {
|
|
5607
|
-
const classIndex = content.indexOf(`export class ${className} extends via`);
|
|
5608
|
-
if (classIndex < 0)
|
|
5609
|
-
return null;
|
|
5610
|
-
const objectEndIndex = content.indexOf("}))", classIndex);
|
|
5611
|
-
if (objectEndIndex < 0)
|
|
5612
|
-
return null;
|
|
5613
|
-
const prefix = content.slice(0, objectEndIndex);
|
|
5614
|
-
const suffix = content.slice(objectEndIndex);
|
|
5615
|
-
const insertion = prefix.endsWith(`
|
|
5616
|
-
`) ? ` ${line}
|
|
5617
|
-
` : `
|
|
5618
|
-
${line}
|
|
5619
|
-
`;
|
|
5620
|
-
return `${prefix}${insertion}${suffix}`;
|
|
5621
|
-
};
|
|
5622
|
-
var ensureEnumImport = (content) => {
|
|
5623
|
-
if (content.includes("enumOf"))
|
|
5624
|
-
return content;
|
|
5625
|
-
const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
|
|
5626
|
-
if (baseImport) {
|
|
5627
|
-
const names = baseImport[1]?.split(",").map((name) => name.trim()) ?? [];
|
|
5628
|
-
return content.replace(baseImport[0], `import { ${[...names, "enumOf"].sort().join(", ")} } from "akanjs/base";`);
|
|
5629
|
-
}
|
|
5630
|
-
return `import { enumOf } from "akanjs/base";
|
|
5631
|
-
${content}`;
|
|
5632
|
-
};
|
|
5633
|
-
var insertEnumClass = (content, enumClassName, enumName, values) => {
|
|
5634
|
-
if (content.includes(`export class ${enumClassName} extends enumOf`))
|
|
5635
|
-
return content;
|
|
5636
|
-
const enumClass = `export class ${enumClassName} extends enumOf("${enumName}", [
|
|
5637
|
-
${values.map((value) => ` ${JSON.stringify(value)},`).join(`
|
|
5638
|
-
`)}
|
|
5639
|
-
] as const) {}
|
|
5640
|
-
|
|
5641
|
-
`;
|
|
5642
|
-
const firstClassIndex = content.indexOf("export class ");
|
|
5643
|
-
if (firstClassIndex < 0)
|
|
5644
|
-
return `${content}
|
|
5645
|
-
${enumClass}`;
|
|
5646
|
-
return `${content.slice(0, firstClassIndex)}${enumClass}${content.slice(firstClassIndex)}`;
|
|
5647
|
-
};
|
|
5648
|
-
var insertDictionaryModelField = (content, moduleClassName, fieldName) => {
|
|
5649
|
-
if (new RegExp(`\\b${fieldName}\\s*:`).test(content))
|
|
5650
|
-
return content;
|
|
5651
|
-
const label = titleize(fieldName);
|
|
5652
|
-
const modelIndex = content.indexOf(`.model<${moduleClassName}>((t) => ({`);
|
|
5653
|
-
if (modelIndex < 0)
|
|
5654
|
-
return null;
|
|
5655
|
-
const objectEndIndex = content.indexOf(" }))", modelIndex);
|
|
5656
|
-
if (objectEndIndex < 0)
|
|
5657
|
-
return null;
|
|
5658
|
-
return `${content.slice(0, objectEndIndex)} ${fieldName}: t([${JSON.stringify(label)}, ${JSON.stringify(label)}]).desc([${JSON.stringify(label)}, ${JSON.stringify(label)}]),
|
|
5659
|
-
${content.slice(objectEndIndex)}`;
|
|
5660
|
-
};
|
|
5661
|
-
var ensureConstantTypeImport = (content, constantPath, typeName) => {
|
|
5662
|
-
if (new RegExp(`import type \\{[^}]*\\b${typeName}\\b[^}]*\\} from "${constantPath}";`).test(content))
|
|
5663
|
-
return content;
|
|
5664
|
-
const importPattern = new RegExp(`import type \\{ ([^}]+) \\} from "${constantPath}";`);
|
|
5665
|
-
const existingImport = content.match(importPattern);
|
|
5666
|
-
if (existingImport !== null) {
|
|
5667
|
-
const names = existingImport[1]?.split(",").map((name) => name.trim()) ?? [];
|
|
5668
|
-
return content.replace(existingImport[0], `import type { ${[...names, typeName].sort().join(", ")} } from "${constantPath}";`);
|
|
6217
|
+
var renderRepairReport = (report, format = "markdown") => format === "json" ? jsonText(report) : renderRepairReportMarkdown(report);
|
|
6218
|
+
var renderWorkflowRunArtifact = (artifact, format = "markdown") => {
|
|
6219
|
+
if ("kind" in artifact)
|
|
6220
|
+
return renderRepairReport(artifact, format);
|
|
6221
|
+
if ("mode" in artifact && artifact.mode === "validate")
|
|
6222
|
+
return renderWorkflowValidation(artifact, format);
|
|
6223
|
+
if ("mode" in artifact && (artifact.mode === "apply" || artifact.mode === "dry-run")) {
|
|
6224
|
+
return renderWorkflowApply(artifact, format);
|
|
5669
6225
|
}
|
|
5670
|
-
return
|
|
5671
|
-
${content}`;
|
|
5672
|
-
};
|
|
5673
|
-
var insertDictionaryEnum = (content, enumClassName, enumName, values) => {
|
|
5674
|
-
if (content.includes(`.enum<${enumClassName}>("${enumName}"`))
|
|
5675
|
-
return content;
|
|
5676
|
-
const enumBlock = ` .enum<${enumClassName}>("${enumName}", (t) => ({
|
|
5677
|
-
${values.map((value) => ` ${value}: t([${JSON.stringify(titleize(value))}, ${JSON.stringify(titleize(value))}]),`).join(`
|
|
5678
|
-
`)}
|
|
5679
|
-
}))
|
|
5680
|
-
`;
|
|
5681
|
-
const chainEndIndex = content.lastIndexOf(";");
|
|
5682
|
-
const insertBeforeIndex = [content.indexOf(".error("), content.indexOf(".translate("), chainEndIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
|
|
5683
|
-
if (insertBeforeIndex === undefined)
|
|
5684
|
-
return null;
|
|
5685
|
-
return `${content.slice(0, insertBeforeIndex)}${enumBlock}${content.slice(insertBeforeIndex)}`;
|
|
6226
|
+
return jsonText(artifact);
|
|
5686
6227
|
};
|
|
5687
|
-
var parseValues = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
|
|
5688
|
-
|
|
5689
6228
|
// pkgs/@akanjs/devkit/akanContext.ts
|
|
5690
6229
|
var resourceList = [
|
|
5691
6230
|
{ uri: "akan://docs/framework", name: "Akan framework guide", mimeType: "text/markdown" },
|
|
@@ -5821,17 +6360,17 @@ var safeReadJson = async (filePath) => {
|
|
|
5821
6360
|
var isWorkflowPlan = (value) => typeof value === "object" && value !== null && ("schemaVersion" in value) && value.schemaVersion === 1 && ("mode" in value) && value.mode === "plan";
|
|
5822
6361
|
var isWorkflowApplyReport = (value) => typeof value === "object" && value !== null && ("schemaVersion" in value) && value.schemaVersion === 1 && ("mode" in value) && (value.mode === "apply" || value.mode === "dry-run");
|
|
5823
6362
|
var isWorkflowRunArtifact = (value) => typeof value === "object" && value !== null && ("schemaVersion" in value) && value.schemaVersion === 1;
|
|
5824
|
-
var planInputString = (
|
|
5825
|
-
const value =
|
|
6363
|
+
var planInputString = (plan2, key) => {
|
|
6364
|
+
const value = plan2.inputs[key];
|
|
5826
6365
|
return typeof value === "string" ? value : "";
|
|
5827
6366
|
};
|
|
5828
|
-
var expandWorkflowTarget = (target,
|
|
5829
|
-
const app = planInputString(
|
|
5830
|
-
const module = planInputString(
|
|
5831
|
-
const moduleClass = module ?
|
|
6367
|
+
var expandWorkflowTarget = (target, plan2) => {
|
|
6368
|
+
const app = planInputString(plan2, "app");
|
|
6369
|
+
const module = planInputString(plan2, "module");
|
|
6370
|
+
const moduleClass = module ? capitalize4(module) : "<Module>";
|
|
5832
6371
|
return target.replace(/^\*\//, app ? `apps/${app}/` : "").replaceAll("<module>", module || "<module>").replaceAll("<Module>", moduleClass);
|
|
5833
6372
|
};
|
|
5834
|
-
var workflowPathsForPlan = (
|
|
6373
|
+
var workflowPathsForPlan = (plan2) => plan2.predictedChanges.map((change) => expandWorkflowTarget(change.target, plan2));
|
|
5835
6374
|
var workflowPathsForArtifact = (artifact) => {
|
|
5836
6375
|
if (isWorkflowPlan(artifact))
|
|
5837
6376
|
return workflowPathsForPlan(artifact);
|
|
@@ -6046,7 +6585,7 @@ class AkanContextAnalyzer {
|
|
|
6046
6585
|
severity: strict ? "error" : "warning",
|
|
6047
6586
|
code: "module-abstract-missing",
|
|
6048
6587
|
path: module.abstract.path,
|
|
6049
|
-
message: `${
|
|
6588
|
+
message: `${capitalize4(module.kind)} module ${sys2.name}:${module.name} should include ${module.abstract.path}`,
|
|
6050
6589
|
repairActions: [action]
|
|
6051
6590
|
});
|
|
6052
6591
|
repairActions.push(action);
|
|
@@ -6058,7 +6597,7 @@ class AkanContextAnalyzer {
|
|
|
6058
6597
|
severity: "error",
|
|
6059
6598
|
code: "module-shape-invalid",
|
|
6060
6599
|
path: module.path,
|
|
6061
|
-
message: `${
|
|
6600
|
+
message: `${capitalize4(module.kind)} module ${sys2.name}:${module.name} is missing required files: ${missingFiles.join(", ")}`,
|
|
6062
6601
|
repairActions: [action]
|
|
6063
6602
|
});
|
|
6064
6603
|
repairActions.push(action);
|
|
@@ -6109,8 +6648,8 @@ class AkanContextAnalyzer {
|
|
|
6109
6648
|
...workflowPaths.length ? { baselineDiagnostics, workflowDiagnostics } : {}
|
|
6110
6649
|
};
|
|
6111
6650
|
}
|
|
6112
|
-
static findModules(context, moduleName) {
|
|
6113
|
-
const modules = [...context.apps, ...context.libs].flatMap((sys2) => sys2.modules);
|
|
6651
|
+
static findModules(context, moduleName, { app = null } = {}) {
|
|
6652
|
+
const modules = [...context.apps, ...context.libs].filter((sys2) => !app || sys2.name === app).flatMap((sys2) => sys2.modules);
|
|
6114
6653
|
return moduleName ? modules.filter((module) => module.name === moduleName || module.folderName === moduleName) : modules;
|
|
6115
6654
|
}
|
|
6116
6655
|
static renderMarkdown(context, { module: moduleName } = {}) {
|
|
@@ -6300,7 +6839,7 @@ void st;
|
|
|
6300
6839
|
` : `const UserLayout = ({ children }) => children;
|
|
6301
6840
|
const userLayout = {};
|
|
6302
6841
|
`;
|
|
6303
|
-
const
|
|
6842
|
+
const source2 = opts.includeSystemProvider ? `import type { LayoutProps, PageProps } from "akanjs/client";
|
|
6304
6843
|
import { loadFonts } from "akanjs/client";
|
|
6305
6844
|
import { System } from "akanjs/ui";
|
|
6306
6845
|
import { env } from "@apps/${opts.appName}/env/env.client";
|
|
@@ -6376,7 +6915,7 @@ export default function GeneratedLayout({ children, params, searchParams }: Layo
|
|
|
6376
6915
|
return <UserLayout params={params} searchParams={searchParams}>{children}</UserLayout>;
|
|
6377
6916
|
}
|
|
6378
6917
|
`;
|
|
6379
|
-
await Bun.write(absPath,
|
|
6918
|
+
await Bun.write(absPath, source2);
|
|
6380
6919
|
return absPath;
|
|
6381
6920
|
}
|
|
6382
6921
|
async function resolveSsrPageEntries(opts) {
|
|
@@ -6533,23 +7072,23 @@ class BarrelAnalyzer {
|
|
|
6533
7072
|
if (visited.has(absFile))
|
|
6534
7073
|
return;
|
|
6535
7074
|
visited.add(absFile);
|
|
6536
|
-
const
|
|
6537
|
-
if (
|
|
7075
|
+
const source2 = await readIfExists(absFile);
|
|
7076
|
+
if (source2 === null)
|
|
6538
7077
|
return;
|
|
6539
7078
|
const currentSubpath = this.#subpathFor(pkg, absFile);
|
|
6540
7079
|
if (!currentSubpath)
|
|
6541
7080
|
return;
|
|
6542
|
-
const authoritative = this.#scanExports(
|
|
7081
|
+
const authoritative = this.#scanExports(source2, absFile);
|
|
6543
7082
|
authoritative.delete("default");
|
|
6544
7083
|
const attributed = new Set;
|
|
6545
7084
|
REEXPORT_RE.lastIndex = 0;
|
|
6546
|
-
let m = REEXPORT_RE.exec(
|
|
7085
|
+
let m = REEXPORT_RE.exec(source2);
|
|
6547
7086
|
while (m !== null) {
|
|
6548
7087
|
const star = m[1];
|
|
6549
7088
|
const nsAs = m[2];
|
|
6550
7089
|
const namedList = m[3];
|
|
6551
7090
|
const spec = m[5] ?? "";
|
|
6552
|
-
m = REEXPORT_RE.exec(
|
|
7091
|
+
m = REEXPORT_RE.exec(source2);
|
|
6553
7092
|
if (!isRelative(spec))
|
|
6554
7093
|
continue;
|
|
6555
7094
|
if (star) {
|
|
@@ -6585,10 +7124,10 @@ class BarrelAnalyzer {
|
|
|
6585
7124
|
}
|
|
6586
7125
|
}
|
|
6587
7126
|
LOCAL_NAMED_RE.lastIndex = 0;
|
|
6588
|
-
let n = LOCAL_NAMED_RE.exec(
|
|
7127
|
+
let n = LOCAL_NAMED_RE.exec(source2);
|
|
6589
7128
|
while (n !== null) {
|
|
6590
7129
|
const body = n[1] ?? "";
|
|
6591
|
-
n = LOCAL_NAMED_RE.exec(
|
|
7130
|
+
n = LOCAL_NAMED_RE.exec(source2);
|
|
6592
7131
|
for (const item of parseNamedList(body)) {
|
|
6593
7132
|
if (item.isType)
|
|
6594
7133
|
continue;
|
|
@@ -6610,10 +7149,10 @@ class BarrelAnalyzer {
|
|
|
6610
7149
|
map.set(name, { subpath: currentSubpath, originalName: name });
|
|
6611
7150
|
}
|
|
6612
7151
|
}
|
|
6613
|
-
#scanExports(
|
|
7152
|
+
#scanExports(source2, absFile) {
|
|
6614
7153
|
try {
|
|
6615
7154
|
const transpiler = [".tsx", ".jsx"].includes(path13.extname(absFile)) ? this.#tsxTranspiler : this.#tsTranspiler;
|
|
6616
|
-
const { exports } = transpiler.scan(
|
|
7155
|
+
const { exports } = transpiler.scan(source2);
|
|
6617
7156
|
return new Set(exports);
|
|
6618
7157
|
} catch (err) {
|
|
6619
7158
|
this.#logger.error(`scan failed: ${err.message}`);
|
|
@@ -6725,28 +7264,28 @@ var createBarrelImportsPlugin = async (app, { skipPath = defaultSkipPath, pipeAf
|
|
|
6725
7264
|
const raw = await Bun.file(realPath).text();
|
|
6726
7265
|
return { contents: raw, loader };
|
|
6727
7266
|
}
|
|
6728
|
-
let
|
|
6729
|
-
const hasMacroAttr = MACRO_ATTR_RE.test(
|
|
7267
|
+
let source2 = await Bun.file(realPath).text();
|
|
7268
|
+
const hasMacroAttr = MACRO_ATTR_RE.test(source2);
|
|
6730
7269
|
if (!hasMacroAttr && barrels.length > 0) {
|
|
6731
7270
|
let maybe = false;
|
|
6732
7271
|
for (const b of barrels) {
|
|
6733
|
-
if (
|
|
7272
|
+
if (source2.includes(b)) {
|
|
6734
7273
|
maybe = true;
|
|
6735
7274
|
break;
|
|
6736
7275
|
}
|
|
6737
7276
|
}
|
|
6738
7277
|
if (maybe) {
|
|
6739
|
-
const rewritten = await rewriteBarrelImports(
|
|
7278
|
+
const rewritten = await rewriteBarrelImports(source2, barrels, analyzer);
|
|
6740
7279
|
if (rewritten !== null)
|
|
6741
|
-
|
|
7280
|
+
source2 = rewritten;
|
|
6742
7281
|
}
|
|
6743
7282
|
}
|
|
6744
7283
|
if (pipeAfter) {
|
|
6745
|
-
const piped = await pipeAfter(
|
|
7284
|
+
const piped = await pipeAfter(source2, { path: realPath });
|
|
6746
7285
|
if (piped !== null)
|
|
6747
|
-
|
|
7286
|
+
source2 = piped;
|
|
6748
7287
|
}
|
|
6749
|
-
return { contents:
|
|
7288
|
+
return { contents: source2, loader };
|
|
6750
7289
|
});
|
|
6751
7290
|
}
|
|
6752
7291
|
};
|
|
@@ -6922,12 +7461,12 @@ var resolveFileCandidate = async (candidate) => {
|
|
|
6922
7461
|
return null;
|
|
6923
7462
|
};
|
|
6924
7463
|
var MACRO_ATTR_RE = /with\s*\{\s*type\s*:\s*["']macro["']\s*\}/;
|
|
6925
|
-
var rewriteBarrelImports = async (
|
|
6926
|
-
const statements = findImportStatements(
|
|
7464
|
+
var rewriteBarrelImports = async (source2, barrels, analyzer) => {
|
|
7465
|
+
const statements = findImportStatements(source2);
|
|
6927
7466
|
if (statements.length === 0)
|
|
6928
7467
|
return null;
|
|
6929
7468
|
let changed = false;
|
|
6930
|
-
let out =
|
|
7469
|
+
let out = source2;
|
|
6931
7470
|
for (let i = statements.length - 1;i >= 0; i--) {
|
|
6932
7471
|
const stmt = statements[i];
|
|
6933
7472
|
if (!stmt)
|
|
@@ -6945,9 +7484,9 @@ var rewriteBarrelImports = async (source, barrels, analyzer) => {
|
|
|
6945
7484
|
}
|
|
6946
7485
|
return changed ? out : null;
|
|
6947
7486
|
};
|
|
6948
|
-
var findImportStatements = (
|
|
7487
|
+
var findImportStatements = (source2) => {
|
|
6949
7488
|
const statements = [];
|
|
6950
|
-
const sourceFile2 = ts4.createSourceFile("barrel-imports.tsx",
|
|
7489
|
+
const sourceFile2 = ts4.createSourceFile("barrel-imports.tsx", source2, ts4.ScriptTarget.Latest, true, ts4.ScriptKind.TSX);
|
|
6951
7490
|
for (const statement of sourceFile2.statements) {
|
|
6952
7491
|
if (!ts4.isImportDeclaration(statement))
|
|
6953
7492
|
continue;
|
|
@@ -6961,10 +7500,10 @@ var findImportStatements = (source) => {
|
|
|
6961
7500
|
statements.push({
|
|
6962
7501
|
start: statementStart,
|
|
6963
7502
|
end: statementEnd,
|
|
6964
|
-
clause:
|
|
7503
|
+
clause: source2.slice(importClause.getStart(sourceFile2), importClause.end).trim(),
|
|
6965
7504
|
specifier: statement.moduleSpecifier.text,
|
|
6966
|
-
trailingSemicolon:
|
|
6967
|
-
raw:
|
|
7505
|
+
trailingSemicolon: source2.slice(statement.moduleSpecifier.end, statement.end).includes(";"),
|
|
7506
|
+
raw: source2.slice(statementStart, statementEnd)
|
|
6968
7507
|
});
|
|
6969
7508
|
}
|
|
6970
7509
|
return statements;
|
|
@@ -7214,13 +7753,13 @@ class GraphClientEntryDiscovery {
|
|
|
7214
7753
|
}
|
|
7215
7754
|
return cached;
|
|
7216
7755
|
}
|
|
7217
|
-
async#getImports(file,
|
|
7756
|
+
async#getImports(file, source2) {
|
|
7218
7757
|
const absPath = path15.resolve(file);
|
|
7219
7758
|
let cached = this.#importCache.get(absPath);
|
|
7220
7759
|
if (!cached) {
|
|
7221
7760
|
cached = Promise.resolve().then(() => {
|
|
7222
7761
|
try {
|
|
7223
|
-
return this.#tsTranspiler.scanImports(
|
|
7762
|
+
return this.#tsTranspiler.scanImports(source2);
|
|
7224
7763
|
} catch {
|
|
7225
7764
|
return [];
|
|
7226
7765
|
}
|
|
@@ -7245,8 +7784,8 @@ class GraphClientEntryDiscovery {
|
|
|
7245
7784
|
entries.add(absPath);
|
|
7246
7785
|
return this.#finishDiscovery(absPath, visiting, entries);
|
|
7247
7786
|
}
|
|
7248
|
-
const
|
|
7249
|
-
const imports = await this.#getImports(absPath,
|
|
7787
|
+
const source2 = await this.#getRewrittenSource(absPath, content);
|
|
7788
|
+
const imports = await this.#getImports(absPath, source2);
|
|
7250
7789
|
const importerDir = path15.dirname(absPath);
|
|
7251
7790
|
for (const imp of imports) {
|
|
7252
7791
|
const spec = imp.path;
|
|
@@ -7282,13 +7821,13 @@ var IMPLICIT_ROOT_LAYOUT_RE = /[/\\]\.akan[/\\]generated[/\\](?:implicit-root-la
|
|
|
7282
7821
|
function toClientReferencePath(absPath, workspaceRoot) {
|
|
7283
7822
|
return path16.relative(path16.resolve(workspaceRoot), path16.resolve(absPath)).split(path16.sep).join("/");
|
|
7284
7823
|
}
|
|
7285
|
-
function transformUseClient(
|
|
7286
|
-
if (!USE_CLIENT_RE2.test(
|
|
7824
|
+
function transformUseClient(source2, args) {
|
|
7825
|
+
if (!USE_CLIENT_RE2.test(source2))
|
|
7287
7826
|
return null;
|
|
7288
7827
|
if (IMPLICIT_ROOT_LAYOUT_RE.test(args.path))
|
|
7289
7828
|
return null;
|
|
7290
7829
|
const transpiler = new Bun.Transpiler({ loader: loaderFor2(args.path) });
|
|
7291
|
-
const { exports } = transpiler.scan(
|
|
7830
|
+
const { exports } = transpiler.scan(source2);
|
|
7292
7831
|
if (exports.length === 0)
|
|
7293
7832
|
return null;
|
|
7294
7833
|
const referencePath = args.workspaceRoot ? toClientReferencePath(args.path, args.workspaceRoot) : args.path;
|
|
@@ -7434,9 +7973,9 @@ ${absEntry}`).toString(36);
|
|
|
7434
7973
|
`;
|
|
7435
7974
|
}
|
|
7436
7975
|
async#scanEntryExportNames(absEntry) {
|
|
7437
|
-
const
|
|
7976
|
+
const source2 = await Bun.file(absEntry).text();
|
|
7438
7977
|
const transpiler = new Bun.Transpiler({ loader: this.#loaderForEntry(absEntry) });
|
|
7439
|
-
return transpiler.scan(
|
|
7978
|
+
return transpiler.scan(source2).exports;
|
|
7440
7979
|
}
|
|
7441
7980
|
#loaderForEntry(absPath) {
|
|
7442
7981
|
if (absPath.endsWith(".tsx"))
|
|
@@ -7483,14 +8022,14 @@ ${absEntry}`).toString(36);
|
|
|
7483
8022
|
if (Object.keys(this.#externalAliases).length === 0)
|
|
7484
8023
|
return;
|
|
7485
8024
|
await Promise.all(outputs.filter((output) => output.path.endsWith(".js")).map(async (output) => {
|
|
7486
|
-
const
|
|
7487
|
-
const rewritten = ClientEntriesBundler.rewriteExternalImportSpecifiers(
|
|
7488
|
-
if (rewritten !==
|
|
8025
|
+
const source2 = await Bun.file(output.path).text();
|
|
8026
|
+
const rewritten = ClientEntriesBundler.rewriteExternalImportSpecifiers(source2, this.#externalAliases);
|
|
8027
|
+
if (rewritten !== source2)
|
|
7489
8028
|
await Bun.write(output.path, rewritten);
|
|
7490
8029
|
}));
|
|
7491
8030
|
}
|
|
7492
|
-
static rewriteExternalImportSpecifiers(
|
|
7493
|
-
let rewritten =
|
|
8031
|
+
static rewriteExternalImportSpecifiers(source2, aliases) {
|
|
8032
|
+
let rewritten = source2;
|
|
7494
8033
|
for (const [specifier, alias] of Object.entries(aliases)) {
|
|
7495
8034
|
if (!alias)
|
|
7496
8035
|
continue;
|
|
@@ -7745,9 +8284,9 @@ ${absEntry}`).toString(36);
|
|
|
7745
8284
|
return { buildEntries, originalByBuildEntry };
|
|
7746
8285
|
}
|
|
7747
8286
|
async#scanExportNames(absEntry) {
|
|
7748
|
-
const
|
|
8287
|
+
const source2 = await Bun.file(absEntry).text();
|
|
7749
8288
|
const transpiler = new Bun.Transpiler({ loader: this.#loaderFor(absEntry) });
|
|
7750
|
-
return transpiler.scan(
|
|
8289
|
+
return transpiler.scan(source2).exports;
|
|
7751
8290
|
}
|
|
7752
8291
|
#loaderFor(absPath) {
|
|
7753
8292
|
if (absPath.endsWith(".tsx"))
|
|
@@ -7758,10 +8297,10 @@ ${absEntry}`).toString(36);
|
|
|
7758
8297
|
return "ts";
|
|
7759
8298
|
return "js";
|
|
7760
8299
|
}
|
|
7761
|
-
static normalizeNamedDefaultFunctionForFastRefresh(
|
|
8300
|
+
static normalizeNamedDefaultFunctionForFastRefresh(source2) {
|
|
7762
8301
|
let changed = false;
|
|
7763
8302
|
const defaultNames = [];
|
|
7764
|
-
const next =
|
|
8303
|
+
const next = source2.replace(/(^|\n)(\s*)export\s+default\s+(async\s+)?function\s+([A-Za-z_$][\w$]*)(?=\s*(?:<|\())/g, (match, lineStart, indent, asyncKeyword, name) => {
|
|
7765
8304
|
changed = true;
|
|
7766
8305
|
defaultNames.push(name);
|
|
7767
8306
|
return `${lineStart}${indent}${asyncKeyword ?? ""}function ${name}`;
|
|
@@ -7989,8 +8528,8 @@ ${entries.join(`
|
|
|
7989
8528
|
}
|
|
7990
8529
|
static #hasAsyncDefaultExport(moduleAbsPath) {
|
|
7991
8530
|
try {
|
|
7992
|
-
const
|
|
7993
|
-
const sourceFile2 = ts5.createSourceFile(moduleAbsPath,
|
|
8531
|
+
const source2 = fs3.readFileSync(path21.resolve(moduleAbsPath), "utf8");
|
|
8532
|
+
const sourceFile2 = ts5.createSourceFile(moduleAbsPath, source2, ts5.ScriptTarget.Latest, true, PagesEntrySourceGenerator.#scriptKind(moduleAbsPath));
|
|
7994
8533
|
return PagesEntrySourceGenerator.#sourceFileHasAsyncDefaultExport(sourceFile2);
|
|
7995
8534
|
} catch {
|
|
7996
8535
|
return false;
|
|
@@ -8250,8 +8789,8 @@ ${CsrArtifactBuilder.escapeInlineScript(await loadScript(src))}
|
|
|
8250
8789
|
return html;
|
|
8251
8790
|
return result + html.slice(lastIndex);
|
|
8252
8791
|
}
|
|
8253
|
-
static escapeInlineScript(
|
|
8254
|
-
return
|
|
8792
|
+
static escapeInlineScript(source2) {
|
|
8793
|
+
return source2.replace(/<\/script/gi, "<\\/script");
|
|
8255
8794
|
}
|
|
8256
8795
|
static resolveHtmlAssetPath(htmlPath, src) {
|
|
8257
8796
|
if (/^[a-z][a-z0-9+.-]*:/i.test(src) || src.startsWith("//")) {
|
|
@@ -8482,17 +9021,17 @@ class CssCompiler {
|
|
|
8482
9021
|
} catch {
|
|
8483
9022
|
continue;
|
|
8484
9023
|
}
|
|
8485
|
-
let
|
|
9024
|
+
let source2 = content;
|
|
8486
9025
|
if (akanConfig2.barrelImports.length > 0) {
|
|
8487
9026
|
try {
|
|
8488
9027
|
const rewritten = await rewriteBarrelImports(content, akanConfig2.barrelImports, analyzer);
|
|
8489
9028
|
if (rewritten !== null)
|
|
8490
|
-
|
|
9029
|
+
source2 = rewritten;
|
|
8491
9030
|
} catch {}
|
|
8492
9031
|
}
|
|
8493
9032
|
let imports;
|
|
8494
9033
|
try {
|
|
8495
|
-
imports = this.#transpiler.scanImports(
|
|
9034
|
+
imports = this.#transpiler.scanImports(source2);
|
|
8496
9035
|
} catch {
|
|
8497
9036
|
continue;
|
|
8498
9037
|
}
|
|
@@ -8931,7 +9470,7 @@ class DevGeneratedIndexSync {
|
|
|
8931
9470
|
const rawModel = moduleSegment.startsWith("_") ? moduleSegment.slice(1) : moduleSegment;
|
|
8932
9471
|
if (!rawModel)
|
|
8933
9472
|
return null;
|
|
8934
|
-
const modelName =
|
|
9473
|
+
const modelName = capitalize5(rawModel);
|
|
8935
9474
|
const allowedTypes = isScalar ? SCALAR_UI_TYPES : moduleSegment.startsWith("_") ? SERVICE_UI_TYPES : MODULE_UI_TYPES;
|
|
8936
9475
|
const fileTypes = [];
|
|
8937
9476
|
for (const type of allowedTypes) {
|
|
@@ -8948,7 +9487,7 @@ export const ${modelName} = { ${fileTypes.join(", ")} };`;
|
|
|
8948
9487
|
}
|
|
8949
9488
|
}
|
|
8950
9489
|
var exists = async (file) => stat3(file).then(() => true).catch(() => false);
|
|
8951
|
-
var
|
|
9490
|
+
var capitalize5 = (value) => `${value.charAt(0).toUpperCase()}${value.slice(1)}`;
|
|
8952
9491
|
var formatError = (err) => err instanceof Error ? err.message : String(err);
|
|
8953
9492
|
// pkgs/@akanjs/devkit/frontendBuild/fontOptimizer.ts
|
|
8954
9493
|
import { mkdir as mkdir7 } from "fs/promises";
|
|
@@ -9025,8 +9564,8 @@ class FontOptimizer {
|
|
|
9025
9564
|
if (faceCss.length > 0)
|
|
9026
9565
|
this.#cssParts.push(...faceCss, this.#buildRootVariableRule(font));
|
|
9027
9566
|
}
|
|
9028
|
-
#extractFontsExport(
|
|
9029
|
-
const sourceFile2 = ts6.createSourceFile(filePath,
|
|
9567
|
+
#extractFontsExport(source2, filePath) {
|
|
9568
|
+
const sourceFile2 = ts6.createSourceFile(filePath, source2, ts6.ScriptTarget.Latest, true, ts6.ScriptKind.TSX);
|
|
9030
9569
|
const fonts = [];
|
|
9031
9570
|
for (const statement of sourceFile2.statements) {
|
|
9032
9571
|
if (!ts6.isVariableStatement(statement))
|
|
@@ -9582,13 +10121,13 @@ function createUseClientBundlePlugin(options = {}) {
|
|
|
9582
10121
|
build.onLoad({ filter: /\.(tsx|ts|jsx|js)$/ }, async (args) => {
|
|
9583
10122
|
if (args.path.includes("/node_modules/"))
|
|
9584
10123
|
return;
|
|
9585
|
-
let
|
|
10124
|
+
let source2;
|
|
9586
10125
|
try {
|
|
9587
|
-
|
|
10126
|
+
source2 = await Bun.file(args.path).text();
|
|
9588
10127
|
} catch {
|
|
9589
10128
|
return;
|
|
9590
10129
|
}
|
|
9591
|
-
const stubbed = transformUseClient(
|
|
10130
|
+
const stubbed = transformUseClient(source2, {
|
|
9592
10131
|
path: args.path,
|
|
9593
10132
|
workspaceRoot: options.workspaceRoot
|
|
9594
10133
|
});
|
|
@@ -9646,7 +10185,7 @@ class PagesBundleBuilder {
|
|
|
9646
10185
|
PagesBundleBuilder.createServerUseClientFetchPlugin(),
|
|
9647
10186
|
await createExternalizeFrameworkPlugin({ app: this.#app, extra: akanConfig2.externalLibs }),
|
|
9648
10187
|
akanConfig2.barrelImports.length > 0 ? await createBarrelImportsPlugin(this.#app, {
|
|
9649
|
-
pipeAfter: (
|
|
10188
|
+
pipeAfter: (source2, args) => transformUseClient(source2, {
|
|
9650
10189
|
path: args.path,
|
|
9651
10190
|
workspaceRoot
|
|
9652
10191
|
})
|
|
@@ -9687,7 +10226,7 @@ class PagesBundleBuilder {
|
|
|
9687
10226
|
...Object.fromEntries(Object.entries(this.#app.getPublicEnv()).map(([key, value]) => [`process.env.${key}`, JSON.stringify(value)]))
|
|
9688
10227
|
};
|
|
9689
10228
|
}
|
|
9690
|
-
static createPagesEntryPlugin(
|
|
10229
|
+
static createPagesEntryPlugin(source2) {
|
|
9691
10230
|
return {
|
|
9692
10231
|
name: "akan-pages-entry",
|
|
9693
10232
|
setup(build) {
|
|
@@ -9696,7 +10235,7 @@ class PagesBundleBuilder {
|
|
|
9696
10235
|
namespace: "akan-virtual"
|
|
9697
10236
|
}));
|
|
9698
10237
|
build.onLoad({ filter: /^akan-pages-entry$/, namespace: "akan-virtual" }, () => ({
|
|
9699
|
-
contents:
|
|
10238
|
+
contents: source2,
|
|
9700
10239
|
loader: "tsx"
|
|
9701
10240
|
}));
|
|
9702
10241
|
}
|
|
@@ -9718,19 +10257,19 @@ class PagesBundleBuilder {
|
|
|
9718
10257
|
name: "akan-server-use-client-fetch",
|
|
9719
10258
|
setup(build) {
|
|
9720
10259
|
build.onLoad({ filter: /[/\\]lib[/\\]useClient\.(ts|tsx|js|jsx)$/ }, async (args) => {
|
|
9721
|
-
const
|
|
9722
|
-
const transformed = PagesBundleBuilder.transformServerUseClientFetchSource(
|
|
9723
|
-
if (transformed ===
|
|
10260
|
+
const source2 = await Bun.file(args.path).text();
|
|
10261
|
+
const transformed = PagesBundleBuilder.transformServerUseClientFetchSource(source2);
|
|
10262
|
+
if (transformed === source2)
|
|
9724
10263
|
return;
|
|
9725
10264
|
return { contents: transformed, loader: loaderFor4(args.path) };
|
|
9726
10265
|
});
|
|
9727
10266
|
}
|
|
9728
10267
|
};
|
|
9729
10268
|
}
|
|
9730
|
-
static transformServerUseClientFetchSource(
|
|
9731
|
-
if (!
|
|
9732
|
-
return
|
|
9733
|
-
return
|
|
10269
|
+
static transformServerUseClientFetchSource(source2) {
|
|
10270
|
+
if (!source2.includes(`with { type: "macro" }`) || !source2.includes("FetchClient.build"))
|
|
10271
|
+
return source2;
|
|
10272
|
+
return source2.replace(/import\s+\{\s*getSerializedSignal\s*\}\s+from\s+["']\.\/sig["']\s+with\s+\{\s*type\s*:\s*["']macro["']\s*\};/, `import { fetch as serverFetch } from "./sig";`).replace(/const\s+fetchProto\s*=\s*FetchClient\.build<[^;]+;/, "const fetchProto = FetchClient.build<typeof signal>(cnst, serverFetch.serializedSignal, { Err: pageProto.Err, base: serverFetch });");
|
|
9734
10273
|
}
|
|
9735
10274
|
}
|
|
9736
10275
|
function loaderFor4(absPath) {
|
|
@@ -10496,8 +11035,8 @@ class Builder {
|
|
|
10496
11035
|
#executor;
|
|
10497
11036
|
#distExecutor;
|
|
10498
11037
|
#pkgJson;
|
|
10499
|
-
constructor({ executor, distExecutor, pkgJson }) {
|
|
10500
|
-
this.#executor =
|
|
11038
|
+
constructor({ executor: executor2, distExecutor, pkgJson }) {
|
|
11039
|
+
this.#executor = executor2;
|
|
10501
11040
|
this.#distExecutor = distExecutor;
|
|
10502
11041
|
this.#pkgJson = pkgJson;
|
|
10503
11042
|
}
|
|
@@ -10623,7 +11162,7 @@ import os from "os";
|
|
|
10623
11162
|
import path38 from "path";
|
|
10624
11163
|
import { select as select2 } from "@inquirer/prompts";
|
|
10625
11164
|
import { MobileProject } from "@trapezedev/project";
|
|
10626
|
-
import { capitalize as
|
|
11165
|
+
import { capitalize as capitalize6 } from "akanjs/common";
|
|
10627
11166
|
|
|
10628
11167
|
// pkgs/@akanjs/devkit/fileEditor.ts
|
|
10629
11168
|
class FileEditor {
|
|
@@ -11714,7 +12253,7 @@ ${error.message}`;
|
|
|
11714
12253
|
this.#setPermissionsInAndroid(["POST_NOTIFICATIONS"]);
|
|
11715
12254
|
}
|
|
11716
12255
|
async#setPermissionInIos(permissions) {
|
|
11717
|
-
const updateNs = Object.fromEntries(Object.entries(permissions).map(([key, value]) => [`NS${
|
|
12256
|
+
const updateNs = Object.fromEntries(Object.entries(permissions).map(([key, value]) => [`NS${capitalize6(key)}`, value]));
|
|
11718
12257
|
await Promise.all([
|
|
11719
12258
|
this.project.ios.updateInfoPlist(this.iosTargetName, "Debug", updateNs),
|
|
11720
12259
|
this.project.ios.updateInfoPlist(this.iosTargetName, "Release", updateNs)
|
|
@@ -11806,8 +12345,8 @@ import chalk6 from "chalk";
|
|
|
11806
12345
|
import { program } from "commander";
|
|
11807
12346
|
|
|
11808
12347
|
// pkgs/@akanjs/devkit/commandDecorators/dependencyBuilder.ts
|
|
11809
|
-
var
|
|
11810
|
-
var createDependencyKey = (refName, kind) => `${refName}${
|
|
12348
|
+
var capitalize7 = (value) => value.slice(0, 1).toUpperCase() + value.slice(1);
|
|
12349
|
+
var createDependencyKey = (refName, kind) => `${refName}${capitalize7(kind)}`;
|
|
11811
12350
|
|
|
11812
12351
|
class CommandContainer {
|
|
11813
12352
|
static #instances = new Map;
|
|
@@ -12287,13 +12826,13 @@ var getInternalArgumentValue = async (argMeta, value, workspace) => {
|
|
|
12287
12826
|
...libNames.map((name2) => ({ name: name2, value: { type: "lib", name: name2 } }))
|
|
12288
12827
|
]
|
|
12289
12828
|
});
|
|
12290
|
-
const
|
|
12291
|
-
const modules = await
|
|
12829
|
+
const executor2 = type === "app" ? AppExecutor.from(workspace, name) : LibExecutor.from(workspace, name);
|
|
12830
|
+
const modules = await executor2.getModules();
|
|
12292
12831
|
const moduleName = await select3({
|
|
12293
12832
|
message: `Select the module name`,
|
|
12294
|
-
choices: modules.map((name2) => ({ name: `${
|
|
12833
|
+
choices: modules.map((name2) => ({ name: `${executor2.name}:${name2}`, value: name2 }))
|
|
12295
12834
|
});
|
|
12296
|
-
return ModuleExecutor.from(
|
|
12835
|
+
return ModuleExecutor.from(executor2, moduleName);
|
|
12297
12836
|
} else
|
|
12298
12837
|
throw new Error(`Invalid system type: ${argMeta.type}`);
|
|
12299
12838
|
};
|
|
@@ -12545,6 +13084,8 @@ var NODE_NATIVE_MODULE_SET = new Set([
|
|
|
12545
13084
|
]);
|
|
12546
13085
|
// pkgs/@akanjs/devkit/getCredentials.ts
|
|
12547
13086
|
import yaml from "js-yaml";
|
|
13087
|
+
// pkgs/@akanjs/devkit/getModelFileData.ts
|
|
13088
|
+
import { capitalize as capitalize8 } from "akanjs/common";
|
|
12548
13089
|
// pkgs/@akanjs/devkit/getRelatedCnsts.ts
|
|
12549
13090
|
import { readFileSync as readFileSync4, realpathSync } from "fs";
|
|
12550
13091
|
import ora2 from "ora";
|
|
@@ -12552,18 +13093,18 @@ import * as ts7 from "typescript";
|
|
|
12552
13093
|
var tsTranspiler = new Bun.Transpiler({ loader: "ts" });
|
|
12553
13094
|
var tsxTranspiler = new Bun.Transpiler({ loader: "tsx" });
|
|
12554
13095
|
var getTranspiler = (filePath) => filePath.endsWith(".tsx") ? tsxTranspiler : tsTranspiler;
|
|
12555
|
-
var scanModuleSpecifiers = (
|
|
12556
|
-
const scannedImports = getTranspiler(filePath).scanImports(
|
|
13096
|
+
var scanModuleSpecifiers = (source2, filePath, includeExports) => {
|
|
13097
|
+
const scannedImports = getTranspiler(filePath).scanImports(source2).map((imp) => imp.path).filter(Boolean);
|
|
12557
13098
|
if (includeExports) {
|
|
12558
13099
|
const specifiers = new Set(scannedImports);
|
|
12559
13100
|
const typeOnlyModuleRegex = /\b(?:import|export)\s+type\s+[\s\S]*?\s+from\s*["']([^"']+)["']/g;
|
|
12560
|
-
for (const match of
|
|
13101
|
+
for (const match of source2.matchAll(typeOnlyModuleRegex)) {
|
|
12561
13102
|
const importPath = match[1];
|
|
12562
13103
|
if (importPath)
|
|
12563
13104
|
specifiers.add(importPath);
|
|
12564
13105
|
}
|
|
12565
13106
|
const exportFromRegex = /\bexport\s+(?:type\s+)?(?:\*|{[\s\S]*?})\s+from\s*["']([^"']+)["']/g;
|
|
12566
|
-
for (const match of
|
|
13107
|
+
for (const match of source2.matchAll(exportFromRegex)) {
|
|
12567
13108
|
const importPath = match[1];
|
|
12568
13109
|
if (importPath)
|
|
12569
13110
|
specifiers.add(importPath);
|
|
@@ -12572,7 +13113,7 @@ var scanModuleSpecifiers = (source, filePath, includeExports) => {
|
|
|
12572
13113
|
}
|
|
12573
13114
|
const importSpecifiers = new Set;
|
|
12574
13115
|
const importDeclarationRegex = /\bimport\s+(?:type\s+)?(?:["']([^"']+)["']|[\s\S]*?\s+from\s*["']([^"']+)["'])/g;
|
|
12575
|
-
for (const match of
|
|
13116
|
+
for (const match of source2.matchAll(importDeclarationRegex)) {
|
|
12576
13117
|
const importPath = match[1] ?? match[2];
|
|
12577
13118
|
if (importPath && (scannedImports.includes(importPath) || match[0].startsWith("import type"))) {
|
|
12578
13119
|
importSpecifiers.add(importPath);
|
|
@@ -12595,8 +13136,8 @@ var collectImportedFiles = (constantFilePath, parsedConfig) => {
|
|
|
12595
13136
|
if (analyzedFiles.has(filePath))
|
|
12596
13137
|
return;
|
|
12597
13138
|
analyzedFiles.add(filePath);
|
|
12598
|
-
const
|
|
12599
|
-
for (const importPath of scanModuleSpecifiers(
|
|
13139
|
+
const source2 = readFileSync4(filePath, "utf-8");
|
|
13140
|
+
for (const importPath of scanModuleSpecifiers(source2, filePath, false)) {
|
|
12600
13141
|
if (!importPath.startsWith("."))
|
|
12601
13142
|
continue;
|
|
12602
13143
|
const resolved = ts7.resolveModuleName(importPath, filePath, parsedConfig.options, ts7.sys).resolvedModule?.resolvedFileName;
|
|
@@ -12645,21 +13186,21 @@ var analyzeProperties = (filesToAnalyze, program2, checker) => {
|
|
|
12645
13186
|
if (analyzedFiles.has(filePath))
|
|
12646
13187
|
return;
|
|
12647
13188
|
analyzedFiles.add(filePath);
|
|
12648
|
-
const
|
|
12649
|
-
if (!
|
|
13189
|
+
const source2 = program2.getSourceFile(filePath);
|
|
13190
|
+
if (!source2)
|
|
12650
13191
|
return;
|
|
12651
13192
|
if (!sourceLineCache.has(filePath)) {
|
|
12652
|
-
sourceLineCache.set(filePath,
|
|
13193
|
+
sourceLineCache.set(filePath, source2.getFullText().split(`
|
|
12653
13194
|
`));
|
|
12654
13195
|
}
|
|
12655
13196
|
const sourceLines = sourceLineCache.get(filePath);
|
|
12656
13197
|
function visit(node) {
|
|
12657
|
-
if (!
|
|
13198
|
+
if (!source2)
|
|
12658
13199
|
return;
|
|
12659
13200
|
if (ts7.isPropertyAccessExpression(node)) {
|
|
12660
13201
|
const left = node.expression;
|
|
12661
13202
|
const right = node.name;
|
|
12662
|
-
const { line } = ts7.getLineAndCharacterOfPosition(
|
|
13203
|
+
const { line } = ts7.getLineAndCharacterOfPosition(source2, node.getStart());
|
|
12663
13204
|
if (ts7.isIdentifier(left) && sourceLines && sourceLines.length > line && sourceLines[line] && (sourceLines[line]?.includes(`@Field.Prop(() => ${left.text}.${right.text}`) || sourceLines[line].includes(`base.Filter(${left.text}.${right.text},`))) {
|
|
12664
13205
|
const symbol = getCachedSymbol(right);
|
|
12665
13206
|
if (symbol?.declarations && symbol.declarations.length > 0) {
|
|
@@ -12710,7 +13251,7 @@ var analyzeProperties = (filesToAnalyze, program2, checker) => {
|
|
|
12710
13251
|
}
|
|
12711
13252
|
ts7.forEachChild(node, visit);
|
|
12712
13253
|
}
|
|
12713
|
-
visit(
|
|
13254
|
+
visit(source2);
|
|
12714
13255
|
}
|
|
12715
13256
|
for (const filePath of filesToAnalyze) {
|
|
12716
13257
|
analyzeFileProperties(filePath);
|