Skip to content

Commit a3175c6

Browse files
authored
chore: cleanup aion engine (#4)
1 parent ae844d4 commit a3175c6

36 files changed

Lines changed: 825 additions & 443 deletions

.aion/Commands/AionSparkCommand.php

Lines changed: 51 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99
use Aion\Engine\Engine;
1010
use Aion\Engine\FeatureRegistry;
1111
use Aion\Engine\PathResolver;
12+
use Aion\Engine\PromptTypeEnum;
1213
use Aion\Features\Authentication\ApiTokensFeature;
1314
use Aion\Features\Authentication\OAuthFeature;
1415
use Aion\Features\ExternalTools\ECSLoggingFeature;
16+
use Aion\Features\OptionDefinition;
1517
use Aion\Features\System\AgenticAiSystemFeature;
1618
use Aion\Features\System\DatabaseSystemFeature;
1719
use Aion\Features\System\LogSystemFeature;
1820
use Aion\Features\System\PhpStanSystemFeature;
19-
use Aion\Stacks\ApiWithDefaultFrontEndSupportStack;
20-
use Aion\Stacks\BareApiStack;
2121
use Aion\Stacks\StackStrategyContract;
2222
use Symfony\Component\Console\Command\Command;
2323
use Symfony\Component\Console\Input\InputInterface;
@@ -40,15 +40,29 @@ class AionSparkCommand extends Command
4040

4141
private bool $isInteractive = false;
4242

43+
private OutputInterface $output;
44+
4345
protected function configure(): void
4446
{
4547
$this->setName($this->name)
4648
->addOption('dry-run', description: 'Execute a dry run of the bake process.')
47-
->addOption(ConfigKeyEnum::Frontend->value, mode: InputOption::VALUE_NEGATABLE, description: 'Include frontend support.');
49+
->addOption(
50+
ConfigKeyEnum::Frontend->value,
51+
mode: InputOption::VALUE_NEGATABLE,
52+
description: $this->getWhetherWantToUseFrontendOptionDefinition()->label,
53+
);
4854

4955
$this->bootstrapFeatureRegistry();
56+
}
5057

51-
$this->addDynamicOptionsFromFeatures();
58+
private static function getWhetherWantToUseFrontendOptionDefinition(): OptionDefinition
59+
{
60+
return new OptionDefinition(
61+
label: 'Do you need a Frontend?',
62+
type: PromptTypeEnum::Confirm,
63+
default: false,
64+
hint: 'Determines the technology stack for your application.',
65+
);
5266
}
5367

5468
private function bootstrapFeatureRegistry(): void
@@ -59,11 +73,6 @@ private function bootstrapFeatureRegistry(): void
5973

6074
self::$featureRegistry = new FeatureRegistry;
6175

62-
self::$featureRegistry->registerStacks([
63-
BareApiStack::class,
64-
ApiWithDefaultFrontEndSupportStack::class,
65-
]);
66-
6776
self::$featureRegistry->registerFeatures([
6877
ApiTokensFeature::class,
6978
OAuthFeature::class,
@@ -73,6 +82,8 @@ private function bootstrapFeatureRegistry(): void
7382
PhpStanSystemFeature::class,
7483
AgenticAiSystemFeature::class,
7584
]);
85+
86+
$this->addDynamicOptionsFromFeatures();
7687
}
7788

7889
private function addDynamicOptionsFromFeatures(): void
@@ -82,7 +93,7 @@ private function addDynamicOptionsFromFeatures(): void
8293
continue;
8394
}
8495

85-
$type = $definition->type === 'confirm'
96+
$type = $definition->type === PromptTypeEnum::Confirm
8697
? InputOption::VALUE_NEGATABLE
8798
: InputOption::VALUE_REQUIRED;
8899

