@akanjs/cli 2.3.6-rc.0 → 2.3.6-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/incrementalBuilder.proc.js +60 -0
- package/index.js +60 -0
- package/package.json +3 -3
- package/templates/workspaceRoot/.cursor/rules/akan-page-routing.mdc.template +17 -0
- package/templates/workspaceRoot/.cursor/rules/akan-scan-conventions.mdc.template +19 -0
- package/templates/workspaceRoot/.cursor/rules/application-test-commands.mdc.template +16 -0
- package/templates/workspaceRoot/.cursor/rules/change-scope.mdc.template +12 -0
- package/templates/workspaceRoot/.cursor/rules/client-server-boundaries.mdc.template +17 -0
- package/templates/workspaceRoot/.cursor/rules/coding-style.mdc.template +24 -0
- package/templates/workspaceRoot/.cursor/rules/domain-module-conventions.mdc.template +17 -0
- package/templates/workspaceRoot/.cursor/rules/format-lint.mdc.template +12 -0
- package/templates/workspaceRoot/.cursor/rules/repo-overview.mdc.template +17 -0
- package/templates/workspaceRoot/.cursor/rules/scalar-modeling.mdc.template +14 -0
- package/templates/workspaceRoot/.cursor/rules/secrets-and-env.mdc.template +13 -0
- package/templates/workspaceRoot/.cursor/rules/service-signal-conventions.mdc.template +14 -0
- package/templates/workspaceRoot/.cursor/rules/typescript-imports.mdc.template +13 -0
- package/templates/workspaceRoot/AGENTS.md.template +12 -9
|
@@ -4136,8 +4136,11 @@ var shouldRestartBackendByDevPlan = (message) => {
|
|
|
4136
4136
|
return null;
|
|
4137
4137
|
if (message.devPlan.actions.includes("report-error"))
|
|
4138
4138
|
return false;
|
|
4139
|
+
if (message.devPlan.actions.includes("restart-builder"))
|
|
4140
|
+
return false;
|
|
4139
4141
|
return message.devPlan.actions.includes("restart-backend");
|
|
4140
4142
|
};
|
|
4143
|
+
var shouldRestartBuilderByDevPlan = (message) => message.devPlan?.actions.includes("restart-builder") ?? false;
|
|
4141
4144
|
var shouldRestartDevHostByDevPlan = (message) => message.devPlan?.actions.includes("restart-dev-host") ?? message.kinds.includes("config");
|
|
4142
4145
|
var RESTART_ROLE_ORDER = ["server", "shared", "barrel", "config"];
|
|
4143
4146
|
var generationValue = (generation) => generation ?? -1;
|
|
@@ -4557,6 +4560,14 @@ class AkanAppHost {
|
|
|
4557
4560
|
this.#sendToBackend(message);
|
|
4558
4561
|
}
|
|
4559
4562
|
async#handleInvalidate(message) {
|
|
4563
|
+
if (shouldRestartBuilderByDevPlan(message)) {
|
|
4564
|
+
try {
|
|
4565
|
+
await this.#restartDevChildren(message);
|
|
4566
|
+
} catch (err) {
|
|
4567
|
+
this.#recordDevHostRestartFailure(message, err);
|
|
4568
|
+
}
|
|
4569
|
+
return;
|
|
4570
|
+
}
|
|
4560
4571
|
if (shouldRestartDevHostByDevPlan(message)) {
|
|
4561
4572
|
this.#recordDevHostRestartRequired(message);
|
|
4562
4573
|
return;
|
|
@@ -4567,6 +4578,27 @@ class AkanAppHost {
|
|
|
4567
4578
|
}
|
|
4568
4579
|
this.#sendToBackend(message);
|
|
4569
4580
|
}
|
|
4581
|
+
async#restartDevChildren(message) {
|
|
4582
|
+
const generation = message.devPlan?.generation ?? message.generation;
|
|
4583
|
+
this.logger.warn(`[dev-host] recycling builder/backend for runtime metadata generation=${generation ?? "(unknown)"} files=${message.files.length}`);
|
|
4584
|
+
if (this.#restartTimer) {
|
|
4585
|
+
clearTimeout(this.#restartTimer);
|
|
4586
|
+
this.#restartTimer = null;
|
|
4587
|
+
}
|
|
4588
|
+
if (this.#backendRecoveryTimer) {
|
|
4589
|
+
clearTimeout(this.#backendRecoveryTimer);
|
|
4590
|
+
this.#backendRecoveryTimer = null;
|
|
4591
|
+
}
|
|
4592
|
+
this.#pendingRestartReason = null;
|
|
4593
|
+
this.#lastGoodFrontend = {};
|
|
4594
|
+
this.#buildStatusByPhase.clear();
|
|
4595
|
+
this.#pendingBuildStatusReplay = [];
|
|
4596
|
+
await this.#stopBackend();
|
|
4597
|
+
this.#stopBuilder();
|
|
4598
|
+
await this.#backendGraph.refresh();
|
|
4599
|
+
await this.#startBuilder();
|
|
4600
|
+
this.#startBackend({ generation, files: message.files });
|
|
4601
|
+
}
|
|
4570
4602
|
#recordLastGood(message) {
|
|
4571
4603
|
if (message.type === "pages-updated") {
|
|
4572
4604
|
if (!shouldReplaceLastGoodMessage(this.#lastGoodFrontend.pages, message))
|
|
@@ -4596,6 +4628,20 @@ class AkanAppHost {
|
|
|
4596
4628
|
this.#sendOrQueueBuildStatus(status);
|
|
4597
4629
|
}
|
|
4598
4630
|
}
|
|
4631
|
+
#recordDevHostRestartFailure(message, err) {
|
|
4632
|
+
const generation = message.devPlan?.generation ?? message.generation ?? this.#nextBackendBuildStatusGeneration();
|
|
4633
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
4634
|
+
this.logger.warn(`[dev-host] runtime metadata restart failed generation=${generation}: ${detail}`);
|
|
4635
|
+
const status = {
|
|
4636
|
+
generation,
|
|
4637
|
+
phase: "scan",
|
|
4638
|
+
ok: false,
|
|
4639
|
+
files: message.files,
|
|
4640
|
+
message: `Runtime metadata change requires restarting \`akan start\` to apply: ${detail}`
|
|
4641
|
+
};
|
|
4642
|
+
this.#recordBuildStatus(status);
|
|
4643
|
+
this.#sendOrQueueBuildStatus(status);
|
|
4644
|
+
}
|
|
4599
4645
|
#recordBuildStatus(status) {
|
|
4600
4646
|
const recovered = shouldMarkBuildPhaseRecovered(this.#buildStatusByPhase, status);
|
|
4601
4647
|
this.#buildStatusByPhase.set(status.phase, status);
|
|
@@ -7509,6 +7555,8 @@ class DevChangePlanner {
|
|
|
7509
7555
|
const fileRoles = this.#rolesForFile(file, { isGenerated: generatedSet.has(path25.resolve(file)), reasons });
|
|
7510
7556
|
for (const role of fileRoles)
|
|
7511
7557
|
roles.add(role);
|
|
7558
|
+
if (reasons.has("runtime-metadata"))
|
|
7559
|
+
actions.add("restart-builder");
|
|
7512
7560
|
if (reasons.size > 0)
|
|
7513
7561
|
reasonByFile[path25.resolve(file)] = [...reasons].sort();
|
|
7514
7562
|
}
|
|
@@ -7561,6 +7609,9 @@ class DevChangePlanner {
|
|
|
7561
7609
|
roles.add("shared");
|
|
7562
7610
|
reasons.add("shared-path");
|
|
7563
7611
|
}
|
|
7612
|
+
if (isSource && this.#isRuntimeMetadataFile(parts, base)) {
|
|
7613
|
+
reasons.add("runtime-metadata");
|
|
7614
|
+
}
|
|
7564
7615
|
if (roles.has("server") && roles.has("client")) {
|
|
7565
7616
|
roles.delete("server");
|
|
7566
7617
|
roles.delete("client");
|
|
@@ -7585,6 +7636,15 @@ class DevChangePlanner {
|
|
|
7585
7636
|
const base = path25.basename(abs);
|
|
7586
7637
|
return parts.includes("common") || SHARED_SUFFIXES2.some((suffix) => base.endsWith(suffix)) || RUNTIME_METADATA_BASENAMES2.has(base);
|
|
7587
7638
|
}
|
|
7639
|
+
#isRuntimeMetadataFile(parts, base) {
|
|
7640
|
+
const parent = parts.at(-2);
|
|
7641
|
+
if (parent === "lib" && RUNTIME_METADATA_BASENAMES2.has(base))
|
|
7642
|
+
return true;
|
|
7643
|
+
const libIndex = parts.lastIndexOf("lib");
|
|
7644
|
+
if (libIndex < 0 || parts.length <= libIndex + 1)
|
|
7645
|
+
return false;
|
|
7646
|
+
return base.endsWith(".dictionary.ts") || base.endsWith(".signal.ts");
|
|
7647
|
+
}
|
|
7588
7648
|
#isBarrelFacetChild(parts) {
|
|
7589
7649
|
if (parts.length < 4)
|
|
7590
7650
|
return false;
|
package/index.js
CHANGED
|
@@ -4134,8 +4134,11 @@ var shouldRestartBackendByDevPlan = (message) => {
|
|
|
4134
4134
|
return null;
|
|
4135
4135
|
if (message.devPlan.actions.includes("report-error"))
|
|
4136
4136
|
return false;
|
|
4137
|
+
if (message.devPlan.actions.includes("restart-builder"))
|
|
4138
|
+
return false;
|
|
4137
4139
|
return message.devPlan.actions.includes("restart-backend");
|
|
4138
4140
|
};
|
|
4141
|
+
var shouldRestartBuilderByDevPlan = (message) => message.devPlan?.actions.includes("restart-builder") ?? false;
|
|
4139
4142
|
var shouldRestartDevHostByDevPlan = (message) => message.devPlan?.actions.includes("restart-dev-host") ?? message.kinds.includes("config");
|
|
4140
4143
|
var RESTART_ROLE_ORDER = ["server", "shared", "barrel", "config"];
|
|
4141
4144
|
var generationValue = (generation) => generation ?? -1;
|
|
@@ -4555,6 +4558,14 @@ class AkanAppHost {
|
|
|
4555
4558
|
this.#sendToBackend(message);
|
|
4556
4559
|
}
|
|
4557
4560
|
async#handleInvalidate(message) {
|
|
4561
|
+
if (shouldRestartBuilderByDevPlan(message)) {
|
|
4562
|
+
try {
|
|
4563
|
+
await this.#restartDevChildren(message);
|
|
4564
|
+
} catch (err) {
|
|
4565
|
+
this.#recordDevHostRestartFailure(message, err);
|
|
4566
|
+
}
|
|
4567
|
+
return;
|
|
4568
|
+
}
|
|
4558
4569
|
if (shouldRestartDevHostByDevPlan(message)) {
|
|
4559
4570
|
this.#recordDevHostRestartRequired(message);
|
|
4560
4571
|
return;
|
|
@@ -4565,6 +4576,27 @@ class AkanAppHost {
|
|
|
4565
4576
|
}
|
|
4566
4577
|
this.#sendToBackend(message);
|
|
4567
4578
|
}
|
|
4579
|
+
async#restartDevChildren(message) {
|
|
4580
|
+
const generation = message.devPlan?.generation ?? message.generation;
|
|
4581
|
+
this.logger.warn(`[dev-host] recycling builder/backend for runtime metadata generation=${generation ?? "(unknown)"} files=${message.files.length}`);
|
|
4582
|
+
if (this.#restartTimer) {
|
|
4583
|
+
clearTimeout(this.#restartTimer);
|
|
4584
|
+
this.#restartTimer = null;
|
|
4585
|
+
}
|
|
4586
|
+
if (this.#backendRecoveryTimer) {
|
|
4587
|
+
clearTimeout(this.#backendRecoveryTimer);
|
|
4588
|
+
this.#backendRecoveryTimer = null;
|
|
4589
|
+
}
|
|
4590
|
+
this.#pendingRestartReason = null;
|
|
4591
|
+
this.#lastGoodFrontend = {};
|
|
4592
|
+
this.#buildStatusByPhase.clear();
|
|
4593
|
+
this.#pendingBuildStatusReplay = [];
|
|
4594
|
+
await this.#stopBackend();
|
|
4595
|
+
this.#stopBuilder();
|
|
4596
|
+
await this.#backendGraph.refresh();
|
|
4597
|
+
await this.#startBuilder();
|
|
4598
|
+
this.#startBackend({ generation, files: message.files });
|
|
4599
|
+
}
|
|
4568
4600
|
#recordLastGood(message) {
|
|
4569
4601
|
if (message.type === "pages-updated") {
|
|
4570
4602
|
if (!shouldReplaceLastGoodMessage(this.#lastGoodFrontend.pages, message))
|
|
@@ -4594,6 +4626,20 @@ class AkanAppHost {
|
|
|
4594
4626
|
this.#sendOrQueueBuildStatus(status);
|
|
4595
4627
|
}
|
|
4596
4628
|
}
|
|
4629
|
+
#recordDevHostRestartFailure(message, err) {
|
|
4630
|
+
const generation = message.devPlan?.generation ?? message.generation ?? this.#nextBackendBuildStatusGeneration();
|
|
4631
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
4632
|
+
this.logger.warn(`[dev-host] runtime metadata restart failed generation=${generation}: ${detail}`);
|
|
4633
|
+
const status = {
|
|
4634
|
+
generation,
|
|
4635
|
+
phase: "scan",
|
|
4636
|
+
ok: false,
|
|
4637
|
+
files: message.files,
|
|
4638
|
+
message: `Runtime metadata change requires restarting \`akan start\` to apply: ${detail}`
|
|
4639
|
+
};
|
|
4640
|
+
this.#recordBuildStatus(status);
|
|
4641
|
+
this.#sendOrQueueBuildStatus(status);
|
|
4642
|
+
}
|
|
4597
4643
|
#recordBuildStatus(status) {
|
|
4598
4644
|
const recovered = shouldMarkBuildPhaseRecovered(this.#buildStatusByPhase, status);
|
|
4599
4645
|
this.#buildStatusByPhase.set(status.phase, status);
|
|
@@ -7507,6 +7553,8 @@ class DevChangePlanner {
|
|
|
7507
7553
|
const fileRoles = this.#rolesForFile(file, { isGenerated: generatedSet.has(path25.resolve(file)), reasons });
|
|
7508
7554
|
for (const role of fileRoles)
|
|
7509
7555
|
roles.add(role);
|
|
7556
|
+
if (reasons.has("runtime-metadata"))
|
|
7557
|
+
actions.add("restart-builder");
|
|
7510
7558
|
if (reasons.size > 0)
|
|
7511
7559
|
reasonByFile[path25.resolve(file)] = [...reasons].sort();
|
|
7512
7560
|
}
|
|
@@ -7559,6 +7607,9 @@ class DevChangePlanner {
|
|
|
7559
7607
|
roles.add("shared");
|
|
7560
7608
|
reasons.add("shared-path");
|
|
7561
7609
|
}
|
|
7610
|
+
if (isSource && this.#isRuntimeMetadataFile(parts, base)) {
|
|
7611
|
+
reasons.add("runtime-metadata");
|
|
7612
|
+
}
|
|
7562
7613
|
if (roles.has("server") && roles.has("client")) {
|
|
7563
7614
|
roles.delete("server");
|
|
7564
7615
|
roles.delete("client");
|
|
@@ -7583,6 +7634,15 @@ class DevChangePlanner {
|
|
|
7583
7634
|
const base = path25.basename(abs);
|
|
7584
7635
|
return parts.includes("common") || SHARED_SUFFIXES2.some((suffix) => base.endsWith(suffix)) || RUNTIME_METADATA_BASENAMES2.has(base);
|
|
7585
7636
|
}
|
|
7637
|
+
#isRuntimeMetadataFile(parts, base) {
|
|
7638
|
+
const parent = parts.at(-2);
|
|
7639
|
+
if (parent === "lib" && RUNTIME_METADATA_BASENAMES2.has(base))
|
|
7640
|
+
return true;
|
|
7641
|
+
const libIndex = parts.lastIndexOf("lib");
|
|
7642
|
+
if (libIndex < 0 || parts.length <= libIndex + 1)
|
|
7643
|
+
return false;
|
|
7644
|
+
return base.endsWith(".dictionary.ts") || base.endsWith(".signal.ts");
|
|
7645
|
+
}
|
|
7586
7646
|
#isBarrelFacetChild(parts) {
|
|
7587
7647
|
if (parts.length < 4)
|
|
7588
7648
|
return false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/cli",
|
|
3
|
-
"version": "2.3.6-rc.
|
|
3
|
+
"version": "2.3.6-rc.2",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"@langchain/openai": "^1.4.6",
|
|
35
35
|
"@tailwindcss/node": "^4.3.0",
|
|
36
36
|
"@trapezedev/project": "^7.1.4",
|
|
37
|
-
"akanjs": "2.3.6-rc.
|
|
37
|
+
"akanjs": "2.3.6-rc.2",
|
|
38
38
|
"chalk": "^5.6.2",
|
|
39
39
|
"commander": "^14.0.3",
|
|
40
|
-
"daisyui": "
|
|
40
|
+
"daisyui": "5.5.23",
|
|
41
41
|
"dayjs": "^1.11.20",
|
|
42
42
|
"fontaine": "^0.8.0",
|
|
43
43
|
"fonteditor-core": "^2.6.3",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Akan page routing conventions
|
|
3
|
+
globs: apps/**/page/**/*.{ts,tsx,js,jsx}
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Akan Page Routing
|
|
8
|
+
|
|
9
|
+
- `apps/<app>/page` may contain route modules only. Do not add helper logic or component-only files there.
|
|
10
|
+
- Route source files under `page/` must use `.tsx`. Do not add `logic.ts`, `.js`, or `.jsx` files under `page/`.
|
|
11
|
+
- Route pages use `_index.tsx`; layouts use `_layout.tsx`.
|
|
12
|
+
- Reserved `_*.tsx` route filenames are limited to `_index.tsx` and `_layout.tsx`; do not add files like `_Component.tsx` or `_helper.tsx`.
|
|
13
|
+
- Page filenames must not start with an uppercase letter. Move helper components like `Component.tsx` to app `ui`, `common`, or `lib` instead.
|
|
14
|
+
- Dynamic segments use `[id]`; route groups use directories like `(user)`, `(public)`, `(tab)`, or `(detail)`.
|
|
15
|
+
- Page modules should usually export `default`, `pageConfig`, `head`, `generateHead`, or `Loading`.
|
|
16
|
+
- Prefer `export default function Page` or `export default async function Page` for page components.
|
|
17
|
+
- Before changing route behavior, check `pkgs/akanjs/server/src/routeTree.tsx` and nearby routes for the expected pattern.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: scanSync-enforced Akan app/lib layout and generated index conventions
|
|
3
|
+
globs: apps/**/*,libs/**/*
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Akan Scan Conventions
|
|
8
|
+
|
|
9
|
+
- `apps/<appName>` root may only contain these files: `akan.app.json`, `akan.config.ts`, `capacitor.config.ts`, `client.ts`, `main.ts`, `package.json`, `server.ts`, `tsconfig.json`.
|
|
10
|
+
- `apps/<appName>` root may only contain these folders: `.akan`, `android`, `common`, `env`, `ios`, `lib`, `page`, `private`, `public`, `script`, `srvkit`, `ui`, `webkit`.
|
|
11
|
+
- Do not add `apps/*/base`; place shared app utilities under `apps/*/common`.
|
|
12
|
+
- `apps/*/lib` and `libs/*/lib` root files are limited to generated/support files: `cnst.ts`, `db.ts`, `dict.ts`, `option.ts`, `sig.ts`, `srv.ts`, `st.ts`, `useClient.ts`, `useServer.ts`.
|
|
13
|
+
- Domain module folders are `lib/<model>` for database modules, `lib/_<service>` for service modules, and `lib/__scalar/<scalar>` for scalar modules.
|
|
14
|
+
- Database module UI files are limited to `<Model>.Template.tsx`, `<Model>.Unit.tsx`, `<Model>.Util.tsx`, `<Model>.View.tsx`, and `<Model>.Zone.tsx`.
|
|
15
|
+
- Service module UI files are limited to `<Service>.Util.tsx` and `<Service>.Zone.tsx`.
|
|
16
|
+
- Scalar module UI files are limited to `<Scalar>.Template.tsx` and `<Scalar>.Unit.tsx`.
|
|
17
|
+
- Module `*.test.ts`, `*.test.tsx`, `*.spec.ts`, and `*.spec.tsx` files are allowed.
|
|
18
|
+
- `ui/index.ts`, `webkit/index.ts`, `srvkit/index.ts`, `common/index.ts`, and module `lib/**/index.ts` files are generated by scanSync; do not hand-edit or track them.
|
|
19
|
+
- Generated facet indexes export only 1-depth files/folders with `export * from "./name";`.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Application test commands after source changes
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Application Test Commands
|
|
7
|
+
|
|
8
|
+
- After changing application source code, test the app with `bun run akan start <appName>`.
|
|
9
|
+
- Test production build generation with `bun run akan build <appName>`.
|
|
10
|
+
- To test a built artifact locally, run it from the generated app directory with the required Akan runtime environment variables.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
cd dist/apps/<appName> && USE_AKANJS_PKGS=true AKAN_PUBLIC_REPO_NAME=akansoft AKAN_PUBLIC_SERVE_DOMAIN="akamir.com" AKAN_PUBLIC_APP_NAME=seon AKAN_PUBLIC_ENV=local AKAN_PUBLIC_OPERATION_MODE=local SERVER_MODE=federation AKAN_PUBLIC_BASE_PATHS=neul,apptest,seon bun main.js
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
- Adjust `<appName>`, `AKAN_PUBLIC_APP_NAME`, and `AKAN_PUBLIC_BASE_PATHS` to match the app being tested.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Keep edits scoped and avoid unrelated churn
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Change Scope
|
|
7
|
+
|
|
8
|
+
- Keep edits scoped to the requested task and the directly related files.
|
|
9
|
+
- Do not reformat unrelated files or rewrite nearby code just for style.
|
|
10
|
+
- Do not revert or overwrite unrelated user changes in a dirty working tree.
|
|
11
|
+
- Prefer established nearby patterns over introducing a new abstraction.
|
|
12
|
+
- Add new abstractions only when they remove real duplication or match an existing project pattern.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Client/server boundary conventions for Akan apps
|
|
3
|
+
globs: apps/**/*.{ts,tsx},libs/**/*.{ts,tsx},pkgs/akanjs/**/*.{ts,tsx}
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Client Server Boundaries
|
|
8
|
+
|
|
9
|
+
- Use `"use client";` at the top of client component files.
|
|
10
|
+
- Be careful when importing client-only code from page or layout modules.
|
|
11
|
+
- Keep page props serializable unless the existing route pattern clearly allows otherwise.
|
|
12
|
+
- Let server page/layout files own route-level concerns: `usePage`, headers, static layout, and `akanjs/ui` `Tab` composition.
|
|
13
|
+
- Keep interactive loading, submit/import actions, local form inputs, and `router.push` inside client `Util`, `Template`, or `Zone` components.
|
|
14
|
+
- Prefer server-rendered `Tab` shells for static mode selection; put one client component inside each `Tab.Panel` instead of managing the selected mode with extra `useState`.
|
|
15
|
+
- In domain UI, `Template`, `Zone`, and `Util` components are usually client components; `Unit` and `View` components are usually server components.
|
|
16
|
+
- Preserve established domain file roles such as `.document.ts`, `.service.ts`, `.store.ts`, `.constant.ts`, and `.client.ts`.
|
|
17
|
+
- When unsure, inspect nearby files in the same app or package before introducing a new boundary pattern.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Coding style conventions for classes and functions
|
|
3
|
+
globs: **/*.{ts,tsx}
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Coding Style
|
|
8
|
+
|
|
9
|
+
- For large units of work, prefer declaring a class and running the flow through an instance instead of scattering many standalone functions.
|
|
10
|
+
- Prefer class methods or `static` methods over unrelated top-level helper functions when the logic belongs to a class-level workflow.
|
|
11
|
+
- Prefer ECMAScript `#private` fields and methods over TypeScript `private`; `*.service.ts` files are the main exception where `private` methods are encouraged.
|
|
12
|
+
- In files that declare a class, avoid top-level functions or variables when they can reasonably live inside the class.
|
|
13
|
+
- Prefer `const` function expressions over `function` declarations unless hoisting, overloads, generators, or framework conventions make `function` the better fit.
|
|
14
|
+
- Prefer declaring only one class per file; split the file when two or more class declarations are needed.
|
|
15
|
+
- For class-centered modules, prefer noun-style filenames that match the primary class name, such as `RouteClientBuilder.ts`, instead of verb-style wrapper filenames like `buildRouteClient.ts`.
|
|
16
|
+
- Avoid keeping exported functions that only instantiate a class and immediately call one method. Prefer migrating callers to instantiate the class directly.
|
|
17
|
+
- Except for React component files or convention files, TypeScript filenames should use camelCase.
|
|
18
|
+
- In React components, keep one-off `className` strings inline. Only extract class name constants when the class is reused, conditionally composed, or too large to read comfortably in JSX.
|
|
19
|
+
|
|
20
|
+
# Test Code
|
|
21
|
+
|
|
22
|
+
- Write TypeScript tests with Bun's test runner and import `describe`, `expect`, and `test` from `bun:test`.
|
|
23
|
+
- Keep tests colocated with the source they cover using `*.test.ts` or `*.spec.ts`, following the existing nearby pattern.
|
|
24
|
+
- Prefer focused behavior tests for public contracts and edge cases over implementation-detail assertions.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Akan domain module file roles and naming conventions
|
|
3
|
+
globs: apps/**/lib/**/*.{ts,tsx},libs/**/lib/**/*.{ts,tsx}
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Domain Module Conventions
|
|
8
|
+
|
|
9
|
+
- Organize business concepts as domain folders under `lib/`; keep related schema, service, signal, store, and UI files together.
|
|
10
|
+
- Use lowercase logic files such as `<model>.constant.ts`, `<model>.document.ts`, `<model>.service.ts`, `<model>.signal.ts`, `<model>.dictionary.ts`, and `<model>.store.ts`.
|
|
11
|
+
- Use PascalCase React component files such as `<Model>.Template.tsx`, `<Model>.Unit.tsx`, `<Model>.View.tsx`, `<Model>.Zone.tsx`, and `<Model>.Util.tsx`.
|
|
12
|
+
- Treat `constant.ts`, `dictionary.ts`, and `signal.ts` as shared contract files that should avoid platform-specific dependencies.
|
|
13
|
+
- Keep backend persistence/query logic in `.document.ts` and domain business orchestration in `.service.ts`.
|
|
14
|
+
- Keep frontend state in `.store.ts`; use `Template` for forms, `Unit` for list/card items, `View` for details, `Zone` for composed page sections, and `Util` for domain-specific UI helpers.
|
|
15
|
+
- Move reusable workflow state and actions from client UI into `<model>.store.ts` when they span loading state, selected lists, messages, or multi-step actions; consume them as `st.use.someState()` and `st.do.someAction()`.
|
|
16
|
+
- Keep `Util` components thin when a store action exists: render controls, call `st.do.*`, read `st.use.*`, and leave orchestration in the store.
|
|
17
|
+
- When adding or formalizing a public workflow such as importing an existing project, update the module `*.abstract.md` with the invariant or behavior change.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Formatting and linting conventions
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Formatting And Linting
|
|
7
|
+
|
|
8
|
+
- Use Biome as the formatter and linter.
|
|
9
|
+
- Format with `bun run akan lint <appName>` from the repo root.
|
|
10
|
+
- Keep formatting consistent with `biome.json`: 2-space indentation, 120 line width, and double quotes for JS/TS.
|
|
11
|
+
- Avoid adding new `console` usage except accepted methods such as `console.error`, `console.info`, and `console.warn`.
|
|
12
|
+
- Do not make broad formatting-only changes in unrelated files.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Akan.js monorepo structure and ownership conventions
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Repo Overview
|
|
7
|
+
|
|
8
|
+
- Akan.js is a full-stack TypeScript framework for building all-stack applications at once.
|
|
9
|
+
- Write one line and deploy across web, app, server, database, and infrastructure.
|
|
10
|
+
- Akan prioritizes actual business code by abstracting technical implementation details as much as possible.
|
|
11
|
+
- The goal is minimal code, high performance, and type-safe services that can deploy to web, mobile, server, and DB infrastructure together.
|
|
12
|
+
- This is a Bun-first Akan.js monorepo.
|
|
13
|
+
- Main top-level areas are `apps/`, `libs/`, `pkgs/`, and `infra/`.
|
|
14
|
+
- `apps/<app>` contains app-level pages, domain code, UI, env files, and `akan.config.ts`.
|
|
15
|
+
- `libs/*` contains shared domain and utility libraries.
|
|
16
|
+
- `pkgs/akanjs/*` contains framework, runtime, and tooling facets published through the single `akanjs` package. Prefer extending existing Akan facets before adding new framework-level patterns.
|
|
17
|
+
- `infra/` contains Helm, deployment templates, edge configs, and Jenkins env/secret scripts.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Akan scalar and constant model conventions
|
|
3
|
+
globs: **/*.constant.ts
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Scalar Modeling
|
|
8
|
+
|
|
9
|
+
- Define Akan models in `.constant.ts` files with `via` from `akanjs/constant`.
|
|
10
|
+
- Import custom scalar helpers such as `ID`, `Int`, `Float`, `JSON`, `Upload`, `Any`, `enumOf`, or `dayjs` from `akanjs/base` when the model needs them.
|
|
11
|
+
- Use `Int` for whole-number counts and quantities; use `Float` only for values that need decimals.
|
|
12
|
+
- Use `ID` for document references and prefer explicit structured fields over `JSON` unless the content is genuinely flexible.
|
|
13
|
+
- For date defaults, prefer a function such as `default: () => dayjs()` so the value is created at runtime.
|
|
14
|
+
- Follow the established model layering pattern: `Input`, `Object`, `Light<Model>`, full `<Model>`, and `<Model>Insight` when the domain needs those views.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Secret and environment file safety conventions
|
|
3
|
+
globs: .env,**/.env,**/*.env,infra/**/*,**/*secret*,**/*Secret*,**/*credential*,**/*Credential*
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Secrets And Env Safety
|
|
8
|
+
|
|
9
|
+
- Never print, summarize, commit, or expose real secret values, credentials, tokens, private keys, or `.env` contents.
|
|
10
|
+
- If env keys are needed for documentation, list only key names and example placeholders, not live values.
|
|
11
|
+
- Preserve the existing env/secret flow through root scripts such as `bun run downloadEnv`, `bun run uploadEnv`, `bun run downloadSecret`, and `bun run uploadSecret`.
|
|
12
|
+
- When editing infra env or secret scripts, keep Jenkins and deployment assumptions intact unless the task explicitly asks to change them.
|
|
13
|
+
- Treat generated env/secret artifacts as sensitive even when they are not named `.env`.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Akan service, signal, and adapter conventions
|
|
3
|
+
globs: apps/**/lib/**/*.{service,signal}.ts,libs/**/lib/**/*.{service,signal}.ts,apps/**/server/**/*.ts,libs/**/server/**/*.ts
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Service And Signal Conventions
|
|
8
|
+
|
|
9
|
+
- Keep domain business operations in `.service.ts` classes built with `serve(...)`.
|
|
10
|
+
- Keep execution contracts and triggers in `.signal.ts` classes built with `internal(...)`, `slice(...)`, and `endpoint(...)`.
|
|
11
|
+
- Use `Internal` for internal triggers such as init, interval, cron, or queue jobs.
|
|
12
|
+
- Use `Slice` for typed data views that feed client stores and zones; keep each slice focused on one purpose.
|
|
13
|
+
- Use `Endpoint` for query and mutation contracts exposed to callers.
|
|
14
|
+
- Connect external APIs or infrastructure through adapters, usually under `srvkit/`, and inject them into services instead of importing vendor clients directly into domain logic.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: TypeScript and import conventions
|
|
3
|
+
globs: **/*.{ts,tsx}
|
|
4
|
+
alwaysApply: false
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# TypeScript And Imports
|
|
8
|
+
|
|
9
|
+
- Use Bun and ESM assumptions from the root `tsconfig.json`.
|
|
10
|
+
- Prefer path aliases over deep relative imports when crossing package boundaries.
|
|
11
|
+
- Use `akanjs/*` for framework facets, `@apps/*` for apps, `@libs/*` for shared libs, and `@contract/*` for contract code.
|
|
12
|
+
- Respect existing client/server entrypoints such as `@libs/shared/client`, `@libs/shared/server`, `@apps/puffinplace/client`, and `@apps/puffinplace/server`.
|
|
13
|
+
- Let Biome organize imports instead of manually reshuffling unrelated imports.
|
|
@@ -47,23 +47,26 @@ Use the local module shape before adding a new abstraction.
|
|
|
47
47
|
- `<model>.signal.ts` defines typed endpoint contracts.
|
|
48
48
|
- `<model>.document.ts` owns persistence and document queries.
|
|
49
49
|
- `<model>.service.ts` owns business logic.
|
|
50
|
-
- `<model>.store.ts` owns
|
|
51
|
-
- `<Model>.Template.tsx` owns form-oriented UI. Client components, with 'use client'.
|
|
50
|
+
- `<model>.store.ts` owns reusable workflow state and actions that span loading, selections, messages, or multi-step client actions. Consume stores through generated `st.use.*` state and `st.do.*` actions.
|
|
51
|
+
- `<Model>.Template.tsx` owns form-oriented UI, including local form inputs and submit/import interactions. Client components, with 'use client'.
|
|
52
52
|
- `<Model>.Unit.tsx` owns list/item UI. Server components, no 'use client'.
|
|
53
53
|
- `<Model>.View.tsx` owns detail UI. Server components, no 'use client'.
|
|
54
|
-
- `<Model>.Zone.tsx` owns page/container integration. Client components, with 'use client'.
|
|
55
|
-
- `<model>.Util.tsx` owns small module UI helpers. Client components, with 'use client'.
|
|
54
|
+
- `<Model>.Zone.tsx` owns page/container integration and interactive loading or action flows that need client state. Client components, with 'use client'.
|
|
55
|
+
- `<model>.Util.tsx` owns small module UI helpers, including buttons, import actions, and client-side navigation such as `router.push`. Client components, with 'use client'.
|
|
56
56
|
|
|
57
57
|
## Agent Workflow
|
|
58
58
|
|
|
59
59
|
1. Read the nearby module and convention before creating files. If `*.abstract.md` exists, read it first.
|
|
60
60
|
2. Put new files in the established Akan location instead of adding parallel architecture.
|
|
61
61
|
3. Prefer CLI generation commands for new apps, libraries, modules, scalars, pages, and generated references.
|
|
62
|
-
4. Keep server-
|
|
63
|
-
5.
|
|
64
|
-
6.
|
|
65
|
-
7.
|
|
66
|
-
8.
|
|
62
|
+
4. Keep page and layout files server-oriented: route-level `usePage`, headers, static layout, and `akanjs/ui` `Tab` composition belong there.
|
|
63
|
+
5. Put interactive loading, submit/import actions, local form state, and `router.push` in client `Util`, `Template`, or `Zone` components.
|
|
64
|
+
6. Prefer `Tab` for static mode selection instead of extra `useState`; render one focused client component inside each `Tab.Panel`.
|
|
65
|
+
7. Keep server-only logic out of client surfaces and client-only code out of server imports.
|
|
66
|
+
8. Treat `AKAN_PUBLIC_*` env vars as public. Never put secrets in them.
|
|
67
|
+
9. Add or update tests when behavior, contracts, or CLI output changes.
|
|
68
|
+
10. Update `*.abstract.md` when business invariants, workflows, or public behavior change.
|
|
69
|
+
11. Run the smallest relevant verification command after changes.
|
|
67
70
|
|
|
68
71
|
## Common Commands
|
|
69
72
|
|