@akanjs/cli 2.3.9-rc.4 → 2.3.9-rc.6
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 +1009 -402
- package/index.js +1174 -430
- package/package.json +2 -2
- package/templates/module/__model__.constant.ts +1 -1
- 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();
|
|
@@ -4807,11 +4807,17 @@ var createWorkflowApplyReport = ({
|
|
|
4807
4807
|
recommendedValidationCommands,
|
|
4808
4808
|
commands = [],
|
|
4809
4809
|
diagnostics = [],
|
|
4810
|
+
postApplyChecks = [],
|
|
4810
4811
|
recommendations = [],
|
|
4811
4812
|
nextActions = [],
|
|
4812
4813
|
plan
|
|
4813
4814
|
}) => {
|
|
4814
4815
|
const validationCommands = recommendedValidationCommands ?? commands;
|
|
4816
|
+
const orderedNextActions = uniqueBy(nextActions, (action) => action.command).sort((left, right) => {
|
|
4817
|
+
const leftRepair = left.command.startsWith("akan workflow explain") ? 0 : 1;
|
|
4818
|
+
const rightRepair = right.command.startsWith("akan workflow explain") ? 0 : 1;
|
|
4819
|
+
return leftRepair - rightRepair;
|
|
4820
|
+
});
|
|
4815
4821
|
return {
|
|
4816
4822
|
schemaVersion: 1,
|
|
4817
4823
|
workflow,
|
|
@@ -4823,8 +4829,9 @@ var createWorkflowApplyReport = ({
|
|
|
4823
4829
|
recommendedValidationCommands: uniqueBy(validationCommands, (command) => command.command),
|
|
4824
4830
|
commands: uniqueBy(validationCommands, (command) => command.command),
|
|
4825
4831
|
diagnostics,
|
|
4832
|
+
postApplyChecks,
|
|
4826
4833
|
recommendations: uniqueBy(recommendations, (recommendation) => `${recommendation.kind}:${recommendation.code}`),
|
|
4827
|
-
nextActions:
|
|
4834
|
+
nextActions: orderedNextActions.slice(0, 3),
|
|
4828
4835
|
plan
|
|
4829
4836
|
};
|
|
4830
4837
|
};
|
|
@@ -4889,6 +4896,12 @@ var statusForScope = (commands, diagnostics, scopes, expectedScope) => {
|
|
|
4889
4896
|
return "unknown";
|
|
4890
4897
|
return hasScopeFailure(scopes, expectedScope, diagnostics) ? "failed" : "passed";
|
|
4891
4898
|
};
|
|
4899
|
+
var statusForValidationKind = (commands, kind) => {
|
|
4900
|
+
const matching = commands.filter((command) => command.kind === kind);
|
|
4901
|
+
if (matching.length === 0)
|
|
4902
|
+
return "unknown";
|
|
4903
|
+
return matching.some((command) => command.status === "failed") ? "failed" : "passed";
|
|
4904
|
+
};
|
|
4892
4905
|
var createKnownBlockers = (commands, diagnostics) => {
|
|
4893
4906
|
const commandBlockers = commands.filter((command) => command.status === "failed" && (command.failureScope === "workspace-config" || command.failureScope === "environment")).map((command) => ({
|
|
4894
4907
|
code: `workflow-validation-${command.failureScope}`,
|
|
@@ -4922,7 +4935,17 @@ var createValidationStatuses = (commands, diagnostics) => {
|
|
|
4922
4935
|
const sourceStatus = statusForScope(commands, diagnostics, scopes, "source-change");
|
|
4923
4936
|
const workspaceStatus = hasScopeFailure(scopes, "workspace-config", diagnostics) || hasScopeFailure(scopes, "environment", diagnostics) ? "failed" : commands.length || diagnostics.length ? "passed" : "unknown";
|
|
4924
4937
|
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";
|
|
4925
|
-
return {
|
|
4938
|
+
return {
|
|
4939
|
+
sourceStatus,
|
|
4940
|
+
workspaceStatus,
|
|
4941
|
+
overallStatus,
|
|
4942
|
+
summary: {
|
|
4943
|
+
sourceChange: sourceStatus,
|
|
4944
|
+
generatedSync: statusForValidationKind(commands, "sync"),
|
|
4945
|
+
workspaceConfig: statusForScope(commands, diagnostics, scopes, "workspace-config"),
|
|
4946
|
+
environment: statusForScope(commands, diagnostics, scopes, "environment")
|
|
4947
|
+
}
|
|
4948
|
+
};
|
|
4926
4949
|
};
|
|
4927
4950
|
var createWorkflowValidationRunReport = async ({
|
|
4928
4951
|
runId = createWorkflowRunId("validation"),
|
|
@@ -5032,221 +5055,7 @@ var createRepairReport = ({
|
|
|
5032
5055
|
});
|
|
5033
5056
|
// pkgs/@akanjs/devkit/workflow/executor.ts
|
|
5034
5057
|
import { capitalize as capitalize2 } from "akanjs/common";
|
|
5035
|
-
|
|
5036
|
-
var primitiveReportToWorkflowStepResult = (report) => ({
|
|
5037
|
-
changedFiles: report.changedFiles,
|
|
5038
|
-
generatedFiles: report.generatedFiles,
|
|
5039
|
-
commands: report.validationCommands,
|
|
5040
|
-
diagnostics: report.diagnostics,
|
|
5041
|
-
recommendations: [],
|
|
5042
|
-
nextActions: report.nextActions
|
|
5043
|
-
});
|
|
5044
|
-
var workflowStringInput = (value) => typeof value === "string" ? value : null;
|
|
5045
|
-
var workflowStringListInput = (value) => Array.isArray(value) ? value.join(",") : null;
|
|
5046
|
-
var resolveWorkflowSys = async (workspace, target) => {
|
|
5047
|
-
if (!target)
|
|
5048
|
-
return null;
|
|
5049
|
-
const [apps, libs] = await workspace.getSyss();
|
|
5050
|
-
if (apps.includes(target))
|
|
5051
|
-
return AppExecutor.from(workspace, target);
|
|
5052
|
-
if (libs.includes(target))
|
|
5053
|
-
return LibExecutor.from(workspace, target);
|
|
5054
|
-
return null;
|
|
5055
|
-
};
|
|
5056
|
-
var targetMissing = (input2 = "app") => ({
|
|
5057
|
-
severity: "error",
|
|
5058
|
-
code: "workflow-target-missing",
|
|
5059
|
-
input: input2,
|
|
5060
|
-
message: "Workflow target app or library was not found."
|
|
5061
|
-
});
|
|
5062
|
-
var inputMissing = (input2) => ({
|
|
5063
|
-
severity: "error",
|
|
5064
|
-
code: "workflow-input-missing",
|
|
5065
|
-
input: input2,
|
|
5066
|
-
message: `Workflow input "${input2}" is required for apply.`
|
|
5067
|
-
});
|
|
5068
|
-
var unsupportedInput = (input2, message) => ({
|
|
5069
|
-
severity: "error",
|
|
5070
|
-
code: "workflow-input-unsupported",
|
|
5071
|
-
input: input2,
|
|
5072
|
-
message
|
|
5073
|
-
});
|
|
5074
|
-
var addFieldUiSurfaceInspection = (plan) => {
|
|
5075
|
-
const app = workflowStringInput(plan.inputs.app);
|
|
5076
|
-
const module = workflowStringInput(plan.inputs.module) ?? "<module>";
|
|
5077
|
-
const field = workflowStringInput(plan.inputs.field) ?? "<field>";
|
|
5078
|
-
const typeName = workflowStringInput(plan.inputs.type);
|
|
5079
|
-
const isNumeric = typeName === "Int" || typeName === "Float" || typeName === "integer" || typeName === "float";
|
|
5080
|
-
const moduleClassName = capitalize2(module);
|
|
5081
|
-
const target = `${app ? `apps/${app}` : "*"}/lib/${module}/${moduleClassName}.Template.tsx`;
|
|
5082
|
-
return {
|
|
5083
|
-
recommendations: [
|
|
5084
|
-
{
|
|
5085
|
-
code: "add-field-ui-surface-review",
|
|
5086
|
-
kind: "manual-action",
|
|
5087
|
-
target,
|
|
5088
|
-
action: isNumeric ? `Review ${moduleClassName} Template/Unit/View/Store surfaces before adding ${field}; confirm the local Field.Text numeric parser/formatter, validation rule, and dictionary label pattern.` : `Add ${field} to ${moduleClassName} UI surfaces only when the local Field.* pattern is clear.`,
|
|
5089
|
-
confidence: "medium",
|
|
5090
|
-
message: isNumeric ? `No UI file was modified automatically because a safe numeric input component pattern is not yet detected for ${module}.${field}.` : `Review Template/Unit/View/Store surfaces for ${module}.${field}; no UI file was modified automatically.`
|
|
5091
|
-
}
|
|
5092
|
-
],
|
|
5093
|
-
nextActions: [
|
|
5094
|
-
{
|
|
5095
|
-
command: `akan workflow explain ${plan.workflow}`,
|
|
5096
|
-
reason: "Review UI surface guidance before manually editing ambiguous UI files."
|
|
5097
|
-
}
|
|
5098
|
-
]
|
|
5099
|
-
};
|
|
5100
|
-
};
|
|
5101
|
-
var createWorkflowStepRegistry = ({
|
|
5102
|
-
workspace,
|
|
5103
|
-
createModule,
|
|
5104
|
-
createScalar,
|
|
5105
|
-
createUi,
|
|
5106
|
-
addField,
|
|
5107
|
-
addEnumField
|
|
5108
|
-
}) => {
|
|
5109
|
-
const inspect = async () => {
|
|
5110
|
-
return;
|
|
5111
|
-
};
|
|
5112
|
-
const commandOnly = async () => {
|
|
5113
|
-
return;
|
|
5114
|
-
};
|
|
5115
|
-
return {
|
|
5116
|
-
inspectSystem: inspect,
|
|
5117
|
-
inspectModule: inspect,
|
|
5118
|
-
syncTarget: commandOnly,
|
|
5119
|
-
lintTarget: commandOnly,
|
|
5120
|
-
[workflowStepKey("create-module", "create-module")]: async (_step, plan) => {
|
|
5121
|
-
const app = workflowStringInput(plan.inputs.app);
|
|
5122
|
-
const module = workflowStringInput(plan.inputs.module);
|
|
5123
|
-
const sys2 = await resolveWorkflowSys(workspace, app);
|
|
5124
|
-
if (!sys2 || !module)
|
|
5125
|
-
return { diagnostics: [!sys2 ? targetMissing() : inputMissing("module")] };
|
|
5126
|
-
return primitiveReportToWorkflowStepResult(await createModule(sys2, module));
|
|
5127
|
-
},
|
|
5128
|
-
[workflowStepKey("create-scalar", "create-scalar")]: async (_step, plan) => {
|
|
5129
|
-
const app = workflowStringInput(plan.inputs.app);
|
|
5130
|
-
const scalar = workflowStringInput(plan.inputs.scalar);
|
|
5131
|
-
const sys2 = await resolveWorkflowSys(workspace, app);
|
|
5132
|
-
if (!sys2 || !scalar)
|
|
5133
|
-
return { diagnostics: [!sys2 ? targetMissing() : inputMissing("scalar")] };
|
|
5134
|
-
return primitiveReportToWorkflowStepResult(await createScalar(sys2, scalar));
|
|
5135
|
-
},
|
|
5136
|
-
[workflowStepKey("create-ui", "create-ui")]: async (_step, plan) => {
|
|
5137
|
-
const surface = workflowStringInput(plan.inputs.surface);
|
|
5138
|
-
if (surface !== "view" && surface !== "unit" && surface !== "template") {
|
|
5139
|
-
return {
|
|
5140
|
-
diagnostics: [
|
|
5141
|
-
unsupportedInput("surface", "Workflow apply currently supports create-ui surfaces: view, unit, template.")
|
|
5142
|
-
]
|
|
5143
|
-
};
|
|
5144
|
-
}
|
|
5145
|
-
return primitiveReportToWorkflowStepResult(await createUi({
|
|
5146
|
-
app: workflowStringInput(plan.inputs.app),
|
|
5147
|
-
module: workflowStringInput(plan.inputs.module),
|
|
5148
|
-
surface
|
|
5149
|
-
}));
|
|
5150
|
-
},
|
|
5151
|
-
[workflowStepKey("add-field", "update-constant")]: async (_step, plan) => {
|
|
5152
|
-
if (workflowStringInput(plan.inputs.type)?.toLowerCase() === "enum") {
|
|
5153
|
-
return primitiveReportToWorkflowStepResult(await addEnumField({
|
|
5154
|
-
app: workflowStringInput(plan.inputs.app),
|
|
5155
|
-
module: workflowStringInput(plan.inputs.module),
|
|
5156
|
-
field: workflowStringInput(plan.inputs.field),
|
|
5157
|
-
values: workflowStringListInput(plan.inputs.values),
|
|
5158
|
-
defaultValue: workflowStringInput(plan.inputs.default)
|
|
5159
|
-
}));
|
|
5160
|
-
}
|
|
5161
|
-
return primitiveReportToWorkflowStepResult(await addField({
|
|
5162
|
-
app: workflowStringInput(plan.inputs.app),
|
|
5163
|
-
module: workflowStringInput(plan.inputs.module),
|
|
5164
|
-
field: workflowStringInput(plan.inputs.field),
|
|
5165
|
-
type: workflowStringInput(plan.inputs.type),
|
|
5166
|
-
defaultValue: workflowStringInput(plan.inputs.default)
|
|
5167
|
-
}));
|
|
5168
|
-
},
|
|
5169
|
-
[workflowStepKey("add-field", "update-dictionary")]: inspect,
|
|
5170
|
-
[workflowStepKey("add-field", "update-ui-surfaces")]: async (_step, plan) => addFieldUiSurfaceInspection(plan),
|
|
5171
|
-
[workflowStepKey("add-enum-field", "update-constant")]: async (_step, plan) => primitiveReportToWorkflowStepResult(await addEnumField({
|
|
5172
|
-
app: workflowStringInput(plan.inputs.app),
|
|
5173
|
-
module: workflowStringInput(plan.inputs.module),
|
|
5174
|
-
field: workflowStringInput(plan.inputs.field),
|
|
5175
|
-
values: workflowStringListInput(plan.inputs.values),
|
|
5176
|
-
defaultValue: workflowStringInput(plan.inputs.default)
|
|
5177
|
-
})),
|
|
5178
|
-
[workflowStepKey("add-enum-field", "update-dictionary")]: inspect,
|
|
5179
|
-
[workflowStepKey("add-enum-field", "update-option")]: inspect
|
|
5180
|
-
};
|
|
5181
|
-
};
|
|
5182
|
-
class WorkflowExecutor {
|
|
5183
|
-
registry;
|
|
5184
|
-
constructor(registry) {
|
|
5185
|
-
this.registry = registry;
|
|
5186
|
-
}
|
|
5187
|
-
async apply(plan) {
|
|
5188
|
-
const changedFiles = [];
|
|
5189
|
-
const generatedFiles = [];
|
|
5190
|
-
const recommendedValidationCommands = [];
|
|
5191
|
-
const diagnostics = [...plan.diagnostics];
|
|
5192
|
-
const recommendations = [...plan.recommendations];
|
|
5193
|
-
const nextActions = [];
|
|
5194
|
-
if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
5195
|
-
return createWorkflowApplyReport({
|
|
5196
|
-
workflow: plan.workflow,
|
|
5197
|
-
mode: "apply",
|
|
5198
|
-
changedFiles,
|
|
5199
|
-
generatedFiles,
|
|
5200
|
-
recommendedValidationCommands,
|
|
5201
|
-
diagnostics,
|
|
5202
|
-
recommendations,
|
|
5203
|
-
nextActions,
|
|
5204
|
-
plan
|
|
5205
|
-
});
|
|
5206
|
-
}
|
|
5207
|
-
recommendedValidationCommands.push(...workflowCommandsForPlan(plan));
|
|
5208
|
-
nextActions.push(...workflowCommandsForPlan(plan));
|
|
5209
|
-
for (const step of plan.steps) {
|
|
5210
|
-
const runner = this.registry[workflowStepKey(plan.workflow, step.id)] ?? this.registry[step.tool] ?? this.registry[step.id];
|
|
5211
|
-
if (!runner) {
|
|
5212
|
-
diagnostics.push({
|
|
5213
|
-
severity: "error",
|
|
5214
|
-
code: "workflow-step-unsupported",
|
|
5215
|
-
message: `Workflow ${plan.workflow} step "${step.id}" is not supported by workflow apply yet.`
|
|
5216
|
-
});
|
|
5217
|
-
nextActions.push({
|
|
5218
|
-
command: `akan workflow explain ${plan.workflow}`,
|
|
5219
|
-
reason: "Review the unsupported workflow step before retrying apply."
|
|
5220
|
-
});
|
|
5221
|
-
break;
|
|
5222
|
-
}
|
|
5223
|
-
const result = await runner(step, plan);
|
|
5224
|
-
if (!result)
|
|
5225
|
-
continue;
|
|
5226
|
-
changedFiles.push(...result.changedFiles ?? []);
|
|
5227
|
-
generatedFiles.push(...result.generatedFiles ?? []);
|
|
5228
|
-
recommendedValidationCommands.push(...result.commands ?? []);
|
|
5229
|
-
diagnostics.push(...result.diagnostics ?? []);
|
|
5230
|
-
recommendations.push(...result.recommendations ?? []);
|
|
5231
|
-
nextActions.push(...result.nextActions ?? []);
|
|
5232
|
-
if (diagnostics.some((diagnostic) => diagnostic.severity === "error"))
|
|
5233
|
-
break;
|
|
5234
|
-
}
|
|
5235
|
-
return createWorkflowApplyReport({
|
|
5236
|
-
workflow: plan.workflow,
|
|
5237
|
-
mode: "apply",
|
|
5238
|
-
changedFiles,
|
|
5239
|
-
generatedFiles,
|
|
5240
|
-
recommendedValidationCommands,
|
|
5241
|
-
diagnostics,
|
|
5242
|
-
recommendations,
|
|
5243
|
-
nextActions,
|
|
5244
|
-
plan
|
|
5245
|
-
});
|
|
5246
|
-
}
|
|
5247
|
-
}
|
|
5248
|
-
// pkgs/@akanjs/devkit/workflow/plan.ts
|
|
5249
|
-
import { capitalize as capitalize3 } from "akanjs/common";
|
|
5058
|
+
import ts4 from "typescript";
|
|
5250
5059
|
|
|
5251
5060
|
// pkgs/@akanjs/devkit/workflow/primitive.ts
|
|
5252
5061
|
var createPrimitiveWriteReport = ({
|
|
@@ -5298,6 +5107,23 @@ var sourceFile = (sys2, path10, action, reason) => ({
|
|
|
5298
5107
|
action,
|
|
5299
5108
|
reason
|
|
5300
5109
|
});
|
|
5110
|
+
var moduleComponentName = (moduleName) => `${moduleName.slice(0, 1).toUpperCase()}${moduleName.slice(1)}`;
|
|
5111
|
+
var moduleSourcePaths = (moduleName) => {
|
|
5112
|
+
const componentName = moduleComponentName(moduleName);
|
|
5113
|
+
return {
|
|
5114
|
+
abstract: `lib/${moduleName}/${moduleName}.abstract.md`,
|
|
5115
|
+
constant: `lib/${moduleName}/${moduleName}.constant.ts`,
|
|
5116
|
+
dictionary: `lib/${moduleName}/${moduleName}.dictionary.ts`,
|
|
5117
|
+
service: `lib/${moduleName}/${moduleName}.service.ts`,
|
|
5118
|
+
signal: `lib/${moduleName}/${moduleName}.signal.ts`,
|
|
5119
|
+
store: `lib/${moduleName}/${moduleName}.store.ts`,
|
|
5120
|
+
template: `lib/${moduleName}/${componentName}.Template.tsx`,
|
|
5121
|
+
unit: `lib/${moduleName}/${componentName}.Unit.tsx`,
|
|
5122
|
+
util: `lib/${moduleName}/${componentName}.Util.tsx`,
|
|
5123
|
+
view: `lib/${moduleName}/${componentName}.View.tsx`,
|
|
5124
|
+
zone: `lib/${moduleName}/${componentName}.Zone.tsx`
|
|
5125
|
+
};
|
|
5126
|
+
};
|
|
5301
5127
|
var generatedFilesForSync = (sys2, reason = "Generated files may change after sync.") => generatedFilePathsForTarget(getSysRoot(sys2), reason);
|
|
5302
5128
|
var validationCommandsForTarget = (target) => [
|
|
5303
5129
|
{ command: `akan sync ${target}`, reason: "Refresh generated Akan files from source conventions." },
|
|
@@ -5324,6 +5150,50 @@ var createPassedPrimitiveReport = ({
|
|
|
5324
5150
|
var scalarChangedFiles = (sys2, scalarName, files) => Object.values(files).map((file) => sourceFile(sys2, `lib/__scalar/${scalarName}/${file.filename}`, "create", "Scalar source file was created."));
|
|
5325
5151
|
var titleize = (value) => value.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
|
|
5326
5152
|
var lowerlize = (value) => `${value.slice(0, 1).toLowerCase()}${value.slice(1)}`;
|
|
5153
|
+
var koLabels = {
|
|
5154
|
+
amount: "\uAE08\uC561",
|
|
5155
|
+
budget: "\uC608\uC0B0",
|
|
5156
|
+
category: "\uCE74\uD14C\uACE0\uB9AC",
|
|
5157
|
+
content: "\uB0B4\uC6A9",
|
|
5158
|
+
count: "\uAC1C\uC218",
|
|
5159
|
+
createdAt: "\uC0DD\uC131\uC77C",
|
|
5160
|
+
date: "\uB0A0\uC9DC",
|
|
5161
|
+
description: "\uC124\uBA85",
|
|
5162
|
+
due: "\uB9C8\uAC10\uC77C",
|
|
5163
|
+
dueAt: "\uB9C8\uAC10\uC77C",
|
|
5164
|
+
email: "\uC774\uBA54\uC77C",
|
|
5165
|
+
enabled: "\uD65C\uC131\uD654",
|
|
5166
|
+
endAt: "\uC885\uB8CC\uC77C",
|
|
5167
|
+
id: "ID",
|
|
5168
|
+
name: "\uC774\uB984",
|
|
5169
|
+
owner: "\uB2F4\uB2F9\uC790",
|
|
5170
|
+
priority: "\uC6B0\uC120\uC21C\uC704",
|
|
5171
|
+
project: "\uD504\uB85C\uC81D\uD2B8",
|
|
5172
|
+
rating: "\uD3C9\uC810",
|
|
5173
|
+
startAt: "\uC2DC\uC791\uC77C",
|
|
5174
|
+
status: "\uC0C1\uD0DC",
|
|
5175
|
+
title: "\uC81C\uBAA9",
|
|
5176
|
+
updatedAt: "\uC218\uC815\uC77C"
|
|
5177
|
+
};
|
|
5178
|
+
var splitFieldWords = (fieldName) => fieldName.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").split(/\s+/).map((word) => word.trim()).filter(Boolean);
|
|
5179
|
+
var koLabelForField = (fieldName) => {
|
|
5180
|
+
if (koLabels[fieldName])
|
|
5181
|
+
return koLabels[fieldName];
|
|
5182
|
+
const words = splitFieldWords(fieldName);
|
|
5183
|
+
const translated = words.map((word) => koLabels[word] ?? koLabels[lowerlize(word)] ?? null);
|
|
5184
|
+
return translated.every(Boolean) ? translated.join(" ") : null;
|
|
5185
|
+
};
|
|
5186
|
+
var bilingualLabelForField = (fieldName) => {
|
|
5187
|
+
const en = titleize(fieldName);
|
|
5188
|
+
return { en, ko: koLabelForField(fieldName) ?? en };
|
|
5189
|
+
};
|
|
5190
|
+
var bilingualDescriptionForField = (fieldName) => {
|
|
5191
|
+
const label = bilingualLabelForField(fieldName);
|
|
5192
|
+
return {
|
|
5193
|
+
en: `Enter ${label.en.toLowerCase()}.`,
|
|
5194
|
+
ko: `${label.ko} \uAC12\uC744 \uC785\uB825\uD569\uB2C8\uB2E4.`
|
|
5195
|
+
};
|
|
5196
|
+
};
|
|
5327
5197
|
var normalizeFieldType = (typeName) => {
|
|
5328
5198
|
const normalizedTypes = {
|
|
5329
5199
|
string: "String",
|
|
@@ -5351,6 +5221,15 @@ var ensureBaseTypeImport = (content, typeName) => {
|
|
|
5351
5221
|
${content}`;
|
|
5352
5222
|
};
|
|
5353
5223
|
var numericDefault = (typeName, rawDefault) => {
|
|
5224
|
+
if (typeof rawDefault === "number") {
|
|
5225
|
+
if (!Number.isFinite(rawDefault))
|
|
5226
|
+
return null;
|
|
5227
|
+
if (typeName === "Int" && !Number.isInteger(rawDefault))
|
|
5228
|
+
return null;
|
|
5229
|
+
return String(rawDefault);
|
|
5230
|
+
}
|
|
5231
|
+
if (typeof rawDefault !== "string")
|
|
5232
|
+
return null;
|
|
5354
5233
|
const trimmed = rawDefault.trim();
|
|
5355
5234
|
if (!trimmed || !Number.isFinite(Number(trimmed)))
|
|
5356
5235
|
return null;
|
|
@@ -5358,16 +5237,41 @@ var numericDefault = (typeName, rawDefault) => {
|
|
|
5358
5237
|
return null;
|
|
5359
5238
|
return trimmed;
|
|
5360
5239
|
};
|
|
5361
|
-
var
|
|
5362
|
-
if (
|
|
5363
|
-
return
|
|
5364
|
-
|
|
5240
|
+
var booleanDefault = (rawDefault) => {
|
|
5241
|
+
if (typeof rawDefault === "boolean")
|
|
5242
|
+
return String(rawDefault);
|
|
5243
|
+
if (typeof rawDefault !== "string")
|
|
5244
|
+
return null;
|
|
5245
|
+
const lowered = rawDefault.trim().toLowerCase();
|
|
5246
|
+
return lowered === "true" || lowered === "false" ? lowered : null;
|
|
5247
|
+
};
|
|
5248
|
+
var dateDefault = (rawDefault) => {
|
|
5249
|
+
if (typeof rawDefault === "number" && Number.isFinite(rawDefault))
|
|
5250
|
+
return `new Date(${rawDefault})`;
|
|
5251
|
+
if (typeof rawDefault !== "string")
|
|
5252
|
+
return null;
|
|
5253
|
+
const trimmed = rawDefault.trim();
|
|
5254
|
+
if (!trimmed)
|
|
5255
|
+
return null;
|
|
5256
|
+
if (trimmed === "now")
|
|
5257
|
+
return "new Date()";
|
|
5258
|
+
if (!Number.isNaN(Date.parse(trimmed)))
|
|
5259
|
+
return `new Date(${JSON.stringify(trimmed)})`;
|
|
5260
|
+
return null;
|
|
5261
|
+
};
|
|
5262
|
+
var coerceFieldDefault = (typeName, defaultValue, options = {}) => {
|
|
5263
|
+
const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
|
|
5264
|
+
if (defaultValue === undefined || defaultValue === null || defaultValue === "") {
|
|
5265
|
+
return { expression: null, normalized: false, normalizedType };
|
|
5266
|
+
}
|
|
5365
5267
|
if (normalizedType === "Int" || normalizedType === "Float") {
|
|
5366
5268
|
const expression = numericDefault(normalizedType, defaultValue);
|
|
5367
5269
|
if (expression !== null)
|
|
5368
|
-
return { expression };
|
|
5270
|
+
return { expression, normalized: typeof defaultValue === "string", normalizedType };
|
|
5369
5271
|
return {
|
|
5370
5272
|
expression: null,
|
|
5273
|
+
normalized: false,
|
|
5274
|
+
normalizedType,
|
|
5371
5275
|
diagnostic: {
|
|
5372
5276
|
severity: "error",
|
|
5373
5277
|
code: "primitive-default-value-invalid",
|
|
@@ -5378,11 +5282,13 @@ var coerceFieldDefault = (typeName, defaultValue) => {
|
|
|
5378
5282
|
};
|
|
5379
5283
|
}
|
|
5380
5284
|
if (normalizedType === "Boolean") {
|
|
5381
|
-
const
|
|
5382
|
-
if (
|
|
5383
|
-
return { expression:
|
|
5285
|
+
const expression = booleanDefault(defaultValue);
|
|
5286
|
+
if (expression !== null)
|
|
5287
|
+
return { expression, normalized: typeof defaultValue === "string", normalizedType };
|
|
5384
5288
|
return {
|
|
5385
5289
|
expression: null,
|
|
5290
|
+
normalized: false,
|
|
5291
|
+
normalizedType,
|
|
5386
5292
|
diagnostic: {
|
|
5387
5293
|
severity: "error",
|
|
5388
5294
|
code: "primitive-default-value-invalid",
|
|
@@ -5392,13 +5298,58 @@ var coerceFieldDefault = (typeName, defaultValue) => {
|
|
|
5392
5298
|
}
|
|
5393
5299
|
};
|
|
5394
5300
|
}
|
|
5395
|
-
|
|
5301
|
+
if (normalizedType === "Date") {
|
|
5302
|
+
const expression = dateDefault(defaultValue);
|
|
5303
|
+
if (expression !== null)
|
|
5304
|
+
return { expression, normalized: true, normalizedType };
|
|
5305
|
+
return {
|
|
5306
|
+
expression: null,
|
|
5307
|
+
normalized: false,
|
|
5308
|
+
normalizedType,
|
|
5309
|
+
diagnostic: {
|
|
5310
|
+
severity: "error",
|
|
5311
|
+
code: "primitive-default-value-invalid",
|
|
5312
|
+
input: "default",
|
|
5313
|
+
failureScope: "source-change",
|
|
5314
|
+
message: `Default value for Date must be "now", a timestamp, or a parseable date string. Received: ${JSON.stringify(defaultValue)}.`
|
|
5315
|
+
}
|
|
5316
|
+
};
|
|
5317
|
+
}
|
|
5318
|
+
if (normalizedType === "enum") {
|
|
5319
|
+
if (typeof defaultValue === "string" && options.enumValues?.includes(defaultValue)) {
|
|
5320
|
+
return { expression: JSON.stringify(defaultValue), normalized: false, normalizedType };
|
|
5321
|
+
}
|
|
5322
|
+
return {
|
|
5323
|
+
expression: null,
|
|
5324
|
+
normalized: false,
|
|
5325
|
+
normalizedType,
|
|
5326
|
+
diagnostic: {
|
|
5327
|
+
severity: "error",
|
|
5328
|
+
code: "primitive-default-value-invalid",
|
|
5329
|
+
input: "default",
|
|
5330
|
+
failureScope: "source-change",
|
|
5331
|
+
message: `Default value for enum must be one of: ${(options.enumValues ?? []).join(", ")}. Received: ${JSON.stringify(defaultValue)}.`
|
|
5332
|
+
}
|
|
5333
|
+
};
|
|
5334
|
+
}
|
|
5335
|
+
return {
|
|
5336
|
+
expression: JSON.stringify(String(defaultValue)),
|
|
5337
|
+
normalized: typeof defaultValue !== "string",
|
|
5338
|
+
normalizedType
|
|
5339
|
+
};
|
|
5396
5340
|
};
|
|
5397
|
-
var fieldExpression = (typeName, defaultValue) => {
|
|
5341
|
+
var fieldExpression = (typeName, defaultValue, options = {}) => {
|
|
5398
5342
|
const typeExpression = normalizeFieldType(typeName);
|
|
5399
|
-
const defaultExpression = coerceFieldDefault(typeExpression, defaultValue).expression;
|
|
5343
|
+
const defaultExpression = coerceFieldDefault(options.enumValues ? "enum" : typeExpression, defaultValue, options).expression;
|
|
5400
5344
|
const defaultOption = defaultExpression ? `, { default: ${defaultExpression} }` : "";
|
|
5401
|
-
return
|
|
5345
|
+
return `${options.builderName ?? "field"}(${typeExpression}${defaultOption})`;
|
|
5346
|
+
};
|
|
5347
|
+
var viaBuilderParameterName = (content, className) => {
|
|
5348
|
+
const classIndex = content.indexOf(`export class ${className} extends via`);
|
|
5349
|
+
if (classIndex < 0)
|
|
5350
|
+
return null;
|
|
5351
|
+
const signature = content.slice(classIndex, content.indexOf("=>", classIndex));
|
|
5352
|
+
return /via\(\(\s*([A-Za-z_$][\w$]*)\s*\)/.exec(signature)?.[1] ?? null;
|
|
5402
5353
|
};
|
|
5403
5354
|
var insertIntoObject = (content, className, line) => {
|
|
5404
5355
|
const classIndex = content.indexOf(`export class ${className} extends via`);
|
|
@@ -5416,74 +5367,538 @@ var insertIntoObject = (content, className, line) => {
|
|
|
5416
5367
|
`;
|
|
5417
5368
|
return `${prefix}${insertion}${suffix}`;
|
|
5418
5369
|
};
|
|
5419
|
-
var
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
return
|
|
5370
|
+
var insertLightProjectionField = (content, moduleClassName, fieldName) => {
|
|
5371
|
+
const classIndex = content.indexOf(`export class Light${moduleClassName} extends via`);
|
|
5372
|
+
if (classIndex < 0)
|
|
5373
|
+
return null;
|
|
5374
|
+
const arrayMatch = /\[([\s\S]*?)\]\s+as const/.exec(content.slice(classIndex));
|
|
5375
|
+
if (!arrayMatch || arrayMatch.index === undefined)
|
|
5376
|
+
return null;
|
|
5377
|
+
const arrayStart = classIndex + arrayMatch.index;
|
|
5378
|
+
const arrayEnd = arrayStart + arrayMatch[0].length;
|
|
5379
|
+
const fields = [...arrayMatch[1].matchAll(/"([^"]+)"/g)].map((match) => match[1]).filter(Boolean);
|
|
5380
|
+
if (fields.includes(fieldName))
|
|
5381
|
+
return content;
|
|
5382
|
+
const nextFields = [...fields, fieldName];
|
|
5383
|
+
const nextArray = nextFields.length === 0 ? "[] as const" : `[
|
|
5384
|
+
${nextFields.map((field) => ` ${JSON.stringify(field)},`).join(`
|
|
5385
|
+
`)}
|
|
5386
|
+
] as const`;
|
|
5387
|
+
return `${content.slice(0, arrayStart)}${nextArray}${content.slice(arrayEnd)}`;
|
|
5388
|
+
};
|
|
5389
|
+
var insertTemplateField = ({
|
|
5390
|
+
content,
|
|
5391
|
+
moduleName,
|
|
5392
|
+
moduleClassName,
|
|
5393
|
+
fieldName,
|
|
5394
|
+
component
|
|
5395
|
+
}) => {
|
|
5396
|
+
if (content.includes(`l("${moduleName}.${fieldName}")`) || content.includes(`.${fieldName}`))
|
|
5397
|
+
return content;
|
|
5398
|
+
const layoutEndIndex = content.indexOf(" </Layout.Template>");
|
|
5399
|
+
if (layoutEndIndex < 0)
|
|
5400
|
+
return null;
|
|
5401
|
+
const formName = `${moduleName}Form`;
|
|
5402
|
+
if (!content.includes(`const ${formName} = st.use.${moduleName}Form();`))
|
|
5403
|
+
return null;
|
|
5404
|
+
const fieldSetter = `st.do.set${moduleComponentName(fieldName)}On${moduleClassName}`;
|
|
5405
|
+
const fieldBlock = ` <${component}
|
|
5406
|
+
label={l("${moduleName}.${fieldName}")}
|
|
5407
|
+
desc={l("${moduleName}.${fieldName}.desc")}
|
|
5408
|
+
value={${formName}.${fieldName}}
|
|
5409
|
+
onChange={${fieldSetter}}
|
|
5410
|
+
/>
|
|
5411
|
+
`;
|
|
5412
|
+
return `${content.slice(0, layoutEndIndex)}${fieldBlock}${content.slice(layoutEndIndex)}`;
|
|
5413
|
+
};
|
|
5414
|
+
var ensureEnumImport = (content) => {
|
|
5415
|
+
if (content.includes("enumOf"))
|
|
5416
|
+
return content;
|
|
5417
|
+
const baseImport = /import \{ ([^}]+) \} from "akanjs\/base";/.exec(content);
|
|
5418
|
+
if (baseImport) {
|
|
5419
|
+
const names = baseImport[1]?.split(",").map((name) => name.trim()) ?? [];
|
|
5420
|
+
return content.replace(baseImport[0], `import { ${[...names, "enumOf"].sort().join(", ")} } from "akanjs/base";`);
|
|
5421
|
+
}
|
|
5422
|
+
return `import { enumOf } from "akanjs/base";
|
|
5423
|
+
${content}`;
|
|
5424
|
+
};
|
|
5425
|
+
var insertEnumClass = (content, enumClassName, enumName, values) => {
|
|
5426
|
+
if (content.includes(`export class ${enumClassName} extends enumOf`))
|
|
5427
|
+
return content;
|
|
5428
|
+
const enumClass = `export class ${enumClassName} extends enumOf("${enumName}", [
|
|
5429
|
+
${values.map((value) => ` ${JSON.stringify(value)},`).join(`
|
|
5430
|
+
`)}
|
|
5431
|
+
] as const) {}
|
|
5432
|
+
|
|
5433
|
+
`;
|
|
5434
|
+
const firstClassIndex = content.indexOf("export class ");
|
|
5435
|
+
if (firstClassIndex < 0)
|
|
5436
|
+
return `${content}
|
|
5437
|
+
${enumClass}`;
|
|
5438
|
+
return `${content.slice(0, firstClassIndex)}${enumClass}${content.slice(firstClassIndex)}`;
|
|
5439
|
+
};
|
|
5440
|
+
var findMatchingBrace = (content, openIndex) => {
|
|
5441
|
+
let depth = 0;
|
|
5442
|
+
let quote = null;
|
|
5443
|
+
let escaped = false;
|
|
5444
|
+
for (let index = openIndex;index < content.length; index++) {
|
|
5445
|
+
const char = content[index];
|
|
5446
|
+
if (quote) {
|
|
5447
|
+
if (escaped) {
|
|
5448
|
+
escaped = false;
|
|
5449
|
+
continue;
|
|
5450
|
+
}
|
|
5451
|
+
if (char === "\\") {
|
|
5452
|
+
escaped = true;
|
|
5453
|
+
continue;
|
|
5454
|
+
}
|
|
5455
|
+
if (char === quote)
|
|
5456
|
+
quote = null;
|
|
5457
|
+
continue;
|
|
5458
|
+
}
|
|
5459
|
+
if (char === '"' || char === "'" || char === "`") {
|
|
5460
|
+
quote = char;
|
|
5461
|
+
continue;
|
|
5462
|
+
}
|
|
5463
|
+
if (char === "{")
|
|
5464
|
+
depth += 1;
|
|
5465
|
+
if (char === "}") {
|
|
5466
|
+
depth -= 1;
|
|
5467
|
+
if (depth === 0)
|
|
5468
|
+
return index;
|
|
5469
|
+
}
|
|
5470
|
+
}
|
|
5471
|
+
return -1;
|
|
5472
|
+
};
|
|
5473
|
+
var dictionaryModelFieldLine = (fieldName) => {
|
|
5474
|
+
const label = bilingualLabelForField(fieldName);
|
|
5475
|
+
const desc = bilingualDescriptionForField(fieldName);
|
|
5476
|
+
return `${fieldName}: t([${JSON.stringify(label.en)}, ${JSON.stringify(label.ko)}]).desc([${JSON.stringify(desc.en)}, ${JSON.stringify(desc.ko)}]),`;
|
|
5477
|
+
};
|
|
5478
|
+
var insertDictionaryModelField = (content, moduleClassName, fieldName) => {
|
|
5479
|
+
if (new RegExp(`\\b${fieldName}\\s*:`).test(content))
|
|
5480
|
+
return content;
|
|
5481
|
+
const modelIndex = content.indexOf(`.model<${moduleClassName}>((t) => (`);
|
|
5482
|
+
if (modelIndex < 0)
|
|
5483
|
+
return null;
|
|
5484
|
+
const objectStartIndex = content.indexOf("{", modelIndex);
|
|
5485
|
+
if (objectStartIndex < 0)
|
|
5486
|
+
return null;
|
|
5487
|
+
const objectEndIndex = findMatchingBrace(content, objectStartIndex);
|
|
5488
|
+
if (objectEndIndex < 0)
|
|
5489
|
+
return null;
|
|
5490
|
+
const fieldLine = dictionaryModelFieldLine(fieldName);
|
|
5491
|
+
const body = content.slice(objectStartIndex + 1, objectEndIndex);
|
|
5492
|
+
if (body.trim().length === 0) {
|
|
5493
|
+
return `${content.slice(0, objectStartIndex + 1)}
|
|
5494
|
+
${fieldLine}
|
|
5495
|
+
${content.slice(objectEndIndex)}`;
|
|
5496
|
+
}
|
|
5497
|
+
const insertion = body.endsWith(`
|
|
5498
|
+
`) ? ` ${fieldLine}
|
|
5499
|
+
` : `
|
|
5500
|
+
${fieldLine}
|
|
5501
|
+
`;
|
|
5502
|
+
return `${content.slice(0, objectEndIndex)}${insertion}${content.slice(objectEndIndex)}`;
|
|
5503
|
+
};
|
|
5504
|
+
var ensureConstantTypeImport = (content, constantPath, typeName) => {
|
|
5505
|
+
if (new RegExp(`import type \\{[^}]*\\b${typeName}\\b[^}]*\\} from "${constantPath}";`).test(content))
|
|
5506
|
+
return content;
|
|
5507
|
+
const importPattern = new RegExp(`import type \\{ ([^}]+) \\} from "${constantPath}";`);
|
|
5508
|
+
const existingImport = content.match(importPattern);
|
|
5509
|
+
if (existingImport !== null) {
|
|
5510
|
+
const names = existingImport[1]?.split(",").map((name) => name.trim()) ?? [];
|
|
5511
|
+
return content.replace(existingImport[0], `import type { ${[...names, typeName].sort().join(", ")} } from "${constantPath}";`);
|
|
5512
|
+
}
|
|
5513
|
+
return `import type { ${typeName} } from "${constantPath}";
|
|
5514
|
+
${content}`;
|
|
5515
|
+
};
|
|
5516
|
+
var insertDictionaryEnum = (content, enumClassName, enumName, values) => {
|
|
5517
|
+
if (content.includes(`.enum<${enumClassName}>("${enumName}"`))
|
|
5518
|
+
return content;
|
|
5519
|
+
const enumBlock = ` .enum<${enumClassName}>("${enumName}", (t) => ({
|
|
5520
|
+
${values.map((value) => ` ${value}: t([${JSON.stringify(titleize(value))}, ${JSON.stringify(titleize(value))}]),`).join(`
|
|
5521
|
+
`)}
|
|
5522
|
+
}))
|
|
5523
|
+
`;
|
|
5524
|
+
const chainEndIndex = content.lastIndexOf(";");
|
|
5525
|
+
const insertBeforeIndex = [content.indexOf(".error("), content.indexOf(".translate("), chainEndIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
|
|
5526
|
+
if (insertBeforeIndex === undefined)
|
|
5527
|
+
return null;
|
|
5528
|
+
return `${content.slice(0, insertBeforeIndex)}${enumBlock}${content.slice(insertBeforeIndex)}`;
|
|
5529
|
+
};
|
|
5530
|
+
var parseValues = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
|
|
5531
|
+
|
|
5532
|
+
// pkgs/@akanjs/devkit/workflow/uiPolicy.ts
|
|
5533
|
+
var addFieldUiPolicyForType = (typeName) => {
|
|
5534
|
+
const normalizedType = typeName.toLowerCase() === "enum" ? "enum" : normalizeFieldType(typeName);
|
|
5535
|
+
if (normalizedType === "Int" || normalizedType === "Float") {
|
|
5536
|
+
return {
|
|
5537
|
+
normalizedType,
|
|
5538
|
+
component: "Field.Number",
|
|
5539
|
+
confidence: "high",
|
|
5540
|
+
autoTemplateSupported: true
|
|
5541
|
+
};
|
|
5542
|
+
}
|
|
5543
|
+
if (normalizedType === "Date") {
|
|
5544
|
+
return {
|
|
5545
|
+
normalizedType,
|
|
5546
|
+
component: "Field.Date",
|
|
5547
|
+
confidence: "high",
|
|
5548
|
+
autoTemplateSupported: true
|
|
5549
|
+
};
|
|
5550
|
+
}
|
|
5551
|
+
if (normalizedType === "Boolean" || normalizedType === "enum") {
|
|
5552
|
+
return {
|
|
5553
|
+
normalizedType,
|
|
5554
|
+
component: "Field.ToggleSelect",
|
|
5555
|
+
confidence: "medium",
|
|
5556
|
+
autoTemplateSupported: false
|
|
5557
|
+
};
|
|
5558
|
+
}
|
|
5559
|
+
if (normalizedType === "String") {
|
|
5560
|
+
return {
|
|
5561
|
+
normalizedType,
|
|
5562
|
+
component: "Field.Text",
|
|
5563
|
+
confidence: "high",
|
|
5564
|
+
autoTemplateSupported: true
|
|
5565
|
+
};
|
|
5566
|
+
}
|
|
5567
|
+
return {
|
|
5568
|
+
normalizedType,
|
|
5569
|
+
component: "Field.Text",
|
|
5570
|
+
confidence: "low",
|
|
5571
|
+
autoTemplateSupported: false
|
|
5572
|
+
};
|
|
5573
|
+
};
|
|
5574
|
+
|
|
5575
|
+
// pkgs/@akanjs/devkit/workflow/executor.ts
|
|
5576
|
+
var workflowStepKey = (workflow, stepId) => `${workflow}:${stepId}`;
|
|
5577
|
+
var primitiveReportToWorkflowStepResult = (report) => ({
|
|
5578
|
+
changedFiles: report.changedFiles,
|
|
5579
|
+
generatedFiles: report.generatedFiles,
|
|
5580
|
+
commands: report.validationCommands,
|
|
5581
|
+
diagnostics: report.diagnostics,
|
|
5582
|
+
recommendations: [],
|
|
5583
|
+
nextActions: report.nextActions
|
|
5584
|
+
});
|
|
5585
|
+
var workflowStringInput = (value) => typeof value === "string" ? value : null;
|
|
5586
|
+
var workflowStringListInput = (value) => Array.isArray(value) ? value.join(",") : null;
|
|
5587
|
+
var workflowStringArrayInput = (value) => Array.isArray(value) ? value : null;
|
|
5588
|
+
var workflowBooleanInput = (value) => typeof value === "boolean" ? value : null;
|
|
5589
|
+
var postApplyDiagnostic = (code, message, target) => ({
|
|
5590
|
+
severity: "error",
|
|
5591
|
+
code,
|
|
5592
|
+
message,
|
|
5593
|
+
failureScope: "source-change",
|
|
5594
|
+
context: { target }
|
|
5595
|
+
});
|
|
5596
|
+
var sourceKindForPath = (filePath) => {
|
|
5597
|
+
if (filePath.endsWith(".tsx"))
|
|
5598
|
+
return ts4.ScriptKind.TSX;
|
|
5599
|
+
if (filePath.endsWith(".ts"))
|
|
5600
|
+
return ts4.ScriptKind.TS;
|
|
5601
|
+
return null;
|
|
5602
|
+
};
|
|
5603
|
+
var checkPathCasing = async (workspace, filePath) => {
|
|
5604
|
+
const segments = filePath.split("/").filter(Boolean);
|
|
5605
|
+
let current = ".";
|
|
5606
|
+
for (const segment of segments) {
|
|
5607
|
+
const entries = await workspace.readdir(current);
|
|
5608
|
+
if (entries.includes(segment)) {
|
|
5609
|
+
current = current === "." ? segment : `${current}/${segment}`;
|
|
5610
|
+
continue;
|
|
5611
|
+
}
|
|
5612
|
+
const caseInsensitiveMatch = entries.find((entry) => entry.toLowerCase() === segment.toLowerCase());
|
|
5613
|
+
return caseInsensitiveMatch ? {
|
|
5614
|
+
code: "workflow-path-casing-mismatch",
|
|
5615
|
+
message: `Reported path segment "${segment}" does not match actual casing "${caseInsensitiveMatch}" in ${filePath}.`
|
|
5616
|
+
} : {
|
|
5617
|
+
code: "workflow-path-missing",
|
|
5618
|
+
message: `Reported path does not exist: ${filePath}.`
|
|
5619
|
+
};
|
|
5620
|
+
}
|
|
5621
|
+
return null;
|
|
5622
|
+
};
|
|
5623
|
+
var checkTypeScriptSyntax = async (workspace, filePath) => {
|
|
5624
|
+
const scriptKind = sourceKindForPath(filePath);
|
|
5625
|
+
if (!scriptKind)
|
|
5626
|
+
return null;
|
|
5627
|
+
const content = await workspace.readFile(filePath);
|
|
5628
|
+
const source = ts4.createSourceFile(filePath, content, ts4.ScriptTarget.Latest, true, scriptKind);
|
|
5629
|
+
const diagnostic = source.parseDiagnostics[0];
|
|
5630
|
+
if (!diagnostic)
|
|
5631
|
+
return null;
|
|
5632
|
+
const position = diagnostic.file?.getLineAndCharacterOfPosition(diagnostic.start ?? 0);
|
|
5633
|
+
const location = position ? `:${position.line + 1}:${position.character + 1}` : "";
|
|
5634
|
+
return {
|
|
5635
|
+
code: "workflow-post-apply-syntax-error",
|
|
5636
|
+
message: `Generated source has a TypeScript syntax error at ${filePath}${location}: ${ts4.flattenDiagnosticMessageText(diagnostic.messageText, " ")}`
|
|
5637
|
+
};
|
|
5638
|
+
};
|
|
5639
|
+
var checkChangedFile = async (workspace, file) => {
|
|
5640
|
+
if (file.action === "remove")
|
|
5641
|
+
return { postApplyChecks: [] };
|
|
5642
|
+
const diagnostics = [];
|
|
5643
|
+
const checks = [];
|
|
5644
|
+
const pathIssue = await checkPathCasing(workspace, file.path);
|
|
5645
|
+
if (pathIssue) {
|
|
5646
|
+
diagnostics.push(postApplyDiagnostic(pathIssue.code, pathIssue.message, file.path));
|
|
5647
|
+
checks.push({ ...pathIssue, target: file.path, status: "failed" });
|
|
5648
|
+
return { diagnostics, postApplyChecks: checks };
|
|
5649
|
+
}
|
|
5650
|
+
const syntaxIssue = await checkTypeScriptSyntax(workspace, file.path);
|
|
5651
|
+
if (syntaxIssue) {
|
|
5652
|
+
diagnostics.push(postApplyDiagnostic(syntaxIssue.code, syntaxIssue.message, file.path));
|
|
5653
|
+
checks.push({ ...syntaxIssue, target: file.path, status: "failed" });
|
|
5654
|
+
return { diagnostics, postApplyChecks: checks };
|
|
5655
|
+
}
|
|
5656
|
+
checks.push({
|
|
5657
|
+
code: "workflow-post-apply-file-valid",
|
|
5658
|
+
target: file.path,
|
|
5659
|
+
status: "passed",
|
|
5660
|
+
message: "Changed file exists with exact casing and parses as source when applicable."
|
|
5661
|
+
});
|
|
5662
|
+
return { postApplyChecks: checks };
|
|
5663
|
+
};
|
|
5664
|
+
var checkRecommendationPath = async (workspace, recommendation) => {
|
|
5665
|
+
if (!recommendation.target || recommendation.target.includes("*") || recommendation.target.startsWith("<"))
|
|
5666
|
+
return null;
|
|
5667
|
+
const pathIssue = await checkPathCasing(workspace, recommendation.target);
|
|
5668
|
+
if (!pathIssue)
|
|
5669
|
+
return null;
|
|
5670
|
+
return {
|
|
5671
|
+
severity: "warning",
|
|
5672
|
+
code: "workflow-recommendation-path-unverified",
|
|
5673
|
+
message: `${pathIssue.message} Recommendation "${recommendation.code}" should not be treated as an exact file target until the path is corrected.`,
|
|
5674
|
+
failureScope: "source-change",
|
|
5675
|
+
context: { target: recommendation.target }
|
|
5676
|
+
};
|
|
5677
|
+
};
|
|
5678
|
+
var resolveWorkflowSys = async (workspace, target) => {
|
|
5679
|
+
if (!target)
|
|
5680
|
+
return null;
|
|
5681
|
+
const [apps, libs] = await workspace.getSyss();
|
|
5682
|
+
if (apps.includes(target))
|
|
5683
|
+
return AppExecutor.from(workspace, target);
|
|
5684
|
+
if (libs.includes(target))
|
|
5685
|
+
return LibExecutor.from(workspace, target);
|
|
5686
|
+
return null;
|
|
5687
|
+
};
|
|
5688
|
+
var targetMissing = (input2 = "app") => ({
|
|
5689
|
+
severity: "error",
|
|
5690
|
+
code: "workflow-target-missing",
|
|
5691
|
+
input: input2,
|
|
5692
|
+
message: "Workflow target app or library was not found."
|
|
5693
|
+
});
|
|
5694
|
+
var inputMissing = (input2) => ({
|
|
5695
|
+
severity: "error",
|
|
5696
|
+
code: "workflow-input-missing",
|
|
5697
|
+
input: input2,
|
|
5698
|
+
message: `Workflow input "${input2}" is required for apply.`
|
|
5699
|
+
});
|
|
5700
|
+
var unsupportedInput = (input2, message) => ({
|
|
5701
|
+
severity: "error",
|
|
5702
|
+
code: "workflow-input-unsupported",
|
|
5703
|
+
input: input2,
|
|
5704
|
+
message
|
|
5705
|
+
});
|
|
5706
|
+
var addFieldUiSurfaceInspection = (plan) => {
|
|
5707
|
+
const app = workflowStringInput(plan.inputs.app);
|
|
5708
|
+
const module = workflowStringInput(plan.inputs.module) ?? "<module>";
|
|
5709
|
+
const field = workflowStringInput(plan.inputs.field) ?? "<field>";
|
|
5710
|
+
const typeName = workflowStringInput(plan.inputs.type);
|
|
5711
|
+
const policy = addFieldUiPolicyForType(typeName ?? "String");
|
|
5712
|
+
const surfaces = workflowStringArrayInput(plan.inputs.surfaces);
|
|
5713
|
+
const templateRequested = surfaces?.includes("template") ?? false;
|
|
5714
|
+
const moduleClassName = capitalize2(module);
|
|
5715
|
+
const target = `${app ? `apps/${app}` : "*"}/${moduleSourcePaths(module).template}`;
|
|
5716
|
+
return {
|
|
5717
|
+
recommendations: [
|
|
5718
|
+
{
|
|
5719
|
+
code: "add-field-ui-surface-review",
|
|
5720
|
+
kind: "manual-action",
|
|
5721
|
+
target,
|
|
5722
|
+
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.`,
|
|
5723
|
+
confidence: "medium",
|
|
5724
|
+
message: `Review UI surfaces for ${module}.${field}; recommended component is ${policy.component}, with manual reasons and candidate positions in action.`
|
|
5725
|
+
}
|
|
5726
|
+
],
|
|
5727
|
+
nextActions: [
|
|
5728
|
+
{
|
|
5729
|
+
command: `akan workflow explain ${plan.workflow}`,
|
|
5730
|
+
reason: "Review UI surface guidance before manually editing ambiguous UI files."
|
|
5731
|
+
}
|
|
5732
|
+
]
|
|
5733
|
+
};
|
|
5734
|
+
};
|
|
5735
|
+
var createWorkflowStepRegistry = ({
|
|
5736
|
+
workspace,
|
|
5737
|
+
createModule,
|
|
5738
|
+
createScalar,
|
|
5739
|
+
createUi,
|
|
5740
|
+
addField,
|
|
5741
|
+
addEnumField
|
|
5742
|
+
}) => {
|
|
5743
|
+
const inspect = async () => {
|
|
5744
|
+
return;
|
|
5745
|
+
};
|
|
5746
|
+
const commandOnly = async () => {
|
|
5747
|
+
return;
|
|
5748
|
+
};
|
|
5749
|
+
return {
|
|
5750
|
+
inspectSystem: inspect,
|
|
5751
|
+
inspectModule: inspect,
|
|
5752
|
+
syncTarget: commandOnly,
|
|
5753
|
+
lintTarget: commandOnly,
|
|
5754
|
+
[workflowStepKey("create-module", "create-module")]: async (_step, plan) => {
|
|
5755
|
+
const app = workflowStringInput(plan.inputs.app);
|
|
5756
|
+
const module = workflowStringInput(plan.inputs.module);
|
|
5757
|
+
const sys2 = await resolveWorkflowSys(workspace, app);
|
|
5758
|
+
if (!sys2 || !module)
|
|
5759
|
+
return { diagnostics: [!sys2 ? targetMissing() : inputMissing("module")] };
|
|
5760
|
+
return primitiveReportToWorkflowStepResult(await createModule(sys2, module));
|
|
5761
|
+
},
|
|
5762
|
+
[workflowStepKey("create-scalar", "create-scalar")]: async (_step, plan) => {
|
|
5763
|
+
const app = workflowStringInput(plan.inputs.app);
|
|
5764
|
+
const scalar = workflowStringInput(plan.inputs.scalar);
|
|
5765
|
+
const sys2 = await resolveWorkflowSys(workspace, app);
|
|
5766
|
+
if (!sys2 || !scalar)
|
|
5767
|
+
return { diagnostics: [!sys2 ? targetMissing() : inputMissing("scalar")] };
|
|
5768
|
+
return primitiveReportToWorkflowStepResult(await createScalar(sys2, scalar));
|
|
5769
|
+
},
|
|
5770
|
+
[workflowStepKey("create-ui", "create-ui")]: async (_step, plan) => {
|
|
5771
|
+
const surface = workflowStringInput(plan.inputs.surface);
|
|
5772
|
+
if (surface !== "view" && surface !== "unit" && surface !== "template") {
|
|
5773
|
+
return {
|
|
5774
|
+
diagnostics: [
|
|
5775
|
+
unsupportedInput("surface", "Workflow apply currently supports create-ui surfaces: view, unit, template.")
|
|
5776
|
+
]
|
|
5777
|
+
};
|
|
5778
|
+
}
|
|
5779
|
+
return primitiveReportToWorkflowStepResult(await createUi({
|
|
5780
|
+
app: workflowStringInput(plan.inputs.app),
|
|
5781
|
+
module: workflowStringInput(plan.inputs.module),
|
|
5782
|
+
surface
|
|
5783
|
+
}));
|
|
5784
|
+
},
|
|
5785
|
+
[workflowStepKey("add-field", "update-constant")]: async (_step, plan) => {
|
|
5786
|
+
if (workflowStringInput(plan.inputs.type)?.toLowerCase() === "enum") {
|
|
5787
|
+
return primitiveReportToWorkflowStepResult(await addEnumField({
|
|
5788
|
+
app: workflowStringInput(plan.inputs.app),
|
|
5789
|
+
module: workflowStringInput(plan.inputs.module),
|
|
5790
|
+
field: workflowStringInput(plan.inputs.field),
|
|
5791
|
+
values: workflowStringListInput(plan.inputs.values),
|
|
5792
|
+
defaultValue: workflowStringInput(plan.inputs.default),
|
|
5793
|
+
surfaces: workflowStringArrayInput(plan.inputs.surfaces),
|
|
5794
|
+
includeInLight: workflowBooleanInput(plan.inputs.includeInLight)
|
|
5795
|
+
}));
|
|
5796
|
+
}
|
|
5797
|
+
return primitiveReportToWorkflowStepResult(await addField({
|
|
5798
|
+
app: workflowStringInput(plan.inputs.app),
|
|
5799
|
+
module: workflowStringInput(plan.inputs.module),
|
|
5800
|
+
field: workflowStringInput(plan.inputs.field),
|
|
5801
|
+
type: workflowStringInput(plan.inputs.type),
|
|
5802
|
+
defaultValue: workflowStringInput(plan.inputs.default),
|
|
5803
|
+
surfaces: workflowStringArrayInput(plan.inputs.surfaces),
|
|
5804
|
+
includeInLight: workflowBooleanInput(plan.inputs.includeInLight)
|
|
5805
|
+
}));
|
|
5806
|
+
},
|
|
5807
|
+
[workflowStepKey("add-field", "update-dictionary")]: inspect,
|
|
5808
|
+
[workflowStepKey("add-field", "update-ui-surfaces")]: async (_step, plan) => addFieldUiSurfaceInspection(plan),
|
|
5809
|
+
[workflowStepKey("add-enum-field", "update-constant")]: async (_step, plan) => primitiveReportToWorkflowStepResult(await addEnumField({
|
|
5810
|
+
app: workflowStringInput(plan.inputs.app),
|
|
5811
|
+
module: workflowStringInput(plan.inputs.module),
|
|
5812
|
+
field: workflowStringInput(plan.inputs.field),
|
|
5813
|
+
values: workflowStringListInput(plan.inputs.values),
|
|
5814
|
+
defaultValue: workflowStringInput(plan.inputs.default)
|
|
5815
|
+
})),
|
|
5816
|
+
[workflowStepKey("add-enum-field", "update-dictionary")]: inspect,
|
|
5817
|
+
[workflowStepKey("add-enum-field", "update-option")]: inspect
|
|
5818
|
+
};
|
|
5819
|
+
};
|
|
5820
|
+
class WorkflowExecutor {
|
|
5821
|
+
registry;
|
|
5822
|
+
workspace;
|
|
5823
|
+
constructor(registry, workspace) {
|
|
5824
|
+
this.registry = registry;
|
|
5825
|
+
this.workspace = workspace;
|
|
5426
5826
|
}
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
}
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5827
|
+
async apply(plan) {
|
|
5828
|
+
const changedFiles = [];
|
|
5829
|
+
const generatedFiles = [];
|
|
5830
|
+
const recommendedValidationCommands = [];
|
|
5831
|
+
const diagnostics = [...plan.diagnostics];
|
|
5832
|
+
const postApplyChecks = [];
|
|
5833
|
+
const recommendations = [...plan.recommendations];
|
|
5834
|
+
const nextActions = [];
|
|
5835
|
+
if (diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
5836
|
+
return createWorkflowApplyReport({
|
|
5837
|
+
workflow: plan.workflow,
|
|
5838
|
+
mode: "apply",
|
|
5839
|
+
changedFiles,
|
|
5840
|
+
generatedFiles,
|
|
5841
|
+
recommendedValidationCommands,
|
|
5842
|
+
diagnostics,
|
|
5843
|
+
postApplyChecks,
|
|
5844
|
+
recommendations,
|
|
5845
|
+
nextActions,
|
|
5846
|
+
plan
|
|
5847
|
+
});
|
|
5848
|
+
}
|
|
5849
|
+
recommendedValidationCommands.push(...workflowCommandsForPlan(plan));
|
|
5850
|
+
nextActions.push(...workflowCommandsForPlan(plan));
|
|
5851
|
+
for (const step of plan.steps) {
|
|
5852
|
+
const runner = this.registry[workflowStepKey(plan.workflow, step.id)] ?? this.registry[step.tool] ?? this.registry[step.id];
|
|
5853
|
+
if (!runner) {
|
|
5854
|
+
diagnostics.push({
|
|
5855
|
+
severity: "error",
|
|
5856
|
+
code: "workflow-step-unsupported",
|
|
5857
|
+
message: `Workflow ${plan.workflow} step "${step.id}" is not supported by workflow apply yet.`
|
|
5858
|
+
});
|
|
5859
|
+
nextActions.push({
|
|
5860
|
+
command: `akan workflow explain ${plan.workflow}`,
|
|
5861
|
+
reason: "Review the unsupported workflow step before retrying apply."
|
|
5862
|
+
});
|
|
5863
|
+
break;
|
|
5864
|
+
}
|
|
5865
|
+
const result = await runner(step, plan);
|
|
5866
|
+
if (!result)
|
|
5867
|
+
continue;
|
|
5868
|
+
changedFiles.push(...result.changedFiles ?? []);
|
|
5869
|
+
generatedFiles.push(...result.generatedFiles ?? []);
|
|
5870
|
+
recommendedValidationCommands.push(...result.commands ?? []);
|
|
5871
|
+
diagnostics.push(...result.diagnostics ?? []);
|
|
5872
|
+
recommendations.push(...result.recommendations ?? []);
|
|
5873
|
+
nextActions.push(...result.nextActions ?? []);
|
|
5874
|
+
if (diagnostics.some((diagnostic) => diagnostic.severity === "error"))
|
|
5875
|
+
break;
|
|
5876
|
+
}
|
|
5877
|
+
if (this.workspace && !diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
|
|
5878
|
+
for (const file of changedFiles) {
|
|
5879
|
+
const result = await checkChangedFile(this.workspace, file);
|
|
5880
|
+
postApplyChecks.push(...result.postApplyChecks ?? []);
|
|
5881
|
+
diagnostics.push(...result.diagnostics ?? []);
|
|
5882
|
+
}
|
|
5883
|
+
const recommendationDiagnostics = await Promise.all(recommendations.map((recommendation) => checkRecommendationPath(this.workspace, recommendation)));
|
|
5884
|
+
diagnostics.push(...recommendationDiagnostics.filter((diagnostic) => !!diagnostic));
|
|
5885
|
+
}
|
|
5886
|
+
return createWorkflowApplyReport({
|
|
5887
|
+
workflow: plan.workflow,
|
|
5888
|
+
mode: "apply",
|
|
5889
|
+
changedFiles,
|
|
5890
|
+
generatedFiles,
|
|
5891
|
+
recommendedValidationCommands,
|
|
5892
|
+
diagnostics,
|
|
5893
|
+
postApplyChecks,
|
|
5894
|
+
recommendations,
|
|
5895
|
+
nextActions,
|
|
5896
|
+
plan
|
|
5897
|
+
});
|
|
5466
5898
|
}
|
|
5467
|
-
|
|
5468
|
-
${content}`;
|
|
5469
|
-
};
|
|
5470
|
-
var insertDictionaryEnum = (content, enumClassName, enumName, values) => {
|
|
5471
|
-
if (content.includes(`.enum<${enumClassName}>("${enumName}"`))
|
|
5472
|
-
return content;
|
|
5473
|
-
const enumBlock = ` .enum<${enumClassName}>("${enumName}", (t) => ({
|
|
5474
|
-
${values.map((value) => ` ${value}: t([${JSON.stringify(titleize(value))}, ${JSON.stringify(titleize(value))}]),`).join(`
|
|
5475
|
-
`)}
|
|
5476
|
-
}))
|
|
5477
|
-
`;
|
|
5478
|
-
const chainEndIndex = content.lastIndexOf(";");
|
|
5479
|
-
const insertBeforeIndex = [content.indexOf(".error("), content.indexOf(".translate("), chainEndIndex].filter((index) => index >= 0).sort((a, b) => a - b)[0];
|
|
5480
|
-
if (insertBeforeIndex === undefined)
|
|
5481
|
-
return null;
|
|
5482
|
-
return `${content.slice(0, insertBeforeIndex)}${enumBlock}${content.slice(insertBeforeIndex)}`;
|
|
5483
|
-
};
|
|
5484
|
-
var parseValues = (value) => value?.split(",").map((item) => item.trim()).filter(Boolean) ?? [];
|
|
5485
|
-
|
|
5899
|
+
}
|
|
5486
5900
|
// pkgs/@akanjs/devkit/workflow/plan.ts
|
|
5901
|
+
import { capitalize as capitalize3 } from "akanjs/common";
|
|
5487
5902
|
var surfaceModes = new Set(["infer", "include", "skip"]);
|
|
5488
5903
|
var parseStringList = (value) => {
|
|
5489
5904
|
if (Array.isArray(value)) {
|
|
@@ -5501,6 +5916,18 @@ var normalizeInputValue = (name, spec, value) => {
|
|
|
5501
5916
|
const values = parseStringList(value);
|
|
5502
5917
|
return values && values.length > 0 ? values : null;
|
|
5503
5918
|
}
|
|
5919
|
+
if (spec.type === "boolean") {
|
|
5920
|
+
if (typeof value === "boolean")
|
|
5921
|
+
return value;
|
|
5922
|
+
if (typeof value !== "string")
|
|
5923
|
+
return null;
|
|
5924
|
+
const lowered = value.trim().toLowerCase();
|
|
5925
|
+
if (lowered === "true")
|
|
5926
|
+
return true;
|
|
5927
|
+
if (lowered === "false")
|
|
5928
|
+
return false;
|
|
5929
|
+
return null;
|
|
5930
|
+
}
|
|
5504
5931
|
if (typeof value === "string" && surfaceModes.has(value))
|
|
5505
5932
|
return value;
|
|
5506
5933
|
throw new Error(`Unsupported workflow input value for ${name}`);
|
|
@@ -5508,17 +5935,45 @@ var normalizeInputValue = (name, spec, value) => {
|
|
|
5508
5935
|
var listWorkflowSpecs = (specs) => [...specs].sort((a, b) => a.name.localeCompare(b.name));
|
|
5509
5936
|
var getWorkflowSpec = (specs, name) => specs.find((spec) => spec.name === name) ?? null;
|
|
5510
5937
|
var compactWorkflowInputs = (inputs) => Object.fromEntries(Object.entries(inputs).filter(([, value]) => value !== null && value !== ""));
|
|
5511
|
-
var
|
|
5512
|
-
|
|
5513
|
-
|
|
5514
|
-
|
|
5515
|
-
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5938
|
+
var addFieldTargetRoot = (app) => app ? `apps/${app}` : "*";
|
|
5939
|
+
var addFieldPaths = (inputs) => {
|
|
5940
|
+
const app = typeof inputs.app === "string" ? inputs.app : null;
|
|
5941
|
+
const module = typeof inputs.module === "string" ? inputs.module : "<module>";
|
|
5942
|
+
const paths = moduleSourcePaths(module);
|
|
5943
|
+
const root = addFieldTargetRoot(app);
|
|
5944
|
+
return {
|
|
5945
|
+
constant: `${root}/${paths.constant}`,
|
|
5946
|
+
dictionary: `${root}/${paths.dictionary}`,
|
|
5947
|
+
template: `${root}/${paths.template}`,
|
|
5948
|
+
unit: `${root}/${paths.unit}`,
|
|
5949
|
+
view: `${root}/${paths.view}`
|
|
5950
|
+
};
|
|
5951
|
+
};
|
|
5952
|
+
var selectedAddFieldSurfaces = (inputs) => Array.isArray(inputs.surfaces) ? new Set(inputs.surfaces) : null;
|
|
5953
|
+
var addFieldDefaultCoercion = (inputs) => {
|
|
5954
|
+
const typeName = typeof inputs.type === "string" ? inputs.type : null;
|
|
5955
|
+
const defaultValue = typeof inputs.default === "string" ? inputs.default : null;
|
|
5956
|
+
if (!typeName || defaultValue === null)
|
|
5957
|
+
return null;
|
|
5958
|
+
if (typeName.toLowerCase() === "number" || typeName.toLowerCase() === "numeric")
|
|
5959
|
+
return null;
|
|
5960
|
+
const enumValues = Array.isArray(inputs.values) ? inputs.values : null;
|
|
5961
|
+
return coerceFieldDefault(typeName.toLowerCase() === "enum" ? "enum" : typeName, defaultValue, { enumValues });
|
|
5962
|
+
};
|
|
5963
|
+
var createAddFieldDefaultRecommendations = (inputs) => {
|
|
5964
|
+
const field = typeof inputs.field === "string" ? inputs.field : "<field>";
|
|
5965
|
+
const coercion = addFieldDefaultCoercion(inputs);
|
|
5966
|
+
if (!coercion?.normalized || !coercion.expression)
|
|
5967
|
+
return [];
|
|
5968
|
+
return [
|
|
5969
|
+
{
|
|
5970
|
+
code: "add-field-default-normalized",
|
|
5971
|
+
kind: "manual-action",
|
|
5972
|
+
confidence: "high",
|
|
5973
|
+
message: `Default for ${field} will be normalized to ${coercion.normalizedType} literal ${coercion.expression}.`,
|
|
5974
|
+
action: "Review the normalized default in the apply report if this value should stay unset."
|
|
5975
|
+
}
|
|
5976
|
+
];
|
|
5522
5977
|
};
|
|
5523
5978
|
var createAddFieldRecommendations = (inputs) => {
|
|
5524
5979
|
const app = typeof inputs.app === "string" ? inputs.app : "<app-or-lib>";
|
|
@@ -5527,16 +5982,18 @@ var createAddFieldRecommendations = (inputs) => {
|
|
|
5527
5982
|
const typeName = typeof inputs.type === "string" ? inputs.type : null;
|
|
5528
5983
|
if (!typeName)
|
|
5529
5984
|
return [];
|
|
5530
|
-
const
|
|
5531
|
-
const
|
|
5532
|
-
const
|
|
5533
|
-
const
|
|
5985
|
+
const policy = addFieldUiPolicyForType(typeName);
|
|
5986
|
+
const normalizedType = policy.normalizedType;
|
|
5987
|
+
const paths = addFieldPaths(inputs);
|
|
5988
|
+
const surfaces = selectedAddFieldSurfaces(inputs);
|
|
5989
|
+
const templateRequested = surfaces?.has("template") ?? false;
|
|
5990
|
+
const includeInLight = inputs.includeInLight === true;
|
|
5534
5991
|
return [
|
|
5535
5992
|
...normalizedType === "Int" || normalizedType === "Float" ? [
|
|
5536
5993
|
{
|
|
5537
5994
|
code: "add-field-import",
|
|
5538
5995
|
kind: "import",
|
|
5539
|
-
target:
|
|
5996
|
+
target: paths.constant,
|
|
5540
5997
|
confidence: "high",
|
|
5541
5998
|
message: `Import ${normalizedType} from "akanjs/base" before writing field(${normalizedType}).`
|
|
5542
5999
|
}
|
|
@@ -5544,35 +6001,122 @@ var createAddFieldRecommendations = (inputs) => {
|
|
|
5544
6001
|
{
|
|
5545
6002
|
code: "add-field-placement-constant",
|
|
5546
6003
|
kind: "placement",
|
|
5547
|
-
target:
|
|
6004
|
+
target: paths.constant,
|
|
5548
6005
|
confidence: "high",
|
|
5549
6006
|
message: `Insert ${field}: field(${normalizedType}) in ${capitalize3(module)}Input.`
|
|
5550
6007
|
},
|
|
5551
6008
|
{
|
|
5552
6009
|
code: "add-field-placement-dictionary",
|
|
5553
6010
|
kind: "placement",
|
|
5554
|
-
target:
|
|
6011
|
+
target: paths.dictionary,
|
|
5555
6012
|
confidence: "high",
|
|
5556
6013
|
message: `Add dictionary labels for ${module}.${field}.`
|
|
5557
6014
|
},
|
|
5558
6015
|
{
|
|
5559
6016
|
code: "add-field-component",
|
|
5560
6017
|
kind: "ui-component",
|
|
5561
|
-
target:
|
|
5562
|
-
confidence:
|
|
5563
|
-
action:
|
|
5564
|
-
message:
|
|
6018
|
+
target: paths.template,
|
|
6019
|
+
confidence: policy.confidence,
|
|
6020
|
+
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.`,
|
|
6021
|
+
message: `Recommended UI component for ${field} (${normalizedType}): ${policy.component}.`
|
|
5565
6022
|
},
|
|
6023
|
+
...!surfaces ? [
|
|
6024
|
+
{
|
|
6025
|
+
code: "add-field-template-surface-choice",
|
|
6026
|
+
kind: "manual-action",
|
|
6027
|
+
target: paths.template,
|
|
6028
|
+
action: `Choose whether to expose ${field} in Template by passing surfaces=["template"].`,
|
|
6029
|
+
confidence: "medium",
|
|
6030
|
+
message: `Template exposure for ${module}.${field} is not selected yet.`
|
|
6031
|
+
}
|
|
6032
|
+
] : [],
|
|
6033
|
+
...!includeInLight ? [
|
|
6034
|
+
{
|
|
6035
|
+
code: "add-field-light-projection-choice",
|
|
6036
|
+
kind: "manual-action",
|
|
6037
|
+
target: paths.constant,
|
|
6038
|
+
action: `Pass includeInLight=true when ${field} should appear in Light${capitalize3(module)} list/card projections.`,
|
|
6039
|
+
confidence: "medium",
|
|
6040
|
+
message: `Light projection exposure for ${module}.${field} is not selected yet.`
|
|
6041
|
+
}
|
|
6042
|
+
] : [],
|
|
6043
|
+
...includeInLight ? [
|
|
6044
|
+
{
|
|
6045
|
+
code: "add-field-light-projection",
|
|
6046
|
+
kind: "placement",
|
|
6047
|
+
target: paths.constant,
|
|
6048
|
+
confidence: "high",
|
|
6049
|
+
message: `Add ${field} to Light${capitalize3(module)} projection fields.`
|
|
6050
|
+
}
|
|
6051
|
+
] : [],
|
|
6052
|
+
...surfaces && !templateRequested ? [
|
|
6053
|
+
{
|
|
6054
|
+
code: "add-field-ui-surface-skip",
|
|
6055
|
+
kind: "manual-action",
|
|
6056
|
+
target: paths.template,
|
|
6057
|
+
confidence: "medium",
|
|
6058
|
+
message: `Template is not included in surfaces, so ${module}.${field} will not be auto-rendered there.`
|
|
6059
|
+
}
|
|
6060
|
+
] : [],
|
|
5566
6061
|
{
|
|
5567
6062
|
code: "add-field-ui-manual-review",
|
|
5568
6063
|
kind: "manual-action",
|
|
5569
|
-
target:
|
|
5570
|
-
action: `Review ${app}:${module} Template
|
|
6064
|
+
target: paths.template,
|
|
6065
|
+
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.`,
|
|
5571
6066
|
confidence: "medium",
|
|
5572
|
-
message:
|
|
6067
|
+
message: "UI manual review includes the reason auto-edit may be skipped and candidate insertion positions."
|
|
6068
|
+
},
|
|
6069
|
+
...createAddFieldDefaultRecommendations(inputs)
|
|
6070
|
+
];
|
|
6071
|
+
};
|
|
6072
|
+
var createWorkflowPlanPredictedChanges = (spec, inputs) => {
|
|
6073
|
+
if (spec.name !== "add-field")
|
|
6074
|
+
return spec.predictedChanges;
|
|
6075
|
+
const paths = addFieldPaths(inputs);
|
|
6076
|
+
const surfaces = selectedAddFieldSurfaces(inputs);
|
|
6077
|
+
const includeInLight = inputs.includeInLight === true;
|
|
6078
|
+
return [
|
|
6079
|
+
{
|
|
6080
|
+
target: paths.constant,
|
|
6081
|
+
action: "modify",
|
|
6082
|
+
applyScope: "auto",
|
|
6083
|
+
reason: includeInLight ? "Field shape and Light projection are added." : "Field shape is added."
|
|
6084
|
+
},
|
|
6085
|
+
{
|
|
6086
|
+
target: paths.dictionary,
|
|
6087
|
+
action: "modify",
|
|
6088
|
+
applyScope: "auto",
|
|
6089
|
+
reason: "Field label is added."
|
|
6090
|
+
},
|
|
6091
|
+
...!surfaces || surfaces.has("template") ? [
|
|
6092
|
+
{
|
|
6093
|
+
target: paths.template,
|
|
6094
|
+
action: "modify",
|
|
6095
|
+
applyScope: surfaces?.has("template") ? "auto" : "manual-review",
|
|
6096
|
+
reason: surfaces?.has("template") ? "Template form is selected for safe-pattern auto insertion." : "Form surface may include the field."
|
|
6097
|
+
}
|
|
6098
|
+
] : [],
|
|
6099
|
+
{
|
|
6100
|
+
target: `${addFieldTargetRoot(typeof inputs.app === "string" ? inputs.app : null)}/lib/cnst.ts`,
|
|
6101
|
+
action: "sync",
|
|
6102
|
+
applyScope: "generated-sync",
|
|
6103
|
+
reason: "Generated constants may change after sync."
|
|
6104
|
+
},
|
|
6105
|
+
{
|
|
6106
|
+
target: `${addFieldTargetRoot(typeof inputs.app === "string" ? inputs.app : null)}/lib/dict.ts`,
|
|
6107
|
+
action: "sync",
|
|
6108
|
+
applyScope: "generated-sync",
|
|
6109
|
+
reason: "Generated dictionary barrel may change after sync."
|
|
5573
6110
|
}
|
|
5574
6111
|
];
|
|
5575
6112
|
};
|
|
6113
|
+
var createWorkflowPlanOptionalSurfaces = (spec, inputs) => {
|
|
6114
|
+
const optionalSurfaces = spec.optionalSurfaces ?? {};
|
|
6115
|
+
const surfaces = selectedAddFieldSurfaces(inputs);
|
|
6116
|
+
if (spec.name !== "add-field" || !surfaces)
|
|
6117
|
+
return optionalSurfaces;
|
|
6118
|
+
return Object.fromEntries(Object.entries(optionalSurfaces).map(([surface, mode]) => [surface, surfaces.has(surface) ? "include" : mode]));
|
|
6119
|
+
};
|
|
5576
6120
|
var createWorkflowPlanRecommendations = (spec, inputs) => [
|
|
5577
6121
|
{
|
|
5578
6122
|
code: "workflow-apply-first",
|
|
@@ -5636,14 +6180,32 @@ var createWorkflowPlan = (spec, rawInputs) => {
|
|
|
5636
6180
|
message: `Field type "${fieldType}" is ambiguous in Akan. Use Int for integer fields or Float for decimal fields.`
|
|
5637
6181
|
});
|
|
5638
6182
|
}
|
|
6183
|
+
if (spec.name === "add-field") {
|
|
6184
|
+
const defaultCoercion = addFieldDefaultCoercion(inputs);
|
|
6185
|
+
if (defaultCoercion?.diagnostic) {
|
|
6186
|
+
diagnostics.push({
|
|
6187
|
+
...defaultCoercion.diagnostic,
|
|
6188
|
+
code: "workflow-default-value-invalid",
|
|
6189
|
+
message: `Plan input default is not valid for ${defaultCoercion.normalizedType}: ${defaultCoercion.diagnostic.message}`
|
|
6190
|
+
});
|
|
6191
|
+
} else if (defaultCoercion?.normalized && defaultCoercion.expression) {
|
|
6192
|
+
diagnostics.push({
|
|
6193
|
+
severity: "warning",
|
|
6194
|
+
code: "workflow-default-value-normalized",
|
|
6195
|
+
input: "default",
|
|
6196
|
+
failureScope: "source-change",
|
|
6197
|
+
message: `Plan input default will be written as ${defaultCoercion.normalizedType} literal ${defaultCoercion.expression}.`
|
|
6198
|
+
});
|
|
6199
|
+
}
|
|
6200
|
+
}
|
|
5639
6201
|
return {
|
|
5640
6202
|
schemaVersion: 1,
|
|
5641
6203
|
workflow: spec.name,
|
|
5642
6204
|
mode: "plan",
|
|
5643
6205
|
inputs,
|
|
5644
|
-
optionalSurfaces: spec
|
|
6206
|
+
optionalSurfaces: createWorkflowPlanOptionalSurfaces(spec, inputs),
|
|
5645
6207
|
steps: spec.steps,
|
|
5646
|
-
predictedChanges: spec
|
|
6208
|
+
predictedChanges: createWorkflowPlanPredictedChanges(spec, inputs),
|
|
5647
6209
|
validation: spec.validation,
|
|
5648
6210
|
diagnostics,
|
|
5649
6211
|
recommendations: createWorkflowPlanRecommendations(spec, inputs),
|
|
@@ -5712,35 +6274,65 @@ var renderWorkflowPlan = (plan) => [
|
|
|
5712
6274
|
""
|
|
5713
6275
|
].join(`
|
|
5714
6276
|
`);
|
|
5715
|
-
var
|
|
5716
|
-
|
|
5717
|
-
""
|
|
5718
|
-
`-
|
|
5719
|
-
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
"
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
5733
|
-
|
|
5734
|
-
|
|
5735
|
-
|
|
5736
|
-
|
|
5737
|
-
|
|
5738
|
-
|
|
5739
|
-
|
|
5740
|
-
|
|
5741
|
-
|
|
5742
|
-
]
|
|
6277
|
+
var renderRecommendation = (recommendation) => {
|
|
6278
|
+
const target = recommendation.target ? ` ${recommendation.target}` : "";
|
|
6279
|
+
const action = recommendation.action ? ` Action: ${recommendation.action}` : "";
|
|
6280
|
+
return `- [${recommendation.kind}]${target} ${recommendation.message}${action}`;
|
|
6281
|
+
};
|
|
6282
|
+
var renderDiagnostic = (diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`;
|
|
6283
|
+
var applySourceStatus = (report) => report.diagnostics.some((diagnostic) => diagnostic.severity === "error" && (diagnostic.failureScope === "source-change" || !diagnostic.failureScope || diagnostic.failureScope === "unknown")) ? "failed" : "passed";
|
|
6284
|
+
var renderWorkflowApplyReport = (report) => {
|
|
6285
|
+
const manualReviewItems = [
|
|
6286
|
+
...report.diagnostics.filter((diagnostic) => diagnostic.severity === "warning").map(renderDiagnostic),
|
|
6287
|
+
...report.recommendations.filter((recommendation) => recommendation.kind === "manual-action").map(renderRecommendation)
|
|
6288
|
+
];
|
|
6289
|
+
const validationBlockers = report.diagnostics.filter((diagnostic) => diagnostic.severity === "error" && (diagnostic.failureScope === "workspace-config" || diagnostic.failureScope === "environment")).map(renderDiagnostic);
|
|
6290
|
+
const sourceBlockers = report.diagnostics.filter((diagnostic) => diagnostic.severity === "error" && (diagnostic.failureScope === "source-change" || !diagnostic.failureScope || diagnostic.failureScope === "unknown")).map(renderDiagnostic);
|
|
6291
|
+
return [
|
|
6292
|
+
`# Workflow Apply: ${report.workflow}`,
|
|
6293
|
+
"",
|
|
6294
|
+
`- Mode: ${report.mode}`,
|
|
6295
|
+
`- Status: ${report.status}`,
|
|
6296
|
+
`- Source-change status: ${applySourceStatus(report)}`,
|
|
6297
|
+
`- Workspace status: ${validationBlockers.length ? "blocked" : "not blocked during apply"}`,
|
|
6298
|
+
...report.validationTarget ? [`- Validation target: ${report.validationTarget}`] : [],
|
|
6299
|
+
"",
|
|
6300
|
+
"## Apply Checks",
|
|
6301
|
+
...report.postApplyChecks?.length ? report.postApplyChecks.map((check) => `- [${check.status}] ${check.code} ${check.target}: ${check.message}`) : ["- not run"],
|
|
6302
|
+
"",
|
|
6303
|
+
"## Source Change Blockers",
|
|
6304
|
+
...sourceBlockers.length ? sourceBlockers : ["- none"],
|
|
6305
|
+
"",
|
|
6306
|
+
"## Automatically Modified",
|
|
6307
|
+
...report.changedFiles.length ? report.changedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
|
|
6308
|
+
"",
|
|
6309
|
+
"## Generated Sync",
|
|
6310
|
+
...report.generatedFiles.length ? report.generatedFiles.map((file) => `- \`${file.action}\` ${file.path}: ${file.reason}`) : ["- none"],
|
|
6311
|
+
"",
|
|
6312
|
+
"## Applied Commands",
|
|
6313
|
+
...report.appliedCommands.length ? report.appliedCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
|
|
6314
|
+
"",
|
|
6315
|
+
"## Recommended Validation Commands",
|
|
6316
|
+
...report.recommendedValidationCommands.length ? report.recommendedValidationCommands.map((command) => `- \`${command.command}\`: ${command.reason}`) : ["- none"],
|
|
6317
|
+
"",
|
|
6318
|
+
"## User Review Required",
|
|
6319
|
+
...manualReviewItems.length ? manualReviewItems : ["- none"],
|
|
6320
|
+
"",
|
|
6321
|
+
"## Validation Blockers",
|
|
6322
|
+
...validationBlockers.length ? validationBlockers : report.recommendedValidationCommands.length ? ["- none detected during apply; run validation with the validation target for command-level blockers."] : ["- none"],
|
|
6323
|
+
"",
|
|
6324
|
+
"## Diagnostics",
|
|
6325
|
+
...report.diagnostics.length ? report.diagnostics.map(renderDiagnostic) : ["- none"],
|
|
6326
|
+
"",
|
|
6327
|
+
"## Recommendations",
|
|
6328
|
+
...report.recommendations.length ? report.recommendations.slice(0, 3).map(renderRecommendation) : ["- none"],
|
|
6329
|
+
"",
|
|
6330
|
+
"## Next Actions",
|
|
6331
|
+
...report.nextActions.length ? report.nextActions.map((action) => `- \`${action.command}\`: ${action.reason}`) : ["- none"],
|
|
6332
|
+
""
|
|
6333
|
+
].join(`
|
|
5743
6334
|
`);
|
|
6335
|
+
};
|
|
5744
6336
|
var renderWorkflowApply = (report, format = "markdown") => format === "json" ? jsonText(report) : renderWorkflowApplyReport(report);
|
|
5745
6337
|
var renderWorkflowValidationRunReport = (report) => [
|
|
5746
6338
|
`# Workflow Validation: ${report.workflow}`,
|
|
@@ -5751,11 +6343,24 @@ var renderWorkflowValidationRunReport = (report) => [
|
|
|
5751
6343
|
`- Workspace status: ${report.workspaceStatus}`,
|
|
5752
6344
|
`- Overall status: ${report.overallStatus}`,
|
|
5753
6345
|
"",
|
|
6346
|
+
"## Status Summary",
|
|
6347
|
+
`- Source-change: ${report.summary.sourceChange}`,
|
|
6348
|
+
`- Generated sync: ${report.summary.generatedSync}`,
|
|
6349
|
+
`- Workspace config: ${report.summary.workspaceConfig}`,
|
|
6350
|
+
`- Environment: ${report.summary.environment}`,
|
|
6351
|
+
"",
|
|
6352
|
+
"## Source Change Diagnostics",
|
|
6353
|
+
...report.workflowDiagnostics?.length ? report.workflowDiagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
6354
|
+
"",
|
|
6355
|
+
"## Existing Workspace Blockers",
|
|
6356
|
+
...report.baselineDiagnostics?.length ? report.baselineDiagnostics.map((diagnostic) => `- [${diagnostic.severity}] ${diagnostic.code}: ${diagnostic.message}`) : ["- none"],
|
|
6357
|
+
"",
|
|
5754
6358
|
"## Known Blockers",
|
|
5755
6359
|
...report.knownBlockers.length ? report.knownBlockers.map((blocker) => {
|
|
5756
6360
|
const command = blocker.command ? ` \`${blocker.command}\`` : "";
|
|
5757
6361
|
const count = blocker.count > 1 ? ` (${blocker.count}x)` : "";
|
|
5758
|
-
|
|
6362
|
+
const known = blocker.known ? " known" : "";
|
|
6363
|
+
return `- [${blocker.failureScope}${known}]${command}${count}: ${blocker.message}`;
|
|
5759
6364
|
}) : ["- none"],
|
|
5760
6365
|
"",
|
|
5761
6366
|
"## Commands",
|
|
@@ -6824,7 +7429,7 @@ var collapseIndex = (relPathNoExt) => {
|
|
|
6824
7429
|
|
|
6825
7430
|
// pkgs/@akanjs/devkit/transforms/barrelImportsPlugin.ts
|
|
6826
7431
|
import path14 from "path";
|
|
6827
|
-
import
|
|
7432
|
+
import ts5 from "typescript";
|
|
6828
7433
|
var createBarrelImportsPlugin = async (app, { skipPath = defaultSkipPath, pipeAfter } = {}) => {
|
|
6829
7434
|
const akanConfig2 = await app.getConfig();
|
|
6830
7435
|
const barrels = [...new Set(akanConfig2.barrelImports)].filter(Boolean);
|
|
@@ -7065,11 +7670,11 @@ var rewriteBarrelImports = async (source2, barrels, analyzer) => {
|
|
|
7065
7670
|
};
|
|
7066
7671
|
var findImportStatements = (source2) => {
|
|
7067
7672
|
const statements = [];
|
|
7068
|
-
const sourceFile2 =
|
|
7673
|
+
const sourceFile2 = ts5.createSourceFile("barrel-imports.tsx", source2, ts5.ScriptTarget.Latest, true, ts5.ScriptKind.TSX);
|
|
7069
7674
|
for (const statement of sourceFile2.statements) {
|
|
7070
|
-
if (!
|
|
7675
|
+
if (!ts5.isImportDeclaration(statement))
|
|
7071
7676
|
continue;
|
|
7072
|
-
if (!
|
|
7677
|
+
if (!ts5.isStringLiteral(statement.moduleSpecifier))
|
|
7073
7678
|
continue;
|
|
7074
7679
|
const importClause = statement.importClause;
|
|
7075
7680
|
if (!importClause)
|
|
@@ -8064,7 +8669,7 @@ import path22 from "path";
|
|
|
8064
8669
|
// pkgs/@akanjs/devkit/frontendBuild/pagesEntrySourceGenerator.ts
|
|
8065
8670
|
import fs3 from "fs";
|
|
8066
8671
|
import path21 from "path";
|
|
8067
|
-
import
|
|
8672
|
+
import ts6 from "typescript";
|
|
8068
8673
|
|
|
8069
8674
|
class PagesEntrySourceGenerator {
|
|
8070
8675
|
#pageEntries;
|
|
@@ -8108,7 +8713,7 @@ ${entries.join(`
|
|
|
8108
8713
|
static #hasAsyncDefaultExport(moduleAbsPath) {
|
|
8109
8714
|
try {
|
|
8110
8715
|
const source2 = fs3.readFileSync(path21.resolve(moduleAbsPath), "utf8");
|
|
8111
|
-
const sourceFile2 =
|
|
8716
|
+
const sourceFile2 = ts6.createSourceFile(moduleAbsPath, source2, ts6.ScriptTarget.Latest, true, PagesEntrySourceGenerator.#scriptKind(moduleAbsPath));
|
|
8112
8717
|
return PagesEntrySourceGenerator.#sourceFileHasAsyncDefaultExport(sourceFile2);
|
|
8113
8718
|
} catch {
|
|
8114
8719
|
return false;
|
|
@@ -8118,31 +8723,31 @@ ${entries.join(`
|
|
|
8118
8723
|
const asyncBindings = new Map;
|
|
8119
8724
|
let defaultIdentifier = null;
|
|
8120
8725
|
for (const statement of sourceFile2.statements) {
|
|
8121
|
-
if (
|
|
8122
|
-
if (PagesEntrySourceGenerator.#hasModifier(statement,
|
|
8123
|
-
return PagesEntrySourceGenerator.#hasModifier(statement,
|
|
8726
|
+
if (ts6.isFunctionDeclaration(statement)) {
|
|
8727
|
+
if (PagesEntrySourceGenerator.#hasModifier(statement, ts6.SyntaxKind.DefaultKeyword)) {
|
|
8728
|
+
return PagesEntrySourceGenerator.#hasModifier(statement, ts6.SyntaxKind.AsyncKeyword);
|
|
8124
8729
|
}
|
|
8125
8730
|
if (statement.name) {
|
|
8126
|
-
asyncBindings.set(statement.name.text, PagesEntrySourceGenerator.#hasModifier(statement,
|
|
8731
|
+
asyncBindings.set(statement.name.text, PagesEntrySourceGenerator.#hasModifier(statement, ts6.SyntaxKind.AsyncKeyword));
|
|
8127
8732
|
}
|
|
8128
8733
|
continue;
|
|
8129
8734
|
}
|
|
8130
|
-
if (
|
|
8735
|
+
if (ts6.isVariableStatement(statement)) {
|
|
8131
8736
|
for (const declaration of statement.declarationList.declarations) {
|
|
8132
|
-
if (!
|
|
8737
|
+
if (!ts6.isIdentifier(declaration.name))
|
|
8133
8738
|
continue;
|
|
8134
8739
|
asyncBindings.set(declaration.name.text, PagesEntrySourceGenerator.#isAsyncFunctionExpression(declaration.initializer));
|
|
8135
8740
|
}
|
|
8136
8741
|
continue;
|
|
8137
8742
|
}
|
|
8138
|
-
if (
|
|
8743
|
+
if (ts6.isExportAssignment(statement)) {
|
|
8139
8744
|
if (PagesEntrySourceGenerator.#isAsyncFunctionExpression(statement.expression))
|
|
8140
8745
|
return true;
|
|
8141
|
-
if (
|
|
8746
|
+
if (ts6.isIdentifier(statement.expression))
|
|
8142
8747
|
defaultIdentifier = statement.expression.text;
|
|
8143
8748
|
continue;
|
|
8144
8749
|
}
|
|
8145
|
-
if (
|
|
8750
|
+
if (ts6.isExportDeclaration(statement) && statement.exportClause && ts6.isNamedExports(statement.exportClause)) {
|
|
8146
8751
|
const exportClause = statement.exportClause;
|
|
8147
8752
|
for (const specifier of exportClause.elements) {
|
|
8148
8753
|
if (specifier.name.text !== "default")
|
|
@@ -8154,13 +8759,13 @@ ${entries.join(`
|
|
|
8154
8759
|
return defaultIdentifier ? asyncBindings.get(defaultIdentifier) === true : false;
|
|
8155
8760
|
}
|
|
8156
8761
|
static #hasModifier(node, kind) {
|
|
8157
|
-
return
|
|
8762
|
+
return ts6.canHaveModifiers(node) && (ts6.getModifiers(node)?.some((modifier) => modifier.kind === kind) ?? false);
|
|
8158
8763
|
}
|
|
8159
8764
|
static #isAsyncFunctionExpression(node) {
|
|
8160
|
-
return Boolean(node && (
|
|
8765
|
+
return Boolean(node && (ts6.isArrowFunction(node) || ts6.isFunctionExpression(node)) && PagesEntrySourceGenerator.#hasModifier(node, ts6.SyntaxKind.AsyncKeyword));
|
|
8161
8766
|
}
|
|
8162
8767
|
static #scriptKind(moduleAbsPath) {
|
|
8163
|
-
return moduleAbsPath.endsWith(".tsx") || moduleAbsPath.endsWith(".jsx") ?
|
|
8768
|
+
return moduleAbsPath.endsWith(".tsx") || moduleAbsPath.endsWith(".jsx") ? ts6.ScriptKind.TSX : ts6.ScriptKind.TS;
|
|
8164
8769
|
}
|
|
8165
8770
|
}
|
|
8166
8771
|
|
|
@@ -9079,7 +9684,7 @@ import {
|
|
|
9079
9684
|
} from "fontaine";
|
|
9080
9685
|
import { createFont, woff2 } from "fonteditor-core";
|
|
9081
9686
|
import subsetFont from "subset-font";
|
|
9082
|
-
import
|
|
9687
|
+
import ts7 from "typescript";
|
|
9083
9688
|
var FONT_URL_PREFIX = "/_akan/fonts";
|
|
9084
9689
|
var DEFAULT_FONT_SUBSETS = ["latin"];
|
|
9085
9690
|
|
|
@@ -9144,17 +9749,17 @@ class FontOptimizer {
|
|
|
9144
9749
|
this.#cssParts.push(...faceCss, this.#buildRootVariableRule(font));
|
|
9145
9750
|
}
|
|
9146
9751
|
#extractFontsExport(source2, filePath) {
|
|
9147
|
-
const sourceFile2 =
|
|
9752
|
+
const sourceFile2 = ts7.createSourceFile(filePath, source2, ts7.ScriptTarget.Latest, true, ts7.ScriptKind.TSX);
|
|
9148
9753
|
const fonts = [];
|
|
9149
9754
|
for (const statement of sourceFile2.statements) {
|
|
9150
|
-
if (!
|
|
9755
|
+
if (!ts7.isVariableStatement(statement))
|
|
9151
9756
|
continue;
|
|
9152
|
-
const modifiers =
|
|
9153
|
-
const isExported = modifiers?.some((modifier) => modifier.kind ===
|
|
9757
|
+
const modifiers = ts7.canHaveModifiers(statement) ? ts7.getModifiers(statement) : undefined;
|
|
9758
|
+
const isExported = modifiers?.some((modifier) => modifier.kind === ts7.SyntaxKind.ExportKeyword) ?? false;
|
|
9154
9759
|
if (!isExported)
|
|
9155
9760
|
continue;
|
|
9156
9761
|
for (const declaration of statement.declarationList.declarations) {
|
|
9157
|
-
if (!
|
|
9762
|
+
if (!ts7.isIdentifier(declaration.name) || declaration.name.text !== "fonts")
|
|
9158
9763
|
continue;
|
|
9159
9764
|
const value = declaration.initializer ? this.#literalToValue(declaration.initializer) : null;
|
|
9160
9765
|
if (Array.isArray(value)) {
|
|
@@ -9165,20 +9770,20 @@ class FontOptimizer {
|
|
|
9165
9770
|
return fonts;
|
|
9166
9771
|
}
|
|
9167
9772
|
#literalToValue(node) {
|
|
9168
|
-
if (
|
|
9773
|
+
if (ts7.isStringLiteralLike(node))
|
|
9169
9774
|
return node.text;
|
|
9170
|
-
if (
|
|
9775
|
+
if (ts7.isNumericLiteral(node))
|
|
9171
9776
|
return Number(node.text);
|
|
9172
|
-
if (node.kind ===
|
|
9777
|
+
if (node.kind === ts7.SyntaxKind.TrueKeyword)
|
|
9173
9778
|
return true;
|
|
9174
|
-
if (node.kind ===
|
|
9779
|
+
if (node.kind === ts7.SyntaxKind.FalseKeyword)
|
|
9175
9780
|
return false;
|
|
9176
|
-
if (
|
|
9781
|
+
if (ts7.isArrayLiteralExpression(node))
|
|
9177
9782
|
return node.elements.map((element) => this.#literalToValue(element));
|
|
9178
|
-
if (
|
|
9783
|
+
if (ts7.isObjectLiteralExpression(node)) {
|
|
9179
9784
|
const obj = {};
|
|
9180
9785
|
for (const prop of node.properties) {
|
|
9181
|
-
if (!
|
|
9786
|
+
if (!ts7.isPropertyAssignment(prop))
|
|
9182
9787
|
continue;
|
|
9183
9788
|
const name = this.#getPropertyName(prop.name);
|
|
9184
9789
|
if (!name)
|
|
@@ -9187,13 +9792,13 @@ class FontOptimizer {
|
|
|
9187
9792
|
}
|
|
9188
9793
|
return obj;
|
|
9189
9794
|
}
|
|
9190
|
-
if (
|
|
9795
|
+
if (ts7.isAsExpression(node) || ts7.isSatisfiesExpression(node) || ts7.isParenthesizedExpression(node)) {
|
|
9191
9796
|
return this.#literalToValue(node.expression);
|
|
9192
9797
|
}
|
|
9193
9798
|
return;
|
|
9194
9799
|
}
|
|
9195
9800
|
#getPropertyName(name) {
|
|
9196
|
-
if (
|
|
9801
|
+
if (ts7.isIdentifier(name) || ts7.isStringLiteral(name) || ts7.isNumericLiteral(name))
|
|
9197
9802
|
return name.text;
|
|
9198
9803
|
return null;
|
|
9199
9804
|
}
|
|
@@ -12663,10 +13268,12 @@ var NODE_NATIVE_MODULE_SET = new Set([
|
|
|
12663
13268
|
]);
|
|
12664
13269
|
// pkgs/@akanjs/devkit/getCredentials.ts
|
|
12665
13270
|
import yaml from "js-yaml";
|
|
13271
|
+
// pkgs/@akanjs/devkit/getModelFileData.ts
|
|
13272
|
+
import { capitalize as capitalize8 } from "akanjs/common";
|
|
12666
13273
|
// pkgs/@akanjs/devkit/getRelatedCnsts.ts
|
|
12667
13274
|
import { readFileSync as readFileSync4, realpathSync } from "fs";
|
|
12668
13275
|
import ora2 from "ora";
|
|
12669
|
-
import * as
|
|
13276
|
+
import * as ts8 from "typescript";
|
|
12670
13277
|
var tsTranspiler = new Bun.Transpiler({ loader: "ts" });
|
|
12671
13278
|
var tsxTranspiler = new Bun.Transpiler({ loader: "tsx" });
|
|
12672
13279
|
var getTranspiler = (filePath) => filePath.endsWith(".tsx") ? tsxTranspiler : tsTranspiler;
|
|
@@ -12699,10 +13306,10 @@ var scanModuleSpecifiers = (source2, filePath, includeExports) => {
|
|
|
12699
13306
|
return importSpecifiers;
|
|
12700
13307
|
};
|
|
12701
13308
|
var parseTsConfig = (tsConfigPath = "./tsconfig.json") => {
|
|
12702
|
-
const configFile =
|
|
12703
|
-
return
|
|
13309
|
+
const configFile = ts8.readConfigFile(tsConfigPath, (path40) => {
|
|
13310
|
+
return ts8.sys.readFile(path40);
|
|
12704
13311
|
});
|
|
12705
|
-
return
|
|
13312
|
+
return ts8.parseJsonConfigFileContent(configFile.config, ts8.sys, realpathSync(tsConfigPath).replace(/[^/\\]+$/, ""));
|
|
12706
13313
|
};
|
|
12707
13314
|
var collectImportedFiles = (constantFilePath, parsedConfig) => {
|
|
12708
13315
|
const allFilesToAnalyze = new Set([constantFilePath]);
|
|
@@ -12717,7 +13324,7 @@ var collectImportedFiles = (constantFilePath, parsedConfig) => {
|
|
|
12717
13324
|
for (const importPath of scanModuleSpecifiers(source2, filePath, false)) {
|
|
12718
13325
|
if (!importPath.startsWith("."))
|
|
12719
13326
|
continue;
|
|
12720
|
-
const resolved =
|
|
13327
|
+
const resolved = ts8.resolveModuleName(importPath, filePath, parsedConfig.options, ts8.sys).resolvedModule?.resolvedFileName;
|
|
12721
13328
|
if (resolved && !allFilesToAnalyze.has(resolved)) {
|
|
12722
13329
|
allFilesToAnalyze.add(resolved);
|
|
12723
13330
|
collectImported(resolved);
|
|
@@ -12734,7 +13341,7 @@ var collectImportedFiles = (constantFilePath, parsedConfig) => {
|
|
|
12734
13341
|
var createTsProgram = (filePaths, options) => {
|
|
12735
13342
|
const spinner = ora2("Creating TypeScript program for all files...");
|
|
12736
13343
|
spinner.start();
|
|
12737
|
-
const program2 =
|
|
13344
|
+
const program2 = ts8.createProgram(Array.from(filePaths), options);
|
|
12738
13345
|
const checker = program2.getTypeChecker();
|
|
12739
13346
|
spinner.succeed("TypeScript program created.");
|
|
12740
13347
|
return {
|
|
@@ -12774,17 +13381,17 @@ var analyzeProperties = (filesToAnalyze, program2, checker) => {
|
|
|
12774
13381
|
function visit(node) {
|
|
12775
13382
|
if (!source2)
|
|
12776
13383
|
return;
|
|
12777
|
-
if (
|
|
13384
|
+
if (ts8.isPropertyAccessExpression(node)) {
|
|
12778
13385
|
const left = node.expression;
|
|
12779
13386
|
const right = node.name;
|
|
12780
|
-
const { line } =
|
|
12781
|
-
if (
|
|
13387
|
+
const { line } = ts8.getLineAndCharacterOfPosition(source2, node.getStart());
|
|
13388
|
+
if (ts8.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},`))) {
|
|
12782
13389
|
const symbol = getCachedSymbol(right);
|
|
12783
13390
|
if (symbol?.declarations && symbol.declarations.length > 0) {
|
|
12784
13391
|
const key = symbol.declarations[0]?.getSourceFile().fileName.split("/").pop()?.split(".")[0] ?? "";
|
|
12785
13392
|
const property = propertyMap.get(key);
|
|
12786
13393
|
const isScalar = symbol.declarations[0]?.getSourceFile().fileName.includes("_") ?? false;
|
|
12787
|
-
const symbolFilePath = symbol.declarations[0]?.getSourceFile().fileName.replace(`${
|
|
13394
|
+
const symbolFilePath = symbol.declarations[0]?.getSourceFile().fileName.replace(`${ts8.sys.getCurrentDirectory()}/`, "");
|
|
12788
13395
|
if (!symbolFilePath)
|
|
12789
13396
|
throw new Error(`No symbol file path found for ${left.text}.${right.text}`);
|
|
12790
13397
|
if (property) {
|
|
@@ -12808,10 +13415,10 @@ var analyzeProperties = (filesToAnalyze, program2, checker) => {
|
|
|
12808
13415
|
}
|
|
12809
13416
|
}
|
|
12810
13417
|
}
|
|
12811
|
-
} else if (
|
|
13418
|
+
} else if (ts8.isImportDeclaration(node) && ts8.isStringLiteral(node.moduleSpecifier)) {
|
|
12812
13419
|
const importPath = node.moduleSpecifier.text;
|
|
12813
13420
|
if (importPath.startsWith(".")) {
|
|
12814
|
-
const resolved =
|
|
13421
|
+
const resolved = ts8.resolveModuleName(importPath, filePath, program2.getCompilerOptions(), ts8.sys).resolvedModule?.resolvedFileName;
|
|
12815
13422
|
const moduleName = importPath.split("/").pop()?.split(".")[0] ?? "";
|
|
12816
13423
|
const property = propertyMap.get(moduleName);
|
|
12817
13424
|
const isScalar = importPath.includes("_");
|
|
@@ -12826,7 +13433,7 @@ var analyzeProperties = (filesToAnalyze, program2, checker) => {
|
|
|
12826
13433
|
}
|
|
12827
13434
|
}
|
|
12828
13435
|
}
|
|
12829
|
-
|
|
13436
|
+
ts8.forEachChild(node, visit);
|
|
12830
13437
|
}
|
|
12831
13438
|
visit(source2);
|
|
12832
13439
|
}
|