@@ -93,27 +104,27 @@ private function addDynamicOptionsFromFeatures(): void
93104
/** @throws Throwable */
94105
protected function execute(InputInterface $input, OutputInterface $output): int
95106
{
107+
$this->output = $output;
96108
$this->bootstrapFeatureRegistry();
97109
$this->ui = new SparkUI($output);
98110
$this->configurationPrompter = new ConfigurationPrompter($input);
99111
$this->isInteractive = $input->isInteractive();
100112

101113
$this->ui->displayHeader();
102114

103-
[$stackStrategy, $aionConfig] = $this->promptForConfiguration($output);
115+
[$stackStrategy, $aionConfig] = $this->promptForConfiguration();
104116

105-
$this->runEngine(
117+
$this->runCompositionEngine(
106118
$stackStrategy,
107119
$aionConfig,
108-
$output,
109120
dryRun: (bool) $input->getOption('dry-run'),
110121
);
111122

112123
if (! $input->getOption('dry-run')) {
113-
$this->installDependencies($output, $aionConfig);
124+
$this->installDependencies($aionConfig);
114125
}
115126

116-
info('>_ ✨ Setup is complete. Enjoy your new Aion-backed project!'.($input->getOption('dry-run') ? ' (Dry Run Complete)' : ''));
127+
$this->ui->displaySuccess('Setup is complete. Enjoy your new Aion-backed project!'.($input->getOption('dry-run') ? ' (Dry Run Complete)' : ''));
117128

118129
if (PHP_OS_FAMILY === 'Windows' && ! $input->getOption('dry-run')) {
119130
info('>_ Note: On Windows, you should manually delete the .aion folder to finish the cleanup.');
@@ -123,37 +134,40 @@ protected function execute(InputInterface $input, OutputInterface $output): int
123134
}
124135

125136
/** @return array{0: StackStrategyContract, 1: AionConfig} */
126-
private function promptForConfiguration(OutputInterface $output): array
137+
private function promptForConfiguration(): array
127138
{
128139
while (true) {
129-
$stack = $this->resolveStack($output);
130-
$aionConfig = $this->resolveAionConfig($output);
140+
$stackDefinition = $this->getWhetherWantToUseFrontendOptionDefinition();
131141

132-
if ($this->confirmSetup($stack, $aionConfig)) {
142+
$stack = $this->resolveStack($stackDefinition);
143+
144+
$aionConfig = $this->resolveAionConfig();
145+
146+
if ($this->confirmSetup($stack, $aionConfig, $stackDefinition)) {
133147
return [$stack, $aionConfig];
134148
}
135149

136-
$output->writeln("\n<fg=yellow>Restarting configuration...</>\n");
150+
$this->output->writeln("\n<fg=yellow>Restarting configuration...</>\n");
137151
}
138152
}
139153

140-
private function resolveStack(OutputInterface $output): StackStrategyContract
154+
private function resolveStack(OptionDefinition $definition): StackStrategyContract
141155
{
142-
$output->writeln("\n<comment>>_ First, let's pick your technology stack.</comment>");
156+
$this->output->writeln("\n<comment>>_ First, let's pick your technology stack.</comment>");
143157

144-
return $this->configurationPrompter->promptForStack();
158+
return $this->configurationPrompter->promptForStack($definition);
145159
}
146160

147-
private function resolveAionConfig(OutputInterface $output): AionConfig
161+
private function resolveAionConfig(): AionConfig
148162
{
149-
$output->writeln("\n<comment>>_ Awesome. Let's configure your application.</comment>\n");
163+
$this->output->writeln("\n<comment>>_ Awesome. Let's configure your application.</comment>\n");
150164

151165
return $this->configurationPrompter->promptForConfiguration(
152166
optionDefinitions: self::$featureRegistry->getOptionDefinitions(),
153167
);
154168
}
155169

156-
private function confirmSetup(StackStrategyContract $stack, AionConfig $config): bool
170+
private function confirmSetup(StackStrategyContract $stack, AionConfig $config, OptionDefinition $stackDefinition): bool
157171
{
158172
if ($this->isInteractive === false) {
159173
return true;
@@ -162,46 +176,35 @@ private function confirmSetup(StackStrategyContract $stack, AionConfig $config):
162176
$this->ui->displaySummary(
163177
stack: $stack,
164178
config: $config,
165-
optionDefinitions: self::$featureRegistry->getOptionDefinitions(),
179+
optionDefinitions: [
180+
ConfigKeyEnum::Frontend->value => $stackDefinition,
181+
...self::$featureRegistry->getOptionDefinitions(),
182+
],
166183
);
167184

168185
return confirm(label: 'Is this configuration correct?');
169186
}
170187

171188
/** @throws Throwable */
172-
private function runEngine(
189+
private function runCompositionEngine(
173190
StackStrategyContract $stackStrategy,
174191
AionConfig $aionConfig,
175-
OutputInterface $output,
176192
bool $dryRun = false,
177193
): void {
178194
$engine = new Engine(
179-
registry: self::$featureRegistry,
195+
featureRegistry: self::$featureRegistry,
180196
stack: $stackStrategy,
181197
configuration: $aionConfig,
182198
pathResolver: new PathResolver(getcwd()),
183199
dryRun: $dryRun,
184200
);
185201

186-
$titleSection = $output->section();
187-
$titleSection->overwrite('Putting things together...');
188-
189-
$progressSection = $output->section();
190-
191-
$engine->bake(function (string $description) use ($progressSection) {
192-
$progressSection->overwrite("> $description");
193-
194-
usleep(rand(50_000, 150_000));
195-
});
196-
197-
$progressSection->overwrite('100%');
198-
$titleSection->overwrite('Putting things together... Done!');
199-
usleep(350_000);
200-
201-
$output->writeln('');
202+
$this->ui->displayProgress(
203+
progressSteps: $engine->yieldBakingOperations(),
204+
);
202205
}
203206

204-
private function installDependencies(OutputInterface $output, AionConfig $config): void
207+
private function installDependencies(AionConfig $config): void
205208
{
206209
if ($this->isInteractive && ! confirm('Would you like to install the composer dependencies now?')) {
207210
return;
@@ -218,6 +221,6 @@ private function installDependencies(OutputInterface $output, AionConfig $config
218221
passthru('php artisan boost:install');
219222
}
220223

221-
$output->writeln('');
224+
$this->output->writeln('');
222225
}
223226
}

.aion/Commands/Prompters/ConfigurationPrompter.php

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Aion\Choices\Enums\ConfigKeyEnum;
66
use Aion\Engine\AionConfig;
7+
use Aion\Engine\PromptTypeEnum;
78
use Aion\Features\OptionDefinition;
89
use Aion\Stacks\ApiWithDefaultFrontEndSupportStack;
910
use Aion\Stacks\BareApiStack;
@@ -21,12 +22,14 @@ public function __construct(
2122
private readonly InputInterface $input
2223
) {}
2324

24-
public function promptForStack(): StackStrategyContract
25+
public function promptForStack(OptionDefinition $definition): StackStrategyContract
2526
{
2627
$requireFe = $this->handleConfirmation(
2728
option: ConfigKeyEnum::Frontend,
28-
label: 'Do you need a Frontend?',
29-
displayName: 'Using Frontend',
29+
label: $definition->label,
30+
displayName: $definition->label,
31+
default: $definition->default,
32+
hint: $definition->hint,
3033
);
3134

3235
return $requireFe
@@ -47,32 +50,31 @@ public function promptForConfiguration(
4750
continue;
4851
}
4952

50-
$config->add($key, $this->askQuestion($key, $definition));
53+
$config->add($key, $this->resolveOptionValue($key, $definition));
5154
}
5255

5356
return $config;
5457
}
5558

56-
private function askQuestion(string|ConfigKeyEnum $key, OptionDefinition $definition): mixed
59+
private function resolveOptionValue(string|ConfigKeyEnum $key, OptionDefinition $definition): mixed
5760
{
5861
$value = match ($definition->type) {
59-
'confirm' => $this->handleConfirmation(
62+
PromptTypeEnum::Confirm => $this->handleConfirmation(
6063
option: $key,
6164
label: $definition->label,
6265
displayName: $definition->label,
6366
default: $definition->default,
6467
hint: $definition->hint,
6568
),
66-
'select', 'multiselect' => $this->handleSelection(
69+
PromptTypeEnum::Select, PromptTypeEnum::MultiSelect => $this->handleSelection(
6770
option: $key,
6871
label: $definition->label,
6972
options: $definition->options,
7073
default: $definition->default,
7174
displayName: $definition->label,
72-
isMulti: $definition->type === 'multiselect',
75+
isMulti: $definition->type === PromptTypeEnum::MultiSelect,
7376
hint: $definition->hint,
7477
),
75-
default => throw new \InvalidArgumentException("Unsupported question type: {$definition->type}"),
7678
};
7779

7880
return $definition->transform($value);

.aion/Commands/UI/SparkUI.php

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,31 @@ public function displaySummary(
5757
$this->output->writeln('');
5858
}
5959

60+
public function displayProgress(iterable $progressSteps): void
61+
{
62+
$titleSection = $this->output->section();
63+
$titleSection->overwrite('Putting things together...');
64+
65+
$progressSection = $this->output->section();
66+
67+
foreach ($progressSteps as $stepDescription) {
68+
$progressSection->overwrite("> $stepDescription");
69+
70+
$this->sleep(rand(50_000, 150_000));
71+
}
72+
73+
$progressSection->overwrite('100%');
74+
$titleSection->overwrite('Putting things together... Done!');
75+
$this->sleep(350_000);
76+
77+
$this->output->writeln('');
78+
}
79+
80+
public function displaySuccess(string $message): void
81+
{
82+
$this->output->writeln("\n<info>>_ ✨ $message</info>");
83+
}
84+
6085
/**
6186
* @param array<string, OptionDefinition> $schemas
6287
*/
@@ -105,6 +130,15 @@ private function formatSingleValue(mixed $value): string
105130
return (string) $value;
106131
}
107132

133+
private function sleep(int $microseconds): void
134+
{
135+
if ($this->output->isQuiet() || ! $this->output->isDecorated()) {
136+
return;
137+
}
138+
139+
usleep($microseconds);
140+
}
141+
108142
private function showHeaderWithAnimation(): void
109143
{
110144
$logo = [
@@ -121,20 +155,20 @@ private function showHeaderWithAnimation(): void
121155

122156
foreach ($logo as $line) {
123157
$this->output->writeln($line);
124-
usleep(40_000);
158+
$this->sleep(40_000);
125159
}
126160

127-
usleep(100_000);
161+
$this->sleep(100_000);
128162

129163
$subtitle = ' S T A R T E R K I T';
130164
$this->output->write(str_repeat(' ', 8));
131165

132166
foreach (mb_str_split($subtitle) as $char) {
133167
$this->output->write("<fg=green;options=bold>$char</>");
134-
usleep(20_000);
168+
$this->sleep(20_000);
135169
}
136170

137171
$this->output->writeln("\n");
138-
usleep(200_000);
172+
$this->sleep(200_000);
139173
}
140174
}

.aion/Engine/AionConfig.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@
99
*/
1010
class AionConfig
1111
{
12+
/** @var array<string, mixed> */
1213
private array $config = [];
1314

1415
public function add(string|ConfigKeyEnum $key, mixed $value): self
1516
{
1617
$key = $key instanceof ConfigKeyEnum ? $key->value : $key;
18+
1719
$this->config[$key] = $value;
1820

1921
return $this;

0 commit comments

Comments
 (0)