@akanjs/devkit 3.0.0-alpha.3 → 3.0.0-alpha.31
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/README.ko.md +1 -1
- package/README.md +1 -1
- package/agentsIndex.test.ts +10 -0
- package/agentsIndex.ts +47 -1
- package/aiEditor.ts +1 -1
- package/akanConfig/akanConfig.test.ts +0 -10
- package/akanConfig/akanConfig.ts +1 -11
- package/akanContext.ts +43 -31
- package/applicationBuildRunner.test.ts +1 -1
- package/applicationBuildRunner.ts +1 -2
- package/biome.base.json +317 -0
- package/biomeBase.ts +9 -0
- package/capacitorApp.test.ts +8 -0
- package/capacitorApp.ts +112 -18
- package/executors.test.ts +29 -5
- package/executors.ts +11 -4
- package/frontendBuild/allRoutesBuilder.ts +27 -8
- package/frontendBuild/buildRouteClient.test.ts +28 -2
- package/frontendBuild/clientBuildTypes.ts +4 -0
- package/frontendBuild/clientEntriesBundler.ts +4 -1
- package/frontendBuild/cssCandidateCache.test.ts +38 -0
- package/frontendBuild/cssCandidateCache.ts +15 -3
- package/frontendBuild/cssCompiler.ts +122 -11
- package/frontendBuild/cssImportResolver.ts +8 -7
- package/frontendBuild/frontendBuild.test.ts +88 -1
- package/frontendBuild/precompressArtifacts.ts +35 -7
- package/frontendBuild/routeClientBuilder.ts +26 -7
- package/frontendBuild/ssrBaseArtifactBuilder.ts +20 -4
- package/frontendBuild/vendorSpecifiers.ts +1 -0
- package/getCredentials.ts +1 -3
- package/incrementalBuilder/devWatchBatch.test.ts +18 -20
- package/incrementalBuilder/incrementalBuilder.host.ts +1 -1
- package/incrementalBuilder/incrementalBuilder.proc.ts +2 -2
- package/integration/devStabilityHarness.ts +1 -5
- package/lint/no-bang-comment-in-client.grit +20 -0
- package/lint/no-import-client-in-server.grit +48 -0
- package/lint/no-import-server-in-client.grit +45 -0
- package/lint/no-model-type-in-util-zone.grit +16 -0
- package/lint/no-return-in-store-action.grit +35 -0
- package/linter.ts +17 -12
- package/package.json +4 -4
- package/qualityScanner.test.ts +23 -0
- package/qualityScanner.ts +5 -44
- package/repoIdentity.ts +42 -0
- package/routeSourceValidator.test.ts +41 -0
- package/routeSourceValidator.ts +2 -44
- package/scanInfo.ts +2 -43
- package/transforms/externalizeFrameworkPlugin.ts +0 -1
- package/workspaceLayout.test.ts +26 -0
- package/workspaceLayout.ts +60 -0
package/README.ko.md
CHANGED
package/README.md
CHANGED
|
@@ -60,7 +60,7 @@ defaults, the shares they derive from `AKAN_MEMORY_LIMIT`, and what a small cont
|
|
|
60
60
|
|
|
61
61
|
## Requirements
|
|
62
62
|
|
|
63
|
-
- [Bun](https://bun.sh) `>=1.
|
|
63
|
+
- [Bun](https://bun.sh) `>=1.4.0`
|
|
64
64
|
- TypeScript
|
|
65
65
|
- Optional peers are only needed for the features that use them, such as Capacitor integration.
|
|
66
66
|
|
package/agentsIndex.test.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
renderRecipeEntries,
|
|
11
11
|
renderScopeAgentBlock,
|
|
12
12
|
renderScopeAgentsMd,
|
|
13
|
+
renderScopeClaudeMd,
|
|
13
14
|
upsertAgentBlock,
|
|
14
15
|
} from "./agentsIndex";
|
|
15
16
|
import type { RecipeInfo } from "./recipeScanner";
|
|
@@ -48,6 +49,15 @@ describe("renderScopeAgentBlock", () => {
|
|
|
48
49
|
});
|
|
49
50
|
});
|
|
50
51
|
|
|
52
|
+
describe("renderScopeClaudeMd", () => {
|
|
53
|
+
test("points at AGENTS.md and restates only the comment rule", () => {
|
|
54
|
+
const claude = renderScopeClaudeMd({ type: "app", name: "minimal" });
|
|
55
|
+
expect(claude).toContain("@AGENTS.md");
|
|
56
|
+
expect(claude).toContain("## Comments — Overrides Your Default");
|
|
57
|
+
expect(claude).not.toContain("## Recipes In Scope");
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
51
61
|
describe("upsertAgentBlock / extractAgentBlock", () => {
|
|
52
62
|
test("round-trips: fresh file → replace block → extract equals block", () => {
|
|
53
63
|
const fresh = renderScopeAgentsMd({ type: "app", name: "minimal" }, "OLD");
|
package/agentsIndex.ts
CHANGED
|
@@ -19,6 +19,32 @@ const loadRecipeScanner = () => (recipeScannerLoad ??= import("./recipeScanner")
|
|
|
19
19
|
export const AGENT_BLOCK_START = "<!-- akan:agent:start -->";
|
|
20
20
|
export const AGENT_BLOCK_END = "<!-- akan:agent:end -->";
|
|
21
21
|
|
|
22
|
+
const AGENT_VERSION_PREFIX = "<!-- akan:agent:version ";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The generating release, stamped into the workspace block. Nothing re-runs `akan agent install` on its own, so
|
|
26
|
+
* without a stamp a workspace carrying conventions from four releases ago is indistinguishable from a current one —
|
|
27
|
+
* `akan doctor` compares this against the installed devkit and says so.
|
|
28
|
+
*/
|
|
29
|
+
export const stampBlockVersion = (block: string, version: string): string =>
|
|
30
|
+
`${AGENT_VERSION_PREFIX}${version} -->\n\n${block}`;
|
|
31
|
+
|
|
32
|
+
/** The stamped version of a committed AGENTS.md, or null when it predates stamping or carries no block. */
|
|
33
|
+
export const extractBlockVersion = (content: string): string | null =>
|
|
34
|
+
content.match(/<!-- akan:agent:version ([^\s]+) -->/)?.[1] ?? null;
|
|
35
|
+
|
|
36
|
+
/** The running `@akanjs/devkit` version; null when its package.json is unreadable, which must not fail a doctor run. */
|
|
37
|
+
export const readDevkitVersion = async (): Promise<string | null> => {
|
|
38
|
+
const { readFile } = await import("node:fs/promises");
|
|
39
|
+
const { getDirname } = await import("./getDirname");
|
|
40
|
+
try {
|
|
41
|
+
const raw = await readFile(`${getDirname(import.meta.url)}/package.json`, "utf-8");
|
|
42
|
+
return (JSON.parse(raw) as { version?: string }).version ?? null;
|
|
43
|
+
} catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
22
48
|
export interface AgentsIndexScope {
|
|
23
49
|
type: "app" | "lib";
|
|
24
50
|
name: string;
|
|
@@ -139,8 +165,28 @@ ${block}
|
|
|
139
165
|
${AGENT_BLOCK_END}
|
|
140
166
|
`;
|
|
141
167
|
|
|
142
|
-
|
|
168
|
+
export const CLAUDE_COMMENT_RULE = `## Comments — Overrides Your Default
|
|
169
|
+
|
|
170
|
+
Write **no comments** unless the comment passes the test below. This is the rule agents break most often here, so it
|
|
171
|
+
is repeated outside the guide: a diff that adds a comment the test rejects is a diff to redo.
|
|
172
|
+
|
|
173
|
+
Before typing \`//\`, \`/*\`, or a doc block, ask — **does this sentence carry a fact that is nowhere in the code?**
|
|
174
|
+
|
|
175
|
+
- Restates the identifier, the signature, or the line under it → delete it.
|
|
176
|
+
- Labels a section (\`// helpers\`, \`// state\`) or narrates a step (\`// fetch the user\`, \`// then save\`) → delete it.
|
|
177
|
+
- JSDoc on an ordinary function, or a why/how preamble on ordinary logic → delete it.
|
|
178
|
+
- Explains the edit you just made, for whoever reads the diff → say it in your reply, not in the file.
|
|
179
|
+
- Names a vendor or protocol quirk, an infrastructure constraint, a library gotcha, security reasoning, a math
|
|
180
|
+
derivation, a domain field's business meaning, a state transition, or why an obvious alternative was rejected →
|
|
181
|
+
keep it, one line.
|
|
182
|
+
|
|
183
|
+
That keep-list is exact — \`Comments\` in the guide is the full version. "It aids readability" and "this logic is
|
|
184
|
+
subtle" are not on it: rename or split the code instead. When you edit an existing file, match its density; if the
|
|
185
|
+
surrounding code carries none, your diff carries none.`;
|
|
186
|
+
|
|
143
187
|
export const renderScopeClaudeMd = (scope: AgentsIndexScope): string => `# ${scope.name} — Claude Code Guide
|
|
144
188
|
|
|
145
189
|
@AGENTS.md
|
|
190
|
+
|
|
191
|
+
${CLAUDE_COMMENT_RULE}
|
|
146
192
|
`;
|
package/aiEditor.ts
CHANGED
|
@@ -19,7 +19,7 @@ import type { FileContent } from "./types";
|
|
|
19
19
|
|
|
20
20
|
const MAX_ASK_TRY = 300;
|
|
21
21
|
|
|
22
|
-
const deepSeekLlmModels = ["deepseek-
|
|
22
|
+
const deepSeekLlmModels = ["deepseek-v4-flash", "deepseek-v4-pro"] as const;
|
|
23
23
|
|
|
24
24
|
const openAiLlmModels = ["gpt-5.5"] as const;
|
|
25
25
|
|
|
@@ -19,7 +19,6 @@ const packageJson: PackageJson = {
|
|
|
19
19
|
react: "19.0.0",
|
|
20
20
|
"react-dom": "19.0.0",
|
|
21
21
|
"react-server-dom-webpack": "19.0.0",
|
|
22
|
-
sharp: "1.0.0",
|
|
23
22
|
"@external/runtime": "2.0.0",
|
|
24
23
|
},
|
|
25
24
|
};
|
|
@@ -144,8 +143,6 @@ describe("AkanAppConfig", () => {
|
|
|
144
143
|
react: "19.0.0",
|
|
145
144
|
"react-dom": "19.0.0",
|
|
146
145
|
"react-server-dom-webpack": "19.0.0",
|
|
147
|
-
croner: akanPackageJson.peerDependencies?.croner,
|
|
148
|
-
sharp: "1.0.0",
|
|
149
146
|
"@external/runtime": "2.0.0",
|
|
150
147
|
},
|
|
151
148
|
});
|
|
@@ -184,8 +181,6 @@ describe("AkanAppConfig", () => {
|
|
|
184
181
|
react: runtimeDependencies.react,
|
|
185
182
|
"react-dom": runtimeDependencies["react-dom"],
|
|
186
183
|
"react-server-dom-webpack": runtimeDependencies["react-server-dom-webpack"],
|
|
187
|
-
croner: runtimeDependencies.croner,
|
|
188
|
-
sharp: runtimeDependencies.sharp,
|
|
189
184
|
});
|
|
190
185
|
});
|
|
191
186
|
|
|
@@ -198,9 +193,6 @@ describe("AkanAppConfig", () => {
|
|
|
198
193
|
const multipleConfig = new AkanAppConfig(app, [], packageJson, { defaultDatabaseMode: "multiple" }, baseDevEnv);
|
|
199
194
|
const clusterConfig = new AkanAppConfig(app, [], packageJson, { defaultDatabaseMode: "cluster" }, baseDevEnv);
|
|
200
195
|
|
|
201
|
-
expect(singleConfig.getProductionPackageJson().dependencies).toMatchObject({
|
|
202
|
-
croner: runtimeDependencies.croner,
|
|
203
|
-
});
|
|
204
196
|
expect(singleConfig.getProductionPackageJson().dependencies).not.toHaveProperty("ioredis");
|
|
205
197
|
expect(singleConfig.getProductionPackageJson().dependencies).not.toHaveProperty("bullmq");
|
|
206
198
|
expect(singleConfig.getProductionPackageJson().dependencies).not.toHaveProperty("@libsql/client");
|
|
@@ -210,7 +202,6 @@ describe("AkanAppConfig", () => {
|
|
|
210
202
|
expect(multipleConfig.getProductionPackageJson().dependencies).toMatchObject({
|
|
211
203
|
"@libsql/client": runtimeDependencies["@libsql/client"],
|
|
212
204
|
bullmq: runtimeDependencies.bullmq,
|
|
213
|
-
croner: runtimeDependencies.croner,
|
|
214
205
|
ioredis: runtimeDependencies.ioredis,
|
|
215
206
|
protobufjs: runtimeDependencies.protobufjs,
|
|
216
207
|
});
|
|
@@ -218,7 +209,6 @@ describe("AkanAppConfig", () => {
|
|
|
218
209
|
|
|
219
210
|
expect(clusterConfig.getProductionPackageJson().dependencies).toMatchObject({
|
|
220
211
|
bullmq: runtimeDependencies.bullmq,
|
|
221
|
-
croner: runtimeDependencies.croner,
|
|
222
212
|
ioredis: runtimeDependencies.ioredis,
|
|
223
213
|
postgres: runtimeDependencies.postgres,
|
|
224
214
|
protobufjs: runtimeDependencies.protobufjs,
|
package/akanConfig/akanConfig.ts
CHANGED
|
@@ -50,8 +50,6 @@ const DEFAULT_OPTIMIZE_IMPORTS = [
|
|
|
50
50
|
];
|
|
51
51
|
const WORKSPACE_BARREL_FACETS = ["ui", "webkit", "common", "client", "server"] as const;
|
|
52
52
|
const SSR_RUNTIME_PACKAGES = ["react", "react-dom", "react-server-dom-webpack"] as const;
|
|
53
|
-
const NATIVE_RUNTIME_PACKAGES = ["sharp"] as const;
|
|
54
|
-
const DEFAULT_BACKEND_RUNTIME_PACKAGES = ["croner"] as const;
|
|
55
53
|
// The firebase client (push tokens) and the Capacitor toolchain — `@capacitor/cli` (`npx cap`),
|
|
56
54
|
// `@capacitor/assets` (`npx @capacitor/assets`) plus the `@capacitor/core`/`ios`/`android` runtime
|
|
57
55
|
// and native-platform packages that `npx cap add`/`sync` resolve from the workspace node_modules.
|
|
@@ -92,8 +90,6 @@ const DATABASE_MODE_RUNTIME_PACKAGES = {
|
|
|
92
90
|
} satisfies Record<DatabaseMode, readonly string[]>;
|
|
93
91
|
const AKAN_RUNTIME_PACKAGES = new Set<string>([
|
|
94
92
|
...SSR_RUNTIME_PACKAGES,
|
|
95
|
-
...NATIVE_RUNTIME_PACKAGES,
|
|
96
|
-
...DEFAULT_BACKEND_RUNTIME_PACKAGES,
|
|
97
93
|
...MOBILE_RUNTIME_PACKAGES,
|
|
98
94
|
...Object.values(DATABASE_MODE_RUNTIME_PACKAGES).flat(),
|
|
99
95
|
]);
|
|
@@ -428,13 +424,7 @@ CMD [${command.map((c) => `"${c}"`).join(",")}]`;
|
|
|
428
424
|
return akanPackageJson.dependencies?.[lib] ?? akanPackageJson.peerDependencies?.[lib];
|
|
429
425
|
}
|
|
430
426
|
#getProductionRuntimePackages() {
|
|
431
|
-
return [
|
|
432
|
-
...this.externalLibs,
|
|
433
|
-
...SSR_RUNTIME_PACKAGES,
|
|
434
|
-
...NATIVE_RUNTIME_PACKAGES,
|
|
435
|
-
...DEFAULT_BACKEND_RUNTIME_PACKAGES,
|
|
436
|
-
...this.getDatabaseModeRuntimePackages(),
|
|
437
|
-
];
|
|
427
|
+
return [...this.externalLibs, ...SSR_RUNTIME_PACKAGES, ...this.getDatabaseModeRuntimePackages()];
|
|
438
428
|
}
|
|
439
429
|
getDatabaseModeRuntimePackages(databaseMode: DatabaseMode = this.defaultDatabaseMode) {
|
|
440
430
|
return [...DATABASE_MODE_RUNTIME_PACKAGES[databaseMode]];
|
package/akanContext.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readdir } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { capitalize } from "akanjs/common";
|
|
4
|
+
import { extractBlockVersion, readDevkitVersion } from "./agentsIndex";
|
|
4
5
|
import { AppExecutor, LibExecutor, type SysExecutor, type WorkspaceExecutor } from "./executors";
|
|
5
6
|
import { FileSys } from "./fileSys";
|
|
6
7
|
import { collectRecipeSources, findInlineRecipeDuplicates, scanRecipes } from "./recipeScanner";
|
|
@@ -14,6 +15,7 @@ import {
|
|
|
14
15
|
workflowRunArtifactPath,
|
|
15
16
|
workflowSyncDir,
|
|
16
17
|
} from "./workflow";
|
|
18
|
+
import { appRootAllowedDirs, appRootAllowedFiles, isScannedAppRootEntry } from "./workspaceLayout";
|
|
17
19
|
|
|
18
20
|
export type AkanContextFormat = "json" | "markdown";
|
|
19
21
|
export type AkanModuleKind = "domain" | "service" | "scalar";
|
|
@@ -275,6 +277,8 @@ const validationCommands = [
|
|
|
275
277
|
"akan test <app-or-lib-or-pkg>",
|
|
276
278
|
"akan build <app-name>",
|
|
277
279
|
"akan doctor --strict --format json",
|
|
280
|
+
"akan quality scan [--format json]",
|
|
281
|
+
"akan quality ssr [--format json]",
|
|
278
282
|
];
|
|
279
283
|
|
|
280
284
|
const unknownGeneratedFilesFreshness: GeneratedFilesFreshness = {
|
|
@@ -313,36 +317,6 @@ const moduleShapeFiles = (module: AkanModuleContext) => {
|
|
|
313
317
|
const constantFieldNames = (content: string) =>
|
|
314
318
|
[...content.matchAll(/\b([A-Za-z_$][\w$]*)\s*:\s*field\(/g)].map((match) => match[1]).filter(Boolean);
|
|
315
319
|
|
|
316
|
-
const appRootAllowFiles = new Set([
|
|
317
|
-
// 스코프 에이전트 가이드 — scan(write) 이 유지 (agentsIndex.ts); scanInfo.ts 의 appRootAllowedFiles 와 동기
|
|
318
|
-
"AGENTS.md",
|
|
319
|
-
"CLAUDE.md",
|
|
320
|
-
"akan.app.json",
|
|
321
|
-
"akan.config.ts",
|
|
322
|
-
"capacitor.config.ts",
|
|
323
|
-
"client.ts",
|
|
324
|
-
"main.ts",
|
|
325
|
-
"package.json",
|
|
326
|
-
"server.ts",
|
|
327
|
-
"tsconfig.json",
|
|
328
|
-
]);
|
|
329
|
-
|
|
330
|
-
const appRootAllowDirs = new Set([
|
|
331
|
-
".akan",
|
|
332
|
-
"android",
|
|
333
|
-
"common",
|
|
334
|
-
"env",
|
|
335
|
-
"ios",
|
|
336
|
-
"lib",
|
|
337
|
-
"page",
|
|
338
|
-
"private",
|
|
339
|
-
"public",
|
|
340
|
-
"script",
|
|
341
|
-
"srvkit",
|
|
342
|
-
"ui",
|
|
343
|
-
"webkit",
|
|
344
|
-
]);
|
|
345
|
-
|
|
346
320
|
const safeReadDir = async (dirPath: string) => {
|
|
347
321
|
try {
|
|
348
322
|
return (await readdir(dirPath, { withFileTypes: true })).sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -647,6 +621,30 @@ export class AkanContextAnalyzer {
|
|
|
647
621
|
};
|
|
648
622
|
}
|
|
649
623
|
|
|
624
|
+
// The conventions in AGENTS.md ship with the package, but nothing re-renders them on `bun update` — a workspace
|
|
625
|
+
// keeps whichever release wrote its block until someone re-runs the install. Comparing the stamp against the
|
|
626
|
+
// running devkit is the only signal that the guide an agent is reading is older than the framework it describes.
|
|
627
|
+
static async #agentGuideDrift(workspace: WorkspaceExecutor) {
|
|
628
|
+
const installed = await readDevkitVersion();
|
|
629
|
+
if (!installed) return null;
|
|
630
|
+
const content = await safeReadText(path.join(workspace.workspaceRoot, "AGENTS.md"));
|
|
631
|
+
if (content === null) return null;
|
|
632
|
+
const stamped = extractBlockVersion(content);
|
|
633
|
+
if (stamped === installed) return null;
|
|
634
|
+
const hint = "Re-render the AGENTS.md managed block from the installed framework release.";
|
|
635
|
+
if (!stamped)
|
|
636
|
+
return {
|
|
637
|
+
code: "agent-guide-unstamped",
|
|
638
|
+
message: `AGENTS.md carries no generated-version stamp, so its conventions may predate @akanjs/devkit ${installed}`,
|
|
639
|
+
hint,
|
|
640
|
+
};
|
|
641
|
+
return {
|
|
642
|
+
code: "agent-guide-stale",
|
|
643
|
+
message: `AGENTS.md was generated by @akanjs/devkit ${stamped}, but ${installed} is installed`,
|
|
644
|
+
hint,
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
|
|
650
648
|
static async doctor(
|
|
651
649
|
workspace: WorkspaceExecutor,
|
|
652
650
|
{
|
|
@@ -671,7 +669,8 @@ export class AkanContextAnalyzer {
|
|
|
671
669
|
for (const app of context.apps) {
|
|
672
670
|
const appPath = path.join(workspace.workspaceRoot, app.path);
|
|
673
671
|
for (const entry of await safeReadDir(appPath)) {
|
|
674
|
-
|
|
672
|
+
if (!isScannedAppRootEntry(entry.name)) continue;
|
|
673
|
+
const allowed = entry.isDirectory() ? appRootAllowedDirs.has(entry.name) : appRootAllowedFiles.has(entry.name);
|
|
675
674
|
if (!allowed) {
|
|
676
675
|
const action = repairAction(
|
|
677
676
|
"module-shape",
|
|
@@ -691,6 +690,19 @@ export class AkanContextAnalyzer {
|
|
|
691
690
|
}
|
|
692
691
|
}
|
|
693
692
|
|
|
693
|
+
const agentDrift = await AkanContextAnalyzer.#agentGuideDrift(workspace);
|
|
694
|
+
if (agentDrift) {
|
|
695
|
+
const action = repairAction("generated", "akan agent install agents-md", agentDrift.hint, true);
|
|
696
|
+
diagnostics.push({
|
|
697
|
+
severity: "warning",
|
|
698
|
+
code: agentDrift.code,
|
|
699
|
+
path: "AGENTS.md",
|
|
700
|
+
message: agentDrift.message,
|
|
701
|
+
repairActions: [action],
|
|
702
|
+
});
|
|
703
|
+
repairActions.push(action);
|
|
704
|
+
}
|
|
705
|
+
|
|
694
706
|
for (const sys of [...context.apps, ...context.libs]) {
|
|
695
707
|
for (const module of sys.modules) {
|
|
696
708
|
if (!module.abstract.exists) {
|
|
@@ -4,7 +4,7 @@ import { AKAN_OPTIONAL_BACKEND_EXTERNALS } from "./applicationBuildRunner";
|
|
|
4
4
|
describe("ApplicationBuildRunner", () => {
|
|
5
5
|
test("externalizes Akan optional backend dependencies", () => {
|
|
6
6
|
expect(AKAN_OPTIONAL_BACKEND_EXTERNALS).toEqual(
|
|
7
|
-
expect.arrayContaining(["@libsql/client", "bullmq", "
|
|
7
|
+
expect.arrayContaining(["@libsql/client", "bullmq", "ioredis", "postgres", "protobufjs"]),
|
|
8
8
|
);
|
|
9
9
|
});
|
|
10
10
|
});
|
|
@@ -56,7 +56,6 @@ const SSR_RENDER_EXTERNALS = [
|
|
|
56
56
|
export const AKAN_OPTIONAL_BACKEND_EXTERNALS = [
|
|
57
57
|
"@libsql/client",
|
|
58
58
|
"bullmq",
|
|
59
|
-
"croner",
|
|
60
59
|
"ioredis",
|
|
61
60
|
"postgres",
|
|
62
61
|
"protobufjs",
|
|
@@ -112,7 +111,7 @@ export class ApplicationBuildRunner {
|
|
|
112
111
|
() => precompressArtifacts(this.#app),
|
|
113
112
|
(result) =>
|
|
114
113
|
result.files > 0
|
|
115
|
-
? `${result.files} files, ${ApplicationBuildRunner.formatBytes(result.inputBytes)} -> ${ApplicationBuildRunner.formatBytes(result.outputBytes)}`
|
|
114
|
+
? `${result.files} files, ${ApplicationBuildRunner.formatBytes(result.inputBytes)} -> gzip ${ApplicationBuildRunner.formatBytes(result.outputBytes)} / br ${ApplicationBuildRunner.formatBytes(result.brotliBytes)}`
|
|
116
115
|
: "no files",
|
|
117
116
|
phaseOptions,
|
|
118
117
|
);
|
package/biome.base.json
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://biomejs.dev/schemas/2.5.8/schema.json",
|
|
3
|
+
"vcs": {
|
|
4
|
+
"enabled": true,
|
|
5
|
+
"clientKind": "git",
|
|
6
|
+
"useIgnoreFile": false
|
|
7
|
+
},
|
|
8
|
+
"formatter": {
|
|
9
|
+
"enabled": true,
|
|
10
|
+
"indentStyle": "space",
|
|
11
|
+
"indentWidth": 2,
|
|
12
|
+
"lineWidth": 120
|
|
13
|
+
},
|
|
14
|
+
"linter": {
|
|
15
|
+
"enabled": true,
|
|
16
|
+
"rules": {
|
|
17
|
+
"preset": "recommended",
|
|
18
|
+
"suspicious": {
|
|
19
|
+
"noConsole": {
|
|
20
|
+
"level": "error",
|
|
21
|
+
"options": {
|
|
22
|
+
"allow": ["assert", "error", "info", "warn"]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"noArrayIndexKey": "off",
|
|
26
|
+
"noShadowRestrictedNames": "off",
|
|
27
|
+
"noUnnecessaryConditions": {
|
|
28
|
+
"level": "warn"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"correctness": {
|
|
32
|
+
"noUnusedFunctionParameters": "off",
|
|
33
|
+
"noUnusedImports": {
|
|
34
|
+
"level": "error",
|
|
35
|
+
"fix": "safe"
|
|
36
|
+
},
|
|
37
|
+
"useParseIntRadix": "off",
|
|
38
|
+
"useExhaustiveDependencies": "off",
|
|
39
|
+
"useHookAtTopLevel": "off"
|
|
40
|
+
},
|
|
41
|
+
"nursery": {
|
|
42
|
+
"useSortedClasses": {
|
|
43
|
+
"level": "error",
|
|
44
|
+
"fix": "safe",
|
|
45
|
+
"options": {
|
|
46
|
+
"attributes": ["classList"],
|
|
47
|
+
"functions": ["cn"]
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"a11y": "off",
|
|
52
|
+
"complexity": {
|
|
53
|
+
"noStaticOnlyClass": "off"
|
|
54
|
+
},
|
|
55
|
+
"style": {
|
|
56
|
+
"useTemplate": {
|
|
57
|
+
"level": "warn",
|
|
58
|
+
"fix": "safe"
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
"domains": {
|
|
63
|
+
"project": "recommended",
|
|
64
|
+
"react": "recommended",
|
|
65
|
+
"test": "recommended",
|
|
66
|
+
"types": "all"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
"javascript": {
|
|
70
|
+
"parser": {
|
|
71
|
+
"unsafeParameterDecoratorsEnabled": true
|
|
72
|
+
},
|
|
73
|
+
"formatter": {
|
|
74
|
+
"quoteStyle": "double"
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
"assist": {
|
|
78
|
+
"enabled": true,
|
|
79
|
+
"actions": {
|
|
80
|
+
"source": {
|
|
81
|
+
"organizeImports": "on"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"overrides": [
|
|
86
|
+
{
|
|
87
|
+
"includes": [
|
|
88
|
+
"**/env.client.local.ts",
|
|
89
|
+
"**/env.client.debug.ts",
|
|
90
|
+
"**/env.client.develop.ts",
|
|
91
|
+
"**/env.client.main.ts",
|
|
92
|
+
"**/env.client.testing.ts",
|
|
93
|
+
"**/env.server.local.ts",
|
|
94
|
+
"**/env.server.develop.ts",
|
|
95
|
+
"**/env.server.main.ts",
|
|
96
|
+
"**/env.server.testing.ts",
|
|
97
|
+
"**/env.server.type.ts",
|
|
98
|
+
"apps/*/lib/cnst.ts",
|
|
99
|
+
"apps/*/lib/dict.ts",
|
|
100
|
+
"apps/*/lib/db.ts",
|
|
101
|
+
"apps/*/lib/srv.ts",
|
|
102
|
+
"apps/*/lib/st.ts",
|
|
103
|
+
"apps/*/lib/sig.ts",
|
|
104
|
+
"apps/*/lib/useClient.ts",
|
|
105
|
+
"apps/*/lib/useServer.ts",
|
|
106
|
+
"apps/*/client.ts",
|
|
107
|
+
"apps/*/server.ts",
|
|
108
|
+
"apps/*/lib/*/index.tsx",
|
|
109
|
+
"libs/*/lib/cnst.ts",
|
|
110
|
+
"libs/*/lib/dict.ts",
|
|
111
|
+
"libs/*/lib/db.ts",
|
|
112
|
+
"libs/*/lib/srv.ts",
|
|
113
|
+
"libs/*/lib/st.ts",
|
|
114
|
+
"libs/*/lib/sig.ts",
|
|
115
|
+
"libs/*/lib/useClient.ts",
|
|
116
|
+
"libs/*/lib/useServer.ts",
|
|
117
|
+
"libs/*/client.ts",
|
|
118
|
+
"libs/*/server.ts",
|
|
119
|
+
"libs/*/index.ts",
|
|
120
|
+
"libs/*/lib/*/index.tsx",
|
|
121
|
+
"apps/*/lib/__lib/**",
|
|
122
|
+
"libs/*/lib/__lib/**"
|
|
123
|
+
],
|
|
124
|
+
"linter": { "enabled": false },
|
|
125
|
+
"formatter": { "enabled": false },
|
|
126
|
+
"assist": { "enabled": false }
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"includes": [
|
|
130
|
+
"apps/**/*.ts",
|
|
131
|
+
"apps/**/*.tsx",
|
|
132
|
+
"libs/**/*.ts",
|
|
133
|
+
"libs/**/*.tsx",
|
|
134
|
+
"!**/*.test.ts",
|
|
135
|
+
"!**/*.test.tsx",
|
|
136
|
+
"!**/*.spec.ts",
|
|
137
|
+
"!**/*.spec.tsx",
|
|
138
|
+
"!**/*.constant.ts",
|
|
139
|
+
"!**/common/**",
|
|
140
|
+
"!**/env/**"
|
|
141
|
+
],
|
|
142
|
+
"plugins": ["./node_modules/@akanjs/devkit/lint/no-throw-raw-error.grit"]
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
"includes": [
|
|
146
|
+
"{apps,libs}/**/page/**/*.ts",
|
|
147
|
+
"{apps,libs}/**/page/**/*.tsx",
|
|
148
|
+
"{apps,libs}/**/*.Unit.tsx",
|
|
149
|
+
"{apps,libs}/**/*.View.tsx"
|
|
150
|
+
],
|
|
151
|
+
"plugins": [
|
|
152
|
+
"./node_modules/@akanjs/devkit/lint/no-import-client-functions.grit",
|
|
153
|
+
"./node_modules/@akanjs/devkit/lint/no-use-client-in-server.grit"
|
|
154
|
+
]
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"includes": ["{apps,libs}/**/page/**/*.ts", "{apps,libs}/**/page/**/*.tsx"],
|
|
158
|
+
"plugins": ["./node_modules/@akanjs/devkit/lint/non-scalar-props-restricted.grit"]
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"includes": [
|
|
162
|
+
"{apps,libs}/**/*.constant.ts",
|
|
163
|
+
"{apps,libs}/**/*.document.ts",
|
|
164
|
+
"{apps,libs}/**/*.service.ts",
|
|
165
|
+
"{apps,libs}/**/*.store.ts"
|
|
166
|
+
],
|
|
167
|
+
"plugins": ["./node_modules/@akanjs/devkit/lint/no-js-private-class-method.grit"]
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
"includes": ["{apps,libs}/**/*.store.ts"],
|
|
171
|
+
"plugins": ["./node_modules/@akanjs/devkit/lint/no-return-in-store-action.grit"]
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"includes": ["{apps,libs}/**/*.signal.ts"],
|
|
175
|
+
"plugins": ["./node_modules/@akanjs/devkit/lint/no-redeclare-predefined-endpoint.grit"]
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
"includes": ["{apps,libs}/**/*.Util.tsx", "{apps,libs}/**/*.Zone.tsx"],
|
|
179
|
+
"plugins": ["./node_modules/@akanjs/devkit/lint/no-model-type-in-util-zone.grit"]
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"includes": [
|
|
183
|
+
"**/ui/**/*.ts",
|
|
184
|
+
"**/ui/**/*.tsx",
|
|
185
|
+
"**/webkit/**/*.ts",
|
|
186
|
+
"**/webkit/**/*.tsx",
|
|
187
|
+
"**/common/**/*.ts",
|
|
188
|
+
"**/page/**/*.tsx",
|
|
189
|
+
"**/*.constant.ts",
|
|
190
|
+
"**/*.store.ts",
|
|
191
|
+
"**/*.Template.tsx",
|
|
192
|
+
"**/*.Unit.tsx",
|
|
193
|
+
"**/*.Util.tsx",
|
|
194
|
+
"**/*.View.tsx",
|
|
195
|
+
"**/*.Zone.tsx",
|
|
196
|
+
"!**/*.test.ts",
|
|
197
|
+
"!**/*.test.tsx",
|
|
198
|
+
"!**/*.spec.ts",
|
|
199
|
+
"!**/*.spec.tsx"
|
|
200
|
+
],
|
|
201
|
+
"plugins": ["./node_modules/@akanjs/devkit/lint/no-bang-comment-in-client.grit"]
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"includes": [
|
|
205
|
+
"{apps,libs}/**/*.constant.ts",
|
|
206
|
+
"{apps,libs}/**/*.dictionary.ts",
|
|
207
|
+
"{apps,libs}/**/*.document.ts",
|
|
208
|
+
"{apps,libs}/**/*.service.ts",
|
|
209
|
+
"{apps,libs}/**/*.signal.ts",
|
|
210
|
+
"{apps,libs}/**/*.store.ts",
|
|
211
|
+
"{apps,libs}/**/*.Template.tsx",
|
|
212
|
+
"{apps,libs}/**/*.Unit.tsx",
|
|
213
|
+
"{apps,libs}/**/*.Util.tsx",
|
|
214
|
+
"{apps,libs}/**/*.View.tsx",
|
|
215
|
+
"{apps,libs}/**/*.Zone.tsx"
|
|
216
|
+
],
|
|
217
|
+
"plugins": ["./node_modules/@akanjs/devkit/lint/no-deep-internal-import.grit"]
|
|
218
|
+
},
|
|
219
|
+
{
|
|
220
|
+
"includes": [
|
|
221
|
+
"{apps,libs}/**/page/**/*.ts",
|
|
222
|
+
"{apps,libs}/**/page/**/*.tsx",
|
|
223
|
+
"{apps,libs}/**/index.ts",
|
|
224
|
+
"{apps,libs}/**/index.tsx",
|
|
225
|
+
"{apps,libs}/**/cnst.ts",
|
|
226
|
+
"{apps,libs}/**/db.ts",
|
|
227
|
+
"{apps,libs}/**/dict.ts",
|
|
228
|
+
"{apps,libs}/**/option.ts",
|
|
229
|
+
"{apps,libs}/**/sig.ts",
|
|
230
|
+
"{apps,libs}/**/srv.ts",
|
|
231
|
+
"{apps,libs}/**/st.ts",
|
|
232
|
+
"{apps,libs}/**/*.constant.ts",
|
|
233
|
+
"{apps,libs}/**/*.dictionary.ts",
|
|
234
|
+
"{apps,libs}/**/*.document.ts",
|
|
235
|
+
"{apps,libs}/**/*.service.ts",
|
|
236
|
+
"{apps,libs}/**/*.signal.ts",
|
|
237
|
+
"{apps,libs}/**/*.signal.test.ts",
|
|
238
|
+
"{apps,libs}/**/*.service.test.ts",
|
|
239
|
+
"{apps,libs}/**/*.store.ts",
|
|
240
|
+
"{apps,libs}/**/*.Template.tsx",
|
|
241
|
+
"{apps,libs}/**/*.Unit.tsx",
|
|
242
|
+
"{apps,libs}/**/*.Util.tsx",
|
|
243
|
+
"{apps,libs}/**/*.View.tsx",
|
|
244
|
+
"{apps,libs}/**/*.Zone.tsx"
|
|
245
|
+
],
|
|
246
|
+
"plugins": [
|
|
247
|
+
"./node_modules/@akanjs/devkit/lint/no-import-external-library.grit",
|
|
248
|
+
"./node_modules/@akanjs/devkit/lint/no-deep-internal-import.grit"
|
|
249
|
+
]
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
"includes": [
|
|
253
|
+
"apps/**/*.tsx",
|
|
254
|
+
"libs/**/*.tsx",
|
|
255
|
+
"apps/**/ui/**/*.ts",
|
|
256
|
+
"libs/**/ui/**/*.ts",
|
|
257
|
+
"apps/**/webkit/**/*.ts",
|
|
258
|
+
"libs/**/webkit/**/*.ts",
|
|
259
|
+
"apps/**/page/**/*.ts",
|
|
260
|
+
"libs/**/page/**/*.ts",
|
|
261
|
+
"apps/**/common/**/*.ts",
|
|
262
|
+
"libs/**/common/**/*.ts",
|
|
263
|
+
"apps/**/*.store.ts",
|
|
264
|
+
"libs/**/*.store.ts",
|
|
265
|
+
"apps/**/*.constant.ts",
|
|
266
|
+
"libs/**/*.constant.ts",
|
|
267
|
+
"!**/*.test.ts",
|
|
268
|
+
"!**/*.test.tsx",
|
|
269
|
+
"!**/*.spec.ts",
|
|
270
|
+
"!**/*.spec.tsx"
|
|
271
|
+
],
|
|
272
|
+
"plugins": ["./node_modules/@akanjs/devkit/lint/no-import-server-in-client.grit"]
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"includes": [
|
|
276
|
+
"apps/**/*.document.ts",
|
|
277
|
+
"libs/**/*.document.ts",
|
|
278
|
+
"apps/**/*.dictionary.ts",
|
|
279
|
+
"libs/**/*.dictionary.ts",
|
|
280
|
+
"apps/**/*.service.ts",
|
|
281
|
+
"libs/**/*.service.ts",
|
|
282
|
+
"apps/**/*.signal.ts",
|
|
283
|
+
"libs/**/*.signal.ts",
|
|
284
|
+
"apps/**/srvkit/**/*.ts",
|
|
285
|
+
"libs/**/srvkit/**/*.ts",
|
|
286
|
+
"apps/**/common/**/*.ts",
|
|
287
|
+
"libs/**/common/**/*.ts",
|
|
288
|
+
"apps/**/*.constant.ts",
|
|
289
|
+
"libs/**/*.constant.ts",
|
|
290
|
+
"!**/*.test.ts",
|
|
291
|
+
"!**/*.test.tsx",
|
|
292
|
+
"!**/*.spec.ts",
|
|
293
|
+
"!**/*.spec.tsx"
|
|
294
|
+
],
|
|
295
|
+
"plugins": ["./node_modules/@akanjs/devkit/lint/no-import-client-in-server.grit"]
|
|
296
|
+
},
|
|
297
|
+
{
|
|
298
|
+
"includes": [
|
|
299
|
+
"apps/**/*.ts",
|
|
300
|
+
"apps/**/*.tsx",
|
|
301
|
+
"libs/**/*.ts",
|
|
302
|
+
"libs/**/*.tsx",
|
|
303
|
+
"!**/*.test.ts",
|
|
304
|
+
"!**/*.test.tsx",
|
|
305
|
+
"!**/*.spec.ts",
|
|
306
|
+
"!**/*.spec.tsx"
|
|
307
|
+
],
|
|
308
|
+
"plugins": [
|
|
309
|
+
"./node_modules/@akanjs/devkit/lint/no-raw-palette-class.grit",
|
|
310
|
+
"./node_modules/@akanjs/devkit/lint/no-arbitrary-color.grit",
|
|
311
|
+
"./node_modules/@akanjs/devkit/lint/no-daisyui-legacy-class.grit",
|
|
312
|
+
"./node_modules/@akanjs/devkit/lint/no-inline-color.grit",
|
|
313
|
+
"./node_modules/@akanjs/devkit/lint/no-interpolated-arbitrary-class.grit"
|
|
314
|
+
]
|
|
315
|
+
}
|
|
316
|
+
]
|
|
317
|
+
}
|
package/biomeBase.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** `extends` target for a workspace `biome.json`; Biome resolves it through node_modules. */
|
|
2
|
+
export const biomeBaseConfig = "@akanjs/devkit/biome.base.json";
|
|
3
|
+
|
|
4
|
+
// Biome moves rules between groups across minors — `noUnnecessaryConditions` sat in `nursery` at 2.4 and moved to
|
|
5
|
+
// `suspicious` at 2.5 — and the stale position is a hard "unknown key" error, not a warning. A workspace whose
|
|
6
|
+
// Biome disagrees with the shipped base config therefore fails to load it at all, which is why the version is
|
|
7
|
+
// pinned here instead of resolved to latest at create time. Bump this and `biome.base.json` in one commit, and run
|
|
8
|
+
// `biome migrate --write` in the workspace root and in `pkgs/@akanjs/devkit` so both configs move together.
|
|
9
|
+
export const biomeVersion = "2.5.8";
